From fd17c2576ddbb9453c183686fe24a403d1bc9a58 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 16 Sep 2022 10:20:54 -0600 Subject: [PATCH 001/158] Enemy drops work --- source/dungeon/EnemyList.py | 2127 +++++++++++++++++++++++++++++++++++ source/logic/Rule.py | 568 ++++++++++ 2 files changed, 2695 insertions(+) create mode 100644 source/dungeon/EnemyList.py create mode 100644 source/logic/Rule.py diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py new file mode 100644 index 00000000..f04aff2d --- /dev/null +++ b/source/dungeon/EnemyList.py @@ -0,0 +1,2127 @@ +import math +import typing + +try: + from fast_enum import FastEnum +except ImportError: + from enum import IntFlag as FastEnum + +from source.logic.Rule import RuleFactory + + +class EnemyStats: + def __init__(self, sprite, static, drop_flag=False, prize_pack=0, sub_type=0): + self.sprite = sprite + self.sub_type = sub_type + self.static = static + # self.health = health + # self.damage = damage + self.drop_flag = drop_flag + self.prize_pack = prize_pack + + +class EnemySprite(FastEnum): + CorrectPullSwitch = 0x04, + WrongPullSwitch = 0x06 + Octorok = 0x08 + Moldorm = 0x09 + Cucco = 0x0b + + Octoballoon = 0x0f + OctoballoonBaby = 0x10 + Hinox = 0x11 + Moblin = 0x12 + MiniHelmasaur = 0x13 + ThievesTownGrate = 0x14 + AntiFairy = 0x15 + Wiseman = 0x16 + Hoarder = 0x17 + MiniMoldorm = 0x18 + Poe = 0x19 + Smithy = 0x1a + Arrow = 0x1b + Statue = 0x1c + FluteQuest = 0x1d + CrystalSwitch = 0x1e + SickKid = 0x1f + Sluggula = 0x20 + WaterSwitch = 0x21 + Ropa = 0x22 + RedBari = 0x23 + BlueBari = 0x24 + TalkingTree = 0x25 + HardhatBeetle = 0x26 + Deadrock = 0x27 + DarkWorldHintNpc = 0x28 + AdultNpc = 0x29 + SweepingLady = 0x2a + Hobo = 0x2b + Lumberjacks = 0x2c + TelepathicTile = 0x2d + FluteKid = 0x2e + RaceGameLady = 0x2f + + FortuneTeller = 0x31 + ArgueBros = 0x32 + RupeePull = 0x33 + YoungSnitch = 0x34 + Innkeeper = 0x35 + Witch = 0x36 + Waterfall = 0x37 + EyeStatue = 0x38 + Locksmith = 0x39 + MagicBat = 0x3a + BonkItem = 0x3b + KidInKak = 0x3c + OldSnitch = 0x3d + Hoarder2 = 0x3e + TutorialGuard = 0x3f + + LightningGate = 0x40 + BlueGuard = 0x41 + GreenGuard = 0x42 + RedSpearGuard = 0x43 + BluesainBolt = 0x44 + UsainBolt = 0x45 + BlueArcher = 0x46 + GreenBushGuard = 0x47 + RedJavelinGuard = 0x48 + RedBushGuard = 0x49 + BombGuard = 0x4a + GreenKnifeGuard = 0x4b + Geldman = 0x4c + Popo = 0x4e + Popo2 = 0x4f + + ArmosKnight = 0x53 + Lanmolas = 0x54 + Zora = 0x56 + DesertStatue = 0x57 + Crab = 0x58 + LostWoodsBird = 0x59 + LostWoodsSquirrel =0x5a + SparkCW = 0x5b + SparkCCW = 0x5c + RollerVerticalUp = 0x5d + RollerVerticalDown = 0x5e + RollerHorizontalLeft = 0x5f + RollerHorizontalRight = 0x60 + Beamos = 0x61 + MasterSword = 0x62 + DebirandoPit = 0x63 + Debirando = 0x64 + ArcheryNpc = 0x65 + WallCannonVertLeft = 0x66 + WallCannonVertRight = 0x67 + WallCannonHorzTop = 0x68 + WallCannonHorzBottom = 0x69 + BallNChain = 0x6a + CannonTrooper = 0x6b + CricketRat = 0x6d + Snake = 0x6e + Keese = 0x6f + + Leever = 0x71 + FairyPondTrigger = 0x72 + UnclePriest = 0x73 + RunningNpc = 0x74 + BottleMerchant = 0x75 + Zelda = 0x76 + Grandma = 0x78 + Agahnim = 0x7a + FloatingSkull = 0x7c + BigSpike = 0x7d + FirebarCW = 0x7e + FirebarCCW = 0x7f + Firesnake = 0x80 + Hover = 0x81 + AntiFairyCircle = 0x82 + GreenEyegoreMimic = 0x83 + RedEyegoreMimic = 0x84 + YellowStalfos = 0x85 # falling stalfos that shoots head + Kondongo = 0x86 + Mothula = 0x88 + SpikeBlock = 0x8a + Gibdo = 0x8b + Arrghus = 0x8c + Arrghi = 0x8d + Terrorpin = 0x8e + Blob = 0x8f + Wallmaster = 0x90 + StalfosKnight = 0x91 + HelmasaurKing = 0x92 + Bumper = 0x93 + Pirogusu = 0x94 + LaserEyeLeft = 0x95 + LaserEyeRight = 0x96 + LaserEyeTop = 0x97 + LaserEyeBottom = 0x98 + Pengator = 0x99 + Kyameron = 0x9a + Wizzrobe = 0x9b + Zoro = 0x9c + Babasu = 0x9d + GroveOstritch = 0x9e + GroveRabbit = 0x9f + GroveBird = 0xa0 + Freezor = 0xa1 + Kholdstare = 0xa2 + KholdstareShell = 0xa3 + FallingIce = 0xa4 + BlueZazak = 0xa5 + RedZazak = 0xa6 + Stalfos = 0xa7 + # ... OW + OldMan = 0xad + PipeDown = 0xae + PipeUp = 0xaf + PipeRight = 0xb0 + PipeLeft = 0xb1 + GoodBee = 0xb2 + PedestalPlaque = 0xb3 + BombShopGuy = 0xb5 + BlindMaiden = 0xb7 + + Whirlpool = 0xba + Shopkeeper = 0xbb + Drunkard = 0xbc + Vitreous = 0xbd + # ... (spawnables) + Catfish = 0xc0 + CutsceneAgahnim = 0xc1 + Boulder = 0xc2 + Gibo = 0xc3 # patrick! + Thief = 0xc4 + Medusa = 0xc5 + FourWayShooter = 0xc6 + Pokey = 0xc7 + BigFairy = 0xc8 + Tektite = 0xc9 # firebat? + Chainchomp = 0xca + TrinexxRockHead = 0xcb + TrinexxFireHead = 0xcc + TrinexxIceHead = 0xcd + Blind = 0xce + Swamola = 0xcf + Lynel = 0xd0 + BunnyBeam = 0xd1 + FloppingFish = 0xd2 + Stal = 0xd3 + Ganon = 0xd6 + + Faerie = 0xe3 + SmallKey = 0xe4 + MagicShopAssistant = 0xe9 + HeartPiece = 0xeb + CastleMantle = 0xee + + +class SpriteType(FastEnum): + Normal = 0x00 + Overlord = 0x07 + + +def init_enemy_stats(): + enemy_stats = { + EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True), + EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True), + EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2), + EnemySprite.Moldorm: EnemyStats(EnemySprite.Moldorm, True, False), + EnemySprite.Cucco: EnemyStats(EnemySprite.Cucco, False, False), + + EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0), + EnemySprite.OctoballoonBaby: EnemyStats(EnemySprite.OctoballoonBaby, False), + EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4), + EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1), + EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7), + EnemySprite.ThievesTownGrate: EnemyStats(EnemySprite.ThievesTownGrate, True), + EnemySprite.AntiFairy: EnemyStats(EnemySprite.AntiFairy, False, False), + EnemySprite.Wiseman: EnemyStats(EnemySprite.Wiseman, True), + EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, False), + EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2), + EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, True, 6), + EnemySprite.Smithy: EnemyStats(EnemySprite.Smithy, True), + EnemySprite.Arrow: EnemyStats(EnemySprite.Arrow, True), + EnemySprite.Statue: EnemyStats(EnemySprite.Statue, True), + EnemySprite.FluteQuest: EnemyStats(EnemySprite.FluteQuest, True), + EnemySprite.CrystalSwitch: EnemyStats(EnemySprite.CrystalSwitch, True), + EnemySprite.SickKid: EnemyStats(EnemySprite.SickKid, True), + EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4), + EnemySprite.WaterSwitch: EnemyStats(EnemySprite.WaterSwitch, True), + EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2), + EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2), + EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2), + EnemySprite.TalkingTree: EnemyStats(EnemySprite.TalkingTree, True), + EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6)), + 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.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), + EnemySprite.TutorialGuard: EnemyStats(EnemySprite.TutorialGuard, True), + + EnemySprite.LightningGate: EnemyStats(EnemySprite.LightningGate, True), + EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1), + EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1), + EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1), + EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7), + EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1), + EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5), + EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5), + EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3), + EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7), + EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4), + EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1), + EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2), + EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2), + EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2), + + EnemySprite.ArmosKnight: EnemyStats(EnemySprite.ArmosKnight, True, False), + EnemySprite.Lanmolas: EnemyStats(EnemySprite.Lanmolas, True, False), + EnemySprite.Zora: EnemyStats(EnemySprite.Zora, False, True, 4), + EnemySprite.DesertStatue: EnemyStats(EnemySprite.DesertStatue, True), + EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1), + EnemySprite.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), + EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2), + EnemySprite.ArcheryNpc: EnemyStats(EnemySprite.ArcheryNpc, True), + EnemySprite.WallCannonVertLeft: EnemyStats(EnemySprite.WallCannonVertLeft, True), + EnemySprite.WallCannonVertRight: EnemyStats(EnemySprite.WallCannonVertRight, True), + EnemySprite.WallCannonHorzTop: EnemyStats(EnemySprite.WallCannonHorzTop, True), + EnemySprite.WallCannonHorzBottom: EnemyStats(EnemySprite.WallCannonHorzBottom, True), + EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2), + EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1), + EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2), + EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7)), + EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, True, (0, 7)), + + EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1), + 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), + EnemySprite.BigSpike: EnemyStats(EnemySprite.BigSpike, False, False), + EnemySprite.FirebarCW: EnemyStats(EnemySprite.FirebarCW, False, False), + EnemySprite.FirebarCCW: EnemyStats(EnemySprite.FirebarCCW, False, False), + EnemySprite.Firesnake: EnemyStats(EnemySprite.Firesnake, False, False), + EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2), + EnemySprite.AntiFairyCircle: EnemyStats(EnemySprite.AntiFairyCircle, False, False), + EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5), + EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5), + EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True), + EnemySprite.Kondongo: EnemyStats(EnemySprite.Kondongo, False, True, 6), + EnemySprite.Mothula: EnemyStats(EnemySprite.Mothula, True), + EnemySprite.SpikeBlock: EnemyStats(EnemySprite.SpikeBlock, False, False), + EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3), + EnemySprite.Arrghus: EnemyStats(EnemySprite.Arrghus, True), + EnemySprite.Arrghi: EnemyStats(EnemySprite.Arrghi, True), + EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2), + EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1), + EnemySprite.Wallmaster: EnemyStats(EnemySprite.Wallmaster, True), + EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4), + 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), + EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False), + EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1), + EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, True), + EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0), + EnemySprite.GroveOstritch: EnemyStats(EnemySprite.GroveOstritch, True), + EnemySprite.GroveRabbit: EnemyStats(EnemySprite.GroveRabbit, True), + EnemySprite.GroveBird: EnemyStats(EnemySprite.GroveBird, True), + EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, True, 0), + EnemySprite.Kholdstare: EnemyStats(EnemySprite.Kholdstare, True), + EnemySprite.KholdstareShell: EnemyStats(EnemySprite.KholdstareShell, True), + EnemySprite.FallingIce: EnemyStats(EnemySprite.FallingIce, True), + EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6), + EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6), + EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6), + # ... 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), + + 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), # patrick! + EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False), # could drop if killable thieves is on + EnemySprite.Medusa: EnemyStats(EnemySprite.Medusa, True), + EnemySprite.FourWayShooter: EnemyStats(EnemySprite.FourWayShooter, True), + EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7), + EnemySprite.BigFairy: EnemyStats(EnemySprite.BigFairy, True), + EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2), + EnemySprite.Chainchomp: EnemyStats(EnemySprite.Chainchomp, False, False), + EnemySprite.TrinexxRockHead: EnemyStats(EnemySprite.TrinexxRockHead, True), + EnemySprite.TrinexxFireHead: EnemyStats(EnemySprite.TrinexxFireHead, True), + EnemySprite.TrinexxIceHead: EnemyStats(EnemySprite.TrinexxIceHead, True), + EnemySprite.Blind: EnemyStats(EnemySprite.Blind, True), + EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, True, 0), + EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7), + EnemySprite.BunnyBeam: EnemyStats(EnemySprite.BunnyBeam, False, False), # todo: medallions can kill bunny beams? + EnemySprite.FloppingFish: EnemyStats(EnemySprite.FloppingFish, True), + EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1), + EnemySprite.Ganon: EnemyStats(EnemySprite.Ganon, True), + + 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), + + } + +class Sprite(object): + def __init__(self, super_tile, kind, sub_type, layer, tile_x, tile_y, + region=None, drops_item=False, drop_item_kind=None): + self.super_tile = super_tile + self.kind = kind + self.sub_type = sub_type + self.layer = layer + self.tile_x = tile_x + self.tile_y = tile_y + self.region = region + self.drops_item = drops_item + self.drop_item_kind = drop_item_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): + if super_tile not in vanilla_sprites: + vanilla_sprites[super_tile] = [] + vanilla_sprites[super_tile].append(Sprite(kind, sub_type, layer, tile_x, tile_y, + region, drops_item, drop_item_kind)) + + +def init_vanilla_sprites(): + create_sprite(0x0000, EnemySprite.Ganon, 0x00, 0, 0x17, 0x05, 'Pyramid') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x12, 0x05, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x15, 0x06, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x0f, 0x08, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x10, 0x08, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x18, 0x09, 'Sewers Yet More Rats') + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0f, 0x17) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x09, 0x18) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0b, 0x18) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0a, 0x19) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0c, 0x19) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x09, 0x1a) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0b, 0x1b) + create_sprite(0x0002, EnemySprite.WrongPullSwitch, 0x00, 1, 0x0a, 0x17) + create_sprite(0x0002, EnemySprite.CorrectPullSwitch, 0x00, 1, 0x15, 0x17) + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x0d, 0x1a, 'Sewers Pull Switch') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x12, 0x1a, 'Sewers Pull Switch') + create_sprite(0x0004, EnemySprite.CrystalSwitch, 0x00, 0, 0x09, 0x04) + create_sprite(0x0004, EnemySprite.RollerVerticalUp, 0x00, 0, 0x14, 0x04, 'TR Rupees') + create_sprite(0x0004, EnemySprite.RollerHorizontalRight, 0x00, 0, 0x1b, 0x04, 'TR Rupees') + create_sprite(0x0004, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x05, 0x07, 'TR Crystaroller Middle') + create_sprite(0x0004, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x15, 0x09, 'TR Rupees') + create_sprite(0x0004, 0x16, SpriteType.Overlord, 0, 0x07, 0x12) + create_sprite(0x0004, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x15, 0x15) + create_sprite(0x0004, EnemySprite.WrongPullSwitch, 0x00, 0, 0x1a, 0x15) + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x18, 0x15) + create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x1c, 0x15, 'TR Tongue Pull') + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x16, 0x17) + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x17) + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x18, 0x18) + create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x1a, 0x1a, 'TR Tongue Pull') + create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x15, 0x1b, 'TR Tongue Pull') + create_sprite(0x0004, EnemySprite.Pokey, 0x00, 0, 0x07, 0x18, 'TR Dash Room') + create_sprite(0x0006, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) + create_sprite(0x0007, EnemySprite.Moldorm, 0x00, 0, 0x12, 0x0e) + create_sprite(0x0008, EnemySprite.BigFairy, 0x00, 0, 0x07, 0x16) + create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08) + create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x08, 0x08) + create_sprite(0x0009, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x0b) + create_sprite(0x000a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x08, 'PoD Stalfos Basement') + create_sprite(0x000a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x09, 'PoD Stalfos Basement') + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x0d, 0x09) + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x11, 0x09) + create_sprite(0x000a, 0x17, SpriteType.Overlord, 0, 0x13, 0x0b) + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x0d, 0x0e) + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x11, 0x0e) + create_sprite(0x000b, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x04) + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x07, 0x08, 'PoD Lonely Turtle') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x0b, 'PoD Dark Pegs Middle') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x1b, 0x0b, 'PoD Dark Pegs Right') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x05, 0x16, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x0a, 0x16, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x07, 0x19, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x08, 0x19, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x06, 0x1b, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x09, 0x1b, 'PoD Turtle Party') + create_sprite(0x000d, EnemySprite.Agahnim, 0x00, 0, 0x07, 0x15) + create_sprite(0x000e, EnemySprite.Freezor, 0x00, 0, 0x16, 0x12, 'Ice Lobby') + create_sprite(0x000e, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x16, 'Ice Jelly Key') + create_sprite(0x000e, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x18, 'Ice Jelly Key') + create_sprite(0x000e, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x1a, 'Ice Jelly Key', True, 0xe4) + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x0a, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x18, 0x0a, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.Keese, 0x00, 0, 0x17, 0x0c, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.Keese, 0x00, 0, 0x18, 0x0c, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1c, 0x11, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1c, 0x12, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1a, 0x16, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1b, 0x16, 'Sewers Rat Path') + create_sprite(0x0012, EnemySprite.UnclePriest, 0x00, 0, 0x0f, 0x07) + create_sprite(0x0012, EnemySprite.Zelda, 0x00, 0, 0x10, 0x06) + create_sprite(0x0013, EnemySprite.CrystalSwitch, 0x00, 0, 0x14, 0x11) + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x04, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x04, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x05, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x05, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.FloatingSkull, 0x00, 0, 0x1b, 0x16, 'TR Pokey 2 Bottom') + create_sprite(0x0013, EnemySprite.Pokey, 0x00, 0, 0x16, 0x18, 'TR Pokey 2 Bottom', True, 0xe4) + create_sprite(0x0013, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x18) + create_sprite(0x0013, EnemySprite.FloatingSkull, 0x00, 0, 0x14, 0x1a, 'TR Pokey 2 Bottom') + create_sprite(0x0013, EnemySprite.BunnyBeam, 0x00, 0, 0x1b, 0x1b) + create_sprite(0x0014, EnemySprite.PipeRight, 0x00, 1, 0x0c, 0x04) + create_sprite(0x0014, EnemySprite.PipeUp, 0x00, 1, 0x0f, 0x0a) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x19, 0x0a) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x03, 0x0d) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x1b, 0x0d) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x0f, 0x13) + create_sprite(0x0014, EnemySprite.PipeRight, 0x00, 1, 0x08, 0x18) + create_sprite(0x0014, EnemySprite.PipeLeft, 0x00, 1, 0x17, 0x18) + create_sprite(0x0014, EnemySprite.PipeRight, 0x00, 1, 0x0c, 0x1b) + create_sprite(0x0014, EnemySprite.PipeLeft, 0x00, 1, 0x13, 0x1b) + create_sprite(0x0015, EnemySprite.PipeDown, 0x00, 1, 0x04, 0x0c) + create_sprite(0x0015, EnemySprite.PipeDown, 0x00, 1, 0x11, 0x11) + create_sprite(0x0015, EnemySprite.PipeUp, 0x00, 1, 0x04, 0x17) + create_sprite(0x0015, EnemySprite.PipeLeft, 0x00, 1, 0x16, 0x1b) + create_sprite(0x0015, EnemySprite.Blob, 0x00, 1, 0x0a, 0x09, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.Blob, 0x00, 1, 0x15, 0x09, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.AntiFairy, 0x00, 1, 0x09, 0x0a, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.Pokey, 0x00, 1, 0x18, 0x16, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.AntiFairy, 0x00, 1, 0x08, 0x17, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.AntiFairy, 0x00, 1, 0x17, 0x17, 'TR Pipe Pit') + create_sprite(0x0016, EnemySprite.Blob, 0x00, 0, 0x15, 0x07, 'Swamp C') + 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(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) + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x07, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x09, 'Hera 5F') + create_sprite(0x0017, EnemySprite.FirebarCW, 0x00, 0, 0x06, 0x11, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x12, 0x11, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x17, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x17, 'Hera 5F') + create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x16, 0x0a, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x1a, 0x0e, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x16, 0x10, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x18, 0x16, 'PoD Dark Maze') + create_sprite(0x001a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x06, 'PoD Falling Bridge Mid') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x06, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x19, 0x06, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x0a, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x19, 0x0a, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x10, 'PoD Falling Bridge Mid') + create_sprite(0x001a, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x15, 'PoD Harmless Hellway') + create_sprite(0x001a, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x17, 'PoD Harmless Hellway') + create_sprite(0x001a, EnemySprite.Statue, 0x00, 0, 0x15, 0x19) + create_sprite(0x001a, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x19, 'PoD Harmless Hellway') + create_sprite(0x001a, 0x0b, SpriteType.Overlord, 0, 0x07, 0x1a) + create_sprite(0x001b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x04) + create_sprite(0x001b, EnemySprite.EyeStatue, 0x00, 0, 0x10, 0x04) + create_sprite(0x001b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x0c, 'PoD Bow Statue Left') + create_sprite(0x001b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x14, 'PoD Mimics 2') + create_sprite(0x001b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x03, 0x1c, 'PoD Mimics 2') + create_sprite(0x001b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1c, 'PoD Mimics 2') + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x15) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x15) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x15) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x18) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x18) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x18) + create_sprite(0x001c, 0x19, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08) + create_sprite(0x001e, EnemySprite.CrystalSwitch, 0x00, 0, 0x1a, 0x09) + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Bomb Drop') # todo: need to hit crystal for 2 of these + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Bomb Drop') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x0a, 'Ice Bomb Drop') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x0a, 'Ice Bomb Drop') + create_sprite(0x001e, EnemySprite.Blob, 0x00, 0, 0x08, 0x18, 'Ice Floor Switch') + create_sprite(0x001e, EnemySprite.Blob, 0x00, 0, 0x05, 0x1c, 'Ice Floor Switch') + 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.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') + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x09, 0x1b, 'Ice Pengator Switch') + create_sprite(0x0020, EnemySprite.Agahnim, 0x00, 0, 0x07, 0x15) + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x05, 0x06, 'Sewers Key Rat', True, 0xe4) + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x17, 0x06, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x18, 0x06, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x09, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x0d, 0x0a, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x07, 0x14, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x0d, 0x14, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x12, 0x14, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x0d, 0x18, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x0a, 0x1c, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x13, 0x1c, 'Sewers Dark Aquabats') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x08, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x12, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x15, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x12, 0x15, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x09, 0x18, 'Sewers Water') + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x15, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x16, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x17, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x18, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x19, 0x14) + create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x13, 0x04) + 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.Pokey, 0x00, 0, 0x0a, 0x08, 'TR Twin Pokeys') + create_sprite(0x0024, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x0c, 'TR Twin Pokeys') + create_sprite(0x0026, EnemySprite.Medusa, 0x00, 0, 0x03, 0x04) + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x1a, 0x05) + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x05, 0x06) + create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x06) + create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x09) + create_sprite(0x0026, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x0c) + 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) + create_sprite(0x0026, EnemySprite.Kyameron, 0x00, 0, 0x15, 0x18) + create_sprite(0x0026, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x19) + create_sprite(0x0026, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x1a) + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09) + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x18, 0x13) + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x13) + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x1a) + create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06) + create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x05, 0x0e) + create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x04, 0x16) + create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x08, 0x08) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x07, 0x0d) + create_sprite(0x0028, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x10) + create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x18, 0x16) + create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) + create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17) + create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x08) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x10, 0x0c) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x0f) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0f, 0x13) + create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11) + create_sprite(0x002b, EnemySprite.Statue, 0x00, 0, 0x0a, 0x0a) + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17) + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17) + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18) + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x05, 0x1a) + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a) + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x17, 0x1a) + create_sprite(0x002c, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x05) + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x09, 0x04) + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x06, 0x05) + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x06) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1c, 0x06) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x16, 0x08) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x19, 0x08) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0b) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x0030, EnemySprite.CutsceneAgahnim, 0x00, 0, 0x07, 0x05) + create_sprite(0x0031, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x1a) + create_sprite(0x0031, EnemySprite.CrystalSwitch, 0x00, 0, 0x16, 0x0b) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x05) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x06) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x09) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x0c) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x15) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x15) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x16) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x18) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x19) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x1c) + create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d) + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x0f, 0x0d) + create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x13, 0x0d) + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x10, 0x0e) + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x12, 0x0f) + create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17) + create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17) + create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19) + create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x0f, 0x0b) + create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x10, 0x12) + create_sprite(0x0034, EnemySprite.Kyameron, 0x00, 0, 0x0f, 0x15) + create_sprite(0x0034, EnemySprite.Firesnake, 0x00, 0, 0x19, 0x17) + create_sprite(0x0034, EnemySprite.Blob, 0x00, 0, 0x03, 0x18) + create_sprite(0x0034, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x18) + create_sprite(0x0034, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x1a) + create_sprite(0x0035, EnemySprite.CrystalSwitch, 0x00, 0, 0x16, 0x06) + create_sprite(0x0035, EnemySprite.WaterSwitch, 0x00, 0, 0x14, 0x05) + create_sprite(0x0035, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05) + create_sprite(0x0035, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x09) + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x0b) + create_sprite(0x0035, EnemySprite.Blob, 0x00, 0, 0x07, 0x14) + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x18) + create_sprite(0x0035, EnemySprite.Firesnake, 0x00, 0, 0x16, 0x19) + create_sprite(0x0035, EnemySprite.FourWayShooter, 0x00, 0, 0x17, 0x1a) + create_sprite(0x0035, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b) + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x1c) + create_sprite(0x0036, 0x12, SpriteType.Overlord, 0, 0x17, 0x02) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x14, 0x0a) + create_sprite(0x0036, EnemySprite.Medusa, 0x00, 0, 0x15, 0x0b) + create_sprite(0x0036, 0x10, SpriteType.Overlord, 0, 0x01, 0x0d) + create_sprite(0x0036, EnemySprite.Kyameron, 0x00, 0, 0x14, 0x13) + create_sprite(0x0036, 0x11, SpriteType.Overlord, 0, 0x1e, 0x13) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x09, 0x14) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x12, 0x17) + 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) + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x06) + create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x17, 0x08) + create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x1a, 0x08) + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x09) + create_sprite(0x0037, EnemySprite.Firesnake, 0x00, 0, 0x15, 0x14) + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x17) + create_sprite(0x0037, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x19) + create_sprite(0x0037, EnemySprite.FourWayShooter, 0x00, 0, 0x17, 0x1a) + create_sprite(0x0037, EnemySprite.RedBari, 0x00, 0, 0x15, 0x1c) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x0c, 0x06) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x0038, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x10) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x06, 0x14) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x18) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x1a) + create_sprite(0x0039, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x18) + create_sprite(0x0039, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0039, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x15, True, 0xe4) + create_sprite(0x0039, EnemySprite.MiniHelmasaur, 0x00, 0, 0x09, 0x15) + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16) + create_sprite(0x0039, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18) + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a) + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11) + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11) + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14) + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14) + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x14) + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x14) + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x06) + create_sprite(0x003b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09) + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x0d) + create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0f) + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x13) + create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16) + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a) + create_sprite(0x003c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x08) + create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14) + create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x14) + create_sprite(0x003d, EnemySprite.CrystalSwitch, 0x00, 0, 0x05, 0x17) + create_sprite(0x003d, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x19) + create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x07, True, 0xe4) + create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x07) + create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x15, 0x08) + create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x08) + create_sprite(0x003d, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x0a) + create_sprite(0x003d, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x0b) + create_sprite(0x003d, 0x0a, SpriteType.Overlord, 0, 0x1b, 0x15) + create_sprite(0x003d, EnemySprite.SparkCCW, 0x00, 0, 0x13, 0x16) + create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x1c, 0x16) + create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x09, 0x16) + create_sprite(0x003d, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17) + create_sprite(0x003d, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x17) + create_sprite(0x003e, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x15) + create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x19, 0x04) + create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x16, 0x0b) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x05, 0x12) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x0e, 0x12) + create_sprite(0x003e, 0x07, SpriteType.Overlord, 0, 0x10, 0x12) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x12, 0x12) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x15, 0x12) + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16) + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x18, True, 0xe4) + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x19) + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x1a) + create_sprite(0x003f, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x04, 0x15) + create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x0c, 0x16) + create_sprite(0x003f, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x13, 0x15) + create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x04, 0x17) + create_sprite(0x003f, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x18) + create_sprite(0x0040, EnemySprite.BlueGuard, 0x00, 1, 0x09, 0x08) + create_sprite(0x0040, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x0f) + create_sprite(0x0040, EnemySprite.Statue, 0x00, 1, 0x18, 0x15) + create_sprite(0x0040, EnemySprite.RedSpearGuard, 0x00, 1, 0x1b, 0x18) + create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x17, 0x1a) + create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x19, 0x1a) + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x0a) + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x0f, 0x0d) + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x15) + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x06) + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x06) + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x06) + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x07) + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x07) + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x07) + create_sprite(0x0043, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x06) + create_sprite(0x0043, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x09, 0x06) + create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x05, 0x08) + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x04) + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x08) + create_sprite(0x0044, EnemySprite.Blob, 0x00, 0, 0x17, 0x08) + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0c) + create_sprite(0x0044, EnemySprite.RedBari, 0x00, 0, 0x17, 0x0f) + create_sprite(0x0044, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x15) + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x16) + create_sprite(0x0045, EnemySprite.BlindMaiden, 0x00, 0, 0x19, 0x06) + create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x06, 0x06) + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x0b) + create_sprite(0x0045, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x0b) + create_sprite(0x0045, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x0b) + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x0c) + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x1a, 0x0c) + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x11) + create_sprite(0x0045, EnemySprite.Blob, 0x00, 0, 0x16, 0x18) + create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x19, 0x1b) + create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x07, 0x1c) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x16, 0x05) + create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x06) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x09, 0x1a) + create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x1a) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x11, 0x1b) + create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x05) + create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x0b) + create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0c) + create_sprite(0x0049, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x06) + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x07, 0x08) + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x0b) + create_sprite(0x0049, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x10) + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x16, 0x14) + create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x09, 0x16) + create_sprite(0x0049, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x17) + create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18) + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x1a, 0x18) + create_sprite(0x004a, EnemySprite.Statue, 0x00, 0, 0x14, 0x07) + create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x08) + create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x08) + create_sprite(0x004b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x04) + create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x05) + create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06) + create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x04, 0x08) + create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0b, 0x08) + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x18) + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x19) + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x19) + create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x15, 0x11) + create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x19, 0x12) + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x15, 0x05) + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x1a, 0x05) + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x06) + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x0a) + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x15) + create_sprite(0x004c, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x18) + create_sprite(0x004d, EnemySprite.Moldorm, 0x00, 0, 0x0e, 0x0e) + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x14, 0x08) + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x16, 0x08) + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x18, 0x08) + create_sprite(0x004e, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x09) + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x17, 0x06) + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x14, 0x08) + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x08) + create_sprite(0x0050, EnemySprite.GreenGuard, 0x00, 1, 0x17, 0x0e) + create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x18, 0x10) + create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x17, 0x12) + create_sprite(0x0051, EnemySprite.CastleMantle, 0x00, 0, 0x0e, 0x02) + create_sprite(0x0051, EnemySprite.BlueGuard, 0x01, 1, 0x09, 0x17) + create_sprite(0x0051, EnemySprite.BlueGuard, 0x02, 1, 0x16, 0x17) + create_sprite(0x0052, EnemySprite.GreenGuard, 0x00, 1, 0x07, 0x0d) + create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x08, 0x0f) + create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x07, 0x12) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x17, 0x07) + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1c, 0x09) + create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x17, 0x0c) + create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x1a, 0x0c) + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x13, 0x0e) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x05, 0x15) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x16) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x17) + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x04, 0x1a) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x1a) + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x1a) + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x1b) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0e, 0x05) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0c, 0x0b) + create_sprite(0x0054, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x0e) + create_sprite(0x0054, EnemySprite.FirebarCW, 0x00, 0, 0x0f, 0x0e) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x10, 0x0f) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x12, 0x14) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0f, 0x15) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x17) + create_sprite(0x0055, EnemySprite.UnclePriest, 0x00, 0, 0x0e, 0x08) + create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x14, 0x15) + create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x16) + create_sprite(0x0056, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x05) + create_sprite(0x0056, EnemySprite.Bumper, 0x00, 0, 0x07, 0x19) + create_sprite(0x0056, EnemySprite.Bumper, 0x00, 0, 0x17, 0x19) + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x04) + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x05) + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x03, 0x06) + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0c, 0x06) + create_sprite(0x0056, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11) + create_sprite(0x0056, EnemySprite.SpikeBlock, 0x00, 0, 0x18, 0x12) + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x1b) + create_sprite(0x0056, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x1c) + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x1c) + create_sprite(0x0057, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x04) + create_sprite(0x0057, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x04) + create_sprite(0x0057, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x05) + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x07) + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0c) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x0057, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x14) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0a, 0x14) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x19, 0x14) + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x15) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x17) + create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18) + create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x18) + create_sprite(0x0057, EnemySprite.Statue, 0x00, 0, 0x0b, 0x18) + create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x14) + create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x16) + create_sprite(0x0058, EnemySprite.Bumper, 0x00, 0, 0x16, 0x16) + create_sprite(0x0058, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x04) + create_sprite(0x0058, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x06) + create_sprite(0x0058, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x08, 0x0a) + create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x16, 0x19) + create_sprite(0x0058, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a) + create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x07, 0x10) + create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x16) + create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x14, 0x0f) + create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x1a, 0x0f) + create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x1a, 0x0a) + create_sprite(0x0059, EnemySprite.Firesnake, 0x00, 0, 0x08, 0x0b) + create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x15, 0x0d) + create_sprite(0x0059, EnemySprite.SparkCW, 0x00, 1, 0x05, 0x0e) + create_sprite(0x0059, EnemySprite.BunnyBeam, 0x00, 1, 0x1a, 0x13) + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14) + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x15, 0x15) + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x1a, 0x15) + create_sprite(0x005a, EnemySprite.HelmasaurKing, 0x00, 0, 0x17, 0x16) + create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x17, 0x0c) + create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x18, 0x13) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x15) + create_sprite(0x005b, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x08) + create_sprite(0x005b, EnemySprite.RedEyegoreMimic, 0x00, 1, 0x19, 0x08) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x0e) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x1b, 0x10) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x11) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x12) + create_sprite(0x005c, EnemySprite.WallCannonHorzTop, 0x00, 0, 0x0b, 0x02) + create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x05, 0x0e) + create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x0e, 0x0e) + create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x18) + create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18) + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x05) + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x08, 0x06) + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x08) + create_sprite(0x005d, EnemySprite.RedZazak, 0x00, 0, 0x15, 0x08) + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x08) + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x19, 0x08) + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x08) + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x0b) + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x15) + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x0b, 0x15) + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x1a) + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x1a) + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x1a) + create_sprite(0x005e, 0x0a, SpriteType.Overlord, 0, 0x1b, 0x05) + create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x1c, 0x05) + create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0b) + create_sprite(0x005e, EnemySprite.BigSpike, 0x00, 0, 0x17, 0x14) + create_sprite(0x005e, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18) + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x18) + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18) + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x1b) + create_sprite(0x0060, EnemySprite.BlueGuard, 0x13, 0, 0x13, 0x08) + create_sprite(0x0061, EnemySprite.GreenGuard, 0x01, 0, 0x0c, 0x0e) + create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x12) + create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x12, 0x12) + create_sprite(0x0062, EnemySprite.BlueGuard, 0x13, 0, 0x0c, 0x08) + create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x0a, 0x0d) + create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x11, 0x0e) + create_sprite(0x0063, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x0063, EnemySprite.Beamos, 0x00, 0, 0x07, 0x18) + create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x12) + create_sprite(0x0064, EnemySprite.WrongPullSwitch, 0x00, 0, 0x0b, 0x13) + create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x13) + create_sprite(0x0064, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x16) + create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x17) + create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x19, 0x19) + create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x05, 0x1a) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x15) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x07, 0x17) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x17) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x0b, 0x17) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x19) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x0c, 0x1b) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x13, 0x15) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x09, 0x17) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x18) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x19) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x1c) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x05) + create_sprite(0x0066, 0x10, SpriteType.Overlord, 1, 0x04, 0x06) + create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x06) + create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x07) + create_sprite(0x0066, EnemySprite.Waterfall, 0x00, 1, 0x17, 0x14) + create_sprite(0x0066, 0x10, SpriteType.Overlord, 1, 0x01, 0x16) + create_sprite(0x0066, EnemySprite.Kyameron, 0x00, 1, 0x0f, 0x16) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x13, 0x16) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x18) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0d, 0x19) + create_sprite(0x0066, 0x11, SpriteType.Overlord, 1, 0x1e, 0x19) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x17, 0x1b) + create_sprite(0x0067, EnemySprite.Bumper, 0x00, 0, 0x07, 0x0c) + create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x06) + create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x06) + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c) + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x0f) + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x13) + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13) + create_sprite(0x0067, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x14) + create_sprite(0x0067, EnemySprite.FirebarCCW, 0x00, 0, 0x07, 0x17) + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x18, 0x1a) + create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x0e, 0x07) + 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) + create_sprite(0x0068, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x0e, 0x12) + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x12, 0x12) + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0a) + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0a) + create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x0b) + create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x1c, 0x0b) + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0e) + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0e) + create_sprite(0x006b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x04) + create_sprite(0x006b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x04) + create_sprite(0x006b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0a, 0x06) + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x06, 0x09) + create_sprite(0x006b, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0a) + create_sprite(0x006b, EnemySprite.Statue, 0x00, 0, 0x06, 0x15) + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x03, 0x18) + create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x18) + create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x1b) + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x1b) + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x15) + create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x15) + create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x14, 0x1b) + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x1b) + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17) + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17) + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19) + create_sprite(0x006c, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x18) + create_sprite(0x006c, EnemySprite.Medusa, 0x00, 0, 0x03, 0x1c) + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x05, 0x06) + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x06) + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x09) + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x0b) + create_sprite(0x006d, EnemySprite.Medusa, 0x00, 0, 0x04, 0x15) + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x15) + create_sprite(0x006d, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x18) + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x18) + create_sprite(0x006d, EnemySprite.SparkCCW, 0x00, 0, 0x06, 0x1a) + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x08) + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x09) + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0a) + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0b) + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0c) + create_sprite(0x0071, EnemySprite.GreenGuard, 0x00, 1, 0x06, 0x18) + create_sprite(0x0071, EnemySprite.BlueGuard, 0x15, 1, 0x1a, 0x18, True, 0xe4) + create_sprite(0x0072, EnemySprite.BlueGuard, 0x05, 0, 0x11, 0x06, True, 0xe4) + create_sprite(0x0072, EnemySprite.BlueGuard, 0x01, 1, 0x0a, 0x19) + create_sprite(0x0073, EnemySprite.Debirando, 0x00, 0, 0x18, 0x18) + create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x17, 0x09) + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x15, 0x15) + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x1b, 0x18) + create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19) + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x16, 0x1b) + create_sprite(0x0073, EnemySprite.BonkItem, 0x00, 0, 0x14, 0x06) + create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x08, 0x18) + create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x17, 0x18) + create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x05) + create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x13, 0x05) + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0c, 0x0a) + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x13, 0x0a) + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0e, 0x1b) + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x12, 0x1b) + create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x08, 0x07) + create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x04, 0x1b) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x05) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x05) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x0a) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x0a) + create_sprite(0x0075, EnemySprite.WallCannonVertLeft, 0x00, 0, 0x11, 0x0b) + create_sprite(0x0075, EnemySprite.WallCannonVertRight, 0x00, 0, 0x1e, 0x0b) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x07, 0x19) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x09, 0x19) + create_sprite(0x0076, EnemySprite.WaterSwitch, 0x00, 0, 0x19, 0x03) + create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a) + create_sprite(0x0076, EnemySprite.Kyameron, 0x00, 0, 0x07, 0x0f) + create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x08, 0x11) + create_sprite(0x0076, EnemySprite.Blob, 0x00, 0, 0x1b, 0x19) + create_sprite(0x0076, 0x13, SpriteType.Overlord, 0, 0x08, 0x1c) + create_sprite(0x0076, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1c) + create_sprite(0x0077, EnemySprite.MiniMoldorm, 0x00, 1, 0x0b, 0x09) + create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x10, 0x18) + create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x09, 0x1a) + create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x16, 0x1a) + create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x07, 0x0a) + create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x17, 0x0a) + create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x07) + create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x09) + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x04, 0x15) + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15) + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x17) + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x17) + create_sprite(0x007b, EnemySprite.Statue, 0x00, 0, 0x13, 0x18) + create_sprite(0x007b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x18) + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x19) + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x1a) + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x007c, EnemySprite.MiniMoldorm, 0x00, 0, 0x19, 0x1c) + create_sprite(0x007c, EnemySprite.FirebarCCW, 0x00, 0, 0x06, 0x0c) + create_sprite(0x007c, EnemySprite.SpikeBlock, 0x00, 0, 0x07, 0x10) + create_sprite(0x007c, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x14) + create_sprite(0x007c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18) + create_sprite(0x007c, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x18) + create_sprite(0x007c, 0x0b, SpriteType.Overlord, 0, 0x07, 0x1a) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x06) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x08) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0a) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0c) + create_sprite(0x007d, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x16) + create_sprite(0x007d, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x17) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x19) + create_sprite(0x007d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x1a) + create_sprite(0x007d, EnemySprite.RedBari, 0x00, 0, 0x17, 0x1a) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x0a, 0x1c) + create_sprite(0x007d, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x1c) + create_sprite(0x007e, EnemySprite.Bumper, 0x00, 0, 0x17, 0x11) + create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x0e) + create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0f) + create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x07, 0x12) + create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x0a, 0x12) + create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x16) + create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x17, 0x17) + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x06, 0x07) + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x08, 0x07) + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x08) + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09) + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x14) + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x17) + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x19) + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x1b) + create_sprite(0x0080, EnemySprite.Zelda, 0x00, 0, 0x16, 0x03) + create_sprite(0x0080, EnemySprite.GreenGuard, 0x00, 0, 0x07, 0x09) + create_sprite(0x0080, EnemySprite.BallNChain, 0x00, 0, 0x1a, 0x09, True, 0xe5) + create_sprite(0x0081, EnemySprite.GreenGuard, 0x1b, 1, 0x0b, 0x0b) + create_sprite(0x0081, EnemySprite.GreenGuard, 0x03, 1, 0x0e, 0x0b) + create_sprite(0x0082, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x05) + create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x10, 0x06) + create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x15, 0x11) + create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x1b, 0x08) + create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x14, 0x10) + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x05) + create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06) + create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08) + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x17, 0x10) + create_sprite(0x0083, EnemySprite.Beamos, 0x00, 0, 0x08, 0x17) + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x18, 0x18) + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x1b) + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x03, 0x05) + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x1b, 0x05) + create_sprite(0x0084, EnemySprite.Beamos, 0x00, 0, 0x0f, 0x07) + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x12) + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x12) + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x1b) + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x1b) + create_sprite(0x0085, EnemySprite.DebirandoPit, 0x00, 0, 0x07, 0x0e) + create_sprite(0x0085, EnemySprite.Debirando, 0x00, 0, 0x09, 0x1b) + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x14, 0x05) + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x1b, 0x05) + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x16, 0x08) + create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a) + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x03, 0x0e) + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x0c, 0x15) + create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x18) + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x07, 0x1c) + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x05) + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x07) + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x13, 0x0b) + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x19) + create_sprite(0x0087, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x17, 0x04) + create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x0c) + create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x15) + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x17) + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x19, 0x18) + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x19) + create_sprite(0x0087, EnemySprite.SmallKey, 0x00, 0, 0x08, 0x1a) + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x1c) + create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x10, 0x0a) + create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x0f, 0x0b) + create_sprite(0x008b, EnemySprite.Bumper, 0x00, 0, 0x15, 0x07) + create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18) + create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18) + create_sprite(0x008b, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x04) + create_sprite(0x008b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x12) + create_sprite(0x008b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x18) + create_sprite(0x008b, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x18) + create_sprite(0x008b, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x18) + create_sprite(0x008c, EnemySprite.WrongPullSwitch, 0x00, 0, 0x1a, 0x03) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x18, 0x05) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x06) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x06) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x0a) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x0a) + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x08, 0x08) + create_sprite(0x008c, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08) + create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x09) + create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0b) + create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x17) + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x17) + create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x18) + create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x1c) + create_sprite(0x008c, EnemySprite.BonkItem, 0x00, 0, 0x09, 0x07) + create_sprite(0x008d, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x04) + create_sprite(0x008d, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08) + create_sprite(0x008d, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x09) + create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x0c) + create_sprite(0x008d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0d) + create_sprite(0x008d, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x008d, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x10) + create_sprite(0x008d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x14) + create_sprite(0x008d, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x18) + create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b) + create_sprite(0x008d, EnemySprite.Medusa, 0x00, 0, 0x13, 0x1c) + create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1c) + create_sprite(0x008e, EnemySprite.Freezor, 0x00, 0, 0x1b, 0x02) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x05) + create_sprite(0x008e, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x06) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x1b, 0x08) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x09) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x16, 0x0a) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x0b) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x0b) + create_sprite(0x0090, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x15) + create_sprite(0x0091, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x04) + create_sprite(0x0091, EnemySprite.SpikeBlock, 0x00, 0, 0x1b, 0x0e) + create_sprite(0x0091, 0x08, SpriteType.Overlord, 0, 0x17, 0x0f) + create_sprite(0x0091, EnemySprite.Medusa, 0x00, 0, 0x17, 0x12) + create_sprite(0x0091, EnemySprite.BunnyBeam, 0x00, 0, 0x18, 0x12) + create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x19, 0x12) + create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x18) + create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x09) + create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x0c) + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x04) + create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x05) + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08) + create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x17, 0x09) + create_sprite(0x0092, EnemySprite.FourWayShooter, 0x00, 0, 0x15, 0x0f) + create_sprite(0x0092, 0x16, SpriteType.Overlord, 0, 0x07, 0x12) + create_sprite(0x0092, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x12) + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x14) + create_sprite(0x0092, EnemySprite.Stalfos, 0x00, 0, 0x0a, 0x16) + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x1b) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x09, 0x09) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x16, 0x09) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0c) + create_sprite(0x0093, EnemySprite.Blob, 0x00, 0, 0x17, 0x0c) + create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x15) + create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x1c) + create_sprite(0x0093, EnemySprite.AntiFairy, 0x00, 0, 0x04, 0x1c) + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x16, 0x0c) + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x17, 0x0c) + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x18, 0x0c) + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x19, 0x0c) + create_sprite(0x0095, 0x0b, SpriteType.Overlord, 0, 0x17, 0x1a) + create_sprite(0x0096, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x0b) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x15) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x17) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x19) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x1b) + create_sprite(0x0097, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x10, 0x13) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x09, 0x14) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0c, 0x14) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0f, 0x14) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x08, 0x17) + create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x06) + create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x08) + create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0e, 0x17) + create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x11, 0x17, True, 0xe4) + create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x0d, 0x18) + create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x12, 0x18) + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0e, 0x19) + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x19) + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x10, 0x19) + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x11, 0x19) + create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x08) + create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x08) + create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x14, 0x08) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x05) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x06) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x07) + create_sprite(0x009b, EnemySprite.FourWayShooter, 0x00, 0, 0x03, 0x08) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x08) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x09) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0a) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0b) + create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x1a) + create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x1b) + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x09) + create_sprite(0x009c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0b, 0x0a) + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x11, 0x0f) + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x0e) + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x12) + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13) + create_sprite(0x009c, EnemySprite.Firesnake, 0x00, 0, 0x0f, 0x1c) + create_sprite(0x009d, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x06) + create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x06, 0x04) + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x04) + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x18, 0x09) + create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c) + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0c) + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x10, 0x14) + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18) + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x1c) + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05) + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x08) + create_sprite(0x009e, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x08) + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x08) + create_sprite(0x009e, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x04, 0x12) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x06, 0x12) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x09, 0x12) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x0b, 0x12) + create_sprite(0x009f, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x17) + create_sprite(0x009f, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18) + create_sprite(0x00a0, EnemySprite.Medusa, 0x00, 0, 0x03, 0x08) + create_sprite(0x00a0, EnemySprite.AntiFairy, 0x00, 0, 0x0e, 0x08) + create_sprite(0x00a0, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x0c) + create_sprite(0x00a1, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x08) + create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x18, 0x07) + create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x0b) + create_sprite(0x00a1, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x10) + create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x15) + create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x15) + create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x19) + create_sprite(0x00a1, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x19) + create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x19) + create_sprite(0x00a4, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x15) + create_sprite(0x00a4, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x15) + create_sprite(0x00a4, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x15) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x16, 0x05) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x05) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x0b, 0x07) + create_sprite(0x00a5, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x15, 0x09) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x1a, 0x09) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x0a) + create_sprite(0x00a5, EnemySprite.LaserEyeTop, 0x00, 0, 0x0c, 0x12) + create_sprite(0x00a5, EnemySprite.LaserEyeTop, 0x00, 0, 0x12, 0x12) + create_sprite(0x00a5, EnemySprite.RedSpearGuard, 0x00, 0, 0x12, 0x17) + create_sprite(0x00a5, EnemySprite.BlueGuard, 0x00, 0, 0x13, 0x18) + create_sprite(0x00a6, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x00a6, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0e) + create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08) + create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x09) + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x0e) + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x0e) + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x12) + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x12) + create_sprite(0x00a8, 0x18, SpriteType.Overlord, 0, 0x08, 0x16) + create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x09, 0x05) + create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x05) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x0d, 0x0c) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x12, 0x0c) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x0d, 0x12) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x12, 0x12) + create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x0a, 0x10) + create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x14, 0x10) + create_sprite(0x00aa, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06) + create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x07) + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b) + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x13) + create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x14) + create_sprite(0x00ab, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x15) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x16) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x17) + create_sprite(0x00ab, EnemySprite.Blob, 0x00, 0, 0x06, 0x18) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x19) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x1b) + create_sprite(0x00ac, EnemySprite.Blind, 0x00, 0, 0x19, 0x15) + create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x07) + create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x07) + create_sprite(0x00af, EnemySprite.FirebarCW, 0x00, 0, 0x0a, 0x08) + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x07, 0x07) + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07) + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x18, 0x07) + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x14, 0x08) + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x1b, 0x08) + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x05, 0x0b) + create_sprite(0x00b0, EnemySprite.BallNChain, 0x00, 0, 0x16, 0x14) + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x04, 0x16) + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x16) + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x0a, 0x16) + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x08, 0x18, True, 0xe4) + create_sprite(0x00b0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x1a) + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x17, 0x1c) + create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x07) + create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x07) + create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x0e) + create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x11) + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x0c, 0x17) + create_sprite(0x00b1, EnemySprite.BigSpike, 0x00, 0, 0x1a, 0x17) + create_sprite(0x00b1, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x18) + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x03, 0x1a) + create_sprite(0x00b1, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x1a) + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x1c) + create_sprite(0x00b2, EnemySprite.Wizzrobe, 0x00, 1, 0x14, 0x08) + create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x0a) + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x12, 0x0a) + create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x13, 0x0a) + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x07, 0x0b) + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x15) + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x15) + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x16) + create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x15, 0x18) + create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x18) + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x1b) + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x14, 0x1b) + create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x1b, 0x1b) + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x15) + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15) + create_sprite(0x00b3, EnemySprite.Beamos, 0x00, 0, 0x06, 0x18) + create_sprite(0x00b3, EnemySprite.FourWayShooter, 0x00, 0, 0x0a, 0x1a) + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x1c) + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x0a) + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x0f) + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x16) + create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x06, 0x07) + create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07) + create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x04) + create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x0c, 0x04) + create_sprite(0x00b6, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) + create_sprite(0x00b6, EnemySprite.Pokey, 0x00, 0, 0x07, 0x15, True, 0xe4) + create_sprite(0x00b6, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x07, 0x1b) + create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x08, 0x1b) + create_sprite(0x00b7, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x04, 0x09) + create_sprite(0x00b7, EnemySprite.RollerVerticalUp, 0x00, 0, 0x04, 0x11) + create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x15, 0x0b) + create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x00b8, EnemySprite.AntiFairyCircle, 0x00, 0, 0x18, 0x0d) + create_sprite(0x00b8, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x13) + create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x16) + create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x1c, 0x16) + create_sprite(0x00b9, 0x03, SpriteType.Overlord, 1, 0x11, 0x05) + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x04) + create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x06) + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x06) + create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x09) + create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x0c, 0x09) + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x0a) + create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x08, 0x0c) + create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x1b, 0x04) + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x06, 0x0a) + create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x16, 0x0a) + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x0a) + create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x0c) + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x09, 0x0e) + create_sprite(0x00bb, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x10) + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x08, 0x14) + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x15) + create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x16) + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x17, 0x1a) + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x06, 0x05) + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x05) + create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x06) + create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x09) + create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x09, 0x0a) + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x05, 0x0b) + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x0a) + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x11) + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x16) + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x17) + create_sprite(0x00bc, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x18) + create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x08, 0x19) + create_sprite(0x00be, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x08) + create_sprite(0x00be, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12) + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x15) + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x15) + create_sprite(0x00be, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x16) + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1a) + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1a) + create_sprite(0x00bf, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18) + create_sprite(0x00bf, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x15) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x17, 0x05) + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x1a, 0x07) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x0b, 0x09) + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x0b, True, 0xe4) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x06, 0x0e) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x04, 0x18) + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x1b) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x1b, 0x1b) + create_sprite(0x00c1, EnemySprite.CrystalSwitch, 0x00, 0, 0x15, 0x17) + create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x14, 0x05) + create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x05) + create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b) + create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x0b) + create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x17, 0x15) + create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x09, 0x16) + create_sprite(0x00c1, 0x14, SpriteType.Overlord, 0, 0x07, 0x18) + create_sprite(0x00c1, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x19) + create_sprite(0x00c1, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x1a) + create_sprite(0x00c1, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x1b, True, 0xe4) + create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x1b, 0x1b) + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x15, 0x0b) + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x0c) + create_sprite(0x00c2, EnemySprite.Medusa, 0x00, 0, 0x08, 0x10) + create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x10, 0x12) + create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x19, 0x12) + create_sprite(0x00c2, EnemySprite.BunnyBeam, 0x00, 1, 0x10, 0x14) + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x16) + create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x16) + create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x05, 0x06) + create_sprite(0x00c3, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x09) + create_sprite(0x00c3, EnemySprite.LaserEyeLeft, 0x00, 0, 0x11, 0x0d) + create_sprite(0x00c3, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x11) + create_sprite(0x00c3, EnemySprite.LaserEyeLeft, 0x00, 0, 0x11, 0x15) + create_sprite(0x00c3, 0x0b, SpriteType.Overlord, 0, 0x17, 0x1a) + create_sprite(0x00c3, EnemySprite.AntiFairy, 0x00, 0, 0x0a, 0x1b) + create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x07, 0x1c) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x0a) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x0f) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x1b) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x0f, 0x15) + create_sprite(0x00c4, EnemySprite.Pokey, 0x00, 0, 0x0f, 0x0e) + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x0f) + create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x14) + create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x14) + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x1a) + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x1a) + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x09) + create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0b) + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x0d) + create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0f) + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x11) + create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x13) + create_sprite(0x00c5, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x15) + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x15) + create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x04) + create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x04) + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x09) + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x09) + create_sprite(0x00c6, EnemySprite.FloatingSkull, 0x00, 0, 0x10, 0x0e) + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x14) + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x17) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x15) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x15) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x15) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x18) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x18) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x18) + create_sprite(0x00c8, 0x19, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x05) + create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x06) + create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x07) + create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x04) + create_sprite(0x00cb, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x09) + create_sprite(0x00cb, EnemySprite.BlueZazak, 0x00, 1, 0x10, 0x0a) + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x13, 0x0a) + create_sprite(0x00cb, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x0a) + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x1c, 0x0a) + create_sprite(0x00cb, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x10) + create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x18, 0x15) + create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x08, 0x17) + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0b, 0x17) + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0c, 0x18) + create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x1c) + create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x04) + create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x0b, 0x09) + create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x08, 0x0a) + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a) + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x0c, 0x0b) + create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x10, 0x0c) + create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x18, 0x0c) + create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 1, 0x0e, 0x14) + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x15) + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x06, 0x16) + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x09, 0x16) + create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x09, 0x18) + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x16) + create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x07, 0x1c) + create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05) + create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05) + create_sprite(0x00ce, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x1c, 0x05) + create_sprite(0x00ce, EnemySprite.Statue, 0x00, 0, 0x14, 0x09) + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x08) + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x08) + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x09) + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x09) + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x05) + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x07) + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07) + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x15, 0x0b) + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x09, 0x0c) + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x08, 0x0f) + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x03, 0, 0x03, 0x10) + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x14) + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x16) + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x06, 0x19) + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x19) + create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x14, 0x06) + create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x06) + create_sprite(0x00d1, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07) + create_sprite(0x00d1, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x08) + create_sprite(0x00d1, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x09) + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x0b) + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x0b) + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x18, 0x06) + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1a, 0x07) + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x08) + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x1c, 0x08) + create_sprite(0x00d2, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a) + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x16, 0x0c) + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x13, 0x0d) + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x10) + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x14, 0x14) + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1c, 0x14) + create_sprite(0x00d5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x09) + create_sprite(0x00d5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0d) + create_sprite(0x00d5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x11) + create_sprite(0x00d5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x15) + create_sprite(0x00d5, EnemySprite.HardhatBeetle, 0x00, 0, 0x04, 0x15) + create_sprite(0x00d6, EnemySprite.LaserEyeTop, 0x00, 0, 0x07, 0x02) + create_sprite(0x00d6, EnemySprite.Medusa, 0x00, 0, 0x03, 0x16) + create_sprite(0x00d6, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x16) + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x05) + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x05) + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x17, 0x09) + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x18, 0x09) + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x16, 0x0a) + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x19, 0x0a) + create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x16, 0x0b) + create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x19, 0x0b) + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x14) + create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x16) + create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x1b) + create_sprite(0x00d9, 0x02, SpriteType.Overlord, 0, 0x0c, 0x14) + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x15) + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x18) + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x1b) + create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x18) + create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x18) + create_sprite(0x00db, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x04) + create_sprite(0x00db, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a) + create_sprite(0x00db, EnemySprite.RedZazak, 0x00, 1, 0x17, 0x0b) + create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x0c) + create_sprite(0x00db, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10) + create_sprite(0x00db, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x10) + create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x15) + create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x0a) + create_sprite(0x00dc, EnemySprite.SparkCCW, 0x00, 1, 0x0e, 0x0a) + create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x0c) + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10) + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x16, 0x10) + create_sprite(0x00dc, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x16) + create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x16) + create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x17) + create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 1, 0x16, 0x17) + create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x1c) + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0f, 0x1c) + create_sprite(0x00de, EnemySprite.KholdstareShell, 0x00, 0, 0x17, 0x05) + create_sprite(0x00de, EnemySprite.FallingIce, 0x00, 0, 0x17, 0x05) + create_sprite(0x00de, EnemySprite.Kholdstare, 0x00, 0, 0x17, 0x05) + create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x15) + create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x16) + create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x04, 0x06) + create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x0b, 0x06) + create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x06) + create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x09) + create_sprite(0x00e1, EnemySprite.HeartPiece, 0x00, 0, 0x17, 0x0d) + create_sprite(0x00e1, EnemySprite.AdultNpc, 0x00, 1, 0x07, 0x12) + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06) + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x06) + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07) + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) + create_sprite(0x00e2, EnemySprite.HeartPiece, 0x00, 0, 0x13, 0x10) + create_sprite(0x00e3, EnemySprite.MagicBat, 0x00, 1, 0x17, 0x05) + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x19, 0x07) + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x18, 0x08) + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x17, 0x09) + create_sprite(0x00e4, EnemySprite.OldMan, 0x00, 0, 0x06, 0x16) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x09) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x10, 0x09) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x09) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0e) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x12) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x12) + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x17, 0x0f) + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x13, 0x13) + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0f, 0x17) + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x10, 0x04) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x13, 0x04) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0b) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0c) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0d) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0f) + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x05) + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x08) + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c) + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0c) + create_sprite(0x00ea, EnemySprite.HeartPiece, 0x00, 0, 0x0b, 0x0b) + create_sprite(0x00eb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x14) + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x10, 0x04) + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x0e) + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x1c) + create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x0b) + create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x0c) + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09) + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x0a) + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x0a) + create_sprite(0x00ef, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x06) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x03) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x10, 0x03) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x08, 0x04) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0a, 0x04) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x07) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x03, 0x0a) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x05, 0x0a) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0e, 0x0c) + create_sprite(0x00f0, EnemySprite.OldMan, 0x00, 0, 0x1b, 0x10) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x13, 0x13) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x19, 0x10) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1c, 0x10) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x18, 0x11) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1d, 0x11) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x17, 0x12) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1e, 0x12) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x06, 0x1b) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x09, 0x1b) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x07, 0x1c) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x08, 0x1c) + create_sprite(0x00f3, EnemySprite.Grandma, 0x00, 0, 0x06, 0x14) + create_sprite(0x00f4, EnemySprite.ArgueBros, 0x00, 0, 0x17, 0x14) + create_sprite(0x00f5, EnemySprite.ArgueBros, 0x00, 0, 0x08, 0x14) + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x05) + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x15, 0x0f) + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x11, 0x13) + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x17) + 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) + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12) + create_sprite(0x00fd, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0e) + create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x08) + create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x16, 0x08) + create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) + create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x11) + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x16, 0x12) + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x16) + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x16) + create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x12) + create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x18) + create_sprite(0x00ff, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x04) + create_sprite(0x0100, EnemySprite.Shopkeeper, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x0101, EnemySprite.RupeePull, 0x00, 0, 0x08, 0x13) + create_sprite(0x0102, EnemySprite.SickKid, 0x00, 0, 0x03, 0x18) + create_sprite(0x0103, EnemySprite.Drunkard, 0x00, 0, 0x06, 0x15) + create_sprite(0x0103, EnemySprite.AdultNpc, 0x00, 0, 0x0a, 0x1b) + create_sprite(0x0103, EnemySprite.Innkeeper, 0x00, 0, 0x17, 0x17) + create_sprite(0x0104, EnemySprite.UnclePriest, 0x00, 0, 0x1a, 0x17) + create_sprite(0x0105, EnemySprite.Wiseman, 0x00, 0, 0x07, 0x18) + create_sprite(0x0106, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x1b) + create_sprite(0x0107, EnemySprite.BonkItem, 0x00, 0, 0x03, 0x15) + create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x1b) + create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x18, 0x1b) + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x16) + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x0c, 0x16) + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x19) + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x06, 0x1a) + create_sprite(0x0109, EnemySprite.MagicShopAssistant, 0x00, 0, 0x0a, 0x1b) + create_sprite(0x010a, EnemySprite.Wiseman, 0x00, 0, 0x19, 0x04) + create_sprite(0x010b, EnemySprite.WrongPullSwitch, 0x00, 0, 0x0f, 0x03) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x0d, 0x06) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x10, 0x06) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x12, 0x07) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x0f, 0x09) + create_sprite(0x010b, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x12, 0x03) + create_sprite(0x010b, EnemySprite.AntiFairy, 0x00, 0, 0x0d, 0x07) + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07) + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08) + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x07, 0x14) + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x08, 0x14) + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x14) + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1a) + create_sprite(0x010d, EnemySprite.SparkCW, 0x00, 0, 0x05, 0x16) + create_sprite(0x010d, EnemySprite.SparkCCW, 0x00, 0, 0x0a, 0x16) + create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x06, 0x06) + create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x18, 0x06) + create_sprite(0x010f, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x15) + create_sprite(0x0110, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x15) + create_sprite(0x0111, EnemySprite.ArcheryNpc, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x0112, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x07, 0x0a) + create_sprite(0x0112, EnemySprite.Shopkeeper, 0x00, 0, 0x17, 0x14) + create_sprite(0x0114, EnemySprite.FairyPondTrigger, 0x00, 0, 0x07, 0x18) + create_sprite(0x0114, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x19, 0x14) + create_sprite(0x0115, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x16) + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) + 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(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) + create_sprite(0x011a, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x18, 0x17) + create_sprite(0x011b, EnemySprite.HeartPiece, 0x00, 0, 0x18, 0x09) + create_sprite(0x011b, EnemySprite.HeartPiece, 0x00, 1, 0x05, 0x16) + create_sprite(0x011c, EnemySprite.BombShopGuy, 0x00, 0, 0x09, 0x19) + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x07) + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x07) + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x08) + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08) + create_sprite(0x011e, EnemySprite.Shopkeeper, 0x00, 0, 0x18, 0x16) + create_sprite(0x011f, EnemySprite.Shopkeeper, 0x00, 0, 0x17, 0x16) + create_sprite(0x0120, EnemySprite.GoodBee, 0x00, 0, 0x17, 0x07) + create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1b, 0x08) + create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x09) + create_sprite(0x0121, EnemySprite.Smithy, 0x00, 0, 0x04, 0x17) + create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x07, 0x18) + create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x17, 0x18) + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x16) + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x16) + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x17) + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x1a) + create_sprite(0x0123, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x05) + create_sprite(0x0124, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) + create_sprite(0x0125, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x15) + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x15) + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x16) + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16) + create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) + create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) + + +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), + # too hard to hit red biris with arrows + EnemySprite.RedBari: defeat_rule(world, player, h(EnemySprite.RedBari) * 3, arrow=None, ice=2, hook=True), + 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.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.Kondongo: defeat_rule(world, player, h(EnemySprite.Kondongo), 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 + + +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) + + +# 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[fire], 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.retro[player]: + # 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.bombag[player]), has('Bomb Upgrade (+10)', player)) + diff --git a/source/logic/Rule.py b/source/logic/Rule.py new file mode 100644 index 00000000..8529ce2b --- /dev/null +++ b/source/logic/Rule.py @@ -0,0 +1,568 @@ +import itertools + +from collections import OrderedDict +try: + from fast_enum import FastEnum +except ImportError: + from enum import IntFlag as FastEnum + +from BaseClasses import CrystalBarrier, KeyRuleType +from Dungeons import dungeon_keys + + +class RuleType(FastEnum): + Conjunction = 0 + Disjunction = 1 + Item = 2 + Glitch = 3 + Reachability = 4 + Static = 5 + Bottle = 6 + Crystal = 7 + Barrier = 8 + Hearts = 9 + Unlimited = 10 + ExtendMagic = 11 + Boss = 12 + Negate = 13 + LocationCheck = 14 + SmallKeyDoor = 15 + + +class Rule(object): + + def __init__(self, rule_type): + self.rule_type = rule_type + self.sub_rules = [] + self.principal = None + self.player = 0 + self.resolution_hint = None + self.barrier = None + self.flag = None + self.locations = [] + self.count = 1 + + self.std_req = None + + self.rule_lambda = lambda state: True + + def eval(self, state): + return self.rule_lambda(state) + + def get_requirements(self, progressive_flag=True): + if not self.std_req: + reqs = rule_requirements[self.rule_type](self, progressive_flag) + self.std_req = standardize_requirements(reqs, progressive_flag) + return self.std_req + + def __str__(self): + return str(self.__unicode__()) + + def __unicode__(self): + return rule_prints[self.rule_type](self) + + +rule_prints = { + RuleType.Conjunction: lambda self: f'({" and ".join([str(x) for x in self.sub_rules])})', + RuleType.Disjunction: lambda self: f'({" or ".join([str(x) for x in self.sub_rules])})', + RuleType.Item: lambda self: f'has {self.principal}' if self.count == 1 else f'has {self.count} {self.principal}(s)', + RuleType.Reachability: lambda self: f'canReach {self.principal}', + RuleType.Static: lambda self: f'{self.principal}', + RuleType.Crystal: lambda self: f'has {self.principal} crystals', + RuleType.Barrier: lambda self: f'{self.barrier} @ {self.principal}', + RuleType.Hearts: lambda self: f'has {self.principal} hearts', + RuleType.Unlimited: lambda self: f'canBuyUnlimited {self.principal}', + RuleType.ExtendMagic: lambda self: f'magicNeeded {self.principal}', + RuleType.Boss: lambda self: f'canDefeat({self.principal.defeat_rule})', + RuleType.Negate: lambda self: f'not ({self.sub_rules[0]})', + RuleType.LocationCheck: lambda self: f'{self.principal} in [{", ".join(self.locations)}]', + RuleType.SmallKeyDoor: lambda self: f'doorOpen {self.principal[0]}:{self.principal[1]}' +} + + +def or_rule(rule1, rule2): + return lambda state: rule1(state) or rule2(state) + + +def and_rule(rule1, rule2): + return lambda state: rule1(state) and rule2(state) + + +class RuleFactory(object): + + @staticmethod + def static_rule(boolean): + rule = Rule(RuleType.Static) + rule.principal = boolean + rule.rule_lambda = lambda state: boolean + return rule + + @staticmethod + def conj(rules): + if len(rules) == 1: + return rules[0] + rule = Rule(RuleType.Conjunction) + rule_lambda = None + for r in rules: + 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 + continue + else: + rule.sub_rules.append(r) + if not rule_lambda: + rule_lambda = r.rule_lambda + else: + rule_lambda = and_rule(rule_lambda, r.rule_lambda) + rule.rule_lambda = rule_lambda + return rule + + @staticmethod + def disj(rules): + if len(rules) == 1: + return rules[0] + rule = Rule(RuleType.Disjunction) + rule_lambda = None + for r in rules: + 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 + continue + else: + rule.sub_rules.append(r) + if not rule_lambda: + rule_lambda = r.rule_lambda + else: + rule_lambda = or_rule(rule_lambda, r.rule_lambda) + rule.rule_lambda = rule_lambda + return rule + + @staticmethod + def item(item, player, count=1): + rule = Rule(RuleType.Item) + rule.principal = item + rule.player = player + rule.count = count + rule.rule_lambda = lambda state: state.has(item, player, count) + return rule + + @staticmethod + def bottle(player): + rule = Rule(RuleType.Bottle) + rule.player = player + rule.rule_lambda = lambda state: state.has_bottle(player) + return rule + + @staticmethod + def crystals(number, player): + rule = Rule(RuleType.Crystal) + rule.principal = number + rule.player = player + rule.rule_lambda = lambda state: state.has_crystals(number, player) + return rule + + @staticmethod + def barrier(region, player, barrier): + rule = Rule(RuleType.Barrier) + rule.principal = region + rule.player = player + rule.barrier = barrier + rule.rule_lambda = lambda state: state.can_cross_barrier(region, player, barrier) + return rule + + @staticmethod + def hearts(number, player): + rule = Rule(RuleType.Hearts) + rule.principal = number + rule.player = player + rule.rule_lambda = lambda state: state.has_hearts(number, player) + return rule + + @staticmethod + def unlimited(item, player, shop_regions): + rule = Rule(RuleType.Unlimited) + rule.principal = item + rule.player = player + rule.locations = shop_regions # list of regions where said item can be bought + rule.rule_lambda = lambda state: state.can_buy_unlimited(item, player) + return rule + + @staticmethod + def extend_magic(player, magic, difficulty, magic_potion_regions, flag): + rule = Rule(RuleType.ExtendMagic) + rule.principal = magic + rule.player = player + rule.resolution_hint = difficulty # world difficulty setting + rule.locations = magic_potion_regions # list of regions where blue/green can be bought + rule.flag = flag + rule.rule_lambda = lambda state: state.can_extend_magic(player, magic, flag) + return rule + + @staticmethod + def boss(boss): + rule = Rule(RuleType.Boss) + rule.principal = boss + rule.rule_lambda = lambda state: boss.defeat_rule.eval(state) + return rule + + @staticmethod + def neg(orig): + rule = Rule(RuleType.Negate) + rule.sub_rules.append(orig) + rule.rule_lambda = lambda state: not orig.rule_lambda(state) + return rule + + @staticmethod + def check_location(item, location, player): + rule = Rule(RuleType.LocationCheck) + rule.principal = item + rule.location = location + rule.player = player + rule.rule_lambda = eval_location(item, location, player) + return rule + + @staticmethod + def small_key_door(door_name, dungeon, player, door_rules): + rule = Rule(RuleType.SmallKeyDoor) + rule.principal = (door_name, dungeon) + rule.player = player + rule.resolution_hint = door_rules # door_rule object from KeyDoorShuffle + rule.rule_lambda = eval_small_key_door(door_name, dungeon, player) + return rule + + +def eval_location(item, location, player): + return lambda state: eval_location_main(item, location, player, state) + + +def eval_location_main(item, location, player, state): + location = state.world.get_location(location, player) + return location.item and location.item.name == item and location.player == player + + +def eval_small_key_door_main(state, door_name, dungeon, player): + if state.is_door_open(door_name, player): + return True + key_logic = state.world.key_logic[player][dungeon] + door_rule = key_logic.door_rules[door_name] + door_openable = False + for ruleType, number in door_rule.new_rules.items(): + if door_openable: + return True + if ruleType == KeyRuleType.WorstCase: + door_openable |= state.has_sm_key(key_logic.small_key_name, player, number) + elif ruleType == KeyRuleType.AllowSmall: + if (door_rule.small_location.item and door_rule.small_location.item.name == key_logic.small_key_name + and door_rule.small_location.item.player == player): + return True # always okay if allow small is on + elif isinstance(ruleType, tuple): + lock, lock_item = ruleType + # this doesn't track logical locks yet, i.e. hammer locks the item and hammer is there, but the item isn't + for loc in door_rule.alternate_big_key_loc: + spot = state.world.get_location(loc, player) + if spot.item and spot.item.name == lock_item: + door_openable |= state.has_sm_key(key_logic.small_key_name, player, number) + break + return door_openable + + +def eval_small_key_door(door_name, dungeon, player): + return lambda state: eval_small_key_door_main(state, door_name, dungeon, player) + + +def conjunction_requirements(rule, f): + combined = [ReqSet()] + for r in rule.sub_rules: + result = r.get_requirements(f) + combined = merge_requirements(combined, result) + return combined + + +def disjunction_requirements(rule, f): + results = [] + for r in rule.sub_rules: + result = r.get_requirements(f) + results.extend(result) + return results + + +rule_requirements = { + RuleType.Conjunction: conjunction_requirements, + RuleType.Disjunction: disjunction_requirements, + RuleType.Item: lambda rule, f: [ReqSet([Requirement(ReqType.Item, rule.principal, rule.player, rule, rule.count)])], + RuleType.Reachability: lambda rule, f: [ReqSet([Requirement(ReqType.Reachable, rule.principal, rule.player, rule)])], + RuleType.Static: lambda rule, f: static_req(rule), + RuleType.Crystal: lambda rule, f: crystal_requirements(rule), + RuleType.Bottle: lambda rule, f: [ReqSet([Requirement(ReqType.Item, 'Bottle', rule.player, rule, 1)])], + RuleType.Barrier: lambda rule, f: barrier_req(rule), + RuleType.Hearts: lambda rule, f: empty_req(), # todo: the one heart container + RuleType.Unlimited: lambda rule, f: unlimited_buys(rule), + RuleType.ExtendMagic: lambda rule, f: magic_requirements(rule), + RuleType.Boss: lambda rule, f: rule.principal.defeat_rule.get_requirements(f), + RuleType.Negate: lambda rule, f: empty_req(), # ignore these and just don't flood the key too early + RuleType.LocationCheck: lambda rule, f: location_check(rule), + RuleType.SmallKeyDoor: lambda rule, f: small_key_reqs(rule) +} + + +avail_crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'] + + +def crystal_requirements(rule): + crystal_rules = map(lambda c: Requirement(ReqType.Item, c, rule.player, rule), avail_crystals) + combinations = itertools.combinations(crystal_rules, rule.principal) + counter_list = [] + for combo in combinations: + counter_list.append(ReqSet(combo)) + return counter_list + + +# todo: 1/4 magic +def magic_requirements(rule): + if rule.principal <= 8: + return [set()] + bottle_val = 1.0 + if rule.resolution_hint == 'expert' and not rule.flag: + bottle_val = 0.25 + elif rule.resolution_hint == 'hard' and not rule.flag: + bottle_val = 0.5 + base, min_bot, reqs = 8, None, [] + for i in range(1, 5): + if base + bottle_val*base*i >= rule.principal: + min_bot = i + break + if min_bot: + for region in rule.locations: + reqs.append(ReqSet([Requirement(ReqType.Item, 'Bottle', rule.player, rule, min_bot), + Requirement(ReqType.Reachable, region, rule.player, rule)])) + if rule.principal <= 16: + reqs.append(ReqSet([Requirement(ReqType.Item, 'Magic Upgrade (1/2)', rule.player, rule, 1)])) + return reqs + else: + base, min_bot = 16, 4 + for i in range(1, 5): + if base + bottle_val*base*i >= rule.principal: + min_bot = i + break + if min_bot: + for region in rule.locations: + reqs.append(ReqSet([Requirement(ReqType.Item, 'Magic Upgrade (1/2)', rule.player, rule, 1), + Requirement(ReqType.Item, 'Bottle', rule.player, rule, min_bot), + Requirement(ReqType.Reachable, region, rule.player, rule)])) + return reqs + + +def static_req(rule): + return [ReqSet()] if rule.principal else [ReqSet([Requirement(ReqType.Item, 'Impossible', rule.player, rule)])] + + +def barrier_req(rule): + return [ReqSet([Requirement(ReqType.Reachable, rule.principal, rule.player, rule, crystal=rule.barrier)])] + + +def empty_req(): + return [ReqSet()] + + +def location_check(rule): + return [ReqSet([Requirement(ReqType.Placement, rule.principal, rule.player, rule, locations=rule.locations)])] + + +def unlimited_buys(rule): + requirements = [] + for region in rule.locations: + requirements.append(ReqSet([Requirement(ReqType.Reachable, region, rule.player, rule)])) + return requirements + + +def small_key_reqs(rule): + requirements = [] + door_name, dungeon = rule.principal + key_name = dungeon_keys[dungeon] + for rule_type, number in rule.resolution_hint.new_rules.items(): + if rule_type == KeyRuleType.WorstCase: + requirements.append(ReqSet([Requirement(ReqType.Item, key_name, rule.player, rule, number)])) + elif rule_type == KeyRuleType.AllowSmall: + small_loc = rule.resolution_hint.small_location.name + requirements.append(ReqSet([ + Requirement(ReqType.Placement, key_name, rule.player, rule, locations=[small_loc]), + Requirement(ReqType.Item, key_name, rule.player, rule, number)])) + elif isinstance(rule_type, tuple): + lock, lock_item = rule_type + locs = [x.name for x in rule.resolution_hint.alternate_big_key_loc] + requirements.append(ReqSet([ + Requirement(ReqType.Placement, lock_item, rule.player, rule, locations=locs), + Requirement(ReqType.Item, key_name, rule.player, rule, number)])) + return requirements + + +class ReqType(FastEnum): + Item = 0 + Placement = 2 + + +class ReqSet(object): + + def __init__(self, requirements=None): + if requirements is None: + requirements = [] + self.keyed = OrderedDict() + for r in requirements: + self.keyed[r.simple_key()] = r + + def append(self, req): + self.keyed[req.simple_key()] = req + + def get_values(self): + return self.keyed.values() + + def merge(self, other): + new_set = ReqSet(self.get_values()) + for r in other.get_values(): + key = r.simple_key() + if key in new_set.keyed: + new_set.keyed[key] = max(r, new_set.keyed[key], key=lambda r: r.amount) + else: + new_set.keyed[key] = r + return new_set + + def redundant(self, other): + for k, req in other.keyed.items(): + if k not in self.keyed: + return False + elif self.keyed[k].amount < req.amount: + return False + return True + + def different(self, other): + for key in self.keyed.keys(): + if key not in other.keyed: + return True + if key in other.keyed and self.keyed[key].amount > other.keyed[key].amount: + return True + return False + + def find_item(self, item_name): + for key, req in self.keyed.items(): + if req.req_type == ReqType.Item and req.item == item_name: + return req + return None + + def __eq__(self, other): + for key, req in self.keyed.items(): + if key not in other.keyed: + return False + if req.amount != other.keyed[key].amount: + return False + for key in other.keyed: + if key not in self.keyed: + return False + return True + + def __str__(self): + return str(self.__unicode__()) + + def __unicode__(self): + return " and ".join([str(x) for x in self.keyed.values()]) + + +class Requirement(object): + + def __init__(self, req_type, item, player, rule, amount=1, crystal=CrystalBarrier.Null, locations=()): + self.req_type = req_type + self.item = item + self.player = player + self.rule = rule + self.amount = amount + self.crystal = crystal + self.locations = tuple(locations) + + def simple_key(self): + return self.req_type, self.item, self.player, self.crystal, self.locations + + def key(self): + return self.req_type, self.item, self.player, self.amount, self.crystal, self.locations + + def __eq__(self, other): + if isinstance(other, Requirement): + return self.key() == other.key() + return NotImplemented + + def __hash__(self): + return hash(self.key()) + + def __str__(self): + return str(self.__unicode__()) + + def __unicode__(self): + if self.req_type == ReqType.Item: + return f'has {self.item}' if self.amount == 1 else f'has {self.amount} {self.item}(s)' + elif self.req_type == ReqType.Placement: + return f'{self.item} located @ {",".join(self.locations)}' + + +# requirement utility methods +def merge_requirements(starting_requirements, new_requirements): + merge = [] + for req in starting_requirements: + for new_r in new_requirements: + merge.append(req.merge(new_r)) + return reduce_requirements(merge) + + +only_one = {'Moon Pearl', 'Hammer', 'Blue Boomerang', 'Red Boomerang', 'Hookshot', 'Mushroom', 'Powder', + 'Fire Rod', 'Ice Rod', 'Bombos', 'Ether', 'Quake', 'Lamp', 'Shovel', 'Ocarina', 'Bug Catching Net', + 'Book of Mudora', 'Magic Mirror', 'Cape', 'Cane of Somaria', 'Cane of Byrna', 'Flippers', 'Pegasus Boots'} + + +def standardize_requirements(requirements, progressive_flag): + assert isinstance(requirements, list) + for req in requirements: + for thing in req.get_values(): + if thing.item in only_one and thing.amount > 1: + thing.amount = 1 + if progressive_flag: + substitute_progressive(req) + return reduce_requirements(requirements) + + +def reduce_requirements(requirements): + removals = [] + reduced = list(requirements) + # subset manip + ttl = len(reduced) + for i in range(0, ttl - 1): + for j in range(i + 1, ttl): + req, other_req = reduced[i], reduced[j] + if req.redundant(other_req): + removals.append(req) + elif other_req.redundant(req): + removals.append(other_req) + for removal in removals: + if removal in reduced: + reduced.remove(removal) + assert len(reduced) != 0 + return reduced + + +progress_sub = { + 'Fighter Sword': ('Progressive Sword', 1), + 'Master Sword': ('Progressive Sword', 2), + 'Tempered Sword': ('Progressive Sword', 3), + 'Golden Sword': ('Progressive Sword', 4), + 'Power Glove': ('Progressive Glove', 1), + 'Titans Mitts': ('Progressive Glove', 2), + 'Bow': ('Progressive Bow', 1), + 'Silver Arrows': ('Progressive Bow', 2), + 'Blue Mail': ('Progressive Armor', 1), + 'Red Mail': ('Progressive Armor', 2), + 'Blue Shield': ('Progressive Shield', 1), + 'Red Shield': ('Progressive Shield', 2), + 'Mirror Shield': ('Progressive Shield', 3), +} + + +def substitute_progressive(req): + for item in req.get_values(): + if item.item in progress_sub.keys(): + item.item, item.amount = progress_sub[item.item] From 4e7632c16ac07d61786dd7046640270264ab5e11 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 16 Sep 2022 10:38:36 -0600 Subject: [PATCH 002/158] Changes for enemy regions --- DoorShuffle.py | 6 ++++-- Doors.py | 2 ++ Regions.py | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 368aa931..27faa618 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -3494,8 +3494,10 @@ logical_connections = [ ('PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony - Ranged Crystal'), ('PoD Map Balcony Ranged Crystal Exit', 'PoD Map Balcony'), ('PoD Basement Ledge Drop Down', 'PoD Stalfos Basement'), - ('PoD Falling Bridge Path N', 'PoD Falling Bridge Ledge'), - ('PoD Falling Bridge Path S', 'PoD Falling Bridge'), + ('PoD Falling Bridge Path N', 'PoD Falling Bridge Mid'), + ('PoD Falling Bridge Path S', 'PoD Falling Bridge Mid'), + ('PoD Falling Bridge Mid Path S', 'PoD Falling Bridge'), + ('PoD Falling Bridge Mid Path N', 'PoD Falling Bridge Ledge'), ('PoD Bow Statue Left to Right Barrier - Orange', 'PoD Bow Statue Right'), ('PoD Bow Statue Left to Right Bypass', 'PoD Bow Statue Right'), ('PoD Bow Statue Left to Crystal', 'PoD Bow Statue Left - Crystal'), diff --git a/Doors.py b/Doors.py index 32ffd9ae..f5dd3a9c 100644 --- a/Doors.py +++ b/Doors.py @@ -425,6 +425,8 @@ def create_doors(world, player): create_door(player, 'PoD Falling Bridge EN', Intr).dir(Ea, 0x1a, Top, High).pos(4), create_door(player, 'PoD Falling Bridge Path N', Lgcl), create_door(player, 'PoD Falling Bridge Path S', Lgcl), + create_door(player, 'PoD Falling Bridge Mid Path S', Lgcl), + create_door(player, 'PoD Falling Bridge Mid Path N', Lgcl), create_door(player, 'PoD Big Chest Balcony W', Nrml).dir(We, 0x1a, Mid, High).pos(2), create_door(player, 'PoD Dark Maze EN', Nrml).dir(Ea, 0x19, Top, High).small_key().pos(1), create_door(player, 'PoD Dark Maze E', Nrml).dir(Ea, 0x19, Mid, High).pos(0), diff --git a/Regions.py b/Regions.py index 3e288727..37caef6b 100644 --- a/Regions.py +++ b/Regions.py @@ -436,6 +436,7 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'PoD Basement Ledge', 'Palace of Darkness', None, ['PoD Basement Ledge Drop Down', 'PoD Basement Ledge Up Stairs']), create_dungeon_region(player, 'PoD Big Key Landing', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest'], ['PoD Big Key Landing Down Stairs', 'PoD Big Key Landing Hole']), create_dungeon_region(player, 'PoD Falling Bridge Ledge', 'Palace of Darkness', None, ['PoD Falling Bridge WN', 'PoD Falling Bridge EN', 'PoD Falling Bridge Path S']), + create_dungeon_region(player, 'PoD Falling Bridge Mid', 'Palace of Darkness', None, ['PoD Falling Bridge Mid Path S', 'PoD Falling Bridge Mid Path N']), create_dungeon_region(player, 'PoD Falling Bridge', 'Palace of Darkness', None, ['PoD Falling Bridge SW', 'PoD Falling Bridge Path N']), create_dungeon_region(player, 'PoD Dark Maze', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom'], ['PoD Dark Maze EN', 'PoD Dark Maze E']), create_dungeon_region(player, 'PoD Big Chest Balcony', 'Palace of Darkness', ['Palace of Darkness - Big Chest'], ['PoD Big Chest Balcony W']), From 5b4bcc02d4ca18859f4949d12bb04d59687c1e35 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 16 Sep 2022 15:49:16 -0600 Subject: [PATCH 003/158] Finished region mapping --- DoorShuffle.py | 3 + Doors.py | 3 + Dungeons.py | 5 +- Regions.py | 6 +- Rules.py | 7 +- source/dungeon/EnemyList.py | 1993 ++++++++++++++++++----------------- 6 files changed, 1015 insertions(+), 1002 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 27faa618..46e6b50b 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -3585,11 +3585,14 @@ logical_connections = [ ('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'), ('Ice Cross Right Push Block Top', 'Ice Bomb Drop'), + ('Ice Bomb Drop Path', 'Ice Bomb Drop - Top'), ('Ice Conveyor to Crystal', 'Ice Conveyor - Crystal'), ('Ice Conveyor Crystal Exit', 'Ice Conveyor'), ('Ice Big Key Push Block', 'Ice Dead End'), ('Ice Bomb Jump Ledge Orange Barrier', 'Ice Bomb Jump Catwalk'), ('Ice Bomb Jump Catwalk Orange Barrier', 'Ice Bomb Jump Ledge'), + ('Ice Right H Path', 'Ice Hammer Block'), + ('Ice Hammer Block Path', 'Ice Right H'), ('Ice Hookshot Ledge Path', 'Ice Hookshot Balcony'), ('Ice Hookshot Balcony Path', 'Ice Hookshot Ledge'), ('Ice Crystal Right Orange Barrier', 'Ice Crystal Left'), diff --git a/Doors.py b/Doors.py index f5dd3a9c..448165d4 100644 --- a/Doors.py +++ b/Doors.py @@ -738,6 +738,7 @@ def create_doors(world, player): create_door(player, 'Ice Cross Top Push Block Right', Lgcl), # dynamic create_door(player, 'Ice Cross Bottom SE', Nrml).dir(So, 0x1e, Right, High).pos(3).portal(X, 0x00), create_door(player, 'Ice Cross Right ES', Nrml).dir(Ea, 0x1e, Bot, High).trap(0x4).pos(0), + create_door(player, 'Ice Bomb Drop Path', Lgcl), create_door(player, 'Ice Bomb Drop Hole', Hole), create_door(player, 'Ice Compass Room NE', Nrml).dir(No, 0x2e, Right, High).pos(0), create_door(player, 'Ice Pengator Switch WS', Nrml).dir(We, 0x1f, Bot, High).trap(0x4).pos(0), @@ -769,6 +770,8 @@ def create_doors(world, player): create_door(player, 'Ice Spike Room WS', Nrml).dir(We, 0x5f, Bot, High).small_key().pos(0), create_door(player, 'Ice Spike Room Down Stairs', Sprl).dir(Dn, 0x5f, 3, HTH).ss(Z, 0x11, 0x48, True, True), create_door(player, 'Ice Spike Room Up Stairs', Sprl).dir(Up, 0x5f, 4, HTH).ss(Z, 0x1a, 0xa4, True, True), + create_door(player, 'Ice Right H Path', Lgcl), + create_door(player, 'Ice Hammer Block Path', Lgcl), create_door(player, 'Ice Hammer Block Down Stairs', Sprl).dir(Dn, 0x3f, 0, HTH).ss(Z, 0x11, 0xb8, True, True).kill(), create_door(player, 'Ice Hammer Block ES', Intr).dir(Ea, 0x3f, Bot, High).pos(0), create_door(player, 'Ice Tongue Pull WS', Intr).dir(We, 0x3f, Bot, High).pos(0), diff --git a/Dungeons.py b/Dungeons.py index 9d862b05..7dd80270 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -99,7 +99,7 @@ pod_regions = [ 'PoD Arena Main', 'PoD Arena Main - Ranged Crystal', 'PoD Arena North', 'PoD Arena Bridge', 'PoD Arena Bridge - Ranged Crystal', 'PoD Arena Landing', 'PoD Arena Right', 'PoD Arena Right - Ranged Crystal', 'PoD Arena Ledge', 'PoD Arena Ledge - Ranged Crystal', 'PoD Sexy Statue', 'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Conveyor', 'PoD Mimics 1', 'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room', - 'PoD Stalfos Basement', 'PoD Basement Ledge', 'PoD Big Key Landing', 'PoD Falling Bridge', + 'PoD Stalfos Basement', 'PoD Basement Ledge', 'PoD Big Key Landing', 'PoD Falling Bridge', 'PoD Falling Bridge Mid', 'PoD Falling Bridge Ledge', 'PoD Dark Maze', 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement', 'PoD Harmless Hellway', 'PoD Mimics 2', 'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal', 'PoD Dark Pegs Landing', 'PoD Dark Pegs Right', 'PoD Dark Pegs Middle', 'PoD Dark Pegs Left', 'PoD Dark Pegs Landing - Ranged Crystal', @@ -144,7 +144,8 @@ thieves_regions = [ ice_regions = [ 'Ice Lobby', 'Ice Jelly Key', 'Ice Floor Switch', 'Ice Cross Left', 'Ice Cross Bottom', 'Ice Cross Right', 'Ice Cross Top', 'Ice Compass Room', 'Ice Pengator Switch', 'Ice Dead End', 'Ice Big Key', 'Ice Bomb Drop', - 'Ice Stalfos Hint', 'Ice Conveyor', 'Ice Conveyor - Crystal', 'Ice Bomb Jump Ledge', 'Ice Bomb Jump Catwalk', 'Ice Narrow Corridor', + 'Ice Stalfos Hint', 'Ice Conveyor', 'Ice Conveyor - Crystal', 'Ice Bomb Jump Ledge', 'Ice Bomb Jump Catwalk', + 'Ice Narrow Corridor', 'Ice Right H', 'Ice Bomb Drop - Top', 'Ice Pengator Trap', 'Ice Spike Cross', 'Ice Firebar', 'Ice Falling Square', 'Ice Spike Room', 'Ice Hammer Block', 'Ice Tongue Pull', 'Ice Freezors', 'Ice Freezors Ledge', 'Ice Tall Hint', 'Ice Hookshot Ledge', 'Ice Hookshot Balcony', 'Ice Spikeball', 'Ice Lonely Freezor', 'Iced T', 'Ice Catwalk', 'Ice Many Pots', diff --git a/Regions.py b/Regions.py index 37caef6b..23dcf79b 100644 --- a/Regions.py +++ b/Regions.py @@ -589,7 +589,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Ice Pengator Switch', 'Ice Palace', None, ['Ice Pengator Switch WS', 'Ice Pengator Switch ES']), create_dungeon_region(player, 'Ice Dead End', 'Ice Palace', None, ['Ice Dead End WS']), create_dungeon_region(player, 'Ice Big Key', 'Ice Palace', ['Ice Palace - Big Key Chest'], ['Ice Big Key Push Block', 'Ice Big Key Down Ladder']), - create_dungeon_region(player, 'Ice Bomb Drop', 'Ice Palace', None, ['Ice Bomb Drop SE', 'Ice Bomb Drop Hole']), + create_dungeon_region(player, 'Ice Bomb Drop', 'Ice Palace', None, ['Ice Bomb Drop SE', 'Ice Bomb Drop Path']), + create_dungeon_region(player, 'Ice Bomb Drop - Top', 'Ice Palace', None, ['Ice Bomb Drop Hole']), create_dungeon_region(player, 'Ice Stalfos Hint', 'Ice Palace', None, ['Ice Stalfos Hint SE']), create_dungeon_region(player, 'Ice Conveyor', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Conveyor NE', 'Ice Conveyor to Crystal', 'Ice Conveyor SW']), create_dungeon_region(player, 'Ice Conveyor - Crystal', 'Ice Palace', None, ['Ice Conveyor Crystal Exit']), @@ -601,7 +602,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Ice Firebar', 'Ice Palace', None, ['Ice Firebar ES', 'Ice Firebar Down Ladder']), create_dungeon_region(player, 'Ice Falling Square', 'Ice Palace', None, ['Ice Falling Square SE', 'Ice Falling Square Hole']), create_dungeon_region(player, 'Ice Spike Room', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Spike Room WS', 'Ice Spike Room Up Stairs', 'Ice Spike Room Down Stairs']), - create_dungeon_region(player, 'Ice Hammer Block', 'Ice Palace', ['Ice Palace - Hammer Block Key Drop', 'Ice Palace - Map Chest'], ['Ice Hammer Block Down Stairs', 'Ice Hammer Block ES']), + create_dungeon_region(player, 'Ice Right H', 'Ice Palace', None, ['Ice Hammer Block Down Stairs', 'Ice Hammer Block ES', 'Ice Right H Path']), + create_dungeon_region(player, 'Ice Hammer Block', 'Ice Palace', ['Ice Palace - Hammer Block Key Drop', 'Ice Palace - Map Chest'], ['Ice Hammer Block Path']), create_dungeon_region(player, 'Ice Tongue Pull', 'Ice Palace', None, ['Ice Tongue Pull Up Ladder', 'Ice Tongue Pull WS']), create_dungeon_region(player, 'Ice Freezors', 'Ice Palace', ['Ice Palace - Freezor Chest'], ['Ice Freezors Up Ladder', 'Ice Freezors Hole', 'Ice Freezors Bomb Hole']), create_dungeon_region(player, 'Ice Freezors Ledge', 'Ice Palace', None, ['Ice Freezors Ledge ES', 'Ice Freezors Ledge Hole']), diff --git a/Rules.py b/Rules.py index 5bd27023..1355b7e7 100644 --- a/Rules.py +++ b/Rules.py @@ -296,6 +296,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Ice Lobby WS', player), lambda state: state.can_melt_things(player)) set_rule(world.get_entrance('Ice Hammer Block ES', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Ice Right H Path', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_location('Ice Palace - Hammer Block Key Drop', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_location('Ice Palace - Map Chest', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_entrance('Ice Antechamber Hole', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) @@ -498,6 +499,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Ice Bomb Jump Ledge Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Ledge', player), player)) set_rule(world.get_entrance('Ice Bomb Jump Catwalk Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Catwalk', player), player)) + set_rule(world.get_entrance('Ice Bomb Drop Path', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Ice Conveyor to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Ice Refill to Crystal', player), lambda state: state.can_hit_crystal(player) or state.can_reach_blue(world.get_region('Ice Refill', player), player)) @@ -2017,8 +2019,9 @@ bunny_impassible_doors = { 'Thieves Hellway Blue Barrier', 'Thieves Hellway Crystal Blue Barrier', 'Thieves Attic ES', 'Thieves Basement Block Path', 'Thieves Blocked Entry Path', 'Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block Path', 'Ice Lobby WS', 'Ice Cross Left Push Block', 'Ice Cross Bottom Push Block Left', - 'Ice Bomb Drop Hole', 'Ice Pengator Switch WS', 'Ice Pengator Switch ES', 'Ice Big Key Push Block', 'Ice Stalfos Hint SE', 'Ice Bomb Jump EN', - 'Ice Pengator Trap NE', 'Ice Hammer Block ES', 'Ice Tongue Pull WS', 'Ice Freezors Bomb Hole', 'Ice Tall Hint WS', + 'Ice Bomb Drop Hole', 'Ice Pengator Switch WS', 'Ice Pengator Switch ES', 'Ice Big Key Push Block', + 'Ice Stalfos Hint SE', 'Ice Bomb Jump EN', 'Ice Pengator Trap NE', 'Ice Hammer Block ES', 'Ice Right H Path', + 'Ice Bomb Drop Path', 'Ice Tongue Pull WS', 'Ice Freezors Bomb Hole', 'Ice Tall Hint WS', 'Ice Hookshot Ledge Path', 'Ice Hookshot Balcony Path', 'Ice Many Pots SW', 'Ice Many Pots WS', 'Ice Crystal Right Blue Hole', 'Ice Crystal Left Blue Barrier', 'Ice Big Chest Landing Push Blocks', 'Ice Backwards Room Hole', 'Ice Switch Room SE', 'Ice Antechamber NE', 'Ice Antechamber Hole', 'Mire Lobby Gap', diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index f04aff2d..cd5bb518 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -418,6 +418,7 @@ def init_enemy_stats(): } + class Sprite(object): def __init__(self, super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, drops_item=False, drop_item_kind=None): @@ -495,7 +496,7 @@ def init_vanilla_sprites(): create_sprite(0x0008, EnemySprite.BigFairy, 0x00, 0, 0x07, 0x16) create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08) create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x08, 0x08) - create_sprite(0x0009, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x0b) + create_sprite(0x0009, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x0b, 'PoD Warp Room') create_sprite(0x000a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x08, 'PoD Stalfos Basement') create_sprite(0x000a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x09, 'PoD Stalfos Basement') create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x0d, 0x09) @@ -607,8 +608,8 @@ def init_vanilla_sprites(): create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08) create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08) create_sprite(0x001e, EnemySprite.CrystalSwitch, 0x00, 0, 0x1a, 0x09) - create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Bomb Drop') # todo: need to hit crystal for 2 of these - create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Bomb Drop') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Bomb Drop - Top') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Bomb Drop - Top') create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x0a, 'Ice Bomb Drop') create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x0a, 'Ice Bomb Drop') create_sprite(0x001e, EnemySprite.Blob, 0x00, 0, 0x08, 0x18, 'Ice Floor Switch') @@ -653,976 +654,976 @@ def init_vanilla_sprites(): 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(0x0026, EnemySprite.Medusa, 0x00, 0, 0x03, 0x04) - create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x1a, 0x05) - create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x05, 0x06) - create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x06) - create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x09) + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x1a, 0x05, 'Swamp Right Elbow') + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x05, 0x06, 'Swamp Shooters') + create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x06, 'Swamp Shooters') + create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x09, 'Swamp Shooters') create_sprite(0x0026, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x0c) 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) - create_sprite(0x0026, EnemySprite.Kyameron, 0x00, 0, 0x15, 0x18) - create_sprite(0x0026, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x19) - create_sprite(0x0026, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x1a) - create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09) - create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x18, 0x13) - create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x13) - create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x1a) - create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06) - create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x05, 0x0e) - create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x04, 0x16) - create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06) - create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x08, 0x08) - create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a) - create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x07, 0x0d) - create_sprite(0x0028, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x10) + 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.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') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x18, 0x13, 'Hera 4F') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x13, 'Hera 4F') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x1a, 'Hera 4F') + create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06, 'Hera Big Chest Landing') + create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x05, 0x0e, 'Hera 4F') + create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x04, 0x16, 'Hera 4F') + create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, 'Hera 4F') + 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.SpikeBlock, 0x00, 0, 0x08, 0x10, 'Swamp Entrance') create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x18, 0x16) create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17) create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f) - create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x08) - create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c) - create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x10, 0x0c) - create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x0f) - create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11) - create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0f, 0x13) - create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11) + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x08, 'PoD Arena North') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x10, 0x0c, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x0f, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0f, 0x13, 'PoD Arena Main') + create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11, 'PoD Arena Main') create_sprite(0x002b, EnemySprite.Statue, 0x00, 0, 0x0a, 0x0a) - create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17) + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17, 'PoD Map Balcony') create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17) create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18) - create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x05, 0x1a) - create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a) + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x05, 0x1a, 'PoD Map Balcony') + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a, 'PoD Map Balcony') create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x17, 0x1a) create_sprite(0x002c, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x05) create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x09, 0x04) create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x06, 0x05) create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) - create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x06) - create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1c, 0x06) - create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x16, 0x08) - create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x19, 0x08) - create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0b) - create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x0b) + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x06, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1c, 0x06, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x16, 0x08, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x19, 0x08, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0b, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x0b, 'Ice Compass Room') create_sprite(0x0030, EnemySprite.CutsceneAgahnim, 0x00, 0, 0x07, 0x05) create_sprite(0x0031, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x1a) create_sprite(0x0031, EnemySprite.CrystalSwitch, 0x00, 0, 0x16, 0x0b) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x05) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x06) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x09) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x0c) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x15) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x15) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x16) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x18) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x19) - create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x1c) - create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d) - create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x0f, 0x0d) - create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x13, 0x0d) - create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x10, 0x0e) - create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x12, 0x0f) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x05, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x06, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x09, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x0c, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x15, 'Hera Startile Corner') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x15, 'Hera Beetles') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x16, 'Hera Beetles') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x18, 'Hera Startile Corner') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x19, 'Hera Beetles') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x1c, 'Hera Startile Corner') + create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x0f, 0x0d, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x13, 0x0d, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x10, 0x0e, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x12, 0x0f, 'Sewers Dark Cross') create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17) create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17) create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19) - create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x0f, 0x0b) - create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x10, 0x12) - create_sprite(0x0034, EnemySprite.Kyameron, 0x00, 0, 0x0f, 0x15) - create_sprite(0x0034, EnemySprite.Firesnake, 0x00, 0, 0x19, 0x17) - create_sprite(0x0034, EnemySprite.Blob, 0x00, 0, 0x03, 0x18) - create_sprite(0x0034, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x18) - create_sprite(0x0034, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x1a) + 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.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') + create_sprite(0x0034, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x1a, 'Swamp West Shallows') create_sprite(0x0035, EnemySprite.CrystalSwitch, 0x00, 0, 0x16, 0x06) create_sprite(0x0035, EnemySprite.WaterSwitch, 0x00, 0, 0x14, 0x05) - create_sprite(0x0035, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05) - create_sprite(0x0035, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x09) - create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x0b) - create_sprite(0x0035, EnemySprite.Blob, 0x00, 0, 0x07, 0x14) - create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x18) - create_sprite(0x0035, EnemySprite.Firesnake, 0x00, 0, 0x16, 0x19) + create_sprite(0x0035, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05, 'Swamp Crystal Switch Outer') + create_sprite(0x0035, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x09, 'Swamp Crystal Switch Outer') + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x0b, 'Swamp Crystal Switch Outer') + create_sprite(0x0035, EnemySprite.Blob, 0x00, 0, 0x07, 0x14, 'Swamp Trench 2 Departure') + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x18, 'Swamp Trench 2 Pots') + create_sprite(0x0035, EnemySprite.Firesnake, 0x00, 0, 0x16, 0x19, 'Swamp Trench 2 Pots') create_sprite(0x0035, EnemySprite.FourWayShooter, 0x00, 0, 0x17, 0x1a) - create_sprite(0x0035, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b) - create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x1c) + 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) - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x14, 0x0a) + 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, 0x10, SpriteType.Overlord, 0, 0x01, 0x0d) - create_sprite(0x0036, EnemySprite.Kyameron, 0x00, 0, 0x14, 0x13) + create_sprite(0x0036, EnemySprite.Kyameron, 0x00, 0, 0x14, 0x13, 'Swamp Hub') create_sprite(0x0036, 0x11, SpriteType.Overlord, 0, 0x1e, 0x13) - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x09, 0x14) - create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x12, 0x17) + 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, 0x13, SpriteType.Overlord, 0, 0x0a, 0x1e) create_sprite(0x0036, 0x13, SpriteType.Overlord, 0, 0x14, 0x1e) create_sprite(0x0037, EnemySprite.WaterSwitch, 0x00, 0, 0x0b, 0x04) - create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x06) - create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x17, 0x08) - create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x1a, 0x08) - create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x09) - create_sprite(0x0037, EnemySprite.Firesnake, 0x00, 0, 0x15, 0x14) - create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x17) - create_sprite(0x0037, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x19) + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x06, 'Swamp Hammer Switch') + create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x17, 0x08, 'Swamp Map Ledge') + create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x1a, 0x08, 'Swamp Map Ledge') + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x09, 'Swamp Hammer Switch') + create_sprite(0x0037, EnemySprite.Firesnake, 0x00, 0, 0x15, 0x14, 'Swamp Trench 1 Approach') + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x17, 'Swamp Trench 1 Approach') + 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) - create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x0c, 0x06) - create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a) - create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x0c) - create_sprite(0x0038, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x10) - create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x06, 0x14) - create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x18) - create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x1a) - create_sprite(0x0039, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x18) + 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(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, True, 0xe4) - create_sprite(0x0039, EnemySprite.MiniHelmasaur, 0x00, 0, 0x09, 0x15) - create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16) - create_sprite(0x0039, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18) - create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a) - create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11) - create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11) - create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14) - create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14) - create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x14) - create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x14) - create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x06) - create_sprite(0x003b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09) - create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x0d) - create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0f) - create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x13) - create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16) - create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a) - create_sprite(0x003c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x08) - create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14) - create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x14) + create_sprite(0x0039, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x15, 'Skull Spike Corner', True, 0xe4) + create_sprite(0x0039, EnemySprite.MiniHelmasaur, 0x00, 0, 0x09, 0x15, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a, 'Skull Spike Corner') + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11, 'PoD Pit Room',) + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11, 'PoD Pit Room',) + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14, 'PoD Pit Room',) + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14, 'PoD Pit Room',) + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x14, 'PoD Pit Room',) + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x14, 'PoD Pit Room',) + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x06, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x0d, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0f, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x13, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a, 'PoD Conveyor') + create_sprite(0x003c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x08, 'Hookshot Cave (Hook Islands)') + create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14, 'Hookshot Cave (Hook Islands)') + create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x14, 'Hookshot Cave (Bonk Islands)') create_sprite(0x003d, EnemySprite.CrystalSwitch, 0x00, 0, 0x05, 0x17) create_sprite(0x003d, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x19) - create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x07, True, 0xe4) - create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x07) - create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x15, 0x08) - create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x08) - create_sprite(0x003d, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x0a) - create_sprite(0x003d, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x0b) + create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x07, 'GT Mini Helmasaur Room', True, 0xe4) + create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x07, 'GT Mini Helmasaur Room') + create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x15, 0x08, 'GT Mini Helmasaur Room') + create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x08, 'GT Mini Helmasaur Room') + create_sprite(0x003d, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x0a, 'GT Bomb Conveyor') + create_sprite(0x003d, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x0b, 'GT Bomb Conveyor') create_sprite(0x003d, 0x0a, SpriteType.Overlord, 0, 0x1b, 0x15) - create_sprite(0x003d, EnemySprite.SparkCCW, 0x00, 0, 0x13, 0x16) - create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x1c, 0x16) - create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x09, 0x16) - create_sprite(0x003d, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17) - create_sprite(0x003d, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x17) + create_sprite(0x003d, EnemySprite.SparkCCW, 0x00, 0, 0x13, 0x16, 'GT Falling Torches') + create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x1c, 0x16, 'GT Falling Torches') + create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x09, 0x16, 'GT Crystal Inner Circle') + create_sprite(0x003d, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17, 'GT Crystal Inner Circle') + create_sprite(0x003d, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x17, 'GT Crystal Inner Circle') create_sprite(0x003e, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x15) - create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x19, 0x04) - create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x16, 0x0b) - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x05, 0x12) - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x0e, 0x12) + 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, 0x07, SpriteType.Overlord, 0, 0x10, 0x12) - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x12, 0x12) - create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x15, 0x12) - create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16) - create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x18, True, 0xe4) - create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x19) - create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x1a) + 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.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') + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x1a, 'Ice Conveyor') create_sprite(0x003f, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x04, 0x15) - create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x0c, 0x16) + create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x0c, 0x16, 'Ice Right H') create_sprite(0x003f, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x13, 0x15) - create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x04, 0x17) - create_sprite(0x003f, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x18) - create_sprite(0x0040, EnemySprite.BlueGuard, 0x00, 1, 0x09, 0x08) - create_sprite(0x0040, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x0f) - create_sprite(0x0040, EnemySprite.Statue, 0x00, 1, 0x18, 0x15) - create_sprite(0x0040, EnemySprite.RedSpearGuard, 0x00, 1, 0x1b, 0x18) - create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x17, 0x1a) - create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x19, 0x1a) - create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x0a) - create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x1b, 0x0b) - create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x0f, 0x0d) - create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x15) - create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x06) - create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x06) - create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x06) - create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x07) - create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x07) - create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x07) - create_sprite(0x0043, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x06) + create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x04, 0x17, 'Ice Hammer Block') + create_sprite(0x003f, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x18, 'Ice Hammer Block') + create_sprite(0x0040, EnemySprite.BlueGuard, 0x00, 1, 0x09, 0x08, 'Tower Catwalk') + create_sprite(0x0040, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x0f, 'Tower Catwalk') + create_sprite(0x0040, EnemySprite.Statue, 0x00, 1, 0x18, 0x15, 'Tower Push Statue') + create_sprite(0x0040, EnemySprite.RedSpearGuard, 0x00, 1, 0x1b, 0x18, 'Tower Push Statue') + create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x17, 0x1a, 'Tower Push Statue') + create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x19, 0x1a, 'Tower Push Statue') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x0a, 'Sewers Behind Tapestry') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x1b, 0x0b, 'Sewers Behind Tapestry') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x0f, 0x0d, 'Sewers Behind Tapestry') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x15, 'Sewers Behind Tapestry') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x06, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x06, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x06, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x07, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x07, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x07, 'Sewers Rope Room') + create_sprite(0x0043, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x06, 'Desert Wall Slide') create_sprite(0x0043, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) - create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x09, 0x06) - create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x05, 0x08) - create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x04) - create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x08) - create_sprite(0x0044, EnemySprite.Blob, 0x00, 0, 0x17, 0x08) - create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0c) - create_sprite(0x0044, EnemySprite.RedBari, 0x00, 0, 0x17, 0x0f) + create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x09, 0x06, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x05, 0x08, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x04, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x08, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.Blob, 0x00, 0, 0x17, 0x08, 'Thieves Conveyor Bridge') + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0c, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.RedBari, 0x00, 0, 0x17, 0x0f, 'Thieves Conveyor Bridge') create_sprite(0x0044, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x15) - create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x16) + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x16, 'Thieves Conveyor Bridge') create_sprite(0x0045, EnemySprite.BlindMaiden, 0x00, 0, 0x19, 0x06) - create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x06, 0x06) - create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x0b) - create_sprite(0x0045, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x0b) - create_sprite(0x0045, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x0b) - create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x0c) - create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x1a, 0x0c) - create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x11) - create_sprite(0x0045, EnemySprite.Blob, 0x00, 0, 0x16, 0x18) - create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x19, 0x1b) - create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x07, 0x1c) - create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x16, 0x05) + 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.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, 0x11, SpriteType.Overlord, 0, 0x1b, 0x06) - create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x09, 0x1a) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x09, 0x1a, 'Swamp Donut Bottom') create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x1a) - create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x11, 0x1b) - create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x05) - create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x0b) - create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0c) - create_sprite(0x0049, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x06) - create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x07, 0x08) - create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x0b) - create_sprite(0x0049, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x10) - create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x16, 0x14) - create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x09, 0x16) - create_sprite(0x0049, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x17) - create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18) - create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x1a, 0x18) - create_sprite(0x004a, EnemySprite.Statue, 0x00, 0, 0x14, 0x07) - create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x08) - create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x08) - create_sprite(0x004b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x04) - create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x05) - create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06) - create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x04, 0x08) - create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0b, 0x08) - create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x18) - create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x19) - create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x19) - create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x15, 0x11) - create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x19, 0x12) - create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x15, 0x05) - create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x1a, 0x05) - create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x06) - create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x0a) - create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x15) - create_sprite(0x004c, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x18) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x11, 0x1b, 'Swamp Donut Bottom') + 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') + create_sprite(0x0049, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x06, 'Skull Vines') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x07, 0x08, 'Skull Vines') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x0b, 'Skull Torch Room') + create_sprite(0x0049, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f, 'Skull Torch Room') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x10, 'Skull Torch Room') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x16, 0x14, 'Skull Torch Room') + create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x09, 0x16, 'Skull Star Pits') + create_sprite(0x0049, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x17, 'Skull Star Pits') + create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18, 'Skull Star Pits') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x1a, 0x18, 'Skull Torch Room') + create_sprite(0x004a, EnemySprite.Statue, 0x00, 0, 0x14, 0x07, 'PoD Middle Cage') + create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x08, 'PoD Left Cage') + create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x08, 'PoD Middle Cage') + create_sprite(0x004b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x04, 'PoD Mimics 1') + create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x05, 'PoD Warp Hint') + create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06, 'PoD Warp Hint') + create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x04, 0x08, 'PoD Mimics 1') + create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0b, 0x08, 'PoD Mimics 1') + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x18, 'PoD Jelly Hall') + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x19, 'PoD Jelly Hall') + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x19, 'PoD Jelly Hall') + create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x15, 0x11, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x19, 0x12, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x15, 0x05, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x1a, 0x05, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x06, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x0a, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x15, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x18, 'GT Frozen Over') create_sprite(0x004d, EnemySprite.Moldorm, 0x00, 0, 0x0e, 0x0e) - create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x14, 0x08) - create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x16, 0x08) - create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x18, 0x08) - create_sprite(0x004e, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x09) + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x14, 0x08, 'Ice Narrow Corridor') + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x16, 0x08, 'Ice Narrow Corridor') + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x18, 0x08, 'Ice Narrow Corridor') + create_sprite(0x004e, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x09, 'Ice Bomb Jump Catwalk') create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x17, 0x06) create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x14, 0x08) create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x08) - create_sprite(0x0050, EnemySprite.GreenGuard, 0x00, 1, 0x17, 0x0e) - create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x18, 0x10) - create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x17, 0x12) - create_sprite(0x0051, EnemySprite.CastleMantle, 0x00, 0, 0x0e, 0x02) - create_sprite(0x0051, EnemySprite.BlueGuard, 0x01, 1, 0x09, 0x17) - create_sprite(0x0051, EnemySprite.BlueGuard, 0x02, 1, 0x16, 0x17) - create_sprite(0x0052, EnemySprite.GreenGuard, 0x00, 1, 0x07, 0x0d) - create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x08, 0x0f) - create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x07, 0x12) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x17, 0x07) - create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1c, 0x09) - create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x17, 0x0c) - create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x1a, 0x0c) - create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x13, 0x0e) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x05, 0x15) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x16) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x17) - create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x04, 0x1a) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x1a) - create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x1a) - create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x1b) - create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0e, 0x05) - create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0c, 0x0b) - create_sprite(0x0054, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x0e) - create_sprite(0x0054, EnemySprite.FirebarCW, 0x00, 0, 0x0f, 0x0e) - create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x10, 0x0f) - create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x12, 0x14) - create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0f, 0x15) - create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x17) - create_sprite(0x0055, EnemySprite.UnclePriest, 0x00, 0, 0x0e, 0x08) - create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x14, 0x15) - create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x16) - create_sprite(0x0056, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x05) + create_sprite(0x0050, EnemySprite.GreenGuard, 0x00, 1, 0x17, 0x0e, 'Hyrule Castle West Hall') + create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x18, 0x10, 'Hyrule Castle West Hall') + create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x17, 0x12, 'Hyrule Castle West Hall') + create_sprite(0x0051, EnemySprite.CastleMantle, 0x00, 0, 0x0e, 0x02, 'Hyrule Castle Throne Room') + create_sprite(0x0051, EnemySprite.BlueGuard, 0x01, 1, 0x09, 0x17, 'Hyrule Castle Throne Room') + create_sprite(0x0051, EnemySprite.BlueGuard, 0x02, 1, 0x16, 0x17, 'Hyrule Castle Throne Room') + create_sprite(0x0052, EnemySprite.GreenGuard, 0x00, 1, 0x07, 0x0d, 'Hyrule Castle East Hall') + create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x08, 0x0f, 'Hyrule Castle East Hall') + create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x07, 0x12, 'Hyrule Castle East Hall') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x17, 0x07, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1c, 0x09, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x17, 0x0c, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x1a, 0x0c, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x13, 0x0e, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x05, 0x15, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x16, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x17, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x04, 0x1a, 'Desert Four Statues') + 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.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(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') + create_sprite(0x0056, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x05, 'Skull X Room') create_sprite(0x0056, EnemySprite.Bumper, 0x00, 0, 0x07, 0x19) create_sprite(0x0056, EnemySprite.Bumper, 0x00, 0, 0x17, 0x19) - create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x04) - create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x05) - create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x03, 0x06) - create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0c, 0x06) + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x04, 'Skull X Room') + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x05, 'Skull Back Drop') + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x03, 0x06, 'Skull X Room') + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0c, 0x06, 'Skull X Room') create_sprite(0x0056, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11) - create_sprite(0x0056, EnemySprite.SpikeBlock, 0x00, 0, 0x18, 0x12) - create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x1b) - create_sprite(0x0056, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x1c) - create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x1c) - create_sprite(0x0057, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x04) - create_sprite(0x0057, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x04) - create_sprite(0x0057, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x05) - create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x07) - create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0c) - create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11, 'Skull Back Drop') + create_sprite(0x0056, EnemySprite.SpikeBlock, 0x00, 0, 0x18, 0x12, 'Skull Back Drop') + 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.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') + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0c, 'Skull Big Key') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0c, 0x0c, 'Skull Big Key') create_sprite(0x0057, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x14) - create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0a, 0x14) - create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14) - create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x19, 0x14) - create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x15) - create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x17) - create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18) - create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x18) - create_sprite(0x0057, EnemySprite.Statue, 0x00, 0, 0x0b, 0x18) - create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x14) - create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x16) - create_sprite(0x0058, EnemySprite.Bumper, 0x00, 0, 0x16, 0x16) - create_sprite(0x0058, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x04) - create_sprite(0x0058, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x06) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x14, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0a, 0x14, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x19, 0x14, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x15, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x17, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x18, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.Statue, 0x00, 0, 0x0b, 0x18, 'Skull 2 East Lobby') + create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x14, 'Skull Big Chest') + create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x16, 'Skull Big Chest') + create_sprite(0x0058, EnemySprite.Bumper, 0x00, 0, 0x16, 0x16, 'Skull Map Room') + create_sprite(0x0058, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x04, 'Skull Pot Circle') + create_sprite(0x0058, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x06, 'Skull Pot Circle') create_sprite(0x0058, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x08, 0x0a) - create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x0b) - create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x16, 0x19) - create_sprite(0x0058, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a) - create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x07, 0x10) - create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x16) - create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x14, 0x0f) - create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x1a, 0x0f) - create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x1a, 0x0a) - create_sprite(0x0059, EnemySprite.Firesnake, 0x00, 0, 0x08, 0x0b) - create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x15, 0x0d) - create_sprite(0x0059, EnemySprite.SparkCW, 0x00, 1, 0x05, 0x0e) - create_sprite(0x0059, EnemySprite.BunnyBeam, 0x00, 1, 0x1a, 0x13) - create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14) - create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x15, 0x15) - create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x1a, 0x15) + create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x0b, 'Skull Pot Circle') + create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x16, 0x19, 'Skull Map Room') + create_sprite(0x0058, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a, 'Skull 1 Lobby') + create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x07, 0x10, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x16, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x14, 0x0f, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x1a, 0x0f, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x1a, 0x0a, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Firesnake, 0x00, 0, 0x08, 0x0b, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x15, 0x0d, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.SparkCW, 0x00, 1, 0x05, 0x0e, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.BunnyBeam, 0x00, 1, 0x1a, 0x13, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x15, 0x15, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x1a, 0x15, 'Skull East Bridge') create_sprite(0x005a, EnemySprite.HelmasaurKing, 0x00, 0, 0x17, 0x16) create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x17, 0x0c) create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x18, 0x13) - create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x15) - create_sprite(0x005b, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x08) - create_sprite(0x005b, EnemySprite.RedEyegoreMimic, 0x00, 1, 0x19, 0x08) - create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x0e) - create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x1b, 0x10) - create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x11) - create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x12) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x15, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x08, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.RedEyegoreMimic, 0x00, 1, 0x19, 0x08, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x0e, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x1b, 0x10, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x11, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x12, 'GT Hidden Spikes') create_sprite(0x005c, EnemySprite.WallCannonHorzTop, 0x00, 0, 0x0b, 0x02) create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x05, 0x0e) create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x0e, 0x0e) create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x18) create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18) - create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x05) - create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x08, 0x06) - create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x08) - create_sprite(0x005d, EnemySprite.RedZazak, 0x00, 0, 0x15, 0x08) - create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x08) - create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x19, 0x08) - create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x08) - create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x0b) - create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x15) - create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x0b, 0x15) - create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x1a) - create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x1a) - create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x1a) + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x05, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x08, 0x06, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x08, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.RedZazak, 0x00, 0, 0x15, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x19, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x0b, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x15, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x0b, 0x15, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x1a, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x1a, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x1a, 'GT Gauntlet 3') create_sprite(0x005e, 0x0a, SpriteType.Overlord, 0, 0x1b, 0x05) - create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x1c, 0x05) - create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0b) - create_sprite(0x005e, EnemySprite.BigSpike, 0x00, 0, 0x17, 0x14) - create_sprite(0x005e, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18) - create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x18) - create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18) - create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x1b) - create_sprite(0x0060, EnemySprite.BlueGuard, 0x13, 0, 0x13, 0x08) - create_sprite(0x0061, EnemySprite.GreenGuard, 0x01, 0, 0x0c, 0x0e) - create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x12) - create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x12, 0x12) - create_sprite(0x0062, EnemySprite.BlueGuard, 0x13, 0, 0x0c, 0x08) - create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x0a, 0x0d) - create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x11, 0x0e) + create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x1c, 0x05, 'Ice Falling Square') + create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0b, 'Ice Falling Square') + create_sprite(0x005e, EnemySprite.BigSpike, 0x00, 0, 0x17, 0x14, 'Ice Spike Cross') + create_sprite(0x005e, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18, 'Ice Firebar') + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x18, 'Ice Spike Room') + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18, 'Ice Spike Room') + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x1b, 'Ice Spike Room') + create_sprite(0x0060, EnemySprite.BlueGuard, 0x13, 0, 0x13, 0x08, 'Hyrule Castle West Lobby') + create_sprite(0x0061, EnemySprite.GreenGuard, 0x01, 0, 0x0c, 0x0e, 'Hyrule Castle Lobby') + create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x12, 'Hyrule Castle Lobby') + create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x12, 0x12, 'Hyrule Castle Lobby') + create_sprite(0x0062, EnemySprite.BlueGuard, 0x13, 0, 0x0c, 0x08, 'Hyrule Castle East Lobby') + create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x0a, 0x0d, 'Hyrule Castle East Lobby') + create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x11, 0x0e, 'Hyrule Castle East Lobby') create_sprite(0x0063, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) - create_sprite(0x0063, EnemySprite.Beamos, 0x00, 0, 0x07, 0x18) - create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x12) + create_sprite(0x0063, EnemySprite.Beamos, 0x00, 0, 0x07, 0x18, 'Desert Back Lobby') + 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) - create_sprite(0x0064, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x16) - create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x17) - create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x19, 0x19) - create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x05, 0x1a) + 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.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') create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x15) create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x07, 0x17) create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x17) create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x0b, 0x17) create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x19) create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x0c, 0x1b) - create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x13, 0x15) - create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x09, 0x17) - create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x18) - create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x19) - create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x1c) - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x05) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x13, 0x15, 'Thieves Attic Window') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x09, 0x17, 'Thieves Cricket Hall Right') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x18, 'Thieves Cricket Hall Right') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x19, 'Thieves Attic Window') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x1c, 'Thieves Attic Window') + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x05, 'Swamp Refill') create_sprite(0x0066, 0x10, SpriteType.Overlord, 1, 0x04, 0x06) - create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x06) - create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x07) - create_sprite(0x0066, EnemySprite.Waterfall, 0x00, 1, 0x17, 0x14) + create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x06, 'Swamp Behind Waterfall') + 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) - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x13, 0x16) - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x18) - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0d, 0x19) + 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, 0x11, SpriteType.Overlord, 1, 0x1e, 0x19) - create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x17, 0x1b) - create_sprite(0x0067, EnemySprite.Bumper, 0x00, 0, 0x07, 0x0c) - create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x06) - create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x06) - create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c) - create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x0f) - create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x13) - create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13) - create_sprite(0x0067, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x14) - create_sprite(0x0067, EnemySprite.FirebarCCW, 0x00, 0, 0x07, 0x17) - create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x18, 0x1a) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x17, 0x1b, 'Swamp Waterfall Room') + 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') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x0f, 'Skull Compass Room') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x13, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x14, 'Skull Compass Room') + create_sprite(0x0067, EnemySprite.FirebarCCW, 0x00, 0, 0x07, 0x17, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x18, 0x1a, 'Skull Compass Room') create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x0e, 0x07) 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) + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x08, 'Skull Compass Room') create_sprite(0x0068, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x0e, 0x12) - create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x12, 0x12) - create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0a) - create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0a) - create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x0b) - create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x1c, 0x0b) - create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0e) - create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0e) + 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(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') + create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x1c, 0x0b, 'PoD Dark Basement') + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0e, 'PoD Dark Alley') + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0e, 'PoD Dark Alley') create_sprite(0x006b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x04) create_sprite(0x006b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x04) - create_sprite(0x006b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0a, 0x06) - create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x06, 0x09) - create_sprite(0x006b, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0a) - create_sprite(0x006b, EnemySprite.Statue, 0x00, 0, 0x06, 0x15) - create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x03, 0x18) - create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x18) - create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x1b) - create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x1b) - create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x15) - create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x15) - create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x14, 0x1b) - create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x1b) - create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17) - create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17) - create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19) - create_sprite(0x006c, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x18) - create_sprite(0x006c, EnemySprite.Medusa, 0x00, 0, 0x03, 0x1c) - create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x05, 0x06) - create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x06) - create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x09) - create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x0b) - create_sprite(0x006d, EnemySprite.Medusa, 0x00, 0, 0x04, 0x15) - create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x15) - create_sprite(0x006d, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x18) - create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x18) - create_sprite(0x006d, EnemySprite.SparkCCW, 0x00, 0, 0x06, 0x1a) - create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x08) - create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x09) - create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0a) - create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0b) - create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0c) - create_sprite(0x0071, EnemySprite.GreenGuard, 0x00, 1, 0x06, 0x18) - create_sprite(0x0071, EnemySprite.BlueGuard, 0x15, 1, 0x1a, 0x18, True, 0xe4) - create_sprite(0x0072, EnemySprite.BlueGuard, 0x05, 0, 0x11, 0x06, True, 0xe4) - create_sprite(0x0072, EnemySprite.BlueGuard, 0x01, 1, 0x0a, 0x19) - create_sprite(0x0073, EnemySprite.Debirando, 0x00, 0, 0x18, 0x18) - create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x17, 0x09) - create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x15, 0x15) - create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x1b, 0x18) - create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19) - create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x16, 0x1b) + create_sprite(0x006b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0a, 0x06, 'GT Crystal Paths') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x06, 0x09, 'GT Crystal Paths') + create_sprite(0x006b, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0a, 'GT Crystal Paths') + create_sprite(0x006b, EnemySprite.Statue, 0x00, 0, 0x06, 0x15, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x03, 0x18, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x18, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x1b, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x1b, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x15, 'GT Mimics 2') + create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x15, 'GT Mimics 2') + create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x14, 0x1b, 'GT Mimics 2') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x1b, 'GT Mimics 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x18, 'GT Beam Dash') + create_sprite(0x006c, EnemySprite.Medusa, 0x00, 0, 0x03, 0x1c, 'GT Lanmolas 2') + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x05, 0x06, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x06, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x09, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x0b, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.Medusa, 0x00, 0, 0x04, 0x15, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x15, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x18, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x18, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.SparkCCW, 0x00, 0, 0x06, 0x1a, 'GT Gauntlet 5') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x08, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x09, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0a, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0b, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0c, 'Ice Pengator Trap') + create_sprite(0x0071, EnemySprite.GreenGuard, 0x00, 1, 0x06, 0x18, 'Hyrule Dungeon Armory Main') + create_sprite(0x0071, EnemySprite.BlueGuard, 0x15, 1, 0x1a, 0x18, 'Hyrule Dungeon Armory Boomerang', True, 0xe4) + create_sprite(0x0072, EnemySprite.BlueGuard, 0x05, 0, 0x11, 0x06, 'Hyrule Dungeon Map Room', True, 0xe4) + create_sprite(0x0072, EnemySprite.BlueGuard, 0x01, 1, 0x0a, 0x19, 'Hyrule Dungeon North Abyss') + create_sprite(0x0073, EnemySprite.Debirando, 0x00, 0, 0x18, 0x18, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x17, 0x09, 'Desert Bonk Torch') + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x15, 0x15, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x1b, 0x18, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19, 'Desert Circle of Pots') + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x16, 0x1b, 'Desert Sandworm Corner') create_sprite(0x0073, EnemySprite.BonkItem, 0x00, 0, 0x14, 0x06) - create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x08, 0x18) - create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x17, 0x18) - create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x05) - create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x13, 0x05) - create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0c, 0x0a) - create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x13, 0x0a) - create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0e, 0x1b) - create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x12, 0x1b) - create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x08, 0x07) - create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x04, 0x1b) - create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x05) - create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x05) - create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x0a) - create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x0a) + create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x08, 0x18, 'Desert North Hall') + create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x17, 0x18, 'Desert North Hall') + create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x05, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x13, 0x05, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0c, 0x0a, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x13, 0x0a, 'Desert Dead End') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0e, 0x1b, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x12, 0x1b, 'Desert Dead End') + create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x08, 0x07, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x04, 0x1b, 'Desert Arrow Pot Corner') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x05, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x05, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x0a, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x0a, 'Desert Trap Room') create_sprite(0x0075, EnemySprite.WallCannonVertLeft, 0x00, 0, 0x11, 0x0b) create_sprite(0x0075, EnemySprite.WallCannonVertRight, 0x00, 0, 0x1e, 0x0b) - create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x07, 0x19) - create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x09, 0x19) + 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) - create_sprite(0x0076, EnemySprite.Kyameron, 0x00, 0, 0x07, 0x0f) - create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x08, 0x11) - create_sprite(0x0076, EnemySprite.Blob, 0x00, 0, 0x1b, 0x19) + 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.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) - create_sprite(0x0077, EnemySprite.MiniMoldorm, 0x00, 1, 0x0b, 0x09) + create_sprite(0x0076, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1c, 'Swamp Flooded Room') + create_sprite(0x0077, EnemySprite.MiniMoldorm, 0x00, 1, 0x0b, 0x09, 'Hera Back') create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x10, 0x18) create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x09, 0x1a) create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x16, 0x1a) - create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x07, 0x0a) - create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x17, 0x0a) - create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x07) - create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x09) - create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x04, 0x15) - create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15) - create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x17) - create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x17) - create_sprite(0x007b, EnemySprite.Statue, 0x00, 0, 0x13, 0x18) - create_sprite(0x007b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x18) - create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x19) - create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x1a) - create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x0b, 0x1b) - create_sprite(0x007c, EnemySprite.MiniMoldorm, 0x00, 0, 0x19, 0x1c) - create_sprite(0x007c, EnemySprite.FirebarCCW, 0x00, 0, 0x06, 0x0c) - create_sprite(0x007c, EnemySprite.SpikeBlock, 0x00, 0, 0x07, 0x10) - create_sprite(0x007c, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x14) - create_sprite(0x007c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18) - create_sprite(0x007c, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x18) + create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x07, 0x0a, 'Hera Back') + create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x17, 0x0a, 'Hera Back') + create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x07, 'GT Conveyor Star Pits') + create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x09, 'GT Conveyor Star Pits') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x04, 0x15, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x17, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x17, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.Statue, 0x00, 0, 0x13, 0x18, 'GT Hidden Star') + create_sprite(0x007b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x18, 'GT Hidden Star') + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x19, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x1a, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x0b, 0x1b, 'GT DMs Room') + create_sprite(0x007c, EnemySprite.MiniMoldorm, 0x00, 0, 0x19, 0x1c, 'GT Randomizer Room') + create_sprite(0x007c, EnemySprite.FirebarCCW, 0x00, 0, 0x06, 0x0c, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.SpikeBlock, 0x00, 0, 0x07, 0x10, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x14, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x18, 'GT Randomizer Room') create_sprite(0x007c, 0x0b, SpriteType.Overlord, 0, 0x07, 0x1a) - create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x06) - create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x08) - create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0a) - create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0c) - create_sprite(0x007d, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x16) - create_sprite(0x007d, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x17) - create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x19) - create_sprite(0x007d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x1a) - create_sprite(0x007d, EnemySprite.RedBari, 0x00, 0, 0x17, 0x1a) - create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x0a, 0x1c) - create_sprite(0x007d, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x1c) - create_sprite(0x007e, EnemySprite.Bumper, 0x00, 0, 0x17, 0x11) - create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x0e) - create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0f) - create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x07, 0x12) - create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x0a, 0x12) - create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x16) - create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x17, 0x17) - create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x06, 0x07) - create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x08, 0x07) - create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x08) - create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09) - create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x14) - create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x17) - create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x19) - create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x1b) - create_sprite(0x0080, EnemySprite.Zelda, 0x00, 0, 0x16, 0x03) - create_sprite(0x0080, EnemySprite.GreenGuard, 0x00, 0, 0x07, 0x09) - create_sprite(0x0080, EnemySprite.BallNChain, 0x00, 0, 0x1a, 0x09, True, 0xe5) - create_sprite(0x0081, EnemySprite.GreenGuard, 0x1b, 1, 0x0b, 0x0b) - create_sprite(0x0081, EnemySprite.GreenGuard, 0x03, 1, 0x0e, 0x0b) - create_sprite(0x0082, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x05) - create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x10, 0x06) - create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x15, 0x11) - create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x1b, 0x08) - create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x14, 0x10) - create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x05) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x06, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x08, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0a, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0c, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x16, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x17, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x19, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x1a, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.RedBari, 0x00, 0, 0x17, 0x1a, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x0a, 0x1c, 'GT Warp Maze - Main Rails') + create_sprite(0x007d, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x1c, 'GT Petting Zoo') + create_sprite(0x007e, EnemySprite.Bumper, 0x00, 0, 0x17, 0x11, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x0e, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0f, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x07, 0x12, 'Ice Freezors') + create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x0a, 0x12, 'Ice Freezors') + create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x16, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x17, 0x17, 'Ice Tall Hint') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x06, 0x07, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x08, 0x07, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x08, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x14, 'Ice Spikeball') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x17, 'Ice Spikeball') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x19, 'Ice Spikeball') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x1b, 'Ice Spikeball') + create_sprite(0x0080, EnemySprite.Zelda, 0x00, 0, 0x16, 0x03, 'Hyrule Dungeon Cell') + create_sprite(0x0080, EnemySprite.GreenGuard, 0x00, 0, 0x07, 0x09, 'Hyrule Dungeon Cellblock') + create_sprite(0x0080, EnemySprite.BallNChain, 0x00, 0, 0x1a, 0x09, 'Hyrule Dungeon Cellblock', True, 0xe5) + create_sprite(0x0081, EnemySprite.GreenGuard, 0x1b, 1, 0x0b, 0x0b, 'Hyrule Dungeon Guardroom') + create_sprite(0x0081, EnemySprite.GreenGuard, 0x03, 1, 0x0e, 0x0b, 'Hyrule Dungeon Guardroom') + create_sprite(0x0082, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x05, 'Hyrule Dungeon South Abyss') + create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x10, 0x06, 'Hyrule Dungeon South Abyss') + create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x15, 0x11, 'Hyrule Dungeon South Abyss') + create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x1b, 0x08, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x14, 0x10, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x05, 'Desert West Wing') create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06) create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08) - create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x1b, 0x0b) - create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x17, 0x10) - create_sprite(0x0083, EnemySprite.Beamos, 0x00, 0, 0x08, 0x17) - create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x18, 0x18) - create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x1b) - create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x03, 0x05) - create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x1b, 0x05) - create_sprite(0x0084, EnemySprite.Beamos, 0x00, 0, 0x0f, 0x07) - create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x12) - create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x12) - create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x1b) - create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x1b) - create_sprite(0x0085, EnemySprite.DebirandoPit, 0x00, 0, 0x07, 0x0e) - create_sprite(0x0085, EnemySprite.Debirando, 0x00, 0, 0x09, 0x1b) - create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x14, 0x05) - create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x1b, 0x05) - create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x16, 0x08) - create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a) - create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x03, 0x0e) - create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x0c, 0x15) - create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x18) - create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x07, 0x1c) - create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x05) - create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x07) - create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x13, 0x0b) - create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x19) + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x1b, 0x0b, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x17, 0x10, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Beamos, 0x00, 0, 0x08, 0x17, 'Desert West Lobby') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x18, 0x18, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x1b, 'Desert West Wing') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x03, 0x05, 'Desert Left Alcove') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x1b, 0x05, 'Desert Right Alcove') + create_sprite(0x0084, EnemySprite.Beamos, 0x00, 0, 0x0f, 0x07, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x12, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x12, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x1b, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x1b, 'Desert Main Lobby') + create_sprite(0x0085, EnemySprite.DebirandoPit, 0x00, 0, 0x07, 0x0e, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Debirando, 0x00, 0, 0x09, 0x1b, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x14, 0x05, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x1b, 0x05, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x16, 0x08, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x03, 0x0e, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x0c, 0x15, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x18, 'Desert East Lobby') + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x07, 0x1c, 'Desert East Wing') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x05, 'Hera Tridorm') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x07, 'Hera Tridorm') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x13, 0x0b, 'Hera Tridorm') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x19, 'Hera Basement Cage') create_sprite(0x0087, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x17, 0x04) create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x0c) create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x15) - create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x17) - create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x19, 0x18) - create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x19) - create_sprite(0x0087, EnemySprite.SmallKey, 0x00, 0, 0x08, 0x1a) - create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x1c) + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x17, 'Hera Basement Cage') + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x19, 0x18, 'Hera Torches') + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x19, 'Hera Basement Cage') + create_sprite(0x0087, EnemySprite.SmallKey, 0x00, 0, 0x08, 0x1a, 'Hera Basement Cage') + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x1c, 'Hera Torches') create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x10, 0x0a) create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x0f, 0x0b) - create_sprite(0x008b, EnemySprite.Bumper, 0x00, 0, 0x15, 0x07) - create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18) - create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18) - create_sprite(0x008b, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x04) - create_sprite(0x008b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x12) - create_sprite(0x008b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x18) - create_sprite(0x008b, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x18) - create_sprite(0x008b, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x18) - create_sprite(0x008c, EnemySprite.WrongPullSwitch, 0x00, 0, 0x1a, 0x03) + create_sprite(0x008b, EnemySprite.Bumper, 0x00, 0, 0x15, 0x07, 'GT Conveyor Cross') + create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18, 'GT Hookshot South Platform') + create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18, 'GT Hookshot South Platform') + create_sprite(0x008b, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x04, 'GT Conveyor Cross') + create_sprite(0x008b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x12, 'GT Hookshot Mid Platform') # todo: boots may be sufficient - special rule? + create_sprite(0x008b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x18, 'GT Hookshot South Platform') + create_sprite(0x008b, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x18, 'GT Map Room') + create_sprite(0x008b, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x18, 'GT Map Room') + create_sprite(0x008c, EnemySprite.WrongPullSwitch, 0x00, 0, 0x1a, 0x03, 'GT Hope Room') create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x18, 0x05) create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x06) create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x06) create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x0a) create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x0a) - create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x08, 0x08) - create_sprite(0x008c, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08) - create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x09) - create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0b) - create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x17) - create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x17) - create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x18) - create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x1b) - create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x1c) + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x08, 0x08, 'GT Bob\'s Torch',) + create_sprite(0x008c, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08, 'GT Hope Room') + create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x09, 'GT Bob\'s Torch') + create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0b, 'GT Bob\'s Torch') + create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x17, 'GT Big Chest') + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x17, 'GT Bob\'s Room') + create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x18, 'GT Bob\'s Room') + create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x1b, 'GT Big Chest') + create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x1c, 'GT Bob\'s Room') create_sprite(0x008c, EnemySprite.BonkItem, 0x00, 0, 0x09, 0x07) create_sprite(0x008d, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) - create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x04) - create_sprite(0x008d, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08) - create_sprite(0x008d, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x09) - create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x0c) - create_sprite(0x008d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0d) + create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x04, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x09, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x0c, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0d, 'GT Speed Torch Upper') create_sprite(0x008d, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x008d, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x10) - create_sprite(0x008d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x14) - create_sprite(0x008d, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x18) - create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b) - create_sprite(0x008d, EnemySprite.Medusa, 0x00, 0, 0x13, 0x1c) - create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1c) - create_sprite(0x008e, EnemySprite.Freezor, 0x00, 0, 0x1b, 0x02) - create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x05) - create_sprite(0x008e, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x06) - create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x1b, 0x08) - create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x09) - create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x16, 0x0a) - create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x0b) - create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x0b) + create_sprite(0x008d, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x10, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x14, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x18, 'GT Pots n Blocks') + create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.Medusa, 0x00, 0, 0x13, 0x1c, 'GT Speed Torch') + 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.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') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x0b, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x0b, 'Ice Lonely Freezor') create_sprite(0x0090, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x15) - create_sprite(0x0091, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x04) - create_sprite(0x0091, EnemySprite.SpikeBlock, 0x00, 0, 0x1b, 0x0e) + create_sprite(0x0091, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x04, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.SpikeBlock, 0x00, 0, 0x1b, 0x0e, 'Mire Falling Foes') create_sprite(0x0091, 0x08, SpriteType.Overlord, 0, 0x17, 0x0f) - create_sprite(0x0091, EnemySprite.Medusa, 0x00, 0, 0x17, 0x12) - create_sprite(0x0091, EnemySprite.BunnyBeam, 0x00, 0, 0x18, 0x12) - create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x19, 0x12) - create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x18) - create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x09) + create_sprite(0x0091, EnemySprite.Medusa, 0x00, 0, 0x17, 0x12, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.BunnyBeam, 0x00, 0, 0x18, 0x12, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x19, 0x12, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x18, 'Mire Falling Foes') + create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x09, 'Mire Tall Dark and Roomy') create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x0c) - create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x04) - create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x05) - create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08) - create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x17, 0x09) - create_sprite(0x0092, EnemySprite.FourWayShooter, 0x00, 0, 0x15, 0x0f) + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x04, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x05, 'Mire Shooter Rupees') + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08, 'Mire Shooter Rupees') + create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x17, 0x09, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.FourWayShooter, 0x00, 0, 0x15, 0x0f, 'Mire Tall Dark and Roomy') create_sprite(0x0092, 0x16, SpriteType.Overlord, 0, 0x07, 0x12) - create_sprite(0x0092, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x12) - create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x14) - create_sprite(0x0092, EnemySprite.Stalfos, 0x00, 0, 0x0a, 0x16) - create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x1b) + create_sprite(0x0092, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x12, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x14, 'Mire Crystal Right') + create_sprite(0x0092, EnemySprite.Stalfos, 0x00, 0, 0x0a, 0x16, 'Mire Crystal Mid') + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x1b, 'Mire Crystal Right') create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x09, 0x09) create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x16, 0x09) create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x0c) create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0c) - create_sprite(0x0093, EnemySprite.Blob, 0x00, 0, 0x17, 0x0c) - create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x15) - create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x1c) - create_sprite(0x0093, EnemySprite.AntiFairy, 0x00, 0, 0x04, 0x1c) - create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x16, 0x0c) - create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x17, 0x0c) - create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x18, 0x0c) - create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x19, 0x0c) + create_sprite(0x0093, EnemySprite.Blob, 0x00, 0, 0x17, 0x0c, 'Mire Dark Shooters') + create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x15, 'Mire Block X') + create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x1c, 'Mire Block X') + create_sprite(0x0093, EnemySprite.AntiFairy, 0x00, 0, 0x04, 0x1c, 'Mire Block X') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x16, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x17, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x18, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x19, 0x0c, 'GT Conveyor Bridge') create_sprite(0x0095, 0x0b, SpriteType.Overlord, 0, 0x17, 0x1a) - create_sprite(0x0096, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x0b) + create_sprite(0x0096, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x0b, 'GT Torch Cross') create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x15) create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x17) create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x19) create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x1b) create_sprite(0x0097, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x10, 0x13) - create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x09, 0x14) - create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0c, 0x14) - create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0f, 0x14) - create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x08, 0x17) - create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x06) - create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x08) - create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0e, 0x17) - create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x11, 0x17, True, 0xe4) - create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x0d, 0x18) - create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x12, 0x18) - create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0e, 0x19) - create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x19) - create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x10, 0x19) - create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x11, 0x19) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x10, 0x13, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x09, 0x14, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0c, 0x14, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0f, 0x14, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x08, 0x17, 'Mire Lobby') + create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x06, 'Eastern Rupees') + create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x08, 'Eastern Rupees') + create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0e, 0x17, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x11, 0x17, 'Eastern Darkness', True, 0xe4) + create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x0d, 0x18, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x12, 0x18, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0e, 0x19, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x19, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x10, 0x19, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x11, 0x19, 'Eastern Darkness') create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x08) create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x08) create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x14, 0x08) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x05) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x06) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x07) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x05, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x06, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x07, 'GT Spike Crystal Right') create_sprite(0x009b, EnemySprite.FourWayShooter, 0x00, 0, 0x03, 0x08) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x08) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x09) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0a) - create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0b) - create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x1a) - create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x1b) - create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x09) - create_sprite(0x009c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0b, 0x0a) - create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x11, 0x0f) - create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x0e) - create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x12) - create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13) - create_sprite(0x009c, EnemySprite.Firesnake, 0x00, 0, 0x0f, 0x1c) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x08, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x09, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0a, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0b, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x1a, 'GT Warp Maze - Pit Section') + create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x1b, 'GT Warp Maze - Pit Section') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x09, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0b, 0x0a, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x11, 0x0f, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x0e, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x12, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.Firesnake, 0x00, 0, 0x0f, 0x1c, 'GT Invisible Catwalk') create_sprite(0x009d, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x06) - create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x06, 0x04) - create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x04) - create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x18, 0x09) - create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c) - create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0c) - create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x10, 0x14) - create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18) - create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x1c) - create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05) - create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x08) - create_sprite(0x009e, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x08) - create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x08) - create_sprite(0x009e, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12) - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x04, 0x12) - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x06, 0x12) - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x09, 0x12) - create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x0b, 0x12) - create_sprite(0x009f, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x17) - create_sprite(0x009f, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18) - create_sprite(0x00a0, EnemySprite.Medusa, 0x00, 0, 0x03, 0x08) - create_sprite(0x00a0, EnemySprite.AntiFairy, 0x00, 0, 0x0e, 0x08) - create_sprite(0x00a0, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x0c) + create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x06, 0x04, 'GT Compass Room') + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x04, 'GT Crystal Conveyor Left') + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x18, 0x09, 'GT Crystal Conveyor') + create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c, 'GT Compass Room') + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0c, 'GT Crystal Conveyor Corner') + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x10, 0x14, 'GT Invisible Bridges') + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18, 'GT Invisible Bridges') + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x1c, 'GT Invisible Bridges') + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05, 'Ice Backwards Room') + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x08, 'Ice Backwards Room') + 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.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') + create_sprite(0x00a0, EnemySprite.AntiFairy, 0x00, 0, 0x0e, 0x08, 'Mire Antechamber') + create_sprite(0x00a0, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x0c, 'Mire Firesnake Skip') create_sprite(0x00a1, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x08) - create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x18, 0x07) - create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x0b) - create_sprite(0x00a1, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x10) - create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x15) - create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x15) - create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x19) - create_sprite(0x00a1, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x19) - create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x19) + create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x18, 0x07, 'Mire Fishbone') + create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x0b, 'Mire Fishbone') + create_sprite(0x00a1, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x10, 'Mire Fishbone') + 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.Stalfos, 0x00, 0, 0x1b, 0x19, 'Mire South Fish') create_sprite(0x00a4, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x15) create_sprite(0x00a4, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x15) create_sprite(0x00a4, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x15) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x16, 0x05) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x05) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x0b, 0x07) - create_sprite(0x00a5, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x15, 0x09) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x1a, 0x09) - create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x0a) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x16, 0x05, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x05, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07, 'GT Wizzrobes 1') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x0b, 0x07, 'GT Wizzrobes 1') + create_sprite(0x00a5, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x15, 0x09, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x1a, 0x09, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x0a, 'GT Wizzrobes 1') create_sprite(0x00a5, EnemySprite.LaserEyeTop, 0x00, 0, 0x0c, 0x12) create_sprite(0x00a5, EnemySprite.LaserEyeTop, 0x00, 0, 0x12, 0x12) - create_sprite(0x00a5, EnemySprite.RedSpearGuard, 0x00, 0, 0x12, 0x17) - create_sprite(0x00a5, EnemySprite.BlueGuard, 0x00, 0, 0x13, 0x18) + create_sprite(0x00a5, EnemySprite.RedSpearGuard, 0x00, 0, 0x12, 0x17, 'GT Dashing Bridge') + create_sprite(0x00a5, EnemySprite.BlueGuard, 0x00, 0, 0x13, 0x18, 'GT Dashing Bridge') create_sprite(0x00a6, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) - create_sprite(0x00a6, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0e) + create_sprite(0x00a6, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0e, 'GT Moldorm Pit') create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08) create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x09) - create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x0e) - create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x0e) - create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x12) - create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x12) + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x0e, 'Eastern West Wing') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x0e, 'Eastern West Wing') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x12, 'Eastern West Wing') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x12, 'Eastern West Wing') create_sprite(0x00a8, 0x18, SpriteType.Overlord, 0, 0x08, 0x16) - create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x09, 0x05) - create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x05) + create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x09, 0x05, 'Eastern Courtyard') + create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x05, 'Eastern Courtyard') create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x0d, 0x0c) create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x12, 0x0c) create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x0d, 0x12) create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x12, 0x12) - create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x0a, 0x10) - create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x14, 0x10) - create_sprite(0x00aa, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06) - create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x07) - create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b) - create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x0c) - create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x13) - create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x14) + create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x0a, 0x10, 'Eastern Courtyard') + create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x14, 0x10, 'Eastern Courtyard') + create_sprite(0x00aa, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06, 'Eastern Pot Switch') + create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x07, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x0c, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x13, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x14, 'Eastern East Wing') create_sprite(0x00ab, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18) - create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x15) - create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x16) - create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x17) - create_sprite(0x00ab, EnemySprite.Blob, 0x00, 0, 0x06, 0x18) - create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x19) - create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a) - create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x1b) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x15, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x16, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x17, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.Blob, 0x00, 0, 0x06, 0x18, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x19, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x1b, 'Thieves Spike Switch') create_sprite(0x00ac, EnemySprite.Blind, 0x00, 0, 0x19, 0x15) - create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x07) - create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x07) - create_sprite(0x00af, EnemySprite.FirebarCW, 0x00, 0, 0x0a, 0x08) - create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x07, 0x07) - create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07) - create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x18, 0x07) - create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x14, 0x08) - create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x1b, 0x08) - create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x05, 0x0b) - create_sprite(0x00b0, EnemySprite.BallNChain, 0x00, 0, 0x16, 0x14) - create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x04, 0x16) - create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x16) - create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x0a, 0x16) - create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x08, 0x18, True, 0xe4) - create_sprite(0x00b0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x1a) - create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x17, 0x1c) - create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x07) - create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x07) - create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x0e) - create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x11) - create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x0c, 0x17) - create_sprite(0x00b1, EnemySprite.BigSpike, 0x00, 0, 0x1a, 0x17) - create_sprite(0x00b1, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x18) - create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x03, 0x1a) - create_sprite(0x00b1, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x1a) - create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x1c) - create_sprite(0x00b2, EnemySprite.Wizzrobe, 0x00, 1, 0x14, 0x08) - create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x0a) - create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x12, 0x0a) - create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x13, 0x0a) - create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x07, 0x0b) - create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x15) - create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x15) - create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x16) - create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x15, 0x18) - create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x18) - create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x1b) - create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x1b) - create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x14, 0x1b) - create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x1b, 0x1b) - create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x15) - create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15) - create_sprite(0x00b3, EnemySprite.Beamos, 0x00, 0, 0x06, 0x18) - create_sprite(0x00b3, EnemySprite.FourWayShooter, 0x00, 0, 0x0a, 0x1a) - create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x1c) - create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x0a) - create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x0f) - create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x16) - create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x06, 0x07) - create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07) + create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x07, 'Iced T') + create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x07, 'Iced T') + create_sprite(0x00af, EnemySprite.FirebarCW, 0x00, 0, 0x0a, 0x08, 'Ice Catwalk') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x07, 0x07, 'Tower Red Guards') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x18, 0x07, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x14, 0x08, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x1b, 0x08, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x05, 0x0b, 'Tower Red Guards') + create_sprite(0x00b0, EnemySprite.BallNChain, 0x00, 0, 0x16, 0x14, 'Tower Pacifist Run') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x04, 0x16, 'Tower Circle of Pots') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x16, 'Tower Circle of Pots') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x0a, 0x16, 'Tower Circle of Pots') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x08, 0x18, 'Tower Circle of Pots', True, 0xe4) + create_sprite(0x00b0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x1a, 'Tower Pacifist Run') + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x17, 0x1c, 'Tower Pacifist Run') + create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x07, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x07, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x0e, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x11, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x0c, 0x17, 'Mire Square Rail') + create_sprite(0x00b1, EnemySprite.BigSpike, 0x00, 0, 0x1a, 0x17, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x18, 'Mire Square Rail') + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x03, 0x1a, 'Mire Square Rail') + 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.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.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') + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x16, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x15, 0x18, 'Mire Hidden Shooters') + create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x18, 'Mire Hidden Shooters') + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x1b, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x1b, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x14, 0x1b, 'Mire Hidden Shooters') + create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x1b, 0x1b, 'Mire Hidden Shooters') + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x15, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.Beamos, 0x00, 0, 0x06, 0x18, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.FourWayShooter, 0x00, 0, 0x0a, 0x1a, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x1c, 'Mire Spikes') + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x0a, 'TR Dark Ride') + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x0f, 'TR Dark Ride') + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x16, 'TR Dark Ride') + create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x06, 0x07, 'TR Chain Chomps Top') + create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07, 'TR Chain Chomps Top') create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x04) create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x0c, 0x04) create_sprite(0x00b6, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) - create_sprite(0x00b6, EnemySprite.Pokey, 0x00, 0, 0x07, 0x15, True, 0xe4) + create_sprite(0x00b6, EnemySprite.Pokey, 0x00, 0, 0x07, 0x15, 'TR Pokey 1', True, 0xe4) create_sprite(0x00b6, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) - create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x07, 0x1b) - create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x08, 0x1b) - create_sprite(0x00b7, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x04, 0x09) - create_sprite(0x00b7, EnemySprite.RollerVerticalUp, 0x00, 0, 0x04, 0x11) - create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x15, 0x0b) - create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x1b, 0x0b) - create_sprite(0x00b8, EnemySprite.AntiFairyCircle, 0x00, 0, 0x18, 0x0d) - create_sprite(0x00b8, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x13) - create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x16) - create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x1c, 0x16) + create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x07, 0x1b, 'TR Pokey 1') + create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x08, 0x1b, 'TR Pokey 1') + create_sprite(0x00b7, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x04, 0x09, 'TR Roller Room') + create_sprite(0x00b7, EnemySprite.RollerVerticalUp, 0x00, 0, 0x04, 0x11, 'TR Roller Room') + create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x15, 0x0b, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x1b, 0x0b, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.AntiFairyCircle, 0x00, 0, 0x18, 0x0d, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x13, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x16, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x1c, 0x16, 'Eastern Big Key') create_sprite(0x00b9, 0x03, SpriteType.Overlord, 1, 0x11, 0x05) - create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x04) - create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x06) - create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x06) - create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x09) - create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x0c, 0x09) - create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x0a) - create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x08, 0x0c) - create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x1b, 0x04) - create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x06, 0x0a) - create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x16, 0x0a) - create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x0a) - create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x0c) - create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x09, 0x0e) - create_sprite(0x00bb, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x10) - create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x08, 0x14) - create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x15) - create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x16) - create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x17, 0x1a) - create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x06, 0x05) - create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x05) - create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x06) - create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x09) - create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x09, 0x0a) - create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x05, 0x0b) - create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x0a) - create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x11) - create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x16) - create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x17) - create_sprite(0x00bc, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x18) - create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x08, 0x19) - create_sprite(0x00be, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x08) - create_sprite(0x00be, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12) - create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x15) - create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x15) - create_sprite(0x00be, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x16) - create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1a) - create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1a) + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x04, 'Eastern Dark Pots') + create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x06, 'Eastern Dark Square') + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x06, 'Eastern Dark Pots') + create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x09, 'Eastern Dark Square') + create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x0c, 0x09, 'Eastern Dark Square') + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x0a, 'Eastern Dark Pots') + create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x08, 0x0c, 'Eastern Dark Square') + create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x1b, 0x04, 'Thieves Triple Bypass') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x06, 0x0a, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x16, 0x0a, 'Thieves Triple Bypass') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x0a, 'Thieves Triple Bypass') + create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x0c, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x09, 0x0e, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x10, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x08, 0x14, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x15, 'Thieves Spike Track') + create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x16, 'Thieves Spike Track') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x17, 0x1a, 'Thieves Spike Track') + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x06, 0x05, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x05, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x06, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x09, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x09, 0x0a, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x05, 0x0b, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x0a, 'Thieves Hallway') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x11, 'Thieves Hallway') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x16, 'Thieves Hallway') + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x17, 'Thieves Pot Alcove Mid') + create_sprite(0x00bc, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x18, 'Thieves Pot Alcove Mid') + create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x08, 0x19, 'Thieves Pot Alcove Mid') + create_sprite(0x00be, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x08, 'Ice Anti-Fairy') + create_sprite(0x00be, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x15, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x15, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x16, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1a, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1a, 'Ice Switch Room') create_sprite(0x00bf, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18) - create_sprite(0x00bf, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x15) - create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x17, 0x05) - create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x1a, 0x07) - create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x0b, 0x09) - create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x0b, True, 0xe4) - create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x06, 0x0e) - create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x04, 0x18) - create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x1b) - create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x1b, 0x1b) + create_sprite(0x00bf, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x15, 'Ice Refill') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x17, 0x05, 'Tower Dark Archers') + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x1a, 0x07, 'Tower Dark Archers') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x0b, 0x09, 'Tower Dark Pits') + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x0b, 'Tower Dark Archers', True, 0xe4) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x06, 0x0e, 'Tower Dark Pits') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x04, 0x18, 'Tower Dark Pits') + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x1b, 'Tower Dual Statues') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x1b, 0x1b, 'Tower Dual Statues') create_sprite(0x00c1, EnemySprite.CrystalSwitch, 0x00, 0, 0x15, 0x17) create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x14, 0x05) create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x05) - create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b) - create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x0b) - create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x17, 0x15) + create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b, 'Mire Compass Room') + create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x0b, 'Mire Wizzrobe Bypass') + create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x17, 0x15, 'Mire Conveyor Crystal') create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x09, 0x16) create_sprite(0x00c1, 0x14, SpriteType.Overlord, 0, 0x07, 0x18) - create_sprite(0x00c1, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x19) - create_sprite(0x00c1, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x1a) - create_sprite(0x00c1, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x1b, True, 0xe4) - create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x1b, 0x1b) - create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x15, 0x0b) - create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x0c) - create_sprite(0x00c2, EnemySprite.Medusa, 0x00, 0, 0x08, 0x10) - create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x10, 0x12) - create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x19, 0x12) - create_sprite(0x00c2, EnemySprite.BunnyBeam, 0x00, 1, 0x10, 0x14) - create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x16) - create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x16) + create_sprite(0x00c1, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x19, 'Mire Conveyor Crystal') + create_sprite(0x00c1, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x1a, 'Mire Conveyor Crystal') + create_sprite(0x00c1, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x1b, 'Mire Conveyor Crystal', True, 0xe4) + create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x1b, 0x1b, 'Mire Conveyor Crystal') + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x15, 0x0b, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x0c, 'Mire Hub') + 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.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) create_sprite(0x00c3, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x09) create_sprite(0x00c3, EnemySprite.LaserEyeLeft, 0x00, 0, 0x11, 0x0d) create_sprite(0x00c3, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x11) create_sprite(0x00c3, EnemySprite.LaserEyeLeft, 0x00, 0, 0x11, 0x15) create_sprite(0x00c3, 0x0b, SpriteType.Overlord, 0, 0x17, 0x1a) - create_sprite(0x00c3, EnemySprite.AntiFairy, 0x00, 0, 0x0a, 0x1b) - create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x07, 0x1c) + create_sprite(0x00c3, EnemySprite.AntiFairy, 0x00, 0, 0x0a, 0x1b, 'Mire Lone Shooter') + create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x07, 0x1c, 'Mire Lone Shooter') create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x0a) create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x0f) create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x1b) create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x0f, 0x15) - create_sprite(0x00c4, EnemySprite.Pokey, 0x00, 0, 0x0f, 0x0e) - create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x0f) - create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x14) - create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x14) - create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x1a) - create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x1a) + create_sprite(0x00c4, EnemySprite.Pokey, 0x00, 0, 0x0f, 0x0e, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x0f, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x14, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x14, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x1a, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x1a, 'TR Crystal Maze Interior') create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x09) create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0b) create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x0d) create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0f) create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x11) create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x13) - create_sprite(0x00c5, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x15) + create_sprite(0x00c5, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x15, 'TR Dash Bridge') create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x15) - create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x04) - create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x04) - create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x09) - create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x09) - create_sprite(0x00c6, EnemySprite.FloatingSkull, 0x00, 0, 0x10, 0x0e) - create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x14) - create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x17) + create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x04, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x04, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x09, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x09, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.FloatingSkull, 0x00, 0, 0x10, 0x0e, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x14, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x17, 'TR Hub Ledges') create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x15) create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x15) create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x15) @@ -1630,124 +1631,124 @@ def init_vanilla_sprites(): create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x18) create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x18) create_sprite(0x00c8, 0x19, SpriteType.Overlord, 0, 0x17, 0x18) - create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x05) - create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x06) - create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x07) - create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x04) - create_sprite(0x00cb, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x09) - create_sprite(0x00cb, EnemySprite.BlueZazak, 0x00, 1, 0x10, 0x0a) - create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x13, 0x0a) - create_sprite(0x00cb, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x0a) - create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x1c, 0x0a) - create_sprite(0x00cb, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x10) - create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x18, 0x15) - create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x08, 0x17) - create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0b, 0x17) - create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0c, 0x18) - create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x1c) - create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x04) - create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x0b, 0x09) - create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x08, 0x0a) - create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a) - create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x0c, 0x0b) - create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x10, 0x0c) - create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x18, 0x0c) - create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 1, 0x0e, 0x14) - create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x15) - create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x06, 0x16) - create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x09, 0x16) - create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x09, 0x18) - create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x16) - create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x07, 0x1c) - create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05) - create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05) - create_sprite(0x00ce, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x1c, 0x05) - create_sprite(0x00ce, EnemySprite.Statue, 0x00, 0, 0x14, 0x09) - create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x08) - create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x08) - create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x09) - create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x09) - create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x05) - create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x07) - create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07) - create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x15, 0x0b) - create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x09, 0x0c) - create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x08, 0x0f) - create_sprite(0x00d0, EnemySprite.BlueGuard, 0x03, 0, 0x03, 0x10) - create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x14) - create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x16) - create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x06, 0x19) - create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x19) - create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x14, 0x06) - create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x06) - create_sprite(0x00d1, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07) - create_sprite(0x00d1, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x08) - create_sprite(0x00d1, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x09) - create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x0b) - create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x0b) - create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x1b, 0x0b) - create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x18, 0x06) - create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1a, 0x07) - create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x08) - create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x1c, 0x08) - create_sprite(0x00d2, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a) - create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x16, 0x0c) - create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x13, 0x0d) - create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x10) - create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x14, 0x14) - create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1c, 0x14) + 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.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') + create_sprite(0x00cb, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x0a, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x1c, 0x0a, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x10, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x18, 0x15, 'Thieves Ambush') + 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(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') + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x0c, 0x0b, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x10, 0x0c, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x18, 0x0c, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 1, 0x0e, 0x14, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x15, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x06, 0x16, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x09, 0x16, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x09, 0x18, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x16, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x07, 0x1c, 'Thieves BK Corner') + create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x1c, 0x05, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.Statue, 0x00, 0, 0x14, 0x09, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x08, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x08, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x09, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x09, 'Ice Antechamber') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x05, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x07, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07, 'Tower Lone Statue') + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x15, 0x0b, 'Tower Lone Statue') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x09, 0x0c, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x08, 0x0f, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x03, 0, 0x03, 0x10, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x14, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x16, 'Tower Dark Chargers') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x06, 0x19, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x19, 'Tower Dark Chargers') + create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x14, 0x06, 'Mire Neglected Room') + create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x06, 'Mire Neglected Room') + create_sprite(0x00d1, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x08, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x09, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x0b, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x0b, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x1b, 0x0b, 'Mire Neglected Room') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x18, 0x06, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1a, 0x07, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x08, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x1c, 0x08, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x16, 0x0c, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x13, 0x0d, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x10, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x14, 0x14, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1c, 0x14, 'Mire 2') create_sprite(0x00d5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x09) create_sprite(0x00d5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0d) create_sprite(0x00d5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x11) create_sprite(0x00d5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x15) - create_sprite(0x00d5, EnemySprite.HardhatBeetle, 0x00, 0, 0x04, 0x15) + create_sprite(0x00d5, EnemySprite.HardhatBeetle, 0x00, 0, 0x04, 0x15, 'TR Eye Bridge') create_sprite(0x00d6, EnemySprite.LaserEyeTop, 0x00, 0, 0x07, 0x02) create_sprite(0x00d6, EnemySprite.Medusa, 0x00, 0, 0x03, 0x16) create_sprite(0x00d6, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x16) - create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x05) - create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x05) - create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x17, 0x09) - create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x18, 0x09) - create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x16, 0x0a) - create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x19, 0x0a) - create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x16, 0x0b) - create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x19, 0x0b) - create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x14) - create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x16) - create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x1b) + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x05, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x05, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x17, 0x09, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x18, 0x09, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x16, 0x0a, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x19, 0x0a, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x16, 0x0b, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x19, 0x0b, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x14, 'Eastern Single Eyegore') + create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x16, 'Eastern Single Eyegore') + create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x1b, 'Eastern Single Eyegore') create_sprite(0x00d9, 0x02, SpriteType.Overlord, 0, 0x0c, 0x14) - create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x15) - create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x18) - create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x1b) - create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x18) - create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x18) - create_sprite(0x00db, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x04) - create_sprite(0x00db, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a) - create_sprite(0x00db, EnemySprite.RedZazak, 0x00, 1, 0x17, 0x0b) - create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x0c) - create_sprite(0x00db, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10) - create_sprite(0x00db, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x10) - create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x15) - create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x0a) - create_sprite(0x00dc, EnemySprite.SparkCCW, 0x00, 1, 0x0e, 0x0a) - create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x0c) - create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10) - create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x16, 0x10) - create_sprite(0x00dc, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x16) - create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x16) - create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x17) - create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 1, 0x16, 0x17) - create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x1c) - create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0f, 0x1c) + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x15, 'Eastern False Switches') + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x18, 'Eastern False Switches') + 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.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') + create_sprite(0x00db, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x10, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x15, 'Thieves Lobby') + create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x0a, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.SparkCCW, 0x00, 1, 0x0e, 0x0a, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x0c, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x16, 0x10, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x16, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x16, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x17, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 1, 0x16, 0x17, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x1c, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0f, 0x1c, 'Thieves Compass Room') create_sprite(0x00de, EnemySprite.KholdstareShell, 0x00, 0, 0x17, 0x05) create_sprite(0x00de, EnemySprite.FallingIce, 0x00, 0, 0x17, 0x05) create_sprite(0x00de, EnemySprite.Kholdstare, 0x00, 0, 0x17, 0x05) - create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x15) - create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x16) - create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x04, 0x06) - create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x0b, 0x06) - create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x06) - create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x09) + create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x15, 'Paradox Cave') + create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x16, 'Paradox Cave') + create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x04, 0x06, 'Tower Gold Knights') + create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x0b, 0x06, 'Tower Gold Knights') + create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x06, 'Tower Room 03') + create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x09, 'Tower Room 03') create_sprite(0x00e1, EnemySprite.HeartPiece, 0x00, 0, 0x17, 0x0d) create_sprite(0x00e1, EnemySprite.AdultNpc, 0x00, 1, 0x07, 0x12) create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06) @@ -1756,86 +1757,86 @@ def init_vanilla_sprites(): create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) create_sprite(0x00e2, EnemySprite.HeartPiece, 0x00, 0, 0x13, 0x10) create_sprite(0x00e3, EnemySprite.MagicBat, 0x00, 1, 0x17, 0x05) - create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x19, 0x07) - create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x18, 0x08) - create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x17, 0x09) - create_sprite(0x00e4, EnemySprite.OldMan, 0x00, 0, 0x06, 0x16) - create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x09) - create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x10, 0x09) - create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x09) - create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0e) - create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x12) - create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x12) - create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0b) - create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x17, 0x0f) - create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x13, 0x13) - create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0f, 0x17) - create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0b, 0x1b) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x10, 0x04) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x13, 0x04) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0b) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0c) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0d) - create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0f) - create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x05) - create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x08) - create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c) - create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0c) + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x19, 0x07, 'Old Man House') + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x18, 0x08, 'Old Man House') + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x17, 0x09, 'Old Man House') + create_sprite(0x00e4, EnemySprite.OldMan, 0x00, 0, 0x06, 0x16, 'Old Man House') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x09, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x10, 0x09, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x09, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0e, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x12, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x12, 'Old Man House Back') + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0b, 'Death Mountain Return Cave (left)') + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x17, 0x0f, 'Death Mountain Return Cave (left)') + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x13, 0x13, 'Death Mountain Return Cave (left)') + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0f, 0x17, 'Death Mountain Return Cave (left)') + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0b, 0x1b, 'Death Mountain Return Cave (left)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x10, 0x04, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x13, 0x04, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0b, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0c, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0d, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0f, 'Death Mountain Return Cave (right)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x05, 'Superbunny Cave (Bottom)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x08, 'Superbunny Cave (Bottom)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c, 'Superbunny Cave (Bottom)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0c, 'Superbunny Cave (Bottom)') create_sprite(0x00ea, EnemySprite.HeartPiece, 0x00, 0, 0x0b, 0x0b) create_sprite(0x00eb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x14) - create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x10, 0x04) - create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x0e) - create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x1c) - create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x0b) - create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x0c) - create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09) - create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x0a) - create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x0a) + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x10, 0x04, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x0e, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x1c, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x0b, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x0c, 'Spiral Cave (Top)') + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09, 'Paradox Cave Chest Area') + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x0a, 'Paradox Cave Chest Area') + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x0a, 'Paradox Cave Chest Area') create_sprite(0x00ef, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x06) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x03) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x10, 0x03) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x08, 0x04) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0a, 0x04) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x07) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x03, 0x0a) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x05, 0x0a) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0e, 0x0c) - create_sprite(0x00f0, EnemySprite.OldMan, 0x00, 0, 0x1b, 0x10) - create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x13, 0x13) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x19, 0x10) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1c, 0x10) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x18, 0x11) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1d, 0x11) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x17, 0x12) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1e, 0x12) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x06, 0x1b) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x09, 0x1b) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x07, 0x1c) - create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x08, 0x1c) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x03, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x10, 0x03, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x08, 0x04, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0a, 0x04, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x07, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x03, 0x0a, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x05, 0x0a, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0e, 0x0c, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.OldMan, 0x00, 0, 0x1b, 0x10, 'Old Man Cave') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x13, 0x13, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x19, 0x10, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1c, 0x10, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x18, 0x11, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1d, 0x11, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x17, 0x12, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1e, 0x12, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x06, 0x1b, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x09, 0x1b, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x07, 0x1c, 'Old Man Cave') + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x08, 0x1c, 'Old Man Cave') create_sprite(0x00f3, EnemySprite.Grandma, 0x00, 0, 0x06, 0x14) create_sprite(0x00f4, EnemySprite.ArgueBros, 0x00, 0, 0x17, 0x14) create_sprite(0x00f5, EnemySprite.ArgueBros, 0x00, 0, 0x08, 0x14) - create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x05) - create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x15, 0x0f) - create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x11, 0x13) - create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x17) + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x05, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x15, 0x0f, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x11, 0x13, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x17, 'Spectacle Rock Cave (Bottom)') 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) - create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12) - create_sprite(0x00fd, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0e) - create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x08) + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0a, 'Bumper Cave') + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12, 'Bumper Cave') + 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) create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) - create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x11) - create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x16, 0x12) - create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x16) - create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x16) - create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x12) - create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x18) + create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x11, 'Fairy Ascension Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x16, 0x12, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x16, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x16, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x12, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x18, 'Spiral Cave (Bottom)') create_sprite(0x00ff, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x04) create_sprite(0x0100, EnemySprite.Shopkeeper, 0x00, 0, 0x0b, 0x1b) create_sprite(0x0101, EnemySprite.RupeePull, 0x00, 0, 0x08, 0x13) @@ -1847,12 +1848,12 @@ def init_vanilla_sprites(): create_sprite(0x0105, EnemySprite.Wiseman, 0x00, 0, 0x07, 0x18) create_sprite(0x0106, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x1b) create_sprite(0x0107, EnemySprite.BonkItem, 0x00, 0, 0x03, 0x15) - create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x1b) - create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x18, 0x1b) - create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x16) - create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x0c, 0x16) - create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x19) - create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x06, 0x1a) + create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x1b, 'Light World Bomb Hut') + create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x18, 0x1b, 'Light World Bomb Hut') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x16, 'Chicken House') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x0c, 0x16, 'Chicken House') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x19, 'Chicken House') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x06, 0x1a, 'Chicken House') create_sprite(0x0109, EnemySprite.MagicShopAssistant, 0x00, 0, 0x0a, 0x1b) create_sprite(0x010a, EnemySprite.Wiseman, 0x00, 0, 0x19, 0x04) create_sprite(0x010b, EnemySprite.WrongPullSwitch, 0x00, 0, 0x0f, 0x03) @@ -1861,17 +1862,17 @@ def init_vanilla_sprites(): create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x12, 0x07) create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x0f, 0x09) create_sprite(0x010b, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x12, 0x03) - create_sprite(0x010b, EnemySprite.AntiFairy, 0x00, 0, 0x0d, 0x07) + create_sprite(0x010b, EnemySprite.AntiFairy, 0x00, 0, 0x0d, 0x07, 'Dam') create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07) create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08) create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) - create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x07, 0x14) - create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x08, 0x14) - create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x14) - create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1a) - create_sprite(0x010d, EnemySprite.SparkCW, 0x00, 0, 0x05, 0x16) - create_sprite(0x010d, EnemySprite.SparkCCW, 0x00, 0, 0x0a, 0x16) + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x07, 0x14, 'Mimic Cave') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x08, 0x14, 'Mimic Cave') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x14, 'Mimic Cave') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1a, 'Mimic Cave') + create_sprite(0x010d, EnemySprite.SparkCW, 0x00, 0, 0x05, 0x16, 'Mimic Cave') + create_sprite(0x010d, EnemySprite.SparkCCW, 0x00, 0, 0x0a, 0x16, 'Mimic Cave') create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x06, 0x06) create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x18, 0x06) create_sprite(0x010f, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x15) @@ -1906,10 +1907,10 @@ def init_vanilla_sprites(): create_sprite(0x0121, EnemySprite.Smithy, 0x00, 0, 0x04, 0x17) create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x07, 0x18) create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x17, 0x18) - create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x16) - create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x16) - create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x17) - create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x1a) + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x16, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x16, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x17, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x1a, 'Mini Moldorm Cave') create_sprite(0x0123, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x05) create_sprite(0x0124, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) create_sprite(0x0125, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) From 037a9479d8d9abf0471bc2e18d8086b4e46a922e Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 26 Sep 2022 14:07:41 -0600 Subject: [PATCH 004/158] UW Enemizer Work --- Fill.py | 85 +++- ItemList.py | 4 + Items.py | 3 +- Main.py | 4 +- PotShuffle.py | 9 +- Regions.py | 436 ++++++++--------- Rom.py | 8 +- Utils.py | 12 +- source/dungeon/EnemyList.py | 441 ++++++++++++++++- source/dungeon/RoomConstants.py | 266 +++++++++++ source/dungeon/RoomHeader.py | 326 +++++++++++++ source/dungeon/RoomList.py | 6 + source/dungeon/__init__.py | 1 + source/enemizer/Enemizer.py | 253 ++++++++++ source/enemizer/EnemizerTestHarness.py | 19 + source/enemizer/SpriteSheets.py | 630 +++++++++++++++++++++++++ source/enemizer/__init__.py | 1 + source/item/FillUtil.py | 8 +- source/rom/DataTables.py | 45 ++ source/rom/__init__.py | 1 + 20 files changed, 2287 insertions(+), 271 deletions(-) create mode 100644 source/dungeon/RoomConstants.py create mode 100644 source/dungeon/RoomHeader.py create mode 100644 source/dungeon/__init__.py create mode 100644 source/enemizer/Enemizer.py create mode 100644 source/enemizer/EnemizerTestHarness.py create mode 100644 source/enemizer/SpriteSheets.py create mode 100644 source/enemizer/__init__.py create mode 100644 source/rom/DataTables.py create mode 100644 source/rom/__init__.py diff --git a/Fill.py b/Fill.py index f695bd9a..4ebd4c04 100644 --- a/Fill.py +++ b/Fill.py @@ -9,7 +9,7 @@ from BaseClasses import CollectionState, FillError, LocationType from Items import ItemFactory from Regions import shop_to_location_table, retro_shops from source.item.FillUtil import filter_locations, classify_major_items, replace_trash_item, vanilla_fallback -from source.item.FillUtil import filter_pot_locations, valid_pot_items +from source.item.FillUtil import filter_special_locations, valid_pot_items def get_dungeon_item_pool(world): @@ -360,22 +360,53 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # get items to distribute classify_major_items(world) - # handle pot shuffle - pots_used = False - pot_item_pool = collections.defaultdict(list) + # handle pot/drop shuffle + chicken_pool = collections.defaultdict(list) + big_magic_pool = collections.defaultdict(list) + fairy_pool = collections.defaultdict(list) + for item in world.itempool: - if item.name in ['Chicken', 'Big Magic']: # can only fill these in that players world - pot_item_pool[item.player].append(item) - for player, pot_pool in pot_item_pool.items(): - if pot_pool: - for pot_item in pot_pool: - world.itempool.remove(pot_item) - pot_locations = [location for location in fill_locations - if location.type == LocationType.Pot and location.player == player] - pot_locations = filter_pot_locations(pot_locations, world) - fast_fill_helper(world, pot_pool, pot_locations) - pots_used = True - if pots_used: + # can only fill these in that players world + if item.name == 'Chicken': + chicken_pool[item.player].append(item) + elif item.name == 'Big Magic': + big_magic_pool[item.player].append(item) + elif item.name == 'Fairy': + fairy_pool[item.player].append(item) + + def fast_fill_certain_items(item_pool, location_types, filter_locations, matcher): + flag = False + for player, pool in item_pool.items(): + if pool: + for certain_item in pool: + world.itempool.remove(certain_item) + cand_locations = [location for location in fill_locations + if location.type in location_types and location.player == player] + locations = filter_locations(cand_locations, world, matcher) + fast_fill_helper(world, pool, locations) + flag = True + return flag + + def chicken_matcher(l): + return l.pot and l.pot.item == PotItem.Chicken + + def magic_matcher(l): + if l.drop: + pack = enemy_stats[l.drop.kind].prize_pack + return pack in {3,7} if isinstance(pack, int) else (3 in pack or 7 in pack) + return l.pot and l.pot.item == PotItem.BigMagic + + def fairy_matcher(l): + if l.drop: + pack = enemy_stats[l.drop.kind].prize_pack + return pack == 7 if isinstance(pack, int) else 7 in pack + return False + + reshuffle = fast_fill_certain_items(chicken_pool, [LocationType.Pot], filter_special_locations, chicken_matcher) + reshuffle |= fast_fill_certain_items(fairy_pool, [LocationType.Drop], filter_special_locations, fairy_matcher) + reshuffle |= fast_fill_certain_items(big_magic_pool, [LocationType.Pot, LocationType.Drop], + filter_special_locations, magic_matcher) + if reshuffle: fill_locations = world.get_unfilled_locations() random.shuffle(fill_locations) @@ -459,6 +490,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None if world.players > 1: fast_fill_pot_for_multiworld(world, restitempool, fill_locations) + # todo: fast fill drops? if world.algorithm == 'vanilla_fill': fast_vanilla_fill(world, restitempool, fill_locations) else: @@ -468,7 +500,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None unfilled = [location.name for location in fill_locations] if unplaced or unfilled: logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled) - ensure_good_pots(world) + ensure_good_items(world) def calc_trash_locations(world, player): @@ -484,7 +516,7 @@ def calc_trash_locations(world, player): return gt_count, total_count -def ensure_good_pots(world, write_skips=False): +def ensure_good_items(world, write_skips=False): for loc in world.get_locations(): if loc.item is None: loc.item = ItemFactory('Nothing', loc.player) @@ -494,12 +526,23 @@ def ensure_good_pots(world, write_skips=False): loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player) # can be placed here by multiworld balancing or shop balancing # change it to something normal for the player it got swapped to - elif (loc.item.name in {'Chicken', 'Big Magic'} - and (loc.type != LocationType.Pot or loc.item.player != loc.player)): + elif loc.item.name == 'Chicken' and (loc.type != LocationType.Pot or loc.item.player != loc.player): if loc.type == LocationType.Pot: loc.item.player = loc.player else: loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) + elif (loc.item.name == 'Big Magic' + and (loc.type not in [LocationType.Pot, LocationType.Drop] or loc.item.player != loc.player)): + if loc.type in [LocationType.Pot, LocationType.Drop]: + loc.item.player = loc.player + else: + loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) + elif (loc.item.name == 'Fairy' + and (loc.type != LocationType.Drop or loc.item.player != loc.player)): + if loc.type == LocationType.Drop: + loc.item.player = loc.player + else: + loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) # do the arrow retro check if world.bow_mode[loc.item.player].startswith('retro') and loc.item.name in {'Arrows (5)', 'Arrows (10)'}: loc.item = ItemFactory('Rupees (5)', loc.item.player) @@ -510,7 +553,7 @@ def ensure_good_pots(world, write_skips=False): invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)', - 'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic'} + 'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic', 'Fairy': 'Small Heart'} def fast_fill_helper(world, item_pool, fill_locations): diff --git a/ItemList.py b/ItemList.py index 2e7f0040..67e06644 100644 --- a/ItemList.py +++ b/ItemList.py @@ -10,6 +10,7 @@ from Fill import FillError, fill_restrictive, get_dungeon_item_pool, is_dungeon_ from PotShuffle import vanilla_pots from Items import ItemFactory +from source.dungeon.EnemyList import add_drop_contents from source.item.FillUtil import trash_items, pot_items import source.classes.constants as CONST @@ -423,6 +424,9 @@ def generate_itempool(world, player): if world.pottery[player] not in ['none', 'keys'] and not skip_pool_adjustments: add_pot_contents(world, player) + if world.dropshuffle[player] == 'underworld' and not skip_pool_adjustments: + add_drop_contents(world, player) + take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', diff --git a/Items.py b/Items.py index da9d7385..8c27bf88 100644 --- a/Items.py +++ b/Items.py @@ -1,5 +1,3 @@ -import logging - from BaseClasses import Item @@ -176,6 +174,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), + 'Fairy': (False, False, None, 0x5A, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), 'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), diff --git a/Main.py b/Main.py index d77f6f58..2db44f17 100644 --- a/Main.py +++ b/Main.py @@ -23,7 +23,7 @@ from DoorShuffle import link_doors, connect_portal, link_doors_prep from RoomData import create_rooms from Rules import set_rules from Dungeons import create_dungeons -from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots +from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_items from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items from Utils import output_path, parse_player_names @@ -304,7 +304,7 @@ def main(args, seed=None, fish=None): customize_shops(world, player) if args.algorithm in ['balanced', 'equitable']: balance_money_progression(world) - ensure_good_pots(world, True) + ensure_good_items(world, True) if args.print_custom_yaml: world.settings.record_item_placements(world) diff --git a/PotShuffle.py b/PotShuffle.py index fd59f6cc..ec6536ef 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -1041,11 +1041,12 @@ class PotSecretTable(object): self.multiworld_count = 0 def write_pot_data_to_rom(self, rom, colorize): - pointer_address = 0x140000 # pots currently in bank 28 - pointer_offset = 0x128 * 2 - empty_address = (pointer_address + pointer_offset) + pointer_address = snes_to_pc(0x09D87E) # pots currently in bank 9 + data_bank_address = snes_to_pc(0x09DACE) + # pointer_offset = 0x128 * 2 + empty_address = data_bank_address empty_pointer = pc_to_snes(empty_address) & 0xFFFF - data_pointer = pointer_address + pointer_offset + 2 + data_pointer = data_bank_address + 2 for room in range(0, 0x128): if room in self.room_map: list_idx = 0 diff --git a/Regions.py b/Regions.py index 23dcf79b..203a564a 100644 --- a/Regions.py +++ b/Regions.py @@ -1179,222 +1179,224 @@ flooded_keys_reverse = { 'Swamp Palace - Trench 1 Pot Key': 'Trench 1 Switch', 'Swamp Palace - Trench 2 Pot Key': 'Trench 2 Switch' } -location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), - 'Bottle Merchant': (0x2eb18, 0x186339, False, 'with a merchant'), - 'Flute Spot': (0x18014a, 0x18633d, False, 'underground'), - 'Sunken Treasure': (0x180145, 0x186354, False, 'underwater'), - 'Purple Chest': (0x33d68, 0x186359, False, 'from a box'), - "Blind's Hideout - Top": (0xeb0f, 0x1862e3, False, 'in a basement'), - "Blind's Hideout - Left": (0xeb12, 0x1862e6, False, 'in a basement'), - "Blind's Hideout - Right": (0xeb15, 0x1862e9, False, 'in a basement'), - "Blind's Hideout - Far Left": (0xeb18, 0x1862ec, False, 'in a basement'), - "Blind's Hideout - Far Right": (0xeb1b, 0x1862ef, False, 'in a basement'), - "Link's Uncle": (0x2df45, 0x18635f, False, 'with your uncle'), - 'Secret Passage': (0xe971, 0x186145, False, 'near your uncle'), - 'King Zora': (0xee1c3, 0x186360, False, 'at a high price'), - "Zora's Ledge": (0x180149, 0x186358, False, 'near Zora'), - 'Waterfall Fairy - Left': (0xe9b0, 0x186184, False, 'near a fairy'), - 'Waterfall Fairy - Right': (0xe9d1, 0x1861a5, False, 'near a fairy'), - "King's Tomb": (0xe97a, 0x18614e, False, 'alone in a cave'), - 'Floodgate Chest': (0xe98c, 0x186160, False, 'in the dam'), - "Link's House": (0xe9bc, 0x186190, False, 'in your home'), - 'Kakariko Tavern': (0xe9ce, 0x1861a2, False, 'in the bar'), - 'Chicken House': (0xe9e9, 0x1861bd, False, 'near poultry'), - "Aginah's Cave": (0xe9f2, 0x1861c6, False, 'with Aginah'), - "Sahasrahla's Hut - Left": (0xea82, 0x186256, False, 'near the elder'), - "Sahasrahla's Hut - Middle": (0xea85, 0x186259, False, 'near the elder'), - "Sahasrahla's Hut - Right": (0xea88, 0x18625c, False, 'near the elder'), - 'Sahasrahla': (0x2f1fc, 0x186365, False, 'with the elder'), - 'Kakariko Well - Top': (0xea8e, 0x186262, False, 'in a well'), - 'Kakariko Well - Left': (0xea91, 0x186265, False, 'in a well'), - 'Kakariko Well - Middle': (0xea94, 0x186268, False, 'in a well'), - 'Kakariko Well - Right': (0xea97, 0x18626b, False, 'in a well'), - 'Kakariko Well - Bottom': (0xea9a, 0x18626e, False, 'in a well'), - 'Blacksmith': (0x18002a, 0x186366, False, 'with the smith'), - 'Magic Bat': (0x180015, 0x18635e, False, 'with the bat'), - 'Sick Kid': (0x339cf, 0x186367, False, 'with the sick'), - 'Hobo': (0x33e7d, 0x186368, False, 'with the hobo'), - 'Lost Woods Hideout': (0x180000, 0x186348, False, 'near a thief'), - 'Lumberjack Tree': (0x180001, 0x186349, False, 'in a hole'), - 'Cave 45': (0x180003, 0x18634b, False, 'alone in a cave'), - 'Graveyard Cave': (0x180004, 0x18634c, False, 'alone in a cave'), - 'Checkerboard Cave': (0x180005, 0x18634d, False, 'alone in a cave'), - 'Mini Moldorm Cave - Far Left': (0xeb42, 0x186316, False, 'near Moldorms'), - 'Mini Moldorm Cave - Left': (0xeb45, 0x186319, False, 'near Moldorms'), - 'Mini Moldorm Cave - Right': (0xeb48, 0x18631c, False, 'near Moldorms'), - 'Mini Moldorm Cave - Far Right': (0xeb4b, 0x18631f, False, 'near Moldorms'), - 'Mini Moldorm Cave - Generous Guy': (0x180010, 0x18635a, False, 'near Moldorms'), - 'Ice Rod Cave': (0xeb4e, 0x186322, False, 'in a frozen cave'), - 'Bonk Rock Cave': (0xeb3f, 0x186313, False, 'alone in a cave'), - 'Library': (0x180012, 0x18635c, False, 'near books'), - 'Potion Shop': (0x180014, 0x18635d, False, 'near potions'), - 'Lake Hylia Island': (0x180144, 0x186353, False, 'on an island'), - 'Maze Race': (0x180142, 0x186351, False, 'at the race'), - 'Desert Ledge': (0x180143, 0x186352, False, 'in the desert'), - 'Desert Palace - Big Chest': (0xe98f, 0x186163, False, 'in Desert Palace'), - 'Desert Palace - Torch': (0x180160, 0x186362, False, 'in Desert Palace'), - 'Desert Palace - Map Chest': (0xe9b6, 0x18618a, False, 'in Desert Palace'), - 'Desert Palace - Compass Chest': (0xe9cb, 0x18619f, False, 'in Desert Palace'), - 'Desert Palace - Big Key Chest': (0xe9c2, 0x186196, False, 'in Desert Palace'), - 'Desert Palace - Boss': (0x180151, 0x18633f, False, 'with Lanmolas'), - 'Eastern Palace - Compass Chest': (0xe977, 0x18614b, False, 'in Eastern Palace'), - 'Eastern Palace - Big Chest': (0xe97d, 0x186151, False, 'in Eastern Palace'), - 'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186187, False, 'in Eastern Palace'), - 'Eastern Palace - Big Key Chest': (0xe9b9, 0x18618d, False, 'in Eastern Palace'), - 'Eastern Palace - Map Chest': (0xe9f5, 0x1861c9, False, 'in Eastern Palace'), - 'Eastern Palace - Boss': (0x180150, 0x18633e, False, 'with the Armos'), - 'Master Sword Pedestal': (0x289b0, 0x186369, False, 'at the pedestal'), - 'Hyrule Castle - Boomerang Chest': (0xe974, 0x186148, False, 'in Hyrule Castle'), - 'Hyrule Castle - Map Chest': (0xeb0c, 0x1862e0, False, 'in Hyrule Castle'), - "Hyrule Castle - Zelda's Chest": (0xeb09, 0x1862dd, False, 'in Hyrule Castle'), - 'Sewers - Dark Cross': (0xe96e, 0x186142, False, 'in the sewers'), - 'Sewers - Secret Room - Left': (0xeb5d, 0x186331, False, 'in the sewers'), - 'Sewers - Secret Room - Middle': (0xeb60, 0x186334, False, 'in the sewers'), - 'Sewers - Secret Room - Right': (0xeb63, 0x186337, False, 'in the sewers'), - 'Sanctuary': (0xea79, 0x18624d, False, 'in Sanctuary'), - 'Castle Tower - Room 03': (0xeab5, 0x186289, False, 'in Castle Tower'), - 'Castle Tower - Dark Maze': (0xeab2, 0x186286, False, 'in Castle Tower'), - 'Old Man': (0xf69fa, 0x186364, False, 'with the old man'), - 'Spectacle Rock Cave': (0x180002, 0x18634a, False, 'alone in a cave'), - 'Paradox Cave Lower - Far Left': (0xeb2a, 0x1862fe, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Left': (0xeb2d, 0x186301, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Right': (0xeb30, 0x186304, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Far Right': (0xeb33, 0x186307, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Middle': (0xeb36, 0x18630a, False, 'in a cave with seven chests'), - 'Paradox Cave Upper - Left': (0xeb39, 0x18630d, False, 'in a cave with seven chests'), - 'Paradox Cave Upper - Right': (0xeb3c, 0x186310, False, 'in a cave with seven chests'), - 'Spiral Cave': (0xe9bf, 0x186193, False, 'in Spiral Cave'), - 'Ether Tablet': (0x180016, 0x18633b, False, 'at a monolith'), - 'Spectacle Rock': (0x180140, 0x18634f, False, 'atop a rock'), - 'Tower of Hera - Basement Cage': (0x180162, 0x18633a, False, 'in Tower of Hera'), - 'Tower of Hera - Map Chest': (0xe9ad, 0x186181, False, 'in Tower of Hera'), - 'Tower of Hera - Big Key Chest': (0xe9e6, 0x1861ba, False, 'in Tower of Hera'), - 'Tower of Hera - Compass Chest': (0xe9fb, 0x1861cf, False, 'in Tower of Hera'), - 'Tower of Hera - Big Chest': (0xe9f8, 0x1861cc, False, 'in Tower of Hera'), - 'Tower of Hera - Boss': (0x180152, 0x186340, False, 'with Moldorm'), - 'Pyramid': (0x180147, 0x186356, False, 'on the Pyramid'), - 'Catfish': (0xee185, 0x186361, False, 'with a catfish'), - 'Stumpy': (0x330c7, 0x18636a, False, 'with tree boy'), - 'Digging Game': (0x180148, 0x186357, False, 'underground'), - 'Bombos Tablet': (0x180017, 0x18633c, False, 'at a monolith'), - 'Hype Cave - Top': (0xeb1e, 0x1862f2, False, 'near a bat-like man'), - 'Hype Cave - Middle Right': (0xeb21, 0x1862f5, False, 'near a bat-like man'), - 'Hype Cave - Middle Left': (0xeb24, 0x1862f8, False, 'near a bat-like man'), - 'Hype Cave - Bottom': (0xeb27, 0x1862fb, False, 'near a bat-like man'), - 'Hype Cave - Generous Guy': (0x180011, 0x18635b, False, 'with a bat-like man'), - 'Peg Cave': (0x180006, 0x18634e, False, 'alone in a cave'), - 'Pyramid Fairy - Left': (0xe980, 0x186154, False, 'near a fairy'), - 'Pyramid Fairy - Right': (0xe983, 0x186157, False, 'near a fairy'), - 'Brewery': (0xe9ec, 0x1861c0, False, 'alone in a home'), - 'C-Shaped House': (0xe9ef, 0x1861c3, False, 'alone in a home'), - 'Chest Game': (0xeda8, 0x18636b, False, 'as a prize'), - 'Bumper Cave Ledge': (0x180146, 0x186355, False, 'on a ledge'), - 'Mire Shed - Left': (0xea73, 0x186247, False, 'near sparks'), - 'Mire Shed - Right': (0xea76, 0x18624a, False, 'near sparks'), - 'Superbunny Cave - Top': (0xea7c, 0x186250, False, 'in a connection'), - 'Superbunny Cave - Bottom': (0xea7f, 0x186253, False, 'in a connection'), - 'Spike Cave': (0xea8b, 0x18625f, False, 'beyond spikes'), - 'Hookshot Cave - Top Right': (0xeb51, 0x186325, False, 'across pits'), - 'Hookshot Cave - Top Left': (0xeb54, 0x186328, False, 'across pits'), - 'Hookshot Cave - Bottom Right': (0xeb5a, 0x18632e, False, 'across pits'), - 'Hookshot Cave - Bottom Left': (0xeb57, 0x18632b, False, 'across pits'), - 'Floating Island': (0x180141, 0x186350, False, 'on an island'), - 'Mimic Cave': (0xe9c5, 0x186199, False, 'in a cave of mimicry'), - 'Swamp Palace - Entrance': (0xea9d, 0x186271, False, 'in Swamp Palace'), - 'Swamp Palace - Map Chest': (0xe986, 0x18615a, False, 'in Swamp Palace'), - 'Swamp Palace - Big Chest': (0xe989, 0x18615d, False, 'in Swamp Palace'), - 'Swamp Palace - Compass Chest': (0xeaa0, 0x186274, False, 'in Swamp Palace'), - 'Swamp Palace - Big Key Chest': (0xeaa6, 0x18627a, False, 'in Swamp Palace'), - 'Swamp Palace - West Chest': (0xeaa3, 0x186277, False, 'in Swamp Palace'), - 'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x18627d, False, 'in Swamp Palace'), - 'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186280, False, 'in Swamp Palace'), - 'Swamp Palace - Waterfall Room': (0xeaaf, 0x186283, False, 'in Swamp Palace'), - 'Swamp Palace - Boss': (0x180154, 0x186342, False, 'with Arrghus'), - "Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves Town"), - "Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves Town"), - "Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves Town"), - "Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves Town"), - "Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves Town"), - "Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves Town"), - "Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves Town"), - "Thieves' Town - Boss": (0x180156, 0x186344, False, 'with Blind'), - 'Skull Woods - Compass Chest': (0xe992, 0x186166, False, 'in Skull Woods'), - 'Skull Woods - Map Chest': (0xe99b, 0x18616f, False, 'in Skull Woods'), - 'Skull Woods - Big Chest': (0xe998, 0x18616c, False, 'in Skull Woods'), - 'Skull Woods - Pot Prison': (0xe9a1, 0x186175, False, 'in Skull Woods'), - 'Skull Woods - Pinball Room': (0xe9c8, 0x18619c, False, 'in Skull Woods'), - 'Skull Woods - Big Key Chest': (0xe99e, 0x186172, False, 'in Skull Woods'), - 'Skull Woods - Bridge Room': (0xe9fe, 0x1861d2, False, 'near Mothula'), - 'Skull Woods - Boss': (0x180155, 0x186343, False, 'with Mothula'), - 'Ice Palace - Compass Chest': (0xe9d4, 0x1861a8, False, 'in Ice Palace'), - 'Ice Palace - Freezor Chest': (0xe995, 0x186169, False, 'in Ice Palace'), - 'Ice Palace - Big Chest': (0xe9aa, 0x18617e, False, 'in Ice Palace'), - 'Ice Palace - Iced T Room': (0xe9e3, 0x1861b7, False, 'in Ice Palace'), - 'Ice Palace - Spike Room': (0xe9e0, 0x1861b4, False, 'in Ice Palace'), - 'Ice Palace - Big Key Chest': (0xe9a4, 0x186178, False, 'in Ice Palace'), - 'Ice Palace - Map Chest': (0xe9dd, 0x1861b1, False, 'in Ice Palace'), - 'Ice Palace - Boss': (0x180157, 0x186345, False, 'with Kholdstare'), - 'Misery Mire - Big Chest': (0xea67, 0x18623b, False, 'in Misery Mire'), - 'Misery Mire - Map Chest': (0xea6a, 0x18623e, False, 'in Misery Mire'), - 'Misery Mire - Main Lobby': (0xea5e, 0x186232, False, 'in Misery Mire'), - 'Misery Mire - Bridge Chest': (0xea61, 0x186235, False, 'in Misery Mire'), - 'Misery Mire - Spike Chest': (0xe9da, 0x1861ae, False, 'in Misery Mire'), - 'Misery Mire - Compass Chest': (0xea64, 0x186238, False, 'in Misery Mire'), - 'Misery Mire - Big Key Chest': (0xea6d, 0x186241, False, 'in Misery Mire'), - 'Misery Mire - Boss': (0x180158, 0x186346, False, 'with Vitreous'), - 'Turtle Rock - Compass Chest': (0xea22, 0x1861f6, False, 'in Turtle Rock'), - 'Turtle Rock - Roller Room - Left': (0xea1c, 0x1861f0, False, 'in Turtle Rock'), - 'Turtle Rock - Roller Room - Right': (0xea1f, 0x1861f3, False, 'in Turtle Rock'), - 'Turtle Rock - Chain Chomps': (0xea16, 0x1861ea, False, 'in Turtle Rock'), - 'Turtle Rock - Big Key Chest': (0xea25, 0x1861f9, False, 'in Turtle Rock'), - 'Turtle Rock - Big Chest': (0xea19, 0x1861ed, False, 'in Turtle Rock'), - 'Turtle Rock - Crystaroller Room': (0xea34, 0x186208, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186205, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186202, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x1861ff, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x1861fc, False, 'in Turtle Rock'), - 'Turtle Rock - Boss': (0x180159, 0x186347, False, 'with Trinexx'), - 'Palace of Darkness - Shooter Room': (0xea5b, 0x18622f, False, 'in Palace of Darkness'), - 'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186211, False, 'in Palace of Darkness'), - 'Palace of Darkness - Stalfos Basement': (0xea49, 0x18621d, False, 'in Palace of Darkness'), - 'Palace of Darkness - Big Key Chest': (0xea37, 0x18620b, False, 'in Palace of Darkness'), - 'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x18620e, False, 'in Palace of Darkness'), - 'Palace of Darkness - Map Chest': (0xea52, 0x186226, False, 'in Palace of Darkness'), - 'Palace of Darkness - Compass Chest': (0xea43, 0x186217, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186220, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186223, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186229, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x18622c, False, 'in Palace of Darkness'), - 'Palace of Darkness - Big Chest': (0xea40, 0x186214, False, 'in Palace of Darkness'), - 'Palace of Darkness - Harmless Hellway': (0xea46, 0x18621a, False, 'in Palace of Darkness'), - 'Palace of Darkness - Boss': (0x180153, 0x186341, False, 'with Helmasaur King'), - "Ganons Tower - Bob's Torch": (0x180161, 0x186363, False, "in Ganon's Tower"), - 'Ganons Tower - Hope Room - Left': (0xead9, 0x1862ad, False, "in Ganon's Tower"), - 'Ganons Tower - Hope Room - Right': (0xeadc, 0x1862b0, False, "in Ganon's Tower"), - 'Ganons Tower - Tile Room': (0xeae2, 0x1862b6, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x1862b9, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x1862bc, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x1862bf, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x1862c2, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x18628c, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x18628f, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186292, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186295, False, "in Ganon's Tower"), - 'Ganons Tower - Map Chest': (0xead3, 0x1862a7, False, "in Ganon's Tower"), - 'Ganons Tower - Firesnake Room': (0xead0, 0x1862a4, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186298, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x18629b, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x18629e, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x1862a1, False, "in Ganon's Tower"), - "Ganons Tower - Bob's Chest": (0xeadf, 0x1862b3, False, "in Ganon's Tower"), - 'Ganons Tower - Big Chest': (0xead6, 0x1862aa, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x1862c8, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x1862cb, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Chest': (0xeaf1, 0x1862c5, False, "in Ganon's Tower"), - 'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x1862d1, False, "atop Ganon's Tower"), - 'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x1862d4, False, "atop Ganon's Tower"), - 'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x1862d7, False, "atop Ganon's Tower"), - 'Ganons Tower - Validation Chest': (0xeb06, 0x1862da, False, "atop Ganon's Tower"), + +lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} +location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'), + 'Bottle Merchant': (0x2eb18, 0x186df9, False, 'with a merchant'), + 'Flute Spot': (0x18014a, 0x186dfd, False, 'underground'), + 'Sunken Treasure': (0x180145, 0x186e14, False, 'underwater'), + 'Purple Chest': (0x33d68, 0x186e19, False, 'from a box'), + "Blind's Hideout - Top": (0xeb0f, 0x186da3, False, 'in a basement'), + "Blind's Hideout - Left": (0xeb12, 0x186da6, False, 'in a basement'), + "Blind's Hideout - Right": (0xeb15, 0x186da9, False, 'in a basement'), + "Blind's Hideout - Far Left": (0xeb18, 0x186dac, False, 'in a basement'), + "Blind's Hideout - Far Right": (0xeb1b, 0x186daf, False, 'in a basement'), + "Link's Uncle": (0x2df45, 0x186e1f, False, 'with your uncle'), + 'Secret Passage': (0xe971, 0x186c05, False, 'near your uncle'), + 'King Zora': (0xee1c3, 0x186e20, False, 'at a high price'), + "Zora's Ledge": (0x180149, 0x186e18, False, 'near Zora'), + 'Waterfall Fairy - Left': (0xe9b0, 0x186c44, False, 'near a fairy'), + 'Waterfall Fairy - Right': (0xe9d1, 0x186c65, False, 'near a fairy'), + "King's Tomb": (0xe97a, 0x186c0e, False, 'alone in a cave'), + 'Floodgate Chest': (0xe98c, 0x186c20, False, 'in the dam'), + "Link's House": (0xe9bc, 0x186c50, False, 'in your home'), + 'Kakariko Tavern': (0xe9ce, 0x186c62, False, 'in the bar'), + 'Chicken House': (0xe9e9, 0x186c7d, False, 'near poultry'), + "Aginah's Cave": (0xe9f2, 0x186c86, False, 'with Aginah'), + "Sahasrahla's Hut - Left": (0xea82, 0x186d16, False, 'near the elder'), + "Sahasrahla's Hut - Middle": (0xea85, 0x186d19, False, 'near the elder'), + "Sahasrahla's Hut - Right": (0xea88, 0x186d1c, False, 'near the elder'), + 'Sahasrahla': (0x2f1fc, 0x186e25, False, 'with the elder'), + 'Kakariko Well - Top': (0xea8e, 0x186d22, False, 'in a well'), + 'Kakariko Well - Left': (0xea91, 0x186d25, False, 'in a well'), + 'Kakariko Well - Middle': (0xea94, 0x186d28, False, 'in a well'), + 'Kakariko Well - Right': (0xea97, 0x186d2b, False, 'in a well'), + 'Kakariko Well - Bottom': (0xea9a, 0x186d2e, False, 'in a well'), + 'Blacksmith': (0x18002a, 0x186e26, False, 'with the smith'), + 'Magic Bat': (0x180015, 0x186e1e, False, 'with the bat'), + 'Sick Kid': (0x339cf, 0x186e27, False, 'with the sick'), + 'Hobo': (0x33e7d, 0x186e28, False, 'with the hobo'), + 'Lost Woods Hideout': (0x180000, 0x186e08, False, 'near a thief'), + 'Lumberjack Tree': (0x180001, 0x186e09, False, 'in a hole'), + 'Cave 45': (0x180003, 0x186e0b, False, 'alone in a cave'), + 'Graveyard Cave': (0x180004, 0x186e0c, False, 'alone in a cave'), + 'Checkerboard Cave': (0x180005, 0x186e0d, False, 'alone in a cave'), + 'Mini Moldorm Cave - Far Left': (0xeb42, 0x186dd6, False, 'near Moldorms'), + 'Mini Moldorm Cave - Left': (0xeb45, 0x186dd9, False, 'near Moldorms'), + 'Mini Moldorm Cave - Right': (0xeb48, 0x186ddc, False, 'near Moldorms'), + 'Mini Moldorm Cave - Far Right': (0xeb4b, 0x186ddf, False, 'near Moldorms'), + 'Mini Moldorm Cave - Generous Guy': (0x180010, 0x186e1a, False, 'near Moldorms'), + 'Ice Rod Cave': (0xeb4e, 0x186de2, False, 'in a frozen cave'), + 'Bonk Rock Cave': (0xeb3f, 0x186dd3, False, 'alone in a cave'), + 'Library': (0x180012, 0x186e1c, False, 'near books'), + 'Potion Shop': (0x180014, 0x186e1d, False, 'near potions'), + 'Lake Hylia Island': (0x180144, 0x186e13, False, 'on an island'), + 'Maze Race': (0x180142, 0x186e11, False, 'at the race'), + 'Desert Ledge': (0x180143, 0x186e12, False, 'in the desert'), + 'Desert Palace - Big Chest': (0xe98f, 0x186c23, False, 'in Desert Palace'), + 'Desert Palace - Torch': (0x180160, 0x186e22, False, 'in Desert Palace'), + 'Desert Palace - Map Chest': (0xe9b6, 0x186c4a, False, 'in Desert Palace'), + 'Desert Palace - Compass Chest': (0xe9cb, 0x186c5f, False, 'in Desert Palace'), + 'Desert Palace - Big Key Chest': (0xe9c2, 0x186c56, False, 'in Desert Palace'), + 'Desert Palace - Boss': (0x180151, 0x186dff, False, 'with Lanmolas'), + 'Eastern Palace - Compass Chest': (0xe977, 0x186c0b, False, 'in Eastern Palace'), + 'Eastern Palace - Big Chest': (0xe97d, 0x186c11, False, 'in Eastern Palace'), + 'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186c47, False, 'in Eastern Palace'), + 'Eastern Palace - Big Key Chest': (0xe9b9, 0x186c4d, False, 'in Eastern Palace'), + 'Eastern Palace - Map Chest': (0xe9f5, 0x186c89, False, 'in Eastern Palace'), + 'Eastern Palace - Boss': (0x180150, 0x186dfe, False, 'with the Armos'), + 'Master Sword Pedestal': (0x289b0, 0x186e29, False, 'at the pedestal'), + 'Hyrule Castle - Boomerang Chest': (0xe974, 0x186c08, False, 'in Hyrule Castle'), + 'Hyrule Castle - Map Chest': (0xeb0c, 0x186da0, False, 'in Hyrule Castle'), + "Hyrule Castle - Zelda's Chest": (0xeb09, 0x186d9d, False, 'in Hyrule Castle'), + 'Sewers - Dark Cross': (0xe96e, 0x186c02, False, 'in the sewers'), + 'Sewers - Secret Room - Left': (0xeb5d, 0x186df1, False, 'in the sewers'), + 'Sewers - Secret Room - Middle': (0xeb60, 0x186df4, False, 'in the sewers'), + 'Sewers - Secret Room - Right': (0xeb63, 0x186df7, False, 'in the sewers'), + 'Sanctuary': (0xea79, 0x186d0d, False, 'in Sanctuary'), + 'Castle Tower - Room 03': (0xeab5, 0x186d49, False, 'in Castle Tower'), + 'Castle Tower - Dark Maze': (0xeab2, 0x186d46, False, 'in Castle Tower'), + 'Old Man': (0xf69fa, 0x186e24, False, 'with the old man'), + 'Spectacle Rock Cave': (0x180002, 0x186e0a, False, 'alone in a cave'), + 'Paradox Cave Lower - Far Left': (0xeb2a, 0x186dbe, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Left': (0xeb2d, 0x186dc1, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Right': (0xeb30, 0x186dc4, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Far Right': (0xeb33, 0x186dc7, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Middle': (0xeb36, 0x186dca, False, 'in a cave with seven chests'), + 'Paradox Cave Upper - Left': (0xeb39, 0x186dcd, False, 'in a cave with seven chests'), + 'Paradox Cave Upper - Right': (0xeb3c, 0x186dd0, False, 'in a cave with seven chests'), + 'Spiral Cave': (0xe9bf, 0x186c53, False, 'in Spiral Cave'), + 'Ether Tablet': (0x180016, 0x186dfb, False, 'at a monolith'), + 'Spectacle Rock': (0x180140, 0x186e0f, False, 'atop a rock'), + 'Tower of Hera - Basement Cage': (0x180162, 0x186dfa, False, 'in Tower of Hera'), + 'Tower of Hera - Map Chest': (0xe9ad, 0x186c41, False, 'in Tower of Hera'), + 'Tower of Hera - Big Key Chest': (0xe9e6, 0x186c7a, False, 'in Tower of Hera'), + 'Tower of Hera - Compass Chest': (0xe9fb, 0x186c8f, False, 'in Tower of Hera'), + 'Tower of Hera - Big Chest': (0xe9f8, 0x186c8c, False, 'in Tower of Hera'), + 'Tower of Hera - Boss': (0x180152, 0x186e00, False, 'with Moldorm'), + 'Pyramid': (0x180147, 0x186e16, False, 'on the Pyramid'), + 'Catfish': (0xee185, 0x186e21, False, 'with a catfish'), + 'Stumpy': (0x330c7, 0x186e2a, False, 'with tree boy'), + 'Digging Game': (0x180148, 0x186e17, False, 'underground'), + 'Bombos Tablet': (0x180017, 0x186dfc, False, 'at a monolith'), + 'Hype Cave - Top': (0xeb1e, 0x186db2, False, 'near a bat-like man'), + 'Hype Cave - Middle Right': (0xeb21, 0x186db5, False, 'near a bat-like man'), + 'Hype Cave - Middle Left': (0xeb24, 0x186db8, False, 'near a bat-like man'), + 'Hype Cave - Bottom': (0xeb27, 0x186dbb, False, 'near a bat-like man'), + 'Hype Cave - Generous Guy': (0x180011, 0x186e1b, False, 'with a bat-like man'), + 'Peg Cave': (0x180006, 0x186e0e, False, 'alone in a cave'), + 'Pyramid Fairy - Left': (0xe980, 0x186c14, False, 'near a fairy'), + 'Pyramid Fairy - Right': (0xe983, 0x186c17, False, 'near a fairy'), + 'Brewery': (0xe9ec, 0x186c80, False, 'alone in a home'), + 'C-Shaped House': (0xe9ef, 0x186c83, False, 'alone in a home'), + 'Chest Game': (0xeda8, 0x186e2b, False, 'as a prize'), + 'Bumper Cave Ledge': (0x180146, 0x186e15, False, 'on a ledge'), + 'Mire Shed - Left': (0xea73, 0x186d07, False, 'near sparks'), + 'Mire Shed - Right': (0xea76, 0x186d0a, False, 'near sparks'), + 'Superbunny Cave - Top': (0xea7c, 0x186d10, False, 'in a connection'), + 'Superbunny Cave - Bottom': (0xea7f, 0x186d13, False, 'in a connection'), + 'Spike Cave': (0xea8b, 0x186d1f, False, 'beyond spikes'), + 'Hookshot Cave - Top Right': (0xeb51, 0x186de5, False, 'across pits'), + 'Hookshot Cave - Top Left': (0xeb54, 0x186de8, False, 'across pits'), + 'Hookshot Cave - Bottom Right': (0xeb5a, 0x186dee, False, 'across pits'), + 'Hookshot Cave - Bottom Left': (0xeb57, 0x186deb, False, 'across pits'), + 'Floating Island': (0x180141, 0x186e10, False, 'on an island'), + 'Mimic Cave': (0xe9c5, 0x186c59, False, 'in a cave of mimicry'), + 'Swamp Palace - Entrance': (0xea9d, 0x186d31, False, 'in Swamp Palace'), + 'Swamp Palace - Map Chest': (0xe986, 0x186c1a, False, 'in Swamp Palace'), + 'Swamp Palace - Big Chest': (0xe989, 0x186c1d, False, 'in Swamp Palace'), + 'Swamp Palace - Compass Chest': (0xeaa0, 0x186d34, False, 'in Swamp Palace'), + 'Swamp Palace - Big Key Chest': (0xeaa6, 0x186d3a, False, 'in Swamp Palace'), + 'Swamp Palace - West Chest': (0xeaa3, 0x186d37, False, 'in Swamp Palace'), + 'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x186d3d, False, 'in Swamp Palace'), + 'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186d40, False, 'in Swamp Palace'), + 'Swamp Palace - Waterfall Room': (0xeaaf, 0x186d43, False, 'in Swamp Palace'), + 'Swamp Palace - Boss': (0x180154, 0x186e02, False, 'with Arrghus'), + "Thieves' Town - Big Key Chest": (0xea04, 0x186c98, False, "in Thieves Town"), + "Thieves' Town - Map Chest": (0xea01, 0x186c95, False, "in Thieves Town"), + "Thieves' Town - Compass Chest": (0xea07, 0x186c9b, False, "in Thieves Town"), + "Thieves' Town - Ambush Chest": (0xea0a, 0x186c9e, False, "in Thieves Town"), + "Thieves' Town - Attic": (0xea0d, 0x186ca1, False, "in Thieves Town"), + "Thieves' Town - Big Chest": (0xea10, 0x186ca4, False, "in Thieves Town"), + "Thieves' Town - Blind's Cell": (0xea13, 0x186ca7, False, "in Thieves Town"), + "Thieves' Town - Boss": (0x180156, 0x186e04, False, 'with Blind'), + 'Skull Woods - Compass Chest': (0xe992, 0x186c26, False, 'in Skull Woods'), + 'Skull Woods - Map Chest': (0xe99b, 0x186c2f, False, 'in Skull Woods'), + 'Skull Woods - Big Chest': (0xe998, 0x186c2c, False, 'in Skull Woods'), + 'Skull Woods - Pot Prison': (0xe9a1, 0x186c35, False, 'in Skull Woods'), + 'Skull Woods - Pinball Room': (0xe9c8, 0x186c5c, False, 'in Skull Woods'), + 'Skull Woods - Big Key Chest': (0xe99e, 0x186c32, False, 'in Skull Woods'), + 'Skull Woods - Bridge Room': (0xe9fe, 0x186c92, False, 'near Mothula'), + 'Skull Woods - Boss': (0x180155, 0x186e03, False, 'with Mothula'), + 'Ice Palace - Compass Chest': (0xe9d4, 0x186c68, False, 'in Ice Palace'), + 'Ice Palace - Freezor Chest': (0xe995, 0x186c29, False, 'in Ice Palace'), + 'Ice Palace - Big Chest': (0xe9aa, 0x186c3e, False, 'in Ice Palace'), + 'Ice Palace - Iced T Room': (0xe9e3, 0x186c77, False, 'in Ice Palace'), + 'Ice Palace - Spike Room': (0xe9e0, 0x186c74, False, 'in Ice Palace'), + 'Ice Palace - Big Key Chest': (0xe9a4, 0x186c38, False, 'in Ice Palace'), + 'Ice Palace - Map Chest': (0xe9dd, 0x186c71, False, 'in Ice Palace'), + 'Ice Palace - Boss': (0x180157, 0x186e05, False, 'with Kholdstare'), + 'Misery Mire - Big Chest': (0xea67, 0x186cfb, False, 'in Misery Mire'), + 'Misery Mire - Map Chest': (0xea6a, 0x186cfe, False, 'in Misery Mire'), + 'Misery Mire - Main Lobby': (0xea5e, 0x186cf2, False, 'in Misery Mire'), + 'Misery Mire - Bridge Chest': (0xea61, 0x186cf5, False, 'in Misery Mire'), + 'Misery Mire - Spike Chest': (0xe9da, 0x186c6e, False, 'in Misery Mire'), + 'Misery Mire - Compass Chest': (0xea64, 0x186cf8, False, 'in Misery Mire'), + 'Misery Mire - Big Key Chest': (0xea6d, 0x186d01, False, 'in Misery Mire'), + 'Misery Mire - Boss': (0x180158, 0x186e06, False, 'with Vitreous'), + 'Turtle Rock - Compass Chest': (0xea22, 0x186cb6, False, 'in Turtle Rock'), + 'Turtle Rock - Roller Room - Left': (0xea1c, 0x186cb0, False, 'in Turtle Rock'), + 'Turtle Rock - Roller Room - Right': (0xea1f, 0x186cb3, False, 'in Turtle Rock'), + 'Turtle Rock - Chain Chomps': (0xea16, 0x186caa, False, 'in Turtle Rock'), + 'Turtle Rock - Big Key Chest': (0xea25, 0x186cb9, False, 'in Turtle Rock'), + 'Turtle Rock - Big Chest': (0xea19, 0x186cad, False, 'in Turtle Rock'), + 'Turtle Rock - Crystaroller Room': (0xea34, 0x186cc8, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186cc5, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186cc2, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x186cbf, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x186cbc, False, 'in Turtle Rock'), + 'Turtle Rock - Boss': (0x180159, 0x186e07, False, 'with Trinexx'), + 'Palace of Darkness - Shooter Room': (0xea5b, 0x186cef, False, 'in Palace of Darkness'), + 'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186cd1, False, 'in Palace of Darkness'), + 'Palace of Darkness - Stalfos Basement': (0xea49, 0x186cdd, False, 'in Palace of Darkness'), + 'Palace of Darkness - Big Key Chest': (0xea37, 0x186ccb, False, 'in Palace of Darkness'), + 'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x186cce, False, 'in Palace of Darkness'), + 'Palace of Darkness - Map Chest': (0xea52, 0x186ce6, False, 'in Palace of Darkness'), + 'Palace of Darkness - Compass Chest': (0xea43, 0x186cd7, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186ce0, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186ce3, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186ce9, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x186cec, False, 'in Palace of Darkness'), + 'Palace of Darkness - Big Chest': (0xea40, 0x186cd4, False, 'in Palace of Darkness'), + 'Palace of Darkness - Harmless Hellway': (0xea46, 0x186cda, False, 'in Palace of Darkness'), + 'Palace of Darkness - Boss': (0x180153, 0x186e01, False, 'with Helmasaur King'), + "Ganons Tower - Bob's Torch": (0x180161, 0x186e23, False, "in Ganon's Tower"), + 'Ganons Tower - Hope Room - Left': (0xead9, 0x186d6d, False, "in Ganon's Tower"), + 'Ganons Tower - Hope Room - Right': (0xeadc, 0x186d70, False, "in Ganon's Tower"), + 'Ganons Tower - Tile Room': (0xeae2, 0x186d76, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x186d79, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x186d7c, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x186d7f, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x186d82, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x186d4c, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x186d4f, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186d52, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186d55, False, "in Ganon's Tower"), + 'Ganons Tower - Map Chest': (0xead3, 0x186d67, False, "in Ganon's Tower"), + 'Ganons Tower - Firesnake Room': (0xead0, 0x186d64, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186d58, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x186d5b, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x186d5e, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x186d61, False, "in Ganon's Tower"), + "Ganons Tower - Bob's Chest": (0xeadf, 0x186d73, False, "in Ganon's Tower"), + 'Ganons Tower - Big Chest': (0xead6, 0x186d6a, False, "in Ganon's Tower"), + 'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x186d88, False, "in Ganon's Tower"), + 'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x186d8b, False, "in Ganon's Tower"), + 'Ganons Tower - Big Key Chest': (0xeaf1, 0x186d85, False, "in Ganon's Tower"), + 'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x186d91, False, "atop Ganon's Tower"), + 'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x186d94, False, "atop Ganon's Tower"), + 'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x186d97, False, "atop Ganon's Tower"), + 'Ganons Tower - Validation Chest': (0xeb06, 0x186d9a, False, "atop Ganon's Tower"), 'Ganon': (None, None, False, 'from me'), 'Agahnim 1': (None, None, False, 'from Ganon\'s wizardry form'), 'Agahnim 2': (None, None, False, 'from Ganon\'s wizardry form'), @@ -1455,8 +1457,6 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Potion Shop - Middle': (None, None, False, 'for sale near potions'), 'Potion Shop - Right': (None, None, False, 'for sale near potions'), } - -lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name.update(shop_table_by_location_id) lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int} lookup_name_to_id.update(shop_table_by_location) diff --git a/Rom.py b/Rom.py index 18677375..26b54dbf 100644 --- a/Rom.py +++ b/Rom.py @@ -1548,16 +1548,18 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery'] and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) - if world.pot_contents[player].size() > 0x2800: + if world.pot_contents[player].size() > 0x11c0: raise Exception('Pot table is too big for current area') world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots) + # todo: write sprites + write_strings(rom, world, player, team) # write initial sram rom.write_initial_sram() - rom.write_byte(0x18636C, 1 if world.remote_items[player] else 0) + rom.write_byte(0x187E30, 1 if world.remote_items[player] else 0) # set rom name # 21 bytes @@ -1615,7 +1617,7 @@ def write_custom_shops(rom, world, player): if item is None: break if world.shopsanity[player] or shop.type == ShopType.TakeAny: - rom.write_byte(0x186560 + shop.sram_address + index, 1) + rom.write_byte(0x187E40 + shop.sram_address + index, 1) if world.shopsanity[player] and shop.region.name in shop_to_location_table: loc_item = world.get_location(shop_to_location_table[shop.region.name][index], player).item elif world.shopsanity[player] and shop.region.name in retro_shops: diff --git a/Utils.py b/Utils.py index d0bbb5fc..a4e3cee2 100644 --- a/Utils.py +++ b/Utils.py @@ -6,6 +6,7 @@ import sys import xml.etree.ElementTree as ET from collections import defaultdict from math import factorial +import fileinput def int16_as_bytes(value): @@ -696,10 +697,19 @@ def check_pots(): print(f'{pot.room}#{i+1} secret: {hex(secret_vram)} tile: {hex(tile_vram)}') +def find_and_replace(): + for data_line in fileinput.input('scratch.txt', inplace=True): + if '=' in data_line: + one, two = data_line.split(' = ') + number = int(two.strip()) + print(data_line.replace(two, hex(number))) + + if __name__ == '__main__': # make_new_base2current() # read_entrance_data(old_rom=sys.argv[1]) # room_palette_data(old_rom=sys.argv[1]) # extract_data_from_us_rom(sys.argv[1]) # extract_data_from_jp_rom(sys.argv[1]) - check_pots() + # check_pots() + find_and_replace() diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index cd5bb518..37e5e627 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -1,3 +1,4 @@ +from collections import defaultdict import math import typing @@ -6,11 +7,17 @@ try: except ImportError: from enum import IntFlag as FastEnum +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=0, sub_type=0): + def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, sub_type=0): self.sprite = sprite self.sub_type = sub_type self.static = static @@ -21,11 +28,16 @@ class EnemyStats: class EnemySprite(FastEnum): - CorrectPullSwitch = 0x04, + Raven = 0x00 + Vulture = 0x01 + CorrectPullSwitch = 0x04 WrongPullSwitch = 0x06 Octorok = 0x08 Moldorm = 0x09 + Octorok4Way = 0x0a Cucco = 0x0b + Buzzblob = 0x0d + Snapdragon = 0x0e Octoballoon = 0x0f OctoballoonBaby = 0x10 @@ -90,16 +102,20 @@ class EnemySprite(FastEnum): BombGuard = 0x4a GreenKnifeGuard = 0x4b Geldman = 0x4c + Toppo = 0x4d Popo = 0x4e Popo2 = 0x4f - + + ArmosStatue = 0x51 + KingZora = 0x52 ArmosKnight = 0x53 Lanmolas = 0x54 + FireballZora = 0x55 Zora = 0x56 DesertStatue = 0x57 Crab = 0x58 LostWoodsBird = 0x59 - LostWoodsSquirrel =0x5a + LostWoodsSquirrel = 0x5a SparkCW = 0x5b SparkCCW = 0x5c RollerVerticalUp = 0x5d @@ -159,8 +175,8 @@ class EnemySprite(FastEnum): Pengator = 0x99 Kyameron = 0x9a Wizzrobe = 0x9b - Zoro = 0x9c - Babasu = 0x9d + Zoro = 0x9c # babasu horizontal? + Babasu = 0x9d # babasu vertical? GroveOstritch = 0x9e GroveRabbit = 0x9f GroveBird = 0xa0 @@ -171,6 +187,10 @@ class EnemySprite(FastEnum): BlueZazak = 0xa5 RedZazak = 0xa6 Stalfos = 0xa7 + GreenZirro = 0xa8 + BlueZirro = 0xa9 + Pikit = 0xaa + CrystalMaiden = 0xab # ... OW OldMan = 0xad PipeDown = 0xae @@ -179,9 +199,12 @@ class EnemySprite(FastEnum): PipeLeft = 0xb1 GoodBee = 0xb2 PedestalPlaque = 0xb3 + PurpleChest = 0xb4 BombShopGuy = 0xb5 + Kiki = 0xb6 BlindMaiden = 0xb7 - + BullyPinkBall = 0xb9 + Whirlpool = 0xba Shopkeeper = 0xbb Drunkard = 0xbc @@ -206,14 +229,18 @@ class EnemySprite(FastEnum): Lynel = 0xd0 BunnyBeam = 0xd1 FloppingFish = 0xd2 - Stal = 0xd3 + Stal = 0xd3 # alive skull rock? + DiggingGameNPC = 0xd5 Ganon = 0xd6 Faerie = 0xe3 SmallKey = 0xe4 + FakeMasterSword = 0xe8 MagicShopAssistant = 0xe9 HeartPiece = 0xeb + SomariaPlatform = 0xed CastleMantle = 0xee + MedallionTablet = 0xf2 class SpriteType(FastEnum): @@ -222,7 +249,7 @@ class SpriteType(FastEnum): def init_enemy_stats(): - enemy_stats = { + stats = { EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True), EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True), EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2), @@ -417,6 +444,22 @@ def init_enemy_stats(): EnemySprite.CastleMantle: EnemyStats(EnemySprite.CastleMantle, True), } + return stats + + +def handle_native_dungeon(location, itemid): + # Keys in their native dungeon should use the original item code for keys + if location.parent_region.dungeon: + if location.parent_region.dungeon.name == location.item.dungeon: + if location.item.bigkey: + return 0x32 + if location.item.smallkey: + return 0x24 + if location.item.map: + return 0x33 + if location.item.compass: + return 0x25 + return itemid class Sprite(object): @@ -432,15 +475,36 @@ class Sprite(object): self.drops_item = drops_item self.drop_item_kind = drop_item_kind + self.location = None + + def copy(self): + return Sprite(self.super_tile, self.kind, self.sub_type, self.layer, self.tile_x, self.tile_y, self.region, + self.drops_item, self.drop_item_kind) + + def sprite_data(self): + data = [(self.layer << 7) | ((self.sub_type & 0x18) << 2) | self.tile_y, + ((self.sub_type & 7) << 5) | self.tile_x, self.kind] + if self.location is not None: + item_id = self.location.item.code if self.location.item is not None else 0x5A + code = 0xF9 if self.location.item.player != self.location.player else 0xF8 + if code == 0xF8: + item_id = handle_native_dungeon(self.location, item_id) + data.append(item_id) + data.append(0 if code == 0xF8 else self.location.item.player) + data.append(code) + return data + # map of super_tile to list of Sprite objects: vanilla_sprites = {} +enemy_stats = {} -def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, drops_item=False, drop_item_kind=None): +def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, + drops_item=False, drop_item_kind=None): if super_tile not in vanilla_sprites: vanilla_sprites[super_tile] = [] - vanilla_sprites[super_tile].append(Sprite(kind, sub_type, layer, tile_x, tile_y, + vanilla_sprites[super_tile].append(Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region, drops_item, drop_item_kind)) @@ -672,22 +736,22 @@ def init_vanilla_sprites(): create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06, 'Hera Big Chest Landing') create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x05, 0x0e, 'Hera 4F') create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x04, 0x16, 'Hera 4F') - create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, '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.SpikeBlock, 0x00, 0, 0x08, 0x10, 'Swamp Entrance') create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x18, 0x16) create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) - create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17) - create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f) + create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f, 'PoD Arena Main') create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x08, 'PoD Arena North') create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c, 'PoD Arena Main') create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x10, 0x0c, 'PoD Arena Main') create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x0f, 'PoD Arena Main') create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11, 'PoD Arena Main') create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0f, 0x13, 'PoD Arena Main') - create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11, 'PoD Arena Main') + create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11) create_sprite(0x002b, EnemySprite.Statue, 0x00, 0, 0x0a, 0x0a) create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17, 'PoD Map Balcony') create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17) @@ -776,9 +840,9 @@ def init_vanilla_sprites(): create_sprite(0x0039, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) create_sprite(0x0039, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x15, 'Skull Spike Corner', True, 0xe4) create_sprite(0x0039, EnemySprite.MiniHelmasaur, 0x00, 0, 0x09, 0x15, 'Skull Spike Corner') - create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16, 'Skull Final Drop') create_sprite(0x0039, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18, 'Skull Spike Corner') - create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a, 'Skull Final Drop') create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11, 'PoD Pit Room',) create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11, 'PoD Pit Room',) create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14, 'PoD Pit Room',) @@ -1920,6 +1984,8 @@ def init_vanilla_sprites(): create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16) create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) + global enemy_stats + enemy_stats = init_enemy_stats() def kill_rules(world, player, stats): @@ -1984,6 +2050,132 @@ def kill_rules(world, player, stats): return defeat_rules +layered_oam_rooms = { + 0x14, 0x15, 0x51, 0x59, 0x5b, 0x60, 0x62, 0x81, 0x86, 0xa8, 0xaa, 0xb2, 0xb9, 0xc2, 0xcb, 0xcc, 0xdb, 0xdc +} + + +class EnemyTable: + def __init__(self): + self.room_map = defaultdict(list) + self.multiworld_count = 0 + + def write_sprite_data_to_rom(self, rom): + pointer_address = snes_to_pc(0x09D62E) + data_pointer = snes_to_pc(0x288000) + empty_pointer = pc_to_snes(data_pointer) & 0xFFFF + rom.write_bytes(data_pointer, [0x00, 0xff]) + data_pointer += 2 + for room in range(0, 0x128): + if room in self.room_map: + data_address = pc_to_snes(data_pointer) & 0xFFFF + rom.write_bytes(pointer_address + room * 2, int16_as_bytes(data_address)) + rom.write_byte(data_pointer, 0x01 if room in layered_oam_rooms else 0x00) + list_offset = 1 + for sprite in self.room_map[room]: + data = sprite.sprite_data() + rom.write_bytes(data_pointer + list_offset, data) + list_offset + len(data) + rom.write_byte(data_pointer, 0xff) + data_pointer += list_offset + 1 + else: + rom.write_bytes(pointer_address + room * 2, int16_as_bytes(empty_pointer)) + + def size(self): + size = 2 + for room in range(0, 0x128): + if room in self.room_map: + size += sum(len(sprite.sprite_data()) for sprite in self.room_map[room]) + 2 + return size + + +def setup_enemy_locations(world, player): + world.enemy_list[player] = EnemyTable() + for super_tile, enemy_list in vanilla_sprites.items(): + for index, sprite in enumerate(enemy_list): + # if sprite.drops_item and sprite.drop_item_kind == 0xe4: + # # normal key drops + # pass + my_sprite = sprite.copy() + world.enemy_list[player].room_map[super_tile].append() + + if valid_drop_location(my_sprite, world, player): + create_drop_location(my_sprite, index, super_tile, world, player) + + +def valid_drop_location(sprite, world, player): + if world.dropshuffle[player] == 'underworld': + if sprite.drops_item and sprite.drop_item_kind == 0xe4: + # already has a location -- hook it up? + return False + else: + stat = enemy_stats[sprite.kind] + return not stat.static and stat.drop_flag + + +def create_drop_location(sprite, index, super_tile, world, player): + + address = drop_address(index, super_tile) + region_name = sprite.region + parent = world.get_region(region_name, player) + descriptor = f'Enemy #{index+1}' + modifier = parent.hint_text not in {'a storyteller', 'fairies deep in a cave', 'a spiky hint', + 'a bounty of five items', 'the sick kid', 'Sahasrahla'} + hint_text = f'{"held by an enemy"} {"in" if modifier else "near"} {parent.hint_text}' + drop_location = Location(player, f'{region_name} {descriptor}', address, hint_text=hint_text, parent=parent) + world.dynamic_locations.append(drop_location) + drop_location.drop = sprite + sprite.location = drop_location + drop_location.type = LocationType.Drop + + +# todo: placeholder address +def drop_address(index, super_tile): + return 0x7f9000 + super_tile * 2 + (index << 24) + + +prize_pack_selector = { + 0: ['Nothing'], + 1: ['Small Heart', 'Small Heart', 'Small Heart', 'Small Heart', + 'Rupee (1)', 'Small Heart', 'Small Heart', 'Rupee (1)'], + 2: ['Rupees (5)', 'Rupee (1)', 'Rupees (5)', 'Rupees (20)', + 'Rupees (5)', 'Rupee (1)', 'Rupees (5)', 'Rupees (5)'], + 3: ['Big Magic', 'Small Magic', 'Small Magic', 'Rupees (5)', + 'Big Magic', 'Small Magic', 'Small Heart', 'Small Magic'], + 4: ['Single Bomb', 'Single Bomb', 'Single Bomb', 'Single Bomb', + 'Single Bomb', 'Single Bomb', 'Bombs (10)', 'Single Bomb'], + 5: ['Arrows (5)', 'Small Heart', 'Arrows (5)', 'Arrows (10)', + 'Arrows (5)', 'Small Heart', 'Arrows (5)', 'Arrows (10)'], + 6: ['Small Magic', 'Rupee (1)', 'Small Heart', 'Arrows (5)', + 'Small Magic', 'Single Bomb', 'Rupee (1)', 'Small Heart'], + 7: ['Small Heart', 'Fairy', 'Big Magic', 'Rupees (20)', + 'Bombs (10)', 'Small Heart', 'Rupees (20)', 'Arrows (10)'], +} + + +def add_drop_contents(world, player): + retro_bow = world.bow_mode[player].startswith('retro') + index_selector = [0]*8 + for super_tile, enemy_list in world.enemy_list[player].room_map.items(): + for sprite in enemy_list: + if sprite.drops_item and sprite.drop_item_kind == 0xe4: + continue + else: + stat = enemy_stats[sprite.kind] + if not stat.static and stat.drop_flag: + pack = 0 + if isinstance(stat.prize_pack, int): + pack = stat.prize_pack + elif isinstance(stat.prize_pack, tuple): + pack = random.choice(stat.prize_pack) + pack_contents = prize_pack_selector[pack] + idx = index_selector[pack] + index_selector[pack] = (idx + 1) % len(pack_contents) + item_name = pack_contents[idx] + item_name = 'Rupees (5)' if retro_bow and 'Arrows' in item_name else item_name + world.itempool.append(ItemFactory(item_name, player)) + + def or_rule(*rules): return RuleFactory.disj(rules) @@ -2126,3 +2318,218 @@ def can_shoot_arrows(world, player): def can_use_bombs(world, player): return or_rule(RuleFactory.static_rule(not world.bombag[player]), has('Bomb Upgrade (+10)', player)) +enemy_names = { + 0x00: 'Raven', + 0x01: 'Vulture', + 0x04: 'CorrectPullSwitch', + 0x06: 'WrongPullSwitch', + 0x08: 'Octorok', + 0x09: 'Moldorm', + 0x0a: 'Octorok4Way', + 0x0b: 'Cucco', + 0x0d: 'Buzzblob', + 0x0e: 'Snapdragon', + + 0x0f: 'Octoballoon', + 0x10: 'OctoballoonBaby', + 0x11: 'Hinox', + 0x12: 'Moblin', + 0x13: 'MiniHelmasaur', + 0x14: 'ThievesTownGrate', + 0x15: 'AntiFairy', + 0x16: 'Wiseman', + 0x17: 'Hoarder', + 0x18: 'MiniMoldorm', + 0x19: 'Poe', + 0x1a: 'Smithy', + 0x1b: 'Arrow', + 0x1c: 'Statue', + 0x1d: 'FluteQuest', + 0x1e: 'CrystalSwitch', + 0x1f: 'SickKid', + 0x20: 'Sluggula', + 0x21: 'WaterSwitch', + 0x22: 'Ropa', + 0x23: 'RedBari', + 0x24: 'BlueBari', + 0x25: 'TalkingTree', + 0x26: 'HardhatBeetle', + 0x27: 'Deadrock', + 0x28: 'DarkWorldHintNpc', + 0x29: 'AdultNpc', + 0x2a: 'SweepingLady', + 0x2b: 'Hobo', + 0x2c: 'Lumberjacks', + 0x2d: 'TelepathicTile', + 0x2e: 'FluteKid', + 0x2f: 'RaceGameLady', + + 0x31: 'FortuneTeller', + 0x32: 'ArgueBros', + 0x33: 'RupeePull', + 0x34: 'YoungSnitch', + 0x35: 'Innkeeper', + 0x36: 'Witch', + 0x37: 'Waterfall', + 0x38: 'EyeStatue', + 0x39: 'Locksmith', + 0x3a: 'MagicBat', + 0x3b: 'BonkItem', + 0x3c: 'KidInKak', + 0x3d: 'OldSnitch', + 0x3e: 'Hoarder2', + 0x3f: 'TutorialGuard', + + 0x40: 'LightningGate', + 0x41: 'BlueGuard', + 0x42: 'GreenGuard', + 0x43: 'RedSpearGuard', + 0x44: 'BluesainBolt', + 0x45: 'UsainBolt', + 0x46: 'BlueArcher', + 0x47: 'GreenBushGuard', + 0x48: 'RedJavelinGuard', + 0x49: 'RedBushGuard', + 0x4a: 'BombGuard', + 0x4b: 'GreenKnifeGuard', + 0x4c: 'Geldman', + 0x4d: 'Toppo', + 0x4e: 'Popo', + 0x4f: 'Popo2', + + 0x51: 'ArmosStatue', + 0x52: 'KingZora', + 0x53: 'ArmosKnight', + 0x54: 'Lanmolas', + 0x55: 'FireballZora', + 0x56: 'Zora', + 0x57: 'DesertStatue', + 0x58: 'Crab', + 0x59: 'LostWoodsBird', + 0x5a: 'LostWoodsSquirrel', + 0x5b: 'SparkCW', + 0x5c: 'SparkCCW', + 0x5d: 'RollerVerticalUp', + 0x5e: 'RollerVerticalDown', + 0x5f: 'RollerHorizontalLeft', + 0x60: 'RollerHorizontalRight', + 0x61: 'Beamos', + 0x62: 'MasterSword', + 0x63: 'DebirandoPit', + 0x64: 'Debirando', + 0x65: 'ArcheryNpc', + 0x66: 'WallCannonVertLeft', + 0x67: 'WallCannonVertRight', + 0x68: 'WallCannonHorzTop', + 0x69: 'WallCannonHorzBottom', + 0x6a: 'BallNChain', + 0x6b: 'CannonTrooper', + 0x6d: 'CricketRat', + 0x6e: 'Snake', + 0x6f: 'Keese', + + 0x71: 'Leever', + 0x72: 'FairyPondTrigger', + 0x73: 'UnclePriest', + 0x74: 'RunningNpc', + 0x75: 'BottleMerchant', + 0x76: 'Zelda', + 0x78: 'Grandma', + 0x7a: 'Agahnim', + 0x7c: 'FloatingSkull', + 0x7d: 'BigSpike', + 0x7e: 'FirebarCW', + 0x7f: 'FirebarCCW', + 0x80: 'Firesnake', + 0x81: 'Hover', + 0x82: 'AntiFairyCircle', + 0x83: 'GreenEyegoreMimic', + 0x84: 'RedEyegoreMimic', + 0x85: 'YellowStalfos', # falling stalfos that shoots head + 0x86: 'Kondongo', + 0x88: 'Mothula', + 0x8a: 'SpikeBlock', + 0x8b: 'Gibdo', + 0x8c: 'Arrghus', + 0x8d: 'Arrghi', + 0x8e: 'Terrorpin', + 0x8f: 'Blob', + 0x90: 'Wallmaster', + 0x91: 'StalfosKnight', + 0x92: 'HelmasaurKing', + 0x93: 'Bumper', + 0x94: 'Pirogusu', + 0x95: 'LaserEyeLeft', + 0x96: 'LaserEyeRight', + 0x97: 'LaserEyeTop', + 0x98: 'LaserEyeBottom', + 0x99: 'Pengator', + 0x9a: 'Kyameron', + 0x9b: 'Wizzrobe', + 0x9c: 'Zoro', # babasu horizontal? + 0x9d: 'Babasu', # babasu vertical? + 0x9e: 'GroveOstritch', + 0x9f: 'GroveRabbit', + 0xa0: 'GroveBird', + 0xa1: 'Freezor', + 0xa2: 'Kholdstare', + 0xa3: 'KholdstareShell', + 0xa4: 'FallingIce', + 0xa5: 'BlueZazak', + 0xa6: 'RedZazak', + 0xa7: 'Stalfos', + 0xa8: 'GreenZirro', + 0xa9: 'BlueZirro', + 0xaa: 'Pikit', + 0xab: 'CrystalMaiden', + # ... OW + 0xad: 'OldMan', + 0xae: 'PipeDown', + 0xaf: 'PipeUp', + 0xb0: 'PipeRight', + 0xb1: 'PipeLeft', + 0xb2: 'GoodBee', + 0xb3: 'PedestalPlaque', + 0xb4: 'PurpleChest', + 0xb5: 'BombShopGuy', + 0xb6: 'Kiki', + 0xb7: 'BlindMaiden', + 0xb9: 'BullyPinkBall', + + 0xba: 'Whirlpool', + 0xbb: 'Shopkeeper', + 0xbc: 'Drunkard', + 0xbd: 'Vitreous', + # ... (spawnables) + 0xc0: 'Catfish', + 0xc1: 'CutsceneAgahnim', + 0xc2: 'Boulder', + 0xc3: 'Gibo', # patrick! + 0xc4: 'Thief', + 0xc5: 'Medusa', + 0xc6: 'FourWayShooter', + 0xc7: 'Pokey', + 0xc8: 'BigFairy', + 0xc9: 'Tektite', # firebat? + 0xca: 'Chainchomp', + 0xcb: 'TrinexxRockHead', + 0xcc: 'TrinexxFireHead', + 0xcd: 'TrinexxIceHead', + 0xce: 'Blind', + 0xcf: 'Swamola', + 0xd0: 'Lynel', + 0xd1: 'BunnyBeam', + 0xd2: 'FloppingFish', + 0xd3: 'Stal', # alive skull rock? + 0xd5: 'DiggingGameNPC', + 0xd6: 'Ganon', + + 0xe3: 'Faerie', + 0xe4: 'SmallKey', + 0xe8: 'FakeMasterSword', + 0xe9: 'MagicShopAssistant', + 0xeb: 'HeartPiece', + 0xed: 'SomariaPlatform', + 0xee: 'CastleMantle', + 0xf2: 'MedallionTablet', +} \ No newline at end of file diff --git a/source/dungeon/RoomConstants.py b/source/dungeon/RoomConstants.py new file mode 100644 index 00000000..82df24fa --- /dev/null +++ b/source/dungeon/RoomConstants.py @@ -0,0 +1,266 @@ +Ganon = 0x0 +HC_NorthCorridor = 0x1 +HC_SwitchRoom = 0x2 +HoulihanRoom = 0x3 +TR_CrystalRollerRoom = 0x4 +Swamp_Arrghus = 0x6 +Hera_Moldorm = 0x7 +Cave_HealingFairy = 0x8 +PalaceofDarkness0x09 = 0x9 +PoD_StalfosTrapRoom = 0xa +PoD_TurtleRoom = 0xb +GT_EntranceRoom = 0xc +GT_Agahnim2 = 0xd +Ice_EntranceRoom = 0xe +GanonEvacuationRoute = 0x10 +HC_BombableStockRoom = 0x11 +Sanctuary = 0x12 +TR_Hokku_BokkuKeyRoom2 = 0x13 +TR_BigKeyRoom = 0x14 +TurtleRock0x15 = 0x15 +Swamp_SwimmingTreadmill = 0x16 +Hera_MoldormFallRoom = 0x17 +Cave0x18_BigFairyDropEntrance = 0x18 +PoD_DarkMaze = 0x19 +PoD_BigChestRoom = 0x1a +PoD_Mimics_MovingWallRoom = 0x1b +GT_IceArmos = 0x1c +GT_FinalHallway = 0x1d +Ice_BombFloor_BariRoom = 0x1e +Ice_Pengator_BigKeyRoom = 0x1f +Tower_Agahnim = 0x20 +HC_KeyRatRoom = 0x21 +HC_SewerTextTriggerRoom = 0x22 +TR_WestExittoBalcony = 0x23 +TR_DoubleHokku_Bokku_BigchestRoom = 0x24 +Swamp_StatueRoom = 0x26 +Hera_BigChest = 0x27 +Swamp_EntranceRoom = 0x28 +Skull_Mothula = 0x29 +PoD_BigHubRoom = 0x2a +PoD_MapChest_FairyRoom = 0x2b +Cave0x2C_HookshotCaveBackdoor = 0x2c +Ice_CompassRoom = 0x2e +Cave_KakarikoWellHP = 0x2f +Tower_MaidenSacrificeChamber = 0x30 +Hera_HardhatBeetlesRoom = 0x31 +HC_SewerKeyChestRoom = 0x32 +Desert_Lanmolas = 0x33 +Swamp_PushBlockPuzzle_Pre_BigKeyRoom = 0x34 +Swamp_BigKey_BSRoom = 0x35 +Swamp_BigChestRoom = 0x36 +Swamp_MapChest_WaterFillRoom = 0x37 +Swamp_KeyPotRoom = 0x38 +Skull_GibdoKey_MothulaHoleRoom = 0x39 +PoD_BombableFloorRoom = 0x3a +PoD_SpikeBlock_ConveyorRoom = 0x3b +Cave0x3C_HookshotCave = 0x3c +GT_TorchRoom2 = 0x3d +Ice_StalfosKnights_ConveyorHellway = 0x3e +Ice_MapChestRoom = 0x3f +Tower_FinalBridgeRoom = 0x40 +HC_FirstDarkRoom = 0x41 +HC_6RopesRoom = 0x42 +Desert_TorchPuzzle_MovingWallRoom = 0x43 +TT_BigChestRoom = 0x44 +TT_JailCellsRoom = 0x45 +Swamp_CompassChestRoom = 0x46 +Skull_GibdoTorchPuzzleRoom = 0x49 +PoD_EntranceRoom = 0x4a +PoD_Warps_SouthMimicsRoom = 0x4b +GT_Mini_HelmasaurConveyorRoom = 0x4c +GT_MoldormRoom = 0x4d +Ice_Bomb_JumpRoom = 0x4e +IcePalaceCloneRoom_FairyRoom = 0x4f +HC_WestCorridor = 0x50 +HC_ThroneRoom = 0x51 +HC_EastCorridor = 0x52 +Desert_Popos2_BeamosHellwayRoom = 0x53 +Swamp_UpstairsPitsRoom = 0x54 +CastleSecretEntrance_UncleDeathRoom = 0x55 +Skull_KeyPot_TrapRoom = 0x56 +Skull_BigKeyRoom = 0x57 +Skull_BigChestRoom = 0x58 +Skull_FinalSectionEntranceRoom = 0x59 +PoD_HelmasaurKing = 0x5a +GT_SpikePitRoom = 0x5b +GT_Ganon_BallZ = 0x5c +GT_Gauntlet1_2_3 = 0x5d +Ice_LonelyFirebar = 0x5e +Ice_HiddenChest_SpikeFloorRoom = 0x5f +HC_WestEntranceRoom = 0x60 +HC_MainEntranceRoom = 0x61 +HC_EastEntranceRoom = 0x62 +Desert_FinalSectionEntranceRoom = 0x63 +TT_WestAtticRoom = 0x64 +TT_EastAtticRoom = 0x65 +Swamp_HiddenChest_HiddenDoorRoom = 0x66 +Skull_CompassChestRoom = 0x67 +Skull_KeyChest_TrapRoom = 0x68 +PoD_RupeeRoom = 0x6a +GT_MimicsRooms = 0x6b +GT_LanmolasRoom = 0x6c +GT_Gauntlet4_5 = 0x6d +Ice_PengatorsRoom = 0x6e +HC_SmallCorridortoJailCells = 0x70 +HC_BoomerangChestRoom = 0x71 +HC_MapChestRoom = 0x72 +Desert_BigChestRoom = 0x73 +Desert_MapChestRoom = 0x74 +Desert_BigKeyChestRoom = 0x75 +Swamp_WaterDrainRoom = 0x76 +Hera_EntranceRoom = 0x77 +GanonsTower = 0x7b +GT_EastSideCollapsingBridge_ExplodingWallRoom = 0x7c +GT_Winder_WarpMazeRoom = 0x7d +Ice_HiddenChest_BombableFloorRoom = 0x7e +Ice_BigSpikeTrapsRoom = 0x7f +HC_JailCellRoom = 0x80 +HC_NextToChasmRoom = 0x81 +HC_BasementChasmRoom = 0x82 +Desert_WestEntranceRoom = 0x83 +Desert_MainEntranceRoom = 0x84 +Desert_EastEntranceRoom = 0x85 +Hera_TileRoom = 0x87 +Eastern_FairyRoom = 0x89 +GT_BlockPuzzle_SpikeSkip_MapChestRoom = 0x8b +GT_EastandWestDownstairs_BigChestRoom = 0x8c +GT_Tile_TorchPuzzleRoom = 0x8d +IcePalace0x8E = 0x8e +Mire_Vitreous = 0x90 +Mire_FinalSwitchRoom = 0x91 +Mire_DarkBombWall_SwitchesRoom = 0x92 +Mire_DarkCaneFloorSwitchPuzzleRoom = 0x93 +GT_FinalCollapsingBridgeRoom = 0x95 +GT_Torches1Room = 0x96 +Mire_TorchPuzzle_MovingWallRoom = 0x97 +Mire_EntranceRoom = 0x98 +Eastern_EyegoreKeyRoom = 0x99 +GT_ManySpikes_WarpMazeRoom = 0x9b +GT_InvisibleFloorMazeRoom = 0x9c +GT_CompassChest_InvisibleFloorRoom = 0x9d +Ice_BigChestRoom = 0x9e +IcePalace0x9F = 0x9f +Mire_Pre_VitreousRoom = 0xa0 +Mire_FishRoom = 0xa1 +Mire_BridgeKeyChestRoom = 0xa2 +MiseryMire0xA3 = 0xa3 +TR_Trinexx = 0xa4 +GT_WizzrobesRooms = 0xa5 +GT_MoldormFallRoom = 0xa6 +Hera_FairyRoom = 0xa7 +Eastern_StalfosSpawnRoom = 0xa8 +Eastern_BigChestRoom = 0xa9 +Eastern_MapChestRoom = 0xaa +TT_MovingSpikes_KeyPotRoom = 0xab +TT_BlindTheThief = 0xac +IcePalace0xAE = 0xae +Ice_IceBridgeRoom = 0xaf +Tower_CircleofPots = 0xb0 +Mire_HourglassRoom = 0xb1 +Mire_SlugRoom = 0xb2 +Mire_SpikeKeyChestRoom = 0xb3 +TR_Pre_TrinexxRoom = 0xb4 +TR_DarkMaze = 0xb5 +TR_ChainChompsRoom = 0xb6 +TR_MapChest_KeyChest_RollerRoom = 0xb7 +Eastern_BigKeyRoom = 0xb8 +Eastern_LobbyCannonballsRoom = 0xb9 +Eastern_DarkAntifairy_KeyPotRoom = 0xba +TT_Hellway = 0xbb +TT_ConveyorToilet = 0xbc +Ice_BlockPuzzleRoom = 0xbe +IcePalaceCloneRoom_SwitchRoom = 0xbf +Tower_DarkBridgeRoom = 0xc0 +Mire_CompassChest_TileRoom = 0xc1 +Mire_BigHubRoom = 0xc2 +Mire_BigChestRoom = 0xc3 +TR_FinalCrystalSwitchPuzzleRoom = 0xc4 +TR_LaserBridge = 0xc5 +TurtleRock0xC6 = 0xc6 +TR_TorchPuzzle = 0xc7 +Eastern_ArmosKnights = 0xc8 +Eastern_EntranceRoom = 0xc9 +UnknownRoom = 0xca +TT_NorthWestEntranceRoom = 0xcb +TT_NorthEastEntranceRoom = 0xcc +Ice_HoletoKholdstareRoom = 0xce +Tower_DarkMaze = 0xd0 +Mire_ConveyorSlug_BigKeyRoom = 0xd1 +Mire_Mire02_WizzrobesRoom = 0xd2 +TR_LaserKeyRoom = 0xd5 +TR_EntranceRoom = 0xd6 +Eastern_PreArmosKnightsRoom = 0xd8 +Eastern_CanonballRoom = 0xd9 +EasternPalace = 0xda +TT_Main_SouthWestEntranceRoom = 0xdb +TT_SouthEastEntranceRoom = 0xdc +Ice_Kholdstare = 0xde +Cave_BackwardsDeathMountainTopFloor = 0xdf +Tower_EntranceRoom = 0xe0 +Cave_LostWoodsHP = 0xe1 +Cave_LumberjacksTreeHP = 0xe2 +Cave_HalfMagic = 0xe3 +Cave_LostOldManFinalCave = 0xe4 +Cave_LostOldManFinalCave2 = 0xe5 +Cave0xE6 = 0xe6 +Cave0xE7 = 0xe7 +Cave0xE8 = 0xe8 +Cave_SpectacleRockHP = 0xea +Cave0xEB = 0xeb +Cave0xED = 0xed +Cave_SpiralCave = 0xee +Cave_CrystalSwitch_5ChestsRoom = 0xef +Cave_LostOldManStartingCave = 0xf0 +Cave_LostOldManStartingCave2 = 0xf1 +House = 0xf2 +House_OldWoman = 0xf3 +House_AngryBrothers = 0xf4 +House_AngryBrothers2 = 0xf5 +Cave0xF8 = 0xf8 +Cave0xF9 = 0xf9 +Cave0xFA = 0xfa +Cave0xFB = 0xfb +Cave0xFD = 0xfd +Cave0xFE = 0xfe +Cave0xFF = 0xff +ShopInLostWoods0x100 = 0x100 +ScaredLadyHouses = 0x101 +SickKid = 0x102 +Inn_BushHouse = 0x103 +LinksHouse = 0x104 +ShabadooHouse = 0x105 +ChestGame_BombHouse = 0x106 +Library_BombFarmRoom = 0x107 +ChickenHouse = 0x108 +WitchHut = 0x109 +Aginah = 0x10a +SwampFloodwayRoom = 0x10b +MimicCave = 0x10c +CaveOutsideMiseryMire = 0x10d +Cave0x10E = 0x10e +Shop0x10F = 0x10f +Shop0x110 = 0x110 +ArcherGame = 0x111 +CaveShop0x112 = 0x112 +KingsTomb = 0x113 +WishingWell_Cave0x114 = 0x114 +WishingWell_BigFairy = 0x115 +FatFairy = 0x116 +SpikeCave = 0x117 +Shop0x118 = 0x118 +BlindsHouse = 0x119 +Mutant = 0x11a +MirrorCaveGroveAndTomb = 0x11b +BombShop = 0x11c +BlindsBasement = 0x11d +HypeCave = 0x11e +Shop0x11F = 0x11f +IceRodCave = 0x120 +SmithHouse = 0x121 +FortuneTellers = 0x122 +MiniMoldormCave = 0x123 +UnknownCave_BonkCave = 0x124 +Cave0x125 = 0x125 +CheckerBoardCave = 0x126 +HammerPegCave = 0x127 \ No newline at end of file diff --git a/source/dungeon/RoomHeader.py b/source/dungeon/RoomHeader.py new file mode 100644 index 00000000..13a4f7b7 --- /dev/null +++ b/source/dungeon/RoomHeader.py @@ -0,0 +1,326 @@ + + +vanilla_headers = { + 0x0000: [0x41, 0x21, 0x13, 0x22, 0x07, 0x3D, 0x00, 0x00, 0x00, 0x10, 0xC0, 0x00, 0x00, 0x04], + 0x0001: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x50, 0x52], + 0x0002: [0xC0, 0x1D, 0x04, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x18, 0x0D], + 0x0003: [0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00], + 0x0004: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x26, 0x14, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x08, 0x08], + 0x0005: [0x00, 0x08, 0x08, 0x14, 0x00, 0x25, 0x00, 0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00], + 0x0006: [0x00, 0x08, 0x08, 0x14, 0x00, 0x25, 0x00, 0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00], + 0x0007: [0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00, 0x00, 0x00, 0x17, 0x17, 0xC0, 0x07, 0x06], + 0x0008: [0xC0, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00], + 0x0009: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00, 0x00, 0x00, 0x4B, 0x4A, 0x4A, 0x00, 0x0F], + 0x000A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00, 0x00, 0x00, 0x09, 0x3A, 0x01, 0x0F, 0x07], + 0x000B: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6A, 0x1B, 0xC0, 0x28, 0x0E], + 0x000C: [0xC0, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x8C, 0x8C, 0x40], + 0x000D: [0x40, 0x1B, 0x0E, 0x18, 0x05, 0x38, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x08, 0x00], + 0x000E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x13], + 0x000F: [0x00, 0x21, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00], + 0x0010: [0x00, 0x21, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00], + 0x0011: [0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0xC0, 0x1D, 0x04], + 0x0012: [0xC0, 0x1D, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x0013: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00], + 0x0014: [0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0xC0, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x0015: [0xC0, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x90, 0x08, 0x08], + 0x0016: [0x90, 0x08, 0x08, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x20, 0x06, 0x05], + 0x0017: [0x20, 0x06, 0x05, 0x19, 0x00, 0x35, 0x00, 0x00, 0x00, 0x27, 0x07, 0x27, 0x01, 0x0F], + 0x0018: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x0019: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x16, 0x00], + 0x001A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x6A, 0x68, 0x0F], + 0x001B: [0x68, 0x0F, 0x07, 0x08, 0x00, 0x03, 0x1C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x1A, 0x0E], + 0x001C: [0x00, 0x1A, 0x0E, 0x09, 0x00, 0x04, 0x3F, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x1B, 0x0E], + 0x001D: [0x00, 0x1B, 0x0E, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x20, 0x13, 0x0B], + 0x001E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3E, 0x0E, 0x00, 0x13, 0x0B], + 0x001F: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x20, 0x0C, 0x02], + 0x0020: [0x20, 0x0C, 0x02, 0x12, 0x00, 0x15, 0x25, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00], + 0x0021: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00], + 0x0022: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00], + 0x0023: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00], + 0x0024: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00, 0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00], + 0x0025: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0x20], + 0x0026: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0x20], + 0x0027: [0x20, 0x06, 0x05, 0x19, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x17, 0x31, 0x80, 0x0A], + 0x0028: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x32, 0x1B, 0x00, 0x00, 0x00, 0x38, 0xCC, 0x0E, 0x09], + 0x0029: [0xCC, 0x0E, 0x09, 0x1A, 0x02, 0x25, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00], + 0x002A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x07, 0x2B, 0x00, 0x16, 0x00], + 0x002B: [0xC0, 0x0F, 0x07, 0x2B, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x13, 0x0B], + 0x002C: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x002D: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x2A, 0x00, 0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00], + 0x002E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x2A, 0x00, 0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00], + 0x002F: [0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00], + 0x0030: [0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x06, 0x05], + 0x0031: [0x20, 0x06, 0x05, 0x19, 0x00, 0x37, 0x04, 0x22, 0x00, 0x77, 0x27, 0x77, 0x01, 0x01], + 0x0032: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x04, 0x05], + 0x0033: [0x00, 0x04, 0x05, 0x0B, 0x00, 0x15, 0x25, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0034: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x80, 0x0A, 0x08], + 0x0035: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x19, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0036: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0037: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x19, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0038: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x20, 0x0D, 0x09], + 0x0039: [0x20, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x20, 0x0F, 0x07, 0x19], + 0x003A: [0x20, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x0F, 0x07], + 0x003B: [0x00, 0x0F, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x07, 0x06], + 0x003C: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x33, 0x00], + 0x003D: [0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x33, 0x00, 0x00, 0x00, 0x96, 0x96, 0xCC, 0x13, 0x0B], + 0x003E: [0xCC, 0x13, 0x0B, 0x29, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x13, 0x0B], + 0x003F: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x27, 0x14, 0x00, 0x00, 0x00, 0x1F, 0x5F, 0xC0, 0x00], + 0x0040: [0xC0, 0x00, 0x02, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB0, 0x01, 0x00], + 0x0041: [0x01, 0x00, 0x00, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x42, 0x01, 0x01, 0x01], + 0x0042: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x32, 0x68, 0x04], + 0x0043: [0x68, 0x04, 0x05, 0x0A, 0x00, 0x00, 0x1D, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00], + 0x0044: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00, 0x60, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00], + 0x0045: [0x60, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x0A, 0x08], + 0x0046: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x3C, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34], + 0x0047: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00], + 0x0048: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00], + 0x0049: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00], + 0x004A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x0F], + 0x004B: [0x00, 0x0F, 0x07, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x1A, 0x0E, 0x0C], + 0x004C: [0x00, 0x1A, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x1A, 0x0E], + 0x004D: [0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x32, 0x3F, 0x00, 0x00, 0xA6, 0xA6, 0x00, 0x13, 0x0B], + 0x004E: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x13, 0x0B], + 0x004F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xC0, 0x00, 0x00, 0x04], + 0x0050: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01], + 0x0051: [0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xC0, 0x00, 0x00], + 0x0052: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01], + 0x0053: [0xC0, 0x04, 0x05, 0x0A, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x63, 0x20, 0x0A, 0x08], + 0x0054: [0x20, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x34, 0x01, 0x01, 0x10], + 0x0055: [0x01, 0x01, 0x10, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x23, 0x00], + 0x0056: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x23, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x16, 0x00], + 0x0057: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x16, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x21, 0x28], + 0x0058: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x21, 0x28, 0xC0, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00], + 0x0059: [0xC0, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x07, 0x15, 0x00, 0x25, 0x00], + 0x005A: [0x00, 0x10, 0x07, 0x15, 0x00, 0x25, 0x00, 0xC0, 0x1B, 0x0E, 0x0A, 0x00, 0x17, 0x00], + 0x005B: [0xC0, 0x1B, 0x0E, 0x0A, 0x00, 0x17, 0x00, 0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x00, 0x00], + 0x005C: [0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x24, 0x0E], + 0x005D: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x20, 0x13, 0x0B], + 0x005E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x13, 0x0B], + 0x005F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0xC0, 0x00], + 0x0060: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00], + 0x0061: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x51, 0x00, 0x09, 0x05], + 0x0062: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00], + 0x0063: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x53, 0xE0, 0x23, 0x0A], + 0x0064: [0xE0, 0x23, 0x0A, 0x21, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xE0, 0x23, 0x0A], + 0x0065: [0xE0, 0x23, 0x0A, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xC0, 0x0A, 0x08, 0x11], + 0x0066: [0xC0, 0x0A, 0x08, 0x11, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x0D, 0x09], + 0x0067: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x22, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00], + 0x0068: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00], + 0x0069: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x1B], + 0x006A: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x1B], + 0x006B: [0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x08, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x24, 0x0E], + 0x006C: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x03, 0x3F, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x24, 0x0E], + 0x006D: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x05, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x02, 0x00], + 0x006E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x01, 0x01], + 0x006F: [0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x71, 0x80, 0xC0, 0x01], + 0x0070: [0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x71, 0x80, 0xC0, 0x01], + 0x0071: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x01], + 0x0072: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x09, 0x05], + 0x0073: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x17, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x27, 0x00], + 0x0074: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x27, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x01, 0x00], + 0x0075: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x01, 0x00, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x18], + 0x0076: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26, 0xC0], + 0x0077: [0xC0, 0x06, 0x05, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0x31, 0x87, 0x87, 0x00], + 0x0078: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x0079: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x007A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x007B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x007C: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x20, 0x00, 0x00, 0x28, 0x0E, 0x13, 0x00, 0x04, 0x3C], + 0x007D: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x9B, 0x20, 0x13, 0x0B, 0x1C], + 0x007E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x2B, 0x17, 0x00, 0x00, 0x9E, 0x5E, 0x00, 0x13, 0x0B], + 0x007F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x60, 0x01, 0x01], + 0x0080: [0x60, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x01], + 0x0081: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00], + 0x0082: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00], + 0x0083: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00], + 0x0084: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x02, 0x00], + 0x0085: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01], + 0x0086: [0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01, 0x28, 0x00, 0x00, 0x77, 0x77, 0x00, 0x0B], + 0x0087: [0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01, 0x28, 0x00, 0x00, 0x77, 0x77, 0x00, 0x0B], + 0x0088: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x28, 0x0E, 0x13], + 0x0089: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x28, 0x0E, 0x13], + 0x008A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x3A, 0x0C, 0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00], + 0x008B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x3A, 0x0C, 0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00], + 0x008C: [0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00, 0x28, 0x00, 0x1C, 0x0C, 0x0C, 0x1C, 0x00], + 0x008D: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x33, 0x29, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00], + 0x008E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x80, 0x12, 0x0C], + 0x008F: [0x80, 0x12, 0x0C, 0x16, 0x00, 0x25, 0x00, 0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00], + 0x0090: [0x80, 0x12, 0x0C, 0x16, 0x00, 0x25, 0x00, 0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00], + 0x0091: [0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x11, 0x0C], + 0x0092: [0x01, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x11, 0x0C, 0x1C, 0x00, 0x16, 0x00], + 0x0093: [0x01, 0x11, 0x0C, 0x1C, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0xA2, 0x00, 0x25, 0x0E], + 0x0094: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00], + 0x0095: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00], + 0x0096: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x68, 0x11, 0x0C], + 0x0097: [0x68, 0x11, 0x0C, 0x1D, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x11, 0x0C], + 0x0098: [0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0x01, 0x0B, 0x05], + 0x0099: [0x01, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x28, 0x0E], + 0x009A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x28, 0x0E, 0x13], + 0x009B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x28, 0x0E, 0x13], + 0x009C: [0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x00, 0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x3B], + 0x009D: [0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x3B, 0x00, 0x00, 0x7B, 0x20, 0x13, 0x0B, 0x1C], + 0x009E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xBE, 0x00, 0x13, 0x0B], + 0x009F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x17, 0x00, 0x00, 0x12, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00A0: [0x00, 0x12, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x11, 0x0C], + 0x00A1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00A2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x60, 0x19, 0x0D], + 0x00A3: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00A4: [0x60, 0x19, 0x0D, 0x17, 0x04, 0x25, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x07, 0x00], + 0x00A5: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x25, 0x0E], + 0x00A6: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x06, 0x05], + 0x00A7: [0x00, 0x06, 0x05, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xC0, 0x0B, 0x05, 0x08], + 0x00A8: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x03, 0x00, 0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00A9: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x89, 0xC0, 0x0B, 0x05, 0x08], + 0x00AA: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00], + 0x00AB: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xE0, 0x17, 0x0A], + 0x00AC: [0xE0, 0x17, 0x0A, 0x20, 0x00, 0x25, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00], + 0x00AD: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x13, 0x0B], + 0x00AE: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x13, 0x0B], + 0x00AF: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x26, 0x02, 0x21, 0x00, 0x05, 0x02], + 0x00B0: [0x00, 0x26, 0x02, 0x21, 0x00, 0x05, 0x02, 0x08, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x11], + 0x00B1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB2, 0xC0, 0x11, 0x0C, 0x1D], + 0x00B2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x03, 0x0E, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00], + 0x00B3: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00, 0x00, 0x19, 0x0D, 0x17, 0x00, 0x00, 0x00], + 0x00B4: [0x00, 0x19, 0x0D, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x18, 0x0D], + 0x00B5: [0x01, 0x18, 0x0D, 0x25, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x18, 0x0D], + 0x00B6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x0B, 0x05], + 0x00B7: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x00B8: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x27, 0x00, 0xC0, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00], + 0x00B9: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x01, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00BA: [0x01, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x40, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00], + 0x00BB: [0x40, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x17, 0x00], + 0x00BC: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x13, 0x0B], + 0x00BD: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4F, 0x9E, 0x00, 0x13, 0x0B], + 0x00BE: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4F, 0x9E, 0x00, 0x13, 0x0B], + 0x00BF: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x27, 0x00, 0x02, 0x0F], + 0x00C0: [0x01, 0x00, 0x02, 0x27, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0xB0, 0xD0, 0x00, 0x11], + 0x00C1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x33, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00], + 0x00C2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00C3: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00], + 0x00C4: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x18, 0x0D], + 0x00C5: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00], + 0x00C6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x00C7: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00, 0x00, 0x0B, 0x05, 0x09, 0x00, 0x15, 0x25], + 0x00C8: [0x00, 0x0B, 0x05, 0x09, 0x00, 0x15, 0x25, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00C9: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00], + 0x00CA: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00CB: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00CC: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00CD: [0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x02, 0x21], + 0x00CE: [0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x02, 0x21], + 0x00CF: [0x01, 0x00, 0x02, 0x21, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x11], + 0x00D0: [0x01, 0x00, 0x02, 0x21, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x11], + 0x00D1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x97, 0x00, 0x11, 0x0C], + 0x00D2: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x0B, 0x05], + 0x00D3: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D4: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D5: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00], + 0x00D6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x00D7: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D8: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D9: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00DA: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x99, 0xE0, 0x14, 0x0B], + 0x00DB: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00DC: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00DD: [0xE0, 0x14, 0x0B, 0x16, 0x00, 0x25, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00DE: [0xE0, 0x14, 0x0B, 0x16, 0x00, 0x25, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00DF: [0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x26, 0x02], + 0x00E0: [0x00, 0x26, 0x02, 0x21, 0x00, 0x01, 0x2A, 0x00, 0x00, 0x00, 0xD0, 0xC0, 0x07, 0x06], + 0x00E1: [0xC0, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00E2: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x09, 0x00, 0x00, 0x00], + 0x00E3: [0xC0, 0x20, 0x06, 0x09, 0x00, 0x00, 0x00, 0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00], + 0x00E4: [0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00], + 0x00E5: [0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00], + 0x00E6: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00E7: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00E8: [0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8], + 0x00E9: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFA, 0x20, 0x07, 0x06], + 0x00EA: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFA, 0x20, 0x07, 0x06], + 0x00EB: [0x20, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xFB, 0x20, 0x20, 0x06], + 0x00EC: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0x20, 0x20], + 0x00ED: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0x20, 0x20], + 0x00EE: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x20, 0x20, 0x06, 0x13], + 0x00EF: [0x20, 0x20, 0x06, 0x13, 0x00, 0x02, 0x00, 0x08, 0x00, 0xFF, 0xDF, 0xFF, 0x00, 0x02], + 0x00F0: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F1: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F2: [0x00, 0x02, 0x03, 0x05, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07], + 0x00F3: [0x00, 0x02, 0x03, 0x05, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07], + 0x00F4: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F5: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F6: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8], + 0x00F7: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8], + 0x00F8: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8], + 0x00F9: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00FA: [0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x07, 0x06], + 0x00FB: [0x00, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x20, 0x06], + 0x00FC: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0x00, 0x07], + 0x00FD: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0x00, 0x07], + 0x00FE: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00FF: [0x00, 0x07, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x05, 0x03], + 0x0100: [0x00, 0x05, 0x03, 0x28, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x0101: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00], + 0x0102: [0x00, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0103: [0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00], + 0x0104: [0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0F, 0x10, 0x00, 0x00, 0x00], + 0x0105: [0x00, 0x1C, 0x0F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0106: [0x00, 0x1F, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00], + 0x0107: [0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0E, 0x00, 0x00, 0x00], + 0x0108: [0x00, 0x02, 0x03, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x0109: [0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x10, 0x00, 0x00, 0x00], + 0x010A: [0x01, 0x07, 0x06, 0x10, 0x00, 0x00, 0x00, 0x80, 0x0A, 0x08, 0x08, 0x00, 0x00, 0x1A], + 0x010B: [0x80, 0x0A, 0x08, 0x08, 0x00, 0x00, 0x1A, 0x00, 0x27, 0x06, 0x08, 0x00, 0x03, 0x00], + 0x010C: [0x00, 0x27, 0x06, 0x08, 0x00, 0x03, 0x00, 0x00, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x010D: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x010E: [0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00], + 0x010F: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0110: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0111: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0112: [0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x08, 0x00, 0x00, 0x00], + 0x0113: [0x00, 0x03, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00], + 0x0114: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x0115: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x0116: [0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x20, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0117: [0x00, 0x20, 0x14, 0x05, 0x00, 0x00, 0x00, 0xE0, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00], + 0x0118: [0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00], + 0x0119: [0xE0, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x1C, 0x0F], + 0x011A: [0x00, 0x1C, 0x0F, 0x05, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x06, 0x08, 0x00, 0x00, 0x00], + 0x011B: [0xC0, 0x07, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00], + 0x011C: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x011D: [0x00, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x20, 0x06], + 0x011E: [0x00, 0x20, 0x06, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x011F: [0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x0120: [0x00, 0x13, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x28, 0x00, 0x03, 0x00], + 0x0121: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0122: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0123: [0x00, 0x07, 0x06, 0x28, 0x00, 0x03, 0x00, 0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00], + 0x0124: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0125: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0126: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0127: [0x00, 0x20, 0x06, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x0128: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0129: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012A: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012B: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012C: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012D: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012E: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] +} + + +class RoomHeader: + def __init__(self, room_id, byte_array): + self.room_id = room_id + + # todo: the rest of the header + self.sprite_sheet = byte_array[3] + + def write_to_rom(self, rom, base_address): + rom.write_byte(base_address + self.room_id*14 + 3, self.sprite_sheet) + + +def init_room_headers(): + header_table = {} + for room_id, header_bytes in vanilla_headers.items(): + header_table[room_id] = RoomHeader(room_id, header_bytes) + return header_table + diff --git a/source/dungeon/RoomList.py b/source/dungeon/RoomList.py index 4b8c2a75..ceff9405 100644 --- a/source/dungeon/RoomList.py +++ b/source/dungeon/RoomList.py @@ -1,3 +1,9 @@ +try: + from fast_enum import FastEnum +except ImportError: + from enum import IntFlag as FastEnum + + from RoomData import DoorKind, Position from source.dungeon.RoomObject import RoomObject, DoorObject diff --git a/source/dungeon/__init__.py b/source/dungeon/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/dungeon/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py new file mode 100644 index 00000000..dfea8102 --- /dev/null +++ b/source/enemizer/Enemizer.py @@ -0,0 +1,253 @@ +import RaceRandom as random + +from source.dungeon.EnemyList import SpriteType +from source.enemizer.SpriteSheets import uw_sub_group_choices, setup_required_dungeon_groups + +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 + 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}, + 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() + water_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + killable_groups = set() + killable_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + key_groups = set() + key_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + for sid, requirement in requirements.items(): + if isinstance(requirement, dict): + continue + if requirement.good_for_uw_water(): + water_groups.update(requirement.groups) + for i in range(0, 4): + limited = [x for x in requirement.sub_groups[i] if x in uw_sub_group_choices[i]] + water_sub_groups[i].update(limited) + if requirement.good_for_shutter(): + killable_groups.update(requirement.groups) + for i in range(0, 4): + killable_sub_groups[i].update(requirement.sub_groups[i]) + if requirement.can_drop: + key_groups.update(requirement.groups) + for i in range(0, 4): + key_sub_groups[i].update(requirement.sub_groups[i]) + return water_groups, water_sub_groups, killable_groups, killable_sub_groups, key_groups, key_sub_groups + + +def get_possible_sheets(room_id, data_tables, specific, uw_sheets): + # forced sprites for room + requirements = data_tables.sprite_requirements + + water_groups, water_sub_groups, killable_groups, killable_sub_groups, key_groups, key_sub_groups = specific + + # forced_req = set() + key_needed = False + killable_needed = room_id in shutter_sprites + water_needed = room_id in water_rooms + + for sheet in data_tables.sprite_sheets.values(): + if room_id in sheet.room_set: + return [sheet] + + match_all_room_groups = set() + match_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + # match_all_sub_groups = {0: set(uw_sub_group_choices[0] + [70, 72]), 1: set(uw_sub_group_choices[1] + [13, 73]), + # 2: set(uw_sub_group_choices[2] + [19]), 3: set(uw_sub_group_choices[3] + [25, 68])} + + for sprite in data_tables.uw_enemy_table.room_map[room_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[room_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]) + # forced_req.add(req) + if sprite.drops_item: + key_needed = True + + match_any_room_groups = set() + match_any_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + 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 key_needed: + if key_groups: + match_any_room_groups.update(key_groups) + for i in range(0, 4): + if key_sub_groups[i]: + match_any_sub_groups[i].update(key_sub_groups[i]) + elif killable_needed: + if killable_groups: + match_any_room_groups.update(killable_groups) + for i in range(0, 4): + if killable_sub_groups[i]: + match_any_sub_groups[i].update(killable_sub_groups[i]) + + possible_sheets = [] + for sheet in uw_sheets: + str(sheet) + if match_all_room_groups and sheet 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: + 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: + 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): + continue + possible_sheets.append(sheet) + return possible_sheets + + +def uw_candidate_sprites(data_tables): + requirements = data_tables.sprite_requirements + uw_sprite_candidates = [] + uw_sheet_candidates = [] + + candidate_groups = set() + candidate_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + for k, r in requirements.items(): + if isinstance(r, dict): + continue + if not r.static and r.uw_valid 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) + + for num in range(65, 124): + sheet = data_tables.sprite_sheets[num] + 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) + + return uw_sprite_candidates, uw_sheet_candidates + + +def get_possible_enemy_sprites(room_id, sheet, uw_sprites, data_tables): + ret = [] + for sprite in uw_sprites: + requirement = data_tables.sprite_requirements[sprite] + if isinstance(requirement, dict): + requirement = requirement[room_id] + if sheet.valid_sprite(requirement) and requirement.can_spawn_in_room(room_id): + ret.append(requirement) + return ret + + +def get_randomize_able_sprites(room_id, data_tables): + sprite_table = {} + for idx, sprite in enumerate(data_tables.uw_enemy_table.room_map[room_id]): + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in data_tables.sprite_requirements: + continue + req = data_tables.sprite_requirements[key] + if isinstance(req, dict): + continue + if not req.static and req.can_randomize: + sprite_table[idx] = sprite + 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 = uw_candidate_sprites(data_tables) + 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}: + 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 randomizeable_sprites: + candidate_sheets = get_possible_sheets(room_id, data_tables, specific, uw_sheets) + 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) + if room_id in water_rooms: + water_sprites = [x for x in candidate_sprites if x.water_only] + 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: + key_sprites = [x for x in candidate_sprites if x.good_for_key_drop() and not x.water_only] + chosen = random.choice(key_sprites) + elif room_id in shutter_sprites and i in shutter_sprites[room_id]: + killable_sprite = [x for x in candidate_sprites if x.good_for_shutter() and not x.water_only] + chosen = random.choice(killable_sprite) + else: + non_water = [x for x in candidate_sprites if not x.water_only] + chosen = random.choice(non_water) + sprite.kind = chosen.sprite + # done with sprites + # done with rooms \ No newline at end of file diff --git a/source/enemizer/EnemizerTestHarness.py b/source/enemizer/EnemizerTestHarness.py new file mode 100644 index 00000000..09395da1 --- /dev/null +++ b/source/enemizer/EnemizerTestHarness.py @@ -0,0 +1,19 @@ +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.rom.DataTables import init_data_tables +import RaceRandom as random + +if __name__ == '__main__': + random.seed(42) + data_tables = init_data_tables(None, None) + + 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: + print(f' Overlord #{i+1} {hex(sprite.kind)}:') + else: + print(f' Enemy #{i+1} {enemy_names[sprite.kind]}:') diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py new file mode 100644 index 00000000..9b6a877a --- /dev/null +++ b/source/enemizer/SpriteSheets.py @@ -0,0 +1,630 @@ +from collections import defaultdict +import RaceRandom as random + +from source.dungeon.EnemyList import EnemySprite, enemy_names +from source.dungeon.RoomConstants import * + + +class SpriteRequirement: + def __init__(self, sprite, overlord=0): + self.sprite = sprite + self.overlord = overlord + + self.boss = False + self.static = False # npcs and do not randomize + self.killable = True + self.can_drop = True + self.water_only = False + self.dont_use = False + self.ow_valid = True + self.uw_valid = True + self.can_randomize = True + + self.groups = [] + self.sub_groups = defaultdict(list) + + self.excluded_rooms = set() + self.allowed_rooms = set() + + def can_spawn_in_room(self, room_id): + return room_id not in self.excluded_rooms and (self.sprite != EnemySprite.Wallmaster or room_id < 0x100) + + def no_drop(self): + self.can_drop = False + return self + + def sub_group(self, key, subs): + if isinstance(subs, list): + self.sub_groups[key].extend(subs) + else: + self.sub_groups[key].append(subs) + return self + + def group(self, group_id): + self.groups.append(group_id) + return self + + def exclude(self, exclusions): + self.excluded_rooms.update(exclusions) + return self + + def allow(self, allowed): + self.allowed_rooms.update(allowed) + return self + + def affix(self): + self.static = True + self.killable = False + self.can_drop = False + return self + + def stasis(self): + self.can_randomize = False + return self + + def exalt(self): + self.boss = True + self.static = True # not randomized by sprite sheet + return self + + def immune(self): + self.killable = False + self.can_drop = False + return self + + def immerse(self): + self.water_only = True + return self + + def skip(self): + self.dont_use = True + return self + + def ow_skip(self): + self.ow_valid = False + return self + + def uw_skip(self): + self.uw_valid = False + return self + + def good_for_uw_water(self): + return self.water_only and not self.static and not self.dont_use and self.uw_valid + + def good_for_shutter(self): + return self.killable and not self.static and not self.dont_use and self.uw_valid + + def good_for_key_drop(self): + return self.good_for_shutter() and self.can_drop + + def __str__(self): + return f'Req for {enemy_names[self.sprite]}' + + +NoFlyingRooms = {0xd2, 0x10c} # Mire 2, Mimic Cave +NoBeamosOrTrapRooms = {0xb, 0x16, 0x19, 0x1e, 0x26, 0x27, 0x36, 0x3f, 0x40, 0x42, 0x46, 0x49, 0x4b, 0x4e, 0x55, 0x57, + 0x5f, 0x65, 0x6a, 0x74, 0x76, 0x7d, 0x7f, 0x83, 0x84, 0x85, 0x8c, 0x8d, 0x92, 0x95, 0x98, 0x9b, + 0x9c, 0x9d, 0x9e, 0xa0, 0xaa, 0xaf, 0xb3, 0xba, 0xbb, 0xbc, 0xc6, 0xcb, 0xce, 0xd0, 0xd2, 0xd5, + 0xd8, 0xdc, 0xdf, 0xe4, 0xe7, 0xee, 0xf9, 0xfd, 0x10c} +LenientTrapsForTesting = {0x16, 0x26, 0x3f, 0x40, 0x42, 0x46, 0x49, 0x4e, 0x57, + 0x65, 0x6a, 0x74, 0x76, 0x7d, 0x98, + 0x9e, 0xaf, 0xba, 0xc6, 0xcb, 0xce, 0xd2, 0xd5, + 0xd8, 0xdf, 0xe4, 0xe7, 0xee, 0xfd, 0x10c} +# this will have to be dynamic if cave rooms are allowed in dungeons +WallmasterValidRooms = { + HC_NorthCorridor, HC_SwitchRoom, HoulihanRoom, TR_CrystalRollerRoom, + PalaceofDarkness0x09, PoD_StalfosTrapRoom, PoD_TurtleRoom, GT_EntranceRoom, Ice_EntranceRoom, + GanonEvacuationRoute, HC_BombableStockRoom, Sanctuary, TR_Hokku_BokkuKeyRoom2, TR_BigKeyRoom, TurtleRock0x15, + Swamp_SwimmingTreadmill, Hera_MoldormFallRoom, PoD_DarkMaze, PoD_BigChestRoom, PoD_Mimics_MovingWallRoom, + GT_IceArmos, GT_FinalHallway, Ice_BombFloor_BariRoom, Ice_Pengator_BigKeyRoom, Tower_Agahnim, HC_KeyRatRoom, + HC_SewerTextTriggerRoom, TR_WestExittoBalcony, TR_DoubleHokku_Bokku_BigchestRoom, Swamp_StatueRoom, Hera_BigChest, + Swamp_EntranceRoom, Skull_Mothula, PoD_BigHubRoom, PoD_MapChest_FairyRoom, Ice_CompassRoom, Hera_HardhatBeetlesRoom, + HC_SewerKeyChestRoom, Desert_Lanmolas, Swamp_PushBlockPuzzle_Pre_BigKeyRoom, Swamp_BigKey_BSRoom, + Swamp_BigChestRoom, Swamp_MapChest_WaterFillRoom, Swamp_KeyPotRoom, Skull_GibdoKey_MothulaHoleRoom, + PoD_BombableFloorRoom, PoD_SpikeBlock_ConveyorRoom, GT_TorchRoom2, Ice_StalfosKnights_ConveyorHellway, + Ice_MapChestRoom, Tower_FinalBridgeRoom, HC_FirstDarkRoom, HC_6RopesRoom, Desert_TorchPuzzle_MovingWallRoom, + TT_BigChestRoom, TT_JailCellsRoom, Swamp_CompassChestRoom, Skull_GibdoTorchPuzzleRoom, PoD_EntranceRoom, + PoD_Warps_SouthMimicsRoom, GT_Mini_HelmasaurConveyorRoom, GT_MoldormRoom, Ice_Bomb_JumpRoom, + IcePalaceCloneRoom_FairyRoom, HC_WestCorridor, HC_ThroneRoom, HC_EastCorridor, Desert_Popos2_BeamosHellwayRoom, + Swamp_UpstairsPitsRoom, CastleSecretEntrance_UncleDeathRoom, Skull_KeyPot_TrapRoom, Skull_BigKeyRoom, + Skull_BigChestRoom, Skull_FinalSectionEntranceRoom, PoD_HelmasaurKing, GT_SpikePitRoom, GT_Ganon_BallZ, + GT_Gauntlet1_2_3, Ice_LonelyFirebar, Ice_HiddenChest_SpikeFloorRoom, HC_WestEntranceRoom, HC_MainEntranceRoom, + HC_EastEntranceRoom, Desert_FinalSectionEntranceRoom, TT_WestAtticRoom, TT_EastAtticRoom, + Swamp_HiddenChest_HiddenDoorRoom, Skull_CompassChestRoom, Skull_KeyChest_TrapRoom, PoD_RupeeRoom, GT_MimicsRooms, + GT_LanmolasRoom, GT_Gauntlet4_5, Ice_PengatorsRoom, HC_SmallCorridortoJailCells, HC_BoomerangChestRoom, + HC_MapChestRoom, Desert_BigChestRoom, Desert_MapChestRoom, Desert_BigKeyChestRoom, Swamp_WaterDrainRoom, + Hera_EntranceRoom, GanonsTower, GT_EastSideCollapsingBridge_ExplodingWallRoom, GT_Winder_WarpMazeRoom, + Ice_HiddenChest_BombableFloorRoom, Ice_BigSpikeTrapsRoom, HC_JailCellRoom, HC_NextToChasmRoom, HC_BasementChasmRoom, + Desert_WestEntranceRoom, Desert_MainEntranceRoom, Desert_EastEntranceRoom, Hera_TileRoom, Eastern_FairyRoom, + GT_BlockPuzzle_SpikeSkip_MapChestRoom, GT_EastandWestDownstairs_BigChestRoom, GT_Tile_TorchPuzzleRoom, + IcePalace0x8E, Mire_Vitreous, Mire_FinalSwitchRoom, Mire_DarkBombWall_SwitchesRoom, + Mire_DarkCaneFloorSwitchPuzzleRoom, GT_FinalCollapsingBridgeRoom, GT_Torches1Room, Mire_TorchPuzzle_MovingWallRoom, + Mire_EntranceRoom, Eastern_EyegoreKeyRoom, GT_ManySpikes_WarpMazeRoom, GT_InvisibleFloorMazeRoom, + GT_CompassChest_InvisibleFloorRoom, Ice_BigChestRoom, IcePalace0x9F, Mire_Pre_VitreousRoom, Mire_FishRoom, + Mire_BridgeKeyChestRoom, MiseryMire0xA3, TR_Trinexx, GT_WizzrobesRooms, GT_MoldormFallRoom, Hera_FairyRoom, + Eastern_StalfosSpawnRoom, Eastern_BigChestRoom, Eastern_MapChestRoom, TT_MovingSpikes_KeyPotRoom, TT_BlindTheThief, + IcePalace0xAE, Ice_IceBridgeRoom, Tower_CircleofPots, Mire_HourglassRoom, Mire_SlugRoom, Mire_SpikeKeyChestRoom, + TR_Pre_TrinexxRoom, TR_DarkMaze, TR_ChainChompsRoom, TR_MapChest_KeyChest_RollerRoom, Eastern_BigKeyRoom, + Eastern_LobbyCannonballsRoom, Eastern_DarkAntifairy_KeyPotRoom, TT_Hellway, TT_ConveyorToilet, Ice_BlockPuzzleRoom, + IcePalaceCloneRoom_SwitchRoom, Tower_DarkBridgeRoom, Mire_CompassChest_TileRoom, Mire_BigHubRoom, Mire_BigChestRoom, + TR_FinalCrystalSwitchPuzzleRoom, TR_LaserBridge, TurtleRock0xC6, TR_TorchPuzzle, + Eastern_EntranceRoom, UnknownRoom, TT_NorthWestEntranceRoom, TT_NorthEastEntranceRoom, Ice_HoletoKholdstareRoom, + Tower_DarkMaze, Mire_ConveyorSlug_BigKeyRoom, Mire_Mire02_WizzrobesRoom, TR_LaserKeyRoom, TR_EntranceRoom, + Eastern_PreArmosKnightsRoom, Eastern_CanonballRoom, EasternPalace, TT_Main_SouthWestEntranceRoom, + TT_SouthEastEntranceRoom, Tower_EntranceRoom +} + + +def init_sprite_requirements(): + reqs = [ + SpriteRequirement(EnemySprite.Raven).no_drop().sub_group(3, [0x11, 0x19]).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Vulture).no_drop().sub_group(2, 0x12).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.CorrectPullSwitch).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.WrongPullSwitch).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.Octorok).sub_group(2, [0xc, 0x18]), + 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), + SpriteRequirement(EnemySprite.Hoarder).no_drop().sub_group(3, 0x11).exclude({0x10c}), + 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.CrystalSwitch).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.SickKid).affix().sub_group(0, 0x51), + SpriteRequirement(EnemySprite.Sluggula).sub_group(2, 0x25), + SpriteRequirement(EnemySprite.WaterSwitch).affix().sub_group(3, 0x53), + 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]), + SpriteRequirement(EnemySprite.SweepingLady).affix().group(6), # no sub groups? + SpriteRequirement(EnemySprite.Lumberjacks).affix().sub_group(2, 0x4a), + SpriteRequirement(EnemySprite.RaceGameLady).affix().group(6), + SpriteRequirement(EnemySprite.FortuneTeller).affix().sub_group(0, 0x4b), + SpriteRequirement(EnemySprite.ArgueBros).affix().sub_group(0, 0x4f), + SpriteRequirement(EnemySprite.RupeePull).affix(), + SpriteRequirement(EnemySprite.YoungSnitch).affix().group(6), + SpriteRequirement(EnemySprite.Innkeeper).affix(), # no groups? + SpriteRequirement(EnemySprite.Witch).affix().sub_group(2, 0x7c), + SpriteRequirement(EnemySprite.Waterfall).affix(), + SpriteRequirement(EnemySprite.EyeStatue).affix(), + SpriteRequirement(EnemySprite.Locksmith).affix().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.MagicBat).affix().sub_group(3, 0x1d), + SpriteRequirement(EnemySprite.KidInKak).affix().group(6), + SpriteRequirement(EnemySprite.OldSnitch).affix().group(6), + SpriteRequirement(EnemySprite.Hoarder2).no_drop().sub_group(3, 0x11).exclude({0x10c}), + SpriteRequirement(EnemySprite.TutorialGuard).affix(), + SpriteRequirement(EnemySprite.LightningGate).affix().sub_group(3, 0x3f), + SpriteRequirement(EnemySprite.BlueGuard).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.GreenGuard).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.RedSpearGuard).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.BluesainBolt).sub_group(0, 0x46).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.UsainBolt).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.BlueArcher).sub_group(0, 0x48).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.GreenBushGuard).sub_group(0, 0x48).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.RedJavelinGuard).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.RedBushGuard).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.BombGuard).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.GreenKnifeGuard).sub_group(1, 0x49).sub_group(2, 0x13), + SpriteRequirement(EnemySprite.Geldman).no_drop().sub_group(2, 0x12).exclude({0x10c}), + SpriteRequirement(EnemySprite.Toppo).no_drop().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.Popo).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.Popo2).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.Popo2).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.ArmosStatue).sub_group(3, 0x10).exclude({0x10c}), + 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.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), + SpriteRequirement(EnemySprite.LostWoodsSquirrel).affix().sub_group(2, 0x37).sub_group(3, 0x36), + SpriteRequirement(EnemySprite.SparkCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.SparkCCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.RollerVerticalUp).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), + 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.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.ArcheryNpc).affix().sub_group(0, 0x4b), + SpriteRequirement(EnemySprite.WallCannonVertLeft).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.WallCannonVertRight).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.WallCannonHorzTop).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.WallCannonHorzBottom).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.BallNChain).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.CannonTrooper).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.CricketRat).sub_group(2, [0x1c, 0x24]), + SpriteRequirement(EnemySprite.Snake).sub_group(2, [0x1c, 0x24]), + SpriteRequirement(EnemySprite.Keese).no_drop().sub_group(2, [0x1c, 0x24]), + SpriteRequirement(EnemySprite.Leever).no_drop().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.FairyPondTrigger).affix().sub_group(3, 0x36), + SpriteRequirement(EnemySprite.UnclePriest).affix().sub_group(0, [0x47, 0x51]), + SpriteRequirement(EnemySprite.RunningNpc).affix().group(6), + SpriteRequirement(EnemySprite.BottleMerchant).affix().group(6), + SpriteRequirement(EnemySprite.Zelda).affix(), + SpriteRequirement(EnemySprite.Grandma).affix().sub_group(0, 0x4b).sub_group(1, 0x4d).sub_group(2, 0x4a), + 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.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.GreenEyegoreMimic).sub_group(2, 0x2e), + SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e), + # kodongos apparently broken? + SpriteRequirement(EnemySprite.Kondongo).skip().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? + SpriteRequirement(EnemySprite.Gibdo).sub_group(2, 0x23), + SpriteRequirement(EnemySprite.Arrghus).exalt().sub_group(2, 0x39), + 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.HelmasaurKing).exalt().sub_group(2, 0x3a).sub_group(3, 0x3e), + SpriteRequirement(EnemySprite.Bumper).affix().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), + SpriteRequirement(EnemySprite.GroveOstritch).affix().sub_group(2, 0x4e), + SpriteRequirement(EnemySprite.GroveRabbit).affix(), + SpriteRequirement(EnemySprite.GroveBird).affix().sub_group(2, 0x4e), + SpriteRequirement(EnemySprite.Freezor).stasis().skip().sub_group(2, 0x26), + SpriteRequirement(EnemySprite.Kholdstare).exalt().sub_group(2, 0x3c), + SpriteRequirement(EnemySprite.KholdstareShell).exalt(), + SpriteRequirement(EnemySprite.FallingIce).exalt().sub_group(2, 0x3c), + SpriteRequirement(EnemySprite.BlueZazak).sub_group(2, 0x28), + SpriteRequirement(EnemySprite.RedZazak).sub_group(2, 0x28), + SpriteRequirement(EnemySprite.Stalfos).sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.GreenZirro).no_drop().sub_group(3, 0x1b).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.BlueZirro).no_drop().sub_group(3, 0x1b).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Pikit).no_drop().sub_group(3, 0x1b), + SpriteRequirement(EnemySprite.CrystalMaiden).affix(), + SpriteRequirement(EnemySprite.OldMan).affix().sub_group(0, 0x46).sub_group(1, 0x49).sub_group(2, 0x1c), + SpriteRequirement(EnemySprite.PipeDown).affix(), + SpriteRequirement(EnemySprite.PipeUp).affix(), + SpriteRequirement(EnemySprite.PipeRight).affix(), + SpriteRequirement(EnemySprite.PipeLeft).affix(), + SpriteRequirement(EnemySprite.GoodBee).affix().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.PurpleChest).affix().sub_group(3, 0x15), + SpriteRequirement(EnemySprite.BombShopGuy).affix().sub_group(1, 0x4d), + SpriteRequirement(EnemySprite.Kiki).affix().sub_group(3, 0x19), + SpriteRequirement(EnemySprite.BlindMaiden).affix(), + # dialog tester.sub_group(1, 0x2c) + SpriteRequirement(EnemySprite.BullyPinkBall).affix().sub_group(3, 0x14), + # shop keepers in complex thing below + SpriteRequirement(EnemySprite.Drunkard).affix().sub_group(0, 0x4f).sub_group(1, 0x4d).sub_group(2, 0x4a). + sub_group(3, 0x50), + SpriteRequirement(EnemySprite.Vitreous).exalt().sub_group(3, 0x3d), + SpriteRequirement(EnemySprite.Catfish).affix().sub_group(2, 0x18), + SpriteRequirement(EnemySprite.CutsceneAgahnim).affix().sub_group(0, 0x55).sub_group(1, 0x3d) + .sub_group(2, 0x42).sub_group(3, 0x43), + SpriteRequirement(EnemySprite.Boulder).affix().sub_group(3, 0x10), + SpriteRequirement(EnemySprite.Gibo).sub_group(2, 0x28), + SpriteRequirement(EnemySprite.Thief).no_drop().sub_group(0, [0xe, 0x15]), + SpriteRequirement(EnemySprite.Medusa).affix(), + SpriteRequirement(EnemySprite.FourWayShooter).affix(), + SpriteRequirement(EnemySprite.Pokey).sub_group(2, 0x27), + SpriteRequirement(EnemySprite.BigFairy).affix().sub_group(2, 0x39).sub_group(3, 0x36), + SpriteRequirement(EnemySprite.Tektite).sub_group(3, 0x10), + SpriteRequirement(EnemySprite.Chainchomp).immune().sub_group(2, 0x27), + SpriteRequirement(EnemySprite.TrinexxRockHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f), + 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.Lynel).sub_group(3, 0x14), + SpriteRequirement(EnemySprite.BunnyBeam).affix(), + SpriteRequirement(EnemySprite.FloppingFish).uw_skip().immune(), + SpriteRequirement(EnemySprite.Stal).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), + SpriteRequirement(EnemySprite.Faerie).affix(), + SpriteRequirement(EnemySprite.FakeMasterSword).immune().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.MagicShopAssistant).affix().sub_group(0, 0x4b).sub_group(3, 0x5a), + SpriteRequirement(EnemySprite.SomariaPlatform).affix().sub_group(2, 0x27), + SpriteRequirement(EnemySprite.CastleMantle).affix().sub_group(0, 0x5d), + SpriteRequirement(EnemySprite.MedallionTablet).affix().sub_group(2, 0x12), + + # todo: overlord requirements + SpriteRequirement(2, 7).affix().sub_group(2, 46), + SpriteRequirement(3, 7).affix().sub_group(2, 46), + SpriteRequirement(5, 7).affix().sub_group(0, 31), + SpriteRequirement(6, 7).affix().sub_group(2, [28, 36]), + SpriteRequirement(7, 7).affix(), + SpriteRequirement(8, 7).affix().sub_group(1, 32), + SpriteRequirement(9, 7).affix().sub_group(2, 35), + SpriteRequirement(0xa, 7).affix().sub_group(3, 82), + SpriteRequirement(0xb, 7).affix().sub_group(3, 82), + SpriteRequirement(0x10, 7).affix().sub_group(2, 34), + SpriteRequirement(0x11, 7).affix().sub_group(2, 34), + SpriteRequirement(0x12, 7).affix().sub_group(2, 34), + SpriteRequirement(0x13, 7).affix().sub_group(2, 34), + SpriteRequirement(0x14, 7).affix(), + SpriteRequirement(0x15, 7).affix().sub_group(2, [37, 41]), + SpriteRequirement(0x16, 7).affix().sub_group(1, 32), + SpriteRequirement(0x17, 7).affix().sub_group(0, 31), + SpriteRequirement(0x18, 7).affix().sub_group(0, 31), + SpriteRequirement(0x19, 7).affix(), + SpriteRequirement(0x1a, 7).affix(), + ] + simple = {(r.sprite, r.overlord): r for r in reqs} + shopkeeper = [ + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 75).sub_group(2, 74).sub_group(3, 90) + .allow({0xff, 0x112, 0x11f}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 75).sub_group(1, 77).sub_group(2, 74) + .sub_group(3, 90).allow({0x10f, 0x110, 0x11f}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 79).sub_group(2, 74).sub_group(3, 90) + .allow({0x118}), + 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}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 21).allow({0x11e}), + ] + complex_r = {} + for req in shopkeeper: + for r in req.allowed_rooms: + complex_r[r] = req + simple[(EnemySprite.Shopkeeper, 0)] = complex_r + return simple + + +vanilla_sheets = [ + (0x00, 0x49, 0x00, 0x00), (0x46, 0x49, 0x0C, 0x1D), (0x48, 0x49, 0x13, 0x1D), (0x46, 0x49, 0x13, 0x0E), + (0x48, 0x49, 0x0C, 0x11), (0x48, 0x49, 0x0C, 0x10), (0x4F, 0x49, 0x4A, 0x50), (0x0E, 0x49, 0x4A, 0x11), + (0x46, 0x49, 0x12, 0x00), (0x00, 0x49, 0x00, 0x50), (0x00, 0x49, 0x00, 0x11), (0x48, 0x49, 0x0C, 0x00), + (0x00, 0x00, 0x37, 0x36), (0x48, 0x49, 0x4C, 0x11), (0x5D, 0x2C, 0x0C, 0x44), (0x00, 0x00, 0x4E, 0x00), + + (0x0F, 0x00, 0x12, 0x10), (0x00, 0x00, 0x00, 0x4C), (0x00, 0x0D, 0x17, 0x00), (0x16, 0x0D, 0x17, 0x1B), + (0x16, 0x0D, 0x17, 0x14), (0x15, 0x0D, 0x17, 0x15), (0x16, 0x0D, 0x18, 0x19), (0x16, 0x0D, 0x17, 0x19), + (0x16, 0x0D, 0x00, 0x00), (0x16, 0x0D, 0x18, 0x1B), (0x0F, 0x49, 0x4A, 0x11), (0x4B, 0x2A, 0x5C, 0x15), + (0x16, 0x49, 0x17, 0x1D), (0x00, 0x00, 0x00, 0x15), (0x16, 0x0D, 0x17, 0x10), (0x16, 0x49, 0x12, 0x00), + + (0x16, 0x49, 0x0C, 0x11), (0x00, 0x00, 0x12, 0x10), (0x16, 0x0D, 0x00, 0x11), (0x16, 0x49, 0x0C, 0x00), + (0x16, 0x0D, 0x4C, 0x11), (0x0E, 0x0D, 0x4A, 0x11), (0x16, 0x1A, 0x17, 0x1B), (0x4F, 0x34, 0x4A, 0x50), + (0x35, 0x4D, 0x65, 0x36), (0x4A, 0x34, 0x4E, 0x00), (0x0E, 0x34, 0x4A, 0x11), (0x51, 0x34, 0x5D, 0x59), + (0x4B, 0x49, 0x4C, 0x11), (0x2D, 0x00, 0x00, 0x00), (0x5D, 0x00, 0x12, 0x59), (0x00, 0x00, 0x00, 0x00), + + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + + (0x47, 0x49, 0x2B, 0x2D), (0x46, 0x49, 0x1C, 0x52), (0x00, 0x49, 0x1C, 0x52), (0x5D, 0x49, 0x00, 0x52), + (0x46, 0x49, 0x13, 0x52), (0x4B, 0x4D, 0x4A, 0x5A), (0x47, 0x49, 0x1C, 0x52), (0x4B, 0x4D, 0x39, 0x36), + (0x1F, 0x2C, 0x2E, 0x52), (0x1F, 0x2C, 0x2E, 0x1D), (0x2F, 0x2C, 0x2E, 0x52), (0x2F, 0x2C, 0x2E, 0x31), + (0x1F, 0x1E, 0x30, 0x52), (0x51, 0x49, 0x13, 0x00), (0x4F, 0x49, 0x13, 0x50), (0x4F, 0x4D, 0x4A, 0x50), + + (0x4B, 0x49, 0x4C, 0x2B), (0x1F, 0x20, 0x22, 0x53), (0x55, 0x3D, 0x42, 0x43), (0x1F, 0x1E, 0x23, 0x52), + (0x1F, 0x1E, 0x39, 0x3A), (0x1F, 0x1E, 0x3A, 0x3E), (0x1F, 0x1E, 0x3C, 0x3D), (0x40, 0x1E, 0x27, 0x3F), + (0x55, 0x1A, 0x42, 0x43), (0x1F, 0x1E, 0x2A, 0x52), (0x1F, 0x1E, 0x38, 0x52), (0x1F, 0x20, 0x28, 0x52), + (0x1F, 0x20, 0x26, 0x52), (0x1F, 0x2C, 0x25, 0x52), (0x1F, 0x20, 0x27, 0x52), (0x1F, 0x1E, 0x29, 0x52), + + (0x1F, 0x2C, 0x3B, 0x52), (0x46, 0x49, 0x24, 0x52), (0x21, 0x41, 0x45, 0x33), (0x1F, 0x2C, 0x28, 0x31), + (0x1F, 0x0D, 0x29, 0x52), (0x1F, 0x1E, 0x27, 0x52), (0x1F, 0x20, 0x27, 0x53), (0x48, 0x49, 0x13, 0x52), + (0x0E, 0x1E, 0x4A, 0x50), (0x1F, 0x20, 0x26, 0x53), (0x15, 0x00, 0x00, 0x00), (0x1F, 0x00, 0x2A, 0x52), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x08), (0x5D, 0x49, 0x00, 0x52), (0x55, 0x49, 0x42, 0x43), + (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), + (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50), + (0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x56, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), + (0x61, 0x56, 0x33, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50) +] + +required_boss_sheets = {EnemySprite.ArmosKnight: 9, EnemySprite.Lanmolas: 11, EnemySprite.Moldorm: 12, + EnemySprite.HelmasaurKing: 21, EnemySprite.Arrghus: 20, EnemySprite.Mothula: 26, + EnemySprite.Blind: 32, EnemySprite.Kholdstare: 22, EnemySprite.Vitreous: 22, + EnemySprite.TrinexxRockHead: 23} + + +class SpriteSheet: + def __init__(self, id, default_sub_groups): + self.id = id + self.sub_groups = list(default_sub_groups) + self.locked = [False] * 4 + self.room_set = set() + + def dungeon_id(self): + return self.id + 0x40 + + def valid_sprite(self, requirement): + if requirement.groups and self.id not in requirement.groups: + return False + for idx, sub in enumerate(self.sub_groups): + if requirement.sub_groups[idx] and sub not in requirement.sub_groups[idx]: + return False + return True + + def lock_sprite_in(self, sprite): + for i, options in sprite.sub_groups.items(): + self.locked[i] = len(options) > 0 + + def add_sprite_to_sheet(self, groups, rooms=None): + for idx, g in enumerate(groups): + if g is not None: + self.sub_groups[idx] = g + self.locked[idx] = True + if rooms is not None: + self.room_set.update(rooms) + + def write_to_rom(self, rom, base_address): + rom.write_bytes(base_address + self.id * 4, self.sub_groups) + + def __str__(self): + return f'{self.id} => [{", ".join([str(x) for x in self.sub_groups])}]' + + +# convert to dungeon id +def did(n): + return n + 0x40 + + +def init_sprite_sheets(requirements): + sheets = {id: SpriteSheet(id, def_sheet) for id, def_sheet in enumerate(vanilla_sheets)} + # wait until bosses are randomized to determine which are randomized + for sprite, sheet_num in required_boss_sheets.items(): + sheet = sheets[did(sheet_num)] # convert to dungeon sheet id + boss_sprite = requirements[(sprite, 0)] + sheet.lock_sprite_in(boss_sprite) + return sheets + + +def setup_required_dungeon_groups(sheets): + + sheets[did(1)].add_sprite_to_sheet([70, 73, 28, 82], {0xe4, 0xf0}) # old man + # various npcs + sheets[did(5)].add_sprite_to_sheet([75, 77, 74, 90], {0xf3, 0x109, 0x10e, 0x10f, 0x110, 0x111, 0x11a, 0x11c, 0x122}) + sheets[did(7)].add_sprite_to_sheet([75, 77, 57, 54], {0x8, 0x2c, 0x114, 0x115}) # big fairies + sheets[did(13)].add_sprite_to_sheet([81, None, None, None], {0x55, 0x102, 0x104}) # uncle, sick kid + sheets[did(14)].add_sprite_to_sheet([71, 73, 76, 80], {0x12, 0x105, 0x10a}) # wisemen + sheets[did(15)].add_sprite_to_sheet([79, 77, 74, 80], {0xf4, 0xf5, 0x101, 0x103, 0x106, 0x118, 0x119}) # more npcs + sheets[did(18)].add_sprite_to_sheet([85, 61, 66, 67], {0x20, 0x30}) # aga alter, aga1 + sheets[did(24)].add_sprite_to_sheet([85, 26, 66, 67], {0xd}) # aga2 + sheets[did(34)].add_sprite_to_sheet([33, 65, 69, 51], {0}) # ganon + sheets[did(40)].add_sprite_to_sheet([14, None, 74, 80], {0x124, 0x125, 0x126}) # fairy + rupee npcs + sheets[did(9)].add_sprite_to_sheet([None, None, None, 29], {0xe3}) # magic bat + sheets[did(28)].add_sprite_to_sheet([None, None, 38, 82], {0xe, 0x7e, 0x8e, 0x9e}) # freezors + sheets[did(3)].add_sprite_to_sheet([93, None, None, None], {0x51}) # mantle + sheets[did(42)].add_sprite_to_sheet([21, None, None, None], {0x11e}) # hype cave + sheets[did(10)].add_sprite_to_sheet([47, None, 46, None], {0x5c, 0x75, 0xb9, 0xd9}) # cannonballs + sheets[did(37)].add_sprite_to_sheet([31, None, 39, 82], {0x24, 0xb4, 0xb5, 0xc6, 0xc7, 0xd6}) # somaria platforms + # not sure 31 is needed above + + free_sheet_reqs = [ + ([75, None, None, None], [0xff, 0x112, 0x11f]), # shopkeepers + ([None, 77, None, 21], [0x121]), # smithy + ([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 + ([31, None, None, None], [0x7f]), # force baris + ([None, None, 35, None], [0x39, 0x49, 0x56, 0x57, 0x68, 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]), + # 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]), + # 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]), + + # 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]), # collasping 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... + ] + + # 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) + + +# RandomizeRooms(optionFlags); + # roomCollection.LoadRooms() + # roomCollection.RandomizeRoomSpriteGroups(spriteGroupCollection, optionFlags); + # more stuff +uw_sub_group_choices = { + 0: [22, 31, 47, 14], # 70, 72 for guards + 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] +} + + +def randomize_underworld_sprite_sheets(sheets): + setup_required_dungeon_groups(sheets) + + for num in range(65, 124): # sheets 0x41 to 0x7B inclusive + sheet = sheets[num] + if not sheet.locked[1] and num in [65, 66, 67, 68]: + sheet.locked[1] = True + sheet.sub_groups[1] = random.choice([13, 73]) + for idx in range(0, 4): + if not sheet.locked[idx]: + sheet.sub_groups[idx] = random.choice(uw_sub_group_choices[idx]) + # lock the group? + + + + + + + + + + + + + + + diff --git a/source/enemizer/__init__.py b/source/enemizer/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/enemizer/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 040497a2..82581a76 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -2,6 +2,7 @@ import RaceRandom as random import logging from collections import defaultdict +from source.dungeon.EnemyList import enemy_stats from source.item.District import resolve_districts from BaseClasses import PotItem, PotFlags from DoorShuffle import validate_vanilla_reservation @@ -421,19 +422,20 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion return locations -def filter_pot_locations(locations, world): + + +def filter_special_locations(locations, world, vanilla_matcher): if world.algorithm == 'district': config = world.item_pool_config restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name not in restricted or l.player not in restricted[l.name]] return filtered if len(filtered) > 0 else locations if world.algorithm == 'vanilla_fill': - filtered = [l for l in locations if l.pot and l.pot.item in [PotItem.Chicken, PotItem.BigMagic]] + filtered = [l for l in locations if vanilla_matcher(l)] return filtered if len(filtered) > 0 else locations return locations - vanilla_mapping = { 'Green Pendant': ['Eastern Palace - Prize'], 'Red Pendant': ['Desert Palace - Prize', 'Tower of Hera - Prize'], diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py new file mode 100644 index 00000000..b05b319c --- /dev/null +++ b/source/rom/DataTables.py @@ -0,0 +1,45 @@ +from Utils import snes_to_pc + +from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites +from source.dungeon.RoomHeader import init_room_headers +from source.dungeon.RoomList import Room0127 +from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements + + +class DataTables: + def __init__(self): + self.room_headers = None + self.room_list = None # todo: for boss rando + self.sprite_sheets = None + self.uw_enemy_table = None + self.ow_enemy_tables = None # todo : data migration + self.pot_secret_table = None # todo : migrate storage + + # associated data + self.sprite_requirements = None + + def write_to_rom(self, rom): + for header in self.room_headers.values(): + header.write_to_rom(rom, snes_to_pc(0x30DA00)) # new header table, bank30, tables.asm + # room list + for sheet in self.sprite_sheets.values(): + sheet.write_to_rom(snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3 + 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) + + +def init_data_tables(world, player): + data_tables = DataTables() + data_tables.room_headers = init_room_headers() + data_tables.room_list = {} + # if world.pottery[player] not in ['none']: + # data_tables.room_list[0x0127] = Room0127 + data_tables.sprite_requirements = init_sprite_requirements() + data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements) + init_vanilla_sprites() + uw_table = data_tables.uw_enemy_table = EnemyTable() + for room, sprite_list in vanilla_sprites.items(): + for sprite in sprite_list: + uw_table.room_map[room].append(sprite.copy()) + return data_tables diff --git a/source/rom/__init__.py b/source/rom/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/rom/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package From 3c0f6ca0e6d04acc0903607657696b2e29e505de Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 27 Sep 2022 14:40:43 -0600 Subject: [PATCH 005/158] UW Enemizer work Refinements for data table support --- BaseClasses.py | 8 +- Fill.py | 2 +- Main.py | 3 + PotShuffle.py | 4 +- Regions.py | 11 ++- Rom.py | 15 +--- Utils.py | 5 ++ data/base2current.bps | Bin 93438 -> 98731 bytes source/dungeon/EnemyList.py | 111 ++++++++++++------------- source/dungeon/RoomHeader.py | 3 +- source/dungeon/RoomList.py | 28 ++++--- source/enemizer/EnemizerTestHarness.py | 5 +- source/rom/DataTables.py | 38 ++++++--- 13 files changed, 128 insertions(+), 105 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 60d04d82..c361ef82 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -87,7 +87,7 @@ class World(object): self._portal_cache = {} self.sanc_portal = {} self.fish = BabelFish() - self.pot_contents = {} + self.data_tables = {} for player in range(1, players + 1): def set_player_attr(attr, val): @@ -151,6 +151,7 @@ class World(object): set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('enabled_entrances', {}) + set_player_attr('data_tables', None) def finish_init(self): for player in range(1, self.players + 1): @@ -393,7 +394,7 @@ class World(object): item.location = location item.world = self if location.player != item.player and location.type == LocationType.Pot: - self.pot_contents[location.player].multiworld_count += 1 + self.data_tables[location.player].pot_secret_table.multiworld_count += 1 if collect: self.state.collect(item, location.event, location) @@ -2136,6 +2137,7 @@ class Location(object): self.skip = False self.type = LocationType.Normal if not crystal else LocationType.Prize self.pot = None + self.drop = None def can_fill(self, state, item, check_access=True): if not self.valid_multiworld(state, item): @@ -2144,7 +2146,7 @@ class Location(object): def valid_multiworld(self, state, item): if self.type == LocationType.Pot and self.player != item.player: - return state.world.pot_contents[self.player].multiworld_count < 256 + return state.world.data_tables[self.player].pot_secret_table.multiworld_count < 256 return True def can_reach(self, state): diff --git a/Fill.py b/Fill.py index 4ebd4c04..6ae73dcf 100644 --- a/Fill.py +++ b/Fill.py @@ -581,7 +581,7 @@ def fast_fill_pot_for_multiworld(world, item_pool, fill_locations): if loc.type == LocationType.Pot: pot_fill_locations[loc.player].append(loc) for player in range(1, world.players+1): - flex = 256 - world.pot_contents[player].multiworld_count + flex = 256 - world.data_tables[player].pot_secret_table.multiworld_count fill_count = len(pot_fill_locations[player]) - flex if fill_count > 0: fill_spots = random.sample(pot_fill_locations[player], fill_count) diff --git a/Main.py b/Main.py index 2db44f17..7ed757cc 100644 --- a/Main.py +++ b/Main.py @@ -32,6 +32,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.rom.DataTables import init_data_tables + __version__ = '1.0.1.3-x' @@ -186,6 +188,7 @@ def main(args, seed=None, fish=None): create_doors(world, player) create_rooms(world, player) create_dungeons(world, player) + world.data_tables[player] = init_data_tables(world, player) adjust_locations(world, player) place_bosses(world, player) diff --git a/PotShuffle.py b/PotShuffle.py index ec6536ef..d2d2547a 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -919,14 +919,14 @@ def shuffle_pots(world, player): new_pot_contents.room_map[super_tile] = new_pots - world.pot_contents[player] = new_pot_contents + world.data_tables[player].pot_secret_table = new_pot_contents def shuffle_pot_switches(world, player): import RaceRandom as random for super_tile in vanilla_pots: - new_pots = world.pot_contents[player].room_map[super_tile] + new_pots = world.data_tables[player].pot_secret_table.room_map[super_tile] # sort in the order Hole, Switch, Key, Other, Nothing sort_order = {PotItem.Hole: 4, PotItem.Switch: 3, PotItem.Key: 2, PotItem.Nothing: 0} old_pots = sorted(new_pots, key=lambda pot: sort_order.get(pot.item, 1), reverse=True) diff --git a/Regions.py b/Regions.py index 203a564a..93a88093 100644 --- a/Regions.py +++ b/Regions.py @@ -3,6 +3,8 @@ from Items import ItemFactory from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType, LocationType, PotItem, PotFlags from PotShuffle import key_drop_data, vanilla_pots, choose_pots, PotSecretTable +from source.dungeon.EnemyList import setup_enemy_locations + def create_regions(world, player): world.regions += [ @@ -1005,7 +1007,7 @@ def create_shops(world, player): def adjust_locations(world, player): # handle pots - world.pot_contents[player] = PotSecretTable() + world.data_tables[player].pot_secret_table = PotSecretTable() for location, datum in key_drop_data.items(): loc = world.get_location(location, player) drop_location = 'Drop' == datum[0] @@ -1013,6 +1015,7 @@ def adjust_locations(world, player): loc.type = LocationType.Drop snes_address, room, sprite_idx = datum[1] loc.address = snes_address + world.data_tables[player].uw_enemy_table.room_map[room][sprite_idx].location = loc else: loc.type = LocationType.Pot pot, pot_index = next((p, i) for i, p in enumerate(vanilla_pots[datum[1]]) if p.item == PotItem.Key) @@ -1044,7 +1047,7 @@ def adjust_locations(world, player): pot = world.get_location(loc, player).pot else: pot = pot_orig.copy() - world.pot_contents[player].room_map[super_tile].append(pot) + world.data_tables[player].pot_secret_table.room_map[super_tile].append(pot) if valid_pot_location(pot, world.pot_pool[player], world, player): create_pot_location(pot, pot_index, super_tile, world, player) @@ -1057,6 +1060,7 @@ def adjust_locations(world, player): loc.type = LocationType.Shop # player address? it is in the shop table index += 1 + setup_enemy_locations(world, player) # unreal events: for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Dark Blacksmith Ruins', 'Frog', 'Missing Smith', 'Floodgate', 'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Attic Cracked Floor', 'Suspicious Maiden', @@ -1180,7 +1184,7 @@ flooded_keys_reverse = { 'Swamp Palace - Trench 2 Pot Key': 'Trench 2 Switch' } -lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} + location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'), 'Bottle Merchant': (0x2eb18, 0x186df9, False, 'with a merchant'), 'Flute Spot': (0x18014a, 0x186dfd, False, 'underground'), @@ -1457,6 +1461,7 @@ location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'), 'Potion Shop - Middle': (None, None, False, 'for sale near potions'), 'Potion Shop - Right': (None, None, False, 'for sale near potions'), } +lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name.update(shop_table_by_location_id) lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int} lookup_name_to_id.update(shop_table_by_location) diff --git a/Rom.py b/Rom.py index 26b54dbf..22b534bd 100644 --- a/Rom.py +++ b/Rom.py @@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127 JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '61c296effe6180274721d570d2471e1c' +RANDOMIZERBASEHASH = '0587709ac8c5f2abf95b14d1e1264945' class JsonRom(object): @@ -1539,20 +1539,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if room.player == player and room.modified: rom.write_bytes(room.address(), room.rom_data()) - if world.pottery[player] not in ['none']: - rom.write_bytes(snes_to_pc(0x1F8375), int32_as_bytes(0x2B8000)) - # make hammer pegs use different tiles - Room0127.write_to_rom(snes_to_pc(0x2B8000), rom) - - if world.pot_contents[player]: + if world.data_tables[player]: colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery'] and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) - if world.pot_contents[player].size() > 0x11c0: - raise Exception('Pot table is too big for current area') - world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots) - - # todo: write sprites + world.data_tables[player].write_to_rom(rom) write_strings(rom, world, player, team) diff --git a/Utils.py b/Utils.py index a4e3cee2..74e79d61 100644 --- a/Utils.py +++ b/Utils.py @@ -14,6 +14,11 @@ def int16_as_bytes(value): return [value & 0xFF, (value >> 8) & 0xFF] +def int24_as_bytes(value): + value = value & 0xFFFFFF + return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF] + + def int32_as_bytes(value): value = value & 0xFFFFFFFF return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF] diff --git a/data/base2current.bps b/data/base2current.bps index 03d6471ed59fbc3a0aa2fcb13383c31d53c06f20..52a73adadce6333bac1f62eeae77f1fc00815ef2 100644 GIT binary patch delta 7003 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 delta 1665 zcmWm8eNYo;9>DSEw`;QDJy@!t1PDu7i09R`lmmfEKtQUc(uP{A0r3S)iPKifL0iYT z8;LdT#l+hQi-eHWqAbUqOI&T*P6tnE3tQ?;PTRTajb1UWqq%9tN@or`eR=rfncwI6 zKF>V!j2%?|dPU(iD&3a#%)9+Z%bFnfsOR#M!_7U%xppX4Tnh`Yd%0nV5&E||53yFG z@Ac7NO%KQ8nuEtu{!(xvJg4mID!tRh{Y0!Lx&qvvNCRPIrQPHLp>PrY&^J!FGGzf%u)L4DIvac#!cV7 z8rs8hcT|t&2XN_PT;%5=3*%Z97Oc_`%5k{&k7@<$u>6#ub-3wmr-YSboHZp|^Li()+oQvlcigmjYFO=Xs#N}UOS}@5dJ_yo z?SCGc)iB9o)|}m-ijo=zO7FLd6^zlq%stL&7?){oC3h%+;WE{F^1<@AIg`4GTv6?m zy4By3$%S|RTADqzMDSDOfb?oc#|kB4B3dQG-CWZ^G@jvVDs^P1_D8P0a)NAJ&Tx;J zHfB`Jx-F|O=2%>A3;%Vf<{($O<4Z1J8YDB6horSt9Y9touSw(8W`fuglN7aL@_|5E z%;<1|JneA>)?n@jxd^iob1%pgrU|nR;)vpJ+9I9nBOBg3!=;XRgMoSaRUxl zkoYtnmyl;Y6QINf?9Y(rW_YFe4CY5@6d}hMPTR+&``8D0$9YQJi2EQC%kl6bS>gFK z(1__ma-qj{@mb9MNM3=W2PpALyxEInBd@*~^i{6Y);=giRoq;!PV*0$@ zz!jNy6T4#EpgG&Ld-5#1Bsj=+OgdOq@GRRl`2njI6%4Bj{=iyrWP*d90+v;K93ED{}o)IU_u8a@60v=^Rx`8L?< zRX23vYW|g{ylf#~+Q@Jw`w{lVDFm}b-aKiqxvv4apZRnX3&Mp7t9v1I39Un;I zBUe*kU6Q)5qT>up_%|&-!=k_KedToIu}t_>K_NzPqEUhWB?t8IJx}Mt6;&R4!+5`S z7r}!L^7OXp=`fP-d5h~mBTf?XLNx7g7zJA?`zR4KQg$X~lz*A8(ZLRQgCEgBbLt+` zX*P4r;TCDi?r-Cl=D{{F@|}5LfZP0F9^|U)#^*B_OW7r*jqW@xOx5x;c~Atu@xSFk zG2G>gX(+?vRT`|y#_(9Hu7kf$!%496&U~m&%9fa=Qg8TzZuBC5DIZEe!zb#Y95zJi z^l)xzdf!qs;6ex{_BVx-m}>RW$g&dH1k09}FsV;OkCczNK}jl3ZqNFM*ww&9UMq(d zg(i@ik4Q2tH}`#SpfMjMn&$rhHQz>m8@X+QuT~Z8+c{9Vx3BMkgvs+Fdp%)JC4O;P>^u@yOvB|q2 diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 37e5e627..732990cd 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -542,21 +542,21 @@ def init_vanilla_sprites(): create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x1a, 0x1a, 'TR Tongue Pull') create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x15, 0x1b, 'TR Tongue Pull') create_sprite(0x0004, EnemySprite.Pokey, 0x00, 0, 0x07, 0x18, 'TR Dash Room') - create_sprite(0x0006, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x17) - create_sprite(0x0007, EnemySprite.Moldorm, 0x00, 0, 0x12, 0x0e) + create_sprite(0x0006, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0007, EnemySprite.Moldorm, 0x00, 0, 0x9, 0x09) create_sprite(0x0008, EnemySprite.BigFairy, 0x00, 0, 0x07, 0x16) create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08) create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x08, 0x08) @@ -660,13 +660,13 @@ def init_vanilla_sprites(): create_sprite(0x001b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x14, 'PoD Mimics 2') create_sprite(0x001b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x03, 0x1c, 'PoD Mimics 2') create_sprite(0x001b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1c, 'PoD Mimics 2') - create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x15) - create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x15) - create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x15) - create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x18) - create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x18) - create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x18) - create_sprite(0x001c, 0x19, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08) + create_sprite(0x001c, 0x19, SpriteType.Overlord, 0, 0x07, 0x08) create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07) create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08) @@ -741,7 +741,7 @@ def init_vanilla_sprites(): 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.SpikeBlock, 0x00, 0, 0x08, 0x10, 'Swamp Entrance') - create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x18, 0x16) + create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x08, 0x06) create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17, 'PoD Arena Main') create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f, 'PoD Arena Main') @@ -787,9 +787,9 @@ def init_vanilla_sprites(): create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x13, 0x0d, 'Sewers Dark Cross') create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x10, 0x0e, 'Sewers Dark Cross') create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x12, 0x0f, 'Sewers Dark Cross') - create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17) - create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17) - create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19) + 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') @@ -965,7 +965,7 @@ def init_vanilla_sprites(): create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x0a, 'GT Frozen Over') create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x15, 'GT Frozen Over') create_sprite(0x004c, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x18, 'GT Frozen Over') - create_sprite(0x004d, EnemySprite.Moldorm, 0x00, 0, 0x0e, 0x0e) + create_sprite(0x004d, EnemySprite.Moldorm, 0x00, 0, 0x09, 0x09) create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x14, 0x08, 'Ice Narrow Corridor') create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x16, 0x08, 'Ice Narrow Corridor') create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x18, 0x08, 'Ice Narrow Corridor') @@ -1056,7 +1056,7 @@ def init_vanilla_sprites(): create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14, 'Skull East Bridge') create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x15, 0x15, 'Skull East Bridge') create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x1a, 0x15, 'Skull East Bridge') - create_sprite(0x005a, EnemySprite.HelmasaurKing, 0x00, 0, 0x17, 0x16) + create_sprite(0x005a, EnemySprite.HelmasaurKing, 0x00, 0, 0x07, 0x06) create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x17, 0x0c) create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x18, 0x13) create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x15, 'GT Hidden Spikes') @@ -1169,9 +1169,9 @@ def init_vanilla_sprites(): create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x15, 'GT Mimics 2') create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x14, 0x1b, 'GT Mimics 2') create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x1b, 'GT Mimics 2') - create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x17, 'GT Lanmolas 2') - create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x17, 'GT Lanmolas 2') - create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x19, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09, 'GT Lanmolas 2') create_sprite(0x006c, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x18, 'GT Beam Dash') create_sprite(0x006c, EnemySprite.Medusa, 0x00, 0, 0x03, 0x1c, 'GT Lanmolas 2') create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x05, 0x06, 'GT Gauntlet 4') @@ -1369,7 +1369,7 @@ def init_vanilla_sprites(): create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x16, 0x0a, 'Ice Lonely Freezor') create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x0b, 'Ice Lonely Freezor') create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x0b, 'Ice Lonely Freezor') - create_sprite(0x0090, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x15) + create_sprite(0x0090, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05) create_sprite(0x0091, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x04, 'Mire Falling Foes') create_sprite(0x0091, EnemySprite.SpikeBlock, 0x00, 0, 0x1b, 0x0e, 'Mire Falling Foes') create_sprite(0x0091, 0x08, SpriteType.Overlord, 0, 0x17, 0x0f) @@ -1475,9 +1475,9 @@ def init_vanilla_sprites(): 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.Stalfos, 0x00, 0, 0x1b, 0x19, 'Mire South Fish') - create_sprite(0x00a4, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x15) - create_sprite(0x00a4, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x15) - create_sprite(0x00a4, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x15) + create_sprite(0x00a4, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x05) + create_sprite(0x00a4, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x05) + create_sprite(0x00a4, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x05) create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x16, 0x05, 'GT Wizzrobes 2') create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x05, 'GT Wizzrobes 2') create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07, 'GT Wizzrobes 1') @@ -1521,7 +1521,7 @@ def init_vanilla_sprites(): create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x19, 'Thieves Spike Switch') create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a, 'Thieves Spike Switch') create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x1b, 'Thieves Spike Switch') - create_sprite(0x00ac, EnemySprite.Blind, 0x00, 0, 0x19, 0x15) + create_sprite(0x00ac, EnemySprite.Blind, 0x00, 0, 0x09, 0x05) create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x07, 'Iced T') create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x07, 'Iced T') create_sprite(0x00af, EnemySprite.FirebarCW, 0x00, 0, 0x0a, 0x08, 'Ice Catwalk') @@ -1688,13 +1688,13 @@ def init_vanilla_sprites(): create_sprite(0x00c6, EnemySprite.FloatingSkull, 0x00, 0, 0x10, 0x0e, 'TR Hub Ledges') create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x14, 'TR Hub Ledges') create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x17, 'TR Hub Ledges') - create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x15) - create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x15) - create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x15) - create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x1a, 0x18) - create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x17, 0x18) - create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x14, 0x18) - create_sprite(0x00c8, 0x19, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08) + create_sprite(0x00c8, 0x19, SpriteType.Overlord, 0, 0x07, 0x08) 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') @@ -1804,9 +1804,9 @@ def init_vanilla_sprites(): create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 1, 0x16, 0x17, 'Thieves Compass Room') create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x1c, 'Thieves Compass Room') create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0f, 0x1c, 'Thieves Compass Room') - create_sprite(0x00de, EnemySprite.KholdstareShell, 0x00, 0, 0x17, 0x05) - create_sprite(0x00de, EnemySprite.FallingIce, 0x00, 0, 0x17, 0x05) - create_sprite(0x00de, EnemySprite.Kholdstare, 0x00, 0, 0x17, 0x05) + create_sprite(0x00de, EnemySprite.KholdstareShell, 0x00, 0, 0x07, 0x05) + create_sprite(0x00de, EnemySprite.FallingIce, 0x00, 0, 0x07, 0x05) + create_sprite(0x00de, EnemySprite.Kholdstare, 0x00, 0, 0x07, 0x05) create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x15, 'Paradox Cave') create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x16, 'Paradox Cave') create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x04, 0x06, 'Tower Gold Knights') @@ -2075,8 +2075,8 @@ class EnemyTable: for sprite in self.room_map[room]: data = sprite.sprite_data() rom.write_bytes(data_pointer + list_offset, data) - list_offset + len(data) - rom.write_byte(data_pointer, 0xff) + list_offset += len(data) + rom.write_byte(data_pointer + list_offset, 0xff) data_pointer += list_offset + 1 else: rom.write_bytes(pointer_address + room * 2, int16_as_bytes(empty_pointer)) @@ -2090,23 +2090,16 @@ class EnemyTable: def setup_enemy_locations(world, player): - world.enemy_list[player] = EnemyTable() - for super_tile, enemy_list in vanilla_sprites.items(): + for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items(): for index, sprite in enumerate(enemy_list): - # if sprite.drops_item and sprite.drop_item_kind == 0xe4: - # # normal key drops - # pass - my_sprite = sprite.copy() - world.enemy_list[player].room_map[super_tile].append() - - if valid_drop_location(my_sprite, world, player): - create_drop_location(my_sprite, index, super_tile, world, player) + if valid_drop_location(sprite, world, player): + create_drop_location(sprite, index, super_tile, world, player) def valid_drop_location(sprite, world, player): if world.dropshuffle[player] == 'underworld': if sprite.drops_item and sprite.drop_item_kind == 0xe4: - # already has a location -- hook it up? + # already has a location return False else: stat = enemy_stats[sprite.kind] diff --git a/source/dungeon/RoomHeader.py b/source/dungeon/RoomHeader.py index 13a4f7b7..37272724 100644 --- a/source/dungeon/RoomHeader.py +++ b/source/dungeon/RoomHeader.py @@ -315,7 +315,8 @@ class RoomHeader: self.sprite_sheet = byte_array[3] def write_to_rom(self, rom, base_address): - rom.write_byte(base_address + self.room_id*14 + 3, self.sprite_sheet) + room_offest = self.room_id*14 + rom.write_byte(base_address + room_offest + 3, self.sprite_sheet) def init_room_headers(): diff --git a/source/dungeon/RoomList.py b/source/dungeon/RoomList.py index ceff9405..11dd8a61 100644 --- a/source/dungeon/RoomList.py +++ b/source/dungeon/RoomList.py @@ -17,23 +17,25 @@ class Room: self.doors = doors def write_to_rom(self, address, rom): + offset = 0 rom.write_bytes(address, self.layout) - address += 2 + offset += 2 for obj in self.layer1: - rom.write_bytes(address, obj.data) - address += 3 - rom.write_bytes(address, [0xFF, 0xFF]) - address += 2 + rom.write_bytes(address + offset, obj.data) + offset += 3 + rom.write_bytes(address + offset, [0xFF, 0xFF]) + offset += 2 for obj in self.layer2: - rom.write_bytes(address, obj.data) - address += 3 - rom.write_bytes(address, [0xFF, 0xFF, 0xF0, 0xFF]) - address += 4 + rom.write_bytes(address + offset, obj.data) + offset += 3 + rom.write_bytes(address + offset, [0xFF, 0xFF, 0xF0, 0xFF]) + offset += 4 + door_start = offset for door in self.doors: - rom.write_bytes(address, door.get_bytes()) - address += 2 - rom.write_bytes(address, [0xFF, 0xFF]) - return address + 2 # where the data ended + rom.write_bytes(address + offset, door.get_bytes()) + offset += 2 + rom.write_bytes(address + offset, [0xFF, 0xFF]) + return door_start, offset + 2 # how many bytes were written Room0127 = Room([0xE1, 0x00], diff --git a/source/enemizer/EnemizerTestHarness.py b/source/enemizer/EnemizerTestHarness.py index 09395da1..a05c0d0c 100644 --- a/source/enemizer/EnemizerTestHarness.py +++ b/source/enemizer/EnemizerTestHarness.py @@ -1,3 +1,5 @@ +from types import SimpleNamespace + 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 @@ -6,7 +8,8 @@ import RaceRandom as random if __name__ == '__main__': random.seed(42) - data_tables = init_data_tables(None, None) + 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) diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index b05b319c..ab946c00 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -1,4 +1,4 @@ -from Utils import snes_to_pc +from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites from source.dungeon.RoomHeader import init_room_headers @@ -9,32 +9,50 @@ from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_require class DataTables: def __init__(self): self.room_headers = None - self.room_list = None # todo: for boss rando + self.room_list = None self.sprite_sheets = None self.uw_enemy_table = None - self.ow_enemy_tables = None # todo : data migration - self.pot_secret_table = None # todo : migrate storage + self.ow_enemy_table = None # todo : data migration + self.pot_secret_table = None # associated data self.sprite_requirements = None - def write_to_rom(self, rom): - for header in self.room_headers.values(): + def write_to_rom(self, rom, colorize_pots=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) + 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)) header.write_to_rom(rom, snes_to_pc(0x30DA00)) # new header table, bank30, tables.asm - # room list + room_start_address = 0x378000 + for room_id, room in self.room_list.items(): + rom.write_bytes(0x1F8000 + room_id * 3, int24_as_bytes(room_start_address)) + door_start, bytes_written = room.write_to_rom(snes_to_pc(room_start_address), rom) + rom.write_bytes(0x1F83C0 + room_id * 3, int24_as_bytes(room_start_address + door_start)) + room_start_address += bytes_written + # todo: room data doors pointers at 1F83C0 + if room_start_address > 0x380000: + raise Exception('Room list exceeded bank size') + # size notes: bank 03 uses 140E bytes + # bank 0A uses 372A bytes + # bank 1F uses 77CE bytes: total is about a bank and a half + # probably should reuse bank 1F if writing all the rooms out for sheet in self.sprite_sheets.values(): - sheet.write_to_rom(snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3 + sheet.write_to_rom(rom, snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3 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) + # todo: write ow enemy table def init_data_tables(world, player): data_tables = DataTables() data_tables.room_headers = init_room_headers() data_tables.room_list = {} - # if world.pottery[player] not in ['none']: - # data_tables.room_list[0x0127] = Room0127 + if world.pottery[player] not in ['none']: + data_tables.room_list[0x0127] = Room0127 data_tables.sprite_requirements = init_sprite_requirements() data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements) init_vanilla_sprites() From b71c7aa2b43463e48d28bd44680e4b574b28919c Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 30 Sep 2022 15:38:35 -0600 Subject: [PATCH 006/158] Underworld dropshuffle --- BaseClasses.py | 8 +- CLI.py | 4 +- DoorShuffle.py | 10 +- ItemList.py | 2 +- Items.py | 6 +- KeyDoorShuffle.py | 2 +- Main.py | 3 + Regions.py | 4 +- Rom.py | 39 ++--- Rules.py | 14 ++ data/base2current.bps | Bin 98731 -> 98983 bytes docs/SuperTrueIceRodHunt.yaml | 2 +- docs/customizer_example.yaml | 2 +- mystery_example.yml | 5 +- mystery_testsuite.yml | 5 +- resources/app/cli/args.json | 7 +- resources/app/cli/lang/en.json | 6 +- resources/app/gui/lang/en.json | 5 +- .../app/gui/randomize/dungeon/widgets.json | 9 +- source/classes/CustomSettings.py | 3 +- source/dungeon/EnemyList.py | 164 ++++++++++-------- source/gui/bottom.py | 2 +- source/item/FillUtil.py | 5 +- source/logic/Rule.py | 4 + source/rom/DataTables.py | 4 +- source/tools/MysteryUtils.py | 3 +- 26 files changed, 182 insertions(+), 136 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index c361ef82..a296dce2 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2132,6 +2132,7 @@ class Location(object): self.real = not crystal self.always_allow = lambda item, state: False self.access_rule = lambda state: True + self.verbose_rule = None self.item_rule = lambda item: True self.player = player self.skip = False @@ -2601,7 +2602,7 @@ class Spoiler(object): outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n") outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n") outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n") - outfile.write(f"Drop Shuffle: {yn(self.metadata['dropshuffle'][player])}\n") + outfile.write(f"Drop Shuffle: {self.metadata['dropshuffle'][player]}\n") outfile.write(f"Pottery Mode: {self.metadata['pottery'][player]}\n") outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n") outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No')) @@ -2861,6 +2862,7 @@ mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2} # new byte 4: TDDD PPPP (tavern shuffle, drop, pottery) # dropshuffle reserves 2 bits, pottery needs 4) +drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2} pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7, 'clustered': 8, 'nonempty': 9} @@ -2919,7 +2921,7 @@ class Settings(object): | (0x8 if w.standardize_palettes[p] == "original" else 0) | (0 if w.intensity[p] == "random" else w.intensity[p]), - (0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]), + (0x80 if w.shuffletavern[p] else 0) | (drop_shuffle_mode[w.dropshuffle[p]] << 4) | (pottery_mode[w.pottery[p]]), ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3) | (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0), @@ -2974,7 +2976,7 @@ class Settings(object): args.intensity[p] = "random" if intensity == 0 else intensity args.shuffletavern[p] = True if settings[4] & 0x80 else False - args.dropshuffle[p] = True if settings[4] & 0x10 else False + args.dropshuffle[p] = r(drop_shuffle_mode)[settings[4] & 0x70] args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F] args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1] diff --git a/CLI.py b/CLI.py index fc70f8e9..272d0bca 100644 --- a/CLI.py +++ b/CLI.py @@ -107,7 +107,7 @@ def parse_cli(argv, no_defaults=False): ret.keyshuffle = 'wild' if ret.keydropshuffle: - ret.dropshuffle = True + ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery if ret.retro or ret.mode == 'retro': @@ -196,7 +196,7 @@ def parse_settings(): "shopsanity": False, 'keydropshuffle': False, - 'dropshuffle': False, + 'dropshuffle': 'none', 'pottery': 'none', 'colorizepots': False, 'shufflepots': False, diff --git a/DoorShuffle.py b/DoorShuffle.py index 46e6b50b..a025349b 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -841,10 +841,10 @@ def main_dungeon_pool(dungeon_pool, world, player): all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) target_items = 34 if world.keyshuffle[player] == 'universal': - target_items += 1 if world.dropshuffle[player] else 0 # the hc big key + target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key else: target_items += 29 # small keys in chests - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': target_items += 14 # 13 dropped smalls + 1 big if world.pottery[player] not in ['none', 'cave']: target_items += 19 # 19 pot keys @@ -1246,10 +1246,10 @@ def cross_dungeon(world, player): all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) target_items = 34 if world.keyshuffle[player] == 'universal': - target_items += 1 if world.dropshuffle[player] else 0 # the hc big key + target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key else: target_items += 29 # small keys in chests - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': target_items += 14 # 13 dropped smalls + 1 big if world.pottery[player] not in ['none', 'cave']: target_items += 19 # 19 pot keys @@ -1335,7 +1335,7 @@ def assign_cross_keys(dungeon_builders, world, player): start = time.process_time() if world.keyshuffle[player] == 'universal': remaining = 29 - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': remaining += 13 if world.pottery[player] not in ['none', 'cave']: remaining += 19 diff --git a/ItemList.py b/ItemList.py index 67e06644..ebf2c55d 100644 --- a/ItemList.py +++ b/ItemList.py @@ -414,7 +414,7 @@ def generate_itempool(world, player): if world.take_any[player] != 'none': set_up_take_anys(world, player, skip_pool_adjustments) if world.keyshuffle[player] == 'universal': - if world.dropshuffle[player] and not skip_pool_adjustments: + if world.dropshuffle[player] != 'none' and not skip_pool_adjustments: world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13 if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments: world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19 diff --git a/Items.py b/Items.py index 8c27bf88..7121c603 100644 --- a/Items.py +++ b/Items.py @@ -80,8 +80,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), - 'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), - 'Chicken': (False, False, None, 0x5A, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), + 'Big Magic': (False, False, None, 0xB4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), + 'Chicken': (False, False, None, 0xB3, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), @@ -174,7 +174,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), - 'Fairy': (False, False, None, 0x5A, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), + 'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), 'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 6d495aed..7d9aadd2 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -1422,7 +1422,7 @@ def prize_relevance_sig2(start_regions, d_name, dungeon_entrance): def validate_bk_layout(proposal, builder, start_regions, world, player): bk_special = check_bk_special(builder.master_sector.regions, world, player) - if world.bigkeyshuffle[player] and (world.dropshuffle[player] or not bk_special): + if world.bigkeyshuffle[player] and (world.dropshuffle[player] != 'none' or not bk_special): return True flat_proposal = flatten_pair_list(proposal) state = ExplorationState(dungeon=builder.name) diff --git a/Main.py b/Main.py index 7ed757cc..b5466a4f 100644 --- a/Main.py +++ b/Main.py @@ -461,11 +461,13 @@ def copy_world(world): ret.bigkeyshuffle = world.bigkeyshuffle.copy() ret.bombbag = world.bombbag.copy() ret.flute_mode = world.flute_mode.copy() + ret.bow_mode = world.bow_mode.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_gt_orig = world.crystals_gt_orig.copy() ret.open_pyramid = world.open_pyramid.copy() + ret.take_any = world.take_any.copy() ret.boss_shuffle = world.boss_shuffle.copy() ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_health = world.enemy_health.copy() @@ -481,6 +483,7 @@ def copy_world(world): ret.mixed_travel = world.mixed_travel.copy() ret.standardize_palettes = world.standardize_palettes.copy() ret.restrict_boss_items = world.restrict_boss_items.copy() + ret.data_tables = world.data_tables # can be changed... for player in range(1, world.players + 1): if world.mode[player] != 'inverted': diff --git a/Regions.py b/Regions.py index 93a88093..10669bc2 100644 --- a/Regions.py +++ b/Regions.py @@ -1023,8 +1023,8 @@ def adjust_locations(world, player): loc.address = pot_address(pot_index, datum[1]) loc.pot = pot pot.location = loc - if (not world.dropshuffle[player] and drop_location)\ - or (not drop_location and world.pottery[player] in ['none', 'cave']): + if ((world.dropshuffle[player] == 'none' and drop_location) + or (not drop_location and world.pottery[player] in ['none', 'cave'])): loc.skip = True else: key_item = loc.item diff --git a/Rom.py b/Rom.py index 22b534bd..27fc8f24 100644 --- a/Rom.py +++ b/Rom.py @@ -33,11 +33,11 @@ from InitialSram import InitialSram from source.classes.SFX import randomize_sfx from source.item.FillUtil import valid_pot_items -from source.dungeon.RoomList import Room0127 +from source.dungeon.EnemyList import EnemySprite JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '0587709ac8c5f2abf95b14d1e1264945' +RANDOMIZERBASEHASH = '81d7cf07a34d06ec875074296c39cd97' class JsonRom(object): @@ -616,18 +616,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): location.pot.indicator = standing_item_flag location.pot.standing_item_code = code continue - elif location.type == LocationType.Drop: - if location.item.player != player: - code = 0xF9 - else: - code = 0xF8 - sprite_pointer = snes_to_pc(location.address) - rom.write_byte(sprite_pointer, handle_native_dungeon(location, itemid)) - if code == 0xF9: - rom.write_byte(sprite_pointer+1, location.item.player) - else: - rom.write_byte(sprite_pointer+1, 0) - rom.write_byte(sprite_pointer+2, code) + elif location.type == LocationType.Drop: # handled in the sprite table routine continue if location.address is None or (type(location.address) is int and location.address >= 0x400000): continue @@ -764,7 +753,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): valid_loc_by_dungeon = valid_dungeon_locations(valid_locations) # fix hc big key problems (map and compass too) - if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] + if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']): rom.write_byte(0x151f1, 2) rom.write_byte(0x15270, 2) @@ -894,9 +883,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): credits_total = len(valid_locations) - if world.dropshuffle[player] or world.pottery[player] != 'none': + if world.dropshuffle[player] != 'none' or world.pottery[player] != 'none': rom.write_byte(0x142A50, 1) # StandingItemsOn - multiClientFlags = ((0x1 if world.dropshuffle[player] else 0) + multiClientFlags = ((0x1 if world.dropshuffle[player] != 'none' else 0) | (0x2 if world.shopsanity[player] else 0) | (0x4 if world.take_any[player] != 'none' else 0) | (0x8 if world.pottery[player] != 'none' else 0) @@ -904,7 +893,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x142A51, multiClientFlags) # StandingItemCounterMask rom.write_byte(0x142A55, ((0x1 if world.pottery[player] not in ['none', 'cave'] else 0) - | (0x2 if world.dropshuffle[player] else 0))) + | (0x2 if world.dropshuffle[player] != 'none' else 0))) if world.pottery[player] not in ['none', 'keys']: # Cuccos should not prevent kill rooms from opening rom.write_byte(snes_to_pc(0x0DB457), 0x40) @@ -1249,8 +1238,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed gametype = 0x04 # item - if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] - or world.pottery[player] != 'none'): + if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' + or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): gametype |= 0x02 # entrance/door if enemized: gametype |= 0x01 # enemizer @@ -1350,7 +1339,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x18003C, 0x00) elif world.dungeon_counters[player] == 'on': compass_mode = 0x02 # always on - elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] + elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']): compass_mode = 0x01 # show on pickup if world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default': @@ -1518,10 +1507,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if item_dungeon == 'Escape': item_dungeon = 'Hyrule Castle' is_small_key_this_dungeon = hera_basement.parent_region.dungeon.name == item_dungeon + # hera small key is 11th in list, 10th sprite because of overlord if is_small_key_this_dungeon: - rom.write_byte(0x4E3BB, 0xE4) + world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.SmallKey else: - rom.write_byte(0x4E3BB, 0xEB) + world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.HeartPiece # fix trock doors for reverse entrances if world.fix_trock_doors[player]: @@ -1533,7 +1523,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0xFED31, 0x0E) # preopen bombable exit rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit - if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] + # todo: combine this with data_tables for in place edits + if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): for room in world.rooms: if room.player == player and room.modified: diff --git a/Rules.py b/Rules.py index 1355b7e7..83512aaf 100644 --- a/Rules.py +++ b/Rules.py @@ -9,6 +9,8 @@ from Dungeons import dungeon_table from RoomData import DoorKind from OverworldGlitchRules import overworld_glitches_rules +from source.dungeon.EnemyList import kill_rules + def set_rules(world, player): @@ -35,6 +37,7 @@ def set_rules(world, player): bomb_rules(world, player) pot_rules(world, player) + drop_rules(world, player) if world.logic[player] == 'noglitches': no_glitches_rules(world, player) @@ -770,6 +773,17 @@ def pot_rules(world, player): add_rule(l, lambda state: state.can_hit_crystal(player)) +def drop_rules(world, player): + data_tables = world.data_tables[player] + defeat_rules = kill_rules(world, player, data_tables.enemy_stats) + for super_tile, enemy_list in data_tables.uw_enemy_table.room_map.items(): + for enemy in enemy_list: + if enemy.location: + # could handle odd health rules here? assume harder variant for now + verbose_rule = defeat_rules[enemy.kind] + enemy.location.verbose_rule = verbose_rule + add_rule(enemy.location, verbose_rule.rule_lambda) + def default_rules(world, player): # overworld requirements diff --git a/data/base2current.bps b/data/base2current.bps index 52a73adadce6333bac1f62eeae77f1fc00815ef2..ff5041eb85276be001a16d3e49639471465916b9 100644 GIT binary patch delta 14245 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 0x11c0: @@ -56,6 +57,7 @@ def init_data_tables(world, player): data_tables.sprite_requirements = init_sprite_requirements() data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements) init_vanilla_sprites() + data_tables.enemy_stats = init_enemy_stats() uw_table = data_tables.uw_enemy_table = EnemyTable() for room, sprite_list in vanilla_sprites.items(): for sprite in sprite_list: diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 38ee8c1a..5212aa36 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -97,7 +97,8 @@ def roll_settings(weights): ret.pseudoboots = get_choice('pseudoboots') == 'on' ret.shopsanity = get_choice('shopsanity') == 'on' keydropshuffle = get_choice('keydropshuffle') == 'on' - ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle + ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none' + ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none' ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery ret.colorizepots = get_choice('colorizepots') == 'on' From a5fc4dd7a663e9324a2503a92c10eea78b82a6eb Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 3 Oct 2022 16:12:05 -0600 Subject: [PATCH 007/158] Rules changes Implement Big Magic/Chicken/Fairy spawn items --- Fill.py | 68 +----------------------------- Rom.py | 2 +- Rules.py | 5 ++- source/dungeon/EnemyList.py | 80 ++++++++++++++++++++++++++++++++---- source/dungeon/RoomHeader.py | 6 ++- 5 files changed, 82 insertions(+), 79 deletions(-) diff --git a/Fill.py b/Fill.py index 6ae73dcf..4d59a35d 100644 --- a/Fill.py +++ b/Fill.py @@ -361,54 +361,6 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # get items to distribute classify_major_items(world) # handle pot/drop shuffle - chicken_pool = collections.defaultdict(list) - big_magic_pool = collections.defaultdict(list) - fairy_pool = collections.defaultdict(list) - - for item in world.itempool: - # can only fill these in that players world - if item.name == 'Chicken': - chicken_pool[item.player].append(item) - elif item.name == 'Big Magic': - big_magic_pool[item.player].append(item) - elif item.name == 'Fairy': - fairy_pool[item.player].append(item) - - def fast_fill_certain_items(item_pool, location_types, filter_locations, matcher): - flag = False - for player, pool in item_pool.items(): - if pool: - for certain_item in pool: - world.itempool.remove(certain_item) - cand_locations = [location for location in fill_locations - if location.type in location_types and location.player == player] - locations = filter_locations(cand_locations, world, matcher) - fast_fill_helper(world, pool, locations) - flag = True - return flag - - def chicken_matcher(l): - return l.pot and l.pot.item == PotItem.Chicken - - def magic_matcher(l): - if l.drop: - pack = enemy_stats[l.drop.kind].prize_pack - return pack in {3,7} if isinstance(pack, int) else (3 in pack or 7 in pack) - return l.pot and l.pot.item == PotItem.BigMagic - - def fairy_matcher(l): - if l.drop: - pack = enemy_stats[l.drop.kind].prize_pack - return pack == 7 if isinstance(pack, int) else 7 in pack - return False - - reshuffle = fast_fill_certain_items(chicken_pool, [LocationType.Pot], filter_special_locations, chicken_matcher) - reshuffle |= fast_fill_certain_items(fairy_pool, [LocationType.Drop], filter_special_locations, fairy_matcher) - reshuffle |= fast_fill_certain_items(big_magic_pool, [LocationType.Pot, LocationType.Drop], - filter_special_locations, magic_matcher) - if reshuffle: - fill_locations = world.get_unfilled_locations() - random.shuffle(fill_locations) random.shuffle(world.itempool) progitempool = [item for item in world.itempool if item.advancement] @@ -524,29 +476,11 @@ def ensure_good_items(world, write_skips=False): if (loc.item.name in {'Arrows (5)', 'Nothing'} and (loc.type != LocationType.Pot or loc.item.player != loc.player)): loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player) - # can be placed here by multiworld balancing or shop balancing - # change it to something normal for the player it got swapped to - elif loc.item.name == 'Chicken' and (loc.type != LocationType.Pot or loc.item.player != loc.player): - if loc.type == LocationType.Pot: - loc.item.player = loc.player - else: - loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) - elif (loc.item.name == 'Big Magic' - and (loc.type not in [LocationType.Pot, LocationType.Drop] or loc.item.player != loc.player)): - if loc.type in [LocationType.Pot, LocationType.Drop]: - loc.item.player = loc.player - else: - loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) - elif (loc.item.name == 'Fairy' - and (loc.type != LocationType.Drop or loc.item.player != loc.player)): - if loc.type == LocationType.Drop: - loc.item.player = loc.player - else: - loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) # do the arrow retro check if world.bow_mode[loc.item.player].startswith('retro') and loc.item.name in {'Arrows (5)', 'Arrows (10)'}: loc.item = ItemFactory('Rupees (5)', loc.item.player) # don't write out all pots to spoiler + # todo: skip uninteresting enemy drops if write_skips: if loc.type == LocationType.Pot and loc.item.name in valid_pot_items: loc.skip = True diff --git a/Rom.py b/Rom.py index 27fc8f24..dc08f9d3 100644 --- a/Rom.py +++ b/Rom.py @@ -1534,7 +1534,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery'] and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) - world.data_tables[player].write_to_rom(rom) + world.data_tables[player].write_to_rom(rom, colorize_pots) write_strings(rom, world, player, team) diff --git a/Rules.py b/Rules.py index 83512aaf..2dbfd1b0 100644 --- a/Rules.py +++ b/Rules.py @@ -9,7 +9,7 @@ from Dungeons import dungeon_table from RoomData import DoorKind from OverworldGlitchRules import overworld_glitches_rules -from source.dungeon.EnemyList import kill_rules +from source.dungeon.EnemyList import kill_rules, special_rules_check, special_rules_for_region def set_rules(world, player): @@ -781,6 +781,9 @@ def drop_rules(world, player): if enemy.location: # could handle odd health rules here? assume harder variant for now verbose_rule = defeat_rules[enemy.kind] + 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) diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index a0944556..295e6762 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -1945,8 +1945,8 @@ def init_vanilla_sprites(): create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x08, 0x14, 'Mimic Cave') create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x14, 'Mimic Cave') create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1a, 'Mimic Cave') - create_sprite(0x010d, EnemySprite.SparkCW, 0x00, 0, 0x05, 0x16, 'Mimic Cave') - create_sprite(0x010d, EnemySprite.SparkCCW, 0x00, 0, 0x0a, 0x16, 'Mimic Cave') + create_sprite(0x010d, EnemySprite.SparkCW, 0x00, 0, 0x05, 0x16, 'Mire Shed') + create_sprite(0x010d, EnemySprite.SparkCCW, 0x00, 0, 0x0a, 0x16, 'Mire Shed') create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x06, 0x06) create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x18, 0x06) create_sprite(0x010f, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x15) @@ -2005,8 +2005,7 @@ def kill_rules(world, player, stats): 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), - # too hard to hit red biris with arrows - EnemySprite.RedBari: defeat_rule(world, player, h(EnemySprite.RedBari) * 3, arrow=None, ice=2, hook=True), + 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), @@ -2101,18 +2100,22 @@ class EnemyTable: def setup_enemy_locations(world, player): for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items(): for index, sprite in enumerate(enemy_list): - if valid_drop_location(sprite, world, player): + if valid_drop_location(sprite, index, world, player): create_drop_location(sprite, index, super_tile, world, player) -def valid_drop_location(sprite, world, player): +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: # already has a location return False elif sprite.sub_type != SpriteType.Overlord: stat = world.data_tables[player].enemy_stats[sprite.kind] - return not stat.static and stat.drop_flag + return stat.drop_flag and (sprite.super_tile not in exceptions + or index not in exceptions[sprite.super_tile]) def create_drop_location(sprite, index, super_tile, world, player): @@ -2120,10 +2123,11 @@ def create_drop_location(sprite, index, super_tile, world, player): address = drop_address(index, super_tile) region_name = sprite.region parent = world.get_region(region_name, player) - descriptor = f'Enemy #{index+1}' + enemy_name = enemy_names[sprite.kind] + descriptor = f'{enemy_name} #{index+1}' modifier = parent.hint_text not in {'a storyteller', 'fairies deep in a cave', 'a spiky hint', 'a bounty of five items', 'the sick kid', 'Sahasrahla'} - hint_text = f'{"held by an enemy"} {"in" if modifier else "near"} {parent.hint_text}' + hint_text = f'held by a {enemy_name} {"in" if modifier else "near"} {parent.hint_text}' drop_location = Location(player, f'{region_name} {descriptor}', address, hint_text=hint_text, parent=parent) world.dynamic_locations.append(drop_location) drop_location.drop = sprite @@ -2272,6 +2276,21 @@ def can_bow_kill(world, player, damage, silver_damage, health): 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)) + # main enemy types def defeat_rule(world, player, health, class1=1, arrow: typing.Optional[int] = 1, silver=1, @@ -2321,6 +2340,49 @@ def can_shoot_arrows(world, 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, 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', diff --git a/source/dungeon/RoomHeader.py b/source/dungeon/RoomHeader.py index 37272724..5dc0bef7 100644 --- a/source/dungeon/RoomHeader.py +++ b/source/dungeon/RoomHeader.py @@ -312,11 +312,15 @@ class RoomHeader: self.room_id = room_id # todo: the rest of the header - self.sprite_sheet = byte_array[3] + self.byte_0 = byte_array[0] # bg2, collision, lights out + self.sprite_sheet = byte_array[3] # sprite gfx # + self.effect = byte_array[4] def write_to_rom(self, rom, base_address): room_offest = self.room_id*14 + rom.write_byte(base_address + room_offest + 0, self.byte_0) rom.write_byte(base_address + room_offest + 3, self.sprite_sheet) + rom.write_byte(base_address + room_offest + 4, self.effect) def init_room_headers(): From 1529ec9473f18a8b49f214efbb687aed5bba1bee Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 6 Oct 2022 14:17:12 -0600 Subject: [PATCH 008/158] First pass on boss randomization --- BaseClasses.py | 3 + Main.py | 26 +-- Rom.py | 29 +-- data/base2current.bps | Bin 98983 -> 98868 bytes source/dungeon/EnemyList.py | 16 +- source/dungeon/RoomList.py | 393 ++++++++++++++++++++++++++++++++++- source/dungeon/RoomObject.py | 11 + source/enemizer/Bossmizer.py | 211 +++++++++++++++++++ source/rom/DataTables.py | 4 +- 9 files changed, 641 insertions(+), 52 deletions(-) create mode 100644 source/enemizer/Bossmizer.py diff --git a/BaseClasses.py b/BaseClasses.py index a296dce2..6eb27720 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -242,6 +242,9 @@ class World(object): return dungeon raise RuntimeError('No such dungeon %s for player %d' % (dungeonname, player)) + def get_dungeons(self, player): + return [d for d in self.dungeons if d.player == player] + def get_door(self, doorname, player): if isinstance(doorname, Door): return doorname diff --git a/Main.py b/Main.py index b5466a4f..c6e0d618 100644 --- a/Main.py +++ b/Main.py @@ -315,34 +315,13 @@ def main(args, seed=None, fish=None): rom_names = [] jsonout = {} - enemized = False if not args.suppress_rom or args.bps: logger.info(world.fish.translate("cli","cli","patching.rom")) for team in range(world.teams): for player in range(1, world.players + 1): - sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit' - use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none' - or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' - or sprite_random_on_hit) + rom = JsonRom() if args.jsonout else LocalRom(args.rom) - rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom) - - if use_enemizer and (args.enemizercli or not args.jsonout): - local_rom = LocalRom(args.rom) # update base2current.json (side effect) - if args.rom and not(os.path.isfile(args.rom)): - raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom) - if os.path.exists(args.enemizercli): - patch_enemizer(world, player, rom, local_rom, args.enemizercli, sprite_random_on_hit) - enemized = True - if not args.jsonout: - rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) - else: - enemizerMsg = world.fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli + "\n" - enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied") - logging.warning(enemizerMsg) - raise EnemizerError(enemizerMsg) - - patch_rom(world, rom, player, team, enemized, bool(args.mystery)) + patch_rom(world, rom, player, team, bool(args.mystery)) if args.race: patch_race_rom(rom) @@ -417,7 +396,6 @@ def main(args, seed=None, fish=None): logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO)) logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO)) logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and args.create_spoiler) else NO)) - logger.info(world.fish.translate("cli","cli","used.enemizer") % (YES if enemized else NO)) logger.info(world.fish.translate("cli","cli","seed") + ": %s", world.seed) logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) diff --git a/Rom.py b/Rom.py index dc08f9d3..ca9de066 100644 --- a/Rom.py +++ b/Rom.py @@ -34,10 +34,11 @@ from InitialSram import InitialSram from source.classes.SFX import randomize_sfx from source.item.FillUtil import valid_pot_items from source.dungeon.EnemyList import EnemySprite +from source.enemizer.Bossmizer import boss_writes JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '81d7cf07a34d06ec875074296c39cd97' +RANDOMIZERBASEHASH = 'c9bf2a8b285fc8cdb98c30844bc3c821' class JsonRom(object): @@ -344,19 +345,6 @@ def patch_enemizer(world, player, rom, local_rom, enemizercli, random_sprite_on_ for patch in json.load(f): rom.write_bytes(patch["address"], patch["patchData"]) - if world.get_dungeon("Thieves Town", player).boss.enemizer_name == "Blind": - rom.write_byte(0x04DE81, 0x6) # maiden spawn - # restore blind spawn code - necessary because the old enemizer clobbers this stuff - # this line could be commented out if ijwu's enemizer is used exclusively - # if keeping this line, note the jump to the dr_baserom's enemizer section - rom.write_bytes(0xEA081, [0x5c, 0x00, 0x80, 0xb7, 0xc9, 0x6, 0xf0, 0x24, - 0xad, 0x3, 0x4, 0x29, 0x20, 0xf0, 0x1d]) - rom.write_byte(0x200101, 0) # Do not close boss room door on entry. - rom.write_byte(0x1B0101, 0) # Do not close boss room door on entry. (for Ijwu's enemizer) - else: - rom.write_byte(0x04DE83, 0xB3) # maiden is now something else - - if random_sprite_on_hit: _populate_sprite_table() sprites = list(_sprite_table.values()) @@ -585,7 +573,7 @@ def handle_native_dungeon(location, itemid): return itemid -def patch_rom(world, rom, player, team, enemized, is_mystery=False): +def patch_rom(world, rom, player, team, is_mystery=False): random.seed(world.rom_seeds[player]) # progressive bow silver arrow hint hack @@ -1241,8 +1229,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): gametype |= 0x02 # entrance/door - if enemized: - gametype |= 0x01 # enemizer rom.write_byte(0x180211, gametype) # Game type # assorted fixes @@ -1427,9 +1413,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x180175, 0x01 if world.bow_mode[player].startswith('retro') else 0x00) # rupee bow rom.write_byte(0x180176, 0x0A if world.bow_mode[player].startswith('retro') else 0x00) # wood arrow cost rom.write_byte(0x180178, 0x32 if world.bow_mode[player].startswith('retro') else 0x00) # silver arrow cost - rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) # rupees replace arrows under pots - if enemized: - rom.write_byte(0x1B152e, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) + # rupees replace arrows under pots for original and enemizer code + rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) + rom.write_byte(snes_to_pc(0x36837D), 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) rom.write_byte(0x30052, 0xDB if world.bow_mode[player].startswith('retro') else 0xE2) # replace arrows in fish prize from bottle merchant rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows @@ -1523,6 +1509,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0xFED31, 0x0E) # preopen bombable exit rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit + if world.boss_shuffle[player] != 'none': + boss_writes(world, player, rom) + # todo: combine this with data_tables for in place edits if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): diff --git a/data/base2current.bps b/data/base2current.bps index ff5041eb85276be001a16d3e49639471465916b9..21edfa06677e3b054bfd11014bec33e409714e3b 100644 GIT binary patch delta 2367 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 diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 295e6762..f7df3c92 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -405,7 +405,7 @@ def init_enemy_stats(): EnemySprite.GroveOstritch: EnemyStats(EnemySprite.GroveOstritch, True), EnemySprite.GroveRabbit: EnemyStats(EnemySprite.GroveRabbit, True), EnemySprite.GroveBird: EnemyStats(EnemySprite.GroveBird, True), - EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, True, 0, health=16), + 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), @@ -2169,7 +2169,7 @@ def add_drop_contents(world, player): continue elif sprite.sub_type != SpriteType.Overlord: stat = world.data_tables[player].enemy_stats[sprite.kind] - if not stat.static and stat.drop_flag: + if stat.drop_flag: pack = 0 if isinstance(stat.prize_pack, int): pack = stat.prize_pack @@ -2291,6 +2291,15 @@ def can_ether_kill(world, player, damage, health): 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, @@ -2359,7 +2368,8 @@ special_rules_check = { 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, 64, stats.health)) + 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]: diff --git a/source/dungeon/RoomList.py b/source/dungeon/RoomList.py index 11dd8a61..e7636ff1 100644 --- a/source/dungeon/RoomList.py +++ b/source/dungeon/RoomList.py @@ -10,10 +10,11 @@ from source.dungeon.RoomObject import RoomObject, DoorObject class Room: - def __init__(self, layout, layer1, layer2, doors): + def __init__(self, layout, layer1, layer2, doors, layer3=None): self.layout = layout self.layer1 = layer1 self.layer2 = layer2 + self.layer3 = layer3 self.doors = doors def write_to_rom(self, address, rom): @@ -28,8 +29,14 @@ class Room: for obj in self.layer2: rom.write_bytes(address + offset, obj.data) offset += 3 - rom.write_bytes(address + offset, [0xFF, 0xFF, 0xF0, 0xFF]) - offset += 4 + rom.write_bytes(address + offset, [0xFF, 0xFF]) + offset += 2 + if self.layer3: + for obj in self.layer3: + rom.write_bytes(address + offset, obj.data) + offset += 3 + rom.write_bytes(address + offset, [0xF0, 0xFF]) + offset += 2 door_start = offset for door in self.doors: rom.write_bytes(address + offset, door.get_bytes()) @@ -38,6 +45,366 @@ class Room: return door_start, offset + 2 # how many bytes were written +Room0006 = Room([0xE1, 0x00], + [RoomObject(0x1FA15C, [0x1B, 0xA3, 0xC8]), + RoomObject(0x1FA15F, [0x58, 0xA3, 0xC8]), + RoomObject(0x1FA162, [0x1B, 0xD8, 0xC8]), + RoomObject(0x1FA165, [0x58, 0xD8, 0xC8]), + RoomObject(0x1FA168, [0x17, 0x9F, 0x3F]), + RoomObject(0x1FA16B, [0x54, 0x9F, 0x3F]), + RoomObject(0x1FA16E, [0x17, 0xA3, 0x79]), + RoomObject(0x1FA171, [0x14, 0xE1, 0x79]), + RoomObject(0x1FA174, [0x17, 0xEB, 0x40]), + RoomObject(0x1FA177, [0x54, 0xEB, 0x40]), + RoomObject(0x1FA17A, [0x6B, 0xA3, 0x7A]), + RoomObject(0x1FA17D, [0x68, 0xE1, 0x7A]), + RoomObject(0x1FA180, [0x21, 0x90, 0xF8]), + RoomObject(0x1FA183, [0x51, 0x90, 0xF8]), + RoomObject(0x1FA186, [0x0C, 0xA5, 0x7F]), + RoomObject(0x1FA189, [0x6C, 0xA5, 0x80])], + [], [DoorObject(Position.SouthW, DoorKind.Trap)]) + + +Room0007 = Room([0x81, 0x1C], + [RoomObject(0x1FCAF0, [0x0A, 0x4E, 0x0D]), + RoomObject(0x1FCAF3, [0x0A, 0xAA, 0x0E]), + RoomObject(0x1FCAF6, [0x0B, 0x51, 0x61]), + RoomObject(0x1FCAF9, [0xC0, 0x2C, 0xA2]), + RoomObject(0x1FCAFC, [0xB0, 0x20, 0x0F]), + RoomObject(0x1FCAFF, [0xB0, 0x22, 0x62]), + RoomObject(0x1FCB02, [0xFE, 0xC1, 0x02]), + RoomObject(0x1FCB05, [0xC9, 0x38, 0x01]), + RoomObject(0x1FCB08, [0xFF, 0xA3, 0x82]), + RoomObject(0x1FCB0B, [0xBA, 0xE6, 0x10]), + RoomObject(0x1FCB0E, [0xE8, 0xAA, 0x62]), + RoomObject(0x1FCB11, [0xFF, 0x43, 0xB9]), + RoomObject(0x1FCB14, [0x53, 0x53, 0xE0]), + RoomObject(0x1FCB17, [0x91, 0x53, 0xE0]), + RoomObject(0x1FCB1A, [0x53, 0x91, 0xE0]), + RoomObject(0x1FCB1D, [0x91, 0x91, 0xE0]), + RoomObject(0x1FCB20, [0x3C, 0x6B, 0xC2]), + RoomObject(0x1FCB23, [0x3D, 0x9B, 0xC3]), + RoomObject(0x1FCB26, [0x54, 0xA6, 0xC3]), + RoomObject(0x1FCB29, [0x5C, 0xAA, 0xC3]), + RoomObject(0x1FCB2C, [0x68, 0xB1, 0xC3]), + RoomObject(0x1FCB2F, [0x75, 0xB0, 0xC3]), + RoomObject(0x1FCB32, [0x8F, 0xB1, 0xC3]), + RoomObject(0x1FCB35, [0x9B, 0xAA, 0xC3]), + RoomObject(0x1FCB38, [0xA6, 0xA0, 0xC3]), + RoomObject(0x1FCB3B, [0xAD, 0x98, 0xC3]), + RoomObject(0x1FCB3E, [0xB4, 0x6A, 0xC2]), + RoomObject(0x1FCB41, [0x51, 0x3D, 0xC3]), + RoomObject(0x1FCB44, [0x45, 0x49, 0xC3]), + RoomObject(0x1FCB47, [0x3D, 0x51, 0xC3]), + RoomObject(0x1FCB4A, [0x9C, 0x39, 0xC2]), + RoomObject(0x1FCB4D, [0xA1, 0x49, 0xC3]), + RoomObject(0x1FCB50, [0xAD, 0x51, 0xC3]), + RoomObject(0x1FCB53, [0x3A, 0x50, 0x8A]), + RoomObject(0x1FCB56, [0x38, 0x50, 0x22]), + RoomObject(0x1FCB59, [0x44, 0x44, 0x69]), + RoomObject(0x1FCB5C, [0x44, 0x44, 0x22]), + RoomObject(0x1FCB5F, [0x58, 0x13, 0x05]), + RoomObject(0x1FCB62, [0x60, 0x15, 0x55]), + RoomObject(0x1FCB65, [0x78, 0x10, 0x3A]), + RoomObject(0x1FCB68, [0x08, 0x5B, 0x65]), + RoomObject(0x1FCB6B, [0x0C, 0x61, 0x7F]), + RoomObject(0x1FCB6E, [0xC8, 0x39, 0x05]), + RoomObject(0x1FCB71, [0xE8, 0x5B, 0x66]), + RoomObject(0x1FCB74, [0xEC, 0x4A, 0x80]), + RoomObject(0x1FCB77, [0x58, 0xEB, 0x06]), + RoomObject(0x1FCB7A, [0x60, 0xED, 0x56]), + RoomObject(0x1FCB7D, [0x78, 0xEC, 0x3B]), + RoomObject(0x1FCB80, [0x50, 0x38, 0x69]), + RoomObject(0x1FCB83, [0x50, 0x38, 0x5F]), + RoomObject(0x1FCB86, [0xA8, 0x38, 0x69]), + RoomObject(0x1FCB89, [0xA8, 0x44, 0x22]), + RoomObject(0x1FCB8C, [0xB4, 0x44, 0x69]), + RoomObject(0x1FCB8F, [0xB4, 0x51, 0x22]), + RoomObject(0x1FCB92, [0xC6, 0x50, 0x8A]), + RoomObject(0x1FCB95, [0x3B, 0xC8, 0x22]), + RoomObject(0x1FCB98, [0x8B, 0xC8, 0x22]), + RoomObject(0x1FCB9B, [0x74, 0xBC, 0x69]), + RoomObject(0x1FCB9E, [0x88, 0xBC, 0x69]), + RoomObject(0x1FCBA1, [0x63, 0x3C, 0xC2]), + RoomObject(0x1FCBA4, [0x66, 0x4F, 0x29]), + RoomObject(0x1FCBA7, [0x64, 0x50, 0x6B]), + RoomObject(0x1FCBAA, [0x5C, 0x54, 0x2B]), + RoomObject(0x1FCBAD, [0x5C, 0x58, 0x6B]), + RoomObject(0x1FCBB0, [0x54, 0x5C, 0x2B]), + RoomObject(0x1FCBB3, [0x54, 0x60, 0x6B]), + RoomObject(0x1FCBB6, [0x4C, 0x64, 0x2B]), + RoomObject(0x1FCBB9, [0x4E, 0x6B, 0x6B]), + RoomObject(0x1FCBBC, [0x4C, 0x98, 0x2D]), + RoomObject(0x1FCBBF, [0x54, 0x9C, 0x6B]), + RoomObject(0x1FCBC2, [0x54, 0xA0, 0x2D]), + RoomObject(0x1FCBC5, [0x5C, 0xA4, 0x6B]), + RoomObject(0x1FCBC8, [0x5C, 0xA8, 0x2D]), + RoomObject(0x1FCBCB, [0x64, 0xAC, 0x6B]), + RoomObject(0x1FCBCE, [0x66, 0xB3, 0x2A]), + RoomObject(0x1FCBD1, [0x98, 0xAC, 0x6A]), + RoomObject(0x1FCBD4, [0x98, 0xA8, 0x2E]), + RoomObject(0x1FCBD7, [0xA0, 0xA4, 0x6A]), + RoomObject(0x1FCBDA, [0xA0, 0xA0, 0x2E]), + RoomObject(0x1FCBDD, [0xA8, 0x9C, 0x6A]), + RoomObject(0x1FCBE0, [0xA8, 0x98, 0x2E]), + RoomObject(0x1FCBE3, [0xB2, 0x6B, 0x6A]), + RoomObject(0x1FCBE6, [0xA8, 0x64, 0x2C]), + RoomObject(0x1FCBE9, [0xA8, 0x60, 0x6A]), + RoomObject(0x1FCBEC, [0xA0, 0x5C, 0x2C]), + RoomObject(0x1FCBEF, [0xA0, 0x58, 0x6A]), + RoomObject(0x1FCBF2, [0x98, 0x54, 0x2C]), + RoomObject(0x1FCBF5, [0x98, 0x50, 0x6A]), + RoomObject(0x1FCBF8, [0x68, 0x74, 0xC2]), + RoomObject(0x1FCBFB, [0x68, 0x71, 0x27]), + RoomObject(0x1FCBFE, [0x68, 0x77, 0x6A]), + RoomObject(0x1FCC01, [0x74, 0x77, 0x6B]), + RoomObject(0x1FCC04, [0x68, 0x85, 0x28]), + RoomObject(0x1FCC07, [0xFC, 0x31, 0x72]), + RoomObject(0x1FCC0A, [0x74, 0xAE, 0x04]), + RoomObject(0x1FCC0D, [0x71, 0xA0, 0xE0]), + RoomObject(0x1FCC10, [0x0A, 0x13, 0xA0]), + RoomObject(0x1FCC13, [0x0A, 0xBF, 0xA1]), + RoomObject(0x1FCC16, [0xBE, 0xF7, 0xA3]), + RoomObject(0x1FCC19, [0xC3, 0x11, 0xC0]), + RoomObject(0x1FCC1C, [0xD1, 0x31, 0x00])], + [], []) + +Room001C = Room([0xE1, 0x00], + [RoomObject(0x1FF74B, [0x2D, 0x32, 0xA4]), + RoomObject(0x1FF74E, [0xA9, 0x1E, 0xDC]), + RoomObject(0x1FF751, [0xA8, 0x91, 0x3A]), + RoomObject(0x1FF754, [0x88, 0xAD, 0x76]), + RoomObject(0x1FF757, [0xEC, 0xAD, 0x77]), + RoomObject(0x1FF75A, [0xA8, 0x50, 0x3D]), + RoomObject(0x1FF75D, [0xD0, 0x50, 0x3D]), + RoomObject(0x1FF760, [0x30, 0xA9, 0x3D]), + RoomObject(0x1FF763, [0x30, 0xC1, 0x3D]), + RoomObject(0x1FF766, [0xFC, 0x69, 0x38]), + RoomObject(0x1FF769, [0x97, 0x9F, 0xD1]), + RoomObject(0x1FF76C, [0xCD, 0x9F, 0xD1]), + RoomObject(0x1FF76F, [0x97, 0xDC, 0xD1]), + RoomObject(0x1FF772, [0xCD, 0xDC, 0xD1]), + RoomObject(0x1FF775, [0xBD, 0x32, 0xF9]), + RoomObject(0x1FF778, [0xB1, 0x22, 0xF9]), + RoomObject(0x1FF77B, [0xC9, 0x22, 0xF9]),], [], + [DoorObject(Position.InteriorE, DoorKind.TrapTriggerable), + DoorObject(Position.InteriorS, DoorKind.Trap), DoorObject(Position.InteriorW, DoorKind.Dashable)]) + + +Room0029 = Room([0xE5, 0x00], + [RoomObject(0x1FC188, [0x97, 0x9C, 0xDE]), + RoomObject(0x1FC18B, [0xB7, 0x9C, 0xDE]), + RoomObject(0x1FC18E, [0xD6, 0x9C, 0xDE]), + RoomObject(0x1FC191, [0x97, 0xE4, 0xDE]), + RoomObject(0x1FC194, [0xB7, 0xE4, 0xDE]), + RoomObject(0x1FC197, [0xD6, 0xE4, 0xDE]), + RoomObject(0x1FC19A, [0x94, 0xA7, 0xDE]), + RoomObject(0x1FC19D, [0x94, 0xC7, 0xDE]), + RoomObject(0x1FC1A0, [0xE4, 0xA7, 0xDE]), + RoomObject(0x1FC1A3, [0xE4, 0xC7, 0xDE])], + [RoomObject(0x1FC1A8, [0x03, 0x03, 0xCA]), + RoomObject(0x1FC1AB, [0x43, 0x03, 0xCA]), + RoomObject(0x1FC1AE, [0x83, 0x03, 0xCA]), + RoomObject(0x1FC1B1, [0xC3, 0x03, 0xCA]), + RoomObject(0x1FC1B4, [0x03, 0x43, 0xCA]), + RoomObject(0x1FC1B7, [0x43, 0x43, 0xCA]), + RoomObject(0x1FC1BA, [0x83, 0x43, 0xCA]), + RoomObject(0x1FC1BD, [0xC3, 0x43, 0xCA]), + RoomObject(0x1FC1C0, [0x03, 0x83, 0xCA]), + RoomObject(0x1FC1C3, [0x43, 0x83, 0xCA]), + RoomObject(0x1FC1C6, [0x83, 0x83, 0xCA]), + RoomObject(0x1FC1C9, [0xC3, 0x83, 0xCA]), + RoomObject(0x1FC1CC, [0x03, 0xC3, 0xCA]), + RoomObject(0x1FC1CF, [0x43, 0xC3, 0xCA]), + RoomObject(0x1FC1D2, [0x83, 0xC3, 0xCA]), + RoomObject(0x1FC1D5, [0xC3, 0xC3, 0xCA])], + [], layer3=[RoomObject(0x1FC1DA, [0x9F, 0xA7, 0xC6]), + RoomObject(0x1FC1DD, [0xD4, 0xA7, 0xC6]), + RoomObject(0x1FC1E0, [0xFE, 0xF9, 0xF4]), + RoomObject(0x1FC1E3, [0xFF, 0x1E, 0x74]), + RoomObject(0x1FC1E6, [0xFE, 0x5C, 0x74]), + RoomObject(0x1FC1E9, [0xFF, 0x9C, 0x74])]) + + +Room0033 = Room([0xE9, 0x00], [], [], [DoorObject(Position.SouthW, DoorKind.Trap)]) + + +Room004D = Room([0x82, 0x1C], + [RoomObject(0x1FFD43, [0x09, 0x34, 0x0D]), + RoomObject(0x1FFD46, [0x08, 0x3A, 0x61]), + RoomObject(0x1FFD49, [0x09, 0xC0, 0x0E]), + RoomObject(0x1FFD4C, [0x08, 0xC2, 0x61]), + RoomObject(0x1FFD4F, [0xD1, 0x10, 0x0F]), + RoomObject(0x1FFD52, [0xE8, 0x3A, 0x62]), + RoomObject(0x1FFD55, [0x5E, 0x1C, 0x03]), + RoomObject(0x1FFD58, [0x17, 0x49, 0x63]), + RoomObject(0x1FFD5B, [0xDF, 0x4B, 0x64]), + RoomObject(0x1FFD5E, [0xDC, 0xCA, 0x64]), + RoomObject(0x1FFD61, [0xFF, 0x7D, 0xCB]), + RoomObject(0x1FFD64, [0x9D, 0xDF, 0x04]), + RoomObject(0x1FFD67, [0x3B, 0x5B, 0xE0]), + RoomObject(0x1FFD6A, [0x7B, 0x5B, 0xE0]), + RoomObject(0x1FFD6D, [0xB8, 0x5B, 0xE0]), + RoomObject(0x1FFD70, [0x6A, 0xB1, 0xE0]), + RoomObject(0x1FFD73, [0x78, 0x54, 0xC2]), + RoomObject(0x1FFD76, [0x5B, 0x2A, 0xC2]), + RoomObject(0x1FFD79, [0x98, 0x2A, 0xC2]), + RoomObject(0x1FFD7C, [0x21, 0x4B, 0xC3]), + RoomObject(0x1FFD7F, [0x21, 0x7B, 0xC3]), + RoomObject(0x1FFD82, [0x21, 0xA1, 0xC3]), + RoomObject(0x1FFD85, [0x38, 0x7B, 0xC2]), + RoomObject(0x1FFD88, [0x48, 0x8A, 0xC2]), + RoomObject(0x1FFD8B, [0x3A, 0xAA, 0xC2]), + RoomObject(0x1FFD8E, [0x5B, 0x9C, 0xC2]), + RoomObject(0x1FFD91, [0xC9, 0x4B, 0xC3]), + RoomObject(0x1FFD94, [0xC9, 0x7B, 0xC3]), + RoomObject(0x1FFD97, [0xB8, 0x79, 0xC2]), + RoomObject(0x1FFD9A, [0xA8, 0x88, 0xC2]), + RoomObject(0x1FFD9D, [0x9B, 0x9B, 0xC2]), + RoomObject(0x1FFDA0, [0x9B, 0xD0, 0xC2]), + RoomObject(0x1FFDA3, [0xD0, 0xA3, 0xC2]), + RoomObject(0x1FFDA6, [0x78, 0x8C, 0xC2]), + RoomObject(0x1FFDA9, [0x15, 0x45, 0x22]), + RoomObject(0x1FFDAC, [0x59, 0x1F, 0x69]), + RoomObject(0x1FFDAF, [0xA5, 0x1F, 0x69]), + RoomObject(0x1FFDB2, [0xC9, 0x45, 0x22]), + RoomObject(0x1FFDB5, [0x68, 0xE4, 0x5E]), + RoomObject(0x1FFDB8, [0x15, 0xB9, 0x22]), + RoomObject(0x1FFDBB, [0x35, 0xB9, 0x69]), + RoomObject(0x1FFDBE, [0x37, 0xD9, 0x22]), + RoomObject(0x1FFDC1, [0x88, 0xD9, 0x22]), + RoomObject(0x1FFDC4, [0x98, 0xD9, 0x69]), + RoomObject(0x1FFDC7, [0x66, 0xCB, 0x2A]), + RoomObject(0x1FFDCA, [0x69, 0xC9, 0x04]), + RoomObject(0x1FFDCD, [0x79, 0xCB, 0xF9]), + RoomObject(0x1FFDD0, [0x8D, 0xBA, 0xF9]), + RoomObject(0x1FFDD3, [0x37, 0x57, 0x29]), + RoomObject(0x1FFDD6, [0x87, 0x57, 0x29]), + RoomObject(0x1FFDD9, [0x78, 0x5A, 0x6A]), + RoomObject(0x1FFDDC, [0x84, 0x5A, 0x6B]), + RoomObject(0x1FFDDF, [0x78, 0x65, 0x28]), + RoomObject(0x1FFDE2, [0x35, 0x5B, 0x6B]), + RoomObject(0x1FFDE5, [0x34, 0x7A, 0x2D]), + RoomObject(0x1FFDE8, [0x44, 0x7E, 0x6B]), + RoomObject(0x1FFDEB, [0x44, 0x8A, 0x2D]), + RoomObject(0x1FFDEE, [0x54, 0x8E, 0x6B]), + RoomObject(0x1FFDF1, [0x55, 0x9B, 0x2A]), + RoomObject(0x1FFDF4, [0x78, 0x8E, 0x6A]), + RoomObject(0x1FFDF7, [0x78, 0x89, 0x27]), + RoomObject(0x1FFDFA, [0x84, 0x8E, 0x6B]), + RoomObject(0x1FFDFD, [0x85, 0x9B, 0x2A]), + RoomObject(0x1FFE00, [0xA8, 0x8E, 0x6A]), + RoomObject(0x1FFE03, [0xA8, 0x8A, 0x2E]), + RoomObject(0x1FFE06, [0xB8, 0x7E, 0x6A]), + RoomObject(0x1FFE09, [0xB8, 0x7A, 0x2E]), + RoomObject(0x1FFE0C, [0xC9, 0x5B, 0x6A]), + RoomObject(0x1FFE0F, [0x66, 0xAF, 0x29]), + RoomObject(0x1FFE12, [0x65, 0xB1, 0x6B]), + RoomObject(0x1FFE15, [0x99, 0xB1, 0x6A]), + RoomObject(0x1FFE18, [0x38, 0x4B, 0x03]), + RoomObject(0x1FFE1B, [0xA8, 0x4B, 0x03]), + RoomObject(0x1FFE1E, [0x48, 0x13, 0x3A]), + RoomObject(0x1FFE21, [0x0C, 0x4A, 0x7F]), + RoomObject(0x1FFE24, [0xEC, 0x4A, 0x80]), + RoomObject(0x1FFE27, [0xFE, 0xE1, 0x39]), + RoomObject(0x1FFE2A, [0x09, 0x11, 0xA0]), + RoomObject(0x1FFE2D, [0xD5, 0x11, 0xA2]), + RoomObject(0x1FFE30, [0x09, 0xD5, 0xA1])], + [RoomObject(0x1FFE35, [0x5B, 0x19, 0xDB]), + RoomObject(0x1FFE38, [0x98, 0x19, 0xDB]), + RoomObject(0x1FFE3B, [0x11, 0x4B, 0xDB]), + RoomObject(0x1FFE3E, [0x11, 0x8A, 0xDB]), + RoomObject(0x1FFE41, [0x39, 0x48, 0xDB]), + RoomObject(0x1FFE44, [0xA9, 0x48, 0xDB]), + RoomObject(0x1FFE47, [0xD9, 0x4B, 0xDB]), + RoomObject(0x1FFE4A, [0xD9, 0x8B, 0xDB]), + RoomObject(0x1FFE4D, [0x6A, 0xC8, 0xDB]), + RoomObject(0x1FFE50, [0x9B, 0xCA, 0xDB]), + RoomObject(0x1FFE53, [0xD9, 0xCA, 0xDB])], + [DoorObject(Position.NorthW, DoorKind.SmallKey), DoorObject(Position.WestS, DoorKind.Normal)]) + +Room005A = Room([0xE9, 0x00], + [RoomObject(0x1FA7CD, [0xA8, 0xA8, 0xDE]), + RoomObject(0x1FA7D0, [0xB0, 0xA0, 0xDE]), + RoomObject(0x1FA7D3, [0xB8, 0xA8, 0xDE]), + RoomObject(0x1FA7D6, [0xC0, 0xA0, 0xDE]), + RoomObject(0x1FA7D9, [0xC8, 0xA8, 0xDE]), + RoomObject(0x1FA7DC, [0xD0, 0xA0, 0xDE])], + [], [DoorObject(Position.SouthE, DoorKind.Trap)]) + +Room006C = Room([0xE2, 0x00], + [RoomObject(0x1FFA58, [0x17, 0x9F, 0xE8]), + RoomObject(0x1FFA5B, [0x4D, 0x9F, 0xE8]), + RoomObject(0x1FFA5E, [0x17, 0xDC, 0xE8]), + RoomObject(0x1FFA61, [0x4D, 0xDC, 0xE8]), + RoomObject(0x1FFA64, [0x18, 0xE1, 0xFE]), + RoomObject(0x1FFA67, [0x88, 0xAD, 0x76]), + RoomObject(0x1FFA6A, [0x99, 0xBC, 0x33]), + RoomObject(0x1FFA6D, [0x9B, 0xBB, 0x34]), + RoomObject(0x1FFA70, [0x9B, 0xCF, 0x34]), + RoomObject(0x1FFA73, [0xD8, 0xB8, 0x34]), + RoomObject(0x1FFA76, [0xD8, 0xCC, 0x34]), + RoomObject(0x1FFA79, [0xAF, 0xAA, 0xFE]), + RoomObject(0x1FFA7C, [0xC7, 0xAA, 0xFE]), + RoomObject(0x1FFA7F, [0xAF, 0xD2, 0xFE]), + RoomObject(0x1FFA82, [0xC7, 0xD2, 0xFE]), + RoomObject(0x1FFA85, [0x28, 0x11, 0x3A]), + RoomObject(0x1FFA88, [0x28, 0x91, 0x3A]), + RoomObject(0x1FFA8B, [0xFC, 0xE1, 0x38]), + RoomObject(0x1FFA8E, [0x2B, 0x33, 0xFA]), + RoomObject(0x1FFA91, [0x53, 0x33, 0xFA]), + RoomObject(0x1FFA94, [0x2B, 0x53, 0xFA]), + RoomObject(0x1FFA97, [0x53, 0x53, 0xFA])], [], + [DoorObject(Position.InteriorS, DoorKind.Trap2), DoorObject(Position.InteriorW, DoorKind.Trap), + DoorObject(Position.EastS, DoorKind.Normal)]) + +Room0090 = Room([0xE1, 0x00], + [RoomObject(0x1FBAA0, [0x28, 0xEC, 0x56]), + RoomObject(0x1FBAA3, [0x48, 0xEC, 0x56]), + RoomObject(0x1FBAA6, [0x1B, 0xA2, 0xFF])], + [RoomObject(0x1FBAAB, [0x16, 0x9C, 0xFE])], [DoorObject(Position.SouthW, DoorKind.Trap)]) + +Room00A4 = Room([0xE1, 0x00], + [RoomObject(0x1FE702, [0xFC, 0x08, 0x00]), + RoomObject(0x1FE705, [0x13, 0x80, 0x01]), + RoomObject(0x1FE708, [0xFD, 0xC8, 0x02]), + RoomObject(0x1FE70B, [0x02, 0x93, 0x61]), + RoomObject(0x1FE70E, [0xFC, 0x0E, 0x81]), + RoomObject(0x1FE711, [0x13, 0xE8, 0x02]), + RoomObject(0x1FE714, [0xFD, 0xCE, 0x83]), + RoomObject(0x1FE717, [0x72, 0x93, 0x62]), + RoomObject(0x1FE71A, [0x13, 0x93, 0xC4]), + RoomObject(0x1FE71D, [0x51, 0x93, 0xC4]), + RoomObject(0x1FE720, [0x51, 0xC9, 0xC4]), + RoomObject(0x1FE723, [0x10, 0xC9, 0xC4]), + RoomObject(0x1FE726, [0x0E, 0x8D, 0xDE]), + RoomObject(0x1FE729, [0x0D, 0x9C, 0xDE]), + RoomObject(0x1FE72C, [0x0C, 0xA5, 0xDE]), + RoomObject(0x1FE72F, [0x5E, 0x8C, 0xDE]), + RoomObject(0x1FE732, [0x65, 0x94, 0xDE]), + RoomObject(0x1FE735, [0x6C, 0x9C, 0xDE])], + [RoomObject(0x1FE73A, [0x2E, 0x98, 0xFF])], [DoorObject(Position.SouthW, DoorKind.Trap)]) + +Room00AC = Room([0xE9, 0x00], + [RoomObject(0x1FD9B1, [0x88, 0xA4, 0x0D]), + RoomObject(0x1FD9B4, [0x88, 0xD0, 0x0E]), + RoomObject(0x1FD9B7, [0xE0, 0x90, 0x0F]), + RoomObject(0x1FD9BA, [0xE0, 0xE4, 0x10]), + RoomObject(0x1FD9BD, [0x89, 0xAB, 0x61]), + RoomObject(0x1FD9C0, [0xE9, 0xAB, 0x62]), + RoomObject(0x1FD9C3, [0x88, 0x91, 0xA0]), + RoomObject(0x1FD9C6, [0x88, 0xE5, 0xA1]), + RoomObject(0x1FD9C9, [0xE4, 0x91, 0xA2]), + RoomObject(0x1FD9CC, [0xE4, 0xF5, 0xA3])], + [RoomObject(0x1FD9D1, [0xB1, 0xA8, 0xFF])], [DoorObject(Position.SouthE, DoorKind.Trap)]) + +Room00C8 = Room([0xE1, 0x00], + [RoomObject(0x0A9587, [0x98, 0x92, 0x3A]), + RoomObject(0x0A958A, [0x88, 0xAA, 0x65]), + RoomObject(0x0A958D, [0xE8, 0xAA, 0x66])], [], [DoorObject(Position.SouthE, DoorKind.Trap)]) + +Room00DE = Room([0xE4, 0x00], [], [RoomObject(0x1FCAE5, [0xAD, 0x21, 0xF9])], []) + Room0127 = Room([0xE1, 0x00], [RoomObject(0x0AB600, [0xFE, 0x89, 0x00]), RoomObject(0x0AB603, [0xA2, 0xA1, 0x61]), @@ -64,3 +431,23 @@ Room0127 = Room([0xE1, 0x00], RoomObject(0x0AB642, [0xE1, 0xD2, 0xC0])], [], [DoorObject(Position.SouthW, DoorKind.CaveEntrance), DoorObject(Position.SouthE, DoorKind.CaveEntrance)]) + +# boss room id, room data, shell x, shell y, clear layer 2 +boss_rooms = { + 'Eastern Palace': (0xc8, Room00C8, 0x2B, 0x28, False), + 'Desert Palace': (0x33, Room0033, 0x0B, 0x28, False), + 'Tower of Hera': (7, Room0007, 0x18, 0x16, False), + 'Palace of Darkness': (0x5A, Room005A, 0x2B, 0x28, False), + 'Swamp Palace': (6, Room0006, 0x0B, 0x28, False), + 'Skull Woods': (0x29, Room0029, 0x2B, 0x28, False), + 'Thieves Town': (0xac, Room00AC, 0x2B, 0x28, True), + 'Ice Palace': (0xde, Room00DE, 0x2B, 0x08, True), + 'Misery Mire': (0x90, Room0090, 0x0B, 0x28, True), + 'Turtle Rock': (0xa4, Room00A4, 0x0B, 0x28, True), +} + +gt_boss_room = { + 'bottom': (0x1C, Room001C, 0x2B, 0x28, False), + 'middle': (0x6C, Room006C, 0x0B, 0x28, False), + 'top': (0x4D, Room004D, 0x18, 0x16, False), +} diff --git a/source/dungeon/RoomObject.py b/source/dungeon/RoomObject.py index 359231e0..4a890021 100644 --- a/source/dungeon/RoomObject.py +++ b/source/dungeon/RoomObject.py @@ -22,6 +22,17 @@ class RoomObject: def write_to_rom(self, rom): rom.write_bytes(snes_to_pc(self.address), self.data) + # subtype 3 only? + def matches_oid(self, oid): + my_oid = (self.data[2] << 4) | ((self.data[1] & 3) << 2) | (self.data[0] & 3) + return my_oid == oid + + @staticmethod + def subtype3_factory(x, y, type_id): + return RoomObject(None, [((x << 2) & 0xFC) | (type_id & 0x3), + ((y << 2) & 0xFC) | ((type_id >> 2) & 0x3), + 0xF0 | ((type_id >> 4) & 0xF)]) + class DoorObject: diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py new file mode 100644 index 00000000..935b11e3 --- /dev/null +++ b/source/enemizer/Bossmizer.py @@ -0,0 +1,211 @@ +import RaceRandom as random +from Utils import snes_to_pc + +from source.dungeon.EnemyList import EnemySprite, SpriteType, Sprite +from source.dungeon.RoomList import boss_rooms, gt_boss_room, Room0006 +from source.dungeon.RoomObject import RoomObject +from source.enemizer.SpriteSheets import required_boss_sheets + + +def get_dungeon_boss_room(dungeon_name, level): + if level is None: + return boss_rooms[dungeon_name] + return gt_boss_room[level] + + +def get_dungeon_boss_default(dungeon_name, level): + if level is None: + return boss_defaults[dungeon_name] + return gt_boss_defaults[level] + + +def add_shell_to_boss_room(data_tables, dungeon_name, level, shell_id): + room_id, room, shell_x, shell_y, clear_layer_2 = get_dungeon_boss_room(dungeon_name, level) + if room_id in data_tables.room_list: + room = data_tables.room_list[room_id] + else: + data_tables.room_list[room_id] = room + room.layout[0] = 0xF0 + if clear_layer_2: + room.layer2.clear() + y_offset = 0 if shell_id == 0xF95 else -2 + room.layer2.append(RoomObject.subtype3_factory(shell_x, shell_y + y_offset, shell_id)) + + +def remove_shell_from_boss_room(data_tables, dungeon_name, level, shell_id): + room_id, room, shell_x, shell_y, clear_layer_2 = get_dungeon_boss_room(dungeon_name, level) + if room_id in data_tables.room_list: + room = data_tables.room_list[room_id] + else: + data_tables.room_list[room_id] = room + room.layer2[:] = [obj for obj in room.layer2 if not obj.matches_oid(shell_id)] + + +def remove_water_tiles(data_tables): + room = Room0006 + if 0x6 in data_tables.room_list: + room = data_tables.room_list[0x6] + else: + data_tables.room_list[0x6] = room + room.layer1.clear() + + +def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y): + return Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, None, False, None) + + +def add_armos_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05)) + sprite_list.insert(3, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08)) + sprite_list.insert(4, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08)) + sprite_list.insert(5, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08)) + sprite_list.insert(6, create_sprite(room_id, 0x19, SpriteType.Overlord, 0, 0x07, 0x08)) + + +def add_lanmolas_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09)) + + +def add_moldorm_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Moldorm, 0x00, 0, 0x09, 0x09)) + + +def add_helmasaur_king_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.HelmasaurKing, 0x00, 0, 0x07, 0x06)) + + +def add_arrghus_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(3, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(4, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(5, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(6, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(7, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(8, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(9, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(10, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(11, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(12, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(13, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + + +def add_mothula_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Mothula, 0x00, 0, 0x08, 0x06)) + + +def add_blind_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Blind, 0x00, 0, 0x09, 0x05)) + + +def add_kholdstare_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.KholdstareShell, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.FallingIce, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.Kholdstare, 0x00, 0, 0x07, 0x05)) + + +def add_vitreous_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05)) + + +def add_trinexx_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x05)) + + +def boss_writes(world, player, rom): + rom.write_byte(snes_to_pc(0x368107), 1) # centralize drops + 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 + data_tables = world.data_tables[player] + arrghus_can_swim = True + water_tiles_on = True + for dungeon in world.get_dungeons(player): + for level, boss in dungeon.bosses.items(): + if not boss or boss.name in ['Agahnim', 'Agahnim2']: + continue + default_boss = get_dungeon_boss_default(dungeon.name, level) + room_data = get_dungeon_boss_room(dungeon.name, level) + room_id = room_data[0] + if default_boss != boss.name: + sprite_list = data_tables.uw_enemy_table.room_map[room_id] + data = boss_room_remove_data[room_id] + del sprite_list[:data] + add_func, sprite_type = boss_addition_table[boss.name] + add_func(sprite_list, room_id) + if len(sprite_list) > 16: + del sprite_list[16:] + data_tables.room_headers[room_id].sprite_sheet = required_boss_sheets[sprite_type] + # room changes + if boss.name == 'Arrghus' and (dungeon.name != 'Swamp Palace' or level is not None): + rom.write_byte(snes_to_pc(0x0DB6BE), 0) # arrghus can stand on ground + arrghus_can_swim = False + if boss.name != 'Arrghus' and dungeon.name == 'Swamp Palace' and level is None: + remove_water_tiles(data_tables) + water_tiles_on = False + if boss.name == 'Trinexx' and (dungeon.name != 'Turtle Rock' or level is not None): + add_shell_to_boss_room(data_tables, dungeon.name, level, 0xFF2) + data_tables.room_headers[room_id].byte_0 = 0x60 + data_tables.room_headers[room_id].effect = 4 + # $2E, $98, $FF (original shell) + if boss.name == 'Kholdstare' and (dungeon.name != 'Ice Palace' or level is not None): + add_shell_to_boss_room(data_tables, dungeon.name, level, 0xF95) + data_tables.room_headers[room_id].byte_0 = 0xE0 + data_tables.room_headers[room_id].effect = 1 + if boss.name != 'Trinexx' and dungeon.name == 'Turtle Rock' and level is None: + remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xFF2) + # disable trinexx ice breath with No-ops + rom.write_bytes(snes_to_pc(0x09B37E), [0xEA, 0xEA, 0xEA, 0xEA]) + if boss.name != 'Kholdstare' and dungeon.name == 'Ice Palace' and level is None: + remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xF95) + if boss.name != 'Blind' and dungeon.name == 'Thieves Town' and level is None: + rom.write_byte(snes_to_pc(0x368101), 1) # set blind boss door flag + # maiden becomes a random invisible enemy + data_tables.uw_enemy_table.room_map[0x45][0].kind = EnemySprite.PedestalPlaque + if not arrghus_can_swim and water_tiles_on: + remove_water_tiles(data_tables) + + +boss_defaults = { + 'Eastern Palace': 'Armos Knights', + 'Desert Palace': 'Lanmolas', + 'Tower of Hera': 'Moldorm', + 'Palace of Darkness': 'Helmasaur King', + 'Swamp Palace': 'Arrghus', + 'Skull Woods': 'Mothula', + 'Thieves Town': 'Blind', + 'Ice Palace': 'Kholdstare', + 'Misery Mire': 'Vitreous', + 'Turtle Rock': 'Trinexx', +} + +gt_boss_defaults = { + 'bottom': 'Armos Knights', + 'middle': 'Lanmolas', + 'top': 'Moldorm', +} + +boss_room_remove_data = { + 6: 14, 7: 1, 0x1c: 7, 0x29: 1, 0x33: 3, 0x4d: 1, 0x5a: 1, + 0x6c: 3, 0x90: 1, 0xa4: 2, 0xac: 1, 0xc8: 7, 0xde: 3 +} + +boss_addition_table = { + 'Armos Knights': (add_armos_to_list, EnemySprite.ArmosKnight), + 'Lanmolas': (add_lanmolas_to_list, EnemySprite.Lanmolas), + 'Moldorm': (add_moldorm_to_list, EnemySprite.Moldorm), + 'Helmasaur King': (add_helmasaur_king_to_list, EnemySprite.HelmasaurKing), + 'Arrghus': (add_arrghus_to_list, EnemySprite.Arrghus), + 'Mothula': (add_mothula_to_list, EnemySprite.Mothula), + 'Blind': (add_blind_to_list, EnemySprite.Blind), + 'Kholdstare': (add_kholdstare_to_list, EnemySprite.Kholdstare), + 'Vitreous': (add_vitreous_to_list, EnemySprite.Vitreous), + 'Trinexx': (add_trinexx_to_list, EnemySprite.TrinexxRockHead) +} diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index 136c9b29..792ae2e7 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -29,9 +29,9 @@ class DataTables: header.write_to_rom(rom, snes_to_pc(0x30DA00)) # new header table, bank30, tables.asm room_start_address = 0x378000 for room_id, room in self.room_list.items(): - rom.write_bytes(0x1F8000 + room_id * 3, int24_as_bytes(room_start_address)) + rom.write_bytes(snes_to_pc(0x1F8000 + room_id * 3), int24_as_bytes(room_start_address)) door_start, bytes_written = room.write_to_rom(snes_to_pc(room_start_address), rom) - rom.write_bytes(0x1F83C0 + room_id * 3, int24_as_bytes(room_start_address + door_start)) + rom.write_bytes(snes_to_pc(0x1F83C0 + room_id * 3), int24_as_bytes(room_start_address + door_start)) room_start_address += bytes_written # todo: room data doors pointers at 1F83C0 if room_start_address > 0x380000: From 57c479e1c2729304d14087edf4978c996c0f283d Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 25 Oct 2022 09:14:19 -0600 Subject: [PATCH 009/158] Overworld enemizer work --- PotShuffle.py | 12 +- Rom.py | 13 +- source/dungeon/EnemyList.py | 32 +- source/dungeon/RoomList.py | 95 +++ source/enemizer/Bossmizer.py | 1 + source/enemizer/Enemizer.py | 162 +++- source/enemizer/EnemizerTestHarness.py | 43 +- source/enemizer/OwEnemyList.py | 1026 ++++++++++++++++++++++++ source/enemizer/SpriteSheets.py | 41 +- source/enemizer/TilePattern.py | 93 +++ source/rom/DataTables.py | 37 +- 11 files changed, 1494 insertions(+), 61 deletions(-) create mode 100644 source/enemizer/OwEnemyList.py create mode 100644 source/enemizer/TilePattern.py diff --git a/PotShuffle.py b/PotShuffle.py index d2d2547a..fc109b4d 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -1040,7 +1040,7 @@ class PotSecretTable(object): self.room_map = defaultdict(list) self.multiworld_count = 0 - def write_pot_data_to_rom(self, rom, colorize): + def write_pot_data_to_rom(self, rom, colorize, data_tables): pointer_address = snes_to_pc(0x09D87E) # pots currently in bank 9 data_bank_address = snes_to_pc(0x09DACE) # pointer_offset = 0x128 * 2 @@ -1057,9 +1057,13 @@ class PotSecretTable(object): rom.write_bytes(data_pointer + list_idx * 3, pot.pot_data()) if pot.location is not None and not pot.location.forced_item: collection_rate_mask |= 1 << (15 - list_idx) - if colorize and pot.obj_ref: - pot.obj_ref.change_type(Shuffled_Pot) - pot.obj_ref.write_to_rom(rom) + if colorize: + if room in data_tables.room_list: + room_object = data_tables.room_list[room] + room_object.find_all_pots()[list_idx].change_type(Shuffled_Pot) + elif pot.obj_ref: + pot.obj_ref.change_type(Shuffled_Pot) + pot.obj_ref.write_to_rom(rom) list_idx += 1 rom.write_bytes(data_pointer + list_idx * 3, [0xFF, 0xFF]) rom.write_bytes(snes_to_pc(0x28AA60) + room * 2, int16_as_bytes(collection_rate_mask)) diff --git a/Rom.py b/Rom.py index ca9de066..a42028dd 100644 --- a/Rom.py +++ b/Rom.py @@ -35,6 +35,7 @@ from source.classes.SFX import randomize_sfx from source.item.FillUtil import valid_pot_items from source.dungeon.EnemyList import EnemySprite from source.enemizer.Bossmizer import boss_writes +from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' @@ -1511,13 +1512,16 @@ def patch_rom(world, rom, player, team, is_mystery=False): if world.boss_shuffle[player] != 'none': boss_writes(world, player, rom) + write_enemy_shuffle_settings(world, player, rom) - # todo: combine this with data_tables for in place edits if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): for room in world.rooms: if room.player == player and room.modified: - rom.write_bytes(room.address(), room.rom_data()) + if room.index in world.data_tables[player].room_list: + world.data_tables[player].room_list[room.index].doors = room.doorList + else: + rom.write_bytes(room.address(), room.rom_data()) if world.data_tables[player]: colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery'] @@ -1536,8 +1540,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): # 21 bytes from Main import __version__ seedstring = f'{world.seed:09}' if isinstance(world.seed, int) else world.seed - # todo: change to DR when Enemizer is okay with DR - rom.name = bytearray(f'ER{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21] + rom.name = bytearray(f'DR{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21] rom.name.extend([0] * (21 - len(rom.name))) rom.write_bytes(0x7FC0, rom.name) @@ -2535,7 +2538,7 @@ def set_inverted_mode(world, player, rom): 0x190F, 0x9D04, 0x9D04]) write_int16s(rom, snes_to_pc(0x1bb810), [0x00BE, 0x00C0, 0x013E]) write_int16s(rom, snes_to_pc(0x1bb836), [0x001B, 0x001B, 0x001B]) - write_int16(rom, snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance + write_int16(rom, snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance write_int16(rom, snes_to_pc(0x308320), 0x001B) if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: rom.write_byte(snes_to_pc(0x308340), 0x7B) diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index f7df3c92..b7d1c6e8 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -37,8 +37,6 @@ class EnemyStats: # Raven light/dark world - - class EnemySprite(FastEnum): Raven = 0x00 Vulture = 0x01 @@ -84,7 +82,7 @@ class EnemySprite(FastEnum): TelepathicTile = 0x2d FluteKid = 0x2e RaceGameLady = 0x2f - + RaceGameGuy = 0x30 FortuneTeller = 0x31 ArgueBros = 0x32 RupeePull = 0x33 @@ -156,6 +154,7 @@ class EnemySprite(FastEnum): BottleMerchant = 0x75 Zelda = 0x76 Grandma = 0x78 + Bee = 0x79 Agahnim = 0x7a FloatingSkull = 0x7c BigSpike = 0x7d @@ -203,7 +202,7 @@ class EnemySprite(FastEnum): BlueZirro = 0xa9 Pikit = 0xaa CrystalMaiden = 0xab - # ... OW + Apple = 0xac OldMan = 0xad PipeDown = 0xae PipeUp = 0xaf @@ -242,17 +241,29 @@ class EnemySprite(FastEnum): BunnyBeam = 0xd1 FloppingFish = 0xd2 Stal = 0xd3 # alive skull rock? + Landmine = 0xd4 DiggingGameNPC = 0xd5 Ganon = 0xd6 + SmallHeart = 0xd8 + BlueRupee = 0xda + RedRupee = 0xdb + BombRefill1 = 0xdc + BombRefill4 = 0xdd + BombRefill8 = 0xde + + LargeMagic = 0xe0 Faerie = 0xe3 SmallKey = 0xe4 + Mushroom = 0xe7 FakeMasterSword = 0xe8 MagicShopAssistant = 0xe9 HeartPiece = 0xeb SomariaPlatform = 0xed CastleMantle = 0xee MedallionTablet = 0xf2 + PositionTarget = 0xf3 + Boulders = 0xf4 class SpriteType(FastEnum): @@ -488,10 +499,13 @@ class Sprite(object): self.drop_item_kind = drop_item_kind self.location = None + self.original_address = None def copy(self): - return Sprite(self.super_tile, self.kind, self.sub_type, self.layer, self.tile_x, self.tile_y, self.region, - self.drops_item, self.drop_item_kind) + sprite = Sprite(self.super_tile, self.kind, self.sub_type, self.layer, self.tile_x, self.tile_y, self.region, + self.drops_item, self.drop_item_kind) + sprite.original_address = self.original_address + return sprite def sprite_data(self): data = [(self.layer << 7) | ((self.sub_type & 0x18) << 2) | self.tile_y, @@ -506,6 +520,9 @@ class Sprite(object): data.append(code) return data + def sprite_data_ow(self): + return [self.tile_y, self.tile_x, self.kind] + # map of super_tile to list of Sprite objects: vanilla_sprites = {} @@ -519,6 +536,8 @@ def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None def init_vanilla_sprites(): + if vanilla_sprites: + return create_sprite(0x0000, EnemySprite.Ganon, 0x00, 0, 0x17, 0x05, 'Pyramid') create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x12, 0x05, 'Sewers Yet More Rats') create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x15, 0x06, 'Sewers Yet More Rats') @@ -2066,7 +2085,6 @@ layered_oam_rooms = { class EnemyTable: def __init__(self): self.room_map = defaultdict(list) - self.multiworld_count = 0 def write_sprite_data_to_rom(self, rom): pointer_address = snes_to_pc(0x09D62E) diff --git a/source/dungeon/RoomList.py b/source/dungeon/RoomList.py index e7636ff1..ecb2d8df 100644 --- a/source/dungeon/RoomList.py +++ b/source/dungeon/RoomList.py @@ -44,6 +44,14 @@ class Room: rom.write_bytes(address + offset, [0xFF, 0xFF]) return door_start, offset + 2 # how many bytes were written + def find_all_pots(self): + pots = [] + pots.extend([x for x in self.layer1 if x.data[2] == 0xFA]) + pots.extend([x for x in self.layer2 if x.data[2] == 0xFA]) + pots.extend([x for x in self.layer3 if x.data[2] == 0xFA]) + return pots + + Room0006 = Room([0xE1, 0x00], [RoomObject(0x1FA15C, [0x1B, 0xA3, 0xC8]), @@ -405,6 +413,93 @@ Room00C8 = Room([0xE1, 0x00], Room00DE = Room([0xE4, 0x00], [], [RoomObject(0x1FCAE5, [0xAD, 0x21, 0xF9])], []) +Room010C = Room([0xE0, 0x08], + [RoomObject(0x03F25D, [0xFC, 0x62, 0x00]), + RoomObject(0x03F260, [0x19, 0x33, 0x61]), + RoomObject(0x03F263, [0xFC, 0x66, 0x81]), + RoomObject(0x03F266, [0x29, 0x22, 0x01]), + RoomObject(0x03F269, [0xFD, 0x62, 0x02]), + RoomObject(0x03F26C, [0x59, 0x33, 0x62]), + RoomObject(0x03F26F, [0xFD, 0x66, 0x83]), + RoomObject(0x03F272, [0xFC, 0x89, 0x00]), + RoomObject(0x03F275, [0x22, 0xA1, 0x61]), + RoomObject(0x03F278, [0xFC, 0x8E, 0x81]), + RoomObject(0x03F27B, [0x90, 0xCA, 0x0E]), + RoomObject(0x03F27E, [0xD0, 0xE6, 0x10]), + RoomObject(0x03F281, [0xFE, 0x41, 0x00]), + RoomObject(0x03F284, [0x93, 0x23, 0x61]), + RoomObject(0x03F287, [0x92, 0x98, 0x61]), + RoomObject(0x03F28A, [0xFF, 0x81, 0x02]), + RoomObject(0x03F28D, [0xE3, 0x23, 0x62]), + RoomObject(0x03F290, [0xE2, 0x98, 0x62]), + RoomObject(0x03F293, [0xFE, 0x71, 0xC8]), + RoomObject(0x03F296, [0xAD, 0x1C, 0x03]), + RoomObject(0x03F299, [0xFF, 0x51, 0xCA]), + RoomObject(0x03F29C, [0x9D, 0x2D, 0x63]), + RoomObject(0x03F29F, [0xFE, 0x75, 0x89]), + RoomObject(0x03F2A2, [0xAD, 0x58, 0x04]), + RoomObject(0x03F2A5, [0xD5, 0x2D, 0x64]), + RoomObject(0x03F2A8, [0xFF, 0x55, 0x8B]), + RoomObject(0x03F2AB, [0xFE, 0x78, 0x08]), + RoomObject(0x03F2AE, [0xAD, 0x80, 0x03]), + RoomObject(0x03F2B1, [0xFF, 0x58, 0x0A]), + RoomObject(0x03F2B4, [0x9C, 0x91, 0x63]), + RoomObject(0x03F2B7, [0xFE, 0x7A, 0x09]), + RoomObject(0x03F2BA, [0xAD, 0xA0, 0x04]), + RoomObject(0x03F2BD, [0xFF, 0x5A, 0x0B]), + RoomObject(0x03F2C0, [0xD4, 0x91, 0x64]), + RoomObject(0x03F2C3, [0x2C, 0x2E, 0xDC]), + RoomObject(0x03F2C6, [0x3D, 0x3E, 0xF9]), + RoomObject(0x03F2C9, [0x32, 0xA9, 0xF9]), + RoomObject(0x03F2CC, [0x3A, 0xA9, 0xF9]), + RoomObject(0x03F2CF, [0x42, 0xA9, 0xF9]), + RoomObject(0x03F2D2, [0x4A, 0xA9, 0xF9]), + RoomObject(0x03F2D5, [0x57, 0x9E, 0x69]), + RoomObject(0x03F2D8, [0x54, 0xDC, 0x69]), + RoomObject(0x03F2DB, [0x38, 0xC8, 0x89]), + RoomObject(0x03F2DE, [0x58, 0x9C, 0x89]), + RoomObject(0x03F2E1, [0x58, 0xA8, 0x89]), + RoomObject(0x03F2E4, [0x58, 0xB4, 0x89]), + RoomObject(0x03F2E7, [0x58, 0xC0, 0x89]), + RoomObject(0x03F2EA, [0x58, 0xCC, 0x89]), + RoomObject(0x03F2ED, [0x58, 0xD8, 0x89]), + RoomObject(0x03F2F0, [0x58, 0xE4, 0x89]), + RoomObject(0x03F2F3, [0xAA, 0x2E, 0xC8]), + RoomObject(0x03F2F6, [0xAA, 0x29, 0x3F]), + RoomObject(0x03F2F9, [0xAA, 0x2E, 0x79]), + RoomObject(0x03F2FC, [0xAA, 0x59, 0x40]), + RoomObject(0x03F2FF, [0xD6, 0x2E, 0x7A]), + RoomObject(0x03F302, [0xAA, 0x90, 0xC8]), + RoomObject(0x03F305, [0xAA, 0x8D, 0x3F]), + RoomObject(0x03F308, [0xA8, 0x93, 0x79]), + RoomObject(0x03F30B, [0xAA, 0xA1, 0x40]), + RoomObject(0x03F30E, [0xD4, 0x93, 0x7A]), + RoomObject(0x03F311, [0xB9, 0x5B, 0xF9]), + RoomObject(0x03F314, [0xB9, 0xA3, 0xF9]), + RoomObject(0x03F317, [0x9C, 0x68, 0x22]), + RoomObject(0x03F31A, [0x9C, 0x6A, 0x69]), + RoomObject(0x03F31D, [0x9C, 0x7C, 0x22]), + RoomObject(0x03F320, [0xD4, 0x7C, 0x22]), + RoomObject(0x03F323, [0x9C, 0xB0, 0x22]), + RoomObject(0x03F326, [0xD4, 0xB0, 0x22]), + RoomObject(0x03F329, [0xB3, 0x73, 0xFA]), + RoomObject(0x03F32C, [0xCD, 0x68, 0xDD]), + RoomObject(0x03F32F, [0xB8, 0xC0, 0xDD]), + RoomObject(0x03F332, [0x08, 0x00, 0x60]), + RoomObject(0x03F335, [0x10, 0x00, 0x60]), + RoomObject(0x03F338, [0x1B, 0x10, 0xC0]), + RoomObject(0x03F33B, [0x59, 0x10, 0xC0]), + RoomObject(0x03F33E, [0x68, 0x23, 0xC0]), + RoomObject(0x03F341, [0x68, 0x61, 0xC0]), + RoomObject(0x03F344, [0x1B, 0x91, 0x60]), + RoomObject(0x03F347, [0x88, 0x10, 0x60]), + RoomObject(0x03F34A, [0xF0, 0x10, 0x60]), + RoomObject(0x03F34D, [0x90, 0xDF, 0xA1]), + RoomObject(0x03F350, [0xD4, 0xF7, 0xA3])], [], + [DoorObject(Position.InteriorW, DoorKind.TrapTriggerable), + DoorObject(Position.SouthW, DoorKind.CaveEntrance), DoorObject(Position.SouthE, DoorKind.CaveEntrance)] + ) + Room0127 = Room([0xE1, 0x00], [RoomObject(0x0AB600, [0xFE, 0x89, 0x00]), RoomObject(0x0AB603, [0xA2, 0xA1, 0x61]), diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py index 935b11e3..dcc34b21 100644 --- a/source/enemizer/Bossmizer.py +++ b/source/enemizer/Bossmizer.py @@ -124,6 +124,7 @@ 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/Enemizer.py b/source/enemizer/Enemizer.py index dfea8102..edd0763c 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -1,7 +1,11 @@ import RaceRandom as random +from Utils import snes_to_pc from source.dungeon.EnemyList import SpriteType -from source.enemizer.SpriteSheets import uw_sub_group_choices, setup_required_dungeon_groups +from source.dungeon.RoomList import Room010C +from source.enemizer.SpriteSheets import sub_group_choices, setup_required_dungeon_groups +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 @@ -52,7 +56,7 @@ def setup_specific_requirements(data_tables): if requirement.good_for_uw_water(): water_groups.update(requirement.groups) for i in range(0, 4): - limited = [x for x in requirement.sub_groups[i] if x in uw_sub_group_choices[i]] + limited = [x for x in requirement.sub_groups[i] if x in sub_group_choices[i]] water_sub_groups[i].update(limited) if requirement.good_for_shutter(): killable_groups.update(requirement.groups) @@ -76,7 +80,7 @@ def get_possible_sheets(room_id, data_tables, specific, uw_sheets): killable_needed = room_id in shutter_sprites water_needed = room_id in water_rooms - for sheet in data_tables.sprite_sheets.values(): + for sheet in uw_sheets: if room_id in sheet.room_set: return [sheet] @@ -156,7 +160,18 @@ def get_possible_sheets(room_id, data_tables, specific, uw_sheets): return possible_sheets -def uw_candidate_sprites(data_tables): +def get_possible_ow_sheets(area_id, ow_sheets): + # 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 + + +def find_candidate_sprites(data_tables, sheet_range): requirements = data_tables.sprite_requirements uw_sprite_candidates = [] uw_sheet_candidates = [] @@ -173,7 +188,7 @@ def uw_candidate_sprites(data_tables): candidate_sub_groups[i].update(r.sub_groups[i]) uw_sprite_candidates.append(k) - for num in range(65, 124): + for num in sheet_range: sheet = data_tables.sprite_sheets[num] if candidate_groups and sheet not in candidate_groups: continue @@ -196,6 +211,17 @@ def get_possible_enemy_sprites(room_id, sheet, uw_sprites, data_tables): return ret +def get_possible_enemy_sprites_ow(sheet, sprites, data_tables): + ret = [] + for sprite in sprites: + requirement = data_tables.sprite_requirements[sprite] + if isinstance(requirement, dict): + continue + if sheet.valid_sprite(requirement): + ret.append(requirement) + return ret + + def get_randomize_able_sprites(room_id, data_tables): sprite_table = {} for idx, sprite in enumerate(data_tables.uw_enemy_table.room_map[room_id]): @@ -211,13 +237,28 @@ def get_randomize_able_sprites(room_id, data_tables): return sprite_table +def get_randomize_able_sprites_ow(area_id, data_tables): + sprite_table = {} + for idx, sprite in enumerate(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 data_tables.sprite_requirements: + continue + req = data_tables.sprite_requirements[key] + if isinstance(req, dict): + continue + if not req.static and req.can_randomize: + sprite_table[idx] = sprite + 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 = uw_candidate_sprites(data_tables) + uw_candidates, uw_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}: @@ -228,26 +269,93 @@ def randomize_underworld_rooms(data_tables): randomizeable_sprites = get_randomize_able_sprites(room_id, data_tables) if randomizeable_sprites: candidate_sheets = get_possible_sheets(room_id, data_tables, specific, uw_sheets) - 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) - if room_id in water_rooms: - water_sprites = [x for x in candidate_sprites if x.water_only] - 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: - key_sprites = [x for x in candidate_sprites if x.good_for_key_drop() and not x.water_only] - chosen = random.choice(key_sprites) - elif room_id in shutter_sprites and i in shutter_sprites[room_id]: - killable_sprite = [x for x in candidate_sprites if x.good_for_shutter() and not x.water_only] - chosen = random.choice(killable_sprite) + 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 else: - non_water = [x for x in candidate_sprites if not x.water_only] - chosen = random.choice(non_water) - sprite.kind = chosen.sprite + 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 + done = randomized # done with sprites - # done with rooms \ No newline at end of file + # 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)) + 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] + area_list = areas_to_randomize + [x + 0x40 for x in areas_to_randomize] # light world + dark world + 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) + 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 + + +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_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 + + +def write_enemy_shuffle_settings(world, player, rom): + if world.enemy_shuffle[player] != 'none': + # killable thief + rom.write_byte(snes_to_pc(0x368108), 0xc4) + rom.write_byte(snes_to_pc(0x0DB237), 4) # health value: # todo: thief health value + + # mimic room barriers + data_tables = world.data_tables[player] + mimic_room = data_tables.room_list[0x10c] = Room010C + mimic_room.layer1[40].data[0] = 0x54 # rail adjust + mimic_room.layer1[40].data[1] = 0x9C + mimic_room.layer1[45].data[1] = 0xB0 # block adjust 1 + mimic_room.layer1[47].data[1] = 0xD0 # block adjust 2 + if world.enemy_shuffle[player] == 'random': + rom.write_byte(snes_to_pc(0x368100), 1) # randomize bushes + # random tile pattern + pattern_name, tile_pattern = random.choice(tile_patterns) + rom.write_byte(snes_to_pc(0x9BA1D), len(tile_pattern)) + for idx, pair in enumerate(tile_pattern): + rom.write_byte(snes_to_pc(0x09BA2A + idx), (pair[0] + 3) * 16) + rom.write_byte(snes_to_pc(0x09BA40 + idx), (pair[1] + 4) * 16) diff --git a/source/enemizer/EnemizerTestHarness.py b/source/enemizer/EnemizerTestHarness.py index a05c0d0c..6e52933d 100644 --- a/source/enemizer/EnemizerTestHarness.py +++ b/source/enemizer/EnemizerTestHarness.py @@ -1,4 +1,5 @@ from types import SimpleNamespace +from collections import Counter, defaultdict from source.dungeon.EnemyList import enemy_names, SpriteType from source.enemizer.Enemizer import randomize_underworld_rooms @@ -8,15 +9,35 @@ import RaceRandom as random if __name__ == '__main__': random.seed(42) - 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: - print(f' Overlord #{i+1} {hex(sprite.kind)}:') - else: - print(f' Enemy #{i+1} {enemy_names[sprite.kind]}:') + 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/OwEnemyList.py b/source/enemizer/OwEnemyList.py new file mode 100644 index 00000000..03c6b2b3 --- /dev/null +++ b/source/enemizer/OwEnemyList.py @@ -0,0 +1,1026 @@ +from source.dungeon.EnemyList import Sprite, EnemySprite +vanilla_sprites_ow = {} + + +def create_sprite(area_id, kind, tile_x, tile_y, region=None, address=None): + 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) + sprite.original_address = address + vanilla_sprites_ow[area_id].append(sprite) + + +def init_vanilla_sprites_ow(): + create_sprite(0x21b, EnemySprite.LightningGate, 0x1F, 0x06, '', 0x09CB42) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x01, 0x12, '', 0x09CB45) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x01, 0x14, '', 0x09CB48) + create_sprite(0x21b, EnemySprite.GreenGuard, 0x1F, 0x13, '', 0x09CB4B) + create_sprite(0x21b, EnemySprite.GreenKnifeGuard, 0x1F, 0x1A, '', 0x09CB4E) + create_sprite(0x21b, EnemySprite.GreenKnifeGuard, 0x20, 0x1A, '', 0x09CB51) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x2D, 0x25, '', 0x09CB54) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x20, 0x29, '', 0x09CB57) + create_sprite(0x21d, EnemySprite.Apple, 0x0B, 0x06, '', 0x09CB5B) + create_sprite(0x22b, EnemySprite.TutorialGuard, 0x09, 0x1E, '', 0x09CB5F) + create_sprite(0x22b, EnemySprite.TutorialGuard, 0x0B, 0x1E, '', 0x09CB62) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x1E, 0x18, '', 0x09CB66) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x1E, 0x1A, '', 0x09CB69) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x0D, 0x1E, '', 0x09CB6C) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x0F, 0x1E, '', 0x09CB6F) + create_sprite(0x232, EnemySprite.BombRefill1, 0x1A, 0x09, '', 0x09CB73) + create_sprite(0x232, EnemySprite.SmallHeart, 0x18, 0x12, '', 0x09CB76) + + # Screen40: + # dark world + create_sprite(0x40, EnemySprite.Ropa, 0x1A, 0x07, '', 0x09CB7A) + create_sprite(0x40, EnemySprite.Ropa, 0x12, 0x11, '', 0x09CB7D) + create_sprite(0x40, EnemySprite.Ropa, 0x0A, 0x1E, '', 0x09CB80) + create_sprite(0x40, EnemySprite.Ropa, 0x2F, 0x09, '', 0x09CB83) + create_sprite(0x40, EnemySprite.Snapdragon, 0x31, 0x0A, '', 0x09CB86) + create_sprite(0x40, EnemySprite.Ropa, 0x33, 0x0B, '', 0x09CB89) + create_sprite(0x40, EnemySprite.Ropa, 0x29, 0x14, '', 0x09CB8C) + create_sprite(0x40, EnemySprite.Ropa, 0x23, 0x16, '', 0x09CB8F) + create_sprite(0x40, EnemySprite.Pikit, 0x39, 0x17, '', 0x09CB92) + create_sprite(0x40, EnemySprite.Ropa, 0x0A, 0x21, '', 0x09CB95) + create_sprite(0x40, EnemySprite.Ropa, 0x1A, 0x25, '', 0x09CB98) + create_sprite(0x40, EnemySprite.Pikit, 0x0B, 0x28, '', 0x09CB9B) + create_sprite(0x40, EnemySprite.Ropa, 0x1E, 0x30, '', 0x09CB9E) + create_sprite(0x40, EnemySprite.Ropa, 0x0E, 0x38, '', 0x09CBA1) + create_sprite(0x40, EnemySprite.Ropa, 0x11, 0x38, '', 0x09CBA4) + create_sprite(0x40, EnemySprite.Ropa, 0x1A, 0x39, '', 0x09CBA7) + create_sprite(0x40, EnemySprite.Ropa, 0x2D, 0x21, '', 0x09CBAA) + create_sprite(0x40, EnemySprite.Ropa, 0x32, 0x28, '', 0x09CBAD) + create_sprite(0x40, EnemySprite.Ropa, 0x37, 0x32, '', 0x09CBB0) + create_sprite(0x40, EnemySprite.Pikit, 0x28, 0x37, '', 0x09CBB3) + # Screen42: + create_sprite(0x42, EnemySprite.Snapdragon, 0x0C, 0x11, '', 0x09CBB7) + create_sprite(0x42, EnemySprite.Snapdragon, 0x0C, 0x13, '', 0x09CBBA) + create_sprite(0x42, EnemySprite.Faerie, 0x06, 0x16, '', 0x09CBBD) + create_sprite(0x42, EnemySprite.Moblin, 0x0E, 0x19, '', 0x09CBC0) + # Screen43: + create_sprite(0x43, EnemySprite.Waterfall, 0x2F, 0x0C, '', 0x09CBC4) + create_sprite(0x43, EnemySprite.BullyPinkBall, 0x20, 0x18, '', 0x09CBC7) + # Screen45: + create_sprite(0x45, EnemySprite.Lynel, 0x06, 0x0C, '', 0x09CBCB) + create_sprite(0x45, EnemySprite.Lynel, 0x1D, 0x0E, '', 0x09CBCE) + create_sprite(0x45, EnemySprite.Lynel, 0x20, 0x0B, '', 0x09CBD1) + # Screen47: + create_sprite(0x47, EnemySprite.RupeePull, 0x16, 0x14, '', 0x09CBD5) + # Screen4A: + create_sprite(0x4a, EnemySprite.RupeePull, 0x0E, 0x06, '', 0x09CBD9) + create_sprite(0x4a, EnemySprite.HeartPiece, 0x18, 0x08, '', 0x09CBDC) + create_sprite(0x4a, EnemySprite.Moblin, 0x0B, 0x0F, '', 0x09CBDF) + create_sprite(0x4a, EnemySprite.Moblin, 0x08, 0x10, '', 0x09CBE2) + create_sprite(0x4a, EnemySprite.Moblin, 0x16, 0x13, '', 0x09CBE5) + create_sprite(0x4a, EnemySprite.Raven, 0x13, 0x13, '', 0x09CBE8) + create_sprite(0x4a, EnemySprite.Raven, 0x13, 0x14, '', 0x09CBEB) + 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.Catfish, 0x04, 0x0B, '', 0x09CBF8) + create_sprite(0x4f, EnemySprite.Stal, 0x18, 0x0D, '', 0x09CBFB) + create_sprite(0x4f, EnemySprite.Ropa, 0x1A, 0x11, '', 0x09CBFE) + # Screen50: + create_sprite(0x50, EnemySprite.Poe, 0x16, 0x0B, '', 0x09CC02) + create_sprite(0x50, EnemySprite.Moblin, 0x05, 0x0C, '', 0x09CC05) + create_sprite(0x50, EnemySprite.TalkingTree, 0x08, 0x0E, '', 0x09CC08) + create_sprite(0x50, EnemySprite.Cucco, 0x19, 0x13, '', 0x09CC0B) + create_sprite(0x50, EnemySprite.Moblin, 0x08, 0x18, '', 0x09CC0E) + # Screen51: + create_sprite(0x51, EnemySprite.UsainBolt, 0x17, 0x0E, '', 0x09CC12) + create_sprite(0x51, EnemySprite.Faerie, 0x08, 0x10, '', 0x09CC15) + create_sprite(0x51, EnemySprite.Faerie, 0x09, 0x10, '', 0x09CC18) + create_sprite(0x51, EnemySprite.Stal, 0x1C, 0x15, '', 0x09CC1B) + create_sprite(0x51, EnemySprite.Moblin, 0x14, 0x16, '', 0x09CC1E) + create_sprite(0x51, EnemySprite.Moblin, 0x0E, 0x17, '', 0x09CC21) + # Screen52: + create_sprite(0x52, EnemySprite.Stal, 0x12, 0x09, '', 0x09CC25) + create_sprite(0x52, EnemySprite.Moblin, 0x15, 0x0D, '', 0x09CC28) + create_sprite(0x52, EnemySprite.BlueGuard, 0x07, 0x10, '', 0x09CC2B) + create_sprite(0x52, EnemySprite.BlueGuard, 0x14, 0x17, '', 0x09CC2E) + create_sprite(0x52, EnemySprite.Moblin, 0x0E, 0x18, '', 0x09CC31) + # Screen53: + create_sprite(0x53, EnemySprite.Stal, 0x06, 0x0B, '', 0x09CC35) + create_sprite(0x53, EnemySprite.Hinox, 0x15, 0x0C, '', 0x09CC38) + create_sprite(0x53, EnemySprite.Ropa, 0x08, 0x0D, '', 0x09CC3B) + create_sprite(0x53, EnemySprite.Moblin, 0x0D, 0x15, '', 0x09CC3E) + create_sprite(0x53, EnemySprite.Snapdragon, 0x16, 0x18, '', 0x09CC41) + # Screen54: + create_sprite(0x54, EnemySprite.Ropa, 0x14, 0x0D, '', 0x09CC45) + create_sprite(0x54, EnemySprite.BombRefill1, 0x05, 0x0B, '', 0x09CC48) + create_sprite(0x54, EnemySprite.RedRupee, 0x19, 0x0B, '', 0x09CC4B) + create_sprite(0x54, EnemySprite.Ropa, 0x07, 0x0F, '', 0x09CC4E) + create_sprite(0x54, EnemySprite.Faerie, 0x0F, 0x0E, '', 0x09CC51) + create_sprite(0x54, EnemySprite.Ropa, 0x19, 0x10, '', 0x09CC54) + create_sprite(0x54, EnemySprite.Ropa, 0x0D, 0x14, '', 0x09CC57) + create_sprite(0x54, EnemySprite.Hinox, 0x11, 0x19, '', 0x09CC5A) + # Screen55: + create_sprite(0x55, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09CC5E) + create_sprite(0x55, EnemySprite.Hinox, 0x16, 0x0E, '', 0x09CC61) + create_sprite(0x55, EnemySprite.Stal, 0x18, 0x0E, '', 0x09CC64) + create_sprite(0x55, EnemySprite.BlueRupee, 0x1B, 0x0F, '', 0x09CC67) + create_sprite(0x55, EnemySprite.Hinox, 0x07, 0x17, '', 0x09CC6A) + 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.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.Octorok, 0x16, 0x08, '', 0x09CC87) + create_sprite(0x57, EnemySprite.Octorok, 0x18, 0x0A, '', 0x09CC8A) + create_sprite(0x57, EnemySprite.Octorok, 0x0E, 0x0E, '', 0x09CC8D) + create_sprite(0x57, EnemySprite.Stal, 0x0E, 0x10, '', 0x09CC90) + create_sprite(0x57, EnemySprite.Octorok, 0x0E, 0x1A, '', 0x09CC93) + create_sprite(0x57, EnemySprite.Octorok, 0x0D, 0x1B, '', 0x09CC96) + # Screen58: + create_sprite(0x58, EnemySprite.Moblin, 0x13, 0x06, '', 0x09CC9A) + create_sprite(0x58, EnemySprite.TalkingTree, 0x18, 0x0C, '', 0x09CC9D) + create_sprite(0x58, EnemySprite.BlueGuard, 0x07, 0x1C, '', 0x09CCA0) + create_sprite(0x58, EnemySprite.Moblin, 0x35, 0x0A, '', 0x09CCA3) + create_sprite(0x58, EnemySprite.Poe, 0x2B, 0x0C, '', 0x09CCA6) + create_sprite(0x58, EnemySprite.Thief, 0x2E, 0x17, '', 0x09CCA9) + create_sprite(0x58, EnemySprite.ThievesTownGrate, 0x20, 0x1C, '', 0x09CCAC) + create_sprite(0x58, EnemySprite.Poe, 0x18, 0x25, '', 0x09CCAF) + create_sprite(0x58, EnemySprite.Thief, 0x0D, 0x27, '', 0x09CCB2) + create_sprite(0x58, EnemySprite.Poe, 0x1D, 0x28, '', 0x09CCB5) + create_sprite(0x58, EnemySprite.Moblin, 0x12, 0x2E, '', 0x09CCB8) + create_sprite(0x58, EnemySprite.Cucco, 0x16, 0x34, '', 0x09CCBB) + create_sprite(0x58, EnemySprite.Cucco, 0x15, 0x37, '', 0x09CCBE) + create_sprite(0x58, EnemySprite.Cucco, 0x28, 0x27, '', 0x09CCC1) + create_sprite(0x58, EnemySprite.BlueGuard, 0x33, 0x2F, '', 0x09CCC4) + create_sprite(0x58, EnemySprite.Poe, 0x2C, 0x34, '', 0x09CCC7) + create_sprite(0x58, EnemySprite.TalkingTree, 0x37, 0x35, '', 0x09CCCA) + # Screen5A: + create_sprite(0x5a, EnemySprite.Moblin, 0x0F, 0x08, '', 0x09CCCE) + create_sprite(0x5a, EnemySprite.TalkingTree, 0x12, 0x08, '', 0x09CCD1) + create_sprite(0x5a, EnemySprite.Stal, 0x12, 0x0D, '', 0x09CCD4) + create_sprite(0x5a, EnemySprite.Moblin, 0x15, 0x0C, '', 0x09CCD7) + create_sprite(0x5a, EnemySprite.Hinox, 0x0B, 0x0F, '', 0x09CCDA) + create_sprite(0x5a, EnemySprite.Moblin, 0x0E, 0x19, '', 0x09CCDD) + # Screen5B: + create_sprite(0x5b, EnemySprite.Ropa, 0x15, 0x17, '', 0x09CCE1) + create_sprite(0x5b, EnemySprite.HeartPiece, 0x34, 0x12, '', 0x09CCE4) + create_sprite(0x5b, EnemySprite.RupeePull, 0x13, 0x24, '', 0x09CCE7) + create_sprite(0x5b, EnemySprite.Moblin, 0x0F, 0x27, '', 0x09CCEA) + create_sprite(0x5b, EnemySprite.Faerie, 0x17, 0x2A, '', 0x09CCED) + create_sprite(0x5b, EnemySprite.Moblin, 0x0C, 0x2A, '', 0x09CCF0) + create_sprite(0x5b, EnemySprite.Hinox, 0x1E, 0x2C, '', 0x09CCF3) + create_sprite(0x5b, EnemySprite.Snapdragon, 0x34, 0x25, '', 0x09CCF6) + create_sprite(0x5b, EnemySprite.Moblin, 0x32, 0x27, '', 0x09CCF9) + create_sprite(0x5b, EnemySprite.Moblin, 0x30, 0x29, '', 0x09CCFC) + create_sprite(0x5b, EnemySprite.Hinox, 0x21, 0x2C, '', 0x09CCFF) + # Screen5D: + create_sprite(0x5d, EnemySprite.TalkingTree, 0x0B, 0x08, '', 0x09CD03) + create_sprite(0x5d, EnemySprite.Hinox, 0x07, 0x09, '', 0x09CD06) + create_sprite(0x5d, EnemySprite.Snapdragon, 0x06, 0x0B, '', 0x09CD09) + create_sprite(0x5d, EnemySprite.Moblin, 0x18, 0x0B, '', 0x09CD0C) + create_sprite(0x5d, EnemySprite.Stal, 0x17, 0x0E, '', 0x09CD0F) + create_sprite(0x5d, EnemySprite.Moblin, 0x1A, 0x10, '', 0x09CD12) + create_sprite(0x5d, EnemySprite.BlueGuard, 0x08, 0x11, '', 0x09CD15) + # Screen5E: + create_sprite(0x5e, EnemySprite.Ropa, 0x0D, 0x04, '', 0x09CD19) + create_sprite(0x5e, EnemySprite.Ropa, 0x03, 0x11, '', 0x09CD1C) + create_sprite(0x5e, EnemySprite.Kiki, 0x15, 0x11, '', 0x09CD1F) + create_sprite(0x5e, EnemySprite.Ropa, 0x12, 0x1A, '', 0x09CD22) + create_sprite(0x5e, EnemySprite.Ropa, 0x27, 0x09, '', 0x09CD25) + create_sprite(0x5e, EnemySprite.Ropa, 0x2F, 0x10, '', 0x09CD28) + create_sprite(0x5e, EnemySprite.Moblin, 0x25, 0x15, '', 0x09CD2B) + create_sprite(0x5e, EnemySprite.Hinox, 0x26, 0x17, '', 0x09CD2E) + create_sprite(0x5e, EnemySprite.Ropa, 0x35, 0x18, '', 0x09CD31) + create_sprite(0x5e, EnemySprite.Ropa, 0x2A, 0x1E, '', 0x09CD34) + create_sprite(0x5e, EnemySprite.Moblin, 0x0A, 0x26, '', 0x09CD37) + create_sprite(0x5e, EnemySprite.Moblin, 0x0C, 0x2B, '', 0x09CD3A) + create_sprite(0x5e, EnemySprite.Ropa, 0x07, 0x35, '', 0x09CD3D) + create_sprite(0x5e, EnemySprite.Hinox, 0x16, 0x37, '', 0x09CD40) + create_sprite(0x5e, EnemySprite.Snapdragon, 0x09, 0x38, '', 0x09CD43) + create_sprite(0x5e, EnemySprite.Moblin, 0x32, 0x24, '', 0x09CD46) + create_sprite(0x5e, EnemySprite.Snapdragon, 0x35, 0x28, '', 0x09CD49) + create_sprite(0x5e, EnemySprite.Ropa, 0x24, 0x30, '', 0x09CD4C) + create_sprite(0x5e, EnemySprite.Faerie, 0x30, 0x30, '', 0x09CD4F) + create_sprite(0x5e, EnemySprite.Hinox, 0x35, 0x36, '', 0x09CD52) + create_sprite(0x5e, EnemySprite.Raven, 0x29, 0x37, '', 0x09CD55) + # Screen62: + create_sprite(0x62, EnemySprite.PurpleChest, 0x0D, 0x05, '', 0x09CD59) + create_sprite(0x62, EnemySprite.Cucco, 0x13, 0x11, '', 0x09CD5C) + create_sprite(0x62, EnemySprite.Cucco, 0x11, 0x13, '', 0x09CD5F) + create_sprite(0x62, EnemySprite.Cucco, 0x15, 0x15, '', 0x09CD62) + create_sprite(0x62, EnemySprite.Cucco, 0x09, 0x16, '', 0x09CD65) + create_sprite(0x62, EnemySprite.Cucco, 0x11, 0x17, '', 0x09CD68) + # Screen65: + create_sprite(0x65, EnemySprite.Moblin, 0x13, 0x07, '', 0x09CD6C) + create_sprite(0x65, EnemySprite.Stal, 0x0F, 0x0A, '', 0x09CD6F) + create_sprite(0x65, EnemySprite.Moblin, 0x0E, 0x0C, '', 0x09CD72) + create_sprite(0x65, EnemySprite.Hinox, 0x05, 0x11, '', 0x09CD75) + create_sprite(0x65, EnemySprite.Moblin, 0x0A, 0x16, '', 0x09CD78) + create_sprite(0x65, EnemySprite.Moblin, 0x13, 0x16, '', 0x09CD7B) + # Screen68: + create_sprite(0x68, EnemySprite.DiggingGameNPC, 0x0E, 0x11, '', 0x09CD7F) + # Screen69: + create_sprite(0x69, EnemySprite.Smithy, 0x06, 0x09, '', 0x09CD83) + # Screen6A: + create_sprite(0x6a, EnemySprite.FluteKid, 0x0E, 0x0F, '', 0x09CD87) + # Screen6B: + create_sprite(0x6b, EnemySprite.TalkingTree, 0x16, 0x08, '', 0x09CD8B) + create_sprite(0x6b, EnemySprite.Ropa, 0x08, 0x09, '', 0x09CD8E) + create_sprite(0x6b, EnemySprite.TalkingTree, 0x17, 0x0F, '', 0x09CD91) + create_sprite(0x6b, EnemySprite.Moblin, 0x13, 0x16, '', 0x09CD94) + create_sprite(0x6b, EnemySprite.Moblin, 0x0F, 0x19, '', 0x09CD97) + # Screen6C: + create_sprite(0x6c, EnemySprite.Snapdragon, 0x15, 0x06, '', 0x09CD9B) + create_sprite(0x6c, EnemySprite.Moblin, 0x15, 0x0A, '', 0x09CD9E) + create_sprite(0x6c, EnemySprite.Moblin, 0x14, 0x0D, '', 0x09CDA1) + create_sprite(0x6c, EnemySprite.Hinox, 0x14, 0x16, '', 0x09CDA4) + create_sprite(0x6c, EnemySprite.Ropa, 0x09, 0x19, '', 0x09CDA7) + # Screen6D: + create_sprite(0x6d, EnemySprite.Ropa, 0x0F, 0x05, '', 0x09CDAB) + create_sprite(0x6d, EnemySprite.Hinox, 0x0D, 0x07, '', 0x09CDAE) + create_sprite(0x6d, EnemySprite.BlueGuard, 0x12, 0x08, '', 0x09CDB1) + create_sprite(0x6d, EnemySprite.BlueGuard, 0x10, 0x0A, '', 0x09CDB4) + create_sprite(0x6d, EnemySprite.Stal, 0x10, 0x1A, '', 0x09CDB7) + create_sprite(0x6d, EnemySprite.Stal, 0x13, 0x1B, '', 0x09CDBA) + # Screen6E: + create_sprite(0x6e, EnemySprite.BlueRupee, 0x0C, 0x08, '', 0x09CDBE) + create_sprite(0x6e, EnemySprite.Bee, 0x10, 0x09, '', 0x09CDC1) + create_sprite(0x6e, EnemySprite.Apple, 0x14, 0x0A, '', 0x09CDC4) + create_sprite(0x6e, EnemySprite.BlueGuard, 0x08, 0x0B, '', 0x09CDC7) + create_sprite(0x6e, EnemySprite.BlueGuard, 0x10, 0x0E, '', 0x09CDCA) + create_sprite(0x6e, EnemySprite.Ropa, 0x1A, 0x18, '', 0x09CDCD) + # Screen6F: + create_sprite(0x6f, EnemySprite.Snapdragon, 0x0D, 0x08, '', 0x09CDD1) + create_sprite(0x6f, EnemySprite.Snapdragon, 0x0F, 0x08, '', 0x09CDD4) + create_sprite(0x6f, EnemySprite.Snapdragon, 0x0E, 0x0B, '', 0x09CDD7) + create_sprite(0x6f, EnemySprite.Raven, 0x17, 0x0C, '', 0x09CDDA) + 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.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) + # Screen72: + create_sprite(0x72, EnemySprite.TalkingTree, 0x1B, 0x0B, '', 0x09CE06) + create_sprite(0x72, EnemySprite.BlueGuard, 0x10, 0x0D, '', 0x09CE09) + create_sprite(0x72, EnemySprite.BlueGuard, 0x13, 0x0E, '', 0x09CE0C) + create_sprite(0x72, EnemySprite.TalkingTree, 0x1A, 0x14, '', 0x09CE0F) + create_sprite(0x72, EnemySprite.Ropa, 0x0B, 0x17, '', 0x09CE12) + # Screen73: + create_sprite(0x73, EnemySprite.Pikit, 0x17, 0x0C, '', 0x09CE16) + create_sprite(0x73, EnemySprite.Moblin, 0x09, 0x0D, '', 0x09CE19) + create_sprite(0x73, EnemySprite.GreenZirro, 0x14, 0x0E, '', 0x09CE1C) + create_sprite(0x73, EnemySprite.GreenZirro, 0x15, 0x1A, '', 0x09CE1F) + create_sprite(0x73, EnemySprite.BlueZirro, 0x1B, 0x1B, '', 0x09CE22) + # Screen74: + create_sprite(0x74, EnemySprite.Moblin, 0x0B, 0x0D, '', 0x09CE26) + create_sprite(0x74, EnemySprite.RupeePull, 0x17, 0x0E, '', 0x09CE29) + create_sprite(0x74, EnemySprite.GreenZirro, 0x10, 0x11, '', 0x09CE2C) + create_sprite(0x74, EnemySprite.Moblin, 0x15, 0x11, '', 0x09CE2F) + create_sprite(0x74, EnemySprite.Pikit, 0x0A, 0x12, '', 0x09CE32) + create_sprite(0x74, EnemySprite.Apple, 0x0E, 0x14, '', 0x09CE35) + create_sprite(0x74, EnemySprite.Moblin, 0x11, 0x17, '', 0x09CE38) + # Screen75: + create_sprite(0x75, EnemySprite.Stal, 0x0A, 0x05, '', 0x09CE3C) + 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.Pikit, 0x09, 0x17, '', 0x09CE4B) + create_sprite(0x75, EnemySprite.FireballZora, 0x30, 0x0C, '', 0x09CE4E) + 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.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.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.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) + # Screen7A: + create_sprite(0x7a, EnemySprite.Hinox, 0x06, 0x07, '', 0x09CE92) + create_sprite(0x7a, EnemySprite.Ropa, 0x16, 0x09, '', 0x09CE95) + create_sprite(0x7a, EnemySprite.Ropa, 0x14, 0x0B, '', 0x09CE98) + create_sprite(0x7a, EnemySprite.Ropa, 0x16, 0x0B, '', 0x09CE9B) + # Screen7B: + create_sprite(0x7b, EnemySprite.BlueZirro, 0x12, 0x06, '', 0x09CE9F) + create_sprite(0x7b, EnemySprite.Pikit, 0x16, 0x0A, '', 0x09CEA2) + create_sprite(0x7b, EnemySprite.Moblin, 0x0D, 0x0F, '', 0x09CEA5) + create_sprite(0x7b, EnemySprite.Stal, 0x0A, 0x10, '', 0x09CEA8) + create_sprite(0x7b, EnemySprite.Moblin, 0x13, 0x14, '', 0x09CEAB) + create_sprite(0x7b, EnemySprite.Ropa, 0x16, 0x18, '', 0x09CEAE) + # Screen7C: + create_sprite(0x7c, EnemySprite.Stal, 0x03, 0x05, '', 0x09CEB2) + create_sprite(0x7c, EnemySprite.BlueGuard, 0x07, 0x06, '', 0x09CEB5) + create_sprite(0x7c, EnemySprite.RupeePull, 0x0F, 0x06, '', 0x09CEB8) + create_sprite(0x7c, EnemySprite.Hinox, 0x11, 0x11, '', 0x09CEBB) + create_sprite(0x7c, EnemySprite.Ropa, 0x18, 0x15, '', 0x09CEBE) + create_sprite(0x7c, EnemySprite.Ropa, 0x16, 0x19, '', 0x09CEC1) + # Screen7F: + 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.GreenZirro, 0x0D, 0x13, '', 0x09CED1) + create_sprite(0x7f, EnemySprite.Pikit, 0x16, 0x14, '', 0x09CED4) + create_sprite(0x7f, EnemySprite.Octorok, 0x0F, 0x17, '', 0x09CED7) + # Screen80: + create_sprite(0x80, EnemySprite.MasterSword, 0x07, 0x08, '', 0x09CEDB) + create_sprite(0x80, EnemySprite.PedestalPlaque, 0x07, 0x0A, '', 0x09CEDE) + create_sprite(0x80, EnemySprite.LostWoodsSquirrel, 0x0F, 0x14, '', 0x09CEE1) + create_sprite(0x80, EnemySprite.LostWoodsBird, 0x00, 0x16, '', 0x09CEE4) + create_sprite(0x80, EnemySprite.LostWoodsSquirrel, 0x02, 0x18, '', 0x09CEE7) + create_sprite(0x80, EnemySprite.LostWoodsBird, 0x0E, 0x1A, '', 0x09CEEA) + create_sprite(0x80, EnemySprite.LostWoodsSquirrel, 0x0F, 0x1B, '', 0x09CEED) + 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.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) + # Screen00_1: + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x07, 0x12, '', 0x09CF4C) + create_sprite(0x0, EnemySprite.Raven, 0x12, 0x0B, '', 0x09CF4F) + create_sprite(0x0, EnemySprite.Mushroom, 0x1E, 0x15, '', 0x09CF52) + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x28, 0x06, '', 0x09CF55) + create_sprite(0x0, EnemySprite.Buzzblob, 0x31, 0x0A, '', 0x09CF58) + create_sprite(0x0, EnemySprite.Raven, 0x2D, 0x0A, '', 0x09CF5B) + create_sprite(0x0, EnemySprite.Hoarder, 0x2A, 0x10, '', 0x09CF5E) + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x39, 0x15, '', 0x09CF61) + create_sprite(0x0, EnemySprite.Thief, 0x0E, 0x22, '', 0x09CF64) + create_sprite(0x0, EnemySprite.Hoarder, 0x1E, 0x2D, '', 0x09CF67) + create_sprite(0x0, EnemySprite.Bee, 0x29, 0x25, '', 0x09CF6A) + create_sprite(0x0, EnemySprite.RupeePull, 0x2A, 0x27, '', 0x09CF6D) + create_sprite(0x0, EnemySprite.Raven, 0x36, 0x2D, '', 0x09CF70) + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x25, 0x35, '', 0x09CF73) + create_sprite(0x0, EnemySprite.Thief, 0x29, 0x35, '', 0x09CF76) + # Screen02_1: + create_sprite(0x2, EnemySprite.Buzzblob, 0x04, 0x13, '', 0x09CF7A) + create_sprite(0x2, EnemySprite.Lumberjacks, 0x0C, 0x13, '', 0x09CF7D) + create_sprite(0x2, EnemySprite.Hoarder, 0x0D, 0x1A, '', 0x09CF80) + # Screen03_1: + create_sprite(0x3, EnemySprite.Boulders, 0x00, 0x00, '', 0x09CF84) + create_sprite(0x3, EnemySprite.MedallionTablet, 0x0B, 0x04, '', 0x09CF87) + create_sprite(0x3, EnemySprite.Deadrock, 0x27, 0x0C, '', 0x09CF8A) + create_sprite(0x3, EnemySprite.HeartPiece, 0x22, 0x16, '', 0x09CF8D) + create_sprite(0x3, EnemySprite.Deadrock, 0x0A, 0x35, '', 0x09CF90) + create_sprite(0x3, EnemySprite.Deadrock, 0x06, 0x36, '', 0x09CF93) + create_sprite(0x3, EnemySprite.Deadrock, 0x0D, 0x3B, '', 0x09CF96) + create_sprite(0x3, EnemySprite.PositionTarget, 0x12, 0x3B, '', 0x09CF99) + create_sprite(0x3, EnemySprite.Deadrock, 0x2C, 0x2D, '', 0x09CF9C) + create_sprite(0x3, EnemySprite.Deadrock, 0x34, 0x33, '', 0x09CF9F) + create_sprite(0x3, EnemySprite.Deadrock, 0x2F, 0x34, '', 0x09CFA2) + # Screen05_1: + create_sprite(0x5, EnemySprite.Deadrock, 0x1E, 0x0E, '', 0x09CFA6) + create_sprite(0x5, EnemySprite.Tektite, 0x1F, 0x0F, '', 0x09CFA9) + create_sprite(0x5, EnemySprite.HeartPiece, 0x2F, 0x03, '', 0x09CFAC) + create_sprite(0x5, EnemySprite.Deadrock, 0x35, 0x0D, '', 0x09CFAF) + create_sprite(0x5, EnemySprite.Tektite, 0x29, 0x0F, '', 0x09CFB2) + create_sprite(0x5, EnemySprite.Deadrock, 0x35, 0x0F, '', 0x09CFB5) + create_sprite(0x5, EnemySprite.Faerie, 0x34, 0x10, '', 0x09CFB8) + create_sprite(0x5, EnemySprite.Tektite, 0x1E, 0x31, '', 0x09CFBB) + create_sprite(0x5, EnemySprite.Tektite, 0x35, 0x2A, '', 0x09CFBE) + create_sprite(0x5, EnemySprite.Deadrock, 0x2A, 0x2F, '', 0x09CFC1) + create_sprite(0x5, EnemySprite.Tektite, 0x2F, 0x2F, '', 0x09CFC4) + create_sprite(0x5, EnemySprite.Deadrock, 0x29, 0x36, '', 0x09CFC7) + create_sprite(0x5, EnemySprite.Deadrock, 0x36, 0x36, '', 0x09CFCA) + # Screen07_1: + create_sprite(0x7, EnemySprite.Deadrock, 0x0E, 0x07, '', 0x09CFCE) + create_sprite(0x7, EnemySprite.Deadrock, 0x0A, 0x0D, '', 0x09CFD1) + create_sprite(0x7, EnemySprite.Deadrock, 0x17, 0x15, '', 0x09CFD4) + create_sprite(0x7, EnemySprite.Deadrock, 0x0F, 0x16, '', 0x09CFD7) + create_sprite(0x7, EnemySprite.Deadrock, 0x12, 0x16, '', 0x09CFDA) + # Screen0A_1: + create_sprite(0xa, EnemySprite.Bee, 0x0E, 0x04, '', 0x09CFDE) + create_sprite(0xa, EnemySprite.RupeePull, 0x0E, 0x06, '', 0x09CFE1) + create_sprite(0xa, EnemySprite.Raven, 0x05, 0x09, '', 0x09CFE4) + create_sprite(0xa, EnemySprite.Buzzblob, 0x10, 0x0D, '', 0x09CFE7) + create_sprite(0xa, EnemySprite.Buzzblob, 0x0B, 0x0E, '', 0x09CFEA) + create_sprite(0xa, EnemySprite.Raven, 0x13, 0x16, '', 0x09CFED) + create_sprite(0xa, EnemySprite.Hoarder, 0x0E, 0x16, '', 0x09CFF0) + create_sprite(0xa, EnemySprite.Buzzblob, 0x16, 0x16, '', 0x09CFF3) + create_sprite(0xa, EnemySprite.Raven, 0x11, 0x17, '', 0x09CFF6) + create_sprite(0xa, EnemySprite.Apple, 0x19, 0x1A, '', 0x09CFF9) + # 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.Crab, 0x11, 0x12, '', 0x09D006) + create_sprite(0xf, EnemySprite.Whirlpool, 0x08, 0x13, '', 0x09D009) + create_sprite(0xf, EnemySprite.Raven, 0x1C, 0x15, '', 0x09D00C) + create_sprite(0xf, EnemySprite.Octorok4Way, 0x0E, 0x17, '', 0x09D00F) + # Screen10_1: + create_sprite(0x10, EnemySprite.GreenGuard, 0x05, 0x0C, '', 0x09D013) + create_sprite(0x10, EnemySprite.Apple, 0x07, 0x0C, '', 0x09D016) + create_sprite(0x10, EnemySprite.LargeMagic, 0x17, 0x0F, '', 0x09D019) + create_sprite(0x10, EnemySprite.GreenGuard, 0x08, 0x18, '', 0x09D01C) + # Screen11_1: + create_sprite(0x11, EnemySprite.GreenGuard, 0x17, 0x0C, '', 0x09D020) + create_sprite(0x11, EnemySprite.GreenGuard, 0x1A, 0x0D, '', 0x09D023) + create_sprite(0x11, EnemySprite.BombRefill1, 0x08, 0x10, '', 0x09D026) + create_sprite(0x11, EnemySprite.Cucco, 0x08, 0x17, '', 0x09D029) + # Screen12_1: + create_sprite(0x12, EnemySprite.GreenGuard, 0x15, 0x0E, '', 0x09D02D) + create_sprite(0x12, EnemySprite.GreenGuard, 0x07, 0x10, '', 0x09D030) + create_sprite(0x12, EnemySprite.Whirlpool, 0x0F, 0x10, '', 0x09D033) + create_sprite(0x12, EnemySprite.GreenGuard, 0x15, 0x15, '', 0x09D036) + # Screen13_1: + create_sprite(0x13, EnemySprite.Apple, 0x18, 0x09, '', 0x09D03A) + create_sprite(0x13, EnemySprite.GreenGuard, 0x11, 0x17, '', 0x09D03D) + # Screen14_1: + create_sprite(0x14, EnemySprite.RedJavelinGuard, 0x15, 0x11, '', 0x09D041) + create_sprite(0x14, EnemySprite.GreenGuard, 0x11, 0x19, '', 0x09D044) + create_sprite(0x14, EnemySprite.Medusa, 0x08, 0x0C, '', 0x09D047) + create_sprite(0x14, EnemySprite.Medusa, 0x17, 0x11, '', 0x09D04A) + create_sprite(0x14, EnemySprite.Medusa, 0x12, 0x0E, '', 0x09D04D) + # Screen15_1: + create_sprite(0x15, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09D051) + create_sprite(0x15, EnemySprite.BlueGuard, 0x16, 0x0E, '', 0x09D054) + create_sprite(0x15, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D057) + create_sprite(0x15, EnemySprite.BlueGuard, 0x0B, 0x17, '', 0x09D05A) + # Screen16_1: + create_sprite(0x16, EnemySprite.Buzzblob, 0x0D, 0x0A, '', 0x09D05E) + create_sprite(0x16, EnemySprite.Witch, 0x0F, 0x15, '', 0x09D061) + create_sprite(0x16, EnemySprite.Buzzblob, 0x06, 0x18, '', 0x09D064) + # Screen17_1: + create_sprite(0x17, EnemySprite.Buzzblob, 0x18, 0x08, '', 0x09D068) + create_sprite(0x17, EnemySprite.Buzzblob, 0x17, 0x0A, '', 0x09D06B) + create_sprite(0x17, EnemySprite.Buzzblob, 0x0D, 0x0B, '', 0x09D06E) + create_sprite(0x17, EnemySprite.Buzzblob, 0x16, 0x0C, '', 0x09D071) + create_sprite(0x17, EnemySprite.Buzzblob, 0x08, 0x16, '', 0x09D074) + # Screen18_1: + create_sprite(0x18, EnemySprite.Faerie, 0x18, 0x0A, '', 0x09D078) + create_sprite(0x18, EnemySprite.PositionTarget, 0x0C, 0x17, '', 0x09D07B) + create_sprite(0x18, EnemySprite.BottleMerchant, 0x18, 0x16, '', 0x09D07E) + create_sprite(0x18, EnemySprite.OldSnitch, 0x0E, 0x1C, '', 0x09D081) + create_sprite(0x18, EnemySprite.FluteQuest, 0x20, 0x18, '', 0x09D084) + create_sprite(0x18, EnemySprite.PositionTarget, 0x34, 0x1B, '', 0x09D087) + create_sprite(0x18, EnemySprite.RunningNpc, 0x1D, 0x2E, '', 0x09D08A) + create_sprite(0x18, EnemySprite.SweepingLady, 0x19, 0x2C, '', 0x09D08D) + create_sprite(0x18, EnemySprite.KidInKak, 0x18, 0x31, '', 0x09D090) + create_sprite(0x18, EnemySprite.Cucco, 0x16, 0x35, '', 0x09D093) + create_sprite(0x18, EnemySprite.Cucco, 0x18, 0x36, '', 0x09D096) + create_sprite(0x18, EnemySprite.YoungSnitch, 0x33, 0x20, '', 0x09D099) + create_sprite(0x18, EnemySprite.BlueRupee, 0x36, 0x33, '', 0x09D09C) + # Screen1A_1: + create_sprite(0x1a, EnemySprite.BlueGuard, 0x14, 0x0C, '', 0x09D0A0) + 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) + # Screen1B_1: + create_sprite(0x1b, EnemySprite.LightningGate, 0x1F, 0x06, '', 0x09D0B3) + create_sprite(0x1b, EnemySprite.RedBushGuard, 0x09, 0x11, '', 0x09D0B6) + create_sprite(0x1b, EnemySprite.RedBushGuard, 0x0A, 0x13, '', 0x09D0B9) + create_sprite(0x1b, EnemySprite.Apple, 0x16, 0x14, '', 0x09D0BC) + create_sprite(0x1b, EnemySprite.BombGuard, 0x0E, 0x19, '', 0x09D0BF) + create_sprite(0x1b, EnemySprite.BlueGuard, 0x1F, 0x1A, '', 0x09D0C2) + create_sprite(0x1b, EnemySprite.RupeePull, 0x29, 0x17, '', 0x09D0C5) + create_sprite(0x1b, EnemySprite.BombGuard, 0x31, 0x19, '', 0x09D0C8) + create_sprite(0x1b, EnemySprite.BlueGuard, 0x20, 0x1A, '', 0x09D0CB) + create_sprite(0x1b, EnemySprite.BombGuard, 0x0E, 0x25, '', 0x09D0CE) + create_sprite(0x1b, EnemySprite.GreenGuard, 0x14, 0x2D, '', 0x09D0D1) + create_sprite(0x1b, EnemySprite.RedJavelinGuard, 0x26, 0x2D, '', 0x09D0D4) + create_sprite(0x1b, EnemySprite.RedJavelinGuard, 0x21, 0x32, '', 0x09D0D7) + # Screen1D_1: + create_sprite(0x1d, EnemySprite.Apple, 0x0B, 0x06, '', 0x09D0DB) + create_sprite(0x1d, EnemySprite.BlueArcher, 0x1B, 0x0C, '', 0x09D0DE) + create_sprite(0x1d, EnemySprite.BlueGuard, 0x07, 0x0D, '', 0x09D0E1) + create_sprite(0x1d, EnemySprite.BlueArcher, 0x1B, 0x0F, '', 0x09D0E4) + create_sprite(0x1d, EnemySprite.Crab, 0x07, 0x12, '', 0x09D0E7) + # Screen1E_1: + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x13, 0x08, '', 0x09D0EB) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x0E, 0x0E, '', 0x09D0EE) + create_sprite(0x1e, EnemySprite.Octorok, 0x11, 0x1A, '', 0x09D0F1) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x19, 0x1A, '', 0x09D0F4) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x33, 0x09, '', 0x09D0F7) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x37, 0x09, '', 0x09D0FA) + create_sprite(0x1e, EnemySprite.BlueGuard, 0x31, 0x10, '', 0x09D0FD) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x2F, 0x17, '', 0x09D100) + create_sprite(0x1e, EnemySprite.Octorok4Way, 0x35, 0x1D, '', 0x09D103) + create_sprite(0x1e, EnemySprite.Octorok4Way, 0x0F, 0x25, '', 0x09D106) + create_sprite(0x1e, EnemySprite.Octorok, 0x09, 0x28, '', 0x09D109) + create_sprite(0x1e, EnemySprite.Octorok, 0x15, 0x2C, '', 0x09D10C) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x14, 0x33, '', 0x09D10F) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x17, 0x33, '', 0x09D112) + create_sprite(0x1e, EnemySprite.Octorok, 0x09, 0x36, '', 0x09D115) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x24, 0x25, '', 0x09D118) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x28, 0x29, '', 0x09D11B) + 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.GreenGuard, 0x17, 0x12, '', 0x09D128) + create_sprite(0x22, EnemySprite.Cucco, 0x12, 0x14, '', 0x09D12B) + # Screen25_1: + create_sprite(0x25, EnemySprite.Octorok, 0x0F, 0x08, '', 0x09D12F) + create_sprite(0x25, EnemySprite.Octorok, 0x05, 0x0C, '', 0x09D132) + create_sprite(0x25, EnemySprite.Octorok, 0x14, 0x0C, '', 0x09D135) + create_sprite(0x25, EnemySprite.Octorok, 0x10, 0x0D, '', 0x09D138) + create_sprite(0x25, EnemySprite.Octorok, 0x0C, 0x11, '', 0x09D13B) + create_sprite(0x25, EnemySprite.Octorok, 0x18, 0x16, '', 0x09D13E) + create_sprite(0x25, EnemySprite.Octorok, 0x08, 0x17, '', 0x09D141) + create_sprite(0x25, EnemySprite.Octorok, 0x10, 0x17, '', 0x09D144) + # Screen28_1: + create_sprite(0x28, EnemySprite.HeartPiece, 0x07, 0x13, '', 0x09D148) + create_sprite(0x28, EnemySprite.RaceGameGuy, 0x08, 0x12, '', 0x09D14B) + create_sprite(0x28, EnemySprite.RaceGameLady, 0x19, 0x18, '', 0x09D14E) + # Screen2A_1: + create_sprite(0x2a, EnemySprite.FluteQuest, 0x09, 0x09, '', 0x09D152) + create_sprite(0x2a, EnemySprite.GroveOstritch, 0x0E, 0x0C, '', 0x09D155) + create_sprite(0x2a, EnemySprite.GroveBird, 0x0D, 0x0E, '', 0x09D158) + create_sprite(0x2a, EnemySprite.FluteKid, 0x0E, 0x0E, '', 0x09D15B) + create_sprite(0x2a, EnemySprite.GroveBird, 0x11, 0x0E, '', 0x09D15E) + create_sprite(0x2a, EnemySprite.GroveRabbit, 0x0C, 0x0F, '', 0x09D161) + create_sprite(0x2a, EnemySprite.GroveRabbit, 0x11, 0x10, '', 0x09D164) + # Screen2B_1: + create_sprite(0x2b, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D168) + create_sprite(0x2b, EnemySprite.GreenGuard, 0x14, 0x11, '', 0x09D16B) + create_sprite(0x2b, EnemySprite.GreenGuard, 0x14, 0x15, '', 0x09D16E) + create_sprite(0x2b, EnemySprite.GreenGuard, 0x10, 0x17, '', 0x09D171) + # Screen2C_1: + create_sprite(0x2c, EnemySprite.GreenGuard, 0x18, 0x14, '', 0x09D175) + create_sprite(0x2c, EnemySprite.BlueGuard, 0x09, 0x19, '', 0x09D178) + # Screen2D_1: + create_sprite(0x2d, EnemySprite.GreenGuard, 0x13, 0x0B, '', 0x09D17C) + create_sprite(0x2d, EnemySprite.BlueArcher, 0x10, 0x10, '', 0x09D17F) + create_sprite(0x2d, EnemySprite.BlueGuard, 0x12, 0x16, '', 0x09D182) + # 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.Octorok, 0x19, 0x17, '', 0x09D18F) + # Screen2F_1: + create_sprite(0x2f, EnemySprite.BlueGuard, 0x0F, 0x0C, '', 0x09D193) + create_sprite(0x2f, EnemySprite.ArmosStatue, 0x07, 0x17, '', 0x09D196) + create_sprite(0x2f, EnemySprite.ArmosStatue, 0x0C, 0x17, '', 0x09D199) + # Screen30_1: + create_sprite(0x30, EnemySprite.DesertStatue, 0x12, 0x14, '', 0x09D19D) + create_sprite(0x30, EnemySprite.PedestalPlaque, 0x12, 0x19, '', 0x09D1A0) + create_sprite(0x30, EnemySprite.DesertStatue, 0x0E, 0x1C, '', 0x09D1A3) + create_sprite(0x30, EnemySprite.DesertStatue, 0x16, 0x1C, '', 0x09D1A6) + create_sprite(0x30, EnemySprite.Geldman, 0x27, 0x19, '', 0x09D1A9) + create_sprite(0x30, EnemySprite.Vulture, 0x22, 0x1C, '', 0x09D1AC) + create_sprite(0x30, EnemySprite.Geldman, 0x2A, 0x1F, '', 0x09D1AF) + create_sprite(0x30, EnemySprite.Geldman, 0x1D, 0x26, '', 0x09D1B2) + create_sprite(0x30, EnemySprite.Vulture, 0x07, 0x29, '', 0x09D1B5) + create_sprite(0x30, EnemySprite.Geldman, 0x0F, 0x29, '', 0x09D1B8) + create_sprite(0x30, EnemySprite.HeartPiece, 0x06, 0x2A, '', 0x09D1BB) + create_sprite(0x30, EnemySprite.Geldman, 0x1B, 0x2C, '', 0x09D1BE) + create_sprite(0x30, EnemySprite.Geldman, 0x0A, 0x30, '', 0x09D1C1) + create_sprite(0x30, EnemySprite.Geldman, 0x14, 0x35, '', 0x09D1C4) + create_sprite(0x30, EnemySprite.MedallionTablet, 0x37, 0x2B, '', 0x09D1C7) + create_sprite(0x30, EnemySprite.Landmine, 0x36, 0x21, '', 0x09D1CA) + create_sprite(0x30, EnemySprite.Geldman, 0x22, 0x24, '', 0x09D1CD) + create_sprite(0x30, EnemySprite.Landmine, 0x29, 0x25, '', 0x09D1D0) + create_sprite(0x30, EnemySprite.Geldman, 0x20, 0x2C, '', 0x09D1D3) + create_sprite(0x30, EnemySprite.Geldman, 0x23, 0x32, '', 0x09D1D6) + create_sprite(0x30, EnemySprite.Landmine, 0x30, 0x32, '', 0x09D1D9) + create_sprite(0x30, EnemySprite.Vulture, 0x34, 0x33, '', 0x09D1DC) + create_sprite(0x30, EnemySprite.Landmine, 0x2D, 0x3B, '', 0x09D1DF) + # Screen32_1: + create_sprite(0x32, EnemySprite.SmallHeart, 0x1A, 0x09, '', 0x09D1E3) + create_sprite(0x32, EnemySprite.BlueGuard, 0x0B, 0x0B, '', 0x09D1E6) + create_sprite(0x32, EnemySprite.BlueGuard, 0x12, 0x0B, '', 0x09D1E9) + create_sprite(0x32, EnemySprite.Faerie, 0x19, 0x12, '', 0x09D1EC) + # Screen33_1: + create_sprite(0x33, EnemySprite.GreenBushGuard, 0x15, 0x0B, '', 0x09D1F0) + create_sprite(0x33, EnemySprite.BlueArcher, 0x09, 0x0E, '', 0x09D1F3) + create_sprite(0x33, EnemySprite.Whirlpool, 0x17, 0x12, '', 0x09D1F6) + create_sprite(0x33, EnemySprite.Octorok, 0x1A, 0x1B, '', 0x09D1F9) + # Screen34_1: + create_sprite(0x34, EnemySprite.BlueArcher, 0x0B, 0x0D, '', 0x09D1FD) + create_sprite(0x34, EnemySprite.Toppo, 0x15, 0x11, '', 0x09D200) + create_sprite(0x34, EnemySprite.GreenBushGuard, 0x11, 0x12, '', 0x09D203) + create_sprite(0x34, EnemySprite.Raven, 0x08, 0x13, '', 0x09D206) + create_sprite(0x34, EnemySprite.Faerie, 0x0E, 0x13, '', 0x09D209) + create_sprite(0x34, EnemySprite.GreenBushGuard, 0x15, 0x17, '', 0x09D20C) + create_sprite(0x34, EnemySprite.BlueArcher, 0x0C, 0x18, '', 0x09D20F) + # Screen35_1: + create_sprite(0x35, EnemySprite.Raven, 0x0E, 0x07, '', 0x09D213) + create_sprite(0x35, EnemySprite.Octorok, 0x0D, 0x09, '', 0x09D216) + create_sprite(0x35, EnemySprite.BlueArcher, 0x0A, 0x0C, '', 0x09D219) + 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.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.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.Whirlpool, 0x29, 0x2B, '', 0x09D246) + create_sprite(0x35, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D249) + create_sprite(0x35, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D24C) + 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, 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) + # Screen3A_1: + create_sprite(0x3a, EnemySprite.Locksmith, 0x17, 0x05, '', 0x09D26C) + create_sprite(0x3a, EnemySprite.Raven, 0x0E, 0x09, '', 0x09D26F) + create_sprite(0x3a, EnemySprite.Hoarder2, 0x0B, 0x0A, '', 0x09D272) + create_sprite(0x3a, EnemySprite.Hoarder2, 0x18, 0x0E, '', 0x09D275) + # 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.Raven, 0x08, 0x0B, '', 0x09D282) + create_sprite(0x3b, EnemySprite.HeartPiece, 0x14, 0x0E, '', 0x09D285) + create_sprite(0x3b, EnemySprite.FloppingFish, 0x1B, 0x10, '', 0x09D288) + create_sprite(0x3b, EnemySprite.Toppo, 0x0F, 0x14, '', 0x09D28B) + create_sprite(0x3b, EnemySprite.Raven, 0x14, 0x1B, '', 0x09D28E) + # Screen3C_1: + create_sprite(0x3c, EnemySprite.GreenBushGuard, 0x08, 0x0C, '', 0x09D292) + create_sprite(0x3c, EnemySprite.Octorok, 0x14, 0x0F, '', 0x09D295) + create_sprite(0x3c, EnemySprite.Raven, 0x0E, 0x0F, '', 0x09D298) + create_sprite(0x3c, EnemySprite.Hoarder, 0x09, 0x11, '', 0x09D29B) + create_sprite(0x3c, EnemySprite.Octorok4Way, 0x14, 0x15, '', 0x09D29E) + create_sprite(0x3c, EnemySprite.Crab, 0x16, 0x17, '', 0x09D2A1) + create_sprite(0x3c, EnemySprite.Octorok, 0x0B, 0x18, '', 0x09D2A4) + # 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.Whirlpool, 0x07, 0x0C, '', 0x09D2B1) + create_sprite(0x3f, EnemySprite.Octoballoon, 0x10, 0x16, '', 0x09D2B4) + # Screen00_2: + create_sprite(0x90, EnemySprite.BlueGuard, 0x0F, 0x11, '', 0x09D2B8) + create_sprite(0x90, EnemySprite.FakeMasterSword, 0x07, 0x12, '', 0x09D2BB) + create_sprite(0x90, EnemySprite.Mushroom, 0x1E, 0x15, '', 0x09D2BE) + create_sprite(0x90, EnemySprite.Thief, 0x0D, 0x1F, '', 0x09D2C1) + create_sprite(0x90, EnemySprite.FakeMasterSword, 0x28, 0x06, '', 0x09D2C4) + create_sprite(0x90, EnemySprite.RupeePull, 0x2B, 0x08, '', 0x09D2C7) + create_sprite(0x90, EnemySprite.BlueGuard, 0x33, 0x08, '', 0x09D2CA) + create_sprite(0x90, EnemySprite.Thief, 0x2B, 0x0A, '', 0x09D2CD) + create_sprite(0x90, EnemySprite.Buzzblob, 0x31, 0x0A, '', 0x09D2D0) + create_sprite(0x90, EnemySprite.Hoarder, 0x2A, 0x10, '', 0x09D2D3) + create_sprite(0x90, EnemySprite.Hoarder, 0x0D, 0x2C, '', 0x09D2D6) + create_sprite(0x90, EnemySprite.BlueGuard, 0x09, 0x33, '', 0x09D2D9) + create_sprite(0x90, EnemySprite.Bee, 0x29, 0x25, '', 0x09D2DC) + create_sprite(0x90, EnemySprite.Hoarder, 0x28, 0x2F, '', 0x09D2DF) + # Screen02_2: + create_sprite(0x92, EnemySprite.BonkItem, 0x0D, 0x12, '', 0x09D2E3) + # Screen03_2: + create_sprite(0x93, EnemySprite.Boulders, 0x00, 0x00, '', 0x09D2E7) + create_sprite(0x93, EnemySprite.MedallionTablet, 0x0B, 0x04, '', 0x09D2EA) + create_sprite(0x93, EnemySprite.Tektite, 0x10, 0x1A, '', 0x09D2ED) + create_sprite(0x93, EnemySprite.Tektite, 0x1A, 0x1E, '', 0x09D2F0) + create_sprite(0x93, EnemySprite.Deadrock, 0x27, 0x0C, '', 0x09D2F3) + create_sprite(0x93, EnemySprite.Tektite, 0x2C, 0x15, '', 0x09D2F6) + create_sprite(0x93, EnemySprite.HeartPiece, 0x22, 0x16, '', 0x09D2F9) + create_sprite(0x93, EnemySprite.Tektite, 0x28, 0x19, '', 0x09D2FC) + create_sprite(0x93, EnemySprite.Deadrock, 0x0A, 0x35, '', 0x09D2FF) + create_sprite(0x93, EnemySprite.Deadrock, 0x06, 0x36, '', 0x09D302) + create_sprite(0x93, EnemySprite.Deadrock, 0x0D, 0x3B, '', 0x09D305) + create_sprite(0x93, EnemySprite.PositionTarget, 0x12, 0x3B, '', 0x09D308) + create_sprite(0x93, EnemySprite.Deadrock, 0x2C, 0x2D, '', 0x09D30B) + create_sprite(0x93, EnemySprite.Deadrock, 0x34, 0x33, '', 0x09D30E) + create_sprite(0x93, EnemySprite.Deadrock, 0x2F, 0x34, '', 0x09D311) + # Screen05_2: + create_sprite(0x95, EnemySprite.Deadrock, 0x07, 0x0B, '', 0x09D315) + create_sprite(0x95, EnemySprite.Tektite, 0x08, 0x0D, '', 0x09D318) + create_sprite(0x95, EnemySprite.Deadrock, 0x1E, 0x0E, '', 0x09D31B) + create_sprite(0x95, EnemySprite.Tektite, 0x1F, 0x0F, '', 0x09D31E) + create_sprite(0x95, EnemySprite.HeartPiece, 0x2F, 0x03, '', 0x09D321) + create_sprite(0x95, EnemySprite.Deadrock, 0x35, 0x0D, '', 0x09D324) + create_sprite(0x95, EnemySprite.Tektite, 0x29, 0x0F, '', 0x09D327) + create_sprite(0x95, EnemySprite.Deadrock, 0x35, 0x0F, '', 0x09D32A) + create_sprite(0x95, EnemySprite.Faerie, 0x34, 0x10, '', 0x09D32D) + create_sprite(0x95, EnemySprite.Tektite, 0x1E, 0x31, '', 0x09D330) + create_sprite(0x95, EnemySprite.Tektite, 0x35, 0x2A, '', 0x09D333) + create_sprite(0x95, EnemySprite.Deadrock, 0x2A, 0x2F, '', 0x09D336) + create_sprite(0x95, EnemySprite.Tektite, 0x2F, 0x2F, '', 0x09D339) + create_sprite(0x95, EnemySprite.Deadrock, 0x29, 0x36, '', 0x09D33C) + create_sprite(0x95, EnemySprite.Deadrock, 0x36, 0x36, '', 0x09D33F) + # Screen07_2: + create_sprite(0x97, EnemySprite.Deadrock, 0x0E, 0x07, '', 0x09D343) + create_sprite(0x97, EnemySprite.Deadrock, 0x0A, 0x0D, '', 0x09D346) + create_sprite(0x97, EnemySprite.Deadrock, 0x17, 0x15, '', 0x09D349) + create_sprite(0x97, EnemySprite.Deadrock, 0x0F, 0x16, '', 0x09D34C) + create_sprite(0x97, EnemySprite.Deadrock, 0x12, 0x16, '', 0x09D34F) + # Screen0A_2: + create_sprite(0x9a, EnemySprite.Bee, 0x0E, 0x04, '', 0x09D353) + create_sprite(0x9a, EnemySprite.BlueGuard, 0x10, 0x0D, '', 0x09D356) + create_sprite(0x9a, EnemySprite.Raven, 0x11, 0x16, '', 0x09D359) + create_sprite(0x9a, EnemySprite.Raven, 0x13, 0x16, '', 0x09D35C) + create_sprite(0x9a, EnemySprite.Hoarder, 0x0E, 0x16, '', 0x09D35F) + create_sprite(0x9a, EnemySprite.Raven, 0x11, 0x17, '', 0x09D362) + create_sprite(0x9a, EnemySprite.Apple, 0x19, 0x1A, '', 0x09D365) + # 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.Crab, 0x11, 0x12, '', 0x09D375) + create_sprite(0x9f, EnemySprite.Whirlpool, 0x08, 0x13, '', 0x09D378) + create_sprite(0x9f, EnemySprite.Octorok4Way, 0x0E, 0x17, '', 0x09D37B) + # Screen10_2: + create_sprite(0xa0, EnemySprite.BlueGuard, 0x05, 0x0C, '', 0x09D37F) + create_sprite(0xa0, EnemySprite.Apple, 0x07, 0x0C, '', 0x09D382) + create_sprite(0xa0, EnemySprite.LargeMagic, 0x17, 0x0F, '', 0x09D385) + create_sprite(0xa0, EnemySprite.BlueGuard, 0x07, 0x12, '', 0x09D388) + create_sprite(0xa0, EnemySprite.BlueGuard, 0x08, 0x18, '', 0x09D38B) + # Screen11_2: + create_sprite(0xa1, EnemySprite.BlueGuard, 0x17, 0x0C, '', 0x09D38F) + create_sprite(0xa1, EnemySprite.BlueGuard, 0x1A, 0x0D, '', 0x09D392) + create_sprite(0xa1, EnemySprite.BombRefill1, 0x08, 0x10, '', 0x09D395) + create_sprite(0xa1, EnemySprite.Cucco, 0x08, 0x17, '', 0x09D398) + # Screen12_2: + create_sprite(0xa2, EnemySprite.Faerie, 0x14, 0x0A, '', 0x09D39C) + create_sprite(0xa2, EnemySprite.BlueGuard, 0x15, 0x0E, '', 0x09D39F) + create_sprite(0xa2, EnemySprite.Whirlpool, 0x0F, 0x10, '', 0x09D3A2) + create_sprite(0xa2, EnemySprite.GreenGuard, 0x15, 0x15, '', 0x09D3A5) + # Screen13_2: + create_sprite(0xa3, EnemySprite.Bee, 0x18, 0x09, '', 0x09D3A9) + create_sprite(0xa3, EnemySprite.BombRefill8, 0x07, 0x0C, '', 0x09D3AC) + create_sprite(0xa3, EnemySprite.BlueGuard, 0x0D, 0x17, '', 0x09D3AF) + create_sprite(0xa3, EnemySprite.BlueGuard, 0x12, 0x1A, '', 0x09D3B2) + # Screen14_2: + create_sprite(0xa4, EnemySprite.Poe, 0x0D, 0x0D, '', 0x09D3B6) + create_sprite(0xa4, EnemySprite.Poe, 0x19, 0x0F, '', 0x09D3B9) + create_sprite(0xa4, EnemySprite.Poe, 0x08, 0x10, '', 0x09D3BC) + create_sprite(0xa4, EnemySprite.Poe, 0x14, 0x11, '', 0x09D3BF) + create_sprite(0xa4, EnemySprite.Poe, 0x13, 0x14, '', 0x09D3C2) + create_sprite(0xa4, EnemySprite.BlueGuard, 0x11, 0x19, '', 0x09D3C5) + # Screen15_2: + create_sprite(0xa5, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09D3C9) + create_sprite(0xa5, EnemySprite.UsainBolt, 0x16, 0x0E, '', 0x09D3CC) + create_sprite(0xa5, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D3CF) + create_sprite(0xa5, EnemySprite.RedSpearGuard, 0x0B, 0x17, '', 0x09D3D2) + create_sprite(0xa5, EnemySprite.Apple, 0x04, 0x1A, '', 0x09D3D5) + # Screen16_2: + create_sprite(0xa6, EnemySprite.Buzzblob, 0x0D, 0x0A, '', 0x09D3D9) + create_sprite(0xa6, EnemySprite.Witch, 0x0F, 0x15, '', 0x09D3DC) + create_sprite(0xa6, EnemySprite.Buzzblob, 0x06, 0x18, '', 0x09D3DF) + # Screen17_2: + create_sprite(0xa7, EnemySprite.Buzzblob, 0x18, 0x08, '', 0x09D3E3) + create_sprite(0xa7, EnemySprite.Buzzblob, 0x17, 0x0A, '', 0x09D3E6) + create_sprite(0xa7, EnemySprite.UsainBolt, 0x0D, 0x0B, '', 0x09D3E9) + create_sprite(0xa7, EnemySprite.Buzzblob, 0x16, 0x0C, '', 0x09D3EC) + create_sprite(0xa7, EnemySprite.Buzzblob, 0x08, 0x16, '', 0x09D3EF) + # Screen18_2: + create_sprite(0xa8, EnemySprite.BlueGuard, 0x12, 0x08, '', 0x09D3F3) + create_sprite(0xa8, EnemySprite.RedRupee, 0x18, 0x0A, '', 0x09D3F6) + create_sprite(0xa8, EnemySprite.BottleMerchant, 0x18, 0x16, '', 0x09D3F9) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x07, 0x1C, '', 0x09D3FC) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x35, 0x0B, '', 0x09D3FF) + create_sprite(0xa8, EnemySprite.FluteQuest, 0x20, 0x18, '', 0x09D402) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x12, 0x2E, '', 0x09D405) + create_sprite(0xa8, EnemySprite.Cucco, 0x14, 0x34, '', 0x09D408) + create_sprite(0xa8, EnemySprite.Cucco, 0x16, 0x35, '', 0x09D40B) + create_sprite(0xa8, EnemySprite.RedSpearGuard, 0x39, 0x22, '', 0x09D40E) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x20, 0x2E, '', 0x09D411) + create_sprite(0xa8, EnemySprite.Bee, 0x36, 0x33, '', 0x09D414) + # Screen1A_2: + 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) + # Screen1B_2: + create_sprite(0xab, EnemySprite.UsainBolt, 0x06, 0x0D, '', 0x09D428) + create_sprite(0xab, EnemySprite.Apple, 0x16, 0x14, '', 0x09D42B) + create_sprite(0xab, EnemySprite.UsainBolt, 0x1F, 0x1A, '', 0x09D42E) + create_sprite(0xab, EnemySprite.BlueGuard, 0x37, 0x13, '', 0x09D431) + create_sprite(0xab, EnemySprite.Whirlpool, 0x1E, 0x25, '', 0x09D434) + create_sprite(0xab, EnemySprite.RedSpearGuard, 0x08, 0x28, '', 0x09D437) + create_sprite(0xab, EnemySprite.GreenGuard, 0x1F, 0x2B, '', 0x09D43A) + create_sprite(0xab, EnemySprite.BlueGuard, 0x38, 0x29, '', 0x09D43D) + create_sprite(0xab, EnemySprite.BlueGuard, 0x21, 0x2D, '', 0x09D440) + create_sprite(0xab, EnemySprite.UsainBolt, 0x21, 0x32, '', 0x09D443) + # Screen1D_2: + create_sprite(0xad, EnemySprite.Bee, 0x0B, 0x06, '', 0x09D447) + create_sprite(0xad, EnemySprite.BlueArcher, 0x1B, 0x0C, '', 0x09D44A) + create_sprite(0xad, EnemySprite.UsainBolt, 0x07, 0x0D, '', 0x09D44D) + create_sprite(0xad, EnemySprite.BlueArcher, 0x1B, 0x0F, '', 0x09D450) + # Screen1E_2: + create_sprite(0xae, EnemySprite.ArmosStatue, 0x0E, 0x0E, '', 0x09D454) + create_sprite(0xae, EnemySprite.UsainBolt, 0x11, 0x1A, '', 0x09D457) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x19, 0x1A, '', 0x09D45A) + create_sprite(0xae, EnemySprite.RupeePull, 0x33, 0x04, '', 0x09D45D) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x33, 0x09, '', 0x09D460) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x37, 0x09, '', 0x09D463) + create_sprite(0xae, EnemySprite.UsainBolt, 0x31, 0x10, '', 0x09D466) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x2F, 0x17, '', 0x09D469) + create_sprite(0xae, EnemySprite.BlueGuard, 0x0F, 0x25, '', 0x09D46C) + create_sprite(0xae, EnemySprite.UsainBolt, 0x09, 0x28, '', 0x09D46F) + create_sprite(0xae, EnemySprite.RedSpearGuard, 0x15, 0x2C, '', 0x09D472) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x14, 0x33, '', 0x09D475) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x17, 0x33, '', 0x09D478) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x24, 0x25, '', 0x09D47B) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x31, 0x28, '', 0x09D47E) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x28, 0x29, '', 0x09D481) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x3A, 0x29, '', 0x09D484) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x3D, 0x29, '', 0x09D487) + 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.Cucco, 0x0C, 0x14, '', 0x09D494) + create_sprite(0xb2, EnemySprite.Cucco, 0x12, 0x14, '', 0x09D497) + # Screen25_2: + create_sprite(0xb5, EnemySprite.BlueGuard, 0x0E, 0x08, '', 0x09D49B) + create_sprite(0xb5, EnemySprite.BlueGuard, 0x05, 0x0C, '', 0x09D49E) + create_sprite(0xb5, EnemySprite.BlueGuard, 0x09, 0x11, '', 0x09D4A1) + create_sprite(0xb5, EnemySprite.UsainBolt, 0x19, 0x16, '', 0x09D4A4) + # Screen28_2: + create_sprite(0xb8, EnemySprite.BlueGuard, 0x12, 0x0C, '', 0x09D4A8) + create_sprite(0xb8, EnemySprite.HeartPiece, 0x07, 0x13, '', 0x09D4AB) + create_sprite(0xb8, EnemySprite.RaceGameGuy, 0x08, 0x12, '', 0x09D4AE) + create_sprite(0xb8, EnemySprite.RaceGameLady, 0x19, 0x18, '', 0x09D4B1) + create_sprite(0xb8, EnemySprite.BlueGuard, 0x0C, 0x19, '', 0x09D4B4) + # Screen29_2: + create_sprite(0xb9, EnemySprite.BlueGuard, 0x0E, 0x05, '', 0x09D4B8) + create_sprite(0xb9, EnemySprite.UsainBolt, 0x0C, 0x0C, '', 0x09D4BB) + create_sprite(0xb9, EnemySprite.BlueGuard, 0x0B, 0x14, '', 0x09D4BE) + # Screen2A_2: + create_sprite(0xba, EnemySprite.FluteQuest, 0x09, 0x09, '', 0x09D4C2) + create_sprite(0xba, EnemySprite.GroveOstritch, 0x0E, 0x0C, '', 0x09D4C5) + create_sprite(0xba, EnemySprite.GroveBird, 0x0D, 0x0E, '', 0x09D4C8) + create_sprite(0xba, EnemySprite.FluteKid, 0x0E, 0x0E, '', 0x09D4CB) + create_sprite(0xba, EnemySprite.GroveBird, 0x11, 0x0E, '', 0x09D4CE) + create_sprite(0xba, EnemySprite.GroveRabbit, 0x0C, 0x0F, '', 0x09D4D1) + create_sprite(0xba, EnemySprite.GroveRabbit, 0x11, 0x10, '', 0x09D4D4) + create_sprite(0xba, EnemySprite.RedRupee, 0x15, 0x14, '', 0x09D4D7) + create_sprite(0xba, EnemySprite.RedRupee, 0x0F, 0x18, '', 0x09D4DA) + # Screen2B_2: + create_sprite(0xbb, EnemySprite.BlueGuard, 0x08, 0x06, '', 0x09D4DE) + create_sprite(0xbb, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D4E1) + create_sprite(0xbb, EnemySprite.BlueGuard, 0x14, 0x11, '', 0x09D4E4) + create_sprite(0xbb, EnemySprite.BlueGuard, 0x14, 0x15, '', 0x09D4E7) + create_sprite(0xbb, EnemySprite.BlueGuard, 0x10, 0x17, '', 0x09D4EA) + # Screen2C_2: + create_sprite(0xbc, EnemySprite.BlueGuard, 0x18, 0x14, '', 0x09D4EE) + create_sprite(0xbc, EnemySprite.BlueGuard, 0x09, 0x19, '', 0x09D4F1) + # Screen2D_2: + 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) + # 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.Octorok, 0x19, 0x17, '', 0x09D511) + # Screen2F_2: + create_sprite(0xbf, EnemySprite.UsainBolt, 0x0F, 0x0C, '', 0x09D515) + create_sprite(0xbf, EnemySprite.ArmosStatue, 0x07, 0x17, '', 0x09D518) + create_sprite(0xbf, EnemySprite.ArmosStatue, 0x0C, 0x17, '', 0x09D51B) + # Screen30_2: + create_sprite(0xc0, EnemySprite.DesertStatue, 0x12, 0x14, '', 0x09D51F) + create_sprite(0xc0, EnemySprite.PedestalPlaque, 0x12, 0x19, '', 0x09D522) + create_sprite(0xc0, EnemySprite.DesertStatue, 0x0E, 0x1C, '', 0x09D525) + create_sprite(0xc0, EnemySprite.DesertStatue, 0x16, 0x1C, '', 0x09D528) + create_sprite(0xc0, EnemySprite.Geldman, 0x27, 0x19, '', 0x09D52B) + create_sprite(0xc0, EnemySprite.Vulture, 0x22, 0x1C, '', 0x09D52E) + create_sprite(0xc0, EnemySprite.Geldman, 0x2A, 0x1F, '', 0x09D531) + create_sprite(0xc0, EnemySprite.Landmine, 0x0C, 0x23, '', 0x09D534) + create_sprite(0xc0, EnemySprite.Geldman, 0x1D, 0x26, '', 0x09D537) + create_sprite(0xc0, EnemySprite.Vulture, 0x07, 0x29, '', 0x09D53A) + create_sprite(0xc0, EnemySprite.Geldman, 0x0F, 0x29, '', 0x09D53D) + create_sprite(0xc0, EnemySprite.HeartPiece, 0x06, 0x2A, '', 0x09D540) + create_sprite(0xc0, EnemySprite.Geldman, 0x1B, 0x2C, '', 0x09D543) + create_sprite(0xc0, EnemySprite.Geldman, 0x0A, 0x30, '', 0x09D546) + create_sprite(0xc0, EnemySprite.Geldman, 0x14, 0x35, '', 0x09D549) + create_sprite(0xc0, EnemySprite.MedallionTablet, 0x37, 0x2B, '', 0x09D54C) + create_sprite(0xc0, EnemySprite.Geldman, 0x22, 0x24, '', 0x09D54F) + create_sprite(0xc0, EnemySprite.Geldman, 0x28, 0x2A, '', 0x09D552) + create_sprite(0xc0, EnemySprite.Geldman, 0x23, 0x32, '', 0x09D555) + create_sprite(0xc0, EnemySprite.Vulture, 0x34, 0x33, '', 0x09D558) + # Screen32_2: + create_sprite(0xc2, EnemySprite.SmallHeart, 0x1A, 0x09, '', 0x09D55C) + create_sprite(0xc2, EnemySprite.GreenGuard, 0x0B, 0x0C, '', 0x09D55F) + create_sprite(0xc2, EnemySprite.BlueGuard, 0x12, 0x0C, '', 0x09D562) + create_sprite(0xc2, EnemySprite.GreenGuard, 0x13, 0x10, '', 0x09D565) + create_sprite(0xc2, EnemySprite.BombRefill4, 0x19, 0x12, '', 0x09D568) + create_sprite(0xc2, EnemySprite.Landmine, 0x08, 0x15, '', 0x09D56B) + # Screen33_2: + create_sprite(0xc3, EnemySprite.Octorok, 0x13, 0x06, '', 0x09D56F) + create_sprite(0xc3, EnemySprite.Octorok4Way, 0x14, 0x0B, '', 0x09D572) + create_sprite(0xc3, EnemySprite.Whirlpool, 0x17, 0x12, '', 0x09D575) + create_sprite(0xc3, EnemySprite.Octorok, 0x12, 0x16, '', 0x09D578) + create_sprite(0xc3, EnemySprite.Octorok, 0x1A, 0x1B, '', 0x09D57B) + # Screen34_2: + create_sprite(0xc4, EnemySprite.RupeePull, 0x17, 0x0E, '', 0x09D57F) + create_sprite(0xc4, EnemySprite.Raven, 0x08, 0x13, '', 0x09D582) + create_sprite(0xc4, EnemySprite.BlueGuard, 0x11, 0x12, '', 0x09D585) + create_sprite(0xc4, EnemySprite.Octorok, 0x06, 0x13, '', 0x09D588) + create_sprite(0xc4, EnemySprite.Octorok, 0x0C, 0x18, '', 0x09D58B) + # Screen35_2: + create_sprite(0xc5, EnemySprite.Raven, 0x0E, 0x07, '', 0x09D58F) + create_sprite(0xc5, EnemySprite.Octorok, 0x0D, 0x09, '', 0x09D592) + 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.Octorok4Way, 0x38, 0x0A, '', 0x09D5A1) + create_sprite(0xc5, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D5A4) + create_sprite(0xc5, EnemySprite.FireballZora, 0x37, 0x19, '', 0x09D5A7) + 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.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.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.Buzzblob, 0x32, 0x37, '', 0x09D5CE) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x34, 0x39, '', 0x09D5D1) + # Screen37_2: + create_sprite(0xc7, EnemySprite.Crab, 0x08, 0x08, '', 0x09D5D5) + 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) + # Screen3A_2: + create_sprite(0xca, EnemySprite.Locksmith, 0x17, 0x05, '', 0x09D5E5) + create_sprite(0xca, EnemySprite.Hoarder2, 0x0B, 0x0A, '', 0x09D5E8) + create_sprite(0xca, EnemySprite.Raven, 0x14, 0x0D, '', 0x09D5EB) + create_sprite(0xca, EnemySprite.Raven, 0x13, 0x0E, '', 0x09D5EE) + create_sprite(0xca, EnemySprite.Raven, 0x14, 0x0F, '', 0x09D5F1) + create_sprite(0xca, EnemySprite.Raven, 0x13, 0x10, '', 0x09D5F4) + 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.Octorok, 0x0C, 0x0F, '', 0x09D601) + create_sprite(0xcb, EnemySprite.HeartPiece, 0x14, 0x0E, '', 0x09D604) + create_sprite(0xcb, EnemySprite.Octorok4Way, 0x0F, 0x14, '', 0x09D607) + create_sprite(0xcb, EnemySprite.BlueGuard, 0x17, 0x18, '', 0x09D60A) + create_sprite(0xcb, EnemySprite.Raven, 0x14, 0x1B, '', 0x09D60D) + # Screen3C_2: + create_sprite(0xcc, EnemySprite.Raven, 0x0B, 0x09, '', 0x09D611) + create_sprite(0xcc, EnemySprite.BlueGuard, 0x08, 0x0A, '', 0x09D614) + create_sprite(0xcc, EnemySprite.Octorok, 0x14, 0x0F, '', 0x09D617) + create_sprite(0xcc, EnemySprite.UsainBolt, 0x09, 0x11, '', 0x09D61A) + create_sprite(0xcc, EnemySprite.Octorok4Way, 0x14, 0x15, '', 0x09D61D) + # 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.Octoballoon, 0x11, 0x16, '', 0x09D62A) diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index 9b6a877a..fdcb575c 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -554,7 +554,7 @@ def setup_required_dungeon_groups(sheets): # 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]), # collasping bridges + ([None, None, None, 82], [0x1a, 0x3d, 0x44, 0x56, 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 @@ -593,7 +593,7 @@ def setup_required_dungeon_groups(sheets): # roomCollection.LoadRooms() # roomCollection.RandomizeRoomSpriteGroups(spriteGroupCollection, optionFlags); # more stuff -uw_sub_group_choices = { +sub_group_choices = { 0: [22, 31, 47, 14], # 70, 72 for guards 1: [44, 30, 32], # 73, 13 2: [12, 18, 23, 24, 28, 46, 34, 35, 39, 40, 38, 41, 36, 37, 42], @@ -611,7 +611,41 @@ def randomize_underworld_sprite_sheets(sheets): sheet.sub_groups[1] = random.choice([13, 73]) for idx in range(0, 4): if not sheet.locked[idx]: - sheet.sub_groups[idx] = random.choice(uw_sub_group_choices[idx]) + sheet.sub_groups[idx] = random.choice(sub_group_choices[idx]) + # lock the group? + + +def setup_required_overworld_groups(sheets): + sheets[7].add_sprite_to_sheet([None, None, 74, None], {0x2}) # lumberjacks + sheets[16].add_sprite_to_sheet([None, None, 18, 16], {0x3, 0x93}) # WDM (pre/post-Aga) + sheets[7].add_sprite_to_sheet([None, None, None, 17], {0xA, 0x9A}) # DM Foothills? (pre/post-Aga) + sheets[4].add_sprite_to_sheet([None, None, None, None], {0xF, 0x9F}) # Waterfall of wishing (pre/post-Aga) + sheets[3].add_sprite_to_sheet([None, None, None, 14], {0x14, 0xA4}) # Graveyard (pre/post-Aga) + 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[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) + sheets[13].add_sprite_to_sheet([None, None, 76, None], {0x16, 0xA6}) # Witch hut (pre/post-Aga) + sheets[29].add_sprite_to_sheet([None, 77, None, 21], {0x69, 0xF9}) # VoO South (pre/post-Aga) + sheets[15].add_sprite_to_sheet([None, None, 78, None], {0x2A, 0xBA}) # Haunted Grove (pre/post-Aga) + sheets[17].add_sprite_to_sheet([None, None, None, 76], {0x6A, 0xFA}) # Stumpy (pre/post-Aga) + sheets[12].add_sprite_to_sheet([None, None, 55, 54], {0x80, 0x110}) # Specials (pre/post-Aga) + sheets[14].add_sprite_to_sheet([None, None, 12, 68], {0x81, 0x111}) # Zora's Domain (pre/post-Aga) + 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 + + +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] + for idx in range(0, 4): + if not sheet.locked[idx]: + sheet.sub_groups[idx] = random.choice(sub_group_choices[idx]) # lock the group? @@ -627,4 +661,3 @@ def randomize_underworld_sprite_sheets(sheets): - diff --git a/source/enemizer/TilePattern.py b/source/enemizer/TilePattern.py new file mode 100644 index 00000000..c5584722 --- /dev/null +++ b/source/enemizer/TilePattern.py @@ -0,0 +1,93 @@ +import os +import json +import codecs + +if __name__ == '__main__': + directory = './EnemizerCLI.Core/tiles' + for filename in os.listdir(directory): + with codecs.open(directory+'/'+filename, 'r', 'utf-8-sig') as fin: + pattern = json.load(fin) + pairs = [f'({x["x"]}, {x["y"]})' for x in pattern["Items"]] + print(f'(\'{filename}\', [{", ".join(pairs)}]),') + +tile_patterns = [ + ('heart soft', [(3, 1), (5, 2), (4, 7), (2, 5), (7, 1), (7, 5), (8, 4), (1, 2), (2, 1), (1, 4), (5, 7), (6, 1), + (6, 6), (4, 2), (8, 3), (1, 3), (3, 6), (8, 2)]), + ('metroid', [(2, 7), (7, 7), (1, 3), (3, 1), (8, 3), (1, 6), (4, 5), (5, 3), (1, 4), (6, 1), (6, 4), (8, 6), (3, 4), + (7, 5), (4, 1), (5, 5), (2, 2), (2, 5), (7, 2), (5, 1), (4, 3), (8, 4)]), + ('moldorm vertical', [(5, 1), (6, 0), (7, 2), (5, 4), (4, 4), (4, 1), (3, 5), (5, 6), (3, 2), (6, 6), (7, 5), + (6, 1), (4, 8), (3, 3), (5, 7), (3, 8), (2, 7), (6, 4), (4, 0), (3, 6), (7, 3), (4, 6)]), + ('scream emoji', [(2, 2), (7, 2), (2, 3), (7, 3), (1, 7), (8, 7), (3, 2), (6, 2), (2, 6), (7, 6), (3, 3), (6, 3), + (2, 7), (7, 7), (4, 5), (5, 7), (5, 5), (4, 7), (4, 6), (5, 6), (2, 5), (7, 5)]), + ('mario mushroom', [(3, 7), (4, 7), (5, 7), (3, 4), (4, 4), (5, 4), (3, 1), (4, 1), (5, 1), (2, 2), (6, 6), (6, 2), + (2, 6), (1, 3), (7, 5), (7, 3), (1, 5), (1, 4), (6, 5), (7, 4), (2, 5)]), + ('moldorm', [(1, 3), (2, 5), (3, 6), (5, 5), (7, 5), (7, 2), (5, 3), (3, 2), (2, 4), (1, 5), (5, 4), (6, 2), (7, 4), + (8, 1), (8, 4), (4, 2), (9, 2), (2, 3), (4, 6), (9, 3), (7, 3), (6, 6)]), + ('thinking emoji', [(5, 6), (6, 4), (4, 3), (3, 1), (2, 6), (5, 3), (6, 6), (6, 1), (3, 0), (4, 7), (2, 4), (3, 8), + (6, 0), (3, 7), (3, 3)]), + ('triangle', [(1, 5), (7, 5), (4, 2), (3, 5), (5, 5), (4, 3), (2, 4), (6, 4), (5, 3), (2, 5), (6, 5), (3, 3), + (4, 5), (5, 4), (3, 4), (4, 4)]), + ('heart', [(8, 3), (2, 3), (5, 7), (2, 4), (8, 4), (4, 6), (6, 6), (8, 2), (2, 2), (5, 2), (7, 1), (3, 1), (3, 5), + (6, 1), (7, 5), (4, 1)]), + ('arrghus', [(4, 1), (6, 2), (2, 3), (4, 3), (3, 4), (4, 5), (5, 6), (3, 7), (1, 4), (2, 5), (5, 1), (6, 3), (2, 2), + (7, 4), (5, 4), (4, 4), (6, 5), (3, 6), (5, 7), (3, 1), (3, 5), (5, 5)]), + ('cowboy smile', [(1, 2), (3, 3), (4, 1), (3, 5), (5, 8), (6, 7), (5, 2), (7, 2), (1, 3), (2, 7), (5, 5), (5, 3), + (3, 2), (4, 3), (7, 3), (2, 3), (4, 2), (3, 8), (4, 8), (6, 3)]), + ('clown face happy', [(2, 2), (7, 6), (7, 2), (2, 6), (3, 3), (6, 7), (6, 3), (3, 7), (2, 3), (5, 6), (7, 3), + (4, 6), (2, 5), (6, 6), (7, 5), (3, 6), (4, 5), (5, 7), (5, 5), (4, 7), (3, 2), (6, 2)]), + ('generic happy face', [(2, 1), (6, 3), (6, 5), (4, 6), (2, 2), (6, 6), (2, 3), (3, 5), (3, 2), (3, 3), (6, 2), + (5, 5), (1, 2), (3, 6), (7, 5), (7, 1), (4, 5), (8, 2), (5, 6), (2, 5), (7, 3), (7, 2)]), + ('YMCA', [(1, 2), (2, 3), (5, 2), (7, 2), (6, 3), (7, 4), (2, 4), (5, 4), (6, 2), (3, 2), (5, 3), (7, 3), (7, 6), + (4, 8), (2, 7), (6, 7), (4, 6), (8, 8), (8, 7), (3, 8), (3, 6), (6, 8)]), + ('ze', [(5, 7), (6, 7), (7, 7), (1, 3), (2, 3), (3, 3), (5, 5), (6, 5), (7, 5), (1, 7), (2, 7), (3, 7), (5, 3), + (6, 3), (7, 3), (3, 4), (2, 5), (1, 6), (5, 4), (5, 6)]), + ('space invader metroid', [(4, 1), (2, 3), (3, 5), (5, 6), (7, 6), (7, 3), (2, 8), (3, 2), (1, 7), (1, 4), (6, 2), + (8, 5), (7, 8), (4, 6), (8, 7), (5, 1), (2, 6), (8, 4), (1, 5), (6, 5)]), + ('screw attack', [(2, 7), (7, 4), (6, 1), (3, 7), (2, 4), (5, 6), (4, 2), (5, 4), (3, 6), (7, 1), (3, 3), (6, 4), + (4, 6), (4, 3), (6, 2), (3, 4), (6, 5), (5, 2), (4, 5), (4, 4), (5, 3), (5, 5)]), + ('vanilla wrong order', [(7, 2), (7, 4), (7, 5), (7, 7), (6, 3), (6, 5), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), + (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (3, 3), (3, 5), (2, 2), (2, 4), (2, 5), (2, 7)]), + ('tile shaped tiles', [(2, 7), (2, 6), (2, 5), (2, 4), (2, 3), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), (7, 2), + (7, 3), (7, 4), (7, 5), (7, 6), (7, 7), (6, 7), (5, 7), (4, 7), (3, 7), (4, 5), (5, 4)]), + ('panda shocked emoji', [(7, 3), (7, 4), (3, 3), (3, 4), (5, 5), (5, 6), (5, 7), (4, 6), (4, 7), (6, 7), (6, 6), + (8, 6), (8, 7), (8, 8), (2, 8), (2, 7), (2, 6), (2, 1), (3, 1), (7, 1), (8, 1), (5, 8)]), + ('JK', [(1, 5), (3, 4), (5, 3), (7, 3), (3, 2), (7, 5), (6, 4), (2, 6), (5, 6), (5, 5), (8, 2), (3, 5), (8, 6), + (5, 2), (3, 3), (5, 4)]), + ('dollar sign', [(6, 2), (5, 1), (4, 1), (3, 1), (2, 2), (2, 6), (3, 7), (4, 7), (5, 7), (6, 6), (2, 3), (6, 5), + (3, 4), (5, 4), (4, 4), (4, 0), (4, 2), (4, 3), (4, 5), (4, 6), (4, 8)]), + ('rupee diagonal', [(1, 4), (1, 5), (1, 6), (1, 7), (2, 7), (3, 7), (4, 7), (5, 6), (6, 5), (7, 4), (7, 3), (7, 2), + (7, 1), (6, 1), (5, 1), (4, 1), (3, 2), (2, 3), (3, 5), (4, 4), (5, 3)]), + ('sword', [(1, 8), (8, 1), (8, 2), (1, 4), (7, 1), (5, 8), (7, 3), (2, 4), (6, 2), (5, 7), (6, 4), (2, 5), (5, 3), + (4, 7), (5, 5), (3, 6), (4, 4), (2, 7), (4, 6), (3, 5)]), + ('z1 dungeon1', [(4, 6), (6, 4), (2, 4), (3, 7), (4, 3), (5, 7), (3, 2), (5, 4), (3, 5), (5, 5), (4, 2), (6, 3), + (4, 7), (3, 4), (7, 3), (4, 5), (4, 4)]), + ('z1 dungeon 4', [(4, 8), (3, 6), (4, 4), (5, 2), (3, 1), (4, 3), (6, 2), (4, 1), (3, 3), (5, 4), (3, 5), (4, 7), + (6, 1), (3, 8), (5, 7), (3, 2), (4, 6), (3, 4), (5, 1)]), + ('LTTP', [(3, 4), (2, 3), (6, 2), (6, 4), (2, 2), (5, 2), (6, 3), (2, 4), (7, 2), (3, 6), (4, 8), (8, 7), (7, 6), + (7, 8), (5, 6), (7, 7), (4, 6), (4, 7), (8, 6)]), + ('triple triforce', [(4, 2), (3, 3), (4, 3), (5, 3), (6, 5), (5, 6), (6, 6), (7, 6), (3, 6), (2, 6), (2, 5), + (1, 6)]), + ('TILE', [(2, 2), (5, 4), (3, 2), (2, 4), (5, 2), (2, 3), (1, 2), (5, 3), (3, 6), (6, 6), (7, 8), (8, 6), (6, 7), + (3, 8), (4, 8), (3, 7), (6, 8), (7, 6), (7, 7), (8, 8)]), + ('panda thinking emoji', [(2, 1), (3, 1), (6, 2), (7, 2), (6, 3), (3, 2), (3, 3), (3, 5), (4, 5), (5, 5), (2, 6), + (2, 7), (1, 7), (2, 8), (1, 8), (3, 7), (4, 7), (3, 8)]), + ('pokata key', [(3, 1), (4, 2), (5, 3), (4, 4), (4, 6), (5, 7), (6, 8), (4, 8), (6, 6), (3, 3), (5, 1), (4, 5), + (3, 2), (4, 3), (5, 4), (5, 6), (4, 7), (5, 8), (3, 4), (5, 2), (4, 1)]), + ('tile shaped tiles randomish', [(4, 2), (2, 2), (7, 5), (5, 4), (3, 2), (4, 5), (4, 7), (7, 7), (2, 7), (2, 4), + (7, 2), (2, 5), (5, 7), (7, 4), (5, 2), (6, 2), (3, 7), (2, 3), (7, 6), (6, 7), + (7, 3), (2, 6)]), + ('NO', [(1, 5), (4, 4), (3, 4), (6, 2), (8, 4), (1, 2), (8, 5), (7, 5), (1, 3), (6, 5), (6, 4), (1, 4), (4, 5), + (4, 2), (2, 3), (7, 2), (8, 2), (8, 3), (4, 3), (6, 3)]), + ('bomb', [(3, 3), (5, 7), (6, 4), (2, 6), (5, 3), (3, 7), (6, 6), (2, 4), (7, 2), (2, 5), (4, 7), (5, 1), (4, 2), + (6, 5), (6, 1), (4, 3), (8, 2)]), + ('boot', [(6, 7), (8, 6), (5, 5), (5, 3), (8, 2), (3, 6), (4, 8), (7, 8), (8, 4), (6, 2), (2, 7), (3, 8), (4, 5), + (5, 4), (7, 2), (8, 7), (8, 8), (8, 3), (5, 8), (2, 8), (5, 2), (8, 5)]), + ('javalogo', [(5, 8), (4, 8), (3, 8), (2, 8), (1, 7), (5, 6), (4, 6), (3, 6), (2, 6), (1, 5), (7, 7), (8, 6), + (7, 5), (3, 4), (3, 3), (2, 2), (3, 1), (5, 4), (5, 3), (6, 2)]), + ('pokata and ender key', [(3, 1), (5, 3), (4, 4), (4, 6), (5, 7), (6, 8), (4, 8), (6, 6), (3, 3), (5, 1), (4, 5), + (3, 2), (4, 7), (4, 1), (5, 8), (5, 2), (3, 4), (5, 6), (5, 4)]), + ('kitty', [(3, 1), (6, 4), (7, 6), (8, 3), (1, 4), (3, 7), (3, 4), (2, 2), (5, 2), (6, 1), (6, 5), (6, 7), (2, 6), + (7, 2), (8, 5), (1, 3), (1, 5), (4, 2), (3, 5), (4, 6), (8, 4), (5, 6)]), + ('creeper face', [(3, 7), (4, 5), (5, 4), (5, 6), (3, 3), (2, 2), (3, 2), (7, 3), (6, 7), (4, 6), (6, 3), (7, 2), + (2, 3), (4, 4), (3, 6), (6, 2), (6, 6), (5, 5)]) +] diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index 792ae2e7..236f2bea 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -1,19 +1,33 @@ +from collections import defaultdict + from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats from source.dungeon.RoomHeader import init_room_headers from source.dungeon.RoomList import Room0127 +from source.enemizer.OwEnemyList import init_vanilla_sprites_ow, vanilla_sprites_ow from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements +def convert_area_id_to_offset(area_id): + if area_id < 0x40: + return area_id + if 0x40 <= area_id < 0x80: + return area_id + 0x40 + if 0x90 <= area_id < 0xCF: + return area_id - 0x50 + raise Exception(f'{hex(area_id)} is not a valid area id for offset math') + + class DataTables: def __init__(self): self.room_headers = None self.room_list = None self.sprite_sheets = None self.uw_enemy_table = None - self.ow_enemy_table = None # todo : data migration + self.ow_enemy_table = None self.pot_secret_table = None + self.overworld_sprite_sheets = None # associated data self.sprite_requirements = None @@ -33,7 +47,6 @@ class DataTables: door_start, bytes_written = room.write_to_rom(snes_to_pc(room_start_address), rom) rom.write_bytes(snes_to_pc(0x1F83C0 + room_id * 3), int24_as_bytes(room_start_address + door_start)) room_start_address += bytes_written - # todo: room data doors pointers at 1F83C0 if room_start_address > 0x380000: raise Exception('Room list exceeded bank size') # size notes: bank 03 uses 140E bytes @@ -45,7 +58,19 @@ 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) - # todo: write ow enemy table + for area_id, sheet_number 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) + else: + offset = convert_area_id_to_offset(area_id) + rom.write_byte(snes_to_pc(0x00FA81+offset), sheet_number) + # _00FA81 is LW normal + # _00FAC1 is LW post-aga + # _00FB01 is DW + for area, sprite_list in vanilla_sprites_ow.items(): + for sprite in sprite_list: + rom.write_bytes(snes_to_pc(sprite.original_address), sprite.sprite_data_ow()) def init_data_tables(world, player): @@ -62,4 +87,10 @@ def init_data_tables(world, player): for room, sprite_list in vanilla_sprites.items(): for sprite in sprite_list: uw_table.room_map[room].append(sprite.copy()) + data_tables.overworld_sprite_sheets = {} + data_tables.ow_enemy_table = defaultdict(list) + init_vanilla_sprites_ow() + for area, sprite_list in vanilla_sprites_ow.items(): + for sprite in sprite_list: + data_tables.ow_enemy_table[area].append(sprite.copy()) return data_tables From 9d9071a9846449e7569fbd950c8be6a20d61f386 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 10 Mar 2023 14:19:09 -0700 Subject: [PATCH 010/158] Unstable rom updates --- Rom.py | 2 +- data/base2current.bps | Bin 98868 -> 99391 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 144b41f5..306c7b24 100644 --- a/Rom.py +++ b/Rom.py @@ -39,7 +39,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'c9bf2a8b285fc8cdb98c30844bc3c821' +RANDOMIZERBASEHASH = '09685acbd19f0f8a9061c1ead16747d2' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 21edfa06677e3b054bfd11014bec33e409714e3b..4bcf03eeeba1e639f3d10ebedd4b9206b69d0f01 100644 GIT binary patch delta 18950 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 JHiM3q)qh2aU9tcG delta 18636 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 Date: Mon, 13 Mar 2023 11:11:59 -0600 Subject: [PATCH 011/158] Damage table start --- source/dungeon/EnemyList.py | 26 ++- source/enemizer/DamageTables.py | 0 source/enemizer/Enemizer.py | 2 +- source/enemizer/SpriteSheets.py | 2 +- source/enemizer/damage_table.yaml | 302 ++++++++++++++++++++++++++++++ 5 files changed, 315 insertions(+), 17 deletions(-) create mode 100644 source/enemizer/DamageTables.py create mode 100644 source/enemizer/damage_table.yaml diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index b7d1c6e8..3c04b9c9 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -166,7 +166,7 @@ class EnemySprite(FastEnum): GreenEyegoreMimic = 0x83 RedEyegoreMimic = 0x84 YellowStalfos = 0x85 # falling stalfos that shoots head - Kondongo = 0x86 + Kodongo = 0x86 Mothula = 0x88 SpikeBlock = 0x8a Gibdo = 0x8b @@ -391,7 +391,7 @@ def init_enemy_stats(): EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5, health=16), EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5, health=8), EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True, health=8), - EnemySprite.Kondongo: EnemyStats(EnemySprite.Kondongo, False, True, 6, health=1), + EnemySprite.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), @@ -668,10 +668,10 @@ def init_vanilla_sprites(): create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x12, 0x11, 'Hera 5F') create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x17, 'Hera 5F') create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x17, 'Hera 5F') - create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x16, 0x0a, 'PoD Dark Maze') - create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x1a, 0x0e, 'PoD Dark Maze') - create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x16, 0x10, 'PoD Dark Maze') - create_sprite(0x0019, EnemySprite.Kondongo, 0x00, 0, 0x18, 0x16, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x16, 0x0a, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x1a, 0x0e, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x16, 0x10, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x18, 0x16, 'PoD Dark Maze') create_sprite(0x001a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x06, 'PoD Falling Bridge Mid') create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x06, 'PoD Compass Room') create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x19, 0x06, 'PoD Compass Room') @@ -763,8 +763,8 @@ def init_vanilla_sprites(): create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x13, 'Hera 4F') create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x1a, 'Hera 4F') create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06, 'Hera Big Chest Landing') - create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x05, 0x0e, 'Hera 4F') - create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x04, 0x16, 'Hera 4F') + 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') @@ -1257,8 +1257,8 @@ def init_vanilla_sprites(): create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x10, 0x18) create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x09, 0x1a) create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x16, 0x1a) - create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x07, 0x0a, 'Hera Back') - create_sprite(0x0077, EnemySprite.Kondongo, 0x00, 1, 0x17, 0x0a, 'Hera Back') + create_sprite(0x0077, EnemySprite.Kodongo, 0x00, 1, 0x07, 0x0a, 'Hera Back') + create_sprite(0x0077, EnemySprite.Kodongo, 0x00, 1, 0x17, 0x0a, 'Hera Back') create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x07, 'GT Conveyor Star Pits') create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x09, 'GT Conveyor Star Pits') create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x04, 0x15, 'GT DMs Room') @@ -2055,7 +2055,7 @@ def kill_rules(world, player, stats): arrow=2, silver=2, fire=None), EnemySprite.RedEyegoreMimic: can_bow_kill(world, player, arrow_damage[1], silver_damage[1], h(EnemySprite.RedEyegoreMimic)), - EnemySprite.Kondongo: defeat_rule(world, player, h(EnemySprite.Kondongo), bomb=None, fire=1), + 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), @@ -2407,10 +2407,6 @@ def special_rules_for_region(world, player, region_name, location, original_rule return original_rule - - - - enemy_names = { 0x00: 'Raven', 0x01: 'Vulture', diff --git a/source/enemizer/DamageTables.py b/source/enemizer/DamageTables.py new file mode 100644 index 00000000..e69de29b diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index edd0763c..4d63bd1d 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -19,7 +19,7 @@ water_rooms = { 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, 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}, diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index fdcb575c..eae51f2d 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -280,7 +280,7 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.GreenEyegoreMimic).sub_group(2, 0x2e), SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e), # kodongos apparently broken? - SpriteRequirement(EnemySprite.Kondongo).skip().sub_group(2, 0x2a), + SpriteRequirement(EnemySprite.Kodongo).skip().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? diff --git a/source/enemizer/damage_table.yaml b/source/enemizer/damage_table.yaml new file mode 100644 index 00000000..e68d98c1 --- /dev/null +++ b/source/enemizer/damage_table.yaml @@ -0,0 +1,302 @@ +# From: https://spannerisms.github.io/damage +# There are 16 damage classes, each with 8 subclasses. +# These subclasses determine the damage or effect inflicted on a sprite. +# Subclass data is stored as a table in ROM at $0D:B8F1 + +# DamageSource and SubClassTable + +#_0DB8F1: db $00, $01, $20, $FF, $FC, $FB, $00, $00 ; 0x00 - Boomerang +#_0DB8F9: db $00, $02, $40, $04, $00, $00, $00, $00 ; 0x01 - Sword 1 +#_0DB901: db $00, $04, $40, $02, $03, $00, $00, $00 ; 0x02 - Sword 2 +#_0DB909: db $00, $08, $40, $04, $00, $00, $00, $00 ; 0x03 - Sword 3 +#_0DB911: db $00, $10, $40, $08, $00, $00, $00, $00 ; 0x04 - Sword 4 +#_0DB919: db $00, $10, $40, $08, $00, $00, $00, $00 ; 0x05 - Sword 5 +#_0DB921: db $00, $04, $40, $10, $00, $00, $00, $00 ; 0x06 - Arrow +#_0DB929: db $00, $FF, $40, $FF, $FC, $FB, $00, $00 ; 0x07 - Hookshot +#_0DB931: db $00, $04, $40, $FF, $FC, $FB, $20, $00 ; 0x08 - Bomb +#_0DB939: db $00, $64, $18, $64, $00, $00, $00, $00 ; 0x09 - Silver arrow +#_0DB941: db $00, $F9, $FA, $FF, $64, $00, $00, $00 ; 0x0A - Powder +#_0DB949: db $00, $08, $40, $FD, $04, $10, $00, $00 ; 0x0B - Fire rod +#_0DB951: db $00, $08, $40, $FE, $04, $00, $00, $00 ; 0x0C - Ice rod +#_0DB959: db $00, $10, $40, $FD, $00, $00, $00, $00 ; 0x0D - Bombos +#_0DB961: db $00, $FE, $40, $10, $00, $00, $00, $00 ; 0x0E - Ether +#_0DB969: db $00, $20, $40, $FF, $00, $00, $00, $FA ; 0x0F - Quake + +# Special Values: +# $F9 Target becomes a faerie +# $FA Target becomes a blob +# $FB Target stunned for 32 frames +# $FC Target stunned for 128 frames +# $FD Target incinerated +# $FE Target becomes frozen +# $FF Target stunned for 255 frames + +DamageSource: + Boomerang: + class: 0x00 + subclass: [0x00, 0x01, 0x20, 0xFF, 0xFC, 0xFB, 0x00, 0x00] + Sword1: + class: 0x01 + subclass: [0x00, 0x02, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00] + Sword2: + class: 0x02 + subclass: [0x00, 0x04, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00] + Sword3: + class: 0x03 + subclass: [0x00, 0x08, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00] + Sword4: + class: 0x04 + subclass: [0x00, 0x10, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00] + Sword5: + class: 0x05 + subclass: [0x00, 0x10, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00] + Arrow: + class: 0x06 + subclass: [0x00, 0x04, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00] + Hookshot: + class: 0x07 + subclass: [0x00, 0xFF, 0x40, 0xFF, 0xFC, 0xFB, 0x00, 0x00] + Bomb: + class: 0x08 + subclass: [0x00, 0x04, 0x40, 0xFF, 0xFC, 0xFB, 0x20, 0x00] + SilverArrow: + class: 0x09 + subclass: [0x00, 0x64, 0x18, 0x64, 0x00, 0x00, 0x00, 0x00] + Powder: + class: 0x0A + subclass: [0x00, 0xF9, 0xFA, 0xFF, 0x64, 0x00, 0x00, 0x00] + FireRod: + class: 0x0B + subclass: [0x00, 0x08, 0x40, 0xFD, 0x04, 0x10, 0x00, 0x00] + IceRod: + class: 0x0C + subclass: [0x00, 0x08, 0x40, 0xFE, 0x04, 0x00, 0x00, 0x00] + Bombos: + class: 0x0D + subclass: [0x00, 0x10, 0x40, 0xFD, 0x00, 0x00, 0x00, 0x00] + Ether: + class: 0x0E + subclass: [0x00, 0xFE, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00] + Quake: + class: 0x0F + subclass: [0x00, 0x20, 0x40, 0xFF, 0x00, 0x00, 0x00, 0xFA] + +# The subclass that each sprite should look for in each damage class is in a table in WRAM at $7F:6000 +# Rando migrated it to $31:C800 +SubClassTable: + 0x0: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 1, 1] + 0x1: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 1, 1] + 0x2: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x8: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0x9: [0, 1, 3, 3, 3, 3, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0] + 0xA: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0xB: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 0, 0, 0] + 0xC: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 3, 1] + 0xD: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3] + 0xE: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 2, 7] + 0xF: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 3, 2] + 0x10: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 3, 1] + 0x11: [4, 1, 1, 1, 1, 2, 1, 0, 2, 1, 0, 1, 3, 3, 1, 7] + 0x12: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 3, 3, 1, 7] + 0x13: [0, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 0, 0, 3, 2, 7] + 0x14: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x15: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + 0x16: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x17: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 7] + 0x18: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 1, 3, 3, 7] + 0x19: [1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, 3, 2, 3] + 0x1A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1B: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 3, 2] + 0x1C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x20: [3, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 3, 3, 1, 7] + 0x21: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x22: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 2, 7] + 0x23: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 3] + 0x24: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 3] + 0x25: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x26: [0, 1, 1, 1, 1, 1, 0, 3, 3, 1, 0, 0, 0, 3, 1, 3] + 0x27: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 7] + 0x28: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x29: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + 0x2B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x30: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x31: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x32: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x33: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x34: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x35: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x36: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x37: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x38: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x39: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3E: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 7] + 0x3F: [0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1] + 0x40: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x41: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 1, 7] + 0x42: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0x43: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x44: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x45: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x46: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 1, 7] + 0x47: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 7] + 0x48: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x49: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 2, 7] + 0x4A: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 1, 7] + 0x4B: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0x4C: [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 3, 3, 3] + 0x4D: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 2, 3, 2, 3] + 0x4E: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x4F: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x50: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x51: [3, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 3, 3, 3, 3] + 0x52: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x53: [1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 1, 1, 0, 0, 0] + 0x54: [0, 1, 3, 3, 3, 3, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0] + 0x55: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 1] + 0x56: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 1] + 0x57: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x58: [3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 3, 3, 1, 7] + 0x59: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3] + 0x5C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3] + 0x5D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x60: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x61: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x62: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x63: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x64: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 3, 3, 3] + 0x65: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x66: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x67: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x68: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x69: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x6A: [5, 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x6B: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1] + 0x6C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x6D: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x6E: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x6F: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 3] + 0x70: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x71: [3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 0, 3, 3, 3, 1, 3] + 0x72: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x73: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x74: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x75: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x76: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x77: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x78: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x79: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1] + 0x7A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7C: [0, 1, 1, 1, 1, 1, 1, 0, 2, 1, 0, 3, 3, 3, 3, 3] + 0x7D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x80: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x81: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 2, 3, 2] + 0x82: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x83: [0, 1, 1, 2, 2, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0] + 0x84: [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0] + 0x85: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 2, 3] + 0x86: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 3, 3, 1, 7] + 0x87: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x88: [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0] # mothula + 0x89: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x8A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x8B: [3, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 3, 3, 3, 2, 3] + 0x8C: [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0] + 0x8D: [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0] + 0x8E: [1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 1, 3, 2, 2, 3] + 0x8F: [3, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0, 3, 3, 3, 1, 2] + 0x90: [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 3, 3, 2] + 0x91: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x92: [0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0x93: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x94: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 2, 3] + 0x95: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x96: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x97: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x98: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x99: [1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1, 0, 3, 1, 2] + 0x9A: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 2, 3, 2, 1, 1] + 0x9B: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 2, 3, 2] + 0x9C: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 2] + 0x9D: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 2] + 0x9E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x9F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xA0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xA1: [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0] + 0xA2: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0] + 0xA3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0] + 0xA4: [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 3, 0, 1, 3, 1] + 0xA5: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 3, 3, 3, 1, 3] + 0xA6: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 3, 3, 3, 1, 3] + 0xA7: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 2, 7] + 0xA8: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 1] + 0xA9: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 1] + 0xAA: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 1, 3] + 0xAB: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0] + 0xAC: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xAD: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xAE: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xAF: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB2: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 3, 1] + 0xB3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB9: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBA: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBB: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBC: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBD: [0, 0, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0xBE: [0, 0, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0xBF: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC3: [0, 1, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0xC4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1] + 0xC6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1] + 0xC7: [0, 1, 1, 1, 1, 1, 1, 0, 1, 2, 0, 3, 1, 3, 1, 3] + 0xC8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC9: [5, 1, 1, 1, 1, 1, 3, 0, 2, 1, 0, 3, 3, 1, 3, 1] + 0xCA: [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xCB: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xCC: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0] + 0xCD: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0] + 0xCE: [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xCF: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 2, 1] + 0xD0: [0, 0, 0, 1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0] + 0xD1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2] + 0xD2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 7] + 0xD3: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 7] + 0xD4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xD5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xD6: [0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xD7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0] From a0b781521fd51c3ee4c9447e0ad7a72fb593fb61 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 30 Mar 2023 16:12:46 -0600 Subject: [PATCH 012/158] 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 From ddfbece2bd2b23f47e7b8f9e150629d7a83d14d3 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 5 Apr 2023 16:38:17 -0600 Subject: [PATCH 013/158] Clean up enemizer cli argument and gui code --- CLI.py | 4 - Main.py | 2 +- Mystery.py | 3 - Rom.py | 163 ------------------------------- resources/app/cli/args.json | 3 - resources/app/gui/lang/en.json | 3 - source/gui/bottom.py | 106 ++++++++++---------- source/gui/loadcliargs.py | 14 --- source/gui/randomize/enemizer.py | 48 +-------- 9 files changed, 54 insertions(+), 292 deletions(-) diff --git a/CLI.py b/CLI.py index 7b9d6920..1acebccc 100644 --- a/CLI.py +++ b/CLI.py @@ -199,7 +199,6 @@ def parse_settings(): "shufflebosses": "none", "enemy_damage": "default", "enemy_health": "default", - "enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"), "shopsanity": False, 'keydropshuffle': False, @@ -348,9 +347,6 @@ def parse_settings(): "startinventoryarray": {} } - if sys.platform.lower().find("windows"): - settings["enemizercli"] += ".exe" - # read saved settings file if it exists and set these settings_path = os.path.join(".", "resources", "user", "settings.json") settings = apply_settings_file(settings, settings_path) diff --git a/Main.py b/Main.py index c7b89090..0d3716b4 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_dark_world_regions, create_dungeon_regions, adjust_locations from OverworldShuffle import create_dynamic_exits from EntranceShuffle import link_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 diff --git a/Mystery.py b/Mystery.py index 786bb774..170dd8bc 100644 --- a/Mystery.py +++ b/Mystery.py @@ -33,7 +33,6 @@ def main(): parser.add_argument('--suppress_meta', action='store_true') parser.add_argument('--bps', action='store_true') parser.add_argument('--rom') - parser.add_argument('--enemizercli') parser.add_argument('--outputpath') parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) for player in range(1, multiargs.multi + 1): @@ -76,8 +75,6 @@ def main(): if args.rom: erargs.rom = args.rom - if args.enemizercli: - erargs.enemizercli = args.enemizercli mw_settings = {'algorithm': False} diff --git a/Rom.py b/Rom.py index 7ce9992e..aa780c5f 100644 --- a/Rom.py +++ b/Rom.py @@ -217,169 +217,6 @@ def read_rom(stream): has_smc_header = True return buffer, has_smc_header -def patch_enemizer(world, player, rom, local_rom, enemizercli, random_sprite_on_hit): - baserom_path = os.path.abspath(local_rom.file) - unheadered_path = None - if local_rom.has_smc_header: - headered_path = baserom_path - unheadered_path = baserom_path = os.path.abspath(output_path('unheadered_rom.sfc')) - with open(headered_path, 'rb') as headered: - with open(baserom_path, 'wb') as unheadered: - unheadered.write(headered.read()[0x200:]) - basepatch_path = os.path.abspath(local_path(os.path.join("data","base2current.json"))) - enemizer_basepatch_path = os.path.join(os.path.dirname(enemizercli), "enemizerBasePatch.json") - randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json')) - options_path = os.path.abspath(output_path('enemizer_options.json')) - enemizer_output_path = os.path.abspath(output_path('enemizer_output.json')) - - # write options file for enemizer - options = { - 'RandomizeEnemies': world.enemy_shuffle[player] != 'none', - 'RandomizeEnemiesType': 3, - 'RandomizeBushEnemyChance': world.enemy_shuffle[player] in ['random', 'legacy'], - 'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default', - 'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]], - 'OHKO': False, - 'RandomizeEnemyDamage': world.enemy_damage[player] != 'default', - 'AllowEnemyZeroDamage': True, - 'ShuffleEnemyDamageGroups': world.enemy_damage[player] != 'default', - 'EnemyDamageChaosMode': world.enemy_damage[player] == 'random', - 'EasyModeEscape': False, - 'EnemiesAbsorbable': False, - 'AbsorbableSpawnRate': 10, - 'AbsorbableTypes': { - 'FullMagic': True, 'SmallMagic': True, 'Bomb_1': True, 'BlueRupee': True, 'Heart': True, 'BigKey': True, 'Key': True, - 'Fairy': True, 'Arrow_10': True, 'Arrow_5': True, 'Bomb_8': True, 'Bomb_4': True, 'GreenRupee': True, 'RedRupee': True - }, - 'BossMadness': False, - 'RandomizeBosses': True, - 'RandomizeBossesType': 0, - 'RandomizeBossHealth': False, - 'RandomizeBossHealthMinAmount': 0, - 'RandomizeBossHealthMaxAmount': 300, - 'RandomizeBossDamage': False, - 'RandomizeBossDamageMinAmount': 0, - 'RandomizeBossDamageMaxAmount': 200, - 'RandomizeBossBehavior': False, - 'RandomizeDungeonPalettes': False, - 'SetBlackoutMode': False, - 'RandomizeOverworldPalettes': False, - 'RandomizeSpritePalettes': False, - 'SetAdvancedSpritePalettes': False, - 'PukeMode': False, - 'NegativeMode': False, - 'GrayscaleMode': False, - 'GenerateSpoilers': False, - 'RandomizeLinkSpritePalette': False, - 'RandomizePots': False, - 'ShuffleMusic': False, - 'BootlegMagic': True, - 'CustomBosses': False, - 'AndyMode': False, - 'HeartBeepSpeed': 0, - 'AlternateGfx': False, - 'ShieldGraphics': "shield_gfx/normal.gfx", - 'SwordGraphics': "sword_gfx/normal.gfx", - 'BeeMizer': False, - 'BeesLevel': 0, - 'RandomizeTileTrapPattern': world.enemy_shuffle[player] in ['random', 'legacy'], - 'RandomizeTileTrapFloorTile': False, - 'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'legacy' else world.enemy_shuffle[player] != 'none', - 'RandomizeSpriteOnHit': random_sprite_on_hit, - 'DebugMode': False, - 'DebugForceEnemy': False, - 'DebugForceEnemyId': 0, - 'DebugForceBoss': False, - 'DebugForceBossId': 0, - 'DebugOpenShutterDoors': False, - 'DebugForceEnemyDamageZero': False, - 'DebugShowRoomIdInRupeeCounter': False, - 'UseManualBosses': True, - 'ManualBosses': { - 'EasternPalace': world.get_dungeon("Eastern Palace", player).boss.enemizer_name, - 'DesertPalace': world.get_dungeon("Desert Palace", player).boss.enemizer_name, - 'TowerOfHera': world.get_dungeon("Tower of Hera", player).boss.enemizer_name, - 'AgahnimsTower': 'Agahnim', - 'PalaceOfDarkness': world.get_dungeon("Palace of Darkness", player).boss.enemizer_name, - 'SwampPalace': world.get_dungeon("Swamp Palace", player).boss.enemizer_name, - 'SkullWoods': world.get_dungeon("Skull Woods", player).boss.enemizer_name, - 'ThievesTown': world.get_dungeon("Thieves Town", player).boss.enemizer_name, - 'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name, - 'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name, - 'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name, - 'GanonsTower1': [x for x in world.dungeons if x.player == player and 'bottom' in x.bosses.keys()][0].bosses['bottom'].enemizer_name, - 'GanonsTower2': [x for x in world.dungeons if x.player == player and 'middle' in x.bosses.keys()][0].bosses['middle'].enemizer_name, - 'GanonsTower3': [x for x in world.dungeons if x.player == player and 'top' in x.bosses.keys()][0].bosses['top'].enemizer_name, - 'GanonsTower4': 'Agahnim2', - 'Ganon': 'Ganon', - } - } - - rom.write_to_file(randopatch_path) - - with open(options_path, 'w') as f: - json.dump(options, f) - - try: - subprocess.run([os.path.abspath(enemizercli), - '--rom', baserom_path, - '--seed', str(world.rom_seeds[player]), - '--base', basepatch_path, - '--randomizer', randopatch_path, - '--enemizer', options_path, - '--output', enemizer_output_path], - cwd=os.path.dirname(enemizercli), - check=True, - capture_output=True) - except subprocess.CalledProcessError as e: - from Main import EnemizerError - enemizerMsg = world.fish.translate("cli","cli","Enemizer returned exit code: ") + str(e.returncode) + "\n" - enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied") - logging.error(f'Enemizer error output: {e.stderr.decode("utf-8")}\n') - raise EnemizerError(enemizerMsg) - - with open(enemizer_basepatch_path, 'r') as f: - for patch in json.load(f): - rom.write_bytes(patch["address"], patch["patchData"]) - - with open(enemizer_output_path, 'r') as f: - for patch in json.load(f): - rom.write_bytes(patch["address"], patch["patchData"]) - - if random_sprite_on_hit: - _populate_sprite_table() - sprites = list(_sprite_table.values()) - if sprites: - while len(sprites) < 32: - sprites.extend(sprites) - random.shuffle(sprites) - - for i, path in enumerate(sprites[:32]): - sprite = Sprite(path) - rom.write_bytes(0x300000 + (i * 0x8000), sprite.sprite) - rom.write_bytes(0x307000 + (i * 0x8000), sprite.palette) - rom.write_bytes(0x307078 + (i * 0x8000), sprite.glove_palette) - - if local_rom.has_smc_header: - try: - os.remove(unheadered_path) - except OSError: - pass - - try: - os.remove(randopatch_path) - except OSError: - pass - - try: - os.remove(options_path) - except OSError: - pass - - try: - os.remove(enemizer_output_path) - except OSError: - pass _sprite_table = {} def _populate_sprite_table(): diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 78496394..c6829d68 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -461,9 +461,6 @@ "bps": { "action": "store_true" }, - "enemizercli": { - "setting": "enemizercli" - }, "shufflebosses": { "choices": [ "none", diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 893237a5..12b36fca 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -130,9 +130,6 @@ "randomizer.enemizer.enemyhealth.hard": "Hard", "randomizer.enemizer.enemyhealth.expert": "Expert", - "randomizer.enemizer.enemizercli": "EnemizerCLI path: ", - "randomizer.enemizer.enemizercli.online": "(get online)", - "randomizer.entrance.openpyramid": "Pre-open Pyramid Hole", "randomizer.entrance.openpyramid.auto": "Auto", "randomizer.entrance.openpyramid.yes": "Yes", diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 23ef3b02..3eff00cd 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -76,59 +76,58 @@ def bottom_frame(self, parent, args=None): elif type(v) is dict: # use same settings for every player setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)}) argsDump = vars(guiargs) - hasEnemizer = "enemizercli" in argsDump and os.path.isfile(argsDump["enemizercli"]) - needEnemizer = False - if hasEnemizer: - falsey = ["none", "default", False, 0] - for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]: - if enemizerOption in argsDump: - if isinstance(argsDump[enemizerOption], dict): - for playerID,playerSetting in argsDump[enemizerOption].items(): - if not playerSetting in falsey: - needEnemizer = True - elif not argsDump[enemizerOption] in falsey: - needEnemizer = True - seeds = [] - if not needEnemizer or (needEnemizer and hasEnemizer): - try: - if guiargs.count is not None and guiargs.seed: - seed = guiargs.seed - for _ in range(guiargs.count): - seeds.append(seed) - main(seed=seed, args=guiargs, fish=parent.fish) - seed = random.randint(0, 999999999) - else: - if guiargs.seed: - seeds.append(guiargs.seed) - else: - random.seed(None) - guiargs.seed = random.randint(0, 999999999) - seeds.append(guiargs.seed) - main(seed=guiargs.seed, args=guiargs, fish=parent.fish) - except (FillError, EnemizerError, Exception, RuntimeError) as e: - logging.exception(e) - messagebox.showerror(title="Error while creating seed", message=str(e)) - else: - YES = parent.fish.translate("cli","cli","yes") - NO = parent.fish.translate("cli","cli","no") - successMsg = "" - made = {} - for k in [ "rom", "playthrough", "spoiler" ]: - made[k] = parent.fish.translate("cli","cli","made." + k) - made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer") - for k in made: - v = made[k] - pattern = "([\w]+)(:)([\s]+)(.*)" - m = re.search(pattern,made[k]) - made[k] = m.group(1) + m.group(2) + ' ' + m.group(4) - successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n" - successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n" - successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n" - successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n" - # FIXME: English - successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds))) - messagebox.showinfo(title="Success", message=successMsg) + needEnemizer = False + falsey = ["none", "default", False, 0] + for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]: + if enemizerOption in argsDump: + if isinstance(argsDump[enemizerOption], dict): + for playerID,playerSetting in argsDump[enemizerOption].items(): + if not playerSetting in falsey: + needEnemizer = True + elif not argsDump[enemizerOption] in falsey: + needEnemizer = True + seeds = [] + + try: + if guiargs.count is not None and guiargs.seed: + seed = guiargs.seed + for _ in range(guiargs.count): + seeds.append(seed) + main(seed=seed, args=guiargs, fish=parent.fish) + seed = random.randint(0, 999999999) + else: + if guiargs.seed: + seeds.append(guiargs.seed) + else: + random.seed(None) + guiargs.seed = random.randint(0, 999999999) + seeds.append(guiargs.seed) + main(seed=guiargs.seed, args=guiargs, fish=parent.fish) + except (FillError, EnemizerError, Exception, RuntimeError) as e: + logging.exception(e) + messagebox.showerror(title="Error while creating seed", message=str(e)) + else: + YES = parent.fish.translate("cli","cli","yes") + NO = parent.fish.translate("cli","cli","no") + successMsg = "" + made = {} + for k in [ "rom", "playthrough", "spoiler" ]: + made[k] = parent.fish.translate("cli","cli","made." + k) + made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer") + for k in made: + v = made[k] + pattern = "([\w]+)(:)([\s]+)(.*)" + m = re.search(pattern,made[k]) + made[k] = m.group(1) + m.group(2) + ' ' + m.group(4) + successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n" + successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n" + successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n" + successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n" + # FIXME: English + successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds))) + + messagebox.showinfo(title="Success", message=successMsg) ## Generate Button # widget ID @@ -217,9 +216,6 @@ def create_guiargs(parent): pagewidgets = page.content.customWidgets if mainpage == "custom" else page.content.startingWidgets if mainpage == "startinventory" else page.widgets setattr(guiargs, arg, pagewidgets[widget].storageVar.get()) - # Get EnemizerCLI setting - guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.get() - # Get Multiworld Worlds count guiargs.multi = int(parent.pages["bottom"].pages["content"].widgets["worlds"].storageVar.get()) diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 6d358853..2b02d551 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -77,20 +77,6 @@ def loadcliargs(gui, args, settings=None): # If we've got a Game Options val and we don't have an Adjust val, use the Game Options val gui.pages["adjust"].content.widgets[widget].storageVar.set(args[arg]) - # Get EnemizerCLI setting - mainpage = "randomizer" - subpage = "enemizer" - widget = "enemizercli" - setting = "enemizercli" - # set storagevar - gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting]) - # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) - gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) - # set get from web label - label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + ".online") - gui.pages[mainpage].pages[subpage].widgets[widget].pieces["online"].label.configure(text=label) - # Get baserom path mainpage = "randomizer" subpage = "generation" diff --git a/source/gui/randomize/enemizer.py b/source/gui/randomize/enemizer.py index f6ba1846..a5cfe686 100644 --- a/source/gui/randomize/enemizer.py +++ b/source/gui/randomize/enemizer.py @@ -6,10 +6,7 @@ import webbrowser from source.classes.Empty import Empty def enemizer_page(parent,settings): - def open_enemizer_download(_evt): - webbrowser.open("https://github.com/Bonta0/Enemizer/releases") - - # Enemizer + # Enemizer self = ttk.Frame(parent) # Enemizer options @@ -45,45 +42,4 @@ def enemizer_page(parent,settings): packAttrs["anchor"] = W self.widgets[key].pack(packAttrs) - ## Enemizer CLI Path - # This one's more-complicated, build it and stuff it - # widget ID - widget = "enemizercli" - - # Empty object - self.widgets[widget] = Empty() - # pieces - self.widgets[widget].pieces = {} - - # frame - self.widgets[widget].pieces["frame"] = Frame(self.frames["bottomEnemizerFrame"]) - # frame: label - self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text="EnemizerCLI path: ") - self.widgets[widget].pieces["frame"].label.pack(side=LEFT) - - # get app online - self.widgets[widget].pieces["online"] = Empty() - # get app online: label - self.widgets[widget].pieces["online"].label = Label(self.widgets[widget].pieces["frame"], text="(get online)", fg="blue", cursor="hand2") - self.widgets[widget].pieces["online"].label.pack(side=LEFT) - # get app online: open browser - self.widgets[widget].pieces["online"].label.bind("", open_enemizer_download) - # storage var - self.widgets[widget].storageVar = StringVar(value=settings["enemizercli"]) - # textbox - self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) - self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True) - - def EnemizerSelectPath(): - path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")], initialdir=os.path.join(".")) - if path: - self.widgets[widget].storageVar.set(path) - settings["enemizercli"] = path - # dialog button - self.widgets[widget].pieces["opendialog"] = Button(self.widgets[widget].pieces["frame"], text='...', command=EnemizerSelectPath) - self.widgets[widget].pieces["opendialog"].pack(side=LEFT) - - # frame: pack - self.widgets[widget].pieces["frame"].pack(fill=X) - - return self,settings + return self, settings From 5d86ed40554f63d2f1e246bd7ae5cd45a3db956a Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 6 Apr 2023 16:57:15 -0600 Subject: [PATCH 014/158] start of any enemy logic option --- source/enemizer/Enemizer.py | 15 +++++++++++---- source/enemizer/SpriteSheets.py | 4 +++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index e5c6f099..82fc05f2 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -257,8 +257,8 @@ def get_randomize_able_sprites_ow(area_id, data_tables): return sprite_table -def randomize_underworld_rooms(data_tables): - +def randomize_underworld_rooms(data_tables, world, player): + any_enemy_logic = world.any_enemy_logic[player] specific = setup_specific_requirements(data_tables) uw_candidates, uw_sheets, all_sheets = find_candidate_sprites(data_tables, range(65, 124)) for room_id in range(0, 0x128): @@ -290,8 +290,15 @@ def randomize_underworld_rooms(data_tables): 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()] + # terrorpin, deadrock, buzzblob, lynel, redmimic/eyegore 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()] + forbidden_set = set() + if not any_enemy_logic: + forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob, + EnemySprite.Lynel}) + if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla + forbidden_set.add(EnemySprite.RedEyegoreMimic) + choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden_set)] else: 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) @@ -358,7 +365,7 @@ 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, data_tables) - randomize_underworld_rooms(data_tables) + randomize_underworld_rooms(data_tables, world, player) randomize_overworld_sprite_sheets(data_tables.sprite_sheets) randomize_overworld_enemies(data_tables) # fix thief stats diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index 8799fb73..4ecd664d 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -91,7 +91,9 @@ class SpriteRequirement: def good_for_uw_water(self): return self.water_only and not self.static and not self.dont_use and self.uw_valid - def good_for_shutter(self): + def good_for_shutter(self, forbidden): + if self.sprite in forbidden: + return False return self.killable and not self.static and not self.dont_use and self.uw_valid def good_for_key_drop(self): From 907639b9848690f08249899fd94e78084906e2a4 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 10 Apr 2023 09:07:01 -0600 Subject: [PATCH 015/158] any_enemy_logic option added --- BaseClasses.py | 4 +++ CLI.py | 3 ++- Main.py | 2 ++ mystery_example.yml | 4 +++ mystery_testsuite.yml | 1 + resources/app/cli/args.json | 7 +++++ resources/app/cli/lang/en.json | 6 +++++ resources/app/gui/lang/en.json | 5 ++++ .../app/gui/randomize/enemizer/widgets.json | 14 ++++++++++ source/classes/CustomSettings.py | 2 ++ source/classes/constants.py | 3 ++- source/enemizer/Enemizer.py | 26 +++++++++++++------ source/enemizer/SpriteSheets.py | 4 +-- source/gui/randomize/enemizer.py | 4 ++- source/tools/MysteryUtils.py | 1 + 15 files changed, 73 insertions(+), 13 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 64e2385c..f07e76e5 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -128,6 +128,7 @@ class World(object): set_player_attr('enemy_shuffle', 'none') set_player_attr('enemy_health', 'default') set_player_attr('enemy_damage', 'default') + set_player_attr('any_enemy_logic', 'allow_all') set_player_attr('beemizer', 0) set_player_attr('escape_assist', []) set_player_attr('crystals_needed_for_ganon', 7) @@ -2497,6 +2498,7 @@ class Spoiler(object): 'enemy_shuffle': self.world.enemy_shuffle, 'enemy_health': self.world.enemy_health, 'enemy_damage': self.world.enemy_damage, + 'any_enemy_logic': self.world.any_enemy_logic, 'players': self.world.players, 'teams': self.world.teams, 'experimental': self.world.experimental, @@ -2701,6 +2703,8 @@ class Spoiler(object): outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player]) outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player]) outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player]) + if self.metadata['enemy_shuffle'][player] != 'none': + outfile.write(f"Enemy logic: {self.metadata['any_enemy_logic'][player]}\n") outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n") outfile.write('Race: %s\n' % ('Yes' if self.world.settings.world_rep['meta']['race'] else 'No')) diff --git a/CLI.py b/CLI.py index 1acebccc..fcb769f0 100644 --- a/CLI.py +++ b/CLI.py @@ -141,7 +141,7 @@ def parse_cli(argv, no_defaults=False): 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode', - 'trap_door_mode', 'key_logic_algorithm']: + 'trap_door_mode', 'key_logic_algorithm', 'any_enemy_logic']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -199,6 +199,7 @@ def parse_settings(): "shufflebosses": "none", "enemy_damage": "default", "enemy_health": "default", + 'any_enemy_logic': 'allow_all', "shopsanity": False, 'keydropshuffle': False, diff --git a/Main.py b/Main.py index 0d3716b4..87b1461f 100644 --- a/Main.py +++ b/Main.py @@ -112,6 +112,7 @@ def main(args, seed=None, fish=None): world.enemy_shuffle = args.shuffleenemies.copy() world.enemy_health = args.enemy_health.copy() world.enemy_damage = args.enemy_damage.copy() + world.any_enemy_logic = args.any_enemy_logic.copy() world.beemizer = args.beemizer.copy() world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)} world.door_type_mode = args.door_type_mode.copy() @@ -454,6 +455,7 @@ def copy_world(world): ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_health = world.enemy_health.copy() ret.enemy_damage = world.enemy_damage.copy() + ret.any_enemy_logic = world.any_enemy_logic.copy() ret.beemizer = world.beemizer.copy() ret.intensity = world.intensity.copy() ret.decoupledoors = world.decoupledoors.copy() diff --git a/mystery_example.yml b/mystery_example.yml index b56f38d8..c3fba1e4 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -162,6 +162,10 @@ none: 3 shuffled: 2 random: 1 + any_enemy_logic: + none: 1 + allow_drops: 2 + allow_all: 3 hints: on: 1 off: 1 diff --git a/mystery_testsuite.yml b/mystery_testsuite.yml index 5d7291b7..403e93f5 100644 --- a/mystery_testsuite.yml +++ b/mystery_testsuite.yml @@ -146,6 +146,7 @@ enemy_shuffle: # shouldn't affect generation shuffled: 1 random: 1 legacy: 0 +any_enemy_logic: allow_all # more interesting logic hints: on: 1 off: 1 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index c6829d68..2e9606a6 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -494,6 +494,13 @@ "random" ] }, + "any_enemy_logic": { + "choices": [ + "none", + "allow_drops", + "allow_all" + ] + }, "remote_items": { "action": "store_true", "type": "bool" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 588a565a..d36dbc16 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -335,6 +335,12 @@ ], "pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"], "bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], + "any_enemy_logic": [ + "How to handle potential traversal between dungeon in Crossed door shuffle", + "None: Enemies that required unusual weaponry are not allowed to logical protect doors, chests, or key drops.", + "Allow_Drops: Drops are okay for those enemies, and a correct weapon is logically required", + "Allow_All: All enemies allowed anywhere. (Logic will be adapted to the placement)" + ], "startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ], "usestartinventory": [ "Toggle usage of Starting Inventory." ], "customizer": ["Path to a customizer file."], diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 12b36fca..faa4b73e 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -130,6 +130,11 @@ "randomizer.enemizer.enemyhealth.hard": "Hard", "randomizer.enemizer.enemyhealth.expert": "Expert", + "randomizer.enemizer.enemylogic": "Enemy Logic", + "randomizer.enemizer.enemylogic.none": "Forbid special enemies", + "randomizer.enemizer.enemylogic.allow_drops": "Item drops may have special enemies", + "randomizer.enemizer.enemylogic.allow_all": "Allow special enemies anywhere", + "randomizer.entrance.openpyramid": "Pre-open Pyramid Hole", "randomizer.entrance.openpyramid.auto": "Auto", "randomizer.entrance.openpyramid.yes": "Yes", diff --git a/resources/app/gui/randomize/enemizer/widgets.json b/resources/app/gui/randomize/enemizer/widgets.json index 7fa097a2..0c78cb2d 100644 --- a/resources/app/gui/randomize/enemizer/widgets.json +++ b/resources/app/gui/randomize/enemizer/widgets.json @@ -39,5 +39,19 @@ "expert" ] } + }, + "bottomEnemizerFrame": { + "enemylogic": { + "type": "selectbox", + "default": "allow_all", + "options": [ + "none", + "allow_drops", + "allow_all" + ], + "config": { + "width": 32 + } + } } } diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index c16c93e7..91bcf371 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -134,6 +134,7 @@ class CustomSettings(object): args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], args.shuffleenemies[p]) args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p]) args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p]) + args.any_enemy_logic[p] = get_setting(settings['any_enemy_logic'], args.any_enemy_logic[p]) args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p]) args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p]) args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p]) @@ -254,6 +255,7 @@ class CustomSettings(object): settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p] settings_dict[p]['enemy_health'] = world.enemy_health[p] settings_dict[p]['enemy_damage'] = world.enemy_damage[p] + settings_dict[p]['any_enemy_logic'] = world.any_enemy_logic[p] settings_dict[p]['shufflepots'] = world.potshuffle[p] settings_dict[p]['bombbag'] = world.bombbag[p] settings_dict[p]['shufflelinks'] = world.shufflelinks[p] diff --git a/source/classes/constants.py b/source/classes/constants.py index 3f0fb620..b7f034d1 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -114,7 +114,8 @@ SETTINGSTOPROCESS = { "enemyshuffle": "shuffleenemies", "bossshuffle": "shufflebosses", "enemydamage": "enemy_damage", - "enemyhealth": "enemy_health" + "enemyhealth": "enemy_health", + "enemylogic": "any_enemy_logic" }, "gameoptions": { "nobgm": "disablemusic", diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 82fc05f2..d97c0cd3 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -289,16 +289,12 @@ def randomize_underworld_rooms(data_tables, world, player): 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()] + forbidden = determine_forbidden(any_enemy_logic == 'none', room_id, True) + choice_list = [x for x in candidate_sprites if x.good_for_key_drop(forbidden)] # terrorpin, deadrock, buzzblob, lynel, redmimic/eyegore elif room_id in shutter_sprites and i in shutter_sprites[room_id]: - forbidden_set = set() - if not any_enemy_logic: - forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob, - EnemySprite.Lynel}) - if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla - forbidden_set.add(EnemySprite.RedEyegoreMimic) - choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden_set)] + forbidden = determine_forbidden(any_enemy_logic != 'allow_all', room_id) + choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden)] else: 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) @@ -317,6 +313,20 @@ def randomize_underworld_rooms(data_tables, world, player): # done with rooms +def determine_forbidden(forbid, room_id, drop_flag=False): + forbidden_set = set() + if forbid: + forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob, + EnemySprite.Lynel}) + if drop_flag: + forbidden_set.add(EnemySprite.RedBari) # requires FireRod to Drop + # else: Not yet able to protect triggers, would change default GT tile room behavior + # forbidden_set.add(EnemySprite.AntiFairy) # can't drop anyway + if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla rooms + forbidden_set.add(EnemySprite.RedEyegoreMimic) + return forbidden_set + + 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]] diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index 4ecd664d..97baf0a1 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -96,8 +96,8 @@ class SpriteRequirement: return False return self.killable and not self.static and not self.dont_use and self.uw_valid - def good_for_key_drop(self): - return self.good_for_shutter() and self.can_drop + def good_for_key_drop(self, forbidden): + return self.good_for_shutter(forbidden) and self.can_drop def __str__(self): return f'Req for {enemy_names[self.sprite]}' diff --git a/source/gui/randomize/enemizer.py b/source/gui/randomize/enemizer.py index a5cfe686..40bf055c 100644 --- a/source/gui/randomize/enemizer.py +++ b/source/gui/randomize/enemizer.py @@ -26,7 +26,7 @@ def enemizer_page(parent,settings): self.frames["selectOptionsFrame"].pack(fill=X) self.frames["leftEnemizerFrame"].pack(side=LEFT) self.frames["rightEnemizerFrame"].pack(side=RIGHT) - self.frames["bottomEnemizerFrame"].pack(fill=X) + self.frames["bottomEnemizerFrame"].pack(fill=X, padx=(12, 0)) # Load Enemizer option widgets as defined by JSON file # Defns include frame name, widget type, widget options, widget placement attributes @@ -40,6 +40,8 @@ def enemizer_page(parent,settings): packAttrs = {"anchor":E} if self.widgets[key].type == "checkbox": packAttrs["anchor"] = W + if framename == 'bottomEnemizerFrame': + packAttrs["anchor"] = W self.widgets[key].pack(packAttrs) return self, settings diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index af20b5e7..6229fac1 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -182,6 +182,7 @@ def roll_settings(weights): ret.enemy_damage = damage_choice ret.enemy_health = get_choice('enemy_health') + ret.any_enemy_logic = get_choice('any_enemy_logic') ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0' From 503be6aa91a82535166635c67a8c5f16057c8286 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 19 May 2023 08:53:01 -0600 Subject: [PATCH 016/158] Enemizer refinement --- BaseClasses.py | 6 +- EntranceShuffle.py | 10 +- PotShuffle.py | 2 +- Regions.py | 11 +- Rom.py | 6 +- Rules.py | 5 +- Utils.py | 19 ++ data/base2current.bps | Bin 93202 -> 99517 bytes source/classes/CustomSettings.py | 5 + source/dungeon/EnemyList.py | 250 ++++++++-------- source/enemizer/DamageTables.py | 6 +- source/enemizer/Enemizer.py | 124 +++++--- source/enemizer/EnemizerTestHarness.py | 138 +++++++-- source/enemizer/EnemyLogic.py | 50 +++- source/enemizer/OwEnemyList.py | 52 ++-- source/enemizer/SpriteSheets.py | 185 ++++++++---- source/enemizer/damage_table.yaml | 2 +- source/enemizer/enemy_deny.yaml | 390 ++++++++++++++++++++++++ source/enemizer/enemy_weight.yaml | 396 ++++++++++++------------- source/enemizer/sheet_weight.yaml | 168 +++++++++++ source/enemizer/uw_enemy_deny.yaml | 354 ---------------------- source/overworld/EntranceShuffle2.py | 10 +- source/rom/DataTables.py | 22 +- 23 files changed, 1365 insertions(+), 846 deletions(-) create mode 100644 source/enemizer/enemy_deny.yaml create mode 100644 source/enemizer/sheet_weight.yaml delete mode 100644 source/enemizer/uw_enemy_deny.yaml diff --git a/BaseClasses.py b/BaseClasses.py index f07e76e5..78630427 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2191,7 +2191,8 @@ class Boss(object): return self.defeat_rule(state, self.player) class Location(object): - def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, player_address=None): + def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, + player_address=None, note=None): self.name = name self.parent_region = parent if forced_item is not None: @@ -2222,6 +2223,7 @@ class Location(object): self.type = LocationType.Normal if not crystal else LocationType.Prize self.pot = None self.drop = None + self.note = note def can_fill(self, state, item, check_access=True): if not self.valid_multiworld(state, item): @@ -2252,6 +2254,8 @@ class Location(object): name += f' @ {self.parent_region.dungeon.name}' if world and world.players > 1: name += f' ({world.get_player_names(self.player)})' + if self.note: + name += f' ({self.note})' return name def __str__(self): diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 2e5162e4..d8d0367e 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2071,7 +2071,9 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), ('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'), ('Death Mountain Return Cave W', 'Death Mountain Return Cave (left)'), - ('Old Man Cave Dropdown', 'Old Man Cave'), + ('Old Man Cave Dropdown', 'Old Man Cave (West)'), + ('Old Man Cave W', 'Old Man Cave (West)'), + ('Old Man Cave E', 'Old Man Cave (East)'), ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), ('Old Man House Front to Back', 'Old Man House Back'), @@ -2434,7 +2436,7 @@ open_default_connections = [('Links House', 'Links House'), ('Links House Exit', 'Light World'), ('Big Bomb Shop', 'Big Bomb Shop'), ('Old Man Cave (West)', 'Old Man Cave Ledge'), - ('Old Man Cave (East)', 'Old Man Cave'), + ('Old Man Cave (East)', 'Old Man Cave (East)'), ('Old Man Cave Exit (West)', 'Light World'), ('Old Man Cave Exit (East)', 'West Death Mountain (Bottom)'), ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave (left)'), @@ -2463,7 +2465,7 @@ inverted_default_connections = [('Links House', 'Big Bomb Shop'), ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), ('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'), ('Bumper Cave Exit (Bottom)', 'Light World'), - ('Dark Death Mountain Fairy', 'Old Man Cave'), + ('Dark Death Mountain Fairy', 'Old Man Cave (East)'), ('Inverted Pyramid Hole', 'Pyramid'), ('Inverted Pyramid Entrance', 'Bottom of Pyramid'), ('Pyramid Exit', 'Hyrule Castle Courtyard') @@ -2541,7 +2543,7 @@ indirect_connections = { 'West Dark World': 'Pyramid Fairy', 'South Dark World': 'Pyramid Fairy', 'Light World': 'Pyramid Fairy', - 'Old Man Cave': 'Old Man S&Q' + 'Old Man Cave (East)': 'Old Man S&Q' } # format: # Key=Name diff --git a/PotShuffle.py b/PotShuffle.py index ea98ffff..e76dcc76 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -750,7 +750,7 @@ vanilla_pots = { Pot(88, 14, PotItem.SmallMagic, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADED, [0xB3, 0x73, 0xFA])), Pot(92, 14, PotItem.Heart, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADF0, [0xBB, 0x73, 0xFA])), Pot(96, 14, PotItem.SmallMagic, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADF3, [0xC3, 0x73, 0xFA]))], - 0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))], + 0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave (East)', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))], 0xF3: [Pot(0x28, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA76F, [0x53, 0xA3, 0xFA])), Pot(0x2C, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA772, [0x5B, 0xA3, 0xFA])), Pot(0x30, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA775, [0x63, 0xA3, 0xFA]))], diff --git a/Regions.py b/Regions.py index 5c077b6a..d804d7a8 100644 --- a/Regions.py +++ b/Regions.py @@ -3,7 +3,7 @@ from Items import ItemFactory from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType, LocationType, PotItem, PotFlags from PotShuffle import key_drop_data, vanilla_pots, choose_pots, PotSecretTable -from source.dungeon.EnemyList import setup_enemy_locations +from source.dungeon.EnemyList import setup_enemy_locations, enemy_names def create_regions(world, player): @@ -119,7 +119,8 @@ def create_regions(world, player): create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']), create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']), create_cave_region(player, 'Lumberjack House', 'a boring house'), - create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']), + create_cave_region(player, 'Old Man Cave (West)', 'a connector', ['Old Man'], ['Old Man Cave E']), + create_cave_region(player, 'Old Man Cave (East)', 'a connector', None, ['Old Man Cave Exit (East)', 'Old Man Cave W']), create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']), create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']), create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), @@ -1032,7 +1033,10 @@ def adjust_locations(world, player): loc.type = LocationType.Drop snes_address, room, sprite_idx = datum[1] loc.address = snes_address - world.data_tables[player].uw_enemy_table.room_map[room][sprite_idx].location = loc + sprite = world.data_tables[player].uw_enemy_table.room_map[room][sprite_idx] + sprite.location = loc + if world.enemy_shuffle[player] != 'none': + loc.note = enemy_names[sprite.kind] else: loc.type = LocationType.Pot pot, pot_index = next((p, i) for i, p in enumerate(vanilla_pots[datum[1]]) if p.item == PotItem.Key) @@ -1046,7 +1050,6 @@ def adjust_locations(world, player): else: key_item = loc.item key_item.location = None - loc.forced_item = None loc.item = None loc.event = False diff --git a/Rom.py b/Rom.py index aa780c5f..f3fdfa59 100644 --- a/Rom.py +++ b/Rom.py @@ -34,12 +34,13 @@ from InitialSram import InitialSram from source.classes.SFX import randomize_sfx from source.item.FillUtil import valid_pot_items from source.dungeon.EnemyList import EnemySprite +from source.dungeon.RoomObject import DoorObject from source.enemizer.Bossmizer import boss_writes from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'e1bf0b1e7ff799fcb6c7c16be9dab7f0' +RANDOMIZERBASEHASH = '79a57594c59b3fa2e9cf344fc7437bf9' class JsonRom(object): @@ -1399,7 +1400,8 @@ def patch_rom(world, rom, player, team, is_mystery=False): for room in world.rooms: if room.player == player and room.modified: if room.index in world.data_tables[player].room_list: - world.data_tables[player].room_list[room.index].doors = room.doorList + t = [DoorObject(x[0], x[1]) for x in room.doorList] + world.data_tables[player].room_list[room.index].doors = t else: rom.write_bytes(room.address(), room.rom_data()) diff --git a/Rules.py b/Rules.py index 153b167a..c9eb67e0 100644 --- a/Rules.py +++ b/Rules.py @@ -868,6 +868,8 @@ def drop_rules(world, player): if enemy.location.parent_region.name in special_rules_check: rule = special_rules_for_region(world, player, enemy.location.parent_region.name, enemy.location, rule) + if rule.rule_lambda is None: + raise Exception(f'Bad rule for enemy drop. Need to inspect this case: {hex(enemy.kind)}') add_rule_new(enemy.location, rule) @@ -1101,7 +1103,8 @@ def add_conditional_lamps(world, player): 'Sewers Water': {'sewer': True, 'entrances': ['Sewers Water S', 'Sewers Water W'], 'locations': []}, 'Sewers Dark Aquabats': {'sewer': True, 'entrances': ['Sewers Dark Aquabats N', 'Sewers Dark Aquabats ES'], 'locations': []}, 'Sewers Key Rat': {'sewer': True, 'entrances': ['Sewers Key Rat S', 'Sewers Key Rat NE'], 'locations': ['Hyrule Castle - Key Rat Key Drop']}, - 'Old Man Cave': {'sewer': False, 'entrances': ['Old Man Cave Exit (East)']}, + 'Old Man Cave (East)': {'sewer': False, 'entrances': ['Old Man Cave Exit (East)', 'Old Man Cave W']}, + 'Old Man Cave (West)': {'sewer': False, 'entrances': ['Old Man Cave E']}, 'Old Man House Back': {'sewer': False, 'entrances': ['Old Man House Back to Front', 'Old Man House Exit (Top)']}, 'Death Mountain Return Cave (left)': {'sewer': False, 'entrances': ['Death Mountain Return Cave E', 'Death Mountain Return Cave Exit (West)']}, 'Death Mountain Return Cave (right)': {'sewer': False, 'entrances': ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']}, diff --git a/Utils.py b/Utils.py index d26a83e8..c32adbd8 100644 --- a/Utils.py +++ b/Utils.py @@ -724,6 +724,25 @@ def load_yaml(path_list): return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader) +yaml_cache = {} + + +def load_cached_yaml(path_list): + path = os.path.join(*path_list) + if path in yaml_cache: + return yaml_cache[path] + else: + if os.path.exists(Path(path)): + with open(path, "r", encoding="utf-8") as f: + data = yaml.load(f, Loader=yaml.SafeLoader) + yaml_cache[path] = data + return data + elif urllib.parse.urlparse(path).scheme in ['http', 'https']: + data = yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader) + yaml_cache[path] = data + return data + + 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 6eefc4cf7ea21bcaa018847699ef29eeda8fab7e..55b2a98f5ba9ff7ba8b1b1b8b03e1f6b46aefc6c 100644 GIT binary patch delta 29106 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 QSw34i|Cm1Mc-ja5571iyX#fBK delta 22430 zcmafbcU%+a8~9}aVecg?oG9SLjfjdH1r^*QDpk~IoYhwAAnyeZ4R9RcH8~+*4oJj+ z7%W;kKoAGSQB$i`u~w_qBH9|OZLO{K_oTny=lkzZ;C-HV+iCX?Y9Z00?(OY8j8lyz zxWMu2$np%isMYABqD?I!MGX1;^MrvZ5iki5jOe-6#Hum-|IYCJi^0l(|gT}1h1&!tDV-Iek z7@jaU4!W=Y;56T*esr2Ia$tuY*gxu57mOkB)_@|Tks+sBcwOqCXNiq!S?)<)CV9nn zAwlTwLBX#!$qZB6i>4DhZ-84g9((x%2)0Tk}eBdF- z$te9SMP2}{XA|?9!%h8>g~<4c)!l+?M-cSW5|MwMhU!gBaRY=DrZ+2 zdv7!3SGSFE1?pX$#!Uy*Gpu|DylC$&zSje(3#@zz9^D`9JHe0>%J_XZ&18RUOnfRK zYdVb?l=@F5Hat&DCL7^hyD1Gh;U?b;OS0)Ee}y4)i(+F68c6mgPkwaQQ&y5|OgX@i z%Hr5V`W*{Y%pt9kx5-qD>{t&UP*z1gb8H3tJI|0?i;NE#@*Ju@%#b-+J_cS<+6A>C zxn-V3v8I!DXWh=k7YmSDdmvVau_?5PTu&PxythkGxK5eU8KY7c<;99&c{JCI z?PX*sZL~S}PNmdGXyeXGZEr8R6^4?X%aFfZgC)TpGjeHhuUMra|1FH|&B)_s9ZFHN zL$RFFlB;rKdwZ{F$ln2;Vo0aF*p`|hm9KG&2h4ePUTjuYxo+{4!rc@@9_{7_a59dUc;X z;~Rz~yNuiiQ}Vhov6h38gh${S8ReE%GdE(DfyIQ zA6+J;50Sr~;HRS*@rM?y%GaK}_?PwKUsIRECE0{mJ55wN-7y~gZ{@}1Wn%F^#J~Uk zn=lHgv6VOKx2-rhHNTcUSeqX(7ItJJEaTK)sf>0i;|-M|g5OVj@ZH&T1(DyWcQ)^$)SagD@i*pQTd3yh?(~t-)mFXeaE+G$ZQke zs~fla+vfFC3$kE|&E>ogDkEu9?rG6oDWB8vZY)FlkZyh?qW7G~52nH?C^^qxlv61x^^)^^ zMhW-HpQkvtYdf_JDLr}YygKvbvG8K`+Hd*rN-c>TPSEoPA+7~Wky~OOS#~47~D3PNsf^>s=d$iKQUz1d0q;8mTu$+71HE1LyDw0 zp`enS53`ck-MEcKVx*s28lJBuH#-Q?CfKih3!WhLDo7 z>W0h4!|G9=^X=+y%Xz7RA#Zf?a-Dj87r#?gy2`&aouWh|^C{7iQ(Ch7#IfF9W%-?ngSfdLUKb$hTl7kzM^&v|R$MRZolm(gwzy$l>;4?gdg4e>cgD%5n zcJsi@$nUxx8~kN~F_qJjpEzddQ_PUAg~rz!@~69eZ^{7;8SH>{8ge5%x)Z5|mv|Xc z|D$X?CT}_-1xmeFWLpsq6yfhuqYQxeEu#z<&v|BYmA-y&6HD)&>P;_JXLj=8xrPtt z<)$94BwI`1MZj^7t52TbftVlC@;w?-gpIukho~8PqL_ewzArJ#4*})8U_8!{Uf6iJ zS=0|ps;lnVc_aoZ{gO|CJzZ1Imxvr(_fB!_4fRf%zoP#8TRs`+r$>n~k%xm{{8ijA zRY@6{QEaq(_Ub;|SGGWh(IGW)2d<2k)ZF5cC>!q1l{>s$+`Ax;A(wp5r_lF5URe@D z-`_)}z`^q=jy-Q8lZt`flLLxl6DUTuET`6(o@%m!@-M-?&1h{GAP{64}$x=8dk;K@&Sp( zH5#(Th!?s?DPg6^y61dJ+18glne>8>W6H?Km-zH2$1{MsS}*avM6sE4zRagfnx%{0 zCSne{YgVUfmJwMHCWHf*1?ALO7a<* z@s2SIc*wZNa2b#5VeMgp;Yg{GeGN}$!IfNp#n`2`74WxjEf@l)?qw&R)kKp5eUx0g zgesFADs-HO0Jxd0g~ma7lp+qU_COFCdIxoC$O4#nBezAY0l}_sfpK3n^bk~RtYFBY zARQWEMJT?vOCt-pW9+RcvlY=q>o-M*X`8b6P+QJQpfuMH@p14Bu-SiT8@nx!exMGZ zs>siDybU(gL9QSjq@sy~$7%aP0@j)?=X)D_G_tiY{w~^9umBmB9B9Wy1fstw>0wCE z@AyXQc1@&8s?m@c!1V&&(d1LSSb^x?k_B)GG(YnmX}Mb2XE3H-@j1%Q|Nnl&)mrjn z7RKXIN;xIYhCU=Gj)ema9(3Z^pnCk7C#j6I8D&7td^(LF=t?AdQ9QGaq6RjWk%Kfw z^36qk&h`TfV4@&0fcRq;61{eYlxd6}P*&Dr3@G?Pxv+p9kX*jo{Yc7Qm3k63rUI>- zR>qTF7md1z8RgFc7nRC^dSA*rcQvc#!Z2@}L?TG>RuR75HUVDYwMeo))1U^%cM5;$ z)ko>}>73aN6Xy(w_o2_(9mrr zSm^?#^!(Y97&ZcFT?LM;PWs8dBR|?)+m4=bvwNhUJ?lVhB0!|VjuJNEWxvXa3V5CKcH)W-H!g5o0UqTuLNs79jTJu0!N-bX36MirxI0|fs~SHh zMQ{LQb9}}pGV^7raX08R^IMD|xs+lgrT+1VgP^@GF+Nw5FZDIPi`65M#dA(c0IDSO z4UUV@dzdC0xJ0L;z)NmxWH+Gjb8bz#W*eM_A6oE0--QA>rhG#MSy=BI>gCLX;K-2j z79$Xm;kd(hV!zY8aU=*tMZB@sK}y&2AWCXve}f#g8>F6G-e_aUkDB`X#R`>iFG>?5 zI>pEIE~4_0WptQuH|0qFI+D5S!UAB-@-9PUQrSV;C-xxi9h(k^K3;aIK0KyOr{gzP3gVvDy z8~I+KUyA<=SOCe0%1S8Gkn=y~vvwC;iz(iR)Tk^6%WBMd1Ud5t5R&vK&t*V7tDrUL zAYW@^JwRLzZ{!Bf_|zk}#x3L1QyTIlP#jyWgbK-P{yN|+<$sv+JB*71LnPHqtQ4lW z8=nY_8aDh}vqyX3&JZMVaQF@ypt=+~R3*YtCueV>^ zljSk89o@#=D#Lj#?IruW+Zb2K$YP`4zVHmJ#dKXZM;^Bv^VN2QSs zd&>95Q9sJg{>=9l%&L1RC6|D{1AKmV_H>6fk#($!^fVe3(jTmf^tLK;$5Y-Wg6$s^ z0t_q+wJCa^YsdtE+q}@dmb$hR*5jWyw|?4u=ER$px~7>S8~XPxdCX^p6sTWV`G5oJ z-H<6%KLgeD2S!Gq=5S}E?y};$6JnL|T{S~@wHlUg0qi(}=KMs@@wZeRj;Zu5=mw%hzRIDA?V=J1E%zwJ|LNsB`u-v;|%%q3FWQ0bNs z^^ywpc`LsgvAbDGg&KHlO0D{ol_!8heRr3aQ<$AdDh-PC?=_F!i->3XooU|}Py3xc zNIlc5sK3j%jwz>}*Nr%JKMuVYAnc0{vyl|`r5qp%MXS8ftl8hckfSI>P`hub6Rdm; zqP#0=mPh@dc}8x%#qT^qo#LkWtET2TUYik&)a_P2RmFXn^01^Z3&qjw4uq$q1SwbQ z_yvbGeWdE7ih<+K#hHAE9gtADK$y!sH(r+1RU*GBqQU-{z<0;)$zo z#3621pTd*_Rb&j#X!5tFVSk5!^tm0x zi{7bKYOy_%bKTUCn6Ex(1n#hr&Ry3ME9f7Ok)@N)U^`&7WS*opbyylr?YvU%;rG0iE69~NeOiW}`G9qf z&C9a)RgT@sJXgPG=tF#BTIiUyn_p<&+a%_h`Z=3lP(*#N2Gj&|hTa?DurUw0)ngvg zW|7`Xt>RR)o#fWe>!HnJeF*+{T7_U3)=wWK2*F*`BjXiFG3E6TYS5U&^}?}-*W`Nm z-5pBp^}pLpthg9Vx$ge?OhOu~L~z!;X2h+{h`G=9y>ezm+L5+_SK3zeJIy7ff3_Z_ z5EVly_t3jdDIs^)qs(EKt|gvyq1JwScW!3oP>S48q)Ql6sb7#@j-v*ao=9I^$&FaU zp7}&j&=#=b(Z#CfY*fA56@^jumAT5y5?nW9_?(Fci-u5$89p7Mfw{{?mxIyz19UsV zMJi2~7pLFx&yQUGuVrln40gm^dRd;w*spQYq#v(3AC^{OBsffq&Wmhf^LtuUufS^x z@W|ADf~9y_YJ?yO?@rw%n1b!8!v(8x-dX~! zp)E%H)Q8dnLn&jGGzMR;W;&!O@ z3rct5IM-bL8`>#}6R9j8=ILfnt;Fr=L1VUZO@FDK%@R(GSXa7qq3Z#*R3~*kKzUUc zaG4(8_oHOf{vs?FUgdxYCQw+q01?@SV$a#(V_)T14p0e+FLu4kDG{?Dzsf<`R-tXE z#w}ZQIPKr^K^uQgq5<4huOtNqQw&Z zFP18lV4q+Sm&D@!bA~H(RE5TOitc4C$jjzLM(Fut1*RR^&xS9p_yLsc=eBs-CLEPK)w-wfhwHX_6V8(Lc z+Y0<)M!%t+yk4G11${LbQ9k!oj3;k(s*P1qlOAnTeYHH#k&mmLPE(T~slJ*>GoJWj z##kRH99KIpkD3OBY0w6HXUX4lkfGp>RhD16h~aJtfq<|7DjsvhyLk=D%9! z+KnaD0?2OuFB@OGvxHjszwB{GcG3T`KRB|BA^Soxo^C#0Pu-~b+Q9r2>1xXI?J#CMSzvqy4+E? z2nxdptK5Mv2HcylQVx6x;360+UQSo;QCNJzEQU|aUoXslgo77EOj>{LG$l4vP#e#w zD2d@Twf5ZSl#j&;QBuRNlne@yp@NO2C?8hEy3|t%dV-<+AE}z>=cSdYvQ}10`92zon)@C-d(5&N3Y}o3_~{R&Km*tfC?4VS3kD5dbEDGB z0{6)#k+a1SRC?M`Ir1T2T8+q!Ot@rpn366TyTKS!*H%)CBNj$0(~esI1_@z0#?C^d zerM~0!w}%7qkY0og``=Cgxv5)V~cPbG*VlK%sD+I&Hc?H5xx!*iT6f+KW_YQld`0+FMHb*SHCgzDQki zOwQHOh31+(7CKDnFdtawcc`oestHZ~k^eB6M~fYS*MX7WvgyUql5PR91HjhZ4xb zIzD|xefq3=>q=WlvzrLjuVmD#V8x@o5hH>s3=^Xku_}EZhOuVZ0&85ok$SvETif3o zQLFX)*$eRO8k-n)i??Rm#n!d9;Clb*6x9cw*m;6xK^H2&k9nvGR zXqr;gFa5Zi@~T(i6&c7G#fVCA($Y;5OvD_(Gd|++iKT-iezC_WMcb@!{BY?6VOTIG zmkmhTvgS`pfqzs7SQg>TNTd<4f(#`VpJNx?tx7c&u!l30a>b{kCS8M8D}G=EijIUM zlloyiZa2mt)0+6oUca`b00#KCZ3eK>zl{P==ijDv)cLoW9d!Y1t&X~Ywr?DD0d0?)`09Pr z8*$c(VZvpbFkZ1`NZL%Rdf>1@SZMq-GmjoNq$#_f)lkK0${*^FaGIfLM6A`aF4kJy zpBjpy2H>G97e>Z-TfaeQ{6ndQMhRBbIKqN6ls110&DL?t*Wv1wV-mN0X3Zu_MCsQ) zv${FpkY=mE0h#7(H_px1>WL(YPkTg9+|Vo9qy4?jjdwPAg?us?`>yKe9s0SRp$LOY zKVr|Hc)}-m+Nxo~HL-a2s!0;gC#u^CkqhwIRsO#7K7SqiyKL<5P9S_qxb%+>;>W8p zQeqEKZid;r|Na8;52fb!Y-WZsUGn8aDYtLXQ76^mYa26^Fp!=v+62}FAfo@Fz|*c} zz||0?fByykIBSw<_}Uxz%dEvp-GZ&{CY#Va)7l@>jd(n2J=P(u5!AJzFZnuY(U*JP za!y14(i4OKs!G51xrv7!J!{7Lzk6v-E_r1}3=u7@{nG3W&;kQdOYL4};+e$|6Zp=A zgkXr=kfF59!=AZS9u`2oD$S*~rzZ6d+8WZybmnECtsOU7^f)}&QkyeMVpr~!O{3>^MFx6&zGN5m5PpZn(&EE z3x#f}@QE6Cq)r#LG@rn8R?8+xTMFzRX|Gqdu!qa0{^6tms*Fb;Xmyq*ot54>Qx6iY z%{}e463@wJ@Ea#ZO+&+o7HC8M{5n$F(t>+e4;Zwq)xp)$Pq%_lEJ~ko29y9nmX4ft zQtM;)Xw7KFYf7~bd`e!oX4THZlh*W8u3yk&w$iTVoYZ=9+kK~|0SkNR`YipV%qQjP z`!4Ed>_rAQpQlucZqhkjznN{OY!_3Xc<{fOP280IPZ%UIH*AFvyWtH#RP?V>$s)E@nQ^6`a1QqQx}P3`29T;~#z zbnsc}BQ9~&;~xVWx`m*L{Q-7kSCfwwtklvGcxv|8**edV+{*j&VK=3X(xahi_q5Cq zLHeq8!?+mQFl#ieze@R*&zn-oyoWa2(rwSrf?fMr!K_(zZL2es4#lOQcHiRXl{QIR z)?<7rJ3%OK$Nyw!M2+&j+yF|6WAjBslG`zE)8Payi@WCvEPjS)JNShIFR z$okQbv`$jpStQ27R=nEdkrt#7$YbAi{XKPO_q?sos)_7Tr`LDjsp~R@FFP=|F3JHu zUl%{@yK`n?cdA8^p{$cjnR7Vd^JySO*<2oY*hSLcJGy${Ush|!*$!*R&5k=AKXzch z^^=63oWl#(_ZLjV`__jB40J0ho#;mgA~^o_S;}+NIrR4u)K zo;|3RUOdlz*ca*d_D9yW=XvA;1>}z7pj|oY591-RsLUE;3tz^^VDCMs+I>Q|PB{-O zlU-mQSca3=BWe$xQ2HZW=p8nETpsMQ`jH`zUh@cFlHZE<>EthnOGG{B_cq^7o|~Ka z{RXPQ4IBr4ojlW%(H4L^HjIq;=X}HS3@B|SG7}$5Dv3-y%Xd!-Ww6rNO;ZSz*7O32Pmz9j1CxcMNPfL~%`wD8wX{Nu(z z;grqz&Bne$i5N#HLWKiEaiU^wUsL+mU1nh=^2{DunoXURGdL0k_63>>?Jw1OODSHq_lzbca zwsY7He0kHDm~|iBH%yv+pHh^r&%c^#A;Cf*Ce@dCOW*%6^CkTzFWi6g8J8JZDQE;hu4L>@UOa8M8id2t@b`P5; z$OC3q#1_5ENk4TJpWc!jIU^99A^0P$_SDt(B%5b*@?c6m&K6O-bue{p_+ae4HOTYF zZ}d9zBcfdjq;xzUzBO@<`Rn#^Hr5N_&CySOG=Ke{8PdL2(jjdq7F+$`LZ=(PwtDNs z$tRQ#u$*wcfpXQCRz(SHZYHx;AjEliCV8K+xtPp`)g=CWYmCbaZZf*_;%oe9>oCDs z9JnprXTx>r{_E*Ke$x&X$7-Q;1Kz$ZL3s!lYXfYq(%9?zVvTo+TYGQ~)&$#}rEuUi zA&~C_`99Df)zCmk*bFUIVNfV-be8hCq)G}EKV6qT{+2yPMb}tPlQySnf%Mnw?R^0e zvNtp_(31$CtmzN6s9BaL+B6uC^L0H$ z(WC=*@w$Ess+tYWGoblJOi)xt{RcFai8(Htrsmaurpbg#+`7G=`}{SC=~E~D73XZk zziclRKK=$%AMO!|@IN2U^Eq@qi+!mM+2F$-I+-Qi@GV}sy7I1w4xHgPOOhs>*mzhg!g{#M3~mlX~MoA2RH@qM63`G_&8x?zumoM3bPk8RG{F3cJ^*Z4gZM1c9ixmB7MZ|hUFp% zTM+@aOak2fVFK8dMCl2*_+x4K$lLtS(>_o$%F(y^z9_$}K5mb~?nB_A^i3wU&E`Q~;!TfvATeiZY6Rx{wlC88jTV?@8 zO4O|lx~sJ(zz{<4#)+tOv`9ro7sIvDsi;7JKFx^(^Vw-V`yETQtgPKweNqNCMay;fznxex?JM;2*Y@B= zyC=K9?Y4?b1W5|%+iqO5Tj3e9>M>0K4V;Vc)?ys?Ng~+o7eG1$GoM6AhdtLm^g#nG z>$r_aHI@M=;aD|+zx=3FBPAhV2z%g5pNtnyzK31+jG4B)lZs)Jk*Th2);<2JNMuXa zj}LpJ8EKZJ)^qzp-&8gxp#IhwHj*8UYPMaZ0?HTpIPM&#_s9c2 z(MBU_htCbdBuuqz1dFmLy&o3r?Hl5w(^_;H%A_Qg(V8JD!mJvH-l)?9aq`~rg28yt z-uZ%l`0Kp`fPVkAx2o@?mvns`UZFr2=G-PKdcqZr;`YNOPUiRTkw~{?JC9eo;?{j( z!Tk!29a3|V0BpUR%QvK)P(w(Vp7;&h_mM%d9j%81$Wx67aF1XC5}CKu59q*g`y-=c z7gDXD*9#k6u9_8(x(YCHy!Qqnycs!$$q7Nv&&3QI(Hat5F~ANzyNKeu}@#nddpr_pp}T zcs8ePb#vrUJW9D%7`hHuD2EHTL}8mUB6$DALt0tu=V3HqNFRGh6KYvO5PFwQSd6AS zl|SWWE_f~q6fW9{*P#i*QP1#EG%gev(ga_loc zPspA)O_T_q2;;!T^iWzPoP2cCy3gq2!TN!LIGPG{PV~J^?|6=rsWfHaZxLuXQ%)$% zAJXNDYQmzV5;~+d=hK9={kg(KNZtFKTn2Crz}2Jw;O4IN?5^|y%F_PaV&`OQu)nHI z`0f{cLN&M|t&3ioiD&1I6E2^K3vwf;PkdQ- z!~&M;v_GnimQ`tg&=X&lAE6&g^&hAHLHnI9&8=6irLLM-iQe0uRHx81%EyJ=$(gB zpfEFlXdHUyE&K2w(e~Wflt!yXLAvM-&o$;00pnCYhK50Nd)>td!XW!o_!- zBF_Y#iXKq4URC>WDGJ<-*Sik08Oh<7cybDkJ(TG^8KO`FI;56;OW3jl%16aiaaxdz&;mmr(E8FMEqz?x9x-^TzdjorG;i=L-TZBwT2cMJesi=t z(mGp&C>H;HHo70%Wh9dh2s(U@@@*d;Fh2AlD-IAHwDBBmoR@F>G2giDfN?vX)DbG2 z5QAkM>G8!gUcW!6->LfcBb9FVCqIQD*L6qdqfH;`D5qDGru8PFH6J{gg2YpLv8^LQ z@G1Vaqo1cv`MLJfgB=NtZaRoPk)D%^maKY*r=08Cr{5Dcr_kb;Qd}xqLSxSv#hOd6 za?AvmJRPHR$-;iK@tJcohdk{x0^g1`WVkSo3aYkH33U2fnWT#?r(&L|N>_T^u7sFT zg=H>EV0*wyKkEFz0r>haLfca_p`e|*tBKZG9z74-?2-#kw%29>SY7mMr!9g=CeF-E^P&qq@(a0_u77fJGGb zP`7z*C1dYK-|4famsQ!w2ZhEgxDt9%9Q)TpX%=d6l$3myKBmYnCYN55SXP3Oi~v#Q z+}GTkI2L^JVj1QNG)YNyGJic|CGeOF@sW!IiprfsRF!t;)&wuBP*J-*`G7V=!Gbqc zKPMUITo~jkKFOCa9Po2Ig_|ynR^Bf%{ic@MJkfBeI|HbX&9SS~YA6A#T7kf$?Ds%m z_j=%DcYEMzcYfew7b2Hpp^#j6nKw&8igVz~yPj{s^pK)BAr$`Cn+Gw7po^`cA zss=x978jiQePxd$;>!Lii%T5R>u~C6vEpJ%Xq@up1qsY8Sl*5N+oWDOR(G4bUca^v zK7TQHI1%{=Uel)t%lZ_7a`}~3EC;u&ocm~Np~(uiHYc;78l;$A`zcKqm=7g5aTETe zbBJ*9$JpFCqpu|WR%!4WO0E2u(tk9F9gE&7Eaf19MO!{}ST5??I$8MWVWrS(DBjUE zLKqr=k9GBP?mrvVOn1iLcEt(jO~UWGqNZnGFmg?1yTr2aRgO(4SICc~kJ00SIgGxK zlKH~pzhdXOw_p!f#J%OlE$*chdKsjyScRu7DJt$W9vK!b!KW6ANK8%YASk(rE2-+*O7*QtCLlf<2_4G^a@8Lx(F;Uj+M6v}INOhe||S z=I8|kh2^jDpiRtVwb1U&4V}Q^krPrz`}PrvS(n+z7#1+{@@#2aw$SMcssUS8m#=H)CdMs`|F$rH_-kp zzToE)lfUYChyR@0Gx!RZ9ft563S4d<2P1>m<{~Z09p)_u^vYo7=2;#Hh@u;2S8-lc zu}54Xaj$T7=lUk8zp3c-uITcvuzFWq@~*h-U2(;`;;J{dJ?dJSgSW)JRdC}oD+}NI z{;nX;cN%45a*gDz6Fm9g1m1L6>N^u6N?RZcA4Eat+?EJjd3k^zIhoR03~-KoxRs^T z5CjS1JC_rD!xr4Dl9-0V)$8AB)XDt{aGxvv=PwxiOn($N=|9D7`ZI->@rZG^t?B1* z`u7;@^)~0~Kg$74O5*w_-v$d4n@_zrsAw0xo~67Eha4UzDvDLn)v;_r>GXHQkj+b) zhfA*%Du?PV#iDKTDTF=Bk^;zk!tP$ZtBn8NM|uvUxatFj8&pJXEw$Bf7?kQm+V8QY zNVJWcD`dW~xmO#?_y_+r9A+uv7J(PTy_z!wAu0klP>5}$Sa>fy^~LwNy0(n}$;UET zv`y-H2wTCgS;r{LdF6BcLGFxxE&}J-pXzeHM;}LVM!ks=v7?aYJv`&feaa0-`umia zZPEN$#>@P;0?ufH9-ATKYng_ZqRQ&FGCs@KGM(Fv$~w#VY+uU^uDI-4887#>qyl!U zjNjyInF-i^n3}I;7GOWa*nJmT(g1x?#`pMIW^>-C%%z-v?rWLDF=gK6e3qXj1F+z7 zKHJZd30PD)FZZ*|11z?j-{fbRj|ZSH_}_diGMHD$A^t;O%L)$k(k)*HDG!wMZ`fbe zXi`$m@I0^{q7e}9=2(z^tcv0Q1_LxW7O3VaZCPfh($tpoyM5UL{5kiX;TB~v+*Z3* zH|sUDWx1i1X@l-+jZ~+h1xl|uN)?7G<`z7Hx94H!JnFt-2*S6hpZ|O8gSzpfY!^5E)PNw3< z73fn6DyxS=BVWrRF6uzpM5y+&Eato^YNsIqY39PWH2$0`L^10V3bCn5=r}x!;d_nR zQk-Ih0)eAoRXHC61xDJkhr8SAWahj;pE=;i$o=~;8~yQ5c$zn3nCi`F20+0et^hAh zD>B5T;jU}LCD=dvzF~6q{Zd3qMKAKvj{!6i&?P>W zGJ2VhCGWJ%$6V;eNPYS+q>p7v>5PiDRxG}rB>EsNA5Xfj7tYv>f4CkTFr&@5=WU$h z0?jdRiC~KTxpg+|`fZZ%4?Ujt?PKLa&te=ML7>_oNGO7}nG?XVU>?SZK}uz>R@xZR zlm;R?mCIZUQSuA=aEP*lI%` z0GevU5&*c`u*{mXhl?UhOxixZEJoT1b=)W4DxV0p0$bu_XqV(0EXjZ3)kIdTnMOBN~oa99w z?+CMq$Fl0`WT$gwnln!360zYBHzTu3oz5FC>TAWBRW--4iE3&3$pvk2s@Cre#+kR{ zl#_;2taLA;2E5(9_h#w9Pafg^1Bxg`b<&5N-hWgvWya%aYD!JqbV@Bm!QGi6U5#{S zQKggRz;8JUvsACxQPeQ4+hP1M6~w35$8ghLZ^*w{YrH@z>>hWI{>APo^5v> zu*9i8?kx%dua$(b;=gWR_x9gyt#mO91Z>EV9D`&v?!GftDKOV}B(O? z{szNeY(4jJer*NyG1x2jH?dU3Ieo2`9(}@6)-dmcpf2E3?v$YJ$SFgG_9h^8CoL_e zw9|sR4*26ww?!k$s$Yq~8O&18&*^W?M9i7yE6r>G(p+d3)O`zI6SNIh#UE@c?H(i$ zW)5j;zk5y~BodqGd!A0h4HKFUJ)A5OPEE$QA4dz*5}W>hoFx=Yz_OlLAJJDw#RcW1 z>Z48t`OQ#?citcpiYU;$A|*~uL% zB@MLY5*HBW>WBn<2+mI3E&;N_*+2xizC4Iov+PNra@bevrbS!6c=%qgsr0|9!vU^_5{)D16NT zQwhQKz5RrQH@@CGQs|wHCC`S5hCA2e4f&|u52p_1jgo$H&a?7 zVcR92zv@DdnoqQYyb2Zo>D#m1Cy6J&k-Fgd&-+fA74mbX*zRw#hHP*>S3^nI2K{>Z zv)Xm?XQi>bH4D+~qt>>+&lr6MTJ1LR6JIpV`qJhkb?HDOt-k^C_AG9GK3aVItn>r? z^7*vD-`g(vygo90=a(NLTZZz|C7(Caw{1A>m;27&IUK0q%lb7WRyX9$>A%{IAsh0t zp0Pq-!er&*U4Li@JqtxOA3S6IdFfjOf&Wm616BWrdO6TSocbbMnB2E%-3yCwY;XY6>S>X_>WbOkn|mkcUEc zQV2L6Y@U|EhK+-ZA@*mLk~K|vodXdBgJ0()an3{up7UFnaJDyA{FXRA@_~L`Y{`W* z=!J#EAyO$=ICtHcy(uQ&U+2k*jr=k`g!~NRjU967+iPAF1M1fRz4t2o;J2VKsIX+z z9kYp~+{Hae5LfH7s+iR|A%&E~zOROi{A6kIxh4~MzMPjGb2QO!6tEgG389e&4dZRu z{EFRzsF|#anfW?Lu8){l%1i3~nEyB~brN3pIzIZ<1#ZK@65Y_WhCAud79`jN zi(?7H`mg|C=(zE9KmVXb#istRb3(R`0rkTh=i@cexaalM;FV!iL*k3cuXET58A>pq z9dQOxd)!Jq<@dq9lJOw2k--L72o0>tMmCU6SLa29-(x?M16S&nMu*IQg~tkwCU z^oY$f3yeKFUO?_c)g^wlL>$YMKn3R?$2QGP{y7jWlN=`&kONQfV0Sn1c4vzdi1kkR z;vcg@ZeBT-mmuwgbeN(W2(gr?;|b}lD>&}Yk?x&WLe@T{A1b6bui*86&hQ+3HA|kD zB`I__YzboVSAXshRN$d+LOf1?eL^UGFn=(Z>j(a#v*ifZQ}jgxhoO`@CBPgmnZAl>|b=?OAt;Ar$9Y@QA70yra9eCDsg!e{~h<*yLuSX+N?r2xCXwFwsD z@850`497G7-Y@L7;jX_I22_=o@yBfJg<~-?#rzVCY`>j#T!6uUOg$LhbNuJWN2} zw+RXLL!WB*`>$^p?ar5c{wzRU5Tg+t-I2g8jgz3i>fGGGG{yK$S;k8V$Fnz$@bOa5 zLEGPa_sIaw37Jq<%+5yd6!RCj?kUCZ z-i3vw#{W|5L$QMVlj}lF0bdC#Jg-Hkg}T=54CL{Me}@iF>UX92_ZRKzHgfM}Cy5Jc zp5^R-M0OvfQicFWm{HX{`l=P=NNqG-@(G|UijRF?l6S>$;ckZ+&6OF!Y|J)zm{gq)!d(PXQo~|y``&HY5 zrQLW{iC$Jhn|HI{z5|DUoN0wyodPtzw#h#xIN}-N+O|A}4=A9-GL}=MmQZn=3|t@~ zImOh{P8B7GzYR^QQtwSg*vo&mMzep}@!LY&T{C1R7b6gC+q@qFo8v{54?MvL;hFT0 zS+2+)MS_A^|;09`EAjrszKG@Vl?LYm5hzUBufTMkiGM zF6;Qit8LX?<6AHsR?q~bf>iSef)>hYk$HrEr^pNGgn}61n{KYZP;fvPn9Ny(0$Mm= z5I0&R7%Wu!aBD?^4Z_qloJ}McuUxf=s!v@)xu|CzaNkpsqo$_n_Pf-Y-n^F(jst8D z;v(W-UG+9ekJd>aqL2;Fyg>Q}Nv|Wtt+@DCYCrzHC7&A;U4Gi`VIq&3p$#vT%BY%< z=yIw*=er-GB%|~T0Ne}N;K9F#D!36-K*P_s=YWSf41nPz4L53RPzjC*D(CtKmzO3| zY=M)CGl&Hdp0N&|3p)K1U(<^$?v_~4SNRQPA{+0R*)K8=c$V+g(foX`u!=0l27frQ!n7?>q8gteZG* zXF;gIor`xCU_mn1>MWSOJbIZQ!ZCCG&`Ej2O0g}wVYAroMw57C{vCLm^j>bA$=B*< z4>wg?Gi^Q~iiH?Fo&NDej+(X?xv?np0Rb*_9%{wr(a7ESYdc{V^t3(Ajpf$52&BrN zb6*cavWT<7EaHzoAwh|@KYaSwiPZl{mWv#R{s}>h-0*?_REbQ#D%!|%0tKGMXzgBd zn@6g?FJi>>9C_&sFz6~y!1zR^?PkUCm14W=RY#7!4xcTW6b;#8+vv2{vOEY`*-<2X zw04ajV$4poZ%x1G{3`Odb3%oz@m~R+SH#$zO^y0}B+RZa9l>yiB!W!;aX(W%x^1b2{g7DNLeqrw zCe8gN5s)Fc0L7?v93@lznO?iYZQ&qsj7iBC5X zS<;31R>B?w)IZ!Ru!mU1jUf;t=8VG%)m~N+ColTKChkZG$?8Z*{68IBdt4J&_Me*s z67nFt6P^ws5D-*eic&2qD!!4T)YocSLD8r{wTiEB2OT9KCS*)5Btm!?As`0Sin?OZ zmP*$3w{iX0-SzXax>{Y^Uu#EeTgA2PjsHx}y>n;o&CI>$+;hI?J8ImGAqg#-g--i{ zq|l<7jb0q4X}|Y=M*t3f*cMGfKlp)!h0~MqIuPNIffR=hKxZ}e$X4B!egqG6!4X3s zGBUvj9&=&BfyZt;&s|CM`8cWw=Y&PEq`hstJM6x_3NUEd+;W#nyKOAM5pZ z#~Zly>v*M*TWSm_hXW6u;OXLChey4h4RI&1f)nd__Vy&HhV0cbJDZt(;Y~U+T!(g) zpcx`C#cw)@gumiR7>xFdKs1Mc99cy`!CA5nxkMnLXx`2s_#{MLqM#KX&eOPBuT+R< zegMtK7fsUx_yvhYKu)V8U)FdLSq*%GyYx;Y{LJJyv`@TH-D(rElKM7bcgHl}U>JK4 zWs5;=KvBWzX7B1~k(U4zp?Qt&h(Tsna09Jyyn-UUSo5*?b*$ren(UbF+eC4g&y0-U zMx4jK_|LW0fW;hGzqmHZmzilpWhc;w5-=gLNI$~NZu(c)$=0C)Ry@RE1w$2Br9OmV zju9B-`rza{M{dkKznN7~kxx8BBn?u_@|fE%CBD0K)3jk6Yo zRWeC1zWt9e+#Ab!dt+t8t)3TnfAJfu8peM0b+U9SeiiWBQaJ{;El(XBM*uFn6~H65 zr5*V1PgPuu**+rIZhjZ#wQ1y_v6X`@nID7QcDbcMYJ7vMeaLkUxYE)K zy;Td0E?!I+y!F(83kqxo4c_y1F5P;__e<5KK_1SI^t1D5fNdj`*m<`7hfQR$0b5Ah z#{P3_E;eU)TgY(>M%+fTLfhn46O?1)r)IwIDZO}ihr!OphEGja8|)-`)PREpM8%=%`kM?4 zM>S0k87L($Fifq{FR4KX@2FAU5u-wO8YQ5@5Regyks9(Hqj?m-Y%?*^4?|%ZypL6G z2uNAZ`CJH1DL1sGZlT_Uj$8NDQU<2l+RBs{==urZS>yZ=hiIO04HJ7-BPtW+Y|~K& zw2?ylb=1_3>bC3~T9~B>uW7fIt990_tvah>D~bpOvA+3jl!uvwW`%;uoIF0N3*9bQNV5QK%SZlUU}AJ+_FanYoHykz9fO#FYCE66 zL7N!l2uyT}{n4s0Fpe~vRPw5`CRLY9&8nK_gpG_iepTY%j@?;on}D2!8pA-mDj_ih zT9+K8q}CPwmIl_BtZeMr8f4MkJ4JMCI`~34`Y6?viV>*qaIdr1nyu4dJ?`myR6~i; z)@s@jJFuy`uBG9-uzIDeqb&{^>wU@B$aT77GkrJdo|oC5>S|?GQ^$sIWbC^}v`Pk| zV^$;V0ch^IK%=UgOl!nq?LsD{n#n8t^&i?P=(r3hBO)7Bqw4lg#&z3eu9>U~@3K^h z#)?T$0@2@PAiio>ltmYosMfn16uDoYyoaV;>la~-+lQTh8$OJd!F_2u9cEWM4^c#C zb<RyXJb&R6=w4o~2tRELcI)ANTM zl;&&22_k&c%FPtg1iJLa+0a&4K15SX56A2E_-4J=#1V-`ccjoC^&Qly~k?cH0bXSy}T?1EE*(nZMQ+(g# z!}s!1%$su^c8R}~AOeZtkWgDf)=etBW7Etl=vE|9aAPt(=w&3>lFo%o_MNuoFE$!i zUaj|OA_#N#mUAY85KH`tmbu&?`w1D*NXWwpB7%q{I-`gm2lP?kD)8ImQ%f}k!ySbK zMKPcPa7vSrdmOgUX%hz6aUd9o#B<(ez_qKX&XUiOe=JzdCtw#&@9IKju^_Ulf)?Cc zjX%rcx_qXK79L}7lO4T=G?!dmr4)LSe1tYEJ;J;^F8(-$hZ7OeBiqo3pJRs-wqc^X zUGVDPPc_yBa^}f%CI2424e08^s;WMb?;_qyqmYlM{Mi+-p5z}>@Si8}yRf1kn&OR* zh7oXK)X*HSC?jCdP@-3o6HqXeh8KY&oTxt`vNas*=nZfdb$78sm%6)4X_>OO&2;a2 zYOjLtjEA|fHkNvnI;t+_p#2JLdPYY23HL9C$@9PFoZ`6Oc%(J`giX!YV!Bo;UyEs5 zNN^BC@8453dQaUyCQ~CLn4|G3_e+h&h0n9e4vs#7uf-#E&E#wKBwy>o7wZ%G+E(Di zC3iX|J$mD6U_^7}vKXf+;;T(>G*?pj7`XTC2;x!nt;fHFFF1ztLUC5vvQ8T5;=niw zQiLCCAVvqv`PQ6W=zJVV2)I#v;2;&=Q(f;=@U@sVf_{ht$)cwD*jOL&M4+fLq*Q{$ zpoQ{7D7K5skKo%I>_bLRg&HlTuMv-&2>&7Q5 zP^S{4SN*lm!1GewOErgx_kRUD=$>5kS$N-D8PLu-wA=WCvsdoPwkbUYXzcdZwL5SOC+d(>oBzv557 zMW^D#Pl;RLN$&lO>xnh5FZ%}FjtBEWFN#k9$yNN_O%ot~)z~ClC(l&GusJ6xVtUJC z@T8l3whKp4-n;khT5ptv_!Y;0QEHhUeZ2Snxef7W@0a2RM@6$W#2j+Y(41vUA1-~* z=?gixao1QAb_#zu3xm!b>c^7hEFKpv7=(OFV$mtHY&@ci`52!4TLK6d?pfs035buZ z=_%-C0?3Y(l^g`x!|704c6S^EdoPN34O@0O$L&6`oimW90%m{T4f5`v=Oqt3o(x#- zcpt7#1#>_6olB)IzzNFB<`G2SrGd?JP39oNq9X`!^}zCe5u6>+fZk057yWK*6+hqo z?ezGQ$dwHu;+C#DpL>`;^|Za9nBb2QxP75@e)5^DtMOq})rCd2mVGnP+iVaXY^%t- z*^!iVgp%B>rUL`bWhr)~`T(TF$+Dn-rsvoPo%O(%>vQUp$X@5pCD64*)RyzdEMQ4L zs`zMN%ZH%AN8aooM{v2)apNu+nwP~9!kO&rf1(n_cLR5F0pRj1nmwn`Kj&f1xit4J z3djQ?u}^S4$Ja+Jk_e?DsX!`a*#NfOz%J|av@Fc*^gA+;mj@t>q=KBDth{J=$5$qU?I)~@miB)E0EdeQ#r5I)@C8z|+s}Ab zlyTY;JFBiGKKVrb&=C3X|4L)Op?|jhZI|dK>i!5g0*vDF;};HWIj-MCQ*Uo5e;+6= z1Uo?tvu9}(JX4kO7i!F`*n5!)^R*f?<0yJ;?A*@0G4ygdNEcgx@Oby^Py3jRSyu+q zR)BfHZ(G(^=CT%MS0#c)V58W`U4GuUrQm#zYSAEiSp>3YnaL?8{ikL$>4<*CLCR%~ zKC0h;y#B%KvHw_d(`en;*kfy9&8f0AKkD9%yQ%8vKEUwRCIrT0?6g6

!!oh8LOk%kFn*^nzEi08iMf99@GfEN9$`~J^zIe5>=QoR zFD!2o=WG>y9|{`|3j15dm4}4HkAz1)7C!D3F8fq?@-v~TPq_69;p}0-_NB3ab4P{X zF=1K1U_L2C4G3SI)|QEz?)_f)a!{Bb68<A-fO~mw_4l&Xte#nc8Al47@r%FZDQM<8HTv#o(Yoc2E;Wf2l zY`usX``;C94dN4N{LQ*+kJhXR z?Y^o#;v0L!H9K01x#eB*Nqd+>+`2=IAi^;sq_9Go4Su{(&k6BP;jBxjDi%^j;is>J zOOhbU!qpz(t5RWgnebz|@bp<>@pr=8s?bv@JXbBu@Cr3Q3fp|b-F{(WtV)v zx{rjD9}5qDBGi5=416X$_POw%F9hEaVb;Io&|YiT*TR#>g@hBrP=Uxa~Qg&DsIfBYtt{98D5QHc9P==f7u^}*r+n3(k3|Jsql~6NqK#M>0sAanLngMkK+AUEnp1)mB~aXizKom~-|@LBGhG z&X}#$3$q4(i)CK@Kx%&FoPBumXSGu{>@&B7Xa4qm=ArNRnKL`TZ+M*8$A;`ThU|}p z>?CC06tZs(*}nf^Y{YQJ^Y~Z(w6$8}Dxp|HYYfk7FDVI#CFM*USR^V~_fd7*1 z6dtF6-AEsm8mPlv<;{JHY4rdyNkG;PC|vF=Tv#flu>Pl91vYA!@a2vujsi(bK(-D* zolfeAqJRkkC3zrXmJ?VQW*vb5#2_4<9%hRz;BKO0#qu!oVR6%Ct5|;f;ecxt7$Wo| zouyov$H(Mu<@?m=U^jIjF6N8G6!YoBa5Un`!=w+y_E~T#mR3bXTr&Oj*I$5&%C$E2 z4k;hAvIufg#StoD9>&5jyaSsh!3UKRT$nUV9Zd#w_PG&hbC&SQxRA=qvFg^uM*won zw0V(%#HAec6`+nX;7Fm=X>?--jWP+)s;M8Bd1mg<2GcmQN0;vK8eK=k}@=EYofMP8<+X?B`(vJjm~mzeZHzUC(R}= zD~Z93+>QC~rW1K87mME&5qZJp0F^8_zeF7k@*5E(6?+yXjP9L5hiG12?EqSp(m5f6 zSOF;t~T@@Gr@LpmkAl5y53U^+kc5Fw4Ki7z}&_F+hCVp)HclRGD%w$w9Yi zRQ~5R#K(Mpp@ymJ=jz_P8NoO>`B)pc8ft(HQLab1VOntq)N>*_$hf9`zN84x@1C+zS;NdRqPSPIb@VwcrlSy6LWgFcE}|7>4?(UdI}a7M4bdFm-$d!k*hmi6iw zoL5hshL>!deX(HN>89X|73e^jsHs;V{tnA=*4Sh266G!`w^d;R4g6b&0|`XEuB}X2 z*w#j?dkppsu2aQwX7*NusjwZ8VqXevwoXR^2-+Q+=72upDi+;&O1bZHp-){hk@u>m zdnot=z!^?H{PsMmJl4`yMVKU`7x-n}!E4!zjk_E6RCvpod2U>+erc=3eNz|e;|IQ} zQwn4e+On33^yneRSLLjk_b9E4dBq4%aFx^%ffxu_2(xO$Pa{bcGqEJtGB(udt8?go zFymZ1y0P=K96GF!&;Nv9{)g1ScX4p3k&kf}68;NL_ut@-rzaeU!!sDK$MQCHbJg;PDo zr7k&JYLoRcVIJv~7gm;U3O523mHdZpU3sxZnQ5z3o^Q(~!N)1Q$rmu9#N=$&2Px@P zCbgI_Uj$ol46Ao!A|@ww$*Y^|ub)cG8slo=S~4qI^(N}#?+*>7?0yR_71w z*OMrwdga~psQUa&irMP*>suQr=N&!rv8$zsKi%Ti51EJfvuEnmm!6~>TdGWxHo2>! zCv7TyBoLVX_Q;cx14 zn!xgY$2`(3aN@Q1t1z?ffQ!~ zruEPoegDiwG_Zv^ChNmvaze&*;Z&>B7E|O{tllD+&6aZR7}GGqdVS1)x9N=axYR^> zaB(HKIjF2W#N#I^#6LccwGf`^3aZQRb9HJF7J*|5A0%mQ{54Eby#rG0w3hK~Q15Si z?Gz7v2-8C%i{%Fsk@^7N%BgF~S;}{-_nodE)2vKt1j~r(gwM*H6Y8?!y^HcN z`!I7rCz88LA`uMvKq{pChJm_VXzWPJpL6Y~q(0F&cti&!AT16|1%%0Ia01Po7X6hj7(eIcRM`SYoh-@1VX; z>Psj7@X2M((a~l2X-yoala1xT0M!)Yz2ATBlL!XSLJXcm1a?FQ2UjOUde3>mSy+?- zDQYUgGZ_l4@yd%|C!*{UY0U+>T5N301J^TdJ;2`<)Xz#xaZooK`u}HHhiUZ{It*pc zGa*@vVvoWUV(28q=>^M;r3xQeg%z2 zhNsV;y%DAi>-M<;=~itqtS*d#)FNL~l1k)prFnou_*#OvU!qdos)Z-~m)Ytd?tss; z){G`@yOqmZa%t;Z^Jx|kL}E$ljD^Iozu++#Mm4b_ub6U?R#2@A7wk&p8Y6LK{1j_- zMO8+hI z&{%+l05-1i=`@?`)8&_+PBw5A?$r&XDFH-SVHT@_v>XOs0Q{y?C2^cB1~c|5VYN^l zQXZibwb|DZx*TjhoZ(t<6LLe2i~hFjI%3Yr2dNWbg=!G zA=TFh5uscX5yp}s_!WsZ`L;cXN+Y-~dMv+hwdzPM8n}w+t6 zx4sQ>^W)um-Q;lG(zMZFIwS(59*G=~E)Ijh3dT{VGL@|)edLAON0&HFdj zT|!I|6;t%)W6>vrgr_;1>h%*xj1A0h+Ft0kGN|LO?CE#dmLnN5LFAhL?6 zN$-$~%g!s0@owJ2wkp@&M=I4Po+4$6xU))czO|yIakARMKj7ArN$_%@2o4~q&wvm< z@E_-2W(OMNps;-FsCv)!_AbhSh>lLBXeq?6?{`Oz)ul}s0XWfV4F<<%NWs))q=7ok z_j`n)5L3+6$kv0XJ93OhoXqK?r96B1|MKb z7K0g*F_3tbH=j4BGP-nc(y8LUOB=lFA z-!3qAxRPJfs9S+!x`R6_gQXS%RRcrTQ2P2kr0Hr3C?0;0E&E;sQsd%5cL0FF{g-I? zM8a08V~@8`=fzg$WTiT5H3ipM1m)_=d{$m?C&_?;h%1GY4Y7{G+PbI%D}r7IlfjyA zT|51>A(7=w}Wt)gqzl2s2a$-R6x`#UKrI>O5V3IYo4KtW4D}zv}FBmEnNiERS0{e3B>AH9f9wYfDrP6fhBWUd$DE1=AxIW52Nj6%& zipnp>kQ^wWLdj~N3n>Rb!pxHK);oF|?3GTXKKv3^HG;@#GYQBJjqJZE$;d3#WDv#j zINRipc!6S7$#xHOUt2>ci9_(+389xU32-dP=0E_O6qPHFyz5m27#atZp5wwOH#pJ> z@V}w?6EOD`+x4@!n`~hO*N%`oYz-4|?LbYzGusVl_W%vFz{!V4;ibSD4r8iVK-nm2 zmY}mnTQ;sj)1dl=@`mnQ2SZ=+V!^kbD8oLbaFWCv|6O_JBlH0~gRf!#yfkpZ!xXgY zAqbb0gr0J39;SY~9>Pqegi>gH+%1j50{aJ23cSeb)`rkJ!jIje1KuodZ!m_b1W^8M zAHPa8umW~n!W!Y1!rA8XA-LcsBgz4vv1aAl4(Xlnz9-|z`^lYvSILU=8^@b3jPymAkGF+DM`sr@I*0*% zZQ=Ym2x#!u&CrR)xc+4}Y?upkbahZyB&ZV)2$YZSIw`dv2kL}*cgC=9j@%I*t5kB7Tk>JPHed%aWa=R5ns7PUw6-kM4Z63MbAK)(9r7 z(Vi$ZVL>KWz*It=jjvbno#&j`A{MS?CZQA#VUty2X1=G_-?pT*D(6fR^FVQ+vORpi zZkTB0`uQZ6YGYpU0GBzNH=;bonLu_vE4lwA9`p}y0zIH#y+n}2#K-q(Z5e$qQqc7&~3BX4Z4 z+8a{r+HBgTG{p&+ma$4IUo&Ft?dfj4fqFYCu76PNpFpmj&-T1W-8VBck0M48awiZo z<_m$tYT`Ci8g0~Qp}OcSb*=&jWI?0=2l@13kL3W^y$zKW(Gd4$oMRG*QD>E?|5Q(du$La*UKkE>5@NO->|y+=oaxJ0s!J7X+ba zZS_}Q!-5%|8BhbGd{0?ye2Pt(b{`u>6~QQNYT!!3;)&!A^Mpa^x$Di&C_+8;tBIwu z@ts=*lOS0Rf2=10o?itynb^GkB}YWPhaokAoK)LJbN=B?B*wa6AR=d2x$yycP!Ztl zcwyEI$xr8~7u)Pjqtq73YFCfr5~0hZs7E3bm@^4mcIph7lQtHB8`Juh!7L3thW1U6hXn{&pmQyUb0L#v0)@1pHIF z`9tAuEH_%gNlT)Gx}uArws`Hv7O(=%lk2B8Wzi}~Kl5yl?yiCYV1E8chk|nC*{hhr zEqdm6O*sTbK5$WE&=Kr`qo9O(D~$>|kP$o8y9LQn5%ve+XEWKvgm2X|Biqf7UR)C! z(AL#M-vWcc^ii(~Mh)XT)9kWE&M6*^!A5(^nKumOquzWxl~*Uvrrkbf!Zy8S9GMJU zz57E{#G%lQ=%KSdCQc4!uIdhnwO=@NOQ0AW(+Ra|qZ-U3;vbUbp(B9+<(oVf*SB?{ z0=AsFVLwoIlQwTQP%30)u$+0UMGv_vJ(lPLflxZj#~!MKJn!13zneY@_qhA(4$MRq z=)@CsN59Ixk;eL}m<{+v$lb#K-mIsSE!uaN|G~W6qPxe2LeK_)x8;m^n@+6L4u%(~ zspwc(2K*g!rjS=O=v zCA!3)JQ#Bs_2vuI6~|irSKL~W zrFfWGG!SZ0@0@QJQRqbiN!&=>d{iqK4CzH_uM>fLKAnf61hYW7Iw4McGW9S2cOEP& zz;^~XWR(tMX`|j;Amcm-^?IonssdxBK-4Y(%+yj^z{&aF!hJLp{3_%>0G+(~<#NV< zc=rdAiKhrpvS1f)gK|d^=?q53QhxCzFHEmt6o7Een=oyg#N5FJ5d6#H0%eAu>l&g! zsiUa{K831lrojF|%kyTe>=DYM=XOu<#a zOjrHwKBx{CG=LQ82Y$3QZm5O%G$?7PZo`;h{wc^L9D(x1T^4&e^A{U%zXS<4BQ}wK zKxZ(G{r4x)6t>**>=8)X5OpQ7a>fk^jnrS)5MgUI4+EXJ64-<5*a9e)8x5=iwdu`4 zoFoRrK;nHx;4Ndw2=E(NWmy0FkmIVA*^^?ev3ykx^SF^X8K5=2v<6CVY=N%a**pwy z-A+-q5(B&=zjz=WU*zK)D!_-|`%Dm?e5(BOD>Eech~Xv!>*7bKOX9POJYwzagmYL zH{C8DrB+uor8juDuuPtvW!^yXyt+f1w5Costf_#7@=(vHFEa{oK^oE^Z5m^OKZWp! z7=To5as}K}gqy%@Yz_2kw_I;a$8P|mp@m$^V)%dnNI);7>EMkyzBVdEQuT4nSYlG- zVCBh`)>$6z`qb*ei8bI?zIznBSqR7TNkAxBxYTB#a6S)b*}^DnL40Qvo5zBTnQRr= zWRfEm6E7%7NyXg@a8g9-lP1)Z$zo6N-IRq0X!Y}OA`ty7MoHV>2a2-#7m)O43A}q3 zD%6;X2+Z5w(<`ocjc_FPo7-;p1A(CA=+qteaq_ZQqlgTJZf+EpeW5Xr)CDu9&|$-$ zhT=heChGn!7}8D&o-hF?vBMN~e+1B=7&9-1@-Hr<#b-xfJLC@m7K_1rB+eeZKGu+< z;_4aL(l&oQH&pLQq#xcqr7tovENu#FnmVN~%oL_a)jV%g@+V(4=60&MCSUz*#ho{_ z)lLW>7a2A2vhic9^TyRyPfk7Xepr~<96g3aMu(+EL`0?y(PuJ=|ML40jk#qaxqT`8 zHd@?JKm7@}d}n>jwQ-uT)0FymvzTSYae`^N8icX#n{RHm@k zIOz+=iTTsL?g`3dosLmh;XzV9yyVcBzALmPWELGa5Z-b-i5!^Fk~9$RPTaF&cX2fQ zllVhp+*2oP-e)y8uf4G0Zqt`_eN$t}ls(be)|8R13mb-UPgu$s^B+0Qsib8d8NNk% zp73eIiHd_a_gyCcgZway%-U0dnOu2n&;H%ToA>1Yy5Z`W3mg8pyKi@KMeLnQdGi9& zGJL1D>;1{idB1L$g72{UIV7rn1Lel_`M;rg`&sK}6K#F8SXU7vs?u1yYUdX0X)f!f z#g)H))aSq5PB&{T-G$bv&2i8N4kkD{)Eb)kC)?|9pr7LUX|w@tmW%%LpxI@7_tc*( zWVIN^xj2*KUm-0~m@J9vtQ;CBHg#6!o+}>rQ}Mq$E9d=G{Pj=P;yZU$q6B^BdL6(U zm4o$34wZRWPuN4DF^e&e^kSNk>Ep;a^`$iT)*Ew#Kq*72Hii4fL~gnnnt%XwOqGI& zFKX8>a2x)W{@I$xYM_GSc!;tg>h?}c4G1A(%?lRj6 za>rh=Xs-Y0I{r^URqrDnS65Y&K)p!h%{&3nbenPdw?*tNfvy!r*P zj@0H;^^R8?29=ywYM}+d>!MekxB>Ywv8&>xIJvs0phU8u-##i{Y>i*(L$EdpqpyPV zTZ#Ht)E53fM+USIVg>}71riTzLkhinvTx>x<=SBh7Cn@TRj1mOb<{=0c!*>cFD*8i zn%h1he6Q&FMBMs`_`xUQflowcL2;m8xx*B!zw%Aw#9n^20fpw4HQS-lh{-0c`SMRI z3+*^seqO2ukWh$q1$wXmp2;n%zxR=i$dDdsMxn8fxuX)66oD#^c}h(g`!1>@k9sT| zEii4_{?Ww(PyIinYi^c5q=6WJ4%)fRV><8kzwatVPeTm zJ1Q0%AENOI2A<&A9E6ie5>+0a8gKP(Gx~V-u=vJd(RCP=NX;)V^K>DlZR68-#e*(} zBI=WvsMf$|lOpA=Exo2?VIPHEZz>I&7S`cmZV&^Z&ho|4gM*>aa5?k)Hq5<+rZv!o zP+bHm{D<;w8%ge0VtZxa;Mbx-MqKY7?GT2=`b}jZjg(HET|8J!xT9YW7PH)mlY^d9 z1EZAkKpV}w11&}g?6-hHd5Dn58?D9w2#qx(HzwaI4iDyjq_t@ z$I<%HDn@VrX#KkhM4r*Xvv^F z##y!gcaa-YCqfD^>EaG0TZk;BB3APE5ea_7oRctPuWZuYaERYtSCe_Qk_%cwlVJI$JO`*ri+}#L!^E#$o^E9m>2l6dofNU#z~JOS}Bpm(}FZ z#wRm;I`(GgoQ|H+ErK6t^y*cM#vq@k-G4bngq(xPAV;RtH?H>Ov)bdY`l9Vd7SGs^ z>WZaNMis=nAxG;HJMutqimWroARCZ~CmuVRsHCmD+gGOET2;mwml5vgl98;3)N=z^ zYU?z=Jco2NE+Spk8$()8)?YU{bIN<(4Wy8Zh)OIZFD&Uk`NWNXk-<=6R z2Rm;BAu=$Rc!#!u79rtixVzbiS|aP_NV4&nEnf}tnV)`zjOMk^`C*QpJ`EB$Vi9&p zVyl^y=PA%H!iVry=bd-l2`TMiZ-|=ejT3 zG-Ylk()MdU*^@ejc5sa2;EV|(b~%P{%%grCFv|*Xho>~S05BjK^f1|qp3~iaJr_YX zMo6YHISf;!=jalidkVzDG&CyukzEXPqRu@r({;+c>cTSt%IMp*_>@X$PLk+hVk-2Ul;UzrK^0u` ze@B-T5uQ|SP%QPE>?S7nG{O~`-wjz1*1HWq>Xn#B%7alH$Lv&Z-bzC~c<}PpDxE|p zu=UaHc>3qWnYvJ4Fp>h|qvELjQ(gtxwq!BaZ~7@QR_+0yArc| zcL0_DmC88P3Z|3_5F#7i9mqVm|0qHoi2N+s6Tn_A+!L_i!-?Gigi-E@aZz8EkNV)b zdZ`168N7P2F1#uen&?$Bn@+1!UnO0|``o25k8|xe68Fa+WQ~^5y^+sCv!Pxr*@pLB zb_ZBhH)!IpGS_hqRJtugAfIxE@<0x|GfOBPpUp`ZM$C6q(mRlyFouN4pQS!zP`t`j zZ`Tc?(hiPt0k15XX>Xz?s9awvqCAjKUlCefNS%>YUIl&3JXlc=&jyyWg=!ro%MN@t z#@nmRS2`!I zfO4X+7r2;(8RKoBbw-5isKmVSWni#b@7ej|(VtFO?2^U&WuTDtcb9XMEe&4=@Niez z=#f|~S&m>xsx80Q6>kJ;5ja7IR&*6FDPtBQRUoJPC1%f&fEjblCdwQ~ha&W93oFf} z{`R^=@RjL(JqJ&}k9Mj4qdYgvN#)A_`>@P%tsr>`u6X|Yw_kI9UYPS&*CLbE^jFtk zrbyF8ciiuFxA(6@teg~a%EHI1a>ik-sLKRp@s%0jF-5x+>B^bboLlYC%T*3{6f>eD znq~w=Pz?j!Cv{L_gp4u~b?ia$B+^Z}(1_^{?b)R)tgw*~$Ly=ij00H~T8)ynRNMg- z-(Pyqb9;N}l%qJb#gmJ>Mo%wLE*tCx(CKh14^IZ>{lIG1MQzdbPHdI$0@xJ_G8qaq z$)Sv&YD>4PGkWk~ais7ZZu0%27Ze?IKo+hfhunE31sh+O()g@RE!KgcgZ*LCYTM zS8l@{iZG<)(hJQUL(Nr3)rS_IF-ojvl09qipBxRSmQTVdLQY9US$5}{VX7e&9!rP* z?j@4cr#bqlc2@o+jsy|g(b8-*I^U-L(*^9KW&fL2HL1BDzt_2W&(=dt9qqfeU)R2S z>qVv7^TF=zyZ+-R{Ga-%N)%9pU93atmh8BOg#X#Jx!tF{8asQ|pO;MkuG+ex5bpX` zmR41;!a$AVNm}*S+CR7Zl*-tL!wxD#u_Iv{l%cCeCYG4a!>w;9U4JEQy)fgxuxU!6 zjo-Jmvow#F+B^QrYaC-!md9-f^C=(2T{{LS_@}sU@!`u^_u+Ns?3*Y1w#tu_ZCzVO zIyZNX_*$QseJ*UG`k1FRg7dw%b(c?BVV)l)&uNlxR7&T(d_XBy9Ogw6t2(xg?Ay9| zTdAcG%SbW6o2lkF^+x-6f`wsrOuVeSlk*@8X+mvW3FI3gVrSb!tl+~@uf3KMp z9=Dz-vzSnnZ)Dr8!ko33^07Hz;nwZfEe)Gl-n@DH&V2_@7618Ko07jtd25fKbLOd$v}r{Y75(g;IXQf1{qR;(eXJD^IAWX4fd*#3cOqlf*yqdT4$rMzGxY z{g}y5*H-P@eDKfqjuHRLaMGG}^Q~iWgZ1arJvHO`p1_MHRC2CKZ)lX z-*l9mYhM&lK1;n8tN1nb-;s0w#rbwA6THf=3pb2;{YK@rMGLICh^AkZe=qvGWPpdj z@|i@P8v98hs{%l{+|-rLZgepVj=A1uqW|qOv8~U~`y>O&f+zr=btp!F0+!6>a5fMs zF_lvvdUSLcm2@_|uELj)B^0Ni*vl{xK*(zBm-~2 zASmflH$)3(DM`0AKQsrfvYPzXi59+wn2|of9O%k%wlQTo zMH&=O>AWcdWuNk!WzK40jrPh6gb~NKA4a zsEcb3!|EeDCzbIZH|a;Uam$c+^JO6*bU}t=uOt8#SR2QqSP7hjeO`qbM~af=)O$WN z{`)3sM{7<3$wyzwB86QT4Lh|c8$`Qo$3KZYR&!L?lWdcnsZ@EQu0;`0#tLg^tPOcL zWNvVxzQ_QYQQt-C0C(EDsjZi{T-@258I3}Ai@CFPhE+9o9N~nC-{MAq7vfNZa{;E&Fz&d#0k!2-n!4p*13SK^*MB88aC;ktK&wQ&Zm2jC&2hF)g zgv=4os!y*x1<%q5QW%qWAo{%;7#>{w#5@}S-Rp5Ahebxa&Hw1D$J+dthxK=YC#mBS z&&+SEkFxswkFVC1f6R9C_c!X1=m=KPiIU}jKQ>)X0jU53XFPDKvpH`TG6FTa@_-(J z4ID7AqkpW~xSeEjVq zADB_J#^bsXKQKlvM46e<|A65n@B?tYFgrZ<@{aY9V?}l?{grA6I8rjkrh&zcQYHrq zS|n^t#7n!N8KhWF6AYFQZ&XD@S860D@|B#GnZ%+NE#r;i z49dclgg6*Gs3x!XAqbP^oJcZPbgJ366K?Sjt#d$^>wJ3?QUb<0 zlBqI-!zScXh(GBcRab}|K^hDz0I%UH=9yLesL8r$bue3w!8!WG?>H*y3%91Min#iR+t}ww}5%wqH4R_cc~L0y_Mx!?P zY3(eZ+@vE$j+spH*3Vq)tv%0dJX^;+`MWQT%5x4xvi?=`eVKT8wnxqR7c62M{<4H3 z>!{pToJ$(hNzh}gV1($?qhno-JmsCJ`MB0O^``7?N)0^HUt)KJJ#C~ zER6YlSb1qyKLpBw^}&Yw5?L+x&SNG>58T>OJ}$)A(EQLL*ccmM9~K%^7tLeuC->e) z>0~uCnMRW48;$>8g9cb?tC{>wpL*5(XUvAG0Lz()l9*xCo#TPCoO#yc2~IiYQ+TZf zR0&T$27eec0R@c;j1HqLN1Jxvi5zB#D!6ui>D$BUp@il<+BEq`Ge{NIfl`~IP2i;fhd}D@EpS^asdi~9Ow4uZy7ty3j`Xh(=%()|nD62csZ7;vSNzXu) z*lWZs$B=?zjlQ^gDSIJ$eZH%1$g7G7XOuI8#Vw%kDvtV;jB;) zj$Y;U?4!NP{P{@v$_wuU^AnR(PCM7-pYTL~x=qeC8+ltpvMwq~ry_02B2m5XZ4z%+ zUcQc^H-}q$xP5c{KhbBVt$xy@C&6}=+T15?xm1qMHu$#B|Mna<-qE-;$JXs%aRaM2 znr|5>qctg3L7hLHLdFQTSUS3M+H4AgkSKzqpRV#M6R$gsox-!P1VvpwP+rPJC<7r= z#A~&un3J#ug{1L$3q^QwVSlB6<>e zpb9x(RsiuuW1CR6;>+qnod=c(On|xo>5}qF7y+`x6V4vN_=7IhGSD&(ckqj9C zJJEb}mWZeFfJoJMz646!P$Oz>@wlqZT5%>gX5}|AHhJQvTrdgvry6j{u(o^DiHk{- zeAny98#*kwxksrvJ6d32SU-q8{p*7gK#Mjh?`RP?7h;!g?aw9JD~}#UF#QG@ z#wgkP1!S$mK$w#H!~>lA!dwp2GmI+ci1=5;dV4w+(G3iS?ZcyIcfUr@D*p&4UcYt8 zT~Lr9g5vS|Q^~L)8+f?bp=(dRb>^rtd&=ofC4LR*ZoDl3qfpbgb5`^zLm?&*?d_yf zgWv3E@fRY0%?<4Q=*Ud6{r}_YOW>L~qK7vJ5RfA%Dk4HeRPe+b6#-Ez9#yKnL{zk> zU|O}OH#?gUAPZq3D_J1GgkTH-F+qz*Ee(QNqgCV4R_!68ty-^G@uEt;w|>9>|NH&E zEbMzTvpX|8=X-D7yxI4do(-SRjm)~?WEXvYql@BI_+bkM%;(U|cvdYKMuUB@W5?@1 zxOb4Hs5@dPR%1c>ZJ?GlFg)zTJHIyV<2QzKy@EloR9&o!zz0WdtcE&=4M^X!(}LQ6 z;-aZN;H-V`V+!_+EM9U$ti)@k0PXU2ig@t0#8GQ)KwK4k4{X|rK@GE`I~O8043z6@ za*O*!aGp zK`2nDtlgB#soyd4noTc@?RcOTyTZ5$20pIAiwvxWssiRizeaG2T{h;{_dxsT#}En^1p`nn-kx#; z*gcJL#8lhqt~R z)lQU0G`-!F>Wi?n?F#JFW2X#(dQp+UYwpOp+7LCUgH6clgy!yS=odj<;RYmAySa6d z6g*SO%;P#($rAnE1x+|>YLyL7HNk4g8X#S-us+{(nsUjrKfs-YJ4eyU_weFyq?ciBcfZfGa$3a)C^Z=HWrWqhK~alyoc zYO}A}L+mL~%tzRfb`ZS*Z>D*Dc7q-C<;3Wqx8ir4oa3bOpr&+Q0lD1ttWF>pKow2} zrSolV>U7v$2@E0Zr;1$O-pYJ<5cH>@)w$R~J*_YR!APnn{7{3p_$H8h6LWxv0eTmP zJdio$1?2$k3Z)G#9i{1D#)Vvf8u_=NHwf*)R|$KRyu0%BO9tFbM(makpRlnX-TPGW zW8|`2?Da`bCX_bh8zcxr6q`RlAW>dJZH2A4FdGBS6-d*a#k7g0Eo%DmO^otzY7KsL zhQyUB`lo$3doP~SfZut9mzH4HaX$EH4vh=w%W68a6W4a)uWsX^20Z*HyeyY~*?^5E z+*FU}y}*}xFx7@L+xZt$3+c%k`rZRP`)@vmc0HbO4l^Y<_c(r2i#sj&*E|~ho7?gI zTslvKzc`P{Uo|wHhus`zb=a^QuV(RnEuMTFe`&-Q>+o;+boOifdpj;Xf$wQ>X%W4+ zh}Pv{$94ScZ+KG$pA7s7TCuSP-!tR%9z6364nK)w&fu}d^ZjKLkAJ_0Ke~<+s&Cx9`U_I#A`0Ry^p!_0RC-=lH$T_|j?o&r$rzVf@)4 z>}tlu9bDIq2f1;}MLeFPOUvkA%jw)}*m{kRYlZ`FbKplsn3NvG#}DG2&3Mio-0Lnr z@;grZ15eS@JM?s)D!SlTyzMIg!W{=>b2R#r!LMxix)s~M#l7z1Sr72VKk$YdSXD}| zs^aVR=PP*PFZ>IdGa$3dDaH9sc$HOuwPf-`Z0TU0XQ7Hx`dKNxMMuB6h`+f^^3UEp zgJ(3-w~O&cik_gPlN9uogY?mF=~qp3*8y5+;Ums!qK`DPA1ME-r|b69#(i|i9=g~> zdz5z#=DdTmvZM5}tSE zALD`&yseFSJr(Ix^pZkaPt#SMIO8^s{~M>&;Bn{hU%B+DeR%U;Jgx!fyubt8cx5}b zwlj5>F&g@!hK_lNAN|ebcqool;?3uAofbbjj(3}|xgKB0ry<#^WIMi?N8i%m#TW2` zB3he=S1d4qFL;DIOcKSj7NL$gKn%RGGUH@yFM zd`!=r^iV*IqmJYCM(n7==X-E!5B}~XR-eJY7t_(jbc~j6%*UTq;B^$e=or3q3_t&# z#4qglFEjq>CI03WzWE9tIfc7VVQ-dxe>c6Qlzy0xmtMywuj7FR-2VqGv@@}&`A0Jz z{1O|w@uF_L;74ef*LKv>Gi&MVnEpbI*U|VWjVD#(znyw@LEf8@F8tVqegDO$e#FC1 zPDl4r}*(x+|h;KJD;@5cVYwU9nFSp_m9HaJA7@uLsb3F3}K7ATbao~#Y@QfpP*ue_i@S}_}m}(Yd!5< zMSoO9%dg_E*l%(?P}CW|G=~-AZ<_EO8~(HzCp^G6AKqN*Uj!yxFB}=uu90D}Qq*59P-a;UJt*~`&3rEGv?94x)KBbxs-vfBB{edIq9wpV6Co@^$Z;>RT5Jn1jGjE_|8=mo<`o6{q6ULq1?%1r~g!5%dl zXP}9;XA584Z0r!a{P-=6F0YIgl^w#259QXcD4Q#isaClpx`8t;fdZ<2+xqrq-k!3L zYyxEwYeor>ZpG-T68ZI1OaFsezSZjO-RTuyN)?u$o@P6|oc$~k(Wba`4o zR@z)*i-#&uuARFDWg(4lppc#VV=P%A?O%S)5?j?Il9%x}%jV>Uh>w+qw1?js*kQ;d zT6k4qH9#h%`T?tyRZ{EHr`gd$S9KhS@%vP)4WKxmbfwB8gp*U;7pl@grD>q6k5)?k zK|< z=YXl^+65n8d$IC^YnuKGA1MyLQhQn?VE&vLnX4T%@@GcNZCZSd@~@?JUxceZpwi~N z0^;f2Akr55c;nOTVlngO(`;)l1YvR*vSAaE-58CoJ=$oh6XeTIV zPq*eDj$C$)Qr}Rh7g_1Ux#cq)V&_zA!QmpR@Xi}ba}d_a#uonN5IDoE?;;xz@?z$6 z3aJ(XbO01^iKIq_M(E4SJwaLgObW>|p#J?sK@)4E1fT}qQZBbrkO_bNdp>^(kmo%i z1G$k~Po$8}KGyk9vz>m8zG8>i=3$LRHu(;2tM{KLg~cL^KyHQhBj*)_RDmY2+RnQ^VMmc0fiqG zhNdJGKD5?Ab;etT$n26?m)6(0B-TyqNKh-6SU==GZ}+3D8(rDaBm;Nr`w-mQD(8cp zKGqFy{oxL+Jfbbv5OMCZu`c2tKqAQ^15?(T-8#$}Xa(r(RX@3(hFRzZMnO71LlJHD zhV9+_TVO)v`xv3qBjRv@p$^$45jnpZi<6R5twajx@`~7;YgjlSw@^*fJ(o9xP`^P*f08Tx0Xv(c0hT9W3X#hr(F7MHVbiwz5!3ghjwW zZcHm2L9Rhioe$V$3UzsFHa3~6VuTR^*NvI-20`bB;rM_loe!i#9C0m}Id zTRy!<*|-^lnvO@@so2w3fUFzch{s=J7Q39{A2xYryGcUZkd${I4^WcP{1M$>&2o%}esB_rbY7k9S6!H|9|v z{I7E#@B9#)+j9BPKh0nks|7W2-J|JS((oIcdD|8XC@^WI&-@+ya9hdd(2so@mm zP7jMm3-aR_O3DF;%mpB|)FI_Glne$0wM?GOM5A(V2@}|rov8<=N^AuQB~~;=K8oQR zVqNK;aHa_sJ!I%~+I7?kJ;GjHEb1>PEq4$oxwy&p_k+izC!9ico% zcq|PXM@zAv1NPykExJ+VAkvoHsIm@*>_GdmstyO~N=>wba!RUDI za#=0ul$3eYy}{lY?m~@IaMzxWY~Au&lBJvnVGB({b(6^ACAWy=Z8B>gg^k-a_VMbt-M8=L;`Y65ONqW+l2D#(@9I|(wf5RZu0kiuGj}0N_sej#_$8RJYtZ2O@;2q@RkATk`v=2n>2amgUi7^#ea-b)SwcYVsf?L@&ad z@FIK(Ki$+(O+{gUnIwde5FTkA{ z66zI0(TRR&m?bbubE2OSc5$qGnjNRu?ehD6(QeDIsPa?&c%Qp>J3@%!J8-CqRDZ8U z+#e0K1dvf0aeqELmAiFQMfOsO%Nt$*8!evwxiP5xHlHQ+)1tGgKpiJUET+WfkC1wp zWiXS={6l3PR%mkjSu+pkQxfY#m28wghstPIP#LuZrCr&lMjyTH>}Lp+&J0!m^tOip z7}3W*2Ec|s_9OuHeIo52@_~Kq8~DH=do~{!WY_Y6LH50zp?2py1-oj+IMJe*(=5B< zGxFV!Ppe0c8^sFu90(p=q#8Ho;JS&YaSc~e(W$TIO2(o|lTTYSC!a1FMvX;-r=EUC zJyf{@nYz?BoJp|jNoI1ved60Zgf%2T@l?7vtkhP>E(mV-g|Hk>#YTWUBPWhH7d*tDqWL1?>5K z@k-zX`psfM3p-`j9AtACY)Muv%=!JTBshaNRS#*`uKBMSlmkiYXHT;)04(lf{^rw0 zi$6>};DUKcq8nEqf-YDy2A@8P& zl^H(V#385skRrQ3Y@MmdL!37`1 z4cI-75}dZ3G&qy&9;aA^zWFPlHVJ`-o8 zkQM^3g}~J7T-uiK%K{X<2{Ki0KaxQn!ylxOb=`Kc!{(Y*b^`$WYx;Q7)G6iXAV;fn z5dMK%F)#O(Q)rpz7zXzZZ=VdV+r6=&Ue*_H@7AS#Lg|OSllyF8Mpc`vahP!0 zv}k`qGfW8W3qDdUb3qHdZ^hZj1bv2JRzbsB!uly|e64^6l-3&95xLZd4GKyJE9Yb? zARK2_M{V!cXXTpum<$eYmtSUPrf%~#rX=l2L9X05FH@-&zyP{k)g&dH-t_$W!CC2P z$cy!-jwYt=A116y6qfk_+4rN7(Ursq>5vfaJ<1R5Xp?rz(jhL3D%tWp*)lb`1^K5@ zd5HI_PPRNswvhL5KZSmz+9Fz*f?+@Ag03RWls%mNTqOsqB7A0?p0f3OWM0Mwc-Pr$ zL6?=Ct=6zFRr1=##!oOj6agkqW&dDyxuOcWf?#ZsUau`Ib&0x>SGRthqDF3o_7Uyn z3ekE#zgTSmwgGmM8XO`3U)Kx*dRvgLTQ{((RO<3=$u_V9G^HKV;;xn=^Ufl6<|lo~ zMn6Y31OEjUp~{mR7k9E^;6Vv{x`M5S#=*L$qN6`7xR zR*c-xiO0zPE}BrG7QmYm5>KAX@G^x&gVjCDWtGHYPkq@2RYXyDpblT` zpcJ}~BP-2?wPK3#pj4@ninwv@%s6$;)Y4j)Xgiv?y<+54D@!+?K7E=z&F#u5w?X?zzn;nlEBYdbzsuh-#^vJ} z>+*EOxP(CUMEzMEGYUqlENE@(qC;no+Na}Kvc>dsz#D<4!^%eVXM4KW4hLR?}D-LD(Xg{Co>5B2t9 zU$VnXN-Y9Dpf;>ng(9q1z=v5`c+fZJ9CR9p3BFe#6ieD7WhNrIQ)WzAZBQkrvdgIw z5i!lDj_Y4RdFmGQw)I6vFPqp6Ya@k31zWOq5$XP9^zDPe-~8DOZS#66&8$u#jf8>V zW>3wrK&$hRweLY?pVqgrE7uJqcNIf55l$bz zEcxL@<>wgV6rupfSh#ybrFk6fvloVk*8A+3@+PUv<9_VDi2L`M=+YRfH zK?CsW@O6n~{r8@}3%~Rmrdy`_rWdCFOrpJP;QD^TjTLP4`Y>{mN-b%VJ@rsj9mRW9 z!tJSLY>F}vsr<|4*EI$hI}1`fUO2hXo*bMm#HHj#prWA&=BB&gckOv%rHR* zdq1<8OsqGY6c>x`qG$G*^#)z$hJq)w4~*eS^#;w|6#Hxd|D-e$?`b+x8ins^I+@az z`SYVigvOO(5>L}SNO8q7`&wS7Xl|#p^rUG1PLYkOYka^KloId7{w1kmlTm4l@V&o$ zsyKz}K?+l*L8nY&ZPrdWs-xg=K`Y4}httF~_?7xE9}QM%VjoA^Aob%Bbx;B= zr#xXAuD3Kq$-!2@l*gyIA-91JfkU7;AWFEe{$>UDC;_K&2<6GZn2B5-vZeJ%Bzvbm zGre|IZNU*H_9c~U%nrI@s>F3R{T^zH!O048n9p!ciB|sBF0z;IlVFP3qAZU@ zHs*_`*|lqeQK@w(IAE)o%H?C6|Nb9TAd?TGM?7KZt(z8LUqC994N^5vOLCg)rYe;; z$Xom|w>~0==a@s2(G)JHJQ~nJ$!G+ifQ2QaLH`#EPexMR@^`OM?{O>RJKrq4)WFj6 zK_s`ryW_j0QP4Vh{o=&QWr!pohF5 zLpGZ@jXx=Y=pmiYEn<1ZqeF(q6XFYlLBgaT9j9t?c)12f5QhzN%LLFwi#w<8WgW?_ z^UNs+R+0C|(mNWUQ3x4(gz-Y&l!ZXsU|9GX+M>)k!6J+ZZZsrYbCHdm_32158P)g# zx8sKfTl}qUn&=*RrheIA1V%Ksharn4fPY&4R-!WXi_(+C5;7#(Ey4hSs6f`JZgzyC zDDZ~*(?$bRp}KqbMRj>cI8l5#s{7QmwXSAiq7VsNg@hfa0Qyfv)Xs`}pJ4^O88bBId zqH+k#h^=VAl(0d;RI|8ZiYU+F4dvqD@O24-<<^r2ZE6_jbff!Il)QRVidJ>NR+P_7 zk4EZ}6tX&X=`%1wq8ZBtc#%2v!_XgTlP*+4TOHvNfaDr();AB|iWkg_hNx;k7@x1P z|GVFCCs#vu?Kk+K&u3B9GpF4EL9At91PGGlATrSo3Rcv3TLhXv0@7AkxmoWaQ`$;v zu5~`tS}eD=-*2U^7)T*lyCDIwlSK+@Fr%PCIR#1e1w;zWBJ$~!RfGTmD90fqZ(Sth z*9-XhL334DZ;3W|z7=Fw3>%PZfx$_pv^I3h_g%$b`u?t6lf!)T1Jl>8vLIZovJl2x zT`?7ON})piwcn{MTeU-`A`M~LN<3PEsATQeI}rZj)89PMXoqooHwkjLMv)jw%Of-G z-|{@!pH0QjGt(ZG=Uq?D5OJGC=$z;F?(bLn!uF%4;%D2t4Kn$4gMjNj47G^5gXN8o zJv-5Als@f_+8?HO7d$GlSE+Gh@?fhlfoB zVwFyzJR-H&pzi~{t1rw)sYO)|_hMIIU9vSyKVtVblyNT&NGR*2|5VHlD3unOza!u2 zEz3RN5El!k$pMjf1baI@tW_zb9Oh=&j%M?T0mG#I6I-KZv!b)i@c*8O ziiJQF*ut`5+J(E^+78r9pWsapxx>wF7y$-QD^+qUvkEoC*ihmc;n2E}_2D@_rHXs&N@FX6N zl?IRnRDGLkjpn;>TFJM$dq444m&j~=+N1|JwUl;B(Fp4>E_iQ=bp*22)z17R=euf$ zECTEdzkO{cpPrXP07LY~>>T#zZR3RJzhx!cr;wkOQ<0!GYO1&Y@~z>VNaT#uPZ|EA zWTHiq+`tKky{NLqpka==&Io1%s`W3Yf-1ybd6X}!Wv5n@*d#!Du+vK4bjkp+2;~9N zK|sWOW=~oG=nZ-^N2qBWEkO40 zb_&gnn4w0%g4?2nE?J|$z_G(Qz*+?Is6^<-T@hch2m^9m=_d^iv8v{1m&pwtc=dYfH!j!4CQ3vK1bRQwg9E% znOeuEB2VVmD)1KvGyx!*VKyRa9t?i-n9q?0r#*$mfYPP>cP_P-muB)vCR3>O@=#(h zd7H#E6m*##1xF}ynpJ(y4~l6>SN1dpZk@7BIbWAR>k(pvIuK!6pJqXcGjR|1RexNOERl z?XlV;)TdZc@EsnTmUk}zQvVc^fhq#fDdWXM@?yS*P=#H=P?#UaiTDs52Kb#{xbT@I=640G|fuoSP()mlh}I#1^dJH;iSeEmJGrp zV6r-8ZPF`*NtoPd2up6P2}`za02)iQ(61fZy&;TuMGIWnjkYjoBwH-?`cGyMNPR#h z9D>s0LATSYOChICV>G1}3|n3m(~%IV7rtp@y%A8Fijtx5&cyW!ni6vg^#tdZ+8Id% zT0kn208kQmU_3mqd6Pm!nPx&7(JsekXY5i`tX}BdqVKEFN2*}n1CJpp4Cdh^SfMWF zor4ruN~r~|#au9|GC1GivuEWz=*6h69ADO)^m&5ofMxy{tO&EN-%NK5zfY^sMVZ2987#OCK+3#O9Iz&XV*EwIYb2gH& z19q1XwrQQRRcL#$rBeovIS6O0<=J8slH2&E4OU8HE&fmp)B?~0E)jxj=yF7thyY3C z5iuak03k~}0Q-vf@&u%WN4x;3;}HoU$9cpXkn23+1ISZAzyki1yai|nDs}jRSM+ie zuP*Te>>d6oe?ZpqNPu&aMX?+fkPiFiv2coJ0^~@^zy95FG(MjhmrtcfE=Mti%-BNe z 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 Date: Wed, 30 Aug 2023 15:26:46 -0600 Subject: [PATCH 024/158] Baserom patch Attempt to repair bee trap and other items Some other minor fixes --- Items.py | 8 ++++---- Rom.py | 10 ++++++---- Rules.py | 2 +- Text.py | 2 +- data/base2current.bps | Bin 116532 -> 116910 bytes docs/presets/async_doors_league/S3_Main.yaml | 2 +- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Items.py b/Items.py index 5043c1a3..8f239fc7 100644 --- a/Items.py +++ b/Items.py @@ -80,8 +80,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), - 'Big Magic': (False, False, None, 0xB4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), - 'Chicken': (False, False, None, 0xB3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), + 'Big Magic': (False, False, None, 0xD4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), + 'Chicken': (False, False, None, 0xD3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), @@ -168,13 +168,13 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ganon\'s Tower'), 'Small Key (Universal)': (False, True, None, 0xAF, 100, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key'), 'Nothing': (False, False, None, 0x5A, 1, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again', 'nothing'), - 'Bee Trap': (False, False, None, 0xB0, 50, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'), + 'Bee Trap': (False, False, None, 0xD0, 50, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'), 'Red Potion': (False, False, None, 0x2E, 150, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a red potion'), 'Green Potion': (False, False, None, 0x2F, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a green potion'), 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), - 'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), + 'Fairy': (False, False, None, 0xD2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), 'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), diff --git a/Rom.py b/Rom.py index f74566f0..2542a3fd 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '51a592209991a054bb40b7e7789738c3' +RANDOMIZERBASEHASH = '2a6dee18eedd42420d26cee7bd8479da' class JsonRom(object): @@ -2535,9 +2535,11 @@ def update_compasses(rom, dungeon_locations, world, player): for name, builder in layouts.items(): dungeon_id = compass_data[name][4] dungeon_count = len(dungeon_locations[name]) - if dungeon_count > 255: - logging.getLogger('').warning(f'{name} has more locations than 255. Need 16-bit compass counts') - rom.write_byte(0x187000 + dungeon_id//2, dungeon_count % 256) + rom.write_bytes(0x187040 + dungeon_id, int16_as_bytes(dungeon_count)) + # total tiles + rom.write_bytes(0x187060 + dungeon_id, int16_as_bytes(((dungeon_count // 100) % 10) + 0x2490)) + rom.write_bytes(0x187080 + dungeon_id, int16_as_bytes(((dungeon_count // 10) % 10) + 0x2490)) + rom.write_bytes(0x1870A0 + dungeon_id, int16_as_bytes((dungeon_count % 10) + 0x2490)) if builder.bk_provided: if provided_dungeon: logging.getLogger('').warning('Multiple dungeons have forced BKs! Compass code might need updating?') diff --git a/Rules.py b/Rules.py index 3fdaed07..052698cd 100644 --- a/Rules.py +++ b/Rules.py @@ -1292,7 +1292,7 @@ def standard_rules(world, player): for loc in region.locations: add_rule(loc, lambda state: standard_escape_rule(state)) if region.name in std_kill_rooms: - for ent in std_kill_rooms[region.name]: + for ent in std_kill_rooms[region.name][0]: add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state)) set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player)) diff --git a/Text.py b/Text.py index ca068926..4d95f516 100644 --- a/Text.py +++ b/Text.py @@ -841,7 +841,7 @@ class RawMBTextMapper(CharTextMapper): '~': 0xCE, ':': 0xEA, '@': [0x6A], # Links name (only works if compressed) - '>': [0x00, 0xD2, 0x00, 0xD3], # Link's face + '>': [0x00, 0x9B, 0x00, 0x9C], # Link's face "'": 0x9D, '’': 0xD8, '¼': [0x00, 0xE5, 0x00, 0xE7], # ¼ heart diff --git a/data/base2current.bps b/data/base2current.bps index 3dbf4fe0982e651811960e01abe0b866fe5e6920..6675a34abf7f5f0fd0bbced111673cdffb4ba488 100644 GIT binary patch delta 21036 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 A*Z=?k delta 20418 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 Date: Wed, 30 Aug 2023 16:17:36 -0600 Subject: [PATCH 025/158] Let dungeons exceed 256 items --- Rom.py | 8 +------- data/base2current.bps | Bin 116910 -> 116909 bytes 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Rom.py b/Rom.py index 2542a3fd..227f4b75 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '2a6dee18eedd42420d26cee7bd8479da' +RANDOMIZERBASEHASH = '6b6b74e487a87fac895dbd7dcd3ac748' class JsonRom(object): @@ -621,12 +621,6 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used rom.write_byte(0x187010+offset, layout.max_chests) builder = world.dungeon_layouts[player][name] - valid_cnt = len(valid_loc_by_dungeon[name]) - if valid_cnt > 256: - logging.getLogger('').warning(f'{name} exceeds 256 in locations ({valid_cnt})') - rom.write_byte(0x13f080+offset, valid_cnt % 10) - rom.write_byte(0x13f090+offset, valid_cnt // 10) - rom.write_byte(0x13f0a0+offset, valid_cnt) bk_status = 1 if builder.bk_required else 0 bk_status = 2 if builder.bk_provided else bk_status rom.write_byte(0x13f040+offset*2, bk_status) diff --git a/data/base2current.bps b/data/base2current.bps index 6675a34abf7f5f0fd0bbced111673cdffb4ba488..7ef9fc0f787ac9496bd90a24fd411407b611d11e 100644 GIT binary patch delta 72 zcmV-O0Js0Hkq51j2eAGE1Y~8KnzI4}$HxH`lgi2+4<5oGzd-P?pAb}=I3b9!llRIv e40D1UD#w-`|$HxI-lgi2+5D&s2zd-P?pAb}k$`JM9Ad~jW fI1G1!VocDM7HH0=0Rgi*%WM4zknSSFRtV>4&jcau From 8d31b0080f07f2194cb466201439671eab176eb2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 1 Sep 2023 14:14:57 -0600 Subject: [PATCH 026/158] Fixed shops not counting items Baserom update --- Rom.py | 4 ++-- data/base2current.bps | Bin 116909 -> 117013 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index 227f4b75..2ddf48b2 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '6b6b74e487a87fac895dbd7dcd3ac748' +RANDOMIZERBASEHASH = 'cd3a693a5f772e83cdad5dd2b53d5586' class JsonRom(object): @@ -1469,7 +1469,7 @@ def write_custom_shops(rom, world, player): if item is None: break if world.shopsanity[player] or shop.type == ShopType.TakeAny: - rom.write_byte(0x187E40 + shop.sram_address + index, 1) + rom.write_byte(0x186E40 + shop.sram_address + index, 1) if world.shopsanity[player] and shop.region.name in shop_to_location_table: loc_item = world.get_location(shop_to_location_table[shop.region.name][index], player).item elif world.shopsanity[player] and shop.region.name in retro_shops: diff --git a/data/base2current.bps b/data/base2current.bps index 7ef9fc0f787ac9496bd90a24fd411407b611d11e..f1aa6074d2f77ffd9aec0f4b359669744bfdff3f 100644 GIT binary patch delta 16305 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 bN+;?5RO`XE+8e;$%HB=;s$$BXld1m?(l`L+ delta 16129 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 Date: Tue, 12 Sep 2023 08:40:24 -0600 Subject: [PATCH 027/158] Various enemizer adjustments and fixes Fix for retro location counting Baserom update --- ItemList.py | 4 +- Rom.py | 4 +- data/base2current.bps | Bin 117013 -> 117370 bytes resources/app/cli/args.json | 4 +- source/dungeon/EnemyList.py | 6 +-- source/enemizer/Enemizer.py | 15 ++++-- source/enemizer/SpriteSheets.py | 15 +++--- source/enemizer/TilePattern.py | 83 ++++++++++++++++++-------------- source/enemizer/enemy_deny.yaml | 1 + 9 files changed, 75 insertions(+), 57 deletions(-) diff --git a/ItemList.py b/ItemList.py index 3e98a693..a5c895c2 100644 --- a/ItemList.py +++ b/ItemList.py @@ -509,7 +509,7 @@ def set_up_take_anys(world, player, skip_adjustments=False): else: if world.shopsanity[player] and not skip_adjustments: world.itempool.append(ItemFactory('Rupees (300)', player)) - old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=world.shopsanity[player]) + old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=True) take_any_type = ShopType.Shop if world.shopsanity[player] else ShopType.TakeAny for num in range(4): @@ -524,7 +524,7 @@ def set_up_take_anys(world, player, skip_adjustments=False): take_any.shop = Shop(take_any, room_id, take_any_type, 0xE3, True, not world.shopsanity[player], 33 + num*2) world.shops[player].append(take_any.shop) take_any.shop.add_inventory(0, 'Blue Potion', 0, 0, create_location=world.shopsanity[player]) - take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=world.shopsanity[player]) + take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=True) if world.shopsanity[player] and not skip_adjustments: world.itempool.append(ItemFactory('Blue Potion', player)) world.itempool.append(ItemFactory('Boss Heart Container', player)) diff --git a/Rom.py b/Rom.py index 2ddf48b2..aa2f47c6 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'cd3a693a5f772e83cdad5dd2b53d5586' +RANDOMIZERBASEHASH = 'f3cc032fa0a1ccf9624d0fb3e9926c3b' class JsonRom(object): @@ -737,7 +737,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(cr_pc+0x1e, 0xEE) # slash rom.write_byte(cr_pc+0x1f, thousands_bot) # modify stat config - stat_address = 0x2297B2 # 0x23B969 - old + stat_address = 0x2397B2 # 0x23B969 - old stat_pc = snes_to_pc(stat_address) rom.write_byte(stat_pc, 0xa9) # change to pos 21 (from b1) rom.write_byte(stat_pc+2, 0xc0) # change to 12 bits (from a0) diff --git a/data/base2current.bps b/data/base2current.bps index f1aa6074d2f77ffd9aec0f4b359669744bfdff3f..947bf97ac43ddc1123d1bfd993c4416c2f512a47 100644 GIT binary patch delta 19500 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 KlVz5LrT+p;7NFGt delta 19154 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 lcQ=xbuOJro6>9q}@$?M1LfcOUzJ@Je Date: Tue, 12 Sep 2023 10:13:15 -0600 Subject: [PATCH 028/158] Unkillable thieves for now Turn off enemy drop indicator for modes that don't need it --- DoorShuffle.py | 1 + RELEASENOTES.md | 32 ++++++++++++++++++ Rom.py | 4 ++- data/base2current.bps | Bin 117370 -> 117379 bytes .../app/gui/randomize/enemizer/widgets.json | 4 +-- source/dungeon/EnemyList.py | 2 +- source/enemizer/Enemizer.py | 12 +++---- source/enemizer/SpriteSheets.py | 2 +- source/enemizer/enemy_deny.yaml | 14 ++++++++ 9 files changed, 59 insertions(+), 12 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 1b7e96b7..e64a3c19 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -3545,6 +3545,7 @@ class DROptions(Flag): Hide_Total = 0x100 DarkWorld_Spawns = 0x200 BigKeyDoor_Shuffle = 0x400 + EnemyDropIndicator = 0x800 # if on, enemy drop indicator show, else it doesn't # DATA GOES DOWN HERE diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3231157f..391ec6a4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,37 @@ # New Features +FastROM changes have been included now. + +## Enemizer Features + +Please see this document for extensive details. [Enemizer in DR](https://docs.google.com/document/d/1iwY7Gy50DR3SsdXVaLFIbx4xRBqo9a-e1_jAl5LMCX8/edit?usp=sharing) + +Key points: +* Enemizer no longer uses a third party program. It is now built-in. +* New option under Shuffle Enemy Drops: Underworld. Any underworld enemy can drop items. +* New option under Enemizer tab: Enemy Logic + +Please read the entire document above for extensive details about enemizer and enemy drop shuffle systems. + +Enemizer main changes: +* Several sprites added to the pool. Most notable is how enemies behave on shallow water. They work now. +* Clearing rooms, spawnable chests, and enemy keys drops can now have enemies with specific logic in the room. This logic is controlled by the new Enemy Logic option +* New system for banning enemies that cause issue is place. If you see an enemy in a place that would cause issue, please report it and it can be banned to never happen again. Initial bans can be found [in the code](source/enemizer/enemy_deny.yaml) for the curious +* Thieves are always unkillable, but banned from the entire underworld. We can selectively ban them from problematic places in the overworld, and if someone wants to figure out where they could be safe in the underworld, I'll allow them there once the major problems have been banned. +* THe old "random" and "legacy" options have been discarded for enemy shuffle. Tile room patterns are currently shuffled with enemies. + +Underworld drops: + +* A flashing blue square added to help locate enemies that have remaining drops on the supertile. (Dungeons and caves without a compass get this for free.) +* Flying enemies, spawned enemies, and enemies with special death routines will not drop items. +* Pikits do not drop their item if they have eaten a shield. +* Hovers in swamp waterway do no drop items due to a layer issue that's not been solved. +* Enemies that are over pits require boomerang or hookshot to collect the item +* Enemies behind rails require the boomerang (hookshot can sequence break in certain cases) +* Enemies that spawn on walls do not drop items. (Keese normally don't, but in enemizer these can be valid drops otherwise. The document has a visual guide.) + +(Older notes below) + One major change with this update is that big key doors and certain trap doors are no longer guaranteed to be vanilla in Dungeon Door Shuffle modes even if you choose not to shuffle those types. A newer algorithm for putting dungeons together has been written and it will remove big key doors and trap doors when necessary to ensure progress can be made. Please note that retro features are now independently customizable as referenced below. Selecting Retro mode or World State: Retro will change Bow Mode to Retro (Progressive). Take Anys to Random, and Small Keys to Universal. diff --git a/Rom.py b/Rom.py index aa2f47c6..5ec5d5c0 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'f3cc032fa0a1ccf9624d0fb3e9926c3b' +RANDOMIZERBASEHASH = 'c8b9a36f11cb3cd070f83f31219f5cba' class JsonRom(object): @@ -573,6 +573,8 @@ def patch_rom(world, rom, player, team, is_mystery=False): dr_flags |= DROptions.Fix_EG if world.door_type_mode[player] in ['big', 'all', 'chaos']: dr_flags |= DROptions.BigKeyDoor_Shuffle + if world.dropshuffle[player] in ['underworld']: + dr_flags |= DROptions.EnemyDropIndicator my_locations = world.get_filled_locations(player) valid_locations = [l for l in my_locations if ((l.type == LocationType.Pot and not l.forced_item) diff --git a/data/base2current.bps b/data/base2current.bps index 947bf97ac43ddc1123d1bfd993c4416c2f512a47..a77da83b747d1a26e581131ea0200b90a8fcd1ca 100644 GIT binary patch delta 609 zcmV-n0-pW)lm~;A2eAGE1j=+Zi<1HaHU*HFry{14Jp(`ihLe5+S_svdrznni? z1J?=xMYFgL!W97}vwI}U8v$msMnEGr0amkWcER5s5S5b}YJkN%uL6Ll&6=2@3XRskdoAHp!N1c0ZB00{5~mvB}AP7(W;jiSe?Fer_r$G;Hpups-F z&sG7A6SkI9=%sr4k8 zL>8h9kf8`5)|Z(?7Jv|=bC>)!iSQ=crvp;)nJ^$Jn8Glnqb8SeSOFOs@|Gxi`fJ+1mY1ihAjX&aYubuRlO}?b zahIc50Vx5nm(5rKJ^?(J3|Rp&0X>&KSpgRo7@~;?uk??Gsn8MPFsT=XUGCrlmra*z zSpg;s@R_g+Aeou4JRqx=oLK=UI;)wmaGz>k7J!As6rTZU7J!)sAbzRE6rYk>xTQd! z1R#E?=oFuVmw0FvfR%(GeyR8rt0i9+fS+oY{aFDR1~YsUfV*0k8(IM$6)_c=l?kN^ z$q)dSRy~Z3rJ%_+w{DkDS^*;w@R|Cs1Em0KsR03NX+Qu>)|Z1?0WtwYm$q5~S_5l| vD7O<^0c>Of1~Zp|zX2lwUbm&c0sR978UdS=Xt$@s0m}vmzpeX#H;B!5LH7K4 delta 624 zcmV-$0+0QJl?VEi2eAGE1e|$ehLZvVHU)>6ry`w`Jp(`ieUp9zS_sUUrznn( z1J?=xJhQkC!W97=vwI}U8v$LjMnEGr0Zp@NcER5s2bGf=YJkN%uL6Ll&6<~5f|Rskdf7Q!%>c~${V5%QOfqQ|K)D2=4YzYy@SAo7>kRsoF@t(KEo zf~5xl&;v}ZmZ=dauXcQwcvk@+1|^lJ7=J;Ro>u`F3C)+8L>7P$qhXi7R{m1~&;v|$mp)hl zAx(sjhO?xeSrvwf@Fv=)aZ&M^Fd!h9!Z4+y6jJepr3aUhiTZ#GmxP7L$e&_?p9O)R zgw5%eD0=#9+P{sLr>Y>mm-=hkib|6vf|FmDtXKgl0jHPNSOGo(Dwh>m0WmHspos$T zGMY4cimlKoukin;#h~yIukw=yqKOEv^pA$A&=KM=sTYM^?%)EKJC}4>0VWIQnXn5W zkeRSNAflJ0Spg>xqM5L8pIBZNfQ7^qpKoZF+gSk_WM*g Date: Thu, 14 Sep 2023 09:32:18 -0500 Subject: [PATCH 029/158] Update sprite author char_map --- Rom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index 5ec5d5c0..53094d0b 100644 --- a/Rom.py +++ b/Rom.py @@ -1594,8 +1594,8 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr "J": (0x66, 0x8C), "K": (0x67, 0x8D), "L": (0x68, 0x8E), "M": (0x69, 0x8F), "N": (0x6A, 0x90), "O": (0x6B, 0x91), "P": (0x6C, 0x92), "Q": (0x6D, 0x93), "R": (0x6E, 0x94), "S": (0x6F, 0x95), "T": (0x70, 0x96), "U": (0x71, 0x97), "V": (0x72, 0x98), "W": (0x73, 0x99), "X": (0x74, 0x9A), - "Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0x77, 0x9d), ".": (0xA0, 0xC0), "/": (0xA2, 0xC2), - ":": (0xA3, 0xC3), "_": (0xA6, 0xC6)} + "Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0xD9, 0xEC), ".": (0xDC, 0xEF), "/": (0xDB, 0xEE), + ":": (0xDD, 0xF0), "_": (0xDE, 0xF1)} return char_map[char] if char in char_map else (0x9F, 0x9F) character_bytes = map(convert_char_to_credits, padded_author) From 26c26acd6e3cc6052b65fb9383c3238ad8f54991 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 14 Sep 2023 08:51:29 -0600 Subject: [PATCH 030/158] Fix item duping and disappearing Fix mutliworld crash Fix assured sword + start inventory New bans on enemy placement --- BaseClasses.py | 2 +- ItemList.py | 27 +++++++++++++-------------- Main.py | 2 +- RELEASENOTES.md | 8 ++++++++ Rom.py | 2 +- data/base2current.bps | Bin 117379 -> 117381 bytes source/enemizer/Enemizer.py | 5 +++++ source/enemizer/SpriteSheets.py | 7 ++++++- source/enemizer/enemy_deny.yaml | 32 +++++++++++++++++--------------- 9 files changed, 52 insertions(+), 33 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index bc168cb4..c5338757 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3095,7 +3095,7 @@ class Settings(object): args.intensity[p] = "random" if intensity == 0 else intensity args.shuffletavern[p] = True if settings[4] & 0x80 else False - args.dropshuffle[p] = r(drop_shuffle_mode)[settings[4] & 0x70] + args.dropshuffle[p] = r(drop_shuffle_mode)[(settings[4] & 0x70) >> 4] args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F] args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1] diff --git a/ItemList.py b/ItemList.py index a5c895c2..7a20c7a0 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1164,20 +1164,19 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer pool.extend(['Nothing'] * nothings) start_inventory = [x for x in world.precollected_items if x.player == player] - if not start_inventory: - if world.logic[player] in ['owglitches', 'nologic']: - precollected_items.append('Pegasus Boots') - if 'Pegasus Boots' in pool: - pool.remove('Pegasus Boots') - pool.append('Rupees (20)') - if world.swords[player] == 'assured': - precollected_items.append('Progressive Sword') - if 'Progressive Sword' in pool: - pool.remove('Progressive Sword') - pool.append('Rupees (50)') - elif 'Fighter Sword' in pool: - pool.remove('Fighter Sword') - pool.append('Rupees (50)') + if world.logic[player] in ['owglitches', 'nologic'] and all(x !=' Pegasus Boots' for x in start_inventory): + precollected_items.append('Pegasus Boots') + if 'Pegasus Boots' in pool: + pool.remove('Pegasus Boots') + pool.append('Rupees (20)') + if world.swords[player] == 'assured' and all(' Sword' not in x for x in start_inventory): + precollected_items.append('Progressive Sword') + if 'Progressive Sword' in pool: + pool.remove('Progressive Sword') + pool.append('Rupees (50)') + elif 'Fighter Sword' in pool: + pool.remove('Fighter Sword') + pool.append('Rupees (50)') return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) diff --git a/Main.py b/Main.py index eaba8769..b159eac3 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.0' +version_number = '1.3.0.1' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 391ec6a4..60b9a8e5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,14 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.1v + * Fixed bugs with item duping and disappearing drops + * Fixed multiworld crash + * Fixed assured sword missing when using start inventory (via GUI/CLI) + * Forbid extra statues in Swamp Push Statue room + * Forbid bumpers on OW waterr + * Forbid Stal on pits + * Text fix on sprite author (thanks Synack) * 1.2.0.19u * Added min/max for triforce pool, goal, and difference for CLI and Customizer. (Thanks Catobat) * Fixed a bug with dungeon generation diff --git a/Rom.py b/Rom.py index 53094d0b..bcbadd1b 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'c8b9a36f11cb3cd070f83f31219f5cba' +RANDOMIZERBASEHASH = '3302050481ab0c324fb6da95c8f3f099' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index a77da83b747d1a26e581131ea0200b90a8fcd1ca..54d6688c330e8e4f4a8baf3759a05ab02f77b1d3 100644 GIT binary patch delta 1465 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 Date: Wed, 20 Sep 2023 13:11:44 -0600 Subject: [PATCH 031/158] Several rom fixes, multiworld keys/lamp, pendant count, collection rate Fix for castle barrier gfx in rain state Chainchomp placement in Spiral Cave generation issue A bunch of new bans, Mimics, Stal, Bumper issues mostly. A few others. --- Main.py | 2 +- RELEASENOTES.md | 20 +++- Rom.py | 2 +- data/base2current.bps | Bin 117381 -> 117403 bytes source/enemizer/Enemizer.py | 3 +- source/enemizer/SpriteSheets.py | 5 +- source/enemizer/enemy_deny.yaml | 195 +++++++++++++++++++++++++------- 7 files changed, 182 insertions(+), 45 deletions(-) diff --git a/Main.py b/Main.py index b159eac3..ce8e2516 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.1' +version_number = '1.3.0.2' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 60b9a8e5..77d7707d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,12 +141,30 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.2v + * Fix for multiworld received keys not counting correctly + * Fix for multiworld lamps incorrect graphics + * Fix for collection rate decreasing on item "pickup" + * Fix for pendants as prizes counting as items + * Fix for castle barrier gfx in rain state + * Enemizer fixes and bans: + * Fixed a generation issue where ChainChomp placement would cause a failure. (Invincible enemies banned in Sprial Cave for early game traversal for now) + * Skull Pot Prison should not be blocked by "impassable" enemies + * Bumpers banned in Ice Hookshot room + * Fixed issue in GT Spike Crystal room + * Fixed blockage issues in TT Ambush and Compass rooms + * Forbid Bumper in Fairy Ascension cave; needed to clip into wall weirdly to pass. + * Enemy Drop bans + * Forbid Stals in many places where they cannot be woken up. Behind rails and on top of blocks, for example. + * A couple minor wizzrobes bans because of despawns. + * Enemies over pits and on conveyors near pits have been issued standard bans for falling enemies. Mimics join the ranks here as they don't work well on pits or on conveyors. + * Mimics banned where conveyors touch walls and could clip out unintentionally * 1.3.0.1v * Fixed bugs with item duping and disappearing drops * Fixed multiworld crash * Fixed assured sword missing when using start inventory (via GUI/CLI) * Forbid extra statues in Swamp Push Statue room - * Forbid bumpers on OW waterr + * Forbid bumpers on OW water * Forbid Stal on pits * Text fix on sprite author (thanks Synack) * 1.2.0.19u diff --git a/Rom.py b/Rom.py index bcbadd1b..010e86d6 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '3302050481ab0c324fb6da95c8f3f099' +RANDOMIZERBASEHASH = 'bc80079ef83a98f718be2a06c40546ce' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 54d6688c330e8e4f4a8baf3759a05ab02f77b1d3..aa06d0481365467b1975891c1bf8e84364e12aa3 100644 GIT binary patch delta 673 zcmV;S0$%-vl?R)Z2e7IE1&KeRKOnQM0Z{=2N^iB0lL7;R0aLSu12_i(U$edn@e&Wo zx2UTfWQ>i9gSP63iTbm?9eDu(A+tCi1O*FhwW#XqnmJP8m6DT3Bq14(x2UiO>gww1 z>VOF%+P0_~A|;ropp_!XvwbQ*}NwZrie63lk6*H5XbQOz* z0HvG1kKMOrHu9HnHu8yy02QOVffbV$fuE_DrKc+d-l>5Ai2wlr0RW`{rK?;7-j`*8 z4}mCx{|$hFLF*2HkNvkdGzfs3YAUEVfJ6u1vmw_B0Rv-hjk7J-_Yn|}&zV5*1R}t; zpp!R%O@cm?HR?b%`e4Ek`Z!?1Fuef6@FIl^rwh;=<_qQvrwh;&#tX&^uRipChYRKl z<_oVt^nQm6;vis?T7i?R>Sh6Uvl8o@cL7Ydu3`cG0u{|@Vt*tG&@TlFpJ-x#;t(M4 zvce#jzGVSh6;6wjWmJ&IJX4T?8LNVguObJ2pCOknW&vjeIx&Pzmzib(DmiMnsAy{< zf&ZRJzn7$$y?_qMB+wm`!hs5tg6RaQB!CTQOZ33D^b~+xI?{$~BEPt($pg>|NT(r} z_+|k%0d-#<#0rAmFzmUG|+Y0c)iV z$q3LZt#Sjo2!eQ!Hp&Y;xPJa07M`}z(i0^;7+4FW1q7(W|e^z zw}P3%AgO)1fD^ZbvBDs!EtlA60XhL#mlJ6LA|7Q@6@Zca|Ns9&y8=@cfQtcuvjBjB z<-3A`?yKKRw^L~WFIWvWNSdBc0hPi`0hPNUmyf{#T?aLBwZ@LO1h@0S0g4C+;|ge( HG#p95Gd(2X delta 632 zcmV-;0*C#Zl?R2D2e7IE1A#v%v#$YB0R+8tLW`3G1A+lcvxfsX2LV>IzzXpa55Tvm zs~u#Fjf#V|>WGQ)v%npB0RbPgIv)fD3u?8f>gt*~QsI@5lSw2Y8HTs0um|eu>gwu% z2_n(9s2U6_XZ$pQ)Fnrz-^BiGcu#qih4-r{I@mfe(Qwg8vPGfkEpI zfWJ|Jl4oU~M}XF|GS>+K1AbVDvp(4O5fPJpfPqPmB!C%#K=1@2z_y^1Me0B{k0yZ0 z`Z!?1Fuef6@FIl^rwh;=<_qSJLV&>(#tX&^uRipChYRKl<_oVtkDiUSh6Ivm)!8cL7SbzG4CW0u#k(Vt*tG&@BZDpJ-x#;t(M4vX{_h0b3PHi<4ngkjFSv zkbxPif{m{t2Y#O&mq2C#X9PAegk_hjW&tWSVYsMhYa)UFo=Clb4#_0Y9h35b3X_8A z1gRu|4QNaBx3=^YfLuD#hHE0WxTwhk&F^Iqs+iqgm#}96ZX3O~t6(74w<2BkoiG7wr47jl&?~KS1JEItS(iCz0f7Omm%C^I z85$~MpR*-qm4Owvf| Date: Tue, 26 Sep 2023 08:59:31 -0600 Subject: [PATCH 032/158] Ability to enemize fairies Fix for blue square in caves Lower limit for sprites on tiles that support it, to help with pot lifting Swamp waterway enemies fixed to not drop --- DoorShuffle.py | 1 + Doors.py | 2 + Dungeons.py | 9 +- EntranceShuffle.py | 8 ++ Regions.py | 17 ++-- Rom.py | 2 +- Rules.py | 4 +- data/base2current.bps | Bin 117403 -> 117439 bytes source/dungeon/EnemyList.py | 142 ++++++++++++++++----------- source/enemizer/Enemizer.py | 4 +- source/enemizer/EnemyLogic.py | 13 ++- source/enemizer/SpriteSheets.py | 3 +- source/enemizer/enemy_deny.yaml | 102 +++++++++++++------ source/enemizer/enemy_weight.yaml | 4 + source/enemizer/sheet_weight.yaml | 2 +- source/overworld/EntranceShuffle2.py | 8 ++ 16 files changed, 217 insertions(+), 104 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index e64a3c19..022c5e5c 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -4351,6 +4351,7 @@ default_door_connections = [ ('PoD Arena Crystals E', 'PoD Sexy Statue W'), ('PoD Mimics 1 NW', 'PoD Conveyor SW'), ('PoD Map Balcony WS', 'PoD Arena Ledge ES'), + ('PoD Map Balcony ES', 'PoD Fairy Pool WS'), ('PoD Falling Bridge WN', 'PoD Dark Maze EN'), ('PoD Dark Maze E', 'PoD Big Chest Balcony W'), ('PoD Sexy Statue NW', 'PoD Mimics 2 SW'), diff --git a/Doors.py b/Doors.py index 309abbab..651f9bd4 100644 --- a/Doors.py +++ b/Doors.py @@ -410,6 +410,8 @@ def create_doors(world, player): create_door(player, 'PoD Map Balcony Drop Down', Lgcl), create_door(player, 'PoD Map Balcony to Ranged Crystal', Lgcl), create_door(player, 'PoD Map Balcony Ranged Crystal Exit', Lgcl), + create_door(player, 'PoD Map Balcony ES', Intr).dir(Ea, 0x2b, Bot, High).pos(0), + create_door(player, 'PoD Fairy Pool WS', Intr).dir(We, 0x2b, Bot, High).pos(0), create_door(player, 'PoD Map Balcony WS', Nrml).dir(We, 0x2b, Bot, High).pos(1), create_door(player, 'PoD Map Balcony South Stairs', StrS).dir(So, 0x2b, Left, High), create_door(player, 'PoD Conveyor North Stairs', StrS).dir(No, 0x3b, Left, High), diff --git a/Dungeons.py b/Dungeons.py index 6cf12837..3284617e 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -98,10 +98,11 @@ pod_regions = [ 'PoD Lobby', 'PoD Left Cage', 'PoD Middle Cage', 'PoD Shooter Room', 'PoD Pit Room', 'PoD Pit Room Blocked', 'PoD Arena Main', 'PoD Arena Main - Ranged Crystal', 'PoD Arena North', 'PoD Arena Bridge', 'PoD Arena Bridge - Ranged Crystal', 'PoD Arena Landing', 'PoD Arena Right', 'PoD Arena Right - Ranged Crystal', 'PoD Arena Ledge', 'PoD Arena Ledge - Ranged Crystal', 'PoD Sexy Statue', - 'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Conveyor', 'PoD Mimics 1', 'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room', - 'PoD Stalfos Basement', 'PoD Basement Ledge', 'PoD Big Key Landing', 'PoD Falling Bridge', 'PoD Falling Bridge Mid', - 'PoD Falling Bridge Ledge', 'PoD Dark Maze', 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement', - 'PoD Harmless Hellway', 'PoD Mimics 2', 'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal', + 'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Fairy Pool', 'PoD Conveyor', 'PoD Mimics 1', + 'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room', 'PoD Stalfos Basement', 'PoD Basement Ledge', + 'PoD Big Key Landing', 'PoD Falling Bridge', 'PoD Falling Bridge Mid', 'PoD Falling Bridge Ledge', 'PoD Dark Maze', + 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement', 'PoD Harmless Hellway', 'PoD Mimics 2', + 'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal', 'PoD Dark Pegs Landing', 'PoD Dark Pegs Right', 'PoD Dark Pegs Middle', 'PoD Dark Pegs Left', 'PoD Dark Pegs Landing - Ranged Crystal', 'PoD Dark Pegs Middle - Ranged Crystal', 'PoD Dark Pegs Left - Ranged Crystal', 'PoD Lonely Turtle', 'PoD Turtle Party', 'PoD Dark Alley', 'PoD Callback', 'PoD Boss', 'Palace of Darkness Portal' diff --git a/EntranceShuffle.py b/EntranceShuffle.py index cbc5cffb..a9907ce5 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2093,11 +2093,19 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Kakariko Well (top to back)', 'Kakariko Well (back)'), ('Blinds Hideout N', 'Blinds Hideout (Top)'), ('Bat Cave Door', 'Bat Cave (left)'), + ('Good Bee Cave Front to Back', 'Good Bee Cave (back)'), + ('Good Bee Cave Back to Front', 'Good Bee Cave'), + ('Capacity Upgrade East', 'Capacity Fairy Pool'), + ('Capacity Fairy Pool West', 'Capacity Upgrade'), + ('Bonk Fairy (Dark) Pool', 'Bonk Fairy Pool'), + ('Bonk Fairy (Light) Pool', 'Bonk Fairy Pool'), ('Hookshot Cave Front to Middle', 'Hookshot Cave (Middle)'), ('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'), ('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'), + ('Hookshot Cave Back to Fairy', 'Hookshot Cave (Fairy Pool)'), + ('Hookshot Cave Fairy to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'), ('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'), ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), diff --git a/Regions.py b/Regions.py index 509f11b7..9f986006 100644 --- a/Regions.py +++ b/Regions.py @@ -177,7 +177,8 @@ def create_regions(world, player): create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), create_cave_region(player, 'Library', 'the library', ['Library']), create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'), - create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'), + create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain', None, ['Bonk Fairy (Light) Pool']), + create_cave_region(player, 'Bonk Fairy Pool', 'a fairy fountain'), create_cave_region(player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']), create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']), create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'), @@ -188,11 +189,13 @@ def create_regions(world, player): create_cave_region(player, 'Light Hype Fairy', 'a fairy fountain'), create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'), create_cave_region(player, 'Lake Hylia Shop', 'a common shop', ['Lake Hylia Shop - Left', 'Lake Hylia Shop - Middle', 'Lake Hylia Shop - Right']), - create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']), + create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right'], ['Capacity Upgrade East']), + create_cave_region(player, 'Capacity Fairy Pool', 'near the queen of fairies', None, ['Capacity Fairy Pool West']), create_cave_region(player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']), create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']), - create_cave_region(player, 'Good Bee Cave', 'a cold bee'), + create_cave_region(player, 'Good Bee Cave', 'a cold bee', None, ['Good Bee Cave Front to Back']), + create_cave_region(player, 'Good Bee Cave (back)', 'a cold bee', None, ['Good Bee Cave Back to Front']), create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'), create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'), create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'), @@ -206,7 +209,8 @@ def create_regions(world, player): create_cave_region(player, 'Hookshot Cave (Bonk Islands)', 'a connector', ['Hookshot Cave - Bottom Right']), create_cave_region(player, 'Hookshot Cave (Hook Islands)', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Left']), create_cave_region(player, 'Hookshot Cave (Middle)', 'a connector', None, ['Hookshot Cave Middle to Back', 'Hookshot Cave Middle to Front']), - create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back Exit']), + create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back to Fairy', 'Hookshot Cave Back Exit']), + create_cave_region(player, 'Hookshot Cave (Fairy Pool)', 'a connector', None, ['Hookshot Cave Fairy to Back']), create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']), create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']), create_cave_region(player, 'Dark Death Mountain Shop', 'a common shop', ['Dark Death Mountain Shop - Left', 'Dark Death Mountain Shop - Middle', 'Dark Death Mountain Shop - Right']), @@ -226,7 +230,7 @@ def create_regions(world, player): create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'), create_cave_region(player, 'Hammer Peg Cave', 'a cave with an item', ['Peg Cave']), create_cave_region(player, 'Archery Game', 'a game of skill'), - create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'), + create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain', None, ['Bonk Fairy (Dark) Pool']), create_cave_region(player, 'Big Bomb Shop', 'the bomb shop'), create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'East Dark World Hint', 'a storyteller'), @@ -447,8 +451,9 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'PoD Arena Ledge', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge'], ['PoD Arena Ledge ES', 'PoD Arena Ledge to Ranged Crystal']), create_dungeon_region(player, 'PoD Arena Ledge - Ranged Crystal', 'Palace of Darkness', None, ['PoD Arena Ledge Ranged Crystal Exit']), create_dungeon_region(player, 'PoD Sexy Statue', 'Palace of Darkness', None, ['PoD Sexy Statue W', 'PoD Sexy Statue NW']), - create_dungeon_region(player, 'PoD Map Balcony', 'Palace of Darkness', ['Palace of Darkness - Map Chest'], ['PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony WS', 'PoD Map Balcony South Stairs', 'PoD Map Balcony Drop Down']), + create_dungeon_region(player, 'PoD Map Balcony', 'Palace of Darkness', ['Palace of Darkness - Map Chest'], ['PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony WS', 'PoD Map Balcony South Stairs', 'PoD Map Balcony Drop Down', 'PoD Map Balcony ES']), create_dungeon_region(player, 'PoD Map Balcony - Ranged Crystal', 'Palace of Darkness', None, ['PoD Map Balcony Ranged Crystal Exit']), + create_dungeon_region(player, 'PoD Fairy Pool', 'Palace of Darkness', None, ['PoD Fairy Pool WS']), create_dungeon_region(player, 'PoD Conveyor', 'Palace of Darkness', None, ['PoD Conveyor North Stairs', 'PoD Conveyor SW']), create_dungeon_region(player, 'PoD Mimics 1', 'Palace of Darkness', None, ['PoD Mimics 1 NW', 'PoD Mimics 1 SW']), create_dungeon_region(player, 'PoD Jelly Hall', 'Palace of Darkness', None, ['PoD Jelly Hall NW', 'PoD Jelly Hall NE']), diff --git a/Rom.py b/Rom.py index 010e86d6..3933ac76 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'bc80079ef83a98f718be2a06c40546ce' +RANDOMIZERBASEHASH = '6fbf895bae14a0c74ec8dc782d3b9a95' class JsonRom(object): diff --git a/Rules.py b/Rules.py index 052698cd..d21f564b 100644 --- a/Rules.py +++ b/Rules.py @@ -716,7 +716,9 @@ def bomb_rules(world, player): bonkable_doors = ['Two Brothers House Exit (West)', 'Two Brothers House Exit (East)'] # Technically this is incorrectly defined, but functionally the same as what is intended. bombable_doors = ['Ice Rod Cave', 'Light World Bomb Hut', 'Paradox Shop', 'Mini Moldorm Cave', 'Hookshot Cave Back to Middle', 'Hookshot Cave Front to Middle', 'Hookshot Cave Middle to Front', - 'Hookshot Cave Middle to Back', 'Dark Lake Hylia Ledge Fairy', 'Hype Cave', 'Brewery', + 'Hookshot Cave Middle to Back', 'Hookshot Cave Back to Fairy', 'Hookshot Cave Fairy to Back', + 'Good Bee Cave Front to Back', 'Good Bee Cave Back to Front', 'Capacity Upgrade East', + 'Capacity Fairy Pool West', 'Dark Lake Hylia Ledge Fairy', 'Hype Cave', 'Brewery', 'Paradox Cave Chest Area NE', 'Blinds Hideout N', 'Kakariko Well (top to back)', 'Light Hype Fairy'] for entrance in bonkable_doors: diff --git a/data/base2current.bps b/data/base2current.bps index aa06d0481365467b1975891c1bf8e84364e12aa3..80984e899379618bfc446f1d737abbe1b45a38c3 100644 GIT binary patch delta 1002 zcmWlYYfMvj9ESUAX?xmo>7cb#!NF0SpmiVvZ4;;C4mx4L?jQt_mX$0HCSz0F{}iFC zTvm2cj!=*@7G^ubfSkc8Rwy*tNCVpn(k0767ERFDn5AZBGFaAi@2BU>o9F%VS}j7C zML2l};5%{-4nscUhg}fnJD2h$8OAd}6=USdcpzs61!jOpqzY4uW$Uz8;LjmUNz6s8 zm!l+e(X|4uH+FFs`=>! zu~#@A9_EP-aAT{h2ek37RW-SBI~mlZ(3ujZI7z(6WH_Y&$C$i3u>d7Z)tp`i73A`& z5{jA4)ddizya z#EHg^e(zJA{RBE_-OFA+3L$}v?MjEGI7np6OCTr;m&^MtqEWF&%AquD)jROLP@DqK zW^{4i?n9TUBQ@|jyv5Gfz!Ja{s-qSVS29gu7vQ59PmtpkTJzr=?rrC~4o+InRSZ+c z3($4uAwmQUQAyrie1yD-a>Ocj})Gu|gPrfqqRE8=vYaM!g*TVD@t z3cf07j!b8tASj3uG#B^Lj|^bb93Lp>Wh Qr3u4IM4aOp>V*gY0Xu1+tN;K2 delta 982 zcmWlTeM}Q~9Eb1Q*Q30&R60ap&_g@XvGF$4>f#G9Dlin7W-KxZZ!I3%L}eq}WWOr{ z0|ctKCsb zkx$+PxJ=K(%aF?l;02Hre=u#KC1NvF^4;P%G06EbnFU~l&cJslWPZ*k3lva2YW_HW zKmT+1v zizdAg1k#f^aU{X+!-te2^O6qHb#sL|vCV7GRn2RZA`+QLPnqwvnuqfCtR|wCl;H|g zdGa?(`WJcdt{%x$C?6ef{lk4SnqHaP06O~VTzdFZU0Wn|yQu=c%%uXT=8xW75AY%1 zHc!SS=IPy2d;I5H>KUsBBQPyifR$mMQ}qm;&i{L70Thy=`?sWO=Q`BP-@a$bkBq~g zQRGDV8)KtNp)MZGk%`Ls{ySpKo{L$;cC~2m*3R}CB~pr}wz39i8gwh8t4?$c`<|HW zucOVQdf|&~2v^7iYYr?WL5%R;K?p@eWod?)2&=-Y!q~$?+dJ@`Tp@wKou;I()6gNd zs1^pHRQRD5mH<}S@p?d_&<$4G03YFj3Yr;%t zu^o+I06qJCBV@oacC8W8Gz-orap;KtZWl?oXKY@vPhq&KWwVWt2d!+A5wgL^ju{~% zGAU}{_n@-)s|)vytltRlCoKw&n$({KqWbB@Odze%Ufj3!+6l2w%aSSVyC(Qly7QCX zo@w@h2@+n~RX5IWYh);mU>^3Q+E34-b;6l%!jv)FSK{cz=aGEmaK#jx(iBd6n|-qh zh!-0wH0azvp-^a_OH`>reSmljQL8R9^{3-OiO}B!9r4o2P4jfLprOE^l$CCO$jSBRv-a!h!|13ftYSYcvwllCwC2P71dAOHXW diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index b07c867e..2a8a2377 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -1,4 +1,4 @@ -from collections import defaultdict +from collections import defaultdict, deque import typing import yaml @@ -10,7 +10,8 @@ except ImportError: from enum import IntFlag as FastEnum import RaceRandom as random -from BaseClasses import Location, LocationType +from BaseClasses import Location, LocationType, RegionType +from EntranceShuffle import door_addresses from Items import ItemFactory from Utils import snes_to_pc, pc_to_snes, int16_as_bytes @@ -491,7 +492,7 @@ def init_enemy_stats(): EnemySprite.RedRupee: EnemyStats(EnemySprite.RedRupee, True, ignore=True, dmg=0), EnemySprite.BombRefill1: EnemyStats(EnemySprite.BombRefill1, True, ignore=True, dmg=0), EnemySprite.BombRefill4: EnemyStats(EnemySprite.BombRefill4, True, ignore=True, dmg=0), - EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, True, ignore=True, dmg=0, dmask=0x10), + EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, False, False, 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), @@ -545,6 +546,7 @@ class Sprite(object): sprite.static = self.static sprite.water = self.water sprite.embedded = self.embedded + sprite.never_drop = self.never_drop return sprite def sprite_data(self): @@ -748,10 +750,10 @@ def init_vanilla_sprites(): create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08) create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08) create_sprite(0x001c, 0x19, SpriteType.Overlord, 0, 0x07, 0x08) - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07) - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08) - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07, 'GT Fairy Abyss') + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'GT Fairy Abyss') + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08, 'GT Fairy Abyss') + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08, 'GT Fairy Abyss') create_sprite(0x001e, EnemySprite.CrystalSwitch, 0x00, 0, 0x1a, 0x09) create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Bomb Drop - Top') create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Bomb Drop - Top') @@ -835,15 +837,15 @@ def init_vanilla_sprites(): create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11) create_sprite(0x002b, EnemySprite.Statue, 0x00, 0, 0x0a, 0x0a, fix=True) create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17, 'PoD Map Balcony') - create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17) - create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18) + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17, 'PoD Fairy Pool') + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18, 'PoD Fairy Pool') create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x05, 0x1a, 'PoD Map Balcony') create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a, 'PoD Map Balcony') - create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x17, 0x1a) + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x17, 0x1a, 'PoD Fairy Pool') create_sprite(0x002c, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x05) - create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x09, 0x04) - create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x06, 0x05) - create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x09, 0x04, 'Hookshot Cave (Fairy Pool)') + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x06, 0x05, 'Hookshot Cave (Fairy Pool)') + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'Hookshot Cave (Fairy Pool)') create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x06, 'Ice Compass Room') create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1c, 0x06, 'Ice Compass Room') create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x16, 0x08, 'Ice Compass Room') @@ -1051,9 +1053,9 @@ def init_vanilla_sprites(): create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x16, 0x08, 'Ice Narrow Corridor') create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x18, 0x08, 'Ice Narrow Corridor') create_sprite(0x004e, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x09, 'Ice Bomb Jump Catwalk') - create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x17, 0x06) - create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x14, 0x08) - create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x08) + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x17, 0x06, 'Ice Fairy') + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x14, 0x08, 'Ice Fairy') + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x08, 'Ice Fairy') create_sprite(0x0050, EnemySprite.GreenGuard, 0x00, 1, 0x17, 0x0e, 'Hyrule Castle West Hall') create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x18, 0x10, 'Hyrule Castle West Hall') create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x17, 0x12, 'Hyrule Castle West Hall') @@ -1150,8 +1152,8 @@ def init_vanilla_sprites(): create_sprite(0x005c, EnemySprite.WallCannonHorzTop, 0x00, 0, 0x0b, 0x02, embed=True) create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x05, 0x0e, embed=True) create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x0e, 0x0e, embed=True) - create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x18) - create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18) + create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x18, 'GT Refill') + create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18, 'GT Refill') create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x05, 'GT Gauntlet 2') create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x08, 0x06, 'GT Gauntlet 2') create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x08, 'GT Gauntlet 2') @@ -1366,8 +1368,8 @@ def init_vanilla_sprites(): create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x1b, 0x08, 'Desert West Wing') create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x14, 0x10, 'Desert West Wing') create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x05, 'Desert West Wing') - create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06) - create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08) + create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06, 'Desert Fairy Fountain') + create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08, 'Desert Fairy Fountain') create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x1b, 0x0b, 'Desert West Wing') create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x17, 0x10, 'Desert West Wing') create_sprite(0x0083, EnemySprite.Beamos, 0x00, 0, 0x08, 0x17, 'Desert West Lobby') @@ -1403,8 +1405,8 @@ def init_vanilla_sprites(): create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x19, 'Hera Basement Cage') create_sprite(0x0087, EnemySprite.SmallKey, 0x00, 0, 0x08, 0x1a, 'Hera Basement Cage') create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x1c, 'Hera Torches') - create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x10, 0x0a) - create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x0f, 0x0b) + create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x10, 0x0a, 'Eastern Fairies') + create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x0f, 0x0b, 'Eastern Fairies') create_sprite(0x008b, EnemySprite.Bumper, 0x00, 0, 0x15, 0x07, 'GT Conveyor Cross') create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18, 'GT Hookshot South Platform') create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18, 'GT Hookshot South Platform') @@ -1573,8 +1575,8 @@ def init_vanilla_sprites(): create_sprite(0x00a5, EnemySprite.BlueGuard, 0x00, 0, 0x13, 0x18, 'GT Dashing Bridge') create_sprite(0x00a6, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) create_sprite(0x00a6, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0e, 'GT Moldorm Pit') - create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08) - create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x09) + create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08, 'Hera Fairies') + create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x09, 'Hera Fairies') create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x0e, 'Eastern West Wing') create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x0e, 'Eastern West Wing') create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x12, 'Eastern West Wing') @@ -1655,7 +1657,7 @@ def init_vanilla_sprites(): create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07, 'TR Chain Chomps Top') create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x04) create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x0c, 0x04) - create_sprite(0x00b6, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) + create_sprite(0x00b6, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07, 'TR Refill') create_sprite(0x00b6, EnemySprite.Pokey, 0x00, 0, 0x07, 0x15, 'TR Pokey 1', True, 0xe4) create_sprite(0x00b6, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x07, 0x1b, 'TR Pokey 1') @@ -1896,10 +1898,10 @@ def init_vanilla_sprites(): create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x09, 'Tower Room 03') create_sprite(0x00e1, EnemySprite.HeartPiece, 0x00, 0, 0x17, 0x0d) create_sprite(0x00e1, EnemySprite.AdultNpc, 0x00, 1, 0x07, 0x12) - create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06) - create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x06) - create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07) - create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07) + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x06, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'Lumberjack Tree (top)') create_sprite(0x00e2, EnemySprite.HeartPiece, 0x00, 0, 0x13, 0x10) create_sprite(0x00e3, EnemySprite.MagicBat, 0x00, 1, 0x17, 0x05) create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x19, 0x07, 'Old Man House', embed=True) # partial @@ -1966,16 +1968,16 @@ def init_vanilla_sprites(): create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x15, 0x0f, 'Spectacle Rock Cave (Bottom)') create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x11, 0x13, 'Spectacle Rock Cave (Bottom)') create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x17, 'Spectacle Rock Cave (Bottom)') - 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(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x17, 0x0e, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x18, 0x10, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x15, 0x11, 'Spectacle Rock Cave (Bottom)') create_sprite(0x00fb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x0d, 'Bumper Cave (bottom)') 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) - create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) + create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x16, 0x08, 'Fairy Ascension Cave (Top)') + create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08, 'Fairy Ascension Cave (Top)') create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x11, 'Fairy Ascension Cave (Bottom)') create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x16, 0x12, 'Spiral Cave (Bottom)') create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x16, 'Spiral Cave (Bottom)') @@ -2008,10 +2010,10 @@ def init_vanilla_sprites(): create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x0f, 0x09) create_sprite(0x010b, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x12, 0x03) create_sprite(0x010b, EnemySprite.AntiFairy, 0x00, 0, 0x0d, 0x07, 'Dam') - create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) - create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07) - create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08) - create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08) + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08, 'Hookshot Fairy') create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x07, 0x14, 'Mimic Cave') create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x08, 0x14, 'Mimic Cave') create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x14, 'Mimic Cave') @@ -2028,10 +2030,10 @@ def init_vanilla_sprites(): create_sprite(0x0114, EnemySprite.FairyPondTrigger, 0x00, 0, 0x07, 0x18) create_sprite(0x0114, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x19, 0x14) create_sprite(0x0115, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x16) - create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07) - 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.Faerie, 0x00, 0, 0x17, 0x07, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08, 'Capacity Fairy Pool') 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) @@ -2040,15 +2042,15 @@ def init_vanilla_sprites(): create_sprite(0x011b, EnemySprite.HeartPiece, 0x00, 0, 0x18, 0x09) create_sprite(0x011b, EnemySprite.HeartPiece, 0x00, 1, 0x05, 0x16) create_sprite(0x011c, EnemySprite.BombShopGuy, 0x00, 0, 0x09, 0x19) - create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x07) - create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x07) - create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x08) - create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08) + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x07, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x07, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x08, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08, 'Long Fairy Cave') create_sprite(0x011e, EnemySprite.Shopkeeper, 0x00, 0, 0x18, 0x16) create_sprite(0x011f, EnemySprite.Shopkeeper, 0x00, 0, 0x17, 0x16) create_sprite(0x0120, EnemySprite.GoodBee, 0x00, 0, 0x17, 0x07) - create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1b, 0x08) - create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x09) + create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1b, 0x08, 'Good Bee Cave (back)') + create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x09, 'Good Bee Cave (back)') create_sprite(0x0121, EnemySprite.Smithy, 0x00, 0, 0x04, 0x17) create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x07, 0x18) create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x17, 0x18) @@ -2059,10 +2061,10 @@ def init_vanilla_sprites(): create_sprite(0x0123, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x05) create_sprite(0x0124, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) create_sprite(0x0125, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) - create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x15) - create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x15) - create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x16) - create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16) + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x15, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x15, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x16, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16, 'Bonk Fairy Pool') create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) @@ -2157,8 +2159,7 @@ class EnemyTable: rom.write_byte(snes_to_pc(0x28AF00) + super_tile, pointer_index) is_even = len(dungeon_list) % 2 == 0 for dungeon, bitmask in dungeon_list.items(): - dungeon_id = dungeon * 2 - rom.write_bytes(snes_to_pc(0x28B028) + pointer, [dungeon_id] + int16_as_bytes(bitmask)) + rom.write_bytes(snes_to_pc(0x28B028) + pointer, [dungeon] + int16_as_bytes(bitmask)) pointer += 3 if is_even: rom.write_bytes(snes_to_pc(0x28B028) + pointer, [0xFF, 0xFF]) @@ -2177,7 +2178,10 @@ def setup_enemy_locations(world, player): # 1,1,1,1,2,1,2,2,1,2,3,2,2,2,2,2,2,1,2,1(2),1,1,1,1,2 splittable_supertiles = {0x9, 0x1a, 0x35, 0x36, 0x37, 0x2a, 0x57, 0x74, 0x75, 0x6a, 0x7b, 0x7c, 0x7d, 0x9d, 0x8c, - 0x9b, 0x87, 0x82, 0xa2, 0xb2, 0xb6, 0xa9, 0xaa, 0xbc, 0xdb, 0xd1} + 0x9b, 0x87, 0x82, 0xa2, 0xb2, 0xb6, 0xa9, 0xaa, 0xbc, 0xdb, 0xd1, + # fairy needs + 0x107, 0x10c, 0x115, 0x11e, 0x120, 0x126} + # minimum 159 bytes maybe reserve 256 (0x100) # tr pipes, mire left/right bridges, eastern cannonball, tr front entrance = have zero enemies so far but are splittable # 0x14, 0xa2, 0xb9, 0xd6 @@ -2190,6 +2194,7 @@ splittable_supertiles = {0x9, 0x1a, 0x35, 0x36, 0x37, 0x2a, 0x57, 0x74, 0x75, 0x def setup_enemy_dungeon_tables(world, player): enemy_table = world.data_tables[player].uw_enemy_table dungeon_map = defaultdict(lambda: defaultdict(list)) + super_tile_entrance_id_map = {} # for memoization, very minor optimization for super_tile, enemy_list in enemy_table.room_map.items(): if super_tile in splittable_supertiles: idx_adj = 0 @@ -2201,8 +2206,14 @@ def setup_enemy_dungeon_tables(world, player): continue # overlords can't have locations if loc: # possible to-do: caves really aren't supported yet - entrance ids? - dungeon = loc.parent_region.dungeon.dungeon_id if loc.parent_region.dungeon else 0xFF - dungeon_map[super_tile][dungeon].append((loc, index-idx_adj)) + if loc.parent_region.dungeon: + dungeon = loc.parent_region.dungeon.dungeon_id * 2 + dungeon_map[super_tile][dungeon].append((loc, index-idx_adj)) + else: + if super_tile not in super_tile_entrance_id_map: + super_tile_entrance_id_map[super_tile] = find_entrance_ids(loc.parent_region) + for entrance_id in super_tile_entrance_id_map[super_tile]: + dungeon_map[super_tile][entrance_id].append((loc, index-idx_adj)) special_bitmasks = defaultdict(lambda: defaultdict(int)) for super_tile, dungeon_list in dungeon_map.items(): for dungeon, data_list in dungeon_list.items(): @@ -2211,6 +2222,21 @@ def setup_enemy_dungeon_tables(world, player): enemy_table.special_bitmasks = special_bitmasks +def find_entrance_ids(region): + entrance_list = [] + queue = deque([region]) + visited = {region} + while len(queue) > 0: + current = queue.popleft() + for ent in current.entrances: + if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]: + entrance_list.append(door_addresses[ent.name][0] + 1) + elif ent.parent_region not in visited: + queue.append(ent.parent_region) + visited.add(ent.parent_region) + return entrance_list + + def valid_drop_location(sprite, index, world, player): if world.dropshuffle[player] == 'underworld': if sprite.drops_item and sprite.drop_item_kind in [0xe4, 0xe5]: @@ -2572,6 +2598,7 @@ sprite_translation = { 'Debirando': EnemySprite.Debirando, 'DebirandoPit': EnemySprite.DebirandoPit, 'FakeMasterSword': EnemySprite.FakeMasterSword, + 'Faerie': EnemySprite.Faerie, 'FireballZora': EnemySprite.FireballZora, 'Firesnake': EnemySprite.Firesnake, 'FloatingSkull': EnemySprite.FloatingSkull, @@ -2632,6 +2659,7 @@ sprite_translation = { 'Toppo': EnemySprite.Toppo, 'UsainBolt': EnemySprite.UsainBolt, 'Vulture': EnemySprite.Vulture, + 'YellowStalfos': EnemySprite.YellowStalfos, 'Wallmaster': EnemySprite.Wallmaster, 'Wizzrobe': EnemySprite.Wizzrobe, 'Zora': EnemySprite.Zora, diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index c7408e66..539591d1 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -273,7 +273,7 @@ sprite_limiter = { def exceeds_sprite_limit(limit, sprite): - return sprite_limiter[sprite.sprite]-1+limit > 16 if sprite.sprite in sprite_limiter else False + return sprite_limiter[sprite.sprite]-1+limit > 15 if sprite.sprite in sprite_limiter else False def randomize_underworld_rooms(data_tables, world, player, custom_uw): @@ -282,7 +282,7 @@ def randomize_underworld_rooms(data_tables, world, player, custom_uw): specific = setup_specific_requirements(data_tables) 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, + if room_id in {0, 1, 3, 6, 7, 0xd, 0x14, 0x20, 0x29, 0x30, 0x33, 0x4d, 0x5a, 0x90, 0xa4, 0xac, 0xc8, 0xde}: continue current_sprites = data_tables.uw_enemy_table.room_map[room_id] diff --git a/source/enemizer/EnemyLogic.py b/source/enemizer/EnemyLogic.py index 4bb66d8f..67f6eb38 100644 --- a/source/enemizer/EnemyLogic.py +++ b/source/enemizer/EnemyLogic.py @@ -223,7 +223,7 @@ special_rules_check = { 'Swamp Waterway': None, 'Hera Back': [5, 6], 'GT Petting Zoo': [5, 8, 9, 11], - 'Mimic Cave': [3, 4], + 'Mimic Cave': [4, 5, 6, 7], 'Ice Hookshot Ledge': None, 'TR Hub Ledges': [3, 4, 5, 6, 7], 'TR Dark Ride': [1, 2, 3], @@ -233,7 +233,8 @@ special_rules_check = { 'Old Man House': [1, 3], 'Old Man House Back': [4, 5, 6], 'Death Mountain Return Cave (left)': None, - 'Death Mountain Return Cave (right)': [1, 2, 3, 6, 7] # 2, 5, 6 are considered embedded + 'Death Mountain Return Cave (right)': [1, 2, 3, 6, 7], # 2, 5, 6 are considered embedded + 'Hookshot Fairy': [0, 1, 2, 3] } @@ -244,6 +245,11 @@ def special_rules_for_region(world, player, region_name, location, original_rule 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 region_name == 'Mimic Cave': + if enemy_number in [4, 5]: # these are behind hammer blocks potentially + return and_rule(original_rule, has('Hammer', player)) + elif enemy_number in special_rules_check[region_name]: # these are behind rails + return and_rule(original_rule, has_boomerang(player)) if enemy_number in special_rules_check[region_name]: return and_rule(original_rule, has_boomerang(player)) else: @@ -255,7 +261,8 @@ def special_rules_for_region(world, player, region_name, location, original_rule else: return original_rule elif region_name in ['Old Man Cave (West)', 'Old Man Cave (East)', 'Old Man House Back', 'Old Man House', - 'Death Mountain Return Cave (left)', 'Death Mountain Return Cave (right)']: + 'Death Mountain Return Cave (left)', 'Death Mountain Return Cave (right)', + 'Hookshot Fairy']: enemy_number = int(location.name.split('#')[1]) if region_name == 'Death Mountain Return Cave (left)': if enemy_number in [1, 5]: diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index 50d12ff6..fa703e20 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -283,6 +283,7 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e), SpriteRequirement(EnemySprite.Kodongo).sub_group(2, 0x2a), + # SpriteRequirement(EnemySprite.YellowStalfos).sub_group(0, 0x1f), # doesn't spawn 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? @@ -360,7 +361,7 @@ def init_sprite_requirements(): 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), - SpriteRequirement(EnemySprite.Faerie).affix(), + SpriteRequirement(EnemySprite.Faerie).immune(), SpriteRequirement(EnemySprite.FakeMasterSword).immune().sub_group(3, 0x11), SpriteRequirement(EnemySprite.MagicShopAssistant).affix().sub_group(0, 0x4b).sub_group(3, 0x5a), SpriteRequirement(EnemySprite.SomariaPlatform).affix().sub_group(2, 0x27), diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 4ff270f0..f4866764 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -33,7 +33,7 @@ UwGeneralDeny: - [ 0x0021, 3, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 2" - [ 0x0021, 4, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 3" - [ 0x0024, 6, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots - - [ 0x0026, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Big Spoon - Red Bari 1" + - [ 0x0026, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 1" - [ 0x0026, 8, [ "AntiFairyCircle", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 3" - [ 0x0026, 9, [ "RollerHorizontalRight", "Statue" ] ] #"Swamp Palace - Big Spoon - Kyameron" - [ 0x0026, 10, [ "Statue" ] ] # multiple push statues in this room can cause issues @@ -45,6 +45,7 @@ UwGeneralDeny: - [ 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, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ]] - [ 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" @@ -289,6 +290,10 @@ UwGeneralDeny: - [ 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" + - [ 0x00ce, 4, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 5, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 6, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - [ 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, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Eye Bridge - Hardhat Beetle" @@ -334,8 +339,14 @@ UwGeneralDeny: - [ 0x00f1, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 5" - [ 0x00f1, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 6" - [ 0x00fd, 0, [ "Bumper" ] ] + - [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] OwGeneralDeny: - - [0x1e, 3, ["Beamos"]] # forbid a beamos here + - [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here + - [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo - [0x5e, 5, ["Gibo"]] # kiki eating Gibo UwEnemyDrop: @@ -343,8 +354,8 @@ UwEnemyDrop: - [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return - [0x00cc, 5, ["Babasu"]] # little hard to see and kill appropriately # the following are behind rails - - [0x0077, 4, ["StalfosKnight", "Geldman", "Blob", "Stal"]] # can't activate here - - [0x0077, 5, ["StalfosKnight", "Geldman", "Blob", "Stal"]] + - [0x0077, 4, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # can't activate here + - [0x0077, 5, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] - [0x007D, 4, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x007D, 7, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x007D, 8, ["StalfosKnight", "Geldman", "Blob", "Stal"]] @@ -357,10 +368,10 @@ UwEnemyDrop: # because they despawned or clipped away or immediately fell, etc - [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0044, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] @@ -381,73 +392,108 @@ UwEnemyDrop: "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0095, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0095, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Bumper", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Bumper", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - - [0x00e6, 6, [ "HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", - "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - # wizzrobe despawn issues - on pots/blocks + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00e6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + # wizzrobe despawn issues - on pots/blocks - too close to some object + - [0x0016, 0, ["Wizzrobe"]] + - [0x0016, 1, ["Wizzrobe"]] + - [0x0016, 2, ["Wizzrobe"]] + - [0x0016, 3, ["Wizzrobe"]] + - [0x0019, 2, ["Wizzrobe"]] + - [0x0019, 3, ["Wizzrobe"]] + - [0x002a, 3, ["Wizzrobe"]] + - [0x002a, 7, ["Wizzrobe"]] + - [0x0035, 5, ["Wizzrobe"]] + - [0x0036, 8, ["Wizzrobe"]] + - [0x003c, 1, ["Wizzrobe"]] + - [0x004b, 2, ["Wizzrobe"]] + - [0x004b, 6, ["Wizzrobe"]] + - [0x004b, 7, ["Wizzrobe"]] - [0x004e, 3, ["Wizzrobe", "Stal"]] + - [0x0054, 3, ["Wizzrobe", "Stal"]] + - [0x0055, 2, ["Wizzrobe"]] # slightly on wall - [0x005e, 4, ["Wizzrobe", "Stal"]] + - [0x0067, 5, ["Wizzrobe"]] + - [0x0067, 6, ["Wizzrobe"]] + - [0x0067, 7, ["Wizzrobe", "Stal"]] + - [0x0067, 8, ["Wizzrobe", "Stal"]] + - [0x0074, 5, ["Wizzrobe"]] - [0x007c, 1, ["Wizzrobe", "Stal"]] - [0x007c, 3, ["Wizzrobe", "Stal"]] - [0x007e, 1, ["Wizzrobe", "Stal"]] - [0x007e, 6, ["Wizzrobe", "Stal"]] + - [0x0083, 9, ["Wizzrobe"]] - [0x008b, 6, ["Wizzrobe", "Stal"]] - [0x008b, 7, ["Wizzrobe", "Stal"]] - [0x008d, 9, ["Wizzrobe", "Stal"]] - [0x009f, 5, ["Wizzrobe", "Stal"]] + - [0x00a1, 1, ["Wizzrobe"]] - [0x00af, 0, ["Wizzrobe", "Stal"]] + - [0x00b2, 4, ["Wizzrobe"]] - [0x00bf, 1, ["Wizzrobe"]] - - [0x00ce, 5, ["Wizzrobe"]] - - [0x00ce, 6, ["Wizzrobe"]] - - [0x00ce, 7, ["Wizzrobe"]] - - [0x00ce, 8, ["Wizzrobe"]] + - [0x00c1, 3, ["Wizzrobe", "Stal"]] + - [0x00c2, 0, ["Wizzrobe"]] + - [0x00c2, 3, ["Wizzrobe"]] + - [0x00c2, 7, ["Wizzrobe"]] + - [0x00ce, 5, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00ce, 6, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00ce, 7, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00ce, 8, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] - [0x00d5, 4, ["Wizzrobe"]] + - [0x00df, 1, ["Wizzrobe"]] # slightly on wall + - [0x00e7, 4, ["Wizzrobe"]] # slightly on wall + - [0x00fd, 1, ["Wizzrobe"]] # slightly on rock - [0x010c, 4, ["Wizzrobe"]] - [0x010c, 5, ["Wizzrobe"]] # other mimic cave spots are in the rail section diff --git a/source/enemizer/enemy_weight.yaml b/source/enemizer/enemy_weight.yaml index 58126bf3..7cef3326 100644 --- a/source/enemizer/enemy_weight.yaml +++ b/source/enemizer/enemy_weight.yaml @@ -25,6 +25,7 @@ UW: # Total 94431 Deadrock: 186 # 0.53796% raw:508 4.49558% Debirando: 154 # 0.65127% raw:615 5.44248% DebirandoPit: 154 # 0.65127% raw:615 5.44248% + Faerie: 19 FakeMasterSword: 120 # 0.81223% raw:767 6.78761% FireballZora: 165 # 0.60679% raw:573 5.07080% FirebarCCW: 49 # 2.02688% raw:1914 16.93805% @@ -97,6 +98,7 @@ UW: # Total 94431 Vulture: 445 # 0.22450% raw:212 1.87611% Wallmaster: 247 # 0.40453% raw:382 3.38053% Wizzrobe: 306 # 0.32722% raw:309 2.73451% +# YellowStalfos: 49 Zora: 609 # 0.16414% raw:155 1.37168% Zoro: 80 # 1.15428% raw:1090 9.64602% OW: # Total 117724 @@ -126,6 +128,7 @@ OW: # Total 117724 Deadrock: 144 # 0.69570% raw:819 7.24779% Debirando: 164 # 0.60905% raw:717 6.34513% DebirandoPit: 164 # 0.60905% raw:717 6.34513% + Faerie: 19 FakeMasterSword: 120 # 1.15609% raw:1361 12.04425% FireballZora: 119 # 0.84350% raw:993 8.78761% FirebarCCW: 47 # 2.11767% raw:2493 22.06195% @@ -198,5 +201,6 @@ OW: # Total 117724 Vulture: 229 # 0.43746% raw:515 4.55752% Wallmaster: 643 # 0.15545% raw:183 1.61947% Wizzrobe: 345 # 0.28966% raw:341 3.01770% +# YellowStalfos: 47 Zora: 558 # 0.17923% raw:211 1.86726% Zoro: 100 # 0.84605% raw:996 8.81416% \ No newline at end of file diff --git a/source/enemizer/sheet_weight.yaml b/source/enemizer/sheet_weight.yaml index 03d151c5..07adea77 100644 --- a/source/enemizer/sheet_weight.yaml +++ b/source/enemizer/sheet_weight.yaml @@ -43,7 +43,7 @@ SheetChoices: - slots: [0] assignments: 0: 0x1f - weight: 7 # Sparks, Firebars, FloatingSkull, RedBari, BlueBari, Firesnake, Stalfos + weight: 7 # Sparks, Firebars, FloatingSkull, RedBari, BlueBari, Firesnake, Stalfos, ~~YellowStalfos~~ - slots: [0] assignments: 0: 0x2f diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 23edc76c..60c56a19 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1888,11 +1888,19 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Kakariko Well (top to back)', 'Kakariko Well (back)'), ('Blinds Hideout N', 'Blinds Hideout (Top)'), ('Bat Cave Door', 'Bat Cave (left)'), + ('Good Bee Cave Front to Back', 'Good Bee Cave (back)'), + ('Good Bee Cave Back to Front', 'Good Bee Cave'), + ('Capacity Upgrade East', 'Capacity Fairy Pool'), + ('Capacity Fairy Pool West', 'Capacity Upgrade'), + ('Bonk Fairy (Dark) Pool', 'Bonk Fairy Pool'), + ('Bonk Fairy (Light) Pool', 'Bonk Fairy Pool'), ('Hookshot Cave Front to Middle', 'Hookshot Cave (Middle)'), ('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'), ('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'), + ('Hookshot Cave Back to Fairy', 'Hookshot Cave (Fairy Pool)'), + ('Hookshot Cave Fairy to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'), ('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'), ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), From f4c465f5c3c504df895bcf08eb3c0ee9780f3b26 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 26 Sep 2023 13:40:22 -0600 Subject: [PATCH 033/158] Baserom update (colletion rate counter for mystery) Minor issue with customizer/mystery yamls and booleans --- Main.py | 2 +- RELEASENOTES.md | 9 +++++++++ Rom.py | 2 +- data/base2current.bps | Bin 117439 -> 117445 bytes source/tools/MysteryUtils.py | 36 +++++++++++++++++------------------ 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Main.py b/Main.py index ce8e2516..eacd2ca3 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.2' +version_number = '1.3.0.3' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 77d7707d..b2b5d838 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,15 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.3v + * Faeries now part of the enemy shuffle pool. Take note, this will increase enemy drop locations to include fairy pools both in dungeons and in caves. + * Enemy drop indicator (blue square) now works in caves based on entrance used + * Fixes: + * Collection rate counter is properly hidden in mystery seeds + * Sprite limit lowered where possible to allow for lifting of pots + * Hovers in Swamp Waterway properly do not drop items anymore + * Lots more bans (thanks to jsd in particular but also thanks to all the reports) + * Minor issue with customizer/mystery files not allowing "true" for booleans * 1.3.0.2v * Fix for multiworld received keys not counting correctly * Fix for multiworld lamps incorrect graphics diff --git a/Rom.py b/Rom.py index 3933ac76..15196757 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '6fbf895bae14a0c74ec8dc782d3b9a95' +RANDOMIZERBASEHASH = '8f9863c742096d16cae181fe314be3d7' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 80984e899379618bfc446f1d737abbe1b45a38c3..1befcc5427d0a3653117beba996e2add6a951559 100644 GIT binary patch delta 692 zcmWNKTS!xJ9LM+b?3QLuol}(R=*C#KI}x>jO(EaMWkkEM-84uf9@U2ra&@ z?&z?Lcs`YN>>lU8A%%syvK>z`>E+Ws5|XPKNe=3r5{<1OY%2`gio&*%u)VYhSq&b(X!1`ZM$G30@n=`5D#Y;G!J28$b(F1Q z{wMqJP zIWZ3F$`jF~8mXHhQ>`oK84{wA$a?(nZz?p_m1AKtL|)7gr5(kjTevr&E5fjr8n*bR z9vM?CR$jZpB{Xa?dOUYLsCP}~H*A?*GBj$9l5xmnjzrkjepb@Ynm+E?bv@DcmB0hU zP2(SsfP*!Zc7VE#J~zV=c!~ZsLpfBTawFtHHR?1%s!B6qm3y{_E$-#S4ZS(h$ zrji{%VxgiAfWXdJ3;ZX~Lf<7wLZJX?puzt=0Dm!w X793QI|HoT+FO{!$CU1QGweamfGDax5 delta 729 zcmW-aT}V@L9ESJvxDOL+=C&j&j#JkbCG#Wms}!n>Y*Bcj?nDL=p{}CrgRq$Y*|s?5 z$&b_a_cZFPbRLgl8x36K#l%|EB=8~)j6k}G=t55BMXjPnckcu5^S&{UGU`$KiPXjB zU60vRqDGXG-HtS)3`4GnH{~RXL_DY7e`$ z4aKU0DM(mIpHcfe$)3KrMUC4Vpa#z4XAO{Fd>C`7Qqc@K(ZW_^PBL`YjRo6rj*k1O zj;^fR$zPax5*N)zW;h;IOQ9rVRU`Clp7gR*S3oISAS{z!G1C}S;gLqjRTO%J?9Mv; zwh^+^mN0inhtxw?@MhzSs?1aac0sq zIAabB8S1~e0!rLZCr~ zf)JKl;Q&4+K^OITJ;ySgu zl7f%nAuAZtB`%asa$KmI9LMjhu(NZV3ti&dnsvzaO2b2|4si<7F2*$c0QEdj%cmyc zi-zyQH(GSa<4n>I&3M6S9s2J$R0FC4#P_(e(}K>h3en>4htZN-S!jy7nT6=xqx^tX zkM0&BF4r#Z8BNZ#I~ZrK-G;SV&XX3{Fqfg0;mZ9zOu#=^7P*M;P>`1?afuKSkl924 zTWhV%vV^B7D29W0l>&;e(Z Date: Wed, 27 Sep 2023 09:10:55 -0600 Subject: [PATCH 034/158] Fix location of new doors --- DoorShuffle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 022c5e5c..6fe70c3d 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -4087,6 +4087,7 @@ interior_doors = [ ('PoD Lobby NE', 'PoD Middle Cage SE'), ('PoD Warp Hint SE', 'PoD Jelly Hall NE'), ('PoD Jelly Hall NW', 'PoD Mimics 1 SW'), + ('PoD Map Balcony ES', 'PoD Fairy Pool WS'), ('PoD Falling Bridge EN', 'PoD Compass Room WN'), ('PoD Compass Room SE', 'PoD Harmless Hellway NE'), ('PoD Mimics 2 NW', 'PoD Bow Statue SW'), @@ -4351,7 +4352,6 @@ default_door_connections = [ ('PoD Arena Crystals E', 'PoD Sexy Statue W'), ('PoD Mimics 1 NW', 'PoD Conveyor SW'), ('PoD Map Balcony WS', 'PoD Arena Ledge ES'), - ('PoD Map Balcony ES', 'PoD Fairy Pool WS'), ('PoD Falling Bridge WN', 'PoD Dark Maze EN'), ('PoD Dark Maze E', 'PoD Big Chest Balcony W'), ('PoD Sexy Statue NW', 'PoD Mimics 2 SW'), From 6c529cc7deffe9a2e6adcbe85cb68bfd78d7cee9 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 29 Sep 2023 13:06:10 -0600 Subject: [PATCH 035/158] Some more trap door exceptions (can be opened by trigger and should be bunny impassible) --- DoorShuffle.py | 6 +++--- Rules.py | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 303963dd..4a48cd18 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -2425,15 +2425,15 @@ def change_door_to_trap(d, world, player): trap_door_exceptions = { 'PoD Mimics 2 SW', 'TR Twin Pokeys NW', 'Thieves Blocked Entry SW', 'Hyrule Dungeon Armory Interior Key Door N', 'Desert Compass Key Door WN', 'TR Tile Room SE', 'Mire Cross SW', 'Tower Circle of Pots ES', - 'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S', + 'PoD Mimics 1 SW', 'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S', 'Skull 2 East Lobby WS', 'GT Hope Room WN', 'Eastern Courtyard Ledge S', 'Ice Lobby SE', 'GT Speed Torch WN', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Skull Torch Room WS', 'GT Speed Torch NE', 'GT Speed Torch WS', 'GT Torch Cross WN', 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Torches WN', 'PoD Lobby N', 'PoD Middle Cage S', - 'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'GT Conveyor Cross EN', 'Eastern Pot Switch WN', + 'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Eastern Pot Switch WN', 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Eastern Dark Square Key Door WN', 'Eastern Lobby NW', 'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Desert Back Lobby S', 'Desert West S', 'Desert West Lobby ES', 'Mire Hidden Shooters SE', 'Mire Hidden Shooters ES', 'Mire Hidden Shooters WS', - 'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS', + 'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS', 'GT Conveyor Cross EN', } diff --git a/Rules.py b/Rules.py index 0553aa8a..1f576316 100644 --- a/Rules.py +++ b/Rules.py @@ -2107,17 +2107,16 @@ bunny_impassible_doors = { 'GT Validation Block Path' } -# todo: for trap_door_exceptions: 'Ice Tall Hint SE' (not excepted) -# todo: 'Eastern Courtyard Ledge S', 'PoD Lobby N', 'Ice Tall Hint SE', 'TR Tongue Pull WS' # these should generally match trap_door_exceptions unless the switch is in the open/push block bunny_impassible_if_trapped = { 'Hyrule Dungeon Armory Interior Key Door N', 'Eastern Pot Switch WN', 'Eastern Lobby NW', - 'Eastern Lobby NE', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES', 'PoD Mimics 2 SW', - 'PoD Middle Cage S', 'Swamp Push Statue S', 'Skull 2 East Lobby WS', 'Skull Torch Room WS', - 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Thieves Blocked Entry SW', 'Ice Bomb Jump NW', - 'Ice Tall Hint EN', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW', - 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN', + 'Eastern Lobby NE', 'Eastern Courtyard Ledge S', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES', + 'PoD Mimics 1 SW', 'PoD Mimics 2 SW', 'PoD Middle Cage S', 'PoD Lobby N', 'Swamp Push Statue S', + 'Skull 2 East Lobby WS', 'Skull Torch Room WS', 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', + 'Thieves Blocked Entry SW', 'Ice Bomb Jump NW', + 'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW', + 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Tongue Pull WS', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN', 'GT Speed Torch NE', 'GT Speed Torch WS', 'GT Torch Cross WN', 'GT Hidden Spikes SE', 'GT Conveyor Cross EN', 'GT Speed Torch WN', 'Ice Lobby SE' } From 047f539f8d1269cc19ae2d16bda8b1a19d86091d Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 2 Oct 2023 09:40:53 -0600 Subject: [PATCH 036/158] More bans --- source/enemizer/enemy_deny.yaml | 37 +++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index f4866764..05bb4e3d 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -94,9 +94,9 @@ UwGeneralDeny: - [ 0x0045, 4, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots - [ 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" + - [ 0x0046, 0, [ "RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"Swamp Palace - Big O Top - Hover 1" + - [ 0x0046, 2, [ "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"Swamp Palace - Big O Top - Hover 2" + - [ 0x0046, 4, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"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" @@ -178,7 +178,7 @@ UwGeneralDeny: - [ 0x007d, 8, [ "RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Ganon's Tower - The Zoo - Red Bari" # todo - consider adding firesnake to 0-3: has a hard time moving, could block hookshots for quite a while - [ 0x007f, 0, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 1" - - [ 0x007f, 1, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 2" + - [ 0x007f, 1, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "ArmosStatue" ] ] #"Ice Palace - Big Spikes - Red Bari 2" - [ 0x007f, 2, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 3" - [ 0x007f, 3, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 4" - [ 0x007f, 4, [ "RollerVerticalDown" ] ] #"Ice Palace - Big Spikes - Big Spike Trap 1" @@ -293,7 +293,11 @@ UwGeneralDeny: - [ 0x00ce, 4, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - [ 0x00ce, 5, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - [ 0x00ce, 6, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00d0, 0, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 5, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00d0, 9, [ "AntiFairyCircle", "BigSpike", "Bumper"]] - [ 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, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Eye Bridge - Hardhat Beetle" @@ -350,10 +354,9 @@ OwGeneralDeny: - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo - [0x5e, 5, ["Gibo"]] # kiki eating Gibo UwEnemyDrop: - - [0x0044, 4, ["HardhatBeetle"]] # kept falling off before killing was possible - [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return - [0x00cc, 5, ["Babasu"]] # little hard to see and kill appropriately -# the following are behind rails +# the following are behind rails or otherwise unactivate-able - [0x0077, 4, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # can't activate here - [0x0077, 5, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] - [0x007D, 4, ["StalfosKnight", "Geldman", "Blob", "Stal"]] @@ -362,6 +365,8 @@ UwEnemyDrop: - [0x007D, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x008D, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x008D, 12, ["StalfosKnight", "Geldman", "Blob", "Stal"]] + - [0x00b0, 7, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay + - [0x00b0, 8, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay - [0x010C, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] - [0x010C, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # the following are not allowed at certain pits (or on conveyors near pits) @@ -372,9 +377,24 @@ UwEnemyDrop: - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x0044, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - [0x0044, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - [0x0044, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] @@ -490,6 +510,11 @@ UwEnemyDrop: "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] - [0x00ce, 8, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00d0, 0, ["Wizzrobe"]] + - [0x00d0, 2, ["Wizzrobe"]] + - [0x00d0, 4, ["Wizzrobe"]] + - [0x00d0, 5, ["Wizzrobe"]] + - [0x00d0, 9, ["Wizzrobe"]] - [0x00d5, 4, ["Wizzrobe"]] - [0x00df, 1, ["Wizzrobe"]] # slightly on wall - [0x00e7, 4, ["Wizzrobe"]] # slightly on wall From 7d371b995214ddfe7c8300f3bf06889626480247 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 2 Oct 2023 12:49:02 -0600 Subject: [PATCH 037/158] Async doors league settings --- docs/presets/async_doors_league/S4_Main.yaml | 31 +++++++++++++++++++ .../{ => prior}/S3_BombBag.yaml | 0 .../{ => prior}/S3_Main.yaml | 0 .../{ => prior}/S3_PotteryLottery.yaml | 0 .../{ => prior}/S3_Standard.yaml | 0 5 files changed, 31 insertions(+) create mode 100644 docs/presets/async_doors_league/S4_Main.yaml rename docs/presets/async_doors_league/{ => prior}/S3_BombBag.yaml (100%) rename docs/presets/async_doors_league/{ => prior}/S3_Main.yaml (100%) rename docs/presets/async_doors_league/{ => prior}/S3_PotteryLottery.yaml (100%) rename docs/presets/async_doors_league/{ => prior}/S3_Standard.yaml (100%) diff --git a/docs/presets/async_doors_league/S4_Main.yaml b/docs/presets/async_doors_league/S4_Main.yaml new file mode 100644 index 00000000..6e27f1e5 --- /dev/null +++ b/docs/presets/async_doors_league/S4_Main.yaml @@ -0,0 +1,31 @@ +meta: + players: 1 + race: true +settings: + 1: + shopsanity: true + pottery: keys + dropshuffle: keys + keysanity: true + accessibility: locations + + goal: crystals + crystals_gt: random + + shuffle: crossed + shufflelinks: true + shuffletavern: true + + door_shuffle: crossed + intensity: 3 + door_type_mode: big + key_logic_algorithm: partial + + experimental: true + dungeon_counters: 'on' + + pseudoboots: true + hints: true + msu_resume: true + collection_rate: true + quickswap: true diff --git a/docs/presets/async_doors_league/S3_BombBag.yaml b/docs/presets/async_doors_league/prior/S3_BombBag.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_BombBag.yaml rename to docs/presets/async_doors_league/prior/S3_BombBag.yaml diff --git a/docs/presets/async_doors_league/S3_Main.yaml b/docs/presets/async_doors_league/prior/S3_Main.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_Main.yaml rename to docs/presets/async_doors_league/prior/S3_Main.yaml diff --git a/docs/presets/async_doors_league/S3_PotteryLottery.yaml b/docs/presets/async_doors_league/prior/S3_PotteryLottery.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_PotteryLottery.yaml rename to docs/presets/async_doors_league/prior/S3_PotteryLottery.yaml diff --git a/docs/presets/async_doors_league/S3_Standard.yaml b/docs/presets/async_doors_league/prior/S3_Standard.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_Standard.yaml rename to docs/presets/async_doors_league/prior/S3_Standard.yaml From 143491172786010ceee4916c6ee8925fea80b21d Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 6 Oct 2023 10:47:22 -0600 Subject: [PATCH 038/158] Freeze fairy abyss/beam dash enemies Fix original key drops on split supertiles (pokey 1) Fix mystery/customizer boolean issue Enemy bans --- Bosses.py | 2 + Items.py | 4 +- Main.py | 4 +- PotShuffle.py | 2 + RELEASENOTES.md | 5 + Rom.py | 2 +- Rules.py | 2 +- data/base2current.bps | Bin 117445 -> 117471 bytes source/dungeon/EnemyList.py | 14 +- source/enemizer/Bossmizer.py | 27 +- source/enemizer/enemy_deny.yaml | 18 +- source/enemizer/enemy_weight.yaml | 2 +- source/item/FillUtil.py | 2 +- source/tools/MysteryUtils.py | 60 +- test/customizer/enemizer_test.yaml | 2058 ++++++++++++++++++++++++++++ test/customizer/test.yaml | 38 + 16 files changed, 2192 insertions(+), 48 deletions(-) create mode 100644 test/customizer/enemizer_test.yaml create mode 100644 test/customizer/test.yaml diff --git a/Bosses.py b/Bosses.py index 7052bb5c..6373fb5c 100644 --- a/Bosses.py +++ b/Bosses.py @@ -2,6 +2,7 @@ import logging import RaceRandom as random from BaseClasses import Boss, FillError +from source.enemizer.Bossmizer import boss_adjust def BossFactory(boss, player): @@ -238,6 +239,7 @@ def place_bosses(world, player): raise FillError('Could not place boss for location %s' % loc_text) place_boss(boss, level, loc, loc_text, world, player) + boss_adjust(world, player) def place_boss(boss, level, loc, loc_text, world, player): diff --git a/Items.py b/Items.py index 8f239fc7..1bbda012 100644 --- a/Items.py +++ b/Items.py @@ -61,8 +61,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'), 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x37, 0x08], 999, None, None, None, None, None, None, None), - 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None), - 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x39, 0x0a], 999, None, None, None, None, None, None, None), + 'Red Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None), + 'Blue Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x39, 0x0a], 999, None, None, None, None, None, None, None), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), 'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), 'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), diff --git a/Main.py b/Main.py index aa543136..3a3a7a42 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.3' +version_number = '1.3.0.4' version_branch = '-v' __version__ = f'{version_number}{version_branch}' @@ -213,9 +213,9 @@ def main(args, seed=None, fish=None): create_dungeons(world, player) world.damage_table[player] = DamageTable() world.data_tables[player] = init_data_tables(world, player) + place_bosses(world, player) randomize_enemies(world, player) adjust_locations(world, player) - place_bosses(world, player) if world.customizer and world.customizer.get_start_inventory(): for p, inv_list in world.customizer.get_start_inventory().items(): diff --git a/PotShuffle.py b/PotShuffle.py index c1d6bc1c..4ad16554 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -1034,6 +1034,8 @@ key_drop_data = { 'Ganons Tower - Mini Helmasaur Key Drop': ['Drop', (0x09DDC4, 0x3D, 2), "dropped atop Ganon's Tower", 'Small Key (Ganons Tower)'] } +key_drop_special = {(data[1][1], data[1][2]): name for name, data in key_drop_data.items() if data[0] == 'Drop'} + class PotSecretTable(object): def __init__(self): diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b7e0bf7a..b38e23e8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,11 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.4v + * Enemizer: The bunny beam near Lanmo 2 and the 4 fairies near Ice Armos are not shuffled anymore. This is due to how bosses shuffle works and since it cannot be guaranteed to work within the current system, they are vanilla. (Vitreous still overwrites the fairies and Arrghus only lets two spawn, etc.) + * Dropshuffle: Pokey 1 has been fixed to drop his item + * Mystery/Customizer: true/false and on/off in yaml files should behave the same. + * More enemy bans as have been reported * 1.3.0.3v * Faeries now part of the enemy shuffle pool. Take note, this will increase enemy drop locations to include fairy pools both in dungeons and in caves. * Enemy drop indicator (blue square) now works in caves based on entrance used diff --git a/Rom.py b/Rom.py index 15196757..42d39e04 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '8f9863c742096d16cae181fe314be3d7' +RANDOMIZERBASEHASH = '4935e770465a9fa00a1d642a657e43a8' class JsonRom(object): diff --git a/Rules.py b/Rules.py index 1f576316..d5371318 100644 --- a/Rules.py +++ b/Rules.py @@ -1211,7 +1211,7 @@ std_kill_rooms = { 'Eastern Single Eyegore': (['Eastern Single Eyegore NE'], ['Eastern Single Eyegore ES'], 0xd8, [8, 9, 10]), 'Eastern Duo Eyegores': - (['Eastern Duo Eyegores NE'], ['Eastern Duo Eyegores S'], 0xd8, [0, 1, 2, 3, 4, 5, 6, 7]), + (['Eastern Duo Eyegores NE'], ['Eastern Duo Eyegores SE'], 0xd8, [0, 1, 2, 3, 4, 5, 6, 7]), 'Desert Compass Room': # Three popos (beamos) (['Desert Compass NE'], ['Desert Compass Key Door WN'], 0x085, [2, 3, 4, 5]), 'Desert Four Statues': # Four popos (beamos) diff --git a/data/base2current.bps b/data/base2current.bps index 1befcc5427d0a3653117beba996e2add6a951559..e6b5fa48e063f14271fe6b1263392af539b1f791 100644 GIT binary patch delta 1351 zcmWMkdrXse6uq~+3Vm2!QckviabQbxyWO<@%vDqMJmwG z`co>@QHGPsKpQQBP$}}LFY2-^V@m*|$(S+1LMCd|?)&HFo^x`NlbkcCahcG#Tyb#x z`=GSZ=;$!#!+TR@{OHSbz_FWtOAu3yA^y?MuBD3GFY7Qb@I0HfR~Fg4cXO`9`#^Hc(X|T2l=QRRCJ+3{c_U3Dy?FTa`0f7xjb@RXbRZa_kGR?l#0R zPaU9Kip|g~6U($!bsoJEQJFM1r)d@c)mTm=h^8hbwr89S0_$@dk9hR*1+~tE;Zux? zB<(qkQo+6w1abjexe zNj8(6bh;m7B!0eLMECviTSMneyK-;o{b{rX?@VC3|Qb1SZdVyC7OCUl!G%3 zE>0%e-SmqA48j3b72!Mm%T^pQS^I(8-b zQ3f&yfX(pvfD=u+k9Cr`^bbFE*%#S)cAsgrn=t+j(R-#k z)^Kly-OiTjDuC$IY5X82`XpwCk>17jV*7aP^a4A7j$foGQj7|yevoT5m_8lEMYw^! z9>mqI!eygMDXcg8)6gNTKtAmpLLaxiSzXE==rJBUK%WkwY~Av8qq_KoqYb{>n_Ats zH#Lal6%;Ew1iO?fJ1}5ctfX0KO0{^~&7Ku;Vq#wTrI=VYJ6&h9Q|QWJjK@@(H;e(k zX~zZJcs+!qeg@vd$Bu?lB5$_@64srz4r99Knv`yp5d6SWrvVrE0aW_6R62q?a1%W= zg5g0WPCi7L4t577YTtZfSsGfcjy+*zX4z1)Y5-1yE-!@M8o^VNh%gUgIoGrBPOao> zTOGTa%B|?NBVNW$cNX5kA20=GIfv+pJOQ~7U3x?F5lUh1?S(k$Gws(Lr#pTG`u zU(}_W=h-9kyh&|v58Zx-`S8%;vucyqhUy9cb5XXKP~KrNX$r_b-SE{+8r}8)Px6!C KFO4I;_~L(`(JiL{ delta 1347 zcmWMkYfw{X5dAhF3FOffgoIb*DHKFV6;P}q%24o0l{!EvRz;0M#YYt>YQBr22{r<` z#!o;JYb;hGMM*7`M}mnwuB|1^^hc>PK&#VYWrP_zrCRm&{@LBLbI#7$*&R3V`wjf7 zZtndBDQ2s?+nkK&p|QRW;JLxmc10w>e(S;o$sV5PHQ)VJ3&mmA={=D>@RSZ;7!NP4 zTNj$Vz(&s|!~sKxDwo2y)@@3~Dj1=UmS)2f>p*D>4-&114u`qJu=V1xJ3Pofol)MA zQ4Y7$;BPY51!d|Zz`Wz~GuHLqOLC^4i{8Cbv3#G(xL7cCSJi~XlM_2{rW zuRvIHl@gG^BqeTDHOYmO69U&nEhi1a1a2B@BA)OT~3kAqN_w zynmweJe8On2BYyPLdC6JBA4(c=(MP3E(h^v4H~0NtO))1H6PH+%$uZBRd*Hd5dt}40?%Pw>4DYv{pwLrj?{`5Vrf|EG!FF(vsvviDI=R~KG zNfNRF@(nd}c zun{||&0%_Kn)VssQ}c<9JYc{dl;jBm$?q2wcT#G)6k95;G+#FJ^Ww4Y4R>RblT_b+ zk@k}M;+lwVl68i9^+R}ib;r_`)l~@DWhx0KVY?;R7`pd#L*Yyy+4%sD3LYKw`F(V{ H?~nfhtT8S6 diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 2a8a2377..7735c635 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -13,6 +13,7 @@ import RaceRandom as random from BaseClasses import Location, LocationType, RegionType from EntranceShuffle import door_addresses from Items import ItemFactory +from PotShuffle import key_drop_special from Utils import snes_to_pc, pc_to_snes, int16_as_bytes @@ -750,10 +751,10 @@ def init_vanilla_sprites(): create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08) create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08) create_sprite(0x001c, 0x19, SpriteType.Overlord, 0, 0x07, 0x08) - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07, 'GT Fairy Abyss') - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'GT Fairy Abyss') - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08, 'GT Fairy Abyss') - create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08, 'GT Fairy Abyss') + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07, 'GT Fairy Abyss', fix=True) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'GT Fairy Abyss', fix=True) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08, 'GT Fairy Abyss', fix=True) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08, 'GT Fairy Abyss', fix=True) create_sprite(0x001e, EnemySprite.CrystalSwitch, 0x00, 0, 0x1a, 0x09) create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Bomb Drop - Top') create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Bomb Drop - Top') @@ -2199,7 +2200,10 @@ def setup_enemy_dungeon_tables(world, player): if super_tile in splittable_supertiles: idx_adj = 0 for index, sprite in enumerate(enemy_list): - loc_name = f'{sprite.region} Enemy #{index+1}' + if (super_tile, index) in key_drop_special: + loc_name = key_drop_special[(super_tile, index)] + else: + loc_name = f'{sprite.region} Enemy #{index+1}' loc = world.get_location_unsafe(loc_name, player) if sprite.sub_type == 0x07: # overlord idx_adj += 1 diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py index 935b11e3..a028c871 100644 --- a/source/enemizer/Bossmizer.py +++ b/source/enemizer/Bossmizer.py @@ -110,7 +110,8 @@ def add_kholdstare_to_list(sprite_list, room_id): def add_vitreous_to_list(sprite_list, room_id): - sprite_list.insert(0, create_sprite(room_id, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05)) + sprite_list.clear() # vitreous does not play nice which other sprites on the tile, just kill them + sprite_list.append(create_sprite(room_id, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05)) def add_trinexx_to_list(sprite_list, room_id): @@ -119,14 +120,8 @@ def add_trinexx_to_list(sprite_list, room_id): sprite_list.insert(2, create_sprite(room_id, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x05)) -def boss_writes(world, player, rom): - rom.write_byte(snes_to_pc(0x368107), 1) # centralize drops - 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 +def boss_adjust(world, player): data_tables = world.data_tables[player] - arrghus_can_swim = True - water_tiles_on = True for dungeon in world.get_dungeons(player): for level, boss in dungeon.bosses.items(): if not boss or boss.name in ['Agahnim', 'Agahnim2']: @@ -143,6 +138,22 @@ def boss_writes(world, player, rom): if len(sprite_list) > 16: del sprite_list[16:] data_tables.room_headers[room_id].sprite_sheet = required_boss_sheets[sprite_type] + + +def boss_writes(world, player, rom): + rom.write_byte(snes_to_pc(0x368107), 1) # centralize drops + 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 + data_tables = world.data_tables[player] + arrghus_can_swim = True + water_tiles_on = True + for dungeon in world.get_dungeons(player): + for level, boss in dungeon.bosses.items(): + if not boss or boss.name in ['Agahnim', 'Agahnim2']: + continue + room_data = get_dungeon_boss_room(dungeon.name, level) + room_id = room_data[0] # room changes if boss.name == 'Arrghus' and (dungeon.name != 'Swamp Palace' or level is not None): rom.write_byte(snes_to_pc(0x0DB6BE), 0) # arrghus can stand on ground diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 05bb4e3d..0af02b7c 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -142,6 +142,7 @@ UwGeneralDeny: - [ 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, 2, ["Bumper"]] #"Skull Woods - Firebar Pits - Blue Bari 2" - [ 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" @@ -293,10 +294,13 @@ UwGeneralDeny: - [ 0x00ce, 4, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - [ 0x00ce, 5, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - [ 0x00ce, 6, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - [ 0x00d0, 0, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 1, [ "AntiFairyCircle", "BigSpike", "Bumper"]] - [ 0x00d0, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - [ 0x00d0, 5, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - - [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00d0, 6, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 7, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - [ 0x00d0, 9, [ "AntiFairyCircle", "BigSpike", "Bumper"]] - [ 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" @@ -347,14 +351,18 @@ UwGeneralDeny: - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] OwGeneralDeny: - [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here - - [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo - [0x5e, 5, ["Gibo"]] # kiki eating Gibo UwEnemyDrop: - [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return + - [0x00cb, 3, ["Zoro"]] # layer issues + - [0x00cb, 5, ["Zoro"]] # layer issues + - [0x00cb, 9, ["Zoro"]] # layer issues + - [0x00cb, 10, ["Zoro"]] # layer issues - [0x00cc, 5, ["Babasu"]] # little hard to see and kill appropriately # the following are behind rails or otherwise unactivate-able - [0x0077, 4, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # can't activate here diff --git a/source/enemizer/enemy_weight.yaml b/source/enemizer/enemy_weight.yaml index 7cef3326..97c011eb 100644 --- a/source/enemizer/enemy_weight.yaml +++ b/source/enemizer/enemy_weight.yaml @@ -53,7 +53,7 @@ UW: # Total 94431 Kyameron: 237 # 0.42147% raw:398 3.52212% Landmine: 19 # 5.40077% raw:5100 45.13274% Leever: 154 # 0.65127% raw:615 5.44248% - Lynel: 500 # 0.17897% raw:169 1.49558% + Lynel: 400 # 0.17897% raw:169 1.49558% MiniHelmasaur: 109 # 0.91495% raw:864 7.64602% MiniMoldorm: 109 # 0.91495% raw:864 7.64602% Moblin: 367 # 0.27216% raw:257 2.27434% diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index d5c63551..1a5c5692 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -357,7 +357,7 @@ def determine_major_items(world, player): major_item_set.add('Single Arrow') if world.keyshuffle[player] == 'universal': major_item_set.add('Small Key (Universal)') - if world.goal[player] in {'triforcehunt', 'ganonhunt'}: + if world.goal[player] in {'triforcehunt', 'ganonhunt', 'trinity'}: major_item_set.add('Triforce Piece') if world.bombbag[player]: major_item_set.add('Bomb Upgrade (+10)') diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 7016d406..00df02ef 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -33,6 +33,22 @@ def roll_settings(weights): return default return choice + def get_choice_bool(option, root=weights): + choice = get_choice(option, root) + if choice is True or choice == 'on': + return True + if choice is False or choice == 'off': + return False + if choice is None: + return choice + raise Exception("This fields needs to be true/false or off/on") + + def get_choice_bool_default(option, root=weights, default=None): + choice = get_choice_bool(option, root) + if choice is None and default is not None: + return default + return choice + while True: subweights = weights.get('subweights', {}) if len(subweights) == 0: @@ -62,8 +78,8 @@ def roll_settings(weights): dungeon_items = get_choice('dungeon_items') dungeon_items = '' if dungeon_items == 'standard' or dungeon_items is None else dungeon_items dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items - ret.mapshuffle = get_choice('map_shuffle') == 'on' if 'map_shuffle' in weights else 'm' in dungeon_items - ret.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else 'c' in dungeon_items + ret.mapshuffle = get_choice_bool('map_shuffle') if 'map_shuffle' in weights else 'm' in dungeon_items + ret.compassshuffle = get_choice_bool('compass_shuffle') if 'compass_shuffle' in weights else 'c' in dungeon_items if 'smallkey_shuffle' in weights: ret.keyshuffle = get_choice('smallkey_shuffle') else: @@ -71,7 +87,7 @@ def roll_settings(weights): ret.keyshuffle = 'wild' if 'u' in dungeon_items: ret.keyshuffle = 'universal' - ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else 'b' in dungeon_items + ret.bigkeyshuffle = get_choice_bool('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items ret.accessibility = get_choice('accessibility') ret.restrict_boss_items = get_choice('restrict_boss_items') @@ -86,26 +102,26 @@ def roll_settings(weights): ret.door_type_mode = get_choice('door_type_mode') ret.trap_door_mode = get_choice('trap_door_mode') ret.key_logic_algorithm = get_choice('key_logic_algorithm') - ret.decoupledoors = get_choice('decoupledoors') - ret.door_self_loops = get_choice('door_self_loops') - ret.experimental = get_choice('experimental') - ret.collection_rate = get_choice('collection_rate') + ret.decoupledoors = get_choice_bool('decoupledoors') + ret.door_self_loops = get_choice_bool('door_self_loops') + ret.experimental = get_choice_bool('experimental') + ret.collection_rate = get_choice_bool('collection_rate') ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default' if ret.dungeon_counters == 'default': ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off' - ret.shufflelinks = get_choice('shufflelinks') - ret.shuffletavern = get_choice('shuffletavern') - ret.pseudoboots = get_choice('pseudoboots') - ret.shopsanity = get_choice('shopsanity') - keydropshuffle = get_choice('keydropshuffle') + ret.shufflelinks = get_choice_bool('shufflelinks') + ret.shuffletavern = get_choice_bool('shuffletavern') + ret.pseudoboots = get_choice_bool('pseudoboots') + ret.shopsanity = get_choice_bool('shopsanity') + keydropshuffle = get_choice_bool('keydropshuffle') ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none' ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none' ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery - ret.colorizepots = get_choice_default('colorizepots', default=True) - ret.shufflepots = get_choice('pot_shuffle') + ret.colorizepots = get_choice_bool_default('colorizepots', default=True) + ret.shufflepots = get_choice_bool('pot_shuffle') ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent' ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize') @@ -140,12 +156,12 @@ def roll_settings(weights): if ret.mode == 'retro': ret.mode = 'open' ret.retro = True - ret.retro = get_choice('retro') # this overrides world_state if used + ret.retro = get_choice_bool('retro') # this overrides world_state if used ret.take_any = get_choice_default('take_any', default='none') - ret.bombbag = get_choice('bombbag') + ret.bombbag = get_choice_bool('bombbag') - ret.hints = get_choice('hints') + ret.hints = get_choice_bool('hints') swords = get_choice('weapons') if swords is not None: @@ -198,15 +214,15 @@ def roll_settings(weights): if 'rom' in weights: romweights = weights['rom'] ret.sprite = get_choice('sprite', romweights) - ret.disablemusic = get_choice('disablemusic', romweights) - ret.quickswap = get_choice('quickswap', romweights) - ret.reduce_flashing = get_choice('reduce_flashing', romweights) + ret.disablemusic = get_choice_bool('disablemusic', romweights) + ret.quickswap = get_choice_bool('quickswap', romweights) + ret.reduce_flashing = get_choice_bool('reduce_flashing', romweights) ret.fastmenu = get_choice('menuspeed', romweights) ret.heartcolor = get_choice('heartcolor', romweights) ret.heartbeep = get_choice('heartbeep', romweights) ret.ow_palettes = get_choice('ow_palettes', romweights) ret.uw_palettes = get_choice('uw_palettes', romweights) - ret.shuffle_sfx = get_choice('shuffle_sfx', romweights) - ret.msu_resume = get_choice('msu_resume', romweights) + ret.shuffle_sfx = get_choice_bool('shuffle_sfx', romweights) + ret.msu_resume = get_choice_bool('msu_resume', romweights) return ret diff --git a/test/customizer/enemizer_test.yaml b/test/customizer/enemizer_test.yaml new file mode 100644 index 00000000..ce3f6753 --- /dev/null +++ b/test/customizer/enemizer_test.yaml @@ -0,0 +1,2058 @@ +#placements: +# 1: +# Dark Lake Hylia Shop - Left: Blue Potion +meta: + players: 1 +# seed: 140 + seed: 773569045 +settings: + 1: +# boss_shuffle: unique + enemy_shuffle: shuffled +# dropshuffle: underworld +# dungeon_counters: 'on' +# door_shuffle: crossed +# intensity: 3 +enemies: + 1: + Underworld: +# 0x2: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 14: Wizzrobe +# 15: Wizzrobe + 0x4: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 9: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 0x9: + 2: Wizzrobe + 0xa: + 0: Wizzrobe + 1: Wizzrobe + 0xb: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xe: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x11: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x13: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x15: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x16: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x17: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x19: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x1a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x1b: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x1e: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x1f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x21: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x22: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x24: + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x26: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x27: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x28: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x2a: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x2b: + 2: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x2e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x31: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x32: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x34: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x35: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x36: + 1: Wizzrobe + 2: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x37: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 0x38: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x39: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x3a: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x3b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x3c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x3d: + 2: Wizzrobe + 3: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0x3e: + 1: Wizzrobe + 2: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x3f: + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x40: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x41: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x42: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x43: + 0: Wizzrobe + 0x44: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 0x45: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe +# 0x46: +# 0: Wizzrobe +# 2: Wizzrobe +# 4: Wizzrobe + 0x49: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x4a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x4b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x4c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x4e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x50: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x51: + 1: Wizzrobe + 2: Wizzrobe + 0x52: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x53: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x54: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x55: + 1: Wizzrobe + 2: Wizzrobe + 0x56: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x57: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x58: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x59: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x5b: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x5d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x5e: + 3: Wizzrobe + 4: Wizzrobe + 0x5f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x60: + 0: Wizzrobe + 0x61: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x62: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x63: + 1: Wizzrobe +# 0x64: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe + 0x65: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe +# 0x66: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 6: Wizzrobe +# 7: Wizzrobe +# 8: Wizzrobe +# 9: Wizzrobe +# 11: Wizzrobe + 0x67: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x68: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x6a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6b: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0x6d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x6e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x71: + 0: Wizzrobe + 1: Wizzrobe + 0x72: + 0: Wizzrobe + 1: Wizzrobe + 0x73: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x74: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x75: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe +# 0x76: +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 6: Wizzrobe + 0x77: + 0: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7b: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x7c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe +# 0x7e: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe + 0x7f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x80: + 1: Wizzrobe + 2: Wizzrobe + 0x81: + 0: Wizzrobe + 1: Wizzrobe + 0x82: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x83: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x84: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x85: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x87: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 0x8b: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x8c: + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x8d: + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 0x8e: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x91: + 1: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x92: + 2: Wizzrobe + 4: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x93: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x95: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x96: + 0: Wizzrobe + 0x98: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x99: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x9b: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x9c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x9d: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe +# 0x9e: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe + 0x9f: + 4: Wizzrobe + 5: Wizzrobe + 0xa0: + 1: Wizzrobe + 2: Wizzrobe + 0xa1: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0xa5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0xa6: + 1: Wizzrobe + 0xa8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xa9: + 0: Wizzrobe + 1: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xaa: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xab: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xae: + 0: Wizzrobe + 1: Wizzrobe + 0xaf: + 0: Wizzrobe + 0xb0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0xb1: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xb2: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0xb3: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 0xb5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xb6: + 0: Wizzrobe + 1: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0xb7: + 0: Wizzrobe + 1: Wizzrobe + 0xb8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xba: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xbb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xbc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe +# 0xbe: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe + 0xbf: + 1: Wizzrobe + 0xc0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xc1: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 8: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0xc2: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xc3: + 6: Wizzrobe + 0xc4: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xc5: + 6: Wizzrobe + 0xc6: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xc9: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xcb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0xcc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0xce: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xd0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xd1: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xd2: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xd5: + 4: Wizzrobe + 0xd8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xd9: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xda: + 0: Wizzrobe + 1: Wizzrobe + 0xdb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xdc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xdf: + 0: Wizzrobe + 1: Wizzrobe + 0xe0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe +# 0xe4: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe + 0xe5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xe6: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xe7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xe8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xee: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xef: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe +# 0xf0: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe +# 7: Wizzrobe +# 9: Wizzrobe + 0xf1: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xf9: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xfb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xfd: + 0: Wizzrobe + 1: Wizzrobe + 4: Wizzrobe + 0xfe: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x107: + 1: Wizzrobe + 2: Wizzrobe + 0x108: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x10b: + 6: Wizzrobe + 0x10c: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x10d: + 0: Wizzrobe + 1: Wizzrobe +# 0x123: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe + Overworld: + 0x21b: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x40: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 0x42: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x45: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x4a: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe +# 0x4f: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe + 0x50: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x51: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x52: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x53: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x54: + 0: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x55: + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 0x56: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x57: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x58: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 0x5a: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x5b: + 0: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x5d: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x5e: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 0x62: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x65: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6b: + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x6c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x6d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6e: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x70: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x72: + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 0x73: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x74: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 0x75: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 0x77: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x7b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7c: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7f: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x81: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 23: Wizzrobe + 24: Wizzrobe + 25: Wizzrobe + 26: Wizzrobe + 27: Wizzrobe + 28: Wizzrobe + 0x0: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe +# 0x2: +# 0: Wizzrobe +# 2: Wizzrobe + 0x3: + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x5: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xa: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0xf: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x10: + 0: Wizzrobe + 3: Wizzrobe + 0x11: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x12: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x13: + 1: Wizzrobe + 0x14: + 0: Wizzrobe + 1: Wizzrobe + 0x15: + 1: Wizzrobe + 3: Wizzrobe +# 0x16: +# 0: Wizzrobe +# 2: Wizzrobe + 0x17: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x18: + 9: Wizzrobe + 10: Wizzrobe + 0x1a: + 0: Wizzrobe + 1: Wizzrobe + 4: Wizzrobe + 0x1b: + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0x1d: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x1e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 0x22: + 1: Wizzrobe + 2: Wizzrobe + 0x25: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x2b: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x2c: + 0: Wizzrobe + 1: Wizzrobe + 0x2d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x2e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x2f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x30: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 0x32: + 1: Wizzrobe + 2: Wizzrobe + 0x33: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x34: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x35: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 0x37: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x3a: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x3b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x3c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x3f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 0x90: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 13: Wizzrobe + 0x93: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x95: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x97: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x9a: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x9f: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 0xa0: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xa1: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0xa2: + 1: Wizzrobe + 3: Wizzrobe + 0xa3: + 2: Wizzrobe + 3: Wizzrobe + 0xa4: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xa5: + 1: Wizzrobe + 3: Wizzrobe +# 0xa6: +# 0: Wizzrobe +# 2: Wizzrobe + 0xa7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xa8: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xaa: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0xab: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xad: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xae: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 19: Wizzrobe + 0xb2: + 1: Wizzrobe + 2: Wizzrobe + 0xb5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xb8: + 0: Wizzrobe + 4: Wizzrobe + 0xb9: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xbb: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xbc: + 0: Wizzrobe + 1: Wizzrobe + 0xbd: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xbe: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xbf: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xc0: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 0xc2: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 0xc3: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xc4: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xc5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 0xc7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xca: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xcb: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xcc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xcf: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe +start_inventory: + 1: + - Lamp + - Progressive Sword + - Ocarina (Activated) + - Hookshot + 2: [] + diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml new file mode 100644 index 00000000..7d6381a7 --- /dev/null +++ b/test/customizer/test.yaml @@ -0,0 +1,38 @@ +meta: + players: 1 + seed: 96 +settings: + 1: +# mode: standard + + boss_shuffle: random + dropshuffle: underworld + enemy_shuffle: shuffled + door_shuffle: crossed + intensity: 3 + + dungeon_counters: 'on' +doors: + 1: + lobbies: + Hyrule Castle South: GT Bob's Room SE +bosses: + 1: + Ganons Tower (bottom): Armos Knights + + +# keyshuffle: wild +#placements: +# 1: +# 'Hera Basement Cage Enemy #4': Small Key (Palace of Darkness) +# 'Hera Basement Cage Enemy #9': Small Key (Tower of Hera) +# 'Hera Basement Cage Enemy #11': Fire Rod +# Tower of Hera - Basement Cage: Small Key (Turtle Rock) +#start_inventory: +# 1: +# - Lamp +# - Progressive Sword +# - Ocarina (Activated) +# - Hookshot + + From c849f8202ca282212f3970e822741be2cc0e4b9e Mon Sep 17 00:00:00 2001 From: Paul Wendelboe Date: Mon, 9 Oct 2023 00:39:54 +0000 Subject: [PATCH 039/158] Update ItemList.py Fix iteration on item instead of name when using a custom pool in some cases. --- ItemList.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ItemList.py b/ItemList.py index 7a20c7a0..2a558b9a 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1164,12 +1164,12 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer pool.extend(['Nothing'] * nothings) start_inventory = [x for x in world.precollected_items if x.player == player] - if world.logic[player] in ['owglitches', 'nologic'] and all(x !=' Pegasus Boots' for x in start_inventory): + if world.logic[player] in ['owglitches', 'nologic'] and all(x.name !=' Pegasus Boots' for x in start_inventory): precollected_items.append('Pegasus Boots') if 'Pegasus Boots' in pool: pool.remove('Pegasus Boots') pool.append('Rupees (20)') - if world.swords[player] == 'assured' and all(' Sword' not in x for x in start_inventory): + if world.swords[player] == 'assured' and all(' Sword' not in x.name for x in start_inventory): precollected_items.append('Progressive Sword') if 'Progressive Sword' in pool: pool.remove('Progressive Sword') From 07e06add110f597cbe71001ee6c9cd2a49650885 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 11 Oct 2023 12:26:54 -0600 Subject: [PATCH 040/158] Fix for bonk torches counting issue Hera basement issue fixed by banning wallmasters Known Statue+Pull Switch interactions banned Making sprite spammers count for more on tile to increase chance of pots being available to pick up. More enemy bans --- RELEASENOTES.md | 6 +++ Rom.py | 2 +- data/base2current.bps | Bin 117471 -> 117459 bytes source/dungeon/EnemyList.py | 2 +- source/enemizer/Enemizer.py | 18 ++++++- source/enemizer/enemy_deny.yaml | 93 ++++++++++++++++++++++++-------- test/customizer/test.yaml | 20 +++---- 7 files changed, 106 insertions(+), 35 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b38e23e8..c875b8ab 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,12 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.5v + * Hud/Map Counter: Collecting a keys for this dungeon of a bonk torch no longer increments the counter twice and immediately updates the hud. + * Enemizer: Hera basement item counting twice fixed by banning wallmasters on the tile. + * Enemizer: Statues banned offscreen for pull switches + * Enemizer: Several sprite producing enemies have been limited on crowded tiles. Offenders: Hinox, Sluggula, Bomb Guard, Beamos, Gibo, Wall Cannons, Probe using Guards. Others do not spam as many projectiles. + * Enemizer: More enemy bans (mostly Wizzrobes near walls where they won't spawn, couple missed firebar spots) * 1.3.0.4v * Enemizer: The bunny beam near Lanmo 2 and the 4 fairies near Ice Armos are not shuffled anymore. This is due to how bosses shuffle works and since it cannot be guaranteed to work within the current system, they are vanilla. (Vitreous still overwrites the fairies and Arrghus only lets two spawn, etc.) * Dropshuffle: Pokey 1 has been fixed to drop his item diff --git a/Rom.py b/Rom.py index 42d39e04..4995784b 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '4935e770465a9fa00a1d642a657e43a8' +RANDOMIZERBASEHASH = 'cafcf71aefe71fc6cd8ab8fef06794b0' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index e6b5fa48e063f14271fe6b1263392af539b1f791..85192376666d8b24ca2f6eb414aeeaa2fdf02123 100644 GIT binary patch delta 5193 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 Date: Tue, 31 Oct 2023 15:35:45 -0600 Subject: [PATCH 041/158] Fix for sprite limit on Lanmo 2 Fix for enemies at GT Falling Tile Square --- RELEASENOTES.md | 3 +++ source/enemizer/Bossmizer.py | 4 ++-- source/enemizer/enemy_deny.yaml | 2 -- test/customizer/test.yaml | 13 ++++++++----- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c875b8ab..26ea759c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.6v + * Enemizer: Arrghus at Lanmo 2 no longer prevents pot pickups + * Various enemy bans * 1.3.0.5v * Hud/Map Counter: Collecting a keys for this dungeon of a bonk torch no longer increments the counter twice and immediately updates the hud. * Enemizer: Hera basement item counting twice fixed by banning wallmasters on the tile. diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py index a028c871..39d190a4 100644 --- a/source/enemizer/Bossmizer.py +++ b/source/enemizer/Bossmizer.py @@ -135,8 +135,8 @@ def boss_adjust(world, player): del sprite_list[:data] add_func, sprite_type = boss_addition_table[boss.name] add_func(sprite_list, room_id) - if len(sprite_list) > 16: - del sprite_list[16:] + if len(sprite_list) > 15: + del sprite_list[15:] data_tables.room_headers[room_id].sprite_sheet = required_boss_sheets[sprite_type] diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index b0b1854b..c0162e9f 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -506,8 +506,6 @@ UwEnemyDrop: - [0x003b, 4, ["Wizzrobe"]] - [0x003b, 6, ["Wizzrobe"]] - [0x003c, 1, ["Wizzrobe"]] - - [0x003d, 9, ["Wizzrobe"]] - - [0x003d, 10, ["Wizzrobe"]] - [0x003d, 11, ["Wizzrobe"]] - [0x003d, 12, ["Wizzrobe"]] - [0x003d, 13, ["Wizzrobe"]] diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml index 64529753..49cfb516 100644 --- a/test/customizer/test.yaml +++ b/test/customizer/test.yaml @@ -6,7 +6,7 @@ settings: # mode: standard # boss_shuffle: random -# dropshuffle: underworld + dropshuffle: underworld enemy_shuffle: shuffled door_shuffle: crossed intensity: 3 @@ -15,12 +15,15 @@ settings: doors: 1: lobbies: - Hyrule Castle South: Hyrule Castle Lobby S + Hyrule Castle South: GT Lobby S doors: - Hyrule Castle Lobby W: Desert Sandworm Corner E Edge -placements: + GT Lobby Left Down Stairs: GT Quad Pot Up Stairs +bosses: 1: - Desert Palace - Torch: Small Key (Escape) + Ganons Tower (middle): Arrghus +#placements: +# 1: +# Desert Palace - Torch: Small Key (Escape) # keyshuffle: wild From 13f9fe8f924a861e3839be2cac937edb2456f145 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 2 Nov 2023 16:24:38 -0600 Subject: [PATCH 042/158] Enemy bans see release notes --- RELEASENOTES.md | 6 ++++ source/enemizer/SpriteSheets.py | 2 +- source/enemizer/enemy_deny.yaml | 58 +++++++++++++++++++++++++-------- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 26ea759c..bbd6b122 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -144,6 +144,12 @@ These are now independent of retro mode and have three options: None, Random, an * 1.3.0.6v * Enemizer: Arrghus at Lanmo 2 no longer prevents pot pickups * Various enemy bans + * More Gibos near kiki and Old Man + * Bumper obstacles + * Damaging roller + * Statue + Pots don't mix + * Toppo in challenge rooms + * Misc * 1.3.0.5v * Hud/Map Counter: Collecting a keys for this dungeon of a bonk torch no longer increments the counter twice and immediately updates the hud. * Enemizer: Hera basement item counting twice fixed by banning wallmasters on the tile. diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index fa703e20..2a935048 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -229,7 +229,7 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.BombGuard).sub_group(0, 0x46).sub_group(1, 0x49), SpriteRequirement(EnemySprite.GreenKnifeGuard).sub_group(1, 0x49).sub_group(2, 0x13), SpriteRequirement(EnemySprite.Geldman).sub_group(2, 0x12).exclude({0x10c}), - SpriteRequirement(EnemySprite.Toppo).no_drop().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.Toppo).immune().sub_group(3, 0x11), SpriteRequirement(EnemySprite.Popo).sub_group(1, 0x2c), SpriteRequirement(EnemySprite.Popo2).sub_group(1, 0x2c), SpriteRequirement(EnemySprite.ArmosStatue).sub_group(3, 0x10).exclude({0x10c}), diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index c0162e9f..4e15f8c3 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -35,10 +35,10 @@ UwGeneralDeny: - [ 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" - - [ 0x001f, 3, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x001f, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x0021, 3, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 2" - [ 0x0021, 4, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 3" - - [ 0x0024, 6, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x0024, 6, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x0026, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 1" - [ 0x0026, 8, [ "AntiFairyCircle", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 3" - [ 0x0026, 9, [ "RollerHorizontalRight", "Statue" ] ] #"Swamp Palace - Big Spoon - Kyameron" @@ -97,7 +97,7 @@ UwGeneralDeny: - [ 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, 4, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x0045, 4, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x0045, 7, [ "AntiFairyCircle", "Bumper" ] ] #"Thieves' Town - Cells - Blue Zazak 4" - [ 0x0045, 8, [ "RollerHorizontalRight" ] ] #"Thieves' Town - Cells - Zol" - [ 0x0046, 0, [ "RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"Swamp Palace - Big O Top - Hover 1" @@ -124,7 +124,7 @@ UwGeneralDeny: - [ 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, 0, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x0057, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 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", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike", "SpikeBlock"]] #"Skull Woods - Big Key Room - Gibdo 6" @@ -149,7 +149,7 @@ UwGeneralDeny: - [ 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, 2, [ "Bumper" , "Beamos" ] ] #"Thieves' Town - Attic Hall Left - Keese 2" - - [ 0x0064, 3, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x0064, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 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" @@ -205,6 +205,7 @@ UwGeneralDeny: - [ 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, 3, ["RollerHorizontalRight"]] - [ 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)" @@ -214,7 +215,7 @@ UwGeneralDeny: - [ 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" - - [ 0x008e, 2, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x008e, 2, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots - [ 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" @@ -250,7 +251,7 @@ UwGeneralDeny: - [ 0x009e, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Fairy Drop - blue - Red Bari 3" - [ 0x00a0, 1, [ "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Boss Antechamber - Antifairy" - [ 0x00a1, 2, [ "Statue", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Fish Room - Spark (Clockwise) 2" - - [ 0x00a1, 7, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x00a1, 7, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots - [ 0x00a5, 2, [ "BigSpike" ] ] #"GT Wizzrobes 1 - Wizzrobe 3" - [ 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" @@ -264,8 +265,9 @@ UwGeneralDeny: - [ 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, 1, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots - - [ 0x00b2, 3, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x00b1, 4, ["Bumper", "BigSpike", "AntiFairyCircle" ]] + - [ 0x00b2, 1, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00b2, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 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" @@ -288,18 +290,18 @@ UwGeneralDeny: - [ 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" - - [ 0x00bf, 0, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on collision + - [ 0x00bf, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on collision - [ 0x00c1, 3, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Misery Mire - 4 Rails - Stalfos 1" - [ 0x00c2, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Misery Mire - Main Lobby - blue - Fire Snake 1" - - [ 0x00c2, 5, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x00c2, 5, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 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, 0, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x00cb, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 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" - [ 0x00cb, 9, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] - [ 0x00cb, 10, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] - - [ 0x00cb, 11, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x00cb, 11, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x00cc, 8, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) - [ 0x00cc, 12, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) - [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper" ] ] #"Ice Palace - Over Boss - top - Red Bari 1" @@ -329,7 +331,7 @@ UwGeneralDeny: - [ 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" - - [ 0x00db, 0, [ "Wizzrobe" ] ] # Wizzrobes can't spawn on pots + - [ 0x00db, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x00dc, 2, [ "AntiFairyCircle", "BigSpike", "Bumper" ] ] - [ 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" @@ -363,14 +365,42 @@ UwGeneralDeny: - [ 0x00fd, 0, [ "Bumper" ] ] - [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [0x010b, 6, ["RollerHorizontalRight"]] OwGeneralDeny: + - [0x03, 2, ["Gibo"]] # OldMan eating Gibo + - [0x03, 4, ["Gibo"]] # OldMan eating Gibo + - [0x03, 5, ["Gibo"]] # OldMan eating Gibo + - [0x03, 6, ["Gibo"]] # OldMan eating Gibo + - [0x03, 8, ["Gibo"]] # OldMan eating Gibo + - [0x03, 9, ["Gibo"]] # OldMan eating Gibo + - [0x03, 10, ["Gibo"]] # OldMan eating Gibo - [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here - [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x5e, 0, ["Gibo"]] # kiki eating Gibo + - [0x5e, 1, ["Gibo"]] # kiki eating Gibo + - [0x5e, 2, ["Gibo"]] # kiki eating Gibo + - [0x5e, 3, ["Gibo"]] # kiki eating Gibo - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo - [0x5e, 5, ["Gibo"]] # kiki eating Gibo + - [0x5e, 6, ["Gibo"]] # kiki eating Gibo + - [0x5e, 7, ["Gibo"]] # kiki eating Gibo + - [0x5e, 8, ["Gibo"]] # kiki eating Gibo + - [0x5e, 9, ["Gibo"]] # kiki eating Gibo + - [0x5e, 10, ["Gibo"]] # kiki eating Gibo + - [0x5e, 11, ["Gibo"]] # kiki eating Gibo + - [0x5e, 12, ["Gibo"]] # kiki eating Gibo + - [0x5e, 13, ["Gibo"]] # kiki eating Gibo + - [0x5e, 14, ["Gibo"]] # kiki eating Gibo + - [0x5e, 15, ["Gibo"]] # kiki eating Gibo + - [0x5e, 16, ["Gibo"]] # kiki eating Gibo + - [0x5e, 17, ["Gibo"]] # kiki eating Gibo + - [0x5e, 18, ["Gibo"]] # kiki eating Gibo + - [0x5e, 19, ["Gibo"]] # kiki eating Gibo + - [0x5e, 20, ["Gibo"]] # kiki eating Gibo + - [0x77, 1, ["Bumper"]] # soft-lock potential near ladder UwEnemyDrop: - [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return - [0x00cb, 3, ["Zoro"]] # layer issues From f4a702951cde4d1c16ae8acb5b3fa77187fd9c91 Mon Sep 17 00:00:00 2001 From: aerinon Date: Sat, 11 Nov 2023 20:00:31 -0700 Subject: [PATCH 043/158] fix(key logic): typo --- BaseClasses.py | 133 ++++++++++++++++++++++++---------------------- KeyDoorShuffle.py | 2 +- 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index d92e5af3..7a463a38 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -10,7 +10,6 @@ try: except ImportError: from enum import IntFlag as FastEnum - from source.classes.BabelFish import BabelFish from EntranceShuffle import door_addresses, indirect_connections from Utils import int16_as_bytes @@ -94,6 +93,7 @@ class World(object): for player in range(1, players + 1): def set_player_attr(attr, val): self.__dict__.setdefault(attr, {})[player] = val + set_player_attr('_region_cache', {}) set_player_attr('player_names', []) set_player_attr('remote_items', False) @@ -408,7 +408,7 @@ class World(object): def push_precollected(self, item): item.world = self if ((item.smallkey and self.keyshuffle[item.player] != 'none') - or (item.bigkey and self.bigkeyshuffle[item.player])): + or (item.bigkey and self.bigkeyshuffle[item.player])): item.advancement = True self.precollected_items.append(item) self.state.collect(item, True) @@ -761,7 +761,7 @@ class CollectionState(object): bc_ = self.blocked_connections[player] for block, crystal in bc_.items(): if (block, crystal) not in terminal_queue and self.possibly_connected_to_dungeon(block.connected_region, player): - terminal_queue.append((block, crystal)) + terminal_queue.append((block, crystal)) self.traverse_world(terminal_queue, rrp_, bc_, player) self.dungeon_limits = None @@ -784,7 +784,7 @@ class CollectionState(object): missing_bc = {} for blocked, crystal in common_bc.items(): if (blocked not in bc and blocked.parent_region in rrp - and self.should_visit(blocked.connected_region, rrp, crystal, player)): + and self.should_visit(blocked.connected_region, rrp, crystal, player)): missing_bc[blocked] = crystal for k in missing_bc: bc[k] = missing_bc[k] @@ -833,15 +833,15 @@ class CollectionState(object): return door_candidates door_candidates, skip = [], set() if (state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal' - and state.placing_item and state.placing_item.name == small_key_name): + and state.placing_item and state.placing_item.name == small_key_name): key_logic = state.world.key_logic[player][dungeon_name] for door, paired in key_logic.sm_doors.items(): if door.name in key_logic.door_rules: rule = key_logic.door_rules[door.name] key = KeyRuleType.AllowSmall if (key in rule.new_rules and key_total >= rule.new_rules[key] and door.name not in skip - and door.name in state.reached_doors[player] and door.name not in state.opened_doors[player] - and rule.small_location.item is None): + and door.name in state.reached_doors[player] and door.name not in state.opened_doors[player] + and rule.small_location.item is None): if paired: door_candidates.append((door.name, paired.name)) skip.add(paired.name) @@ -965,8 +965,8 @@ class CollectionState(object): 'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna', 'Ocarina (Activated)', 'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)', 'Magic Upgrade (1/4)'] - or item_name.startswith(('Bottle', 'Small Key', 'Big Key')) - or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass')))) + or item_name.startswith(('Bottle', 'Small Key', 'Big Key')) + or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass')))) def can_reach(self, spot, resolution_hint=None, player=None): try: @@ -1013,7 +1013,6 @@ class CollectionState(object): self.collect(event.item, True, event) new_locations = True - def can_reach_blue(self, region, player): return region in self.reachable_regions[player] and self.reachable_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either] @@ -1026,7 +1025,7 @@ class CollectionState(object): if event.name in flooded_keys.keys(): flood_location = self.world.get_location(flooded_keys[event.name], event.player) if (flood_location.item and flood_location not in self.locations_checked - and self.location_can_be_flooded(flood_location)): + and self.location_can_be_flooded(flood_location)): adjusted_checks.remove(event) if len(adjusted_checks) < len(reachable_events): return adjusted_checks @@ -1115,16 +1114,16 @@ class CollectionState(object): # Warning: This only considers items that are marked as advancement items diff = self.world.difficulty_requirements[player] return ( - min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) - + self.item_count('Sanctuary Heart Container', player) - + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 - + 3 # starting hearts + min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) + + self.item_count('Sanctuary Heart Container', player) + + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 + + 3 # starting hearts ) def can_lift_heavy_rocks(self, player): return self.has('Titans Mitts', player) - def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has. + def can_extend_magic(self, player, smallmagic=16, fullrefill=False): # This reflects the total magic Link has, not the total extra he has. basemagic = 8 if self.has('Magic Upgrade (1/4)', player): basemagic = 32 @@ -1162,19 +1161,19 @@ class CollectionState(object): or self.has('Ice Rod', player) or self.has('Cane of Somaria', player) or self.has('Cane of Byrna', player)) - + def can_hit_crystal_through_barrier(self, player): return (self.can_use_bombs(player) - or self.can_shoot_arrows(player) - or self.has('Blue Boomerang', player) - or self.has('Red Boomerang', player) - or self.has('Fire Rod', player) - or self.has('Ice Rod', player) - or self.has('Cane of Somaria', player)) + or self.can_shoot_arrows(player) + or self.has('Blue Boomerang', player) + or self.has('Red Boomerang', player) + or self.has('Fire Rod', player) + or self.has('Ice Rod', player) + or self.has('Cane of Somaria', player)) def can_shoot_arrows(self, player): if self.world.bow_mode[player] in ['retro', 'retro_silvers']: - #todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe + # todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe return self.has('Bow', player) and (self.can_buy_unlimited('Single Arrow', player) or self.has('Single Arrow', player)) return self.has('Bow', player) @@ -1215,7 +1214,7 @@ class CollectionState(object): return False # can't flute in rain state lw = self.world.get_region('Light World', player) return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self) - and self.is_not_bunny(lw, player)) + and self.is_not_bunny(lw, player)) def can_melt_things(self, player): return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) @@ -1403,13 +1402,14 @@ class CollectionState(object): def __getattr__(self, item): if item.startswith('can_reach_'): return self.can_reach(item[10]) - #elif item.startswith('has_'): + # elif item.startswith('has_'): # return self.has(item[4]) if item == '__len__': return raise RuntimeError('Cannot parse %s.' % item) + @unique class RegionType(Enum): Menu = 0 @@ -1435,7 +1435,7 @@ class Region(object): self.dungeon = None self.shop = None self.world = None - self.is_light_world = False # will be set aftermaking connections. + self.is_light_world = False # will be set aftermaking connections. self.is_dark_world = False self.spot_type = 'Region' self.hint_text = hint @@ -1747,7 +1747,7 @@ class Door(object): self.edge_id = None self.edge_width = None - #portal items + # portal items self.portalAble = False self.roomLayout = 0x22 # free scroll- both directions self.entranceFlag = False @@ -2093,17 +2093,17 @@ class Portal(object): return self.door.roomIndex def relative_coords(self): - y_rel = (self.door.roomIndex & 0xf0) >> 3 #todo: fix the shift!!!! + y_rel = (self.door.roomIndex & 0xf0) >> 3 # todo: fix the shift!!!! x_rel = (self.door.roomIndex & 0x0f) * 2 quad = self.door.quadrant if quad == 0: - return [y_rel, y_rel, y_rel, y_rel+1, x_rel, x_rel, x_rel, x_rel+1] + return [y_rel, y_rel, y_rel, y_rel + 1, x_rel, x_rel, x_rel, x_rel + 1] elif quad == 1: - return [y_rel, y_rel, y_rel, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1] + return [y_rel, y_rel, y_rel, y_rel + 1, x_rel + 1, x_rel, x_rel + 1, x_rel + 1] elif quad == 2: - return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel, x_rel, x_rel, x_rel+1] + return [y_rel + 1, y_rel, y_rel + 1, y_rel + 1, x_rel, x_rel, x_rel, x_rel + 1] else: - return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1] + return [y_rel + 1, y_rel, y_rel + 1, y_rel + 1, x_rel + 1, x_rel, x_rel + 1, x_rel + 1] def scroll_x(self): x_rel = (self.door.roomIndex & 0x0f) * 2 @@ -2112,7 +2112,7 @@ class Portal(object): elif self.door.doorIndex == 1: return [0x80, x_rel] else: - return [0x00, x_rel+1] + return [0x00, x_rel + 1] def scroll_y(self): y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1 @@ -2132,7 +2132,7 @@ class Portal(object): elif self.door.doorIndex == 1: return [0xf8, x_rel] else: - return [0x78, x_rel+1] + return [0x78, x_rel + 1] # def camera_y(self): # return [0x87, 0x01] @@ -2191,21 +2191,22 @@ class Boss(object): def can_defeat(self, state): return self.defeat_rule(state, self.player) + class Location(object): def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, player_address=None, note=None): self.name = name self.parent_region = parent if forced_item is not None: - from Items import ItemFactory - self.forced_item = ItemFactory([forced_item], player)[0] - self.item = self.forced_item - self.item.location = self - self.event = True + from Items import ItemFactory + self.forced_item = ItemFactory([forced_item], player)[0] + self.item = self.forced_item + self.item.location = self + self.event = True else: - self.forced_item = None - self.item = None - self.event = False + self.forced_item = None + self.item = None + self.event = False self.crystal = crystal self.address = address self.player_address = player_address @@ -2353,12 +2354,14 @@ class Item(object): class Crystal(Item): pass + @unique class ShopType(Enum): Shop = 0 TakeAny = 1 UpgradeShop = 2 + class Shop(object): def __init__(self, region, room_id, type, shopkeeper_config, custom, locked, sram_address): self.region = region @@ -2382,7 +2385,7 @@ class Shop(object): entrances = self.region.entrances config = self.item_count if len(entrances) == 1 and entrances[0].name in door_addresses: - door_id = door_addresses[entrances[0].name][0]+1 + door_id = door_addresses[entrances[0].name][0] + 1 else: door_id = 0 config |= 0x40 # ignore door id @@ -2390,7 +2393,7 @@ class Shop(object): config |= 0x80 if self.type == ShopType.UpgradeShop: config |= 0x10 # Alt. VRAM - return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00] + return [0x00] + int16_as_bytes(self.room_id) + [door_id, 0x00, config, self.shopkeeper_config, 0x00] def has_unlimited(self, item): for inv in self.inventory: @@ -2632,7 +2635,7 @@ class Spoiler(object): out['Special'] = self.medallions out['Bottles'] = self.bottles if self.hashes: - out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()} + out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team + 1})": hash for (player, team), hash in self.hashes.items()} if self.shops: out['Shops'] = self.shops out['playthrough'] = self.playthrough @@ -2723,7 +2726,7 @@ class Spoiler(object): if self.startinventory: outfile.write('Starting Inventory:'.ljust(line_width)) - outfile.write('\n'.ljust(line_width+1).join(self.startinventory) + '\n') + outfile.write('\n'.ljust(line_width + 1).join(self.startinventory) + '\n') def hashes_to_file(self, filename): with open(filename, 'r') as infile: @@ -2743,7 +2746,7 @@ class Spoiler(object): if len(self.hashes) > 0: for team in range(self.world.teams): player_name = self.world.player_names[player][team] - label = f"Hash - {player_name} (Team {team+1}): " if self.world.teams > 1 else 'Hash: ' + label = f"Hash - {player_name} (Team {team + 1}): " if self.world.teams > 1 else 'Hash: ' idx = insert(contents, idx, f'{label}{self.hashes[player, team]}\n') if self.world.players > 1: insert(contents, idx, '\n') # return value ignored here, if you want to add more lines @@ -2774,16 +2777,16 @@ class Spoiler(object): if self.entrances: # entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly outfile.write('\nEntrances:\n\n') - outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","entrances",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","entrances",entry['exit'])) for entry in self.entrances.values()])) + outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta", "entrances", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta", "entrances", entry['exit'])) for entry in self.entrances.values()])) if self.doors: outfile.write('\n\nDoors:\n\n') outfile.write('\n'.join( ['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', - self.world.fish.translate("meta","doors",entry['entrance']), + self.world.fish.translate("meta", "doors", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', - self.world.fish.translate("meta","doors",entry['exit']), - '({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for + self.world.fish.translate("meta", "doors", entry['exit']), + '({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] != 'basic' else '') for entry in self.doors.values()])) if self.lobbies: outfile.write('\n\nDungeon Lobbies:\n\n') @@ -2795,7 +2798,7 @@ class Spoiler(object): # doorNames: For some reason these come in combined, somehow need to split on the thing to translate # doorTypes: Small Key, Bombable, Bonkable outfile.write('\n\nDoor Types:\n\n') - outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta","doors",entry['doorNames']), self.world.fish.translate("meta","doorTypes",entry['type'])) for entry in self.doorTypes.values()])) + outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta", "doors", entry['doorNames']), self.world.fish.translate("meta", "doorTypes", entry['type'])) for entry in self.doorTypes.values()])) # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names @@ -2805,7 +2808,7 @@ class Spoiler(object): # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names outfile.write('\n\nShops:\n\n') - outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta","locations",shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta","items",item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) + outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta", "locations", shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta", "items", item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) for player in range(1, self.world.players + 1): if self.world.boss_shuffle[player] != 'none': @@ -2818,23 +2821,23 @@ class Spoiler(object): 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 '' + 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(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 '' + 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') + 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 # items: Item names outfile.write('\n\nPlaythrough:\n\n') - outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta","locations",location), self.world.fish.translate("meta","items",item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()])) + outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta", "locations", location), self.world.fish.translate("meta", "items", item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()])) if self.unreachables: # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names @@ -2852,10 +2855,10 @@ class Spoiler(object): path_lines = [] for region, exit in path: if exit is not None: - path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit))) + path_lines.append("{} -> {}".format(self.world.fish.translate("meta", "rooms", region), self.world.fish.translate("meta", "entrances", exit))) else: - path_lines.append(self.world.fish.translate("meta","rooms",region)) - path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines))) + path_lines.append(self.world.fish.translate("meta", "rooms", region)) + path_listings.append("{}\n {}".format(self.world.fish.translate("meta", "locations", location), "\n => ".join(path_lines))) outfile.write('\n'.join(path_listings)) @@ -2887,6 +2890,7 @@ dungeon_keys = { 'Universal': 'Small Key (Universal)' } + class PotItem(FastEnum): Nothing = 0x0 OneRupee = 0x1 @@ -2968,7 +2972,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, # byte 1: LLLW WSS? (logic, mode, sword) logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4} world_mode = {"open": 0, "standard": 1, "inverted": 2} -sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3} +sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3} # byte 2: GGGD DFFH (goal, diff, item_func, hints) goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5, @@ -3008,7 +3012,6 @@ rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2} algo_mode = {"balanced": 0, "equitable": 1, "vanilla_fill": 2, "dungeon_only": 3, "district": 4, 'major_only': 5} boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique': 4} - # byte 10: settings_version # byte 11: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode) flute_mode = {'normal': 0, 'active': 1} @@ -3071,7 +3074,7 @@ class Settings(object): ((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5 | trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]), - ]) + ]) return base64.b64encode(code, "+-".encode()).decode() @staticmethod diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index e85ed703..9a0b6f89 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -1800,7 +1800,7 @@ def imp_locations_factory(world, player): imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden'] if world.mode[player] == 'standard': imp_locations.append('Zelda Pickup') - imp_locations.append('Zelda Dropoff') + imp_locations.append('Zelda Drop Off') return imp_locations From 215c80eb4a005f6cdca7d514b2f3e9ec72bfb0be Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 15 Nov 2023 13:36:09 -0700 Subject: [PATCH 044/158] fix(algorithm): District algorithm fails if unable to comply with restriction --- Fill.py | 3 ++- source/item/FillUtil.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Fill.py b/Fill.py index b55e620c..2c5e4f01 100644 --- a/Fill.py +++ b/Fill.py @@ -280,9 +280,10 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp return spot_to_fill return None # explicitly fail these cases - elif world.algorithm in ['dungeon_only', 'major_only']: + elif world.algorithm in ['dungeon_only', 'major_only', 'district']: raise FillError(f'Rare placement for {world.algorithm} detected. {item_to_place} unable to be placed.' f' Try a different seed') + # I don't think any algorithm uses fallback placement anymore, vanilla is special. Others simply fail. else: other_locations = [x for x in locations if x not in attempted] for location in other_locations: diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 1a5c5692..3fbdfe86 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -416,11 +416,11 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion return filtered if world.algorithm == 'district': config = world.item_pool_config - if ((isinstance(item_to_place,str) and item_to_place == 'Placeholder') + if ((isinstance(item_to_place, str) and item_to_place == 'Placeholder') or item_to_place.name in config.item_pool[item_to_place.player]): restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name in restricted and l.player in restricted[l.name]] - return filtered if len(filtered) > 0 else locations + return filtered elif potion: restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name not in restricted or l.player not in restricted[l.name]] From ae3215ff9adcc94a88b4c3bc644a750a00ba66b1 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 13 Nov 2023 15:43:27 -0700 Subject: [PATCH 045/158] fix(key logic): typo fix(bunny logic): multiple paths considered --- Main.py | 2 +- RELEASENOTES.md | 3 ++ Rules.py | 105 ++++++++++++++++++++++++++---------------------- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/Main.py b/Main.py index 3a3a7a42..b5d5c61e 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.4' +version_number = '1.3.0.6' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bbd6b122..027e22cf 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -143,6 +143,9 @@ These are now independent of retro mode and have three options: None, Random, an * 1.3.0.6v * Enemizer: Arrghus at Lanmo 2 no longer prevents pot pickups + * Fixed logic issues: + * Self-locking key not allowed in Sanctuary in standard (typo fixed) + * More advanced bunny-walking logic in dungeons (multiple paths considered) * Various enemy bans * More Gibos near kiki and Old Man * Bumper obstacles diff --git a/Rules.py b/Rules.py index fd785eee..7cd1e7f1 100644 --- a/Rules.py +++ b/Rules.py @@ -108,9 +108,11 @@ def mirrorless_path_to_castle_courtyard(world, player): else: queue.append((entrance.connected_region, new_path)) + def set_rule(spot, rule): spot.access_rule = rule + 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)) @@ -157,22 +159,26 @@ def forbid_item(location, item, player): old_rule = location.item_rule location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i) + def add_item_rule(location, rule): old_rule = location.item_rule location.item_rule = lambda item: rule(item) and old_rule(item) + def item_in_locations(state, item, player, locations): for location in locations: if item_name(state, location[0], location[1]) == (item, player): return True return False + def item_name(state, location, player): location = state.world.get_location(location, player) if location.item is None: return None return (location.item.name, location.item.player) + def global_rules(world, player): # ganon can only carry triforce add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) @@ -204,7 +210,7 @@ def global_rules(world, player): set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) - set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest @@ -615,7 +621,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Swamp Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('Swamp Barrier', player), player)) set_rule(world.get_entrance('Swamp Crystal Switch Inner to Crystal', player), lambda state: state.can_hit_crystal(player)) - set_rule(world.get_entrance('Swamp Crystal Switch Outer to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('Swamp Crystal Switch Outer', player), player))) # It is the length of the sword, not the beam itself that allows this + set_rule(world.get_entrance('Swamp Crystal Switch Outer to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('Swamp Crystal Switch Outer', player), player))) # It is the length of the sword, not the beam itself that allows this set_rule(world.get_entrance('Swamp Crystal Switch Outer to Inner Bypass', player), lambda state: state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) set_rule(world.get_entrance('Swamp Crystal Switch Inner to Outer Bypass', player), lambda state: state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) @@ -659,8 +665,8 @@ def global_rules(world, player): set_rule(world.get_entrance('Mire Crystal Left Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Left', player), player)) set_rule(world.get_entrance('Mire Conveyor to Crystal', player), lambda state: state.can_hit_crystal(player)) - set_rule(world.get_entrance('Mire Tall Dark and Roomy to Ranged Crystal', player), lambda state: True) # Can always throw pots - set_rule(world.get_entrance('Mire Fishbone Blue Barrier Bypass', player), lambda state: False) # (state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) and state.can_tastate.can_use_bombs(player) // Easy to do but obscure. Should it be in logic? + set_rule(world.get_entrance('Mire Tall Dark and Roomy to Ranged Crystal', player), lambda state: True) # Can always throw pots + set_rule(world.get_entrance('Mire Fishbone Blue Barrier Bypass', player), lambda state: False) # (state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) and state.can_tastate.can_use_bombs(player) // Easy to do but obscure. Should it be in logic? set_rule(world.get_location('Turtle Rock - Chain Chomps', player), lambda state: state.can_reach('TR Chain Chomps Top', 'Region', player) and state.can_hit_crystal_through_barrier(player)) set_rule(world.get_entrance('TR Chain Chomps Top to Bottom Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('TR Chain Chomps Top', player), player)) @@ -682,14 +688,14 @@ def global_rules(world, player): set_rule(world.get_entrance('TR Pokey 2 Top to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('TR Crystaroller Top to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('TR Crystal Maze Start to Crystal', player), lambda state: state.can_hit_crystal(player)) - set_rule(world.get_entrance('TR Chain Chomps Bottom to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Chain Chomps Bottom', player), player))) # or state.has_beam_sword(player) - set_rule(world.get_entrance('TR Pokey 2 Bottom to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('TR Pokey 2 Bottom', player), player))) # or state.has_beam_sword(player) - set_rule(world.get_entrance('TR Crystaroller Bottom to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Crystaroller Bottom', player), player))) # or state.has_beam_sword(player) - set_rule(world.get_entrance('TR Crystaroller Middle to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Crystaroller Middle', player), player))) # or state.has_beam_sword(player) + set_rule(world.get_entrance('TR Chain Chomps Bottom to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Chain Chomps Bottom', player), player))) # or state.has_beam_sword(player) + set_rule(world.get_entrance('TR Pokey 2 Bottom to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('TR Pokey 2 Bottom', player), player))) # or state.has_beam_sword(player) + set_rule(world.get_entrance('TR Crystaroller Bottom to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Crystaroller Bottom', player), player))) # or state.has_beam_sword(player) + set_rule(world.get_entrance('TR Crystaroller Middle to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Crystaroller Middle', player), player))) # or state.has_beam_sword(player) set_rule(world.get_entrance('TR Crystaroller Middle to Bottom Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player)) - set_rule(world.get_entrance('TR Crystal Maze End to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) # or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) // These work by clipping the rang through the two stone blocks, which works sometimes. - set_rule(world.get_entrance('TR Crystal Maze Interior to End Bypass', player), lambda state: state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # Beam sword does NOT work - set_rule(world.get_entrance('TR Crystal Maze Interior to Start Bypass', player), lambda state: True) # Can always grab a pot from the interior and walk it to the start region and throw it there + set_rule(world.get_entrance('TR Crystal Maze End to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) # or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) // These work by clipping the rang through the two stone blocks, which works sometimes. + set_rule(world.get_entrance('TR Crystal Maze Interior to End Bypass', player), lambda state: state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # Beam sword does NOT work + set_rule(world.get_entrance('TR Crystal Maze Interior to Start Bypass', player), lambda state: True) # Can always grab a pot from the interior and walk it to the start region and throw it there set_rule(world.get_entrance('GT Hookshot Platform Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Hookshot South Platform', player), player)) set_rule(world.get_entrance('GT Hookshot Entry Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Hookshot South Entry', player), player)) @@ -779,20 +785,20 @@ def bomb_rules(world, 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: - add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player)) + add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player)) if world.doorShuffle[player] == 'vanilla': - add_rule(world.get_entrance('TR Lazy Eyes SE', player), lambda state: state.can_use_bombs(player)) # ToDo: Add always true for inverted, cross-entrance, and door-variants and so on. - add_rule(world.get_entrance('Turtle Rock Ledge Exit (West)', player), lambda state: state.can_use_bombs(player)) # Is this the same as above? + add_rule(world.get_entrance('TR Lazy Eyes SE', player), lambda state: state.can_use_bombs(player)) # ToDo: Add always true for inverted, cross-entrance, and door-variants and so on. + add_rule(world.get_entrance('Turtle Rock Ledge Exit (West)', player), lambda state: state.can_use_bombs(player)) # Is this the same as above? dungeon_bonkable = ['Sewers Rat Path WS', 'Sewers Rat Path WN', 'PoD Warp Hint SE', 'PoD Jelly Hall NW', 'PoD Jelly Hall NE', 'PoD Mimics 1 SW', 'Thieves Ambush E', 'Thieves Rail Ledge W', 'TR Dash Room NW', 'TR Crystaroller SW', 'TR Dash Room ES', - 'GT Four Torches NW','GT Fairy Abyss SW' + 'GT Four Torches NW', 'GT Fairy Abyss SW' ] dungeon_bombable = ['PoD Map Balcony WS', 'PoD Arena Ledge ES', 'PoD Dark Maze E', 'PoD Big Chest Balcony W', - 'Swamp Pot Row WN','Swamp Map Ledge EN', 'Swamp Hammer Switch WN', 'Swamp Hub Dead Ledge EN', 'Swamp Waterway N', 'Swamp I S', + 'Swamp Pot Row WN', 'Swamp Map Ledge EN', 'Swamp Hammer Switch WN', 'Swamp Hub Dead Ledge EN', 'Swamp Waterway N', 'Swamp I S', 'Skull Pot Circle WN', 'Skull Pull Switch EN', 'Skull Big Key EN', 'Skull Lone Pot WN', 'Thieves Rail Ledge NW', 'Thieves Pot Alcove Bottom SW', 'Ice Bomb Drop Hole', 'Ice Freezors Bomb Hole', @@ -800,9 +806,9 @@ def bomb_rules(world, player): 'GT Warp Maze (Rails) WS', 'GT Bob\'s Room Hole', 'GT Randomizer Room ES', 'GT Bomb Conveyor SW', 'GT Crystal Circles NW', 'GT Cannonball Bridge SE', 'GT Refill NE' ] for entrance in dungeon_bonkable: - add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) for entrance in dungeon_bombable: - add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player)) + add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player)) else: doors_to_bomb_check = [x for x in world.doors if x.player == player and x.type in [DoorType.Normal, DoorType.Interior]] for door in doors_to_bomb_check: @@ -965,13 +971,13 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_entrance('20 Rupee Cave', player), player) add_bunny_rule(world.get_entrance('50 Rupee Cave', player), player) - add_bunny_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), player) # bunny cannot lift bush - add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) # bunny cannot lift bush - add_bunny_rule(world.get_entrance('Skull Woods Final Section', player), player) # bunny cannot use fire rod + add_bunny_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), player) # bunny cannot lift bush + add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) # bunny cannot lift bush + add_bunny_rule(world.get_entrance('Skull Woods Final Section', player), player) # bunny cannot use fire rod add_bunny_rule(world.get_entrance('Hookshot Cave', player), player) - add_bunny_rule(world.get_entrance('Thieves Town', player), player) # bunny cannot pull + add_bunny_rule(world.get_entrance('Thieves Town', player), player) # bunny cannot pull add_bunny_rule(world.get_entrance('Turtle Rock', player), player) - add_bunny_rule(world.get_entrance('Palace of Darkness', player), player) # kiki needs pearl + add_bunny_rule(world.get_entrance('Palace of Darkness', player), player) # kiki needs pearl add_bunny_rule(world.get_entrance('Hammer Peg Cave', player), player) add_bunny_rule(world.get_entrance('Bonk Fairy (Dark)', player), player) add_bunny_rule(world.get_entrance('Misery Mire', player), player) @@ -1090,17 +1096,20 @@ def forbid_bomb_jump_requirements(world, player): set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False) set_rule(world.get_entrance('Ice Island To East Pier', player), lambda state: False) + # Light cones in standard depend on which world we actually are in, not which one the location would normally be # We add Lamp requirements only to those locations which lie in the dark world (or everything if open DW_Entrances = ['Bumper Cave (Bottom)', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', 'Hookshot Cave', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance', 'Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance', 'Thieves Town', 'Skull Woods Final Section', 'Ice Palace', 'Misery Mire', 'Palace of Darkness', 'Swamp Palace', 'Turtle Rock', 'Dark Death Mountain Ledge (West)'] + def check_is_dark_world(region): for entrance in region.entrances: if entrance.name in DW_Entrances: return True return False + def add_conditional_lamps(world, player): def add_conditional_lamp(spot, region, spottype='Location'): if spottype == 'Location': @@ -1285,6 +1294,7 @@ kill_chests = { } + def add_connection(parent_name, target_name, entrance_name, world, player): parent = world.get_region(parent_name, player) target = world.get_region(target_name, player) @@ -1322,7 +1332,7 @@ def standard_rules(world, player): return loc.item and loc.item.name in ['Bomb Upgrade (+10)' if world.bombbag[player] else 'Bombs (10)'] def standard_escape_rule(state): - return state.can_kill_most_things(player) or bomb_escape_rule() + return state.can_kill_most_things(player) or bomb_escape_rule() add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule) @@ -1349,6 +1359,7 @@ def standard_rules(world, player): def check_rule_list(state, r_list): return True if len(r_list) <= 0 else r_list[0](state) and check_rule_list(state, r_list[1:]) + rule_list, debug_path = find_rules_for_zelda_delivery(world, player) set_rule(world.get_entrance('Hyrule Castle Throne Room Tapestry', player), lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list)) @@ -1525,7 +1536,7 @@ def set_big_bomb_rules(world, player): set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) - #crossing peg bridge starting from the southern dark world + # crossing peg bridge starting from the southern dark world def cross_peg_bridge(state): return state.has('Hammer', player) and state.has_Pearl(player) @@ -1547,28 +1558,28 @@ def set_big_bomb_rules(world, player): # G = Glove if bombshop_entrance.name in Normal_LW_entrances: - #1. basic routes - #2. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror + # 1. basic routes + # 2. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror # -> M or BR add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: basic_routes(state) or state.has_Mirror(player)) elif bombshop_entrance.name in LW_walkable_entrances: - #1. Mirror then basic routes + # 1. Mirror then basic routes # -> M and BR add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and basic_routes(state)) elif bombshop_entrance.name in Northern_DW_entrances: - #1. Mirror and basic routes - #2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl + # 1. Mirror and basic routes + # 2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl # -> (Mitts and CPB) or (M and BR) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has_Mirror(player) and basic_routes(state))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': - #1. Mirror and Lift rock and basic_routes - #2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case) - #3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl + # 1. Mirror and Lift rock and basic_routes + # 2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case) + # 3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl # -> (Mitts and CPB) or (((G or Flute) and M) and BR)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.can_flute(player)) and state.has_Mirror(player)) and basic_routes(state))) elif bombshop_entrance.name in Southern_DW_entrances: - #1. Mirror and enter via gate: Need mirror and Aga1 - #2. cross peg bridge: Need hammer and moon pearl + # 1. Mirror and enter via gate: Need mirror and Aga1 + # 2. cross peg bridge: Need hammer and moon pearl # -> CPB or (M and A) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or (state.has_Mirror(player) and state.has('Beat Agahnim 1', player))) elif bombshop_entrance.name in Isolated_DW_entrances: @@ -1832,7 +1843,6 @@ def set_inverted_big_bomb_rules(world, player): def set_bunny_rules(world, player, inverted): - # regions for the exits of multi-entrace caves/drops that bunny cannot pass # Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing. bunny_impassable_caves = ['Bumper Cave (top)', 'Bumper Cave (bottom)', 'Two Brothers House', @@ -1871,13 +1881,14 @@ def set_bunny_rules(world, player, inverted): return region.is_light_world else: return region.is_dark_world + def is_link(region): if inverted: return region.is_dark_world else: return region.is_light_world - def get_rule_to_add(region, location = None, connecting_entrance = None): + def get_rule_to_add(region, location=None, connecting_entrance=None): # In OWG, a location can potentially be superbunny-mirror accessible or # bunny revival accessible. if world.logic[player] == 'owglitches': @@ -1900,16 +1911,15 @@ def set_bunny_rules(world, player, inverted): # for each such entrance a new option is added that consist of: # a) being able to reach it, and # b) being able to access all entrances from there to `region` - seen = {region} - queue = deque([(region, [])]) + queue = deque([(region, [], {region})]) while queue: - (current, path) = queue.popleft() + (current, path, seen) = queue.popleft() for entrance in current.entrances: new_region = entrance.parent_region if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_region in seen: continue new_path = path + [entrance.access_rule] - seen.add(new_region) + new_seen = seen.union({new_region}) if not is_link(new_region): if world.logic[player] == 'owglitches': if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: @@ -1946,7 +1956,7 @@ def set_bunny_rules(world, player, inverted): else: continue if is_bunny(new_region): - queue.append((new_region, new_path)) + queue.append((new_region, new_path, new_seen)) else: # we have reached pure light world, so we have a new possible option possible_options.append(path_to_access_rule(new_path, entrance)) @@ -1998,7 +2008,6 @@ drop_dungeon_entrances = { "Skull Back Drop" } - bunny_revivable_entrances = { "Sewers Pull Switch", "TR Dash Room", "Swamp Boss", "Hera Boss", "Tower Agahnim 1", "Ice Lobby", "Sewers Rat Path", "PoD Falling Bridge", @@ -2058,14 +2067,14 @@ bunny_impassible_doors = { 'PoD Arena Landing Bonk Path', 'PoD Sexy Statue NW', 'PoD Map Balcony Drop Down', 'PoD Mimics 1 NW', 'PoD Falling Bridge Path N', 'PoD Falling Bridge Path S', 'PoD Mimics 2 NW', 'PoD Bow Statue Down Ladder', 'PoD Dark Pegs Landing to Right', - 'PoD Dark Pegs Left to Middle Barrier - Blue', 'PoD Dark Pegs Left to Ranged Crystal', + 'PoD Dark Pegs Left to Middle Barrier - Blue', 'PoD Dark Pegs Left to Ranged Crystal', 'PoD Turtle Party ES', 'PoD Turtle Party NW', 'PoD Callback Warp', 'Swamp Lobby Moat', 'Swamp Entrance Moat', 'Swamp Trench 1 Approach Swim Depart', 'Swamp Trench 1 Approach Key', 'Swamp Trench 1 Key Approach', 'Swamp Trench 1 Key Ledge Depart', 'Swamp Trench 1 Departure Approach', 'Swamp Trench 1 Departure Key', 'Swamp Hub Hook Path', 'Swamp Shortcut Blue Barrier', 'Swamp Trench 2 Pots Blue Barrier', 'Swamp Trench 2 Pots Wet', 'Swamp Trench 2 Departure Wet', 'Swamp West Ledge Hook Path', 'Swamp Barrier Ledge Hook Path', 'Swamp Attic Left Pit', 'Swamp Attic Right Pit', 'Swamp Push Statue NW', 'Swamp Push Statue NE', - 'Swamp Drain Right Switch', 'Swamp Waterway NE', 'Swamp Waterway N', 'Swamp Waterway NW', + 'Swamp Drain Right Switch', 'Swamp Waterway NE', 'Swamp Waterway N', 'Swamp Waterway NW', 'Skull Pot Circle WN', 'Skull Pot Circle Star Path', 'Skull Pull Switch S', 'Skull Big Chest N', 'Skull Big Chest Hookpath', 'Skull 2 East Lobby NW', 'Skull Back Drop Star Path', 'Skull 2 West Lobby NW', 'Skull 3 Lobby EN', 'Skull Star Pits SW', 'Skull Star Pits ES', 'Skull Torch Room WN', 'Skull Vines NW', @@ -2101,7 +2110,7 @@ bunny_impassible_doors = { 'GT Double Switch Exit to Blue Barrier', 'GT Firesnake Room Hook Path', 'GT Falling Bridge WN', 'GT Falling Bridge WS', 'GT Ice Armos NE', 'GT Ice Armos WS', 'GT Crystal Paths SW', 'GT Mimics 1 NW', 'GT Mimics 1 ES', 'GT Mimics 2 WS', 'GT Mimics 2 NE', 'GT Hidden Spikes EN', 'GT Cannonball Bridge SE', 'GT Gauntlet 1 WN', 'GT Gauntlet 2 EN', - 'GT Gauntlet 2 SW', 'GT Gauntlet 3 NW', 'GT Gauntlet 3 SW', 'GT Gauntlet 4 NW', 'GT Gauntlet 4 SW', + 'GT Gauntlet 2 SW', 'GT Gauntlet 3 NW', 'GT Gauntlet 3 SW', 'GT Gauntlet 4 NW', 'GT Gauntlet 4 SW', 'GT Gauntlet 5 NW', 'GT Gauntlet 5 WS', 'GT Lanmolas 2 ES', 'GT Lanmolas 2 NW', 'GT Wizzrobes 1 SW', 'GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE', 'GT Torch Cross ES', 'GT Falling Torches NE', 'GT Moldorm Gap', 'GT Validation Block Path' @@ -2254,7 +2263,7 @@ def create_key_rule(small_key_name, player, keys): def create_key_rule_allow_small(small_key_name, player, keys, location): loc = location.name - return lambda state: state.has_sm_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys-1)) + return lambda state: state.has_sm_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys - 1)) def create_key_rule_bk_exception(small_key_name, big_key_name, player, keys, bk_keys, bk_locs): @@ -2265,7 +2274,7 @@ def create_key_rule_bk_exception(small_key_name, big_key_name, player, keys, bk_ def create_key_rule_bk_exception_or_allow(small_key_name, big_key_name, player, keys, location, bk_keys, bk_locs): loc = location.name chest_names = [x.name for x in bk_locs] - return lambda state: (state.has_sm_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys-1)) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_sm_key(small_key_name, player, bk_keys)) + return lambda state: (state.has_sm_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys - 1)) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_sm_key(small_key_name, player, bk_keys)) def create_advanced_key_rule(key_logic, player, rule): From 876e6e09d7ae3b10d05493e87e7630cf0de63b58 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 15 Nov 2023 13:25:13 -0700 Subject: [PATCH 046/158] fix(ER): Links house on Dm won't allow a cave to be used twice anymore --- RELEASENOTES.md | 1 + source/overworld/EntranceShuffle2.py | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 027e22cf..54bb3a48 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -146,6 +146,7 @@ These are now independent of retro mode and have three options: None, Random, an * Fixed logic issues: * Self-locking key not allowed in Sanctuary in standard (typo fixed) * More advanced bunny-walking logic in dungeons (multiple paths considered) + * ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used) * Various enemy bans * More Gibos near kiki and Old Man * Bumper obstacles diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 86bac34e..c4fcc771 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -497,10 +497,14 @@ def do_links_house(entrances, exits, avail, cross_world): entrances.remove(chosen_dm_escape) entrances.remove(chosen_landing) else: - connect_entrance(chosen_dm_escape, chosen_cave.pop(0), avail) + chosen_cave_first = chosen_cave.pop(0) + connect_entrance(chosen_dm_escape, chosen_cave_first, avail) connect_exit(chosen_cave.pop(), chosen_landing, avail) entrances.remove(chosen_dm_escape) + avail.decoupled_exits.remove(chosen_cave_first) avail.decoupled_entrances.remove(chosen_landing) + # chosen cave has already been removed from exits + exits.add(chosen_cave_first) # this needs to be added back in if len(chosen_cave): exits.update([x for x in chosen_cave]) exits.update([x for item in multi_exit_caves for x in item]) @@ -1094,7 +1098,7 @@ def connect_entrance(entrancename, exit_name, avail): def connect_exit(exit_name, entrancename, avail): - world, player = avail.world, avail. player + world, player = avail.world, avail.player entrance = world.get_entrance(entrancename, player) exit = world.get_entrance(exit_name, player) From 3f0fcd45ff04b05decd0b8be04ec4358211af769 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 15 Nov 2023 14:33:11 -0700 Subject: [PATCH 047/158] fix: Flute doesn't work in rain state (even if pre-activated) except for glitched modes fix(msu): GTBK Music in DR --- RELEASENOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 54bb3a48..63bdbde7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -142,11 +142,13 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes * 1.3.0.6v + * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) * Enemizer: Arrghus at Lanmo 2 no longer prevents pot pickups * Fixed logic issues: * Self-locking key not allowed in Sanctuary in standard (typo fixed) * More advanced bunny-walking logic in dungeons (multiple paths considered) * ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used) + * MSU: GTBK song fix for DR (Thanks codemann!) * Various enemy bans * More Gibos near kiki and Old Man * Bumper obstacles From c27c5455a409040558b16cbd79b8097461dba1dc Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 15 Nov 2023 15:48:13 -0700 Subject: [PATCH 048/158] fix: YA Hera Boss music fix: Various enemizer enemy bans --- RELEASENOTES.md | 10 +++++++--- Rom.py | 2 +- data/base2current.bps | Bin 117459 -> 27 bytes source/enemizer/Enemizer.py | 2 ++ source/enemizer/enemy_deny.yaml | 24 ++++++++++++++++-------- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 63bdbde7..dcd4c502 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -143,19 +143,23 @@ These are now independent of retro mode and have three options: None, Random, an * 1.3.0.6v * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) - * Enemizer: Arrghus at Lanmo 2 no longer prevents pot pickups + * Enemizer + * Arrghus at Lanmo 2 no longer prevents pot pickups + * Lift-able Blocks require a sprite slot (should help reduce problems) * Fixed logic issues: * Self-locking key not allowed in Sanctuary in standard (typo fixed) * More advanced bunny-walking logic in dungeons (multiple paths considered) * ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used) * MSU: GTBK song fix for DR (Thanks codemann!) + * District Algorithm: Fails if no available location outside chosen districts * Various enemy bans * More Gibos near kiki and Old Man - * Bumper obstacles + * Bumper/AntiFairy obstacles * Damaging roller * Statue + Pots don't mix + * Statues on Skull Big Key Chest tile * Toppo in challenge rooms - * Misc + * Misc others * 1.3.0.5v * Hud/Map Counter: Collecting a keys for this dungeon of a bonk torch no longer increments the counter twice and immediately updates the hud. * Enemizer: Hera basement item counting twice fixed by banning wallmasters on the tile. diff --git a/Rom.py b/Rom.py index 4995784b..836e3503 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'cafcf71aefe71fc6cd8ab8fef06794b0' +RANDOMIZERBASEHASH = '03a63945398191337e896e5771f77173' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 85192376666d8b24ca2f6eb414aeeaa2fdf02123..eacabeae42efe76ce27222c229278924263435b8 100644 GIT binary patch literal 27 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 S>FtV$={VPGcvALu-~RysPz^!= diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 411011a8..242cbd8c 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -302,6 +302,8 @@ def randomize_underworld_rooms(data_tables, world, player, custom_uw): continue current_sprites = data_tables.uw_enemy_table.room_map[room_id] sprite_limit = sum(sprite_limiter[x.kind] if x.kind in sprite_limiter else 1 for x in current_sprites) + if room_id in {0x3f, 0x44, 0x45, 0x93, 0xce, 0x117}: + sprite_limit += 1 # for liftable blocks see PotFlags.Block in PotShuffle 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, uw_sheets) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 4e15f8c3..4a4b0e90 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -73,6 +73,7 @@ UwGeneralDeny: - [ 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" + - [ 0x003b, 1, [ "Bumper" ]] - [ 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)" @@ -81,7 +82,7 @@ UwGeneralDeny: - [ 0x003d, 13, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Antifairy" - [ 0x003f, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 1" - [ 0x003f, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 2" - - [ 0x003f, 4, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots + - [ 0x003f, 4, [ "Wizzrobe", "Statue", "Bumper", "BigSpike", "AntiFairyCircle"]] # Wizzrobes can't spawn on pots - [ 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" @@ -125,20 +126,27 @@ UwGeneralDeny: - [ 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, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - - [ 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", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike", "SpikeBlock"]] #"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" + - [ 0x0057, 1, ["Statue"]] # Statue switch issues + - [ 0x0057, 2, [ "RollerVerticalUp", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Spike Trap" + - [ 0x0057, 3, ["Statue"]] # Statue switch issues + - [ 0x0057, 4, ["Statue"]] # Statue switch issues + - [ 0x0057, 5, ["Statue"]] # Statue switch issues + - [ 0x0057, 7, [ "RollerVerticalUp", "RollerVerticalDown", "Statue" ] ] #"Skull Woods - Big Key Room - Gibdo 2" + - [ 0x0057, 8, ["Statue"]] # Statue switch issues + - [ 0x0057, 9, ["Statue"]] # Statue switch issues + - [ 0x0057, 10, ["Statue"]] # Statue switch issues + - [ 0x0057, 11, ["Statue"]] # Statue switch issues + - [ 0x0057, 12, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike", "SpikeBlock", "Statue"]] #"Skull Woods - Big Key Room - Gibdo 6" + - [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 1" + - [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 2" - [ 0x0058, 0, ["Statue"]] - [ 0x0058, 1, ["Statue"]] - [ 0x0058, 2, ["Statue"]] - [ 0x0058, 3, ["Statue"]] - [ 0x0058, 4, ["Statue"]] - [ 0x0058, 6, ["Statue"]] - - [ 0x0058, 7, ["Statue"]] + - [ 0x0058, 7, [ "RollerHorizontalLeft", "Statue" ] ] #"Skull Woods - Lever Room - Hardhat Beetle 2" - [ 0x0058, 8, ["Statue"]] - - [ 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" From c0a06e79864c85d31003d15334ba8987ccd9fbc4 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 15 Nov 2023 15:54:54 -0700 Subject: [PATCH 049/158] build: fix rom build --- Rom.py | 2 +- data/base2current.bps | Bin 27 -> 117541 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 836e3503..afe69a0e 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '03a63945398191337e896e5771f77173' +RANDOMIZERBASEHASH = '0f97b8f1f54afaef84e630e3bb407207' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index eacabeae42efe76ce27222c229278924263435b8..802aa38ef04f42ff76cf6bf3328829e8e30873ed 100644 GIT binary patch literal 117541 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 Date: Wed, 15 Nov 2023 16:38:19 -0700 Subject: [PATCH 050/158] fix(logic): Backwards to Lanmo 2 with Trinexx requires the cape --- RELEASENOTES.md | 1 + Regions.py | 2 +- Rules.py | 4 ++++ test/customizer/test.yaml | 20 ++++++++++++-------- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index dcd4c502..4ed6081a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -145,6 +145,7 @@ These are now independent of retro mode and have three options: None, Random, an * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) * Enemizer * Arrghus at Lanmo 2 no longer prevents pot pickups + * Trinexx at Lanmo 2 requires the Cape go backwards to face him * Lift-able Blocks require a sprite slot (should help reduce problems) * Fixed logic issues: * Self-locking key not allowed in Sanctuary in standard (typo fixed) diff --git a/Regions.py b/Regions.py index 9f986006..688764af 100644 --- a/Regions.py +++ b/Regions.py @@ -851,7 +851,7 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'GT Cannonball Bridge', 'Ganon\'s Tower', None, ['GT Cannonball Bridge WN', 'GT Cannonball Bridge Up Stairs', 'GT Cannonball Bridge SE']), create_dungeon_region(player, 'GT Refill', 'Ganon\'s Tower', None, ['GT Refill NE']), create_dungeon_region(player, 'GT Gauntlet 1', 'Ganon\'s Tower', None, ['GT Gauntlet 1 Down Stairs', 'GT Gauntlet 1 WN']), - create_dungeon_region(player, 'GT Gauntlet 2', 'Ganon\'s Tower', None, ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW']), + create_dungeon_region(player, 'GT Gauntlet 2', 'Ganon\'s TowerA', None, ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW']), create_dungeon_region(player, 'GT Gauntlet 3', 'Ganon\'s Tower', None, ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW']), create_dungeon_region(player, 'GT Gauntlet 4', 'Ganon\'s Tower', None, ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW']), create_dungeon_region(player, 'GT Gauntlet 5', 'Ganon\'s Tower', None, ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS']), diff --git a/Rules.py b/Rules.py index 7cd1e7f1..e6ebed97 100644 --- a/Rules.py +++ b/Rules.py @@ -542,6 +542,10 @@ def global_rules(world, player): set_rule(world.get_entrance('GT Cannonball Bridge SE', player), lambda state: state.has_Boots(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)) + # Need cape to safely get past trinexx backwards in this room, makes magic usage tighter + # Could not guarantee safety with byrna, not sure why + if world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].name == 'Trinexx': + add_rule(world.get_entrance('GT Quad Pot SW', player), lambda state: state.has('Cape', player)) set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player)) if is_trapped('GT Torch Cross WN'): set_rule(world.get_entrance('GT Torch Cross WN', player), lambda state: state.has_fire_source(player)) diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml index 49cfb516..5c1136c0 100644 --- a/test/customizer/test.yaml +++ b/test/customizer/test.yaml @@ -1,13 +1,13 @@ meta: players: 1 - seed: 94 + seed: 91 settings: 1: # mode: standard -# boss_shuffle: random - dropshuffle: underworld - enemy_shuffle: shuffled + boss_shuffle: random +# dropshuffle: underworld +# enemy_shuffle: shuffled door_shuffle: crossed intensity: 3 @@ -18,12 +18,16 @@ doors: Hyrule Castle South: GT Lobby S doors: GT Lobby Left Down Stairs: GT Quad Pot Up Stairs + GT Lobby Right Down Stairs: GT Lobby Up Stairs + GT Beam Dash ES: Swamp Big Key Ledge WN + bosses: 1: - Ganons Tower (middle): Arrghus -#placements: -# 1: -# Desert Palace - Torch: Small Key (Escape) + Ganons Tower (middle): Trinexx +placements: + 1: + Swamp Palace - Big Key Chest: Progressive Glove + Lumberjack Tree: Piece of Heart # keyshuffle: wild From a2c4be4c4cc547d86d469182907143c9cb64daf2 Mon Sep 17 00:00:00 2001 From: Cody Bailey Date: Sun, 19 Nov 2023 17:32:45 -0500 Subject: [PATCH 051/158] Add aga_randomness to exposed settings --- BaseClasses.py | 2 +- CLI.py | 3 ++- Main.py | 1 + Rom.py | 2 +- resources/app/cli/args.json | 4 ++++ source/classes/CustomSettings.py | 2 ++ source/tools/MysteryUtils.py | 1 + 7 files changed, 12 insertions(+), 3 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 7a463a38..231cf89b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -54,7 +54,6 @@ class World(object): self.shuffle_bonk_prizes = False self.clock_mode = 'none' self.rupoor_cost = 10 - self.aga_randomness = True self.lock_aga_door_in_escape = False self.save_and_quit_from_boss = True self.accessibility = accessibility.copy() @@ -150,6 +149,7 @@ class World(object): set_player_attr('door_type_mode', 'original') set_player_attr('trap_door_mode', 'optional') set_player_attr('key_logic_algorithm', 'default') + set_player_attr('aga_randomness', True) set_player_attr('shopsanity', False) set_player_attr('mixed_travel', 'prevent') diff --git a/CLI.py b/CLI.py index 4c9d4fc0..38af7c52 100644 --- a/CLI.py +++ b/CLI.py @@ -141,7 +141,7 @@ def parse_cli(argv, no_defaults=False): 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode', - 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'any_enemy_logic']: + 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'any_enemy_logic', 'aga_randomness']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -223,6 +223,7 @@ def parse_settings(): 'dungeon_counters': 'default', 'mixed_travel': 'prevent', 'standardize_palettes': 'standardize', + 'aga_randomness': True, "triforce_pool": 0, "triforce_goal": 0, diff --git a/Main.py b/Main.py index b5d5c61e..3ffda1f9 100644 --- a/Main.py +++ b/Main.py @@ -137,6 +137,7 @@ def main(args, seed=None, fish=None): world.restrict_boss_items = args.restrict_boss_items.copy() world.collection_rate = args.collection_rate.copy() world.colorizepots = args.colorizepots.copy() + world.aga_randomness = args.aga_randomness.copy() world.treasure_hunt_count = {} world.treasure_hunt_total = {} diff --git a/Rom.py b/Rom.py index afe69a0e..7b018e13 100644 --- a/Rom.py +++ b/Rom.py @@ -1118,7 +1118,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.initial_sram.pre_open_ganons_tower() rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable - rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness + rom.write_byte(0x180086, 0x00 if world.aga_randomness[player] else 0x01) # set blue ball and ganon warp randomness rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp rom.write_byte(0x180174, 0x01 if world.fix_fake_world[player] else 0x00) diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 354a5618..42934aba 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -514,6 +514,10 @@ "action": "store_true", "type": "bool" }, + "aga_randomness": { + "action": "store_false", + "type": "bool" + }, "saveonexit": { "choices": [ "ask", diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index 631ca260..c72a111c 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -155,6 +155,7 @@ class CustomSettings(object): args.triforce_min_difference[p] = get_setting(settings['triforce_min_difference'], args.triforce_min_difference[p]) args.triforce_max_difference[p] = get_setting(settings['triforce_max_difference'], args.triforce_max_difference[p]) args.beemizer[p] = get_setting(settings['beemizer'], args.beemizer[p]) + args.aga_randomness[p] = get_setting(settings['aga_randomness'], args.aga_randomness[p]) # mystery usage args.usestartinventory[p] = get_setting(settings['usestartinventory'], args.usestartinventory[p]) @@ -283,6 +284,7 @@ class CustomSettings(object): settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p] settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p] settings_dict[p]['beemizer'] = world.beemizer[p] + settings_dict[p]['aga_randomness'] = world.aga_randomness[p] # rom adjust stuff # settings_dict[p]['sprite'] = world.sprite[p] diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 00df02ef..017144eb 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -122,6 +122,7 @@ def roll_settings(weights): ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery ret.colorizepots = get_choice_bool_default('colorizepots', default=True) ret.shufflepots = get_choice_bool('pot_shuffle') + ret.aga_randomness = get_choice_bool('aga_randomness') ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent' ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize') From f2b07a7f8f5b7f83f35803bc9d8461fa5ba530f0 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 19 Nov 2023 14:07:47 -0600 Subject: [PATCH 052/158] Added missing saved user settings in Adjust tab --- source/gui/bottom.py | 4 +++- source/gui/loadcliargs.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 39b07eed..402e9286 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -249,7 +249,9 @@ def create_guiargs(parent): "heartbeep": "heartbeep", "menuspeed": "fastmenu", "owpalettes": "ow_palettes", - "uwpalettes": "uw_palettes" + "uwpalettes": "uw_palettes", + "reduce_flashing": "reduce_flashing", + "shuffle_sfx": "shuffle_sfx" } for adjustarg in adjustargs: internal = adjustargs[adjustarg] diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index f8551c4c..24d76aaf 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -192,7 +192,9 @@ def loadadjustargs(gui, settings): "heartbeep": "adjust.heartbeep", "menuspeed": "adjust.menuspeed", "owpalettes": "adjust.owpalettes", - "uwpalettes": "adjust.uwpalettes" + "uwpalettes": "adjust.uwpalettes", + "reduce_flashing": "adjust.reduce_flashing", + "shuffle_sfx": "adjust.shuffle_sfx" } } } From 3e5d4abfd36538b3bec7a9ef9e47242da7a3ceec Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 20 Nov 2023 16:46:42 -0700 Subject: [PATCH 053/158] fix: don't shuffle bonk fairies fix: fix mimic cave enemy drops --- Main.py | 2 +- RELEASENOTES.md | 3 ++ source/dungeon/EnemyList.py | 91 +++++++++++++++++----------------- source/enemizer/OwEnemyList.py | 40 +++++++-------- 4 files changed, 70 insertions(+), 66 deletions(-) diff --git a/Main.py b/Main.py index 3ffda1f9..98cac70e 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.6' +version_number = '1.3.0.7' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4ed6081a..870bc31d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.7v + * Fix for Mimic Cave enemy drops + * No longer shuffles fairy bonks (from trees) as part of Enemizer * 1.3.0.6v * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) * Enemizer diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index d69a0a88..5038fca1 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -70,7 +70,7 @@ class EnemySprite(FastEnum): Smithy = 0x1a Arrow = 0x1b Statue = 0x1c - FluteQuest = 0x1d + FluteQuest = 0x1d CrystalSwitch = 0x1e SickKid = 0x1f Sluggula = 0x20 @@ -105,7 +105,7 @@ class EnemySprite(FastEnum): OldSnitch = 0x3d Hoarder2 = 0x3e TutorialGuard = 0x3f - + LightningGate = 0x40 BlueGuard = 0x41 GreenGuard = 0x42 @@ -115,7 +115,7 @@ class EnemySprite(FastEnum): BlueArcher = 0x46 GreenBushGuard = 0x47 RedJavelinGuard = 0x48 - RedBushGuard = 0x49 + RedBushGuard = 0x49 BombGuard = 0x4a GreenKnifeGuard = 0x4b Geldman = 0x4c @@ -148,13 +148,13 @@ class EnemySprite(FastEnum): WallCannonVertLeft = 0x66 WallCannonVertRight = 0x67 WallCannonHorzTop = 0x68 - WallCannonHorzBottom = 0x69 + WallCannonHorzBottom = 0x69 BallNChain = 0x6a CannonTrooper = 0x6b CricketRat = 0x6d Snake = 0x6e Keese = 0x6f - + Leever = 0x71 FairyPondTrigger = 0x72 UnclePriest = 0x73 @@ -182,7 +182,7 @@ class EnemySprite(FastEnum): Arrghus = 0x8c Arrghi = 0x8d Terrorpin = 0x8e - Blob = 0x8f + Blob = 0x8f Wallmaster = 0x90 StalfosKnight = 0x91 HelmasaurKing = 0x92 @@ -199,7 +199,7 @@ class EnemySprite(FastEnum): Babasu = 0x9d # babasu vertical? GroveOstritch = 0x9e GroveRabbit = 0x9f - GroveBird = 0xa0 + GroveBird = 0xa0 Freezor = 0xa1 Kholdstare = 0xa2 KholdstareShell = 0xa3 @@ -228,7 +228,7 @@ class EnemySprite(FastEnum): Whirlpool = 0xba Shopkeeper = 0xbb Drunkard = 0xbc - Vitreous = 0xbd + Vitreous = 0xbd # ... (spawnables) Catfish = 0xc0 CutsceneAgahnim = 0xc1 @@ -245,7 +245,7 @@ class EnemySprite(FastEnum): TrinexxFireHead = 0xcc TrinexxIceHead = 0xcd Blind = 0xce - Swamola = 0xcf + Swamola = 0xcf Lynel = 0xd0 BunnyBeam = 0xd1 FloppingFish = 0xd2 @@ -275,8 +275,8 @@ class EnemySprite(FastEnum): MedallionTablet = 0xf2 PositionTarget = 0xf3 Boulders = 0xf4 - - + + class SpriteType(FastEnum): Normal = 0x00 Overlord = 0x07 @@ -765,7 +765,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') #, fix=True) + 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') @@ -800,7 +800,7 @@ def init_vanilla_sprites(): create_sprite(0x0024, EnemySprite.Pokey, 0x00, 0, 0x05, 0x08, 'TR Twin Pokeys') 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') #, fix=True) + 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') @@ -927,12 +927,12 @@ def init_vanilla_sprites(): create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16, 'Skull Final Drop') create_sprite(0x0039, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18, 'Skull Spike Corner') create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a, 'Skull Final Drop') - create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11, 'PoD Pit Room',) - create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11, 'PoD Pit Room',) - create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14, 'PoD Pit Room',) - create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14, 'PoD Pit Room',) - create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x14, 'PoD Pit Room',) - create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x14, 'PoD Pit Room',) + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x14, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x14, 'PoD Pit Room') create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x06, 'PoD Conveyor') create_sprite(0x003b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09, 'PoD Conveyor') create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x0d, 'PoD Conveyor') @@ -1005,7 +1005,7 @@ 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") #, fix=True) + 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") @@ -1103,7 +1103,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') #, fix=True) + 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') @@ -1188,7 +1188,7 @@ def init_vanilla_sprites(): create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x12, 'Thieves Attic Hint', embed=True) 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') #, fix=True) + 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') @@ -1422,7 +1422,7 @@ def init_vanilla_sprites(): create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x06) create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x0a) create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x0a) - create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x08, 0x08, 'GT Bob\'s Torch',) + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x08, 0x08, 'GT Bob\'s Torch') create_sprite(0x008c, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08, 'GT Hope Room') create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x09, 'GT Bob\'s Torch') create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0b, 'GT Bob\'s Torch') @@ -1447,7 +1447,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') #, fix=True) + 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') @@ -1557,7 +1557,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') #, fix=True) + 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) @@ -1633,9 +1633,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') #, fix=True) + 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') #, fix=True) + 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') @@ -1736,7 +1736,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') #, fix=True) + 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) @@ -1782,7 +1782,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') #, fix=True) + 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') @@ -1793,7 +1793,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') #, fix=True) + 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') @@ -1870,7 +1870,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') #, fix=True) + 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') @@ -2183,6 +2183,7 @@ splittable_supertiles = {0x9, 0x1a, 0x35, 0x36, 0x37, 0x2a, 0x57, 0x74, 0x75, 0x # fairy needs 0x107, 0x10c, 0x115, 0x11e, 0x120, 0x126} + # minimum 159 bytes maybe reserve 256 (0x100) # tr pipes, mire left/right bridges, eastern cannonball, tr front entrance = have zero enemies so far but are splittable # 0x14, 0xa2, 0xb9, 0xd6 @@ -2203,7 +2204,7 @@ def setup_enemy_dungeon_tables(world, player): if (super_tile, index) in key_drop_special: loc_name = key_drop_special[(super_tile, index)] else: - loc_name = f'{sprite.region} Enemy #{index+1}' + loc_name = f'{sprite.region} Enemy #{index + 1}' loc = world.get_location_unsafe(loc_name, player) if sprite.sub_type == 0x07: # overlord idx_adj += 1 @@ -2212,12 +2213,13 @@ def setup_enemy_dungeon_tables(world, player): # possible to-do: caves really aren't supported yet - entrance ids? if loc.parent_region.dungeon: dungeon = loc.parent_region.dungeon.dungeon_id * 2 - dungeon_map[super_tile][dungeon].append((loc, index-idx_adj)) + dungeon_map[super_tile][dungeon].append((loc, index - idx_adj)) else: - if super_tile not in super_tile_entrance_id_map: - super_tile_entrance_id_map[super_tile] = find_entrance_ids(loc.parent_region) - for entrance_id in super_tile_entrance_id_map[super_tile]: - dungeon_map[super_tile][entrance_id].append((loc, index-idx_adj)) + map_key = super_tile, loc.parent_region.name + if map_key not in super_tile_entrance_id_map: + super_tile_entrance_id_map[map_key] = find_entrance_ids(loc.parent_region) + for entrance_id in super_tile_entrance_id_map[map_key]: + dungeon_map[super_tile][entrance_id].append((loc, index - idx_adj)) special_bitmasks = defaultdict(lambda: defaultdict(int)) for super_tile, dungeon_list in dungeon_map.items(): for dungeon, data_list in dungeon_list.items(): @@ -2254,12 +2256,11 @@ def valid_drop_location(sprite, index, world, player): def create_drop_location(sprite, index, super_tile, world, player): - address = drop_address(index, super_tile) region_name = sprite.region parent = world.get_region(region_name, player) enemy_name = enemy_names[sprite.kind] - descriptor = f'Enemy #{index+1}' + descriptor = f'Enemy #{index + 1}' modifier = parent.hint_text not in {'a storyteller', 'fairies deep in a cave', 'a spiky hint', 'a bounty of five items', 'the sick kid', 'Sahasrahla'} hint_text = f'held by a {enemy_name} {"in" if modifier else "near"} {parent.hint_text}' @@ -2298,7 +2299,7 @@ prize_pack_selector = { def add_drop_contents(world, player): retro_bow = world.bow_mode[player].startswith('retro') - index_selector = [0]*8 + index_selector = [0] * 8 for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items(): for sprite in enemy_list: if sprite.drops_item and sprite.drop_item_kind == 0xe4: @@ -2552,14 +2553,14 @@ enemy_names = { } overlord_names = { - 0x01: 'PositionTarget', 0x02: 'FullRoomCannons', 0x03: 'VerticalCanon', + 0x01: 'PositionTarget', 0x02: 'FullRoomCannons', 0x03: 'VerticalCanon', 0x05: 'FallingStalfos', 0x06: 'SnakeTrap', - 0x07: 'MovingFloor', 0x08: 'BlobSpawner', 0x09: 'Wallmaster', - 0x0A: 'FallingSquare', 0x0B: 'FallingBridge', + 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', + 0x15: 'WizzrobeSpawner', 0x16: 'ZoroSpawner', 0x17: 'PotTrap', 0x18: 'InvisibleStalfos', + 0x19: 'ArmosCoordinator', 0x1A: 'BombTrap', } sprite_translation = { diff --git a/source/enemizer/OwEnemyList.py b/source/enemizer/OwEnemyList.py index 84cca225..73758def 100644 --- a/source/enemizer/OwEnemyList.py +++ b/source/enemizer/OwEnemyList.py @@ -62,7 +62,7 @@ def init_vanilla_sprites_ow(): # Screen42: create_sprite(0x42, EnemySprite.Snapdragon, 0x0C, 0x11, '', 0x09CBB7) create_sprite(0x42, EnemySprite.Snapdragon, 0x0C, 0x13, '', 0x09CBBA) - create_sprite(0x42, EnemySprite.Faerie, 0x06, 0x16, '', 0x09CBBD) + create_sprite(0x42, EnemySprite.Faerie, 0x06, 0x16, '', 0x09CBBD, fix=True) create_sprite(0x42, EnemySprite.Moblin, 0x0E, 0x19, '', 0x09CBC0) # Screen43: create_sprite(0x43, EnemySprite.Waterfall, 0x2F, 0x0C, '', 0x09CBC4) @@ -96,8 +96,8 @@ def init_vanilla_sprites_ow(): create_sprite(0x50, EnemySprite.Moblin, 0x08, 0x18, '', 0x09CC0E) # Screen51: create_sprite(0x51, EnemySprite.UsainBolt, 0x17, 0x0E, '', 0x09CC12) - create_sprite(0x51, EnemySprite.Faerie, 0x08, 0x10, '', 0x09CC15) - create_sprite(0x51, EnemySprite.Faerie, 0x09, 0x10, '', 0x09CC18) + create_sprite(0x51, EnemySprite.Faerie, 0x08, 0x10, '', 0x09CC15, fix=True) + create_sprite(0x51, EnemySprite.Faerie, 0x09, 0x10, '', 0x09CC18, fix=True) create_sprite(0x51, EnemySprite.Stal, 0x1C, 0x15, '', 0x09CC1B) create_sprite(0x51, EnemySprite.Moblin, 0x14, 0x16, '', 0x09CC1E) create_sprite(0x51, EnemySprite.Moblin, 0x0E, 0x17, '', 0x09CC21) @@ -118,7 +118,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x54, EnemySprite.BombRefill1, 0x05, 0x0B, '', 0x09CC48) create_sprite(0x54, EnemySprite.RedRupee, 0x19, 0x0B, '', 0x09CC4B) create_sprite(0x54, EnemySprite.Ropa, 0x07, 0x0F, '', 0x09CC4E) - create_sprite(0x54, EnemySprite.Faerie, 0x0F, 0x0E, '', 0x09CC51) + create_sprite(0x54, EnemySprite.Faerie, 0x0F, 0x0E, '', 0x09CC51, fix=True) create_sprite(0x54, EnemySprite.Ropa, 0x19, 0x10, '', 0x09CC54) create_sprite(0x54, EnemySprite.Ropa, 0x0D, 0x14, '', 0x09CC57) create_sprite(0x54, EnemySprite.Hinox, 0x11, 0x19, '', 0x09CC5A) @@ -174,7 +174,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x5b, EnemySprite.HeartPiece, 0x34, 0x12, '', 0x09CCE4) create_sprite(0x5b, EnemySprite.RupeePull, 0x13, 0x24, '', 0x09CCE7) create_sprite(0x5b, EnemySprite.Moblin, 0x0F, 0x27, '', 0x09CCEA) - create_sprite(0x5b, EnemySprite.Faerie, 0x17, 0x2A, '', 0x09CCED) + create_sprite(0x5b, EnemySprite.Faerie, 0x17, 0x2A, '', 0x09CCED, fix=True) create_sprite(0x5b, EnemySprite.Moblin, 0x0C, 0x2A, '', 0x09CCF0) create_sprite(0x5b, EnemySprite.Hinox, 0x1E, 0x2C, '', 0x09CCF3) create_sprite(0x5b, EnemySprite.Snapdragon, 0x34, 0x25, '', 0x09CCF6) @@ -208,7 +208,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x5e, EnemySprite.Moblin, 0x32, 0x24, '', 0x09CD46) create_sprite(0x5e, EnemySprite.Snapdragon, 0x35, 0x28, '', 0x09CD49) create_sprite(0x5e, EnemySprite.Ropa, 0x24, 0x30, '', 0x09CD4C) - create_sprite(0x5e, EnemySprite.Faerie, 0x30, 0x30, '', 0x09CD4F) + create_sprite(0x5e, EnemySprite.Faerie, 0x30, 0x30, '', 0x09CD4F, fix=True) create_sprite(0x5e, EnemySprite.Hinox, 0x35, 0x36, '', 0x09CD52) create_sprite(0x5e, EnemySprite.Raven, 0x29, 0x37, '', 0x09CD55, embed=True) # Screen62: @@ -431,7 +431,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x5, EnemySprite.Deadrock, 0x35, 0x0D, '', 0x09CFAF) create_sprite(0x5, EnemySprite.Tektite, 0x29, 0x0F, '', 0x09CFB2) create_sprite(0x5, EnemySprite.Deadrock, 0x35, 0x0F, '', 0x09CFB5) - create_sprite(0x5, EnemySprite.Faerie, 0x34, 0x10, '', 0x09CFB8) + create_sprite(0x5, EnemySprite.Faerie, 0x34, 0x10, '', 0x09CFB8, fix=True) create_sprite(0x5, EnemySprite.Tektite, 0x1E, 0x31, '', 0x09CFBB) create_sprite(0x5, EnemySprite.Tektite, 0x35, 0x2A, '', 0x09CFBE) create_sprite(0x5, EnemySprite.Deadrock, 0x2A, 0x2F, '', 0x09CFC1) @@ -490,7 +490,7 @@ def init_vanilla_sprites_ow(): # Screen15_1: create_sprite(0x15, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09D051) create_sprite(0x15, EnemySprite.BlueGuard, 0x16, 0x0E, '', 0x09D054) - create_sprite(0x15, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D057) + create_sprite(0x15, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D057, fix=True) create_sprite(0x15, EnemySprite.BlueGuard, 0x0B, 0x17, '', 0x09D05A) # Screen16_1: create_sprite(0x16, EnemySprite.Buzzblob, 0x0D, 0x0A, '', 0x09D05E) @@ -503,7 +503,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x17, EnemySprite.Buzzblob, 0x16, 0x0C, '', 0x09D071) create_sprite(0x17, EnemySprite.Buzzblob, 0x08, 0x16, '', 0x09D074) # Screen18_1: - create_sprite(0x18, EnemySprite.Faerie, 0x18, 0x0A, '', 0x09D078) + create_sprite(0x18, EnemySprite.Faerie, 0x18, 0x0A, '', 0x09D078, fix=True) create_sprite(0x18, EnemySprite.PositionTarget, 0x0C, 0x17, '', 0x09D07B) create_sprite(0x18, EnemySprite.BottleMerchant, 0x18, 0x16, '', 0x09D07E) create_sprite(0x18, EnemySprite.OldSnitch, 0x0E, 0x1C, '', 0x09D081) @@ -519,7 +519,7 @@ def init_vanilla_sprites_ow(): # Screen1A_1: create_sprite(0x1a, EnemySprite.BlueGuard, 0x14, 0x0C, '', 0x09D0A0) create_sprite(0x1a, EnemySprite.GreenGuard, 0x0C, 0x0E, '', 0x09D0A3) - create_sprite(0x1a, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D0A6) + create_sprite(0x1a, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D0A6, fix=True) create_sprite(0x1a, EnemySprite.BlueRupee, 0x17, 0x17, '', 0x09D0A9) # create_sprite(0x1a, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D0AC) create_sprite(0x1a, EnemySprite.RedSpearGuard, 0x0F, 0x18, '', 0x09D0AC) # was 0x09D0AF @@ -590,7 +590,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x2a, EnemySprite.GroveRabbit, 0x0C, 0x0F, '', 0x09D161) create_sprite(0x2a, EnemySprite.GroveRabbit, 0x11, 0x10, '', 0x09D164) # Screen2B_1: - create_sprite(0x2b, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D168) + create_sprite(0x2b, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D168, fix=True) create_sprite(0x2b, EnemySprite.GreenGuard, 0x14, 0x11, '', 0x09D16B) create_sprite(0x2b, EnemySprite.GreenGuard, 0x14, 0x15, '', 0x09D16E) create_sprite(0x2b, EnemySprite.GreenGuard, 0x10, 0x17, '', 0x09D171) @@ -638,7 +638,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x32, EnemySprite.SmallHeart, 0x1A, 0x09, '', 0x09D1E3) create_sprite(0x32, EnemySprite.BlueGuard, 0x0B, 0x0B, '', 0x09D1E6) create_sprite(0x32, EnemySprite.BlueGuard, 0x12, 0x0B, '', 0x09D1E9) - create_sprite(0x32, EnemySprite.Faerie, 0x19, 0x12, '', 0x09D1EC) + create_sprite(0x32, EnemySprite.Faerie, 0x19, 0x12, '', 0x09D1EC, fix=True) # Screen33_1: create_sprite(0x33, EnemySprite.GreenBushGuard, 0x15, 0x0B, '', 0x09D1F0) create_sprite(0x33, EnemySprite.BlueArcher, 0x09, 0x0E, '', 0x09D1F3) @@ -649,7 +649,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x34, EnemySprite.Toppo, 0x15, 0x11, '', 0x09D200) create_sprite(0x34, EnemySprite.GreenBushGuard, 0x11, 0x12, '', 0x09D203) create_sprite(0x34, EnemySprite.Raven, 0x08, 0x13, '', 0x09D206) - create_sprite(0x34, EnemySprite.Faerie, 0x0E, 0x13, '', 0x09D209) + create_sprite(0x34, EnemySprite.Faerie, 0x0E, 0x13, '', 0x09D209, fix=True) create_sprite(0x34, EnemySprite.GreenBushGuard, 0x15, 0x17, '', 0x09D20C) create_sprite(0x34, EnemySprite.BlueArcher, 0x0C, 0x18, '', 0x09D20F) # Screen35_1: @@ -753,7 +753,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x95, EnemySprite.Deadrock, 0x35, 0x0D, '', 0x09D324) create_sprite(0x95, EnemySprite.Tektite, 0x29, 0x0F, '', 0x09D327) create_sprite(0x95, EnemySprite.Deadrock, 0x35, 0x0F, '', 0x09D32A) - create_sprite(0x95, EnemySprite.Faerie, 0x34, 0x10, '', 0x09D32D) + create_sprite(0x95, EnemySprite.Faerie, 0x34, 0x10, '', 0x09D32D, fix=True) create_sprite(0x95, EnemySprite.Tektite, 0x1E, 0x31, '', 0x09D330) create_sprite(0x95, EnemySprite.Tektite, 0x35, 0x2A, '', 0x09D333) create_sprite(0x95, EnemySprite.Deadrock, 0x2A, 0x2F, '', 0x09D336) @@ -794,7 +794,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xa1, EnemySprite.BombRefill1, 0x08, 0x10, '', 0x09D395) create_sprite(0xa1, EnemySprite.Cucco, 0x08, 0x17, '', 0x09D398) # Screen12_2: - create_sprite(0xa2, EnemySprite.Faerie, 0x14, 0x0A, '', 0x09D39C) + create_sprite(0xa2, EnemySprite.Faerie, 0x14, 0x0A, '', 0x09D39C, fix=True) create_sprite(0xa2, EnemySprite.BlueGuard, 0x15, 0x0E, '', 0x09D39F) create_sprite(0xa2, EnemySprite.Whirlpool, 0x0F, 0x10, '', 0x09D3A2) create_sprite(0xa2, EnemySprite.GreenGuard, 0x15, 0x15, '', 0x09D3A5) @@ -813,7 +813,7 @@ def init_vanilla_sprites_ow(): # Screen15_2: create_sprite(0xa5, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09D3C9) create_sprite(0xa5, EnemySprite.UsainBolt, 0x16, 0x0E, '', 0x09D3CC) - create_sprite(0xa5, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D3CF) + create_sprite(0xa5, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D3CF, fix=True) create_sprite(0xa5, EnemySprite.RedSpearGuard, 0x0B, 0x17, '', 0x09D3D2) create_sprite(0xa5, EnemySprite.Apple, 0x04, 0x1A, '', 0x09D3D5) # Screen16_2: @@ -842,7 +842,7 @@ def init_vanilla_sprites_ow(): # Screen1A_2: 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.Faerie, 0x0D, 0x11, '', 0x09D41E, fix=True) # create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421) create_sprite(0xaa, EnemySprite.UsainBolt, 0x0F, 0x18, '', 0x09D421) # was 0x09D424 # Screen1B_2: @@ -881,7 +881,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xae, EnemySprite.ArmosStatue, 0x28, 0x29, '', 0x09D481) create_sprite(0xae, EnemySprite.ArmosStatue, 0x3A, 0x29, '', 0x09D484) create_sprite(0xae, EnemySprite.ArmosStatue, 0x3D, 0x29, '', 0x09D487) - create_sprite(0xae, EnemySprite.Faerie, 0x22, 0x37, '', 0x09D48A) + create_sprite(0xae, EnemySprite.Faerie, 0x22, 0x37, '', 0x09D48A, fix=True) create_sprite(0xae, EnemySprite.UsainBolt, 0x2D, 0x3A, '', 0x09D48D) # Screen22_2: create_sprite(0xb2, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D491, fix=True) # smithy smoke @@ -914,7 +914,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xba, EnemySprite.RedRupee, 0x0F, 0x18, '', 0x09D4DA) # Screen2B_2: create_sprite(0xbb, EnemySprite.BlueGuard, 0x08, 0x06, '', 0x09D4DE) - create_sprite(0xbb, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D4E1) + create_sprite(0xbb, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D4E1, fix=True) create_sprite(0xbb, EnemySprite.BlueGuard, 0x14, 0x11, '', 0x09D4E4) create_sprite(0xbb, EnemySprite.BlueGuard, 0x14, 0x15, '', 0x09D4E7) create_sprite(0xbb, EnemySprite.BlueGuard, 0x10, 0x17, '', 0x09D4EA) @@ -927,7 +927,7 @@ def init_vanilla_sprites_ow(): create_sprite(0xbd, EnemySprite.UsainBolt, 0x12, 0x16, '', 0x09D4FB) create_sprite(0xbd, EnemySprite.FireballZora, 0x1C, 0x17, '', 0x09D4FE, water=True) # Screen2E_2: - create_sprite(0xbe, EnemySprite.Faerie, 0x0C, 0x09, '', 0x09D502) + create_sprite(0xbe, EnemySprite.Faerie, 0x0C, 0x09, '', 0x09D502, fix=True) create_sprite(0xbe, EnemySprite.Bee, 0x14, 0x0B, '', 0x09D505) create_sprite(0xbe, EnemySprite.UsainBolt, 0x0E, 0x0C, '', 0x09D508) create_sprite(0xbe, EnemySprite.BlueGuard, 0x17, 0x0E, '', 0x09D50B) From 070f489be868c27bab6a1f7cd28c04dd4b830135 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 20 Nov 2023 16:53:24 -0700 Subject: [PATCH 054/158] fix: fix spectacle rock cave enemy drops --- EntranceShuffle.py | 6 ++++-- RELEASENOTES.md | 1 + Regions.py | 3 ++- source/dungeon/EnemyList.py | 6 +++--- source/overworld/EntranceShuffle2.py | 6 ++++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index a9907ce5..1ebe7e30 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2074,8 +2074,10 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Old Man Cave Dropdown', 'Old Man Cave (West)'), ('Old Man Cave W', 'Old Man Cave (West)'), ('Old Man Cave E', 'Old Man Cave (East)'), - ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), - ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave West Edge', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave East Edge', 'Spectacle Rock Cave Pool'), ('Old Man House Front to Back', 'Old Man House Back'), ('Old Man House Back to Front', 'Old Man House'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 870bc31d..c1e6ca47 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -143,6 +143,7 @@ These are now independent of retro mode and have three options: None, Random, an * 1.3.0.7v * Fix for Mimic Cave enemy drops + * Fix for Spectacle Rock Cave enemy drops (the mini-moldorms) * No longer shuffles fairy bonks (from trees) as part of Enemizer * 1.3.0.6v * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) diff --git a/Regions.py b/Regions.py index 688764af..e4f0cd41 100644 --- a/Regions.py +++ b/Regions.py @@ -128,7 +128,8 @@ def create_regions(world, player): create_cave_region(player, 'Death Mountain Return Cave (left)', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave E']), create_cave_region(player, 'Death Mountain Return Cave (right)', 'a connector', None, ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']), create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), - create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), + create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit', 'Spectacle Rock Cave East Edge']), + create_cave_region(player, 'Spectacle Rock Cave Pool', 'a connector', None, ['Spectacle Rock Cave West Edge']), create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']), create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']), diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index 5038fca1..21ca04b6 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -1969,9 +1969,9 @@ def init_vanilla_sprites(): create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x15, 0x0f, 'Spectacle Rock Cave (Bottom)') create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x11, 0x13, 'Spectacle Rock Cave (Bottom)') create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x17, 'Spectacle Rock Cave (Bottom)') - create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x17, 0x0e, 'Spectacle Rock Cave (Bottom)') - create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x18, 0x10, 'Spectacle Rock Cave (Bottom)') - create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x15, 0x11, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x17, 0x0e, 'Spectacle Rock Cave Pool') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x18, 0x10, 'Spectacle Rock Cave Pool') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x15, 0x11, 'Spectacle Rock Cave Pool') create_sprite(0x00fb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x0d, 'Bumper Cave (bottom)') create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0a, 'Bumper Cave (bottom)') create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12, 'Bumper Cave (bottom)') diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index c4fcc771..79dc89e5 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1877,8 +1877,10 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Old Man Cave Dropdown', 'Old Man Cave (East)'), ('Old Man Cave W', 'Old Man Cave (West)'), ('Old Man Cave E', 'Old Man Cave (East)'), - ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), - ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave West Edge', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave East Edge', 'Spectacle Rock Cave Pool'), ('Old Man House Front to Back', 'Old Man House Back'), ('Old Man House Back to Front', 'Old Man House'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), From e094d079058198793d63bc31919dbd60901f9efa Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 21 Nov 2023 09:46:43 -0700 Subject: [PATCH 055/158] fix: ban enemies around hera big chest --- source/enemizer/enemy_deny.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 4a4b0e90..a1fadca5 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -47,6 +47,7 @@ UwGeneralDeny: - [ 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, 4, ["Bumper", "BigSpike", "AntiFairyCircle"]] - [ 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" From 5c58782d0cabcb69915d602289aaa22be4f0b01b Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 22 Nov 2023 15:37:46 -0700 Subject: [PATCH 056/158] fix: multiworld lamp gfx again --- RELEASENOTES.md | 1 + Rom.py | 2 +- data/base2current.bps | Bin 117541 -> 117545 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c1e6ca47..2233e8f5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -144,6 +144,7 @@ These are now independent of retro mode and have three options: None, Random, an * 1.3.0.7v * Fix for Mimic Cave enemy drops * Fix for Spectacle Rock Cave enemy drops (the mini-moldorms) + * Fix for multiworld lamps with incorrect graphics * No longer shuffles fairy bonks (from trees) as part of Enemizer * 1.3.0.6v * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) diff --git a/Rom.py b/Rom.py index 7b018e13..98255a04 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '0f97b8f1f54afaef84e630e3bb407207' +RANDOMIZERBASEHASH = '5661a616546e7dc0ee4bdfa9b152bc68' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 802aa38ef04f42ff76cf6bf3328829e8e30873ed..f60adeb0902890bdb1b0ebe2e3767d323b37a1a7 100644 GIT binary patch delta 6559 zcmX9?30xD$_s`1(gj2as!*a{1cz~kdje-hl>xC8-3pU=jXkj-RAYcsP8CD38m2d^b z1foPMG`4CBtw-8wYpvF*^}?fAp|$*e*8lqZ@8`4gX6LD|Ks9_6UgU1g+K8nFJ4sG$yA1H&?$&57|D-! zT;9PLeaSImQbiFrP{N-HpP`Z88c<_q97rWcm0l!2e}V}nDXKw5tbSVG??B3V?YLL4 z8!qA>f>qFf^Mwmw2y58#0X`G{04B89Y=S@J>WuA}Q6xd;!43SkNUS9B9D;|=>N_!_r3_(B_&In1#A^m}GdMG`acM~71` zZ%#AMWyGF$Q%R0gAtk=VYDW#Y;RL4-ARiAq#faU$X3ndKv}4WveqX4GMI{aW{Y#`O z;-~MJ{#2QYcx<}m#PESO?RTvJSYmQ=&jGME?eNG2sK;MCe*_73dga4gQ?&PJ0K{cJ zGocY{eHO6w=RWfx2PgSPkJembj;V>ba>g1+FFVv-cDGl~Hl0^4QJhjTN6R9}%gQOo z)OsGV1XaG?HaA-tqk{MjJA5NOUzwN_RPhH5`)Y|z;vq(AN(?X}sD(Tknv&!T|XLA}k z{ggbAIG`)qNm0c|<=v)r{>cC%Q5O(nBWY%SR}dFXHv|3!_7{H=w?c?X8ki4uN`F#T z!yGQVhJv7_FdtV1ZH3=VSA)vH=T#^3J5>}+wiEkHy86pOLL4pWYEcjZ6YdCEH^TK4 z)9*)8ia3f=~ zyhsJ%tZc4PWR;*H?5t=1Nv4;QcQ?w3olomYUfSE*Wn>j`5DUY-p%|;e4?-^bN5l$+ zXPDcyf8(NvNo<260_|ibrayyJ5$7;s^byOOl*+3ZO355`{=a zr51KvVEX${Xd(hA1@ZSq=82jZG&23=$5dI4UpMs6l*+v>*I!o<$(>BoOh;Dc%irq1 z@@-HMIrr*s{^ z-c~w^RT0+7_(@e#BCdm3!e{@l-t;1B0e}UQV&Yg=*%Y&t^}JtWy5Sp>CUzAU#^XO@ zCV)T2jr|)!F+4sPqD}MTm++h}UuMpQ?2;ux@o_2fr^zs(+;+A6jAnkW;&7EqSh32v$%$7BzP!#z@MeIW+`=WWX zj8Dq=3S|nZNULJ983j>xsKHCBAgoxMOu$JzpFCk|Qx(&;b~}cXrX2?}iWNjKtE8MF z9avACbd>35JvIFQNjgiCNGdz-u!@*c$z&&#sh#oCBDUFfRJdqQtF=bX@@TOj-pP@80lqQS3mo@wq(_cET^}d+c z$ihc|f6n}z*~A-^&8mlXQ~2yl;ank$ft&rjbK4oWV)@Gp4Cf$Kn?S0H2)Dm#ZHwgV zFnG%yNHKA@js~K>P3KTpt6lyHgQJOJd3PIQD`jU&QpMertUQ{!^6f7@eH(YmIzu1=FHZ5cBbm?PFjJ2JV;+Ntm-E5uRcl zTSno{9g$qYDHQDd7xc*Am1!46hb?7h9+F3yR_|i1D8bae`wxIPtlhgG#-aDV*Kh$H z^ILp>y~qShDFvg*$|4(%{cmkTo6N`1Uy_c?rswugOLm)yFUgH>*Oy57nwR*o4z67Q-G?S46;9 zXsPgbQysM&cwG?jj`R7RqZ>TJYl}Q+Hyo%45Vjp1aO~cLZWsoaFbPM&B+SC`$@}+x zyNR^K(oAjtNXrbCjCQpwWyvS5770sQT`h(0d9D_G&vUahzvsDG&b{ZkS+1cKCxRGl zk8X(>wY*mzIVK9Z4V_-mM`UASObO$AwZ~~y^?gkpt%@aWCiWU8P3%1qO2v{pu<__L zD8R=@qXT0{UQt_!0i(>ZTBOH0K}S0N^sv0EVg!y=9L|#`nk6`1O~4dfre5Gaf#^L; zl2h)B4KmW$M;hY|h^}}}J$+P+rZ0z6&P!Kl`b6*Pb4?$-r)f1gB3c-XewAME498c7 zz)!fcQtCdkmN8^z6!yKd3W&fVnWUbRHt2$XR_=^6*4-D=?kj!}NRM61&M0Jc80#$1 zXJkeHSLO5>uVrKu4)D?&P(@FN7`#q@m>7Fc(Py>#YqRdrvGa=lv2yiu`$F0B6Ya)~ z!r_)L<8;t!iO@2w;_=%2VS2>g0nLHrH(#VHaNV&mH?xjGuOjNhHYs&RBc)cb^eomL zE1J4pTRPy7HfYo;kG+`rmrPQY*4D`M*;$2Rd4o8jzO9FJvf8H&euop}3Vb7z=-Tx% zEh|Jk9;_2P;M%Iuqt7#aTz2My<9$4q(8v4uR>-d0J+e;iGT_j|Flet(|K?IY*ki=5 zs$kfKzf{E(ZWli~5??I`nULO-a_LHrO`}hQ?SM!>B(v2sGCR^%|CUaec1i0?QhbzB zGJ5i|9%^>=xRm>f+4DGDddY`9>J(P}p5x7jsp4iY>0-)LTDQ*kp0-@)B&AXeJW6I) z-+YMjZg!#;Q;t;Svf!HL!#X~t6i~7i5w_JyV=gGi%a13TbpyP7a$J6OWa7aoq&TZV zd`?F^I#^WB6CY?0kHwB^Z;;}*)u~?dJ)fu*F_3;zr{(6{*XGSQq`5+k(Co)ZO#mCd z=V=naj0ZIv!43yByVr%q{BH8v{p)FVwQj8_Rj$b>tmiNs+IG?*J-bo@@xum^-^bN@ zN!4KGv0u2p;^zSI{X8i8B8MKs_{=XQ5kZR#DE_j38eZP~i?ujJ{x zChV(O3v)55cKqBmZym`2`(Zs-8Fjuu52VAep7Xvc9^0(v-byvF!?)&%pEhCCgIQz* zX|?&yVSp!|-_-kreX`w~yqf}>qMODvC1Pvsbhv>pYQwlZ4SLi?34Uq1({vB#)cHrG zc^=Y}wmJ}rh(n(#bd{U8Vq*xqW9Mad`hqfgw#^}`4TTk$iHf>;!UddXbg~rh)P<(6 zZ&8-W#px}&MO^YU=~NrXC2H@~r`}?5iryt4WvpW+{ijwLeZF8gPm-!xGu+qL%#b!L zn-JWs&y$E*1;=o2_BO_y&mQwpnEl&8zqk@7z@Ey}fI&sRd$wR~IK!X+RUPyFL6dgVh3u!_p2r4*Nr zdV&-CDWDa7TuCYmsk{M!_+l%`6PsJ}vUU4)W#_ef-%zRb1#Z_HYxAn&xaWvxcj~{F zjJkulBX9K-R&8~bq}mcWZA>{KzT4V7c`;TVA06#wM`GGbML92lL^!0}{P4J{Lh@e9 zXj8e)i&B1SPgVt!M&IaH;$kq|}rvYr41j?m3^nh``&1DM06YkFrw zf$Mdla1RgbLZA>kb%fv6u)pgnC`pA~aYw3wAdOZ&n}#rkAiS>%!~tD0gkW4l+=zq_ znJ#%;iNca=U>+AK3OC@kh8!+G8h>gK+x^$jHY_=DQ~NSA5(-_Rgr9T>d*xW?al;E^9?AJO@JgJb0NGF3AW;AU} zbPOaZ_XkpQ&XL-_cLO6@QVju`)T8^zoTpi}RmFzy|07fGe_bHy5j*vjSnb3Srapc- zOp=HraJVUh&GOTl#cZm$vDp{aV@Y!u8%K=IpFkw~ws^Bqe|$?kY{LyL)7|!+X0Gsf z)+9|}#NVoMdfU`CdLvp|RtV;fB$azHKp(?(oP!JWk&APtlPZ@l#fBthh59jXfUiu# z$V5^zE|I*HHdx@JWDCN+i;*a|ye_z<$swsNN@-b36`M3MD;O`FY?c#yhm|$d@PmzA zibmgovARIKtoOE0dUVB*(b~e|#}ypVdpcd|3LloF-onh&)KuJx?yXjV-DDDdu)p;~ zU;D0978Ay26w;eXdXMeTwT_0#?+2eu@5PmEqaEzK=%rK0%Gk7aWs*tN<_*DkLC;Yz z0ftW~V+0Hov>OBZf~|Y>t(q;#98%sX;|7r%@#+b0;@}0eBU$f}szO-)xLj!|#iyn6 zc+5NU@|ZIh8PbhiwgV=SbHBXrBJJg0t@Ti&z06?AN>tT_OC zjKQM2UrGA79P2dkU3_L7#dThX4jp1PV~g&XF(PQ}U+UCWON!J;$L&A4fx3rO$Hx$k zPdZXq{Ep}hW3e=|Gs`B`;~&bCcEwYjJKf*)N*X!h$=^sUDHjTewtuDC^*${)-<0H| zSspfo%fg0O10KTcu%XJrm6vK=SZsNRXljJXn0PAAaYWdV;u!f!V+DuQevG?MtrR6s zzo*DLF_$#A$70Hfret5nRlAb1t5>R<%DU*-sYBFNIso^du62yDGUXzx zt)V8M;93-hbj@R(W^0$X4bir7%s=0WvAoOQUuCOP40b^5h0{--W0tCKIkR ziPxFl>&(&{%%dC3V_f)|HypqXpUqj|zH#_JZX1Uwkv%EZ?3DhnOIkT0<)JURYgE-D za`BGGl;E`_WipkDAD-I1ZXf-YU_Nb6eWuJfN z&7KQ|-=7DO?VLNG3r2Ril6fZU5B|37-NVaY!XOMiZihe`Cf!bkt9anHFO0=ww^zZ3 z_}A@^`~(Y&Z{4-GDHWxgP===;Oci~3hS7uKDVJDfdi&sGVCR?fa0|HO^@p+IQEN(MF2fjzxF+FY zt)81oIGpR`>UF z3n${dXJHdJr<713+Nf@fAJ10UOfN(!Rc<~=2gj9Aq4cV5DpbqqL7NHe7t0J)O68%0 z1uV+8JX_^3C#gi~`e*f|xwkRYG~)SqaH{NOh`9$DVt{1Yb}YmtFMfokOHygHm!K5)ESER$UMe3&<}QX#9|F+-rPwR*U-?Yd0Y)d2w6I?`;PJh9Rx>5h zj>QjN&Spb+&C4+mYr6V!v6ExriqfjKqm8{@8#r_p#=MOXK}xBDsC-Cj7Ms?*-OP3O z9dkrwFR5khE6S?;4LUa1yIos_zrS1VvC!>E`+YI%(`w47*uj%j^26228S2Fl?;$$F zuqzy<4WE1DL22z~$|y@8&Wcdiz++M3+G54EO3Fz4(F%Y&w%h|hV#q^H5WSrX(eQD>qM)N8Cj@F-#WGv(k>x<7lA}WDrLm z7D5=<)2Dk9%6+YHqE@fin48t9pDpb z=6E|`uLKv;J47%G=9$lmU?fkFIZmM!o2`!E&jUO9r3?5$mf3y;+~C1<`kot1cIECl zLh&`y5K@>~sONHIeBw48>kg$r(4V!N=?u1%-U^LeFA_fHPpH zO|dEdS$JPuXLZcJFK!%R_0v}}KzN$Z#lZ`3{B(O!m31&rKZQ(THJu*I8Y&E$AH>5# zfVp)1c$fwm=3V1q*d8X+Tc?3Ptf!AmgKMyaUOgScVG>z>QP{(x{<5S&e zH1g~1NN&BTl;)eWKY;H6a_RV`tU)``+m=Fr+t#CuVHRnOcxMGo@CSd(27sPH>z2OX zV*2?~*lKTIP^}seid~Pj1<<+6pa=HSAS-7G?H7Sh#hgooF?d4?OUnK4i?w2U+e+4Xg3YRx@J1-Ig}=M< z{7wdu%gwkJmhxOCF4d}JTuZo7vmi^0dnVGu>mh~}kN6nY0c)j-T!?bL(8c6Q4sk5m zHU&!MRax>ay3l+%mz_KJ(@ff91KVLHJ!=DubuQdqqJC8IsG=M{~_5%i(0WAa_-fc7l1$SqSID$8>2A__6~x^+2`IDJ68M>!_NO a!kNh_(Ri2_T!d>v+YuT0w~k)xP5VEPR5rN) delta 6769 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 C%ATMA From 871a15393c619d4ff22fdd82b0e7bac54892ba78 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 22 Nov 2023 15:44:35 -0700 Subject: [PATCH 057/158] fix: couple more enemy bans for hera big --- source/enemizer/enemy_deny.yaml | 2 +- test/customizer/test.yaml | 52 ++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index a1fadca5..bcf49947 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -47,7 +47,7 @@ UwGeneralDeny: - [ 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, 4, ["Bumper", "BigSpike", "AntiFairyCircle"]] + - [ 0x0027, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "RollerVerticalDown", "RollerVerticalUp"]] - [ 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" diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml index 5c1136c0..5cef71d0 100644 --- a/test/customizer/test.yaml +++ b/test/customizer/test.yaml @@ -1,33 +1,43 @@ meta: - players: 1 - seed: 91 + players: 2 + seed: 400 settings: 1: # mode: standard - boss_shuffle: random +# boss_shuffle: random # dropshuffle: underworld # enemy_shuffle: shuffled - door_shuffle: crossed - intensity: 3 +# door_shuffle: crossed +# intensity: 3 + shuffle: lean - dungeon_counters: 'on' -doors: +# dungeon_counters: 'on' + 2: {} +entrances: 1: - lobbies: - Hyrule Castle South: GT Lobby S - doors: - GT Lobby Left Down Stairs: GT Quad Pot Up Stairs - GT Lobby Right Down Stairs: GT Lobby Up Stairs - GT Beam Dash ES: Swamp Big Key Ledge WN + entrances: + Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top) + two-way: + Hyrule Castle Entrance (South): Links House Exit + 2: {} +#doors: +# 1: +# lobbies: +# Hyrule Castle South: GT Lobby S +# doors: +# GT Lobby Left Down Stairs: GT Quad Pot Up Stairs +# GT Lobby Right Down Stairs: GT Lobby Up Stairs +# GT Beam Dash ES: Swamp Big Key Ledge WN -bosses: - 1: - Ganons Tower (middle): Trinexx +#bosses: +# 1: +# Ganons Tower (middle): Trinexx placements: 1: - Swamp Palace - Big Key Chest: Progressive Glove - Lumberjack Tree: Piece of Heart + Lumberjack Tree: Lamp#2 + Link's House: Lamp + 2: {} # keyshuffle: wild @@ -37,9 +47,9 @@ placements: # 'Hera Basement Cage Enemy #9': Small Key (Tower of Hera) # 'Hera Basement Cage Enemy #11': Fire Rod # Tower of Hera - Basement Cage: Small Key (Turtle Rock) -start_inventory: - 1: - - Pegasus Boots +#start_inventory: +# 1: +# - Pegasus Boots # - Progressive Sword # - Ocarina (Activated) # - Hookshot From f2a40b09414e7ad1eec79dac00ce121da384ecf9 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 27 Nov 2023 10:27:04 -0700 Subject: [PATCH 058/158] fix: typo --- Regions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Regions.py b/Regions.py index e4f0cd41..fc8bb98e 100644 --- a/Regions.py +++ b/Regions.py @@ -852,7 +852,7 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'GT Cannonball Bridge', 'Ganon\'s Tower', None, ['GT Cannonball Bridge WN', 'GT Cannonball Bridge Up Stairs', 'GT Cannonball Bridge SE']), create_dungeon_region(player, 'GT Refill', 'Ganon\'s Tower', None, ['GT Refill NE']), create_dungeon_region(player, 'GT Gauntlet 1', 'Ganon\'s Tower', None, ['GT Gauntlet 1 Down Stairs', 'GT Gauntlet 1 WN']), - create_dungeon_region(player, 'GT Gauntlet 2', 'Ganon\'s TowerA', None, ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW']), + create_dungeon_region(player, 'GT Gauntlet 2', 'Ganon\'s Tower', None, ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW']), create_dungeon_region(player, 'GT Gauntlet 3', 'Ganon\'s Tower', None, ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW']), create_dungeon_region(player, 'GT Gauntlet 4', 'Ganon\'s Tower', None, ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW']), create_dungeon_region(player, 'GT Gauntlet 5', 'Ganon\'s Tower', None, ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS']), From 93211567c2d68061864ab545ba71e189acedc7ee Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 27 Nov 2023 16:41:58 -0700 Subject: [PATCH 059/158] fix: customizer errors fix: poor enemy placement fix: insanity rng --- EntranceShuffle.py | 2 ++ Main.py | 9 +++++---- source/enemizer/enemy_deny.yaml | 1 + source/gui/bottom.py | 4 ++-- source/tools/MysteryUtils.py | 23 ++++++++++++++++++++--- test/customizer/test.yaml | 32 +++++++++++++++----------------- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 1ebe7e30..b95048c4 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -836,6 +836,7 @@ def link_entrances(world, player): random.shuffle(hole_targets) random.shuffle(exit_pool) + # fill up holes for hole in hole_entrances: connect_entrance(world, hole, hole_targets.pop(), player) @@ -851,6 +852,7 @@ def link_entrances(world, player): caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) if not invFlag: exit_pool.append('Hyrule Castle Entrance (South)') + random.shuffle(doors) # place links house if world.mode[player] == 'standard' or not world.shufflelinks[player]: diff --git a/Main.py b/Main.py index 98cac70e..853350fe 100644 --- a/Main.py +++ b/Main.py @@ -220,10 +220,11 @@ def main(args, seed=None, fish=None): if world.customizer and world.customizer.get_start_inventory(): for p, inv_list in world.customizer.get_start_inventory().items(): - for inv_item in inv_list: - item = ItemFactory(inv_item.strip(), p) - if item: - world.push_precollected(item) + if inv_list: + for inv_item in inv_list: + item = ItemFactory(inv_item.strip(), p) + if item: + world.push_precollected(item) if args.print_custom_yaml: world.settings.record_info(world) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index bcf49947..196a408f 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -167,6 +167,7 @@ UwGeneralDeny: - [ 0x0067, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Blue Bari 1" - [ 0x0067, 2, ["Bumper"]] #"Skull Woods - Firebar Pits - Blue Bari 2" - [ 0x0067, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 1" + - [ 0x0067, 4, [ "AntiFairyCircle", "Bumper" ]] - [ 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", "BunnyBeam" ] ] #"Skull Woods - Firebar Pits - Fire Bar (Clockwise)" diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 402e9286..3457f60a 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -70,11 +70,11 @@ def bottom_frame(self, parent, args=None): def generateRom(): guiargs = create_guiargs(parent) # get default values for missing parameters - for k,v in vars(parse_cli(['--multi', str(guiargs.multi)])).items(): + for k,v in vars(parse_cli(['--multi', str(guiargs.multi), '--customizer', str(guiargs.customizer)])).items(): if k not in vars(guiargs): setattr(guiargs, k, v) elif type(v) is dict: # use same settings for every player - setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)}) + setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, len(v) + 1)}) argsDump = vars(guiargs) needEnemizer = False diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 017144eb..eb6f0fee 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -43,6 +43,23 @@ def roll_settings(weights): return choice raise Exception("This fields needs to be true/false or off/on") + def get_choice_non_bool(option, root=weights): + choice = get_choice(option, root) + if choice is True or choice == 'on': + return 'on' + if choice is False or choice == 'off': + return 'off' + return choice + + def get_choice_yn(option, root=weights): + choice = get_choice(option, root) + if choice is True or choice == 'yes': + return 'yes' + if choice is False or choice == 'no': + return 'no' + return choice + + def get_choice_bool_default(option, root=weights, default=None): choice = get_choice_bool(option, root) if choice is None and default is not None: @@ -107,7 +124,7 @@ def roll_settings(weights): ret.experimental = get_choice_bool('experimental') ret.collection_rate = get_choice_bool('collection_rate') - ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default' + ret.dungeon_counters = get_choice_non_bool('dungeon_counters') if 'dungeon_counters' in weights else 'default' if ret.dungeon_counters == 'default': ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off' @@ -138,7 +155,7 @@ def roll_settings(weights): 'ganonhunt': 'ganonhunt', 'completionist': 'completionist' }[goal] - ret.openpyramid = get_choice('open_pyramid') if 'open_pyramid' in weights else 'auto' + ret.openpyramid = get_choice_yn('open_pyramid') if 'open_pyramid' in weights else 'auto' ret.crystals_gt = get_choice('tower_open') @@ -220,7 +237,7 @@ def roll_settings(weights): ret.reduce_flashing = get_choice_bool('reduce_flashing', romweights) ret.fastmenu = get_choice('menuspeed', romweights) ret.heartcolor = get_choice('heartcolor', romweights) - ret.heartbeep = get_choice('heartbeep', romweights) + ret.heartbeep = get_choice_non_bool('heartbeep', romweights) ret.ow_palettes = get_choice('ow_palettes', romweights) ret.uw_palettes = get_choice('uw_palettes', romweights) ret.shuffle_sfx = get_choice_bool('shuffle_sfx', romweights) diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml index 5cef71d0..d4d6ebe2 100644 --- a/test/customizer/test.yaml +++ b/test/customizer/test.yaml @@ -1,6 +1,5 @@ meta: - players: 2 - seed: 400 + seed: 398 settings: 1: # mode: standard @@ -10,17 +9,16 @@ settings: # enemy_shuffle: shuffled # door_shuffle: crossed # intensity: 3 - shuffle: lean + shuffle: insanity + experimental: on # dungeon_counters: 'on' - 2: {} -entrances: - 1: - entrances: - Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top) - two-way: - Hyrule Castle Entrance (South): Links House Exit - 2: {} +#entrances: +# 1: +# entrances: +# Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top) +# two-way: +# Hyrule Castle Entrance (South): Links House Exit #doors: # 1: # lobbies: @@ -33,14 +31,14 @@ entrances: #bosses: # 1: # Ganons Tower (middle): Trinexx -placements: - 1: - Lumberjack Tree: Lamp#2 - Link's House: Lamp - 2: {} -# keyshuffle: wild +#placements: +# 1: +# Lumberjack Tree: Lamp#2 +# Link's House: Lamp + + #placements: # 1: # 'Hera Basement Cage Enemy #4': Small Key (Palace of Darkness) From 792ba081a68ca5cce7302239d0b95f492ef51053 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 11 Dec 2023 15:15:26 -0700 Subject: [PATCH 060/158] fix: customizer errors --- source/gui/bottom.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 3457f60a..96d7880e 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -1,4 +1,4 @@ -from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, E, W, LEFT, RIGHT, X +from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, LEFT, RIGHT, X from argparse import Namespace import logging import os @@ -70,7 +70,10 @@ def bottom_frame(self, parent, args=None): def generateRom(): guiargs = create_guiargs(parent) # get default values for missing parameters - for k,v in vars(parse_cli(['--multi', str(guiargs.multi), '--customizer', str(guiargs.customizer)])).items(): + cliargs = ['--multi', str(guiargs.multi)] + if hasattr(guiargs, 'customizer'): + cliargs.extend(['--customizer', str(guiargs.customizer)]) + for k,v in vars(parse_cli(cliargs)).items(): if k not in vars(guiargs): setattr(guiargs, k, v) elif type(v) is dict: # use same settings for every player From d117bc4fda317af5f0f5e5d71ed3ae96ecb0afa1 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 13 Dec 2023 11:23:08 -0700 Subject: [PATCH 061/158] fix: several enemy bans --- source/enemizer/enemy_deny.yaml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 196a408f..67f3c673 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -75,6 +75,7 @@ UwGeneralDeny: - [ 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" - [ 0x003b, 1, [ "Bumper" ]] + - [ 0x003c, 0, ["BigSpike"]] - [ 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)" @@ -199,8 +200,10 @@ UwGeneralDeny: - [ 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" + - [ 0x007d, 4, ["StalfosKnight", "Geldman", "Blob", "Stal"]] + - [ 0x007d, 7, ["RollerVerticalUp", "RollerHorizontalLeft", "StalfosKnight", "Geldman", "Blob", "Stal"]] #"Ganon's Tower - The Zoo - Mini Helmasaur" + - [ 0x007d, 8, ["RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight", "StalfosKnight", "Geldman", "Blob", "Stal"]] #"Ganon's Tower - The Zoo - Red Bari" + - [ 0x007d, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] # todo - consider adding firesnake to 0-3: has a hard time moving, could block hookshots for quite a while - [ 0x007f, 0, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 1" - [ 0x007f, 1, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "ArmosStatue" ] ] #"Ice Palace - Big Spikes - Red Bari 2" @@ -216,9 +219,10 @@ UwGeneralDeny: - [ 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, 3, ["RollerHorizontalRight"]] - - [ 0x008b, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Map Room - Spike Trap" + - [ 0x008b, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike"]] #"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)" + - [ 0x008c, 14, ["AntiFairyCircle", "BigSpike", "Bumper"]] - [ 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" @@ -376,6 +380,8 @@ UwGeneralDeny: - [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [0x010b, 6, ["RollerHorizontalRight"]] + - [0x010c, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] + - [0x010c, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] OwGeneralDeny: - [0x03, 2, ["Gibo"]] # OldMan eating Gibo - [0x03, 4, ["Gibo"]] # OldMan eating Gibo @@ -384,6 +390,7 @@ OwGeneralDeny: - [0x03, 8, ["Gibo"]] # OldMan eating Gibo - [0x03, 9, ["Gibo"]] # OldMan eating Gibo - [0x03, 10, ["Gibo"]] # OldMan eating Gibo + - [0x05, 11, ["Bumper", "AntiFairyCircle"]] # Blocks path to portal - [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here - [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] @@ -421,16 +428,10 @@ UwEnemyDrop: # the following are behind rails or otherwise unactivate-able - [0x0077, 4, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # can't activate here - [0x0077, 5, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] - - [0x007D, 4, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - - [0x007D, 7, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - - [0x007D, 8, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - - [0x007D, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x008D, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x008D, 12, ["StalfosKnight", "Geldman", "Blob", "Stal"]] - [0x00b0, 7, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay - [0x00b0, 8, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay - - [0x010C, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] - - [0x010C, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # the following are not allowed at certain pits (or on conveyors near pits) # because they despawned or clipped away or immediately fell, etc - [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", From ffac17f330095fa108e322abba9037095e8a5aef Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 13 Dec 2023 11:42:35 -0700 Subject: [PATCH 062/158] fix: mimics are forbidden except in previous kill rooms --- source/enemizer/Enemizer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 242cbd8c..f4139fd0 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -369,6 +369,7 @@ def determine_forbidden(forbid, room_id, drop_flag=False): # forbidden_set.add(EnemySprite.AntiFairy) # can't drop anyway if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla rooms forbidden_set.add(EnemySprite.RedEyegoreMimic) + forbidden_set.add(EnemySprite.RedMimic) return forbidden_set From af03a8a2431c441a2edbb1e5531aa4a39dbe1ea8 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 13 Dec 2023 11:45:59 -0700 Subject: [PATCH 063/158] fix: don't bother blocking rain doors in no logic --- Rom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index 98255a04..eff87247 100644 --- a/Rom.py +++ b/Rom.py @@ -1160,10 +1160,11 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(0x1801A2, 0x00) # ped requirement is vanilla, set to 0x1 for special requirements # block HC upstairs doors in rain state in standard mode - prevent_rain = world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' + prevent_rain = world.mode[player] == 'standard' and world.shuffle[player] != 'vanilla' and world.logic[player] != 'nologic' rom.write_byte(0x18008A, 0x01 if prevent_rain else 0x00) # block sanc door in rain state and the dungeon is not vanilla - rom.write_byte(0x13f0fa, 0x01 if world.mode[player] == "standard" and world.doorShuffle[player] != 'vanilla' else 0x00) + block_sanc = world.mode[player] == 'standard' and world.doorShuffle[player] != 'vanilla' and world.logic[player] != 'nologic' + rom.write_byte(0x13f0fa, 0x01 if block_sanc else 0x00) if prevent_rain: portals = [world.get_portal('Hyrule Castle East', player), world.get_portal('Hyrule Castle West', player)] From c4ec28da76e13cdefd2bd7670ef5c794f272fc56 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 13 Dec 2023 13:59:34 -0700 Subject: [PATCH 064/158] fix: minor fix for take_anys fix: money balancing - initialization in a good case --- BaseClasses.py | 3 +++ Fill.py | 1 + ItemList.py | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 231cf89b..fcd4e6b7 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1467,6 +1467,9 @@ class Region(object): return self.dungeon and self.dungeon.is_dungeon_item(item) and item.player == self.player return True + def is_outdoors(self): + return self.type in {RegionType.LightWorld, RegionType.DarkWorld} + def __str__(self): return str(self.__unicode__()) diff --git a/Fill.py b/Fill.py index 2c5e4f01..a1d5d01a 100644 --- a/Fill.py +++ b/Fill.py @@ -969,6 +969,7 @@ def balance_money_progression(world): logger.debug(f'Money balancing needed: Player {target_player} short {difference}') else: difference = 0 + target_player = next(p for p in solvent) while difference > 0: swap_targets = [x for x in unchecked_locations if x not in sphere_locations and x.item.name.startswith('Rupees') and x.item.player == target_player] if len(swap_targets) == 0: diff --git a/ItemList.py b/ItemList.py index 2a558b9a..33d29233 100644 --- a/ItemList.py +++ b/ItemList.py @@ -518,7 +518,9 @@ def set_up_take_anys(world, player, skip_adjustments=False): world.dynamic_regions.append(take_any) target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) reg = regions.pop() - entrance = world.get_region(reg, player).entrances[0] + entrance = next((ent for ent in world.get_region(reg, player).entrances if ent.parent_region.is_outdoors()), None) + if entrance is None: + raise Exception(f'No outside entrance found for {reg}') connect_entrance(world, entrance, take_any, player) entrance.target = target take_any.shop = Shop(take_any, room_id, take_any_type, 0xE3, True, not world.shopsanity[player], 33 + num*2) From 0bd1f90bcaac90240625bdd1bbbc92fa3d304fa2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 14 Dec 2023 10:13:22 -0700 Subject: [PATCH 065/158] feat: MW progresssion balancing tweaked to be percentage based instead of raw count. Tries to keep each player's locations in each sphere within 80% of the player with the most locations available. (Measured with percentage instead of raw count.) Old algo tried to keep everyone within 20 locations of each other. Difficult if one player has a lot more locations than another. fix: Potential fix for early Trinexx start --- BaseClasses.py | 8 ++++---- Fill.py | 46 +++++++++++++++++++++++++++++++++--------- Main.py | 2 +- RELEASENOTES.md | 8 ++++++++ Rom.py | 2 +- data/base2current.bps | Bin 117545 -> 117546 bytes 6 files changed, 50 insertions(+), 16 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index fcd4e6b7..3f1371fa 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -512,7 +512,7 @@ class World(object): if not sphere: # ran out of places and did not finish yet, quit if log_error: - missing_locations = ", ".join([x.name for x in prog_locations]) + missing_locations = ", ".join([f'{x.name} (#{x.player})' for x in prog_locations]) logging.getLogger('').error(f'Cannot reach the following locations: {missing_locations}') return False @@ -547,7 +547,7 @@ class CollectionState(object): self.opened_doors = {player: set() for player in range(1, parent.players + 1)} self.dungeons_to_check = {player: defaultdict(dict) for player in range(1, parent.players + 1)} self.dungeon_limits = None - self.placing_item = None + self.placing_items = None # self.trace = None def update_reachable_regions(self, player): @@ -833,7 +833,7 @@ class CollectionState(object): return door_candidates door_candidates, skip = [], set() if (state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal' - and state.placing_item and state.placing_item.name == small_key_name): + and state.placing_items and any(i.name == small_key_name and i.player == player for i in state.placing_items)): key_logic = state.world.key_logic[player][dungeon_name] for door, paired in key_logic.sm_doors.items(): if door.name in key_logic.door_rules: @@ -878,7 +878,7 @@ class CollectionState(object): player: defaultdict(dict, {name: copy.copy(checklist) for name, checklist in self.dungeons_to_check[player].items()}) for player in range(1, self.world.players + 1)} - ret.placing_item = self.placing_item + ret.placing_items = self.placing_items return ret def apply_dungeon_exploration(self, rrp, player, dungeon_name, checklist): diff --git a/Fill.py b/Fill.py index a1d5d01a..d72c5b6c 100644 --- a/Fill.py +++ b/Fill.py @@ -3,6 +3,7 @@ import collections import itertools import logging import math +from collections import Counter from contextlib import suppress from BaseClasses import CollectionState, FillError, LocationType @@ -71,13 +72,13 @@ def fill_dungeons_restrictive(world, shuffled_locations): def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False, vanilla=False): - def sweep_from_pool(placing_item=None): + def sweep_from_pool(placing_items=None): new_state = base_state.copy() for item in itempool: new_state.collect(item, True) - new_state.placing_item = placing_item + new_state.placing_items = placing_items new_state.sweep_for_events() - new_state.placing_item = None + new_state.placing_items = None return new_state unplaced_items = [] @@ -94,7 +95,7 @@ def fill_restrictive(world, base_state, locations, itempool, key_pool=None, sing while any(player_items.values()) and locations: items_to_place = [[itempool.remove(items[-1]), items.pop()][-1] for items in player_items.values() if items] - maximum_exploration_state = sweep_from_pool(placing_item=items_to_place[0]) + maximum_exploration_state = sweep_from_pool(placing_items=items_to_place) has_beaten_game = world.has_beaten_game(maximum_exploration_state) for item_to_place in items_to_place: @@ -703,24 +704,44 @@ def balance_multiworld_progression(world): checked_locations = set() unchecked_locations = set(world.get_locations()) + total_locations_count = Counter(location.player for location in world.get_locations() if not location.locked and not location.forced_item) + reachable_locations_count = {} for player in range(1, world.players + 1): reachable_locations_count[player] = 0 + sphere_num = 1 + moved_item_count = 0 def get_sphere_locations(sphere_state, locations): sphere_state.sweep_for_events(key_only=True, locations=locations) return {loc for loc in locations if sphere_state.can_reach(loc) and sphere_state.not_flooding_a_key(sphere_state.world, loc)} + def item_percentage(player, num): + return num / total_locations_count[player] + while True: sphere_locations = get_sphere_locations(state, unchecked_locations) for location in sphere_locations: unchecked_locations.remove(location) - reachable_locations_count[location.player] += 1 + if not location.locked and not location.forced_item: + reachable_locations_count[location.player] += 1 + + logging.debug(f'Sphere {sphere_num}') + logging.debug(f'Reachable locations: {reachable_locations_count}') + debug_percentages = { + player: round(item_percentage(player, num), 2) + for player, num in reachable_locations_count.items() + } + logging.debug(f'Reachable percentages: {debug_percentages}\n') + sphere_num += 1 if checked_locations: - threshold = max(reachable_locations_count.values()) - 20 + max_percentage = max(map(lambda p: item_percentage(p, reachable_locations_count[p]), reachable_locations_count)) + threshold_percentages = {player: max_percentage * .8 for player in range(1, world.players + 1)} + logging.debug(f'Thresholds: {threshold_percentages}') - balancing_players = {player for player, reachables in reachable_locations_count.items() if reachables < threshold} + balancing_players = {player for player, reachables in reachable_locations_count.items() + if item_percentage(player, reachables) < threshold_percentages[player]} if balancing_players: balancing_state = state.copy() balancing_unchecked_locations = unchecked_locations.copy() @@ -738,7 +759,8 @@ def balance_multiworld_progression(world): for location in balancing_sphere: balancing_unchecked_locations.remove(location) balancing_reachables[location.player] += 1 - if world.has_beaten_game(balancing_state) or all(reachables >= threshold for reachables in balancing_reachables.values()): + if world.has_beaten_game(balancing_state) or all(item_percentage(player, reachables) >= threshold_percentages[player] + for player, reachables in balancing_reachables.items()): break elif not balancing_sphere: raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') @@ -765,7 +787,8 @@ def balance_multiworld_progression(world): items_to_replace.append(testing) else: reduced_sphere = get_sphere_locations(reducing_state, locations_to_test) - if reachable_locations_count[player] + len(reduced_sphere) < threshold: + p = item_percentage(player, reachable_locations_count[player] + len(reduced_sphere)) + if p < threshold_percentages[player]: items_to_replace.append(testing) replaced_items = False @@ -790,6 +813,7 @@ def balance_multiworld_progression(world): new_location.event, old_location.event = True, False logging.debug(f"Progression balancing moved {new_location.item} to {new_location}, " f"displacing {old_location.item} into {old_location}") + moved_item_count += 1 state.collect(new_location.item, True, new_location) replaced_items = True break @@ -797,6 +821,7 @@ def balance_multiworld_progression(world): logging.warning(f"Could not Progression Balance {old_location.item}") if replaced_items: + logging.debug(f'Moved {moved_item_count} items so far\n') unlocked = {fresh for player in balancing_players for fresh in unlocked_locations[player]} for location in get_sphere_locations(state, unlocked): unchecked_locations.remove(location) @@ -811,7 +836,8 @@ def balance_multiworld_progression(world): if world.has_beaten_game(state): break elif not sphere_locations: - raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') + logging.warning('Progression Balancing ran out of paths.') + break def check_shop_swap(l): diff --git a/Main.py b/Main.py index 853350fe..ea1bfc62 100644 --- a/Main.py +++ b/Main.py @@ -37,7 +37,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.7' +version_number = '1.3.0.8' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2233e8f5..bdd4d99b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,14 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.8v + * Enemizer: Red Mimics correctly banned from challenge rooms in appropriate logic setting + * No Logic Standard ER: Rain doors aren't blocked if no logic is enabled. + * Trinexx: attempt to fix early start + * MW Progression Balancing: Change to be percentage based instead of raw count. (80% threshold) + * Take anys: Good Bee cave chosen as take any should no longer prevent generation + * Money balancing: Fixed generation issue + * Enemizer: various enemy bans * 1.3.0.7v * Fix for Mimic Cave enemy drops * Fix for Spectacle Rock Cave enemy drops (the mini-moldorms) diff --git a/Rom.py b/Rom.py index eff87247..7c7217b2 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '5661a616546e7dc0ee4bdfa9b152bc68' +RANDOMIZERBASEHASH = '4d1f3e36e316077823a3e2eb5359ca17' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index f60adeb0902890bdb1b0ebe2e3767d323b37a1a7..bce1390a3bf9807351fb15408bc4ff3547866f09 100644 GIT binary patch delta 44 zcmV+{0Mq}emItbq2cQE4`BDb0vjhXoYzL7d)Q7g}RD;%Ux7Kd~OIrx&vhJ{?jjQvt C?iHv2 delta 43 zcmV+`0M!4gmItYp2cQE4)>A30vjhXoYzB=Y)Q7fMgV%4j*KYw!TL=+QdfJrF$V-hg B65{{> From 126e055cec5442ebd4b6402e606866cf860961ca Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 18 Dec 2023 14:10:14 -0700 Subject: [PATCH 066/158] fix: accept shufflebosses and shuffleenemies --- source/classes/CustomSettings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index c72a111c..14084f0b 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -134,8 +134,8 @@ class CustomSettings(object): args.mapshuffle[p] = True args.compassshuffle[p] = True - args.shufflebosses[p] = get_setting(settings['boss_shuffle'], args.shufflebosses[p]) - args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], args.shuffleenemies[p]) + args.shufflebosses[p] = get_setting(settings['boss_shuffle'], get_setting(settings['shufflebosses'], args.shufflebosses[p])) + args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], get_setting(settings['shuffleenemies'], args.shuffleenemies[p])) args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p]) args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p]) args.any_enemy_logic[p] = get_setting(settings['any_enemy_logic'], args.any_enemy_logic[p]) @@ -270,8 +270,8 @@ class CustomSettings(object): settings_dict[p]['keyshuffle'] = world.keyshuffle[p] settings_dict[p]['mapshuffle'] = world.mapshuffle[p] settings_dict[p]['compassshuffle'] = world.compassshuffle[p] - settings_dict[p]['shufflebosses'] = world.boss_shuffle[p] - settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p] + settings_dict[p]['boss_shuffle'] = world.boss_shuffle[p] + settings_dict[p]['enemy_shuffle'] = world.enemy_shuffle[p] settings_dict[p]['enemy_health'] = world.enemy_health[p] settings_dict[p]['enemy_damage'] = world.enemy_damage[p] settings_dict[p]['any_enemy_logic'] = world.any_enemy_logic[p] From 44d33ea629b9c2885068f0e6a43fcca5a72bfca7 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 18 Dec 2023 14:10:30 -0700 Subject: [PATCH 067/158] fix: ganonhunt playthrough --- Rules.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index e6ebed97..f98523a8 100644 --- a/Rules.py +++ b/Rules.py @@ -752,8 +752,11 @@ def global_rules(world, player): add_mc_rule('Agahnim 2') add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player) + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + if world.goal[player] != 'ganonhunt': + add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player)) + set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop From 7619eb480d0beb9a4aae1fb57f4580bb91450180 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 18 Dec 2023 16:05:23 -0700 Subject: [PATCH 068/158] fix: small issue with lite (TR) using all must exits --- source/overworld/EntranceShuffle2.py | 33 +++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 79dc89e5..53635e79 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -549,19 +549,29 @@ def figure_out_must_exits_same_world(entrances, exits, avail): if hyrule_forced: remove_from_list(multi_exit_caves, hyrule_forced) - must_exit_lw, must_exit_dw = must_exits_helper(avail, lw_entrances, dw_entrances) + must_exit_lw, must_exit_dw, unfiltered_lw, unfiltered_dw = must_exits_helper(avail, lw_entrances, dw_entrances) return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves, hyrule_forced def must_exits_helper(avail, lw_entrances, dw_entrances): - must_exit_lw = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy() - must_exit_dw = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy() + must_exit_lw_orig = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy() + must_exit_dw_orig = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy() if not avail.inverted and not avail.skull_handled: - must_exit_dw.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section')) - must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances) - must_exit_dw = must_exit_filter(avail, must_exit_dw, dw_entrances) - return must_exit_lw, must_exit_dw + must_exit_dw_orig.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section')) + must_exit_lw = must_exit_filter(avail, must_exit_lw_orig, lw_entrances) + must_exit_dw = must_exit_filter(avail, must_exit_dw_orig, dw_entrances) + return must_exit_lw, must_exit_dw, flatten(must_exit_lw_orig), flatten(must_exit_dw_orig) + + +def flatten(list_to_flatten): + ret = [] + for item in list_to_flatten: + if isinstance(item, tuple): + ret.extend(item) + else: + ret.append(item) + return ret def figure_out_must_exits_cross_world(entrances, exits, avail): @@ -766,18 +776,25 @@ def do_limited_shuffle(pool_def, avail): def do_limited_shuffle_exclude_drops(pool_def, avail, lw=True): ignored_entrances, exits = find_entrances_and_exits(avail, pool_def['entrances']) reserved_drops = set(linked_drop_map.values()) - must_exit_lw, must_exit_dw = must_exits_helper(avail, LW_Entrances, DW_Entrances) + must_exit_lw, must_exit_dw, unfiltered_lw, unfiltered_dw = must_exits_helper(avail, LW_Entrances, DW_Entrances) must_exit = set(must_exit_lw if lw else must_exit_dw) + unfiltered = set(unfiltered_lw if lw else unfiltered_dw) base_set = LW_Entrances if lw else DW_Entrances entrance_pool = [x for x in base_set if x in avail.entrances and x not in reserved_drops] random.shuffle(entrance_pool) + all_connectors = {c: tuple(connector) for connector in Connector_List for c in connector} + multi_tracker = {tuple(connector): False for connector in Connector_List} # ensures multi_entrance for next_exit in exits: if next_exit not in Connector_Exit_Set: reduced_pool = [x for x in entrance_pool if x not in must_exit] + if next_exit in all_connectors and not multi_tracker[all_connectors[next_exit]]: + reduced_pool = [x for x in entrance_pool if x not in unfiltered] chosen_entrance = reduced_pool.pop() entrance_pool.remove(chosen_entrance) else: chosen_entrance = entrance_pool.pop() + if next_exit in all_connectors and chosen_entrance not in must_exit: + multi_tracker[all_connectors[next_exit]] = True connect_two_way(chosen_entrance, next_exit, avail) From 78713a633ed483da7d726808b998914d5478ca02 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 18 Dec 2023 16:05:57 -0700 Subject: [PATCH 069/158] fix: different door types with TR in door shuffle modes --- Rom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index 7c7217b2..8fafcc98 100644 --- a/Rom.py +++ b/Rom.py @@ -1381,8 +1381,9 @@ def patch_rom(world, rom, player, team, is_mystery=False): world.get_room(0x23, player).change(0, DoorKind.CaveEntrance) if world.get_door('TR Eye Bridge SW', player).entranceFlag: world.get_room(0xd5, player).change(0, DoorKind.CaveEntrance) - # do this unconditionally - gets overwritten by RoomData in doorShufflemodes - rom.initial_sram.pre_open_tr_bomb_doors() # preopen bombable exits + # do this conditionally - don't mess with doors + if world.doorShuffle[player] == 'vanilla': + rom.initial_sram.pre_open_tr_bomb_doors() # preopen bombable exits if world.boss_shuffle[player] != 'none' or world.doorShuffle[player] != 'vanilla': rom.write_byte(snes_to_pc(0x30835A), 1) # fix Prize On The Eyes From 4b888b3c4807d02091238984b1b2f57133c32e1e Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 19 Dec 2023 16:31:29 -0700 Subject: [PATCH 070/158] feat: swapped ER --- BaseClasses.py | 4 +- Doors.py | 2 +- DungeonGenerator.py | 2 +- ItemList.py | 2 +- Main.py | 2 +- Rom.py | 10 +- TestSuite.py | 3 + mystery_example.yml | 3 + mystery_testsuite.yml | 3 + resources/app/cli/args.json | 1 + resources/app/cli/lang/en.json | 1 + resources/app/gui/lang/en.json | 1 + .../app/gui/randomize/entrando/widgets.json | 1 + source/overworld/EntranceShuffle2.py | 316 +++++++++++++++--- test/customizer/test.yaml | 32 +- 15 files changed, 316 insertions(+), 67 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 3f1371fa..242161e6 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -112,7 +112,7 @@ class World(object): set_player_attr('can_access_trock_big_chest', None) set_player_attr('can_access_trock_middle', None) set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic'] - or shuffle[player] in ['lean', 'crossed', 'insanity']) + or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity']) set_player_attr('mapshuffle', False) set_player_attr('compassshuffle', False) set_player_attr('keyshuffle', 'none') @@ -2970,7 +2970,7 @@ class Pot(object): # byte 0: DDDE EEEE (DR, ER) dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4} er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8, - 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6} + 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, 'swapped': 10} # byte 1: LLLW WSS? (logic, mode, sword) logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4} diff --git a/Doors.py b/Doors.py index 651f9bd4..ad83b67f 100644 --- a/Doors.py +++ b/Doors.py @@ -1506,7 +1506,7 @@ def create_doors(world, player): # static portal flags world.get_door('Sanctuary S', player).dead_end(allowPassage=True) - if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']: + if world.mode[player] == 'open' and world.shuffle[player] not in ['lean', 'swapped', 'crossed', 'insanity']: world.get_door('Sanctuary S', player).lw_restricted = True world.get_door('Eastern Hint Tile Blocked Path SE', player).passage = False world.get_door('TR Big Chest Entrance SE', player).passage = False diff --git a/DungeonGenerator.py b/DungeonGenerator.py index d96b654a..ed0a7b78 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -1360,7 +1360,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge for name, builder in dungeon_map.items(): calc_allowance_and_dead_ends(builder, connections_tuple, world, player) - if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']: + if world.mode[player] == 'open' and world.shuffle[player] not in ['lean', 'swapped', 'crossed', 'insanity']: sanc = find_sector('Sanctuary', candidate_sectors) if sanc: # only run if sanc if a candidate lw_builders = [] diff --git a/ItemList.py b/ItemList.py index 33d29233..03e89419 100644 --- a/ItemList.py +++ b/ItemList.py @@ -809,7 +809,7 @@ def balance_prices(world, player): def check_hints(world, player): - if world.shuffle[player] in ['simple', 'restricted', 'full', 'crossed', 'insanity']: + if world.shuffle[player] in ['simple', 'restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity']: for shop, location_list in shop_to_location_table.items(): if shop in ['Capacity Upgrade', 'Paradox Shop', 'Potion Shop']: continue # near the queen, near potions, and near 7 chests are fine diff --git a/Main.py b/Main.py index ea1bfc62..ac68d48c 100644 --- a/Main.py +++ b/Main.py @@ -241,7 +241,7 @@ def main(args, seed=None, fish=None): for player in range(1, world.players + 1): create_dynamic_exits(world, player) - if world.experimental[player] or world.shuffle[player] in ['lite', 'lean'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()): + if world.experimental[player] or world.shuffle[player] in ['lite', 'lean', 'swapped'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()): link_entrances_new(world, player) else: link_entrances(world, player) diff --git a/Rom.py b/Rom.py index 8fafcc98..1f533b5d 100644 --- a/Rom.py +++ b/Rom.py @@ -1358,7 +1358,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required[player] else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F]) # allow smith into multi-entrance caves in appropriate shuffles - if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'): + if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'): rom.write_byte(0x18004C, 0x01) # set correct flag for hera basement item @@ -1885,7 +1885,7 @@ def write_strings(rom, world, player, team): break # Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones. entrances_to_hint.update(InconvenientOtherEntrances) - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: + if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean', 'swapped']: hint_count = 0 elif world.shuffle[player] in ['simple', 'restricted']: hint_count = 2 @@ -1935,7 +1935,7 @@ def write_strings(rom, world, player, team): entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'}) else: entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'}) - hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 0 + hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 0 hint_count -= 2 if world.shuffle[player] not in ['simple', 'restricted'] else 0 for entrance in all_entrances: if entrance.name in entrances_to_hint: @@ -1954,7 +1954,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: locations_to_hint.extend(InconvenientVanillaLocations) random.shuffle(locations_to_hint) - hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 5 + hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 5 hint_count -= 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0 del locations_to_hint[hint_count:] for location in locations_to_hint: @@ -2017,7 +2017,7 @@ def write_strings(rom, world, player, team): if world.bigkeyshuffle[player]: items_to_hint.extend(BigKeys) random.shuffle(items_to_hint) - hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8 + hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 8 hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0 while hint_count > 0 and len(items_to_hint) > 0: this_item = items_to_hint.pop(0) diff --git a/TestSuite.py b/TestSuite.py index ede5ae53..d8a1f3e1 100644 --- a/TestSuite.py +++ b/TestSuite.py @@ -49,6 +49,9 @@ def main(args=None): test("Shopsanity", "--shuffle vanilla --shopsanity") test("Simple ", "--shuffle simple") test("Full ", "--shuffle full") + test("Lite ", "--shuffle lite") + test("Lean ", "--shuffle lean") + test("Swapped ", "--shuffle swapped") test("Crossed ", "--shuffle crossed") test("Insanity ", "--shuffle insanity") test("OWG ", "--logic owglitches") diff --git a/mystery_example.yml b/mystery_example.yml index 47cbb2b2..b7ba403e 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -72,6 +72,9 @@ simple: 2 restricted: 2 full: 2 + lite: 2 + lean: 2 + swapped: 2 crossed: 3 insanity: 1 open_pyramid: diff --git a/mystery_testsuite.yml b/mystery_testsuite.yml index 7dc9bb61..d3a83234 100644 --- a/mystery_testsuite.yml +++ b/mystery_testsuite.yml @@ -60,6 +60,9 @@ entrance_shuffle: simple: 1 restricted: 1 full: 1 + lite: 1 + lean: 1 + swapped: 1 crossed: 1 insanity: 1 shufflelinks: diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 42934aba..1d7dbcb2 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -167,6 +167,7 @@ "simple", "restricted", "full", + "swapped", "crossed", "insanity", "dungeonsfull", diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 569f523e..024f16e0 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -206,6 +206,7 @@ " connect remaining entrances.", "Crossed: Mix cave and dungeon entrances freely while allowing", " caves to cross between worlds.", + "Swapped: Same as Crossed, but entrances switch places in pairs.", "Insanity: Decouple entrances and exits from each other and", " shuffle them freely. Caves that used to be single", " entrance will still exit to the same location from", diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 8b127bf1..bd8e755b 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -156,6 +156,7 @@ "randomizer.entrance.entranceshuffle.simple": "Simple", "randomizer.entrance.entranceshuffle.restricted": "Restricted", "randomizer.entrance.entranceshuffle.full": "Full", + "randomizer.entrance.entranceshuffle.swapped": "Swapped", "randomizer.entrance.entranceshuffle.crossed": "Crossed", "randomizer.entrance.entranceshuffle.insanity": "Insanity", "randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full", diff --git a/resources/app/gui/randomize/entrando/widgets.json b/resources/app/gui/randomize/entrando/widgets.json index a3104bf1..c325ea26 100644 --- a/resources/app/gui/randomize/entrando/widgets.json +++ b/resources/app/gui/randomize/entrando/widgets.json @@ -9,6 +9,7 @@ "full", "lite", "lean", + "swapped", "crossed", "insanity", "dungeonsfull", diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 53635e79..e6fe631d 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -11,6 +11,7 @@ class EntrancePool(object): self.exits = set() self.inverted = False self.coupled = True + self.swapped = False self.default_map = {} self.one_way_map = {} self.skull_handled = False @@ -92,6 +93,7 @@ def link_entrances_new(world, player): if mode not in modes: raise RuntimeError(f'Shuffle mode {mode} is not yet supported') mode_cfg = copy.deepcopy(modes[mode]) + avail_pool.swapped = mode_cfg['undefined'] == 'swap' if avail_pool.is_standard(): do_standard_connections(avail_pool) pool_list = mode_cfg['pools'] if 'pools' in mode_cfg else {} @@ -99,7 +101,10 @@ def link_entrances_new(world, player): special_shuffle = pool['special'] if 'special' in pool else None if special_shuffle == 'drops': holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances']) - connect_random(holes, targets, avail_pool) + if avail_pool.swapped: + connect_swapped(holes, targets, avail_pool) + else: + connect_random(holes, targets, avail_pool) elif special_shuffle == 'fixed_shuffle': do_fixed_shuffle(avail_pool, pool['entrances']) elif special_shuffle == 'same_world': @@ -123,7 +128,10 @@ def link_entrances_new(world, player): do_vanilla_connect(pool, avail_pool) elif special_shuffle == 'skull': entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances']) - connect_random(entrances, exits, avail_pool, True) + if avail_pool.swapped: + connect_swapped(entrances, exits, avail_pool, True) + else: + connect_random(entrances, exits, avail_pool, True) avail_pool.skull_handled = True else: entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances']) @@ -131,7 +139,7 @@ def link_entrances_new(world, player): undefined_behavior = mode_cfg['undefined'] if undefined_behavior == 'vanilla': do_vanilla_connections(avail_pool) - elif undefined_behavior == 'shuffle': + elif undefined_behavior in {'shuffle', 'swap'}: do_main_shuffle(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, mode_cfg) # afterward @@ -186,6 +194,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def): if not avail.coupled: avail.decoupled_entrances.remove('Agahnims Tower') avail.decoupled_exits.remove('Ganons Tower Exit') + if avail.swapped: + connect_swap('Agahnims Tower', 'Ganons Tower Exit', avail) + entrances.remove('Ganons Tower') + exits.remove('Agahnims Tower Exit') elif 'Ganons Tower' in entrances: connect_two_way('Ganons Tower', 'Ganons Tower Exit', avail) entrances.remove('Ganons Tower') @@ -207,7 +219,13 @@ def do_main_shuffle(entrances, exits, avail, mode_def): # inverted sanc if avail.inverted and 'Dark Sanctuary Hint' in exits: - choices = [e for e in Inverted_Dark_Sanctuary_Doors if e in entrances] + forbidden = set() + if avail.swapped: + forbidden.add('Dark Sanctuary Hint') + forbidden.update(Forbidden_Swap_Entrances) + if not avail.inverted: + forbidden.append('Links House') + choices = [e for e in Inverted_Dark_Sanctuary_Doors if e in entrances and e not in forbidden] choice = random.choice(choices) entrances.remove(choice) exits.remove('Dark Sanctuary Hint') @@ -216,6 +234,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def): ext.connect(avail.world.get_entrance(choice, avail.player).parent_region) if not avail.coupled: avail.decoupled_entrances.remove(choice) + if avail.swapped and choice != 'Dark Sanctuary Hint': + swap_ent, swap_ext = connect_swap(choice, 'Dark Sanctuary Hint', avail) + entrances.remove(swap_ent) + exits.remove(swap_ext) # mandatory exits rem_entrances, rem_exits = set(), set() @@ -241,12 +263,19 @@ def do_main_shuffle(entrances, exits, avail, mode_def): else: # cross world mandantory entrance_list = list(entrances) + if avail.swapped: + forbidden = [e for e in Forbidden_Swap_Entrances if e in entrance_list] + entrance_list = [e for e in entrance_list if e not in forbidden] must_exit, multi_exit_caves = figure_out_must_exits_cross_world(entrances, exits, avail) do_mandatory_connections(avail, entrance_list, multi_exit_caves, must_exit) rem_entrances.update(entrance_list) + if avail.swapped: + rem_entrances.update(forbidden) rem_exits.update([x for item in multi_exit_caves for x in item]) rem_exits.update(exits) + if avail.swapped: + rem_exits = [x for x in rem_exits if x in avail.exits] # old man cave do_old_man_cave_exit(rem_entrances, rem_exits, avail, cross_world) @@ -254,9 +283,15 @@ def do_main_shuffle(entrances, exits, avail, mode_def): # blacksmith if 'Blacksmiths Hut' in rem_exits: blacksmith_options = [x for x in Blacksmith_Options if x in rem_entrances] + if avail.swapped: + blacksmith_options = [e for e in blacksmith_options if e not in Forbidden_Swap_Entrances] blacksmith_choice = random.choice(blacksmith_options) connect_entrance(blacksmith_choice, 'Blacksmiths Hut', avail) rem_entrances.remove(blacksmith_choice) + if avail.swapped and blacksmith_choice != 'Blacksmiths Hut': + swap_ent, swap_ext = connect_swap(blacksmith_choice, 'Blacksmiths Hut', avail) + rem_entrances.remove(swap_ent) + rem_exits.remove(swap_ext) if not avail.coupled: avail.decoupled_exits.remove('Blacksmiths Hut') rem_exits.remove('Blacksmiths Hut') @@ -266,9 +301,15 @@ def do_main_shuffle(entrances, exits, avail, mode_def): if bomb_shop in rem_exits: bomb_shop_options = Inverted_Bomb_Shop_Options if avail.inverted else Bomb_Shop_Options bomb_shop_options = [x for x in bomb_shop_options if x in rem_entrances] + if avail.swapped and len(bomb_shop_options) > 1: + bomb_shop_options = [x for x in bomb_shop_options if x != 'Big Bomb Shop'] bomb_shop_choice = random.choice(bomb_shop_options) connect_entrance(bomb_shop_choice, bomb_shop, avail) rem_entrances.remove(bomb_shop_choice) + if avail.swapped and bomb_shop_choice != 'Big Bomb Shop': + swap_ent, swap_ext = connect_swap(bomb_shop_choice, bomb_shop, avail) + rem_exits.remove(swap_ext) + rem_entrances.remove(swap_ent) if not avail.coupled: avail.decoupled_exits.remove(bomb_shop) rem_exits.remove(bomb_shop) @@ -326,12 +367,17 @@ def do_main_shuffle(entrances, exits, avail, mode_def): rem_entrances = list(unused_entrances) rem_entrances.sort() rem_exits = list(rem_exits if avail.coupled else avail.decoupled_exits) + if avail.swapped: + rem_exits = [x for x in rem_exits if x in avail.exits] rem_exits.sort() random.shuffle(rem_entrances) random.shuffle(rem_exits) placing = min(len(rem_entrances), len(rem_exits)) - for door, target in zip(rem_entrances, rem_exits): - connect_entrance(door, target, avail) + if avail.swapped: + connect_swapped(rem_entrances, rem_exits, avail) + else: + for door, target in zip(rem_entrances, rem_exits): + connect_entrance(door, target, avail) rem_entrances[:] = rem_entrances[placing:] rem_exits[:] = rem_exits[placing:] if rem_entrances or rem_exits: @@ -344,6 +390,8 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world): if avail.inverted and cross_world: om_cave_options = Inverted_Old_Man_Entrances + Old_Man_Entrances om_cave_options = [x for x in om_cave_options if x in entrances] + if avail.swapped: + om_cave_options = [e for e in om_cave_options if e not in Forbidden_Swap_Entrances] om_cave_choice = random.choice(om_cave_options) if not avail.coupled: connect_exit('Old Man Cave Exit (East)', om_cave_choice, avail) @@ -351,6 +399,10 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world): else: connect_two_way(om_cave_choice, 'Old Man Cave Exit (East)', avail) entrances.remove(om_cave_choice) + if avail.swapped and om_cave_choice != 'Old Man Cave (East)': + swap_ent, swap_ext = connect_swap(om_cave_choice, 'Old Man Cave Exit (East)', avail) + entrances.remove(swap_ent) + exits.remove(swap_ext) exits.remove('Old Man Cave Exit (East)') @@ -405,32 +457,74 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe random.shuffle(hole_entrances) if not cross_world and 'Sanctuary Grave' in holes_to_shuffle: - lw_entrance = next(entrance for entrance in hole_entrances if entrance[0] in LW_Entrances) - hole_entrances.remove(lw_entrance) - sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') - hole_targets.remove(sanc_interior) - connect_two_way(lw_entrance[0], sanc_interior[0], avail) # two-way exit - connect_entrance(lw_entrance[1], sanc_interior[1], avail) # hole - remove_from_list(entrances, [lw_entrance[0], lw_entrance[1]]) - remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) + hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player) + is_hc_in_dw = avail.world.mode[avail.player] == 'inverted' + if hc.connected_region: + is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld + chosen_entrance = None + if is_hc_in_dw: + if avail.swapped: + chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary') + if not chosen_entrance: + chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances) + if not chosen_entrance: + if avail.swapped: + chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary') + if not chosen_entrance: + chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances) + + if chosen_entrance: + hole_entrances.remove(chosen_entrance) + sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') + hole_targets.remove(sanc_interior) + connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit + connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole + remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]]) + remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) + if avail.swapped and drop_map[chosen_entrance[1]] != sanc_interior[1]: + swap_ent, swap_ext = connect_swap(chosen_entrance[0], sanc_interior[0], avail) + swap_drop, swap_tgt = connect_swap(chosen_entrance[1], sanc_interior[1], avail) + hole_entrances.remove((swap_ent, swap_drop)) + hole_targets.remove((swap_ext, swap_tgt)) + remove_from_list(entrances, [swap_ent, swap_drop]) + remove_from_list(exits, [swap_ext, swap_tgt]) random.shuffle(hole_targets) - for entrance, drop in hole_entrances: - ext, target = hole_targets.pop() + while len(hole_entrances): + entrance, drop = hole_entrances.pop() + if avail.swapped and len(hole_targets) > 1: + ext, target = next((x, t) for x, t in hole_targets if x != entrance_map[entrance]) + hole_targets.remove((ext, target)) + else: + ext, target = hole_targets.pop() connect_two_way(entrance, ext, avail) connect_entrance(drop, target, avail) remove_from_list(entrances, [entrance, drop]) remove_from_list(exits, [ext, target]) + if avail.swapped and drop_map[drop] != target: + swap_ent, swap_ext = connect_swap(entrance, ext, avail) + swap_drop, swap_tgt = connect_swap(drop, target, avail) + hole_entrances.remove((swap_ent, swap_drop)) + hole_targets.remove((swap_ext, swap_tgt)) + remove_from_list(entrances, [swap_ent, swap_drop]) + remove_from_list(exits, [swap_ext, swap_tgt]) def do_links_house(entrances, exits, avail, cross_world): lh_exit = 'Links House Exit' if lh_exit in exits: + links_house_vanilla = 'Big Bomb Shop' if avail.inverted else 'Links House' if not avail.world.shufflelinks[avail.player]: - links_house = 'Big Bomb Shop' if avail.inverted else 'Links House' + links_house = links_house_vanilla else: forbidden = list((Isolated_LH_Doors_Inv + Inverted_Dark_Sanctuary_Doors) if avail.inverted else Isolated_LH_Doors_Open) + if not avail.inverted: + if avail.world.doorShuffle[avail.player] != 'vanilla' and avail.world.intensity[avail.player] > 2: + forbidden.append('Hyrule Castle Entrance (South)') + if avail.swapped: + forbidden.append(links_house_vanilla) + forbidden.extend(Forbidden_Swap_Entrances) shuffle_mode = avail.world.shuffle[avail.player] # simple shuffle - if shuffle_mode == 'simple': @@ -471,6 +565,11 @@ def do_links_house(entrances, exits, avail, cross_world): avail.decoupled_entrances.remove(links_house) avail.decoupled_exits.remove('Links House Exit') avail.decoupled_exits.remove('Chris Houlihan Room Exit') + if avail.swapped and links_house != links_house_vanilla: + swap_ent, swap_ext = connect_swap(links_house, lh_exit, avail) + entrances.remove(swap_ent) + exits.remove(swap_ext) + # links on dm dm_spots = LH_DM_Connector_List.union(LH_DM_Exit_Forbidden) if links_house in dm_spots: @@ -491,20 +590,20 @@ def do_links_house(entrances, exits, avail, cross_world): possible_exits.sort() chosen_dm_escape = random.choice(possible_dm_exits) chosen_landing = random.choice(possible_exits) + chosen_exit_start = chosen_cave.pop(0) + chosen_exit_end = chosen_cave.pop() if avail.coupled: - connect_two_way(chosen_dm_escape, chosen_cave.pop(0), avail) - connect_two_way(chosen_landing, chosen_cave.pop(), avail) + connect_two_way(chosen_dm_escape, chosen_exit_start, avail) + connect_two_way(chosen_landing, chosen_exit_end, avail) entrances.remove(chosen_dm_escape) entrances.remove(chosen_landing) else: - chosen_cave_first = chosen_cave.pop(0) - connect_entrance(chosen_dm_escape, chosen_cave_first, avail) - connect_exit(chosen_cave.pop(), chosen_landing, avail) + connect_entrance(chosen_dm_escape, chosen_exit_start, avail) + connect_exit(chosen_exit_end, chosen_landing, avail) entrances.remove(chosen_dm_escape) - avail.decoupled_exits.remove(chosen_cave_first) + avail.decoupled_exits.remove(chosen_exit_start) avail.decoupled_entrances.remove(chosen_landing) - # chosen cave has already been removed from exits - exits.add(chosen_cave_first) # this needs to be added back in + exits.add(chosen_exit_start) # this needs to be added back in if len(chosen_cave): exits.update([x for x in chosen_cave]) exits.update([x for item in multi_exit_caves for x in item]) @@ -626,21 +725,44 @@ def do_cross_world_connectors(entrances, caves, avail): cave_candidate = (None, 0) for i, cave in enumerate(caves): if isinstance(cave, str): - cave = (cave,) + cave = [cave] if len(cave) > cave_candidate[1]: cave_candidate = (i, len(cave)) cave = caves.pop(cave_candidate[0]) if isinstance(cave, str): - cave = (cave,) + cave = [cave] - for ext in cave: + while len(cave): + ext = cave.pop() if not avail.coupled: choice = random.choice(avail.decoupled_entrances) connect_exit(ext, choice, avail) avail.decoupled_entrances.remove(choice) else: - connect_two_way(entrances.pop(), ext, avail) + if avail.swapped and len(entrances) > 1: + chosen_entrance = next(e for e in entrances if combine_map[e] != ext) + entrances.remove(chosen_entrance) + else: + chosen_entrance = entrances.pop() + connect_two_way(chosen_entrance, ext, avail) + if avail.swapped: + swap_ent, swap_ext = connect_swap(chosen_entrance, ext, avail) + if swap_ent: + entrances.remove(swap_ent) + if chosen_entrance not in single_entrance_map: + if swap_ext in cave: + cave.remove(swap_ext) + else: + for c in caves: + if swap_ext == c: + caves.remove(swap_ext) + break + if not isinstance(c, str) and swap_ext in c: + c.remove(swap_ext) + if len(c) == 0: + caves.remove(c) + break def do_fixed_shuffle(avail, entrance_list): @@ -841,7 +963,12 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): invalid_connections[entrance] = set() if entrance in must_exit: must_exit.remove(entrance) - entrances.append(entrance) + if entrance not in entrances: + entrances.append(entrance) + if avail.swapped: + swap_forbidden = [e for e in entrances if combine_map[e] in must_exit] + for e in swap_forbidden: + entrances.remove(e) entrances.sort() # sort these for consistency random.shuffle(entrances) random.shuffle(cave_options) @@ -854,6 +981,19 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): invalid_connections[ext] = invalid_connections[ext].union({'Agahnims Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'}) break + def connect_cave_swap(entrance, exit, current_cave): + swap_entrance, swap_exit = connect_swap(entrance, exit, avail) + if swap_entrance and entrance not in single_entrance_map: + for option in cave_options: + if swap_exit in option and option == current_cave: + x=0 + if swap_exit in option and option != current_cave: + option.remove(swap_exit) + if len(option) == 0: + cave_options.remove(option) + break + return swap_entrance, swap_exit + used_caves = [] required_entrances = 0 # Number of entrances reserved for used_caves while must_exit: @@ -861,9 +1001,10 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): # find multi exit cave candidates = [] for candidate in cave_options: - if not isinstance(candidate, str) and (candidate in used_caves - or len(candidate) < len(entrances) - required_entrances): - candidates.append(candidate) + if not isinstance(candidate, str) and len(candidate) > 1 and (candidate in used_caves + or len(candidate) < len(entrances) - required_entrances): + if not avail.swapped or (combine_map[exit] not in candidate and not any(e for e in must_exit if combine_map[e] in candidate)): #maybe someday allow these, but we need to disallow mutual locks in Swapped + candidates.append(candidate) cave = random.choice(candidates) if cave is None: raise RuntimeError('No more caves left. Should not happen!') @@ -871,13 +1012,23 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): # all caves are sorted so that the last exit is always reachable rnd_cave = list(cave) shuffle_connector_exits(rnd_cave) # should be the same as unbiasing some entrances... - entrances.remove(exit) + if avail.swapped and exit in swap_forbidden: + swap_forbidden.remove(exit) + else: + entrances.remove(exit) connect_two_way(exit, rnd_cave[-1], avail) + if avail.swapped: + swap_ent, _ = connect_cave_swap(exit, rnd_cave[-1], cave) + entrances.remove(swap_ent) if len(cave) == 2: entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] - and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit) + and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit + and (not avail.swapped or rnd_cave[0] != combine_map[e])) entrances.remove(entrance) connect_two_way(entrance, rnd_cave[0], avail) + if avail.swapped and combine_map[entrance] != rnd_cave[0]: + swap_ent, _ = connect_cave_swap(entrance, rnd_cave[0], cave) + entrances.remove(swap_ent) if cave in used_caves: required_entrances -= 2 used_caves.remove(cave) @@ -887,10 +1038,18 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): elif cave[-1] == 'Spectacle Rock Cave Exit': # Spectacle rock only has one exit cave_entrances = [] for cave_exit in rnd_cave[:-1]: - entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit) - cave_entrances.append(entrance) - entrances.remove(entrance) - connect_two_way(entrance, cave_exit, avail) + if avail.swapped and cave_exit not in avail.exits: + entrance = avail.world.get_entrance(cave_exit, avail.player).parent_region.entrances[0].name + cave_entrances.append(entrance) + else: + entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit + and (not avail.swapped or cave_exit != combine_map[e])) + cave_entrances.append(entrance) + entrances.remove(entrance) + connect_two_way(entrance, cave_exit, avail) + if avail.swapped and combine_map[entrance] != cave_exit: + swap_ent, _ = connect_cave_swap(entrance, cave_exit, cave) + entrances.remove(swap_ent) if entrance not in invalid_connections: invalid_connections[exit] = set() if all(entrance in invalid_connections for entrance in cave_entrances): @@ -911,11 +1070,20 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): for cave in used_caves: if cave in cave_options: # check if we placed multiple entrances from this 3 or 4 exit for cave_exit in cave: - entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)]) - invalid_cave_connections[tuple(cave)] = set() - entrances.remove(entrance) - connect_two_way(entrance, cave_exit, avail) + if avail.swapped and cave_exit not in avail.exits: + continue + else: + entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)] + and (not avail.swapped or cave_exit != combine_map[e])) + invalid_cave_connections[tuple(cave)] = set() + entrances.remove(entrance) + connect_two_way(entrance, cave_exit, avail) + if avail.swapped and combine_map[entrance] != cave_exit: + swap_ent, _ = connect_cave_swap(entrance, cave_exit, cave) + entrances.remove(swap_ent) cave_options.remove(cave) + if avail.swapped: + entrances.extend(swap_forbidden) def do_mandatory_connections_decoupled(avail, cave_options, must_exit): @@ -1033,6 +1201,47 @@ def inverted_substitution(avail_pool, collection, is_entrance, is_set=False): pass +def connect_swapped(entrancelist, targetlist, avail, two_way=False): + random.shuffle(entrancelist) + sorted_targets = list() + for ent in entrancelist: + if ent in combine_map: + if combine_map[ent] not in targetlist: + logging.getLogger('').error(f'{combine_map[ent]} not in target list, cannot swap entrance') + raise Exception(f'{combine_map[ent]} not in target list, cannot swap entrance') + sorted_targets.append(combine_map[ent]) + if len(sorted_targets): + targetlist = list(sorted_targets) + else: + targetlist = list(targetlist) + indexlist = list(range(len(targetlist))) + random.shuffle(indexlist) + + while len(indexlist) > 1: + index1 = indexlist.pop() + index2 = indexlist.pop() + targetlist[index1], targetlist[index2] = targetlist[index2], targetlist[index1] + + for exit, target in zip(entrancelist, targetlist): + if two_way: + connect_two_way(exit, target, avail) + else: + connect_entrance(exit, target, avail) + + +def connect_swap(entrance, exit, avail): + swap_exit = combine_map[entrance] + if swap_exit != exit: + swap_entrance = next(e for e, x in combine_map.items() if x == exit) + if swap_entrance in ['Pyramid Entrance', 'Pyramid Hole'] and avail.inverted: + swap_entrance = 'Inverted ' + swap_entrance + if entrance in entrance_map: + connect_two_way(swap_entrance, swap_exit, avail) + else: + connect_entrance(swap_entrance, swap_exit, avail) + return swap_entrance, swap_exit + return None, None + def connect_random(exitlist, targetlist, avail, two_way=False): targetlist = list(targetlist) random.shuffle(targetlist) @@ -1479,6 +1688,23 @@ modes = { }, } }, + 'swapped': { + 'undefined': 'swap', + 'keep_drops_together': 'on', + 'cross_world': 'on', + 'pools': { + 'skull_drops': { + 'special': 'drops', + 'entrances': ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', + 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] + }, + 'skull_doors': { + 'special': 'skull', + 'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', + 'Skull Woods Second Section Door (West)'] + }, + } + }, 'crossed': { 'undefined': 'shuffle', 'keep_drops_together': 'on', @@ -1641,6 +1867,8 @@ single_entrance_map = { 'Blinds Hideout': 'Blinds Hideout', 'Waterfall of Wishing': 'Waterfall of Wishing' } +combine_map = {**entrance_map, **single_entrance_map, **drop_map} + default_dw = { 'Thieves Town Exit', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit', @@ -1882,6 +2110,8 @@ Inverted_Bomb_Shop_Options = [ 'Agahnims Tower', 'Ganons Tower', 'Dark Sanctuary Hint', 'Big Bomb Shop', 'Links House'] + Blacksmith_Options +Forbidden_Swap_Entrances = {'Old Man Cave (East)', 'Blacksmiths Hut', 'Big Bomb Shop'} + # these are connections that cannot be shuffled and always exist. # They link together separate parts of the world we need to divide into regions mandatory_connections = [('Links House S&Q', 'Links House'), diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml index d4d6ebe2..86d9a2e9 100644 --- a/test/customizer/test.yaml +++ b/test/customizer/test.yaml @@ -1,5 +1,5 @@ meta: - seed: 398 + seed: 394 settings: 1: # mode: standard @@ -7,10 +7,11 @@ settings: # boss_shuffle: random # dropshuffle: underworld # enemy_shuffle: shuffled -# door_shuffle: crossed -# intensity: 3 - shuffle: insanity + door_shuffle: partitioned + intensity: 3 + shuffle: lite experimental: on + shopsanity: on # dungeon_counters: 'on' #entrances: @@ -19,23 +20,28 @@ settings: # Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top) # two-way: # Hyrule Castle Entrance (South): Links House Exit -#doors: -# 1: +doors: + 1: # lobbies: # Hyrule Castle South: GT Lobby S -# doors: -# GT Lobby Left Down Stairs: GT Quad Pot Up Stairs -# GT Lobby Right Down Stairs: GT Lobby Up Stairs -# GT Beam Dash ES: Swamp Big Key Ledge WN + doors: + TR Eye Bridge SW: Ice Compass Room NE + TR Lazy Eyes SE: Mire Conveyor Barrier NW + Hera Basement Cage Up Stairs: Hera Lobby Down Stairs + Hera Lobby Key Stairs: + dest: Hera Boss Down Stairs + type: Key Door + Hera Tile Room Up Stairs: Hera 5F Down Stairs + #bosses: # 1: # Ganons Tower (middle): Trinexx -#placements: -# 1: -# Lumberjack Tree: Lamp#2 +placements: + 1: + Tower of Hera - Basement Cage: Small Key (Tower of Hera) # Link's House: Lamp From c4ebbe5421f5dc6f790272266914addc471311bd Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 22 Dec 2023 14:41:05 -0700 Subject: [PATCH 071/158] fix(vanilla_fill): make uncle weapon non-random, make medallions vanilla fix(enemizer): enemy bans --- ItemList.py | 12 ++++++++++-- RELEASENOTES.md | 7 +++++++ source/enemizer/enemy_deny.yaml | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ItemList.py b/ItemList.py index 03e89419..a0247619 100644 --- a/ItemList.py +++ b/ItemList.py @@ -301,6 +301,8 @@ def generate_itempool(world, player): if not found_sword and world.swords[player] != 'swordless': found_sword = True possible_weapons.append(item) + if world.algorithm == 'vanilla_fill': # skip other possibilities + continue if (item in ['Progressive Bow', 'Bow'] and not found_bow and not world.bow_mode[player].startswith('retro')): found_bow = True @@ -394,9 +396,15 @@ def generate_itempool(world, player): if tr_medallion == 'Random': tr_medallion = None if not mm_medallion: - mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] + if world.algorithm == 'vanilla_fill': + mm_medallion = 'Ether' + else: + mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] if not tr_medallion: - tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] + if world.algorithm == 'vanilla_fill': + tr_medallion = 'Quake' + else: + tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] world.required_medallions[player] = (mm_medallion, tr_medallion) # shuffle bottle refills diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bdd4d99b..1f6f4747 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,13 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.3.0.9v + * ER: New Swapped ER mode borrowed from OWR + * ER: fixed a generation error where TR chooses all "must-exits" + * Ganonhunt: playthrough no longer collects crystals + * Vanilla Fill: Uncle weapon is always a sword, medallions for Mire/TR will be vanilla + * Customizer: support shufflebosses/shuffleenemies as well as boss_shuffle/enemy_shuffle + * Enemizer: enemy bans * 1.3.0.8v * Enemizer: Red Mimics correctly banned from challenge rooms in appropriate logic setting * No Logic Standard ER: Rain doors aren't blocked if no logic is enabled. diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 67f3c673..46842052 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -50,7 +50,7 @@ UwGeneralDeny: - [ 0x0027, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "RollerVerticalDown", "RollerVerticalUp"]] - [ 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, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"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, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ]] - [ 0x002a, 6, [ "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 5" @@ -165,6 +165,7 @@ UwGeneralDeny: - [ 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" + - [ 0x0066, 2, [ "AntiFairyCircle", "Bumper"]] - [ 0x0067, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Blue Bari 1" - [ 0x0067, 2, ["Bumper"]] #"Skull Woods - Firebar Pits - Blue Bari 2" - [ 0x0067, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 1" From dad1ee8336b927b6447bfed67509d29bdaee3a2b Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sun, 12 Feb 2023 09:44:52 +0100 Subject: [PATCH 072/158] Implement basic HMG logic * Ice Palace Lobby Clip * Kikiskip to Pod * Mire -> Hera -> Swamp - Mire small door to rupee room is removed - Hera and Swamp keys are placed without logic - Swamp locked by vanilla rules or having all mire smalls * Above as connectors in ER --- BaseClasses.py | 7 +- Doors.py | 6 +- EntranceShuffle.py | 2 +- Fill.py | 5 +- ItemList.py | 6 +- KeyDoorShuffle.py | 16 ++ Main.py | 14 +- Rules.py | 18 +- UnderworldGlitchRules.py | 173 ++++++++++++++++++ resources/app/cli/args.json | 1 + resources/app/cli/lang/en.json | 12 +- resources/app/gui/lang/en.json | 1 + resources/app/gui/randomize/item/widgets.json | 1 + source/overworld/EntranceShuffle2.py | 2 +- source/tools/MysteryUtils.py | 3 +- test/customizer/hmg/fireless_ice.yaml | 35 ++++ test/customizer/hmg/hammer_in_swamp.yaml | 42 +++++ test/customizer/hmg/mearl_in_pod.yaml | 34 ++++ test/customizer/hmg/mirrorless_swamp.yaml | 42 +++++ test/customizer/hmg/pod_as_connector.yaml | 44 +++++ test/customizer/hmg/swamp_as_connector.yaml | 55 ++++++ .../hmg/swamp_small_in_swamp_back.yaml | 44 +++++ 22 files changed, 540 insertions(+), 23 deletions(-) create mode 100644 UnderworldGlitchRules.py create mode 100644 test/customizer/hmg/fireless_ice.yaml create mode 100644 test/customizer/hmg/hammer_in_swamp.yaml create mode 100644 test/customizer/hmg/mearl_in_pod.yaml create mode 100644 test/customizer/hmg/mirrorless_swamp.yaml create mode 100644 test/customizer/hmg/pod_as_connector.yaml create mode 100644 test/customizer/hmg/swamp_as_connector.yaml create mode 100644 test/customizer/hmg/swamp_small_in_swamp_back.yaml diff --git a/BaseClasses.py b/BaseClasses.py index 242161e6..dc977cde 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -111,7 +111,7 @@ class World(object): set_player_attr('can_access_trock_front', None) set_player_attr('can_access_trock_big_chest', None) set_player_attr('can_access_trock_middle', None) - set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic'] + set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'hybridglitches', 'nologic'] or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity']) set_player_attr('mapshuffle', False) set_player_attr('compassshuffle', False) @@ -1099,6 +1099,9 @@ class CollectionState(object): def can_lift_rocks(self, player): return self.has('Power Glove', player) or self.has('Titans Mitts', player) + + def can_bomb_clip(self, region, player: int) -> bool: + return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) and self.can_use_bombs(player) def has_bottle(self, player): return self.bottle_count(player) > 0 @@ -2973,7 +2976,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, 'swapped': 10} # byte 1: LLLW WSS? (logic, mode, sword) -logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4} +logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5} world_mode = {"open": 0, "standard": 1, "inverted": 2} sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3} diff --git a/Doors.py b/Doors.py index ad83b67f..a3934e9c 100644 --- a/Doors.py +++ b/Doors.py @@ -938,8 +938,10 @@ def create_doors(world, player): create_door(player, 'Mire Dark Shooters Up Stairs', Sprl).dir(Up, 0x93, 0, LTH).ss(A, 0x32, 0xec), create_door(player, 'Mire Dark Shooters SW', Intr).dir(So, 0x93, Left, High).pos(0), create_door(player, 'Mire Block X NW', Intr).dir(No, 0x93, Left, High).pos(0), - create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1), - create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1), + create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).pos(1) if world.logic[player] == 'hybridglitches' else create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1), + create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).pos(1) if world.logic[player] == 'hybridglitches' else create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1), + # create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1), + # create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1), create_door(player, 'Mire Block X WS', Nrml).dir(We, 0x93, Bot, High).pos(2), create_door(player, 'Mire Tall Dark and Roomy ES', Nrml).dir(Ea, 0x92, Bot, High).pos(4), create_door(player, 'Mire Tall Dark and Roomy WN', Intr).dir(We, 0x92, Top, High).pos(0), diff --git a/EntranceShuffle.py b/EntranceShuffle.py index b95048c4..a1e15991 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1112,7 +1112,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): invalid_connections = Must_Exit_Invalid_Connections.copy() invalid_cave_connections = defaultdict(set) - if world.logic[player] in ['owglitches', 'nologic']: + if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: import OverworldGlitchRules for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'): invalid_connections[entrance] = set() diff --git a/Fill.py b/Fill.py index d72c5b6c..49b9f113 100644 --- a/Fill.py +++ b/Fill.py @@ -175,6 +175,9 @@ def valid_key_placement(item, location, key_pool, collection_state, world): if dungeon: if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name): return True + # Small key and big key in Swamp and Hera are placed without logic + if world.logic[item.player] == 'hybridglitches' and dungeon.name in ['Tower of Hera', 'Swamp Palace'] and dungeon.name in item.name: + return True key_logic = world.key_logic[item.player][dungeon.name] unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player]) prize_loc = None @@ -398,7 +401,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # fill in gtower locations with trash first for player in range(1, world.players + 1): if (not gftower_trash or not world.ganonstower_vanilla[player] - or world.logic[player] in ['owglitches', 'nologic']): + or world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']): continue gt_count, total_count = calc_trash_locations(world, player) scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7) diff --git a/ItemList.py b/ItemList.py index a0247619..5bfe04d5 100644 --- a/ItemList.py +++ b/ItemList.py @@ -881,7 +881,7 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt return random.choice([True, False]) if progressive == 'random' else progressive == 'on' # provide boots to boots glitch dependent modes - if logic in ['owglitches', 'nologic']: + if logic in ['owglitches', 'hybridglitches', 'nologic']: precollected_items.append('Pegasus Boots') pool.remove('Pegasus Boots') pool.extend(['Rupees (20)']) @@ -1174,7 +1174,7 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer pool.extend(['Nothing'] * nothings) start_inventory = [x for x in world.precollected_items if x.player == player] - if world.logic[player] in ['owglitches', 'nologic'] and all(x.name !=' Pegasus Boots' for x in start_inventory): + if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name !=' Pegasus Boots' for x in start_inventory): precollected_items.append('Pegasus Boots') if 'Pegasus Boots' in pool: pool.remove('Pegasus Boots') @@ -1316,7 +1316,7 @@ def make_customizer_pool(world, player): sphere_0 = world.customizer.get_start_inventory() no_start_inventory = not sphere_0 or not sphere_0[player] init_equip = [] if no_start_inventory else sphere_0[player] - if (world.logic[player] in ['owglitches', 'nologic'] + if (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and (no_start_inventory or all(x != 'Pegasus Boots' for x in init_equip))): precollected_items.append('Pegasus Boots') if 'Pegasus Boots' in pool: diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 9a0b6f89..5331ad4c 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -2106,6 +2106,22 @@ def validate_key_placement(key_layout, world, player): if i.player == player and i.name == smallkey_name: keys_outside += 1 + if world.logic[player] == 'hybridglitches': + # Swamp keylogic + if smallkey_name.endswith('(Swamp Palace)'): + swamp_entrance = world.get_location('Swamp Palace - Entrance', player) + # Swamp small not vanilla + if swamp_entrance.item is None or (swamp_entrance.item.name != smallkey_name or swamp_entrance.item.player != player): + mire_keylayout = world.key_layout[player]['Misery Mire'] + mire_smallkey_name = dungeon_keys[mire_keylayout.sector.name] + # Check if any mire keys are in swamp (excluding entrance), if none then add one to keys_outside + mire_keys_in_swamp = sum([1 if x.item.name == mire_smallkey_name else 0 for x in key_layout.item_locations if x.item is not None and x != swamp_entrance]) + if mire_keys_in_swamp == 0: + keys_outside +=1 + # Mire keylogic + if smallkey_name.endswith('(Tower of Hera)'): + # TODO: Make sure that mire medallion isn't in hera basement, or if it it, the small key is available downstairs + big_key_outside = True for code, counter in key_layout.key_counters.items(): if len(counter.child_doors) == 0: continue diff --git a/Main.py b/Main.py index ac68d48c..b273d922 100644 --- a/Main.py +++ b/Main.py @@ -27,6 +27,7 @@ from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dunge from Fill import dungeon_tracking from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items +from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors from Utils import output_path, parse_player_names from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config @@ -205,7 +206,7 @@ def main(args, seed=None, fish=None): for player in range(1, world.players + 1): create_regions(world, player) - if world.logic[player] in ('owglitches', 'nologic'): + if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'): create_owg_connections(world, player) create_dungeon_regions(world, player) create_shops(world, player) @@ -216,6 +217,8 @@ def main(args, seed=None, fish=None): world.data_tables[player] = init_data_tables(world, player) place_bosses(world, player) randomize_enemies(world, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(world, player) adjust_locations(world, player) if world.customizer and world.customizer.get_start_inventory(): @@ -245,6 +248,8 @@ def main(args, seed=None, fish=None): link_entrances_new(world, player) else: link_entrances(world, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connectors(world, player) logger.info(world.fish.translate("cli", "cli", "shuffling.prep")) for player in range(1, world.players + 1): @@ -497,8 +502,11 @@ def copy_world(world): create_shops(ret, player) create_rooms(ret, player) create_dungeons(ret, player) - if world.logic[player] in ('owglitches', 'nologic'): + if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'): create_owg_connections(ret, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(ret, player) + # there are region references here they must be migrated to preserve integrity # ret.exp_cache = world.exp_cache.copy() @@ -579,6 +587,8 @@ def copy_world(world): ret.sanc_portal = world.sanc_portal for player in range(1, world.players + 1): + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connectors(ret, player) set_rules(ret, player) return ret diff --git a/Rules.py b/Rules.py index f98523a8..6e1f40f8 100644 --- a/Rules.py +++ b/Rules.py @@ -8,6 +8,7 @@ from BaseClasses import PotFlags from Dungeons import dungeon_table from RoomData import DoorKind from OverworldGlitchRules import overworld_glitches_rules +from UnderworldGlitchRules import underworld_glitches_rules from source.logic.Rule import RuleFactory from source.dungeon.EnemyList import EnemySprite, Sprite @@ -48,7 +49,7 @@ def set_rules(world, player): logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.') no_glitches_rules(world, player) fake_flipper_rules(world, player) - elif world.logic[player] == 'owglitches': + elif world.logic[player] in ['owglitches', 'hybridglitches']: logging.getLogger('').info('There is a chance OWG has bugged edge case rulesets, especially in inverted. Definitely file a report on GitHub if you see anything strange.') # Initially setting no_glitches_rules to set the baseline rules for some # entrances. The overworld_glitches_rules set is primarily additive. @@ -73,7 +74,7 @@ def set_rules(world, player): if world.mode[player] != 'inverted': set_big_bomb_rules(world, player) - if world.logic[player] == 'owglitches' and world.shuffle[player] != 'insanity': + if world.logic[player] in ['owglitches', 'hybridglitches'] and world.shuffle[player] != 'insanity': path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.world.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or') else: @@ -85,7 +86,11 @@ def set_rules(world, player): set_bunny_rules(world, player, world.mode[player] == 'inverted') - if world.mode[player] != 'inverted' and world.logic[player] == 'owglitches': + # These rules go here because the overwrite/add to some of the above rules + if world.logic[player] == 'hybridglitches': + underworld_glitches_rules(world, player) + + if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches']: add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or') @@ -1898,7 +1903,7 @@ def set_bunny_rules(world, player, inverted): def get_rule_to_add(region, location=None, connecting_entrance=None): # In OWG, a location can potentially be superbunny-mirror accessible or # bunny revival accessible. - if world.logic[player] == 'owglitches': + if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type != RegionType.Dungeon \ and (location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations()) \ and not is_link(region): @@ -1928,7 +1933,7 @@ def set_bunny_rules(world, player, inverted): new_path = path + [entrance.access_rule] new_seen = seen.union({new_region}) if not is_link(new_region): - if world.logic[player] == 'owglitches': + if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): continue @@ -2154,6 +2159,9 @@ def add_key_logic_rules(world, player): add_rule(dep.entrance, eval_func(door_name, d_name, player)) for location in d_logic.bk_restricted: if not location.forced_item: + # Skip BK restricted locations in hybrid glitches. Bad, but necessary for now. + if world.logic[player] == 'hybridglitches' and d_name in ['Tower of Hera', 'Swamp Palace']: + continue forbid_item(location, d_logic.bk_name, player) for location in d_logic.sm_restricted: forbid_item(location, d_logic.small_key_name, player) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py new file mode 100644 index 00000000..ca99a5bb --- /dev/null +++ b/UnderworldGlitchRules.py @@ -0,0 +1,173 @@ +from BaseClasses import Entrance +import Rules +from OverworldGlitchRules import create_no_logic_connections + + +def get_kikiskip_spots(): + """ + Spectacle Rock Cave (Bottom) -> Palace of Darkness Exit, a.k.a. Kiki Skip + """ + yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal") + + +# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions +def get_kikiskip_connectors(world, player): + if world.fix_palaceofdarkness_exit[player] or world.fix_fake_world[player]: + yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", world.get_entrance("Palace of Darkness Exit", player).connected_region) + + +def get_mireheraswamp_connectors(world, player): + if world.fix_palaceofdarkness_exit[player] or world.fix_fake_world[player]: + yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Tower of Hera Exit", player).connected_region) + yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Swamp Palace Exit", player).connected_region) + + +def get_mireheraswamp_spots(): + """ + "Mire Torches Top -> Tower of Hera Exit, a.k.a. Mire to Hera Clip + "Mire Torches Top -> Swamp Palace Exit, a.k.a. Hera to Swamp Clip + """ + + yield ("Mire to Hera Clip", "Mire Torches Top", "Hera Portal") + yield ("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal") + + +def get_icepalace_spots(): + """ + "Ice Palace Exit -> Ice Palace Exit, a.k.a. Ice Palace Clip + """ + yield ("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop") + + +# Create connections between dungeons +def create_hybridmajor_connections(world, player): + create_no_logic_connections(player, world, get_kikiskip_spots()) + create_no_logic_connections(player, world, get_mireheraswamp_spots()) + create_no_logic_connections(player, world, get_icepalace_spots()) + + +# Turn dungeons into connectors 4 +def create_hybridmajor_connectors(world, player): + create_no_logic_connections(player, world, get_kikiskip_connectors(world, player)) + create_no_logic_connections(player, world, get_mireheraswamp_connectors(world, player)) + + +# For some entrances, we need to fake having pearl, because we're in fake DW/LW. +# This creates a copy of the input state that has Moon Pearl. +def fake_pearl_state(state, player): + if state.has("Moon Pearl", player): + return state + fake_state = state.copy() + fake_state.prog_items["Moon Pearl", player] += 1 + return fake_state + + +# Sets the rules on where we can actually go using this clip. +# Behavior differs based on what type of ER shuffle we're playing. +def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, dungeon_exit: str): + fix_dungeon_exits = world.fix_palaceofdarkness_exit[player] + fix_fake_worlds = world.fix_fake_world[player] + + dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0] + if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix + # Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially. + + # entrance doesn't exist until you fire rod it from the other side + if dungeon_entrance.name == "Skull Woods Final Section": + Rules.set_rule(clip, lambda state: False) + + elif dungeon_entrance.name == "Misery Mire": + if world.swords[player] == "swordless": + Rules.add_rule(clip, lambda state: state.has_misery_mire_medallion(player)) + else: + Rules.add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) + + elif dungeon_entrance.name == "Agahnims Tower": + Rules.add_rule(clip, lambda state: state.has("Cape", player) or state.has_beam_sword(player) or state.has("Beat Agahnim 1", player)) + + # Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally. + Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) + elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix + # Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region. + Rules.add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player))) + # exiting restriction + Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) + + # Otherwise, the shuffle type is lean, crossed, or insanity; all of these do not need additional rules on where we can go, + # since the clip links directly to the exterior region. + + +def underworld_glitches_rules(world, player): + # Ice Palace Entrance Clip + Rules.add_rule(world.get_entrance("Ice Lobby SE", player), lambda state: state.can_bomb_clip(world.get_region("Ice Lobby", player), player), combine="or") + + # Kiki Skip + kks = world.get_entrance("Kiki Skip", player) + Rules.set_rule(kks, lambda state: state.can_bomb_clip(kks.parent_region, player)) + dungeon_reentry_rules(world, player, kks, "Palace of Darkness Portal", "Palace of Darkness Exit") + + # Mire -> Hera -> Swamp + def mire_clip(state): + return state.can_reach("Mire Torches Top", "Region", player) and state.can_bomb_clip(world.get_region("Mire Torches Top", player), player) and state.has_fire_source(player) + + def hera_clip(state): + return state.can_reach("Hera 4F", "Region", player) and state.can_bomb_clip(world.get_region("Hera 4F", player), player) + + Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") + + # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots + # Flippers required for the doors going _into_ the dungeon + for door in [ + "Swamp Entrance Down Stairs", + "Swamp Pot Row WS", + "Swamp Trench 1 Key Ledge NW", + "Swamp Hub WN", + "Swamp Hub North Ledge N", + "Swamp Crystal Switch EN", + "Swamp Push Statue S", + "Swamp Waterway NW", + "Swamp T SW", + ]: + Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") + + # These doors let us go backwards so we don't require flippers + for door in [ "Swamp Trench 1 Approach ES", "Swamp Hammer Switch SW",]: + Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6), combine="or") + + Rules.add_rule(world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or") + + # Build the rule for SP moat. + # We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT. + # First we require a certain type of entrance shuffle, then build the rule from its pieces. + if not world.swamp_patch_required[player]: + if world.shuffle[player] in [ + "vanilla", + "dungeonssimple", + "dungeonsfull", + "dungeonscrossed", + ]: + rule_map = { + "Mire Portal": (lambda state: True), + "Hera Portal": (lambda state: state.can_reach("Hera Startile Corner NW", "Entrance", player)), + } + inverted = world.mode[player] == "inverted" + + def hera_rule(state): + return (state.has("Moon Pearl", player) or not inverted) and rule_map.get(world.get_entrance("Tower of Hera", player).connected_region.name, lambda state: False)(state) + + def gt_rule(state): + return (state.has("Moon Pearl", player) or inverted) and rule_map.get( + world.get_entrance(("Ganons Tower" if not inverted else "Inverted Ganons Tower"), player).connected_region.name, lambda state: False)(state) + + def mirrorless_moat_rule(state): + return state.can_reach("Old Man S&Q", "Entrance", player) and mire_clip(state) and (hera_rule(state) or gt_rule(state)) + + Rules.add_rule(world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or") + + # Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys + mire_to_hera = world.get_entrance("Mire to Hera Clip", player) + mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) + Rules.set_rule(mire_to_hera, mire_clip) + Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player)) + dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit") + dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 1d7dbcb2..6aedcad8 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -23,6 +23,7 @@ "noglitches", "minorglitches", "owglitches", + "hybridglitches", "nologic" ] }, diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 024f16e0..3cd6aa19 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -63,11 +63,13 @@ "bps": [ "Output BPS patches instead of ROMs"], "logic": [ "Select Enforcement of Item Requirements. (default: %(default)s)", - "No Glitches: No Glitch knowledge required.", - "Minor Glitches: May require Fake Flippers, Bunny Revival", - " and Dark Room Navigation.", - "No Logic: Distribute items without regard for", - " item requirements." + "No Glitches: No Glitch knowledge required.", + "Minor Glitches: May require Fake Flippers, Bunny Revival", + " and Dark Room Navigation.", + "Overworld Glitches: May require overworld clips and teleports.", + "Hybrid Major Glitches: May require underworld clips.", + "No Logic: Distribute items without regard for", + " item requirements." ], "mode": [ "Select game mode. (default: %(default)s)", diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index bd8e755b..c14dee25 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -237,6 +237,7 @@ "randomizer.item.logiclevel.noglitches": "No Glitches", "randomizer.item.logiclevel.minorglitches": "Minor Glitches", "randomizer.item.logiclevel.owglitches": "Overworld Glitches", + "randomizer.item.logiclevel.hybridglitches": "Hybrid Major Glitches", "randomizer.item.logiclevel.nologic": "No Logic", "randomizer.item.goal": "Goal", diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 6e7ee6b8..0a895d3c 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -20,6 +20,7 @@ "noglitches", "minorglitches", "owglitches", + "hybridglitches", "nologic" ] }, diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index e6fe631d..9847e472 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -957,7 +957,7 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): invalid_connections = Must_Exit_Invalid_Connections.copy() invalid_cave_connections = defaultdict(set) - if avail.world.logic[avail.player] in ['owglitches', 'nologic']: + if avail.world.logic[avail.player] in ['owglitches', 'hybridglitches', 'nologic']: import OverworldGlitchRules for entrance in OverworldGlitchRules.get_non_mandatory_exits(avail.inverted): invalid_connections[entrance] = set() diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index eb6f0fee..ec3518fb 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -80,7 +80,8 @@ def roll_settings(weights): ret.algorithm = get_choice('algorithm') - glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owglitches': 'owglitches', + glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'hybridglitches': 'hybridglitches', + 'hmg': 'hybridglitches', 'owglitches': 'owglitches', 'owg': 'owglitches', 'minorglitches': 'minorglitches'} glitches_required = get_choice('glitches_required') if glitches_required is not None: diff --git a/test/customizer/hmg/fireless_ice.yaml b/test/customizer/hmg/fireless_ice.yaml new file mode 100644 index 00000000..86affd94 --- /dev/null +++ b/test/customizer/hmg/fireless_ice.yaml @@ -0,0 +1,35 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Ice Palace - Compass Chest: Fire Rod + Ice Palace - Freezor Chest: Bombos diff --git a/test/customizer/hmg/hammer_in_swamp.yaml b/test/customizer/hmg/hammer_in_swamp.yaml new file mode 100644 index 00000000..f2e3aae7 --- /dev/null +++ b/test/customizer/hmg/hammer_in_swamp.yaml @@ -0,0 +1,42 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Flippers + - Lamp + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Swamp Palace - Big Chest: Hammer diff --git a/test/customizer/hmg/mearl_in_pod.yaml b/test/customizer/hmg/mearl_in_pod.yaml new file mode 100644 index 00000000..ec2d5b9a --- /dev/null +++ b/test/customizer/hmg/mearl_in_pod.yaml @@ -0,0 +1,34 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Palace of Darkness - Shooter Room: Moon Pearl diff --git a/test/customizer/hmg/mirrorless_swamp.yaml b/test/customizer/hmg/mirrorless_swamp.yaml new file mode 100644 index 00000000..c8a109c3 --- /dev/null +++ b/test/customizer/hmg/mirrorless_swamp.yaml @@ -0,0 +1,42 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Flippers + - Lamp + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Swamp Palace - Big Chest: Magic Mirror diff --git a/test/customizer/hmg/pod_as_connector.yaml b/test/customizer/hmg/pod_as_connector.yaml new file mode 100644 index 00000000..48f72d6e --- /dev/null +++ b/test/customizer/hmg/pod_as_connector.yaml @@ -0,0 +1,44 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: crossed +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +entrances: + 1: + entrances: + Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave + exits: + Links House: Chris Houlihan Room Exit + two-way: + Dark Lake Hylia Ledge Fairy: Palace of Darkness Exit + Lake Hylia Fortune Teller: Spectacle Rock Cave Exit + Links House: Links House Exit +placements: + 1: + Peg Cave: Moon Pearl diff --git a/test/customizer/hmg/swamp_as_connector.yaml b/test/customizer/hmg/swamp_as_connector.yaml new file mode 100644 index 00000000..ae343937 --- /dev/null +++ b/test/customizer/hmg/swamp_as_connector.yaml @@ -0,0 +1,55 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: crossed +start_inventory: + 1: + - Hookshot + - Lamp + - Hammer + - Magic Mirror + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +entrances: + 1: + entrances: + Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave + exits: + Links House: Chris Houlihan Room Exit + two-way: + Dark Lake Hylia Ledge Fairy: Swamp Palace Exit + Lake Hylia Fortune Teller: Misery Mire Exit + Links House: Links House Exit +placements: + 1: + Peg Cave: Moon Pearl + diff --git a/test/customizer/hmg/swamp_small_in_swamp_back.yaml b/test/customizer/hmg/swamp_small_in_swamp_back.yaml new file mode 100644 index 00000000..b57cc9b6 --- /dev/null +++ b/test/customizer/hmg/swamp_small_in_swamp_back.yaml @@ -0,0 +1,44 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Hookshot + - Lamp + - Hammer + - Magic Mirror + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +# placements: +# 1: +# Swamp Palace - Entrance: Boss Heart Container From 0ff25d7fd9ce2fd258f601f78f6b573a3d927490 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Mon, 13 Feb 2023 20:25:02 +0100 Subject: [PATCH 073/158] Give access to correct IP door for lobby clip --- UnderworldGlitchRules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index ca99a5bb..e94df74b 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -99,7 +99,7 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du def underworld_glitches_rules(world, player): # Ice Palace Entrance Clip - Rules.add_rule(world.get_entrance("Ice Lobby SE", player), lambda state: state.can_bomb_clip(world.get_region("Ice Lobby", player), player), combine="or") + Rules.add_rule(world.get_entrance("Ice Bomb Drop SE", player), lambda state: state.can_bomb_clip(world.get_region("Ice Lobby", player), player), combine="or") # Kiki Skip kks = world.get_entrance("Kiki Skip", player) From 95703fe2b5e6277ecb38221d1f03701fec654a91 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Mon, 13 Feb 2023 20:25:59 +0100 Subject: [PATCH 074/158] Fix flippers logic preventing locks on drain --- UnderworldGlitchRules.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index e94df74b..af51de57 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -116,8 +116,10 @@ def underworld_glitches_rules(world, player): Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots - # Flippers required for the doors going _into_ the dungeon + # Flippers required for all of these doors to prevent locks when flooding for door in [ + "Swamp Trench 1 Approach ES", + "Swamp Hammer Switch SW", "Swamp Entrance Down Stairs", "Swamp Pot Row WS", "Swamp Trench 1 Key Ledge NW", @@ -130,10 +132,6 @@ def underworld_glitches_rules(world, player): ]: Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") - # These doors let us go backwards so we don't require flippers - for door in [ "Swamp Trench 1 Approach ES", "Swamp Hammer Switch SW",]: - Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6), combine="or") - Rules.add_rule(world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or") # Build the rule for SP moat. From 9352067d696fba251eb63e1cec7147997636cfdc Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 14 Feb 2023 11:35:00 +0100 Subject: [PATCH 075/158] Fix swamp smalls in pottery to get out of swamp --- ItemList.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ItemList.py b/ItemList.py index 5bfe04d5..39584262 100644 --- a/ItemList.py +++ b/ItemList.py @@ -287,6 +287,11 @@ def generate_itempool(world, player): for _ in range(0, amt): pool.append('Rupees (20)') + if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: + # In HMG force swamp smalls in pots to allow getting out of swamp palace + placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' + placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' + start_inventory = list(world.precollected_items) for item in precollected_items: world.push_precollected(ItemFactory(item, player)) From 2b826077fc93a53e6efa270b9f305353e12b9440 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 14 Feb 2023 12:14:28 +0100 Subject: [PATCH 076/158] Undo test doors check --- Doors.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Doors.py b/Doors.py index a3934e9c..ad83b67f 100644 --- a/Doors.py +++ b/Doors.py @@ -938,10 +938,8 @@ def create_doors(world, player): create_door(player, 'Mire Dark Shooters Up Stairs', Sprl).dir(Up, 0x93, 0, LTH).ss(A, 0x32, 0xec), create_door(player, 'Mire Dark Shooters SW', Intr).dir(So, 0x93, Left, High).pos(0), create_door(player, 'Mire Block X NW', Intr).dir(No, 0x93, Left, High).pos(0), - create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).pos(1) if world.logic[player] == 'hybridglitches' else create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1), - create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).pos(1) if world.logic[player] == 'hybridglitches' else create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1), - # create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1), - # create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1), + create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1), + create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1), create_door(player, 'Mire Block X WS', Nrml).dir(We, 0x93, Bot, High).pos(2), create_door(player, 'Mire Tall Dark and Roomy ES', Nrml).dir(Ea, 0x92, Bot, High).pos(4), create_door(player, 'Mire Tall Dark and Roomy WN', Intr).dir(We, 0x92, Top, High).pos(0), From 228f18fed4ea2684e48ee25eaa7771933c7f527c Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Fri, 17 Feb 2023 20:29:31 +0100 Subject: [PATCH 077/158] Logic updates - Add Thieves -> Desert clip (+ as connector) - Spec rock bomb clip (+ as connector) - Paradox teleport for chests in crystal area and from front - Require bombs or Somaria to get out of IP clip - Add dash clip checks --- BaseClasses.py | 3 ++ UnderworldGlitchRules.py | 98 ++++++++++++++++++++++++++++++++-------- 2 files changed, 81 insertions(+), 20 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index dc977cde..cff79a25 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1102,6 +1102,9 @@ class CollectionState(object): def can_bomb_clip(self, region, player: int) -> bool: return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) and self.can_use_bombs(player) + + def can_dash_clip(self, region, player: int) -> bool: + return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) def has_bottle(self, player): return self.bottle_count(player) > 0 diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index af51de57..a7988d52 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -10,18 +10,6 @@ def get_kikiskip_spots(): yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal") -# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions -def get_kikiskip_connectors(world, player): - if world.fix_palaceofdarkness_exit[player] or world.fix_fake_world[player]: - yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", world.get_entrance("Palace of Darkness Exit", player).connected_region) - - -def get_mireheraswamp_connectors(world, player): - if world.fix_palaceofdarkness_exit[player] or world.fix_fake_world[player]: - yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Tower of Hera Exit", player).connected_region) - yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Swamp Palace Exit", player).connected_region) - - def get_mireheraswamp_spots(): """ "Mire Torches Top -> Tower of Hera Exit, a.k.a. Mire to Hera Clip @@ -39,17 +27,67 @@ def get_icepalace_spots(): yield ("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop") -# Create connections between dungeons +def get_thievesdesert_spots(): + """ + "Thieves' Town -> Desert Palace , a.k.a. Thieves to Desert Clip + Accessing any of the exits will be in logic because of the ability to dungeon bunny revive + """ + yield ("Thieves to Desert Clip", "Thieves Attic", "Desert West Portal") + yield ("Thieves to Desert Clip", "Thieves Attic", "Desert South Portal") + yield ("Thieves to Desert Clip", "Thieves Attic", "Desert East Portal") + + +def get_specrock_spots(): + """ + "Spectacle Rock Cave (Peak) -> Spectacle Rock Cave (Top), a.k.a. Spectacle Rock Cave Clip + """ + yield ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave (Top)") + + +def get_paradox_spots(): + """ + "Paradox Cave Front -> Paradox Cave Chest Area, a.k.a. Paradox Cave Teleport (dash citrus, 1f right, teleport up) + """ + yield ("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area") + + +# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions +def get_kikiskip_connectors(world, player): + yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", world.get_entrance("Palace of Darkness Exit", player).connected_region) + + +def get_mireheraswamp_connectors(world, player): + yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Tower of Hera Exit", player).connected_region) + yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Swamp Palace Exit", player).connected_region) + + +def get_thievesdesert_connectors(world, player): + yield ("Thieves to Desert Clip", "Thieves Attic", world.get_entrance("Desert Palace Exit (West)", player).connected_region) + yield ("Thieves to Desert Clip", "Thieves Attic", world.get_entrance("Desert Palace Exit (South)", player).connected_region) + yield ("Thieves to Desert Clip", "Thieves Attic", world.get_entrance("Desert Palace Exit (East)", player).connected_region) + +def get_specrock_connectors(world, player): + yield ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", world.get_entrance("Spectacle Rock Cave Exit (Top)", player).connected_region) + yield ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", world.get_entrance("Spectacle Rock Cave Exit", player).connected_region) + + + +# Create connections between dungeons/locations def create_hybridmajor_connections(world, player): create_no_logic_connections(player, world, get_kikiskip_spots()) create_no_logic_connections(player, world, get_mireheraswamp_spots()) create_no_logic_connections(player, world, get_icepalace_spots()) + create_no_logic_connections(player, world, get_thievesdesert_spots()) + create_no_logic_connections(player, world, get_specrock_spots()) + create_no_logic_connections(player, world, get_paradox_spots()) -# Turn dungeons into connectors 4 +# Turn dungeons into connectors def create_hybridmajor_connectors(world, player): create_no_logic_connections(player, world, get_kikiskip_connectors(world, player)) create_no_logic_connections(player, world, get_mireheraswamp_connectors(world, player)) + create_no_logic_connections(player, world, get_thievesdesert_connectors(world, player)) + create_no_logic_connections(player, world, get_specrock_connectors(world, player)) # For some entrances, we need to fake having pearl, because we're in fake DW/LW. @@ -93,13 +131,16 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du # exiting restriction Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) - # Otherwise, the shuffle type is lean, crossed, or insanity; all of these do not need additional rules on where we can go, + # Otherwise, the shuffle type is lean, lite, crossed, or insanity; all of these do not need additional rules on where we can go, # since the clip links directly to the exterior region. def underworld_glitches_rules(world, player): - # Ice Palace Entrance Clip - Rules.add_rule(world.get_entrance("Ice Bomb Drop SE", player), lambda state: state.can_bomb_clip(world.get_region("Ice Lobby", player), player), combine="or") + # Ice Palace Entrance Clip, needs bombs or cane of somaria to exit bomb drop room + Rules.add_rule(world.get_entrance("Ice Bomb Drop SE", player), + lambda state: state.can_dash_clip(world.get_region("Ice Lobby", player), player) and + (state.can_use_bombs(player) or state.has('Cane of Somaria', player)), + combine="or") # Kiki Skip kks = world.get_entrance("Kiki Skip", player) @@ -108,13 +149,15 @@ def underworld_glitches_rules(world, player): # Mire -> Hera -> Swamp def mire_clip(state): - return state.can_reach("Mire Torches Top", "Region", player) and state.can_bomb_clip(world.get_region("Mire Torches Top", player), player) and state.has_fire_source(player) + return state.can_reach("Mire Torches Top", "Region", player) and state.can_dash_clip(world.get_region("Mire Torches Top", player), player) def hera_clip(state): - return state.can_reach("Hera 4F", "Region", player) and state.can_bomb_clip(world.get_region("Hera 4F", player), player) + return state.can_reach("Hera 4F", "Region", player) and state.can_dash_clip(world.get_region("Hera 4F", player), player) Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") + Rules.add_rule(world.get_entrance("Thieves to Desert Clip", player), lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player), combine="or") + # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots # Flippers required for all of these doors to prevent locks when flooding for door in [ @@ -145,7 +188,7 @@ def underworld_glitches_rules(world, player): "dungeonscrossed", ]: rule_map = { - "Mire Portal": (lambda state: True), + "Mire Portal": (lambda state: state.can_reach("Mire Torches Top", "Entrance", player)), "Hera Portal": (lambda state: state.can_reach("Hera Startile Corner NW", "Entrance", player)), } inverted = world.mode[player] == "inverted" @@ -167,5 +210,20 @@ def underworld_glitches_rules(world, player): mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) Rules.set_rule(mire_to_hera, mire_clip) Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player)) + dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit") dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") + dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert West Portal", "Swamp Palace Exit") + dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert South Portal", "Swamp Palace Exit") + dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert East Portal", "Swamp Palace Exit") + + # Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up + paradox_left_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Middle'] + for location in paradox_left_chests: + Rules.add_rule(world.get_location(location, player), lambda state: state.can_dash_clip(world.get_location(location, player)), 'or') + + # Collecting right chests in Paradox Cave using a dash clip on left side -> dash citrus, 1f right, teleport up, then hitting the switch + paradox_right_chests = ['Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right'] + for location in paradox_right_chests: + Rules.add_rule(world.get_location(location, player), lambda state: (state.can_dash_clip(world.get_location(location, player)) and state.can_hit_crystal(player)), 'or') + From 4de3544e0d2c528e92ac3ed44e96476ccd717722 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Thu, 2 Mar 2023 11:10:11 +0100 Subject: [PATCH 078/158] Syntax fixes --- UnderworldGlitchRules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index a7988d52..13acc599 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -220,10 +220,10 @@ def underworld_glitches_rules(world, player): # Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up paradox_left_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Middle'] for location in paradox_left_chests: - Rules.add_rule(world.get_location(location, player), lambda state: state.can_dash_clip(world.get_location(location, player)), 'or') + Rules.add_rule(world.get_location(location, player), lambda state: state.can_dash_clip(world.get_location(location, player).parent_region, player), 'or') # Collecting right chests in Paradox Cave using a dash clip on left side -> dash citrus, 1f right, teleport up, then hitting the switch paradox_right_chests = ['Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right'] for location in paradox_right_chests: - Rules.add_rule(world.get_location(location, player), lambda state: (state.can_dash_clip(world.get_location(location, player)) and state.can_hit_crystal(player)), 'or') + Rules.add_rule(world.get_location(location, player), lambda state: (state.can_dash_clip(world.get_location(location, player).parent_region, player) and state.can_hit_crystal(player)), 'or') From 0f28cd97d8bc2482584c726584a25521ecefacef Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sat, 1 Apr 2023 22:11:47 +0200 Subject: [PATCH 079/158] Logic fixes --- UnderworldGlitchRules.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 13acc599..4b8e248f 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -153,11 +153,18 @@ def underworld_glitches_rules(world, player): def hera_clip(state): return state.can_reach("Hera 4F", "Region", player) and state.can_dash_clip(world.get_region("Hera 4F", player), player) + Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") - Rules.add_rule(world.get_entrance("Thieves to Desert Clip", player), lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player), combine="or") - + mire_to_hera = world.get_entrance("Mire to Hera Clip", player) + mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) + Rules.set_rule(mire_to_hera, mire_clip) + Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player)) + + # Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys + dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit") + dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots # Flippers required for all of these doors to prevent locks when flooding for door in [ @@ -173,7 +180,8 @@ def underworld_glitches_rules(world, player): "Swamp Waterway NW", "Swamp T SW", ]: - Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") + # Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") + Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has('Flippers', player), combine="or") Rules.add_rule(world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or") @@ -205,17 +213,11 @@ def underworld_glitches_rules(world, player): Rules.add_rule(world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or") - # Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys - mire_to_hera = world.get_entrance("Mire to Hera Clip", player) - mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) - Rules.set_rule(mire_to_hera, mire_clip) - Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player)) - - dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit") - dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") - dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert West Portal", "Swamp Palace Exit") - dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert South Portal", "Swamp Palace Exit") - dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert East Portal", "Swamp Palace Exit") + # Thieves -> Hera + Rules.add_rule(world.get_entrance("Thieves to Desert Clip", player), lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player)) + dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert West Portal", "Desert Palace Exit (West)") + dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert South Portal", "Desert Palace Exit (South)") + dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert East Portal", "Desert Palace Exit (East)") # Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up paradox_left_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Middle'] From 98702176980261798d3a816f25675dd2c9aaadf5 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sun, 2 Apr 2023 09:48:38 +0200 Subject: [PATCH 080/158] Undo test --- UnderworldGlitchRules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 4b8e248f..98eb6626 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -180,8 +180,8 @@ def underworld_glitches_rules(world, player): "Swamp Waterway NW", "Swamp T SW", ]: - # Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") - Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has('Flippers', player), combine="or") + Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") + # Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has('Flippers', player), combine="or") Rules.add_rule(world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or") From e1f2369f13f444aa443e99671f0be5f52f7d5a6c Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Mon, 4 Dec 2023 19:43:13 +0100 Subject: [PATCH 081/158] Refactor OWG rules to use lists rather than generators --- OverworldGlitchRules.py | 467 +++++++++++++++++++++------------------- Rules.py | 16 +- 2 files changed, 259 insertions(+), 224 deletions(-) diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index 890e8420..ba44ba25 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -4,261 +4,296 @@ Helper functions to deliver entrance/exit/region sets to OWG rules. from BaseClasses import Entrance +# Cave regions that superbunny can get through - but only with a sword. +sword_required_superbunny_mirror_regions = ["Spiral Cave (Top)"] -def get_sword_required_superbunny_mirror_regions(): - """ - Cave regions that superbunny can get through - but only with a sword. - """ - yield 'Spiral Cave (Top)' +# Cave regions that superbunny can get through - but only with boots. +boots_required_superbunny_mirror_regions = ["Two Brothers House"] -def get_boots_required_superbunny_mirror_regions(): - """ - Cave regions that superbunny can get through - but only with boots. - """ - yield 'Two Brothers House' +# Cave locations that superbunny can access - but only with boots. +boots_required_superbunny_mirror_locations = [ + "Sahasrahla's Hut - Left", + "Sahasrahla's Hut - Middle", + "Sahasrahla's Hut - Right", +] -def get_boots_required_superbunny_mirror_locations(): - """ - Cave locations that superbunny can access - but only with boots. - """ - yield 'Sahasrahla\'s Hut - Left' - yield 'Sahasrahla\'s Hut - Middle' - yield 'Sahasrahla\'s Hut - Right' +# Entrances that can't be superbunny-mirrored into. +invalid_mirror_bunny_entrances = [ + "Skull Woods Final Section", + "Hype Cave", + "Bonk Fairy (Dark)", + "Thieves Town", + "Hammer Peg Cave", + "Brewery", + "Hookshot Cave", + "Dark Lake Hylia Ledge Fairy", + "Dark Lake Hylia Ledge Spike Cave", + "Palace of Darkness", + "Misery Mire", + "Turtle Rock", + "Bonk Rock Cave", + "Bonk Fairy (Light)", + "50 Rupee Cave", + "20 Rupee Cave", + "Checkerboard Cave", + "Light Hype Fairy", + "Waterfall of Wishing", + "Light World Bomb Hut", + "Mini Moldorm Cave", + "Ice Rod Cave", + "Sanctuary Grave", + "Kings Grave", + "Sanctuary Grave", + "Hyrule Castle Secret Entrance Drop", + "Skull Woods Second Section Hole", + "Skull Woods First Section Hole (North)", +] + +# Interior locations that can be accessed with superbunny state. +superbunny_accessible_locations = [ + "Waterfall of Wishing - Left", + "Waterfall of Wishing - Right", + "King's Tomb", + "Floodgate", + "Floodgate Chest", + "Cave 45", + "Bonk Rock Cave", + "Brewery", + "C-Shaped House", + "Chest Game", + "Mire Shed - Left", + "Mire Shed - Right", + "Secret Passage", + "Ice Rod Cave", + "Pyramid Fairy - Left", + "Pyramid Fairy - Right", + "Superbunny Cave - Top", + "Superbunny Cave - Bottom", + "Blind's Hideout - Left", + "Blind's Hideout - Right", + "Blind's Hideout - Far Left", + "Blind's Hideout - Far Right", + "Kakariko Well - Left", + "Kakariko Well - Middle", + "Kakariko Well - Right", + "Kakariko Well - Bottom", + "Kakariko Tavern", + "Library", + "Spiral Cave", +] + boots_required_superbunny_mirror_locations -def get_invalid_mirror_bunny_entrances(): - """ - Entrances that can't be superbunny-mirrored into. - """ - yield 'Skull Woods Final Section' - yield 'Hype Cave' - yield 'Bonk Fairy (Dark)' - yield 'Thieves Town' - yield 'Hammer Peg Cave' - yield 'Brewery' - yield 'Hookshot Cave' - yield 'Dark Lake Hylia Ledge Fairy' - yield 'Dark Lake Hylia Ledge Spike Cave' - yield 'Palace of Darkness' - yield 'Misery Mire' - yield 'Turtle Rock' - yield 'Bonk Rock Cave' - yield 'Bonk Fairy (Light)' - yield '50 Rupee Cave' - yield '20 Rupee Cave' - yield 'Checkerboard Cave' - yield 'Light Hype Fairy' - yield 'Waterfall of Wishing' - yield 'Light World Bomb Hut' - yield 'Mini Moldorm Cave' - yield 'Ice Rod Cave' - yield 'Sanctuary Grave' - yield 'Kings Grave' - yield 'Sanctuary Grave' - yield 'Hyrule Castle Secret Entrance Drop' - yield 'Skull Woods Second Section Hole' - yield 'Skull Woods First Section Hole (North)' +# Entrances that can be reached with full equipment using overworld glitches and don't need to be an exit. +# The following are still be mandatory exits: +# Open: +# Turtle Rock Isolated Ledge Entrance +# Skull Woods Second Section Door (West) (or Skull Woods Final Section) +# Inverted: +# Two Brothers House (West) +# Desert Palace Entrance (East) -def get_superbunny_accessible_locations(): - """ - Interior locations that can be accessed with superbunny state. - """ +non_mandatory_exits = [ + "Bumper Cave (Top)", + "Death Mountain Return Cave (West)", + "Hookshot Cave Back Entrance", +] - yield 'Waterfall of Wishing - Left' - yield 'Waterfall of Wishing - Right' - yield 'King\'s Tomb' - yield 'Floodgate' - yield 'Floodgate Chest' - yield 'Cave 45' - yield 'Bonk Rock Cave' - yield 'Brewery' - yield 'C-Shaped House' - yield 'Chest Game' - yield 'Mire Shed - Left' - yield 'Mire Shed - Right' - yield 'Secret Passage' - yield 'Ice Rod Cave' - yield 'Pyramid Fairy - Left' - yield 'Pyramid Fairy - Right' - yield 'Superbunny Cave - Top' - yield 'Superbunny Cave - Bottom' - yield 'Blind\'s Hideout - Left' - yield 'Blind\'s Hideout - Right' - yield 'Blind\'s Hideout - Far Left' - yield 'Blind\'s Hideout - Far Right' - yield 'Kakariko Well - Left' - yield 'Kakariko Well - Middle' - yield 'Kakariko Well - Right' - yield 'Kakariko Well - Bottom' - yield 'Kakariko Tavern' - yield 'Library' - yield 'Spiral Cave' - for location in get_boots_required_superbunny_mirror_locations(): - yield location +inverted_non_mandatory_exits = [ + "Desert Palace Entrance (North)", + "Desert Palace Entrance (West)", + "Agahnims Tower", + "Hyrule Castle Entrance (West)", + "Hyrule Castle Entrance (East)", +] + non_mandatory_exits + +open_non_mandatory_exits_ = [ + "Dark Death Mountain Ledge (West)", + "Dark Death Mountain Ledge (East)", + "Mimic Cave", + "Desert Palace Entrance (East)", +] + non_mandatory_exits -def get_non_mandatory_exits(inverted): - """ - Entrances that can be reached with full equipment using overworld glitches and don't need to be an exit. - The following are still be mandatory exits: +# Special Light World region exits that require boots clips. - Open: - Turtle Rock Isolated Ledge Entrance - Skull Woods Second Section Door (West) (or Skull Woods Final Section) +inverted_boots_clip_exits_lw = [ + ("Light World DMA Clip Spot", "Light World", "West Death Mountain (Bottom)"), + ("Hera Ascent", "West Death Mountain (Bottom)", "West Death Mountain (Top)"), + ("Death Mountain Return Ledge Clip Spot", "Light World", "Death Mountain Return Ledge"), + ("Death Mountain Entrance Clip Spot", "Light World", "Death Mountain Entrance"), + ("Death Mountain Glitched Bridge", "West Death Mountain (Bottom)", "East Death Mountain (Top)"), + ("Zora Descent Clip Spot", "East Death Mountain (Top)", "Zoras Domain"), + ("Desert Northern Cliffs", "Light World", "Desert Northern Cliffs"), + ("Desert Ledge Dropdown", "Desert Northern Cliffs", "Desert Ledge"), + ("Desert Palace Entrance Dropdown", "Desert Northern Cliffs", "Desert Palace Entrance (North) Spot"), + ("Lake Hylia Island Clip Spot", "Light World", "Lake Hylia Island"), + ("Death Mountain Descent", "West Death Mountain (Bottom)", "Light World"), + ("Kings Grave Clip Spot", "West Death Mountain (Bottom)", "Kings Grave Area"), + ("Maze Race Clip Spot", "Light World", "Maze Race Ledge"), +] - Inverted: - Two Brothers House (West) - Desert Palace Entrance (East) - """ +open_boots_clip_exits_lw = [ + ("Graveyard Ledge Clip Spot", "West Death Mountain (Bottom)", "Graveyard Ledge"), + ("Desert Ledge (Northeast) Dropdown", "Desert Northern Cliffs", "Desert Checkerboard Ledge"), + ("Spectacle Rock Clip Spot", "West Death Mountain (Top)", "Spectacle Rock"), + ("Bombos Tablet Clip Spot", "Light World", "Bombos Tablet Ledge"), + ("Floating Island Clip Spot", "East Death Mountain (Top)", "Death Mountain Floating Island"), + ("Cave 45 Clip Spot", "Light World", "Cave 45 Ledge"), +] + inverted_boots_clip_exits_lw - yield 'Bumper Cave (Top)' - yield 'Death Mountain Return Cave (West)' - yield 'Hookshot Cave Back Entrance' +# Special Dark World region exits that require boots clips. +boots_clip_exits_dw = [ + ("Dark World DMA Clip Spot", "West Dark World", "West Dark Death Mountain (Bottom)"), + ("Bumper Cave Ledge Clip Spot", "West Dark World", "Bumper Cave Ledge"), + ("Bumper Cave Entrance Clip Spot", "West Dark World", "Bumper Cave Entrance"), + ("Catfish Descent", "Dark Death Mountain (Top)", "Catfish Area"), + ("Hammer Pegs River Clip Spot", "East Dark World", "Hammer Peg Area"), + ("Dark Lake Hylia Ledge Clip Spot", "East Dark World", "Southeast Dark World"), + ("Dark Desert Cliffs Clip Spot", "South Dark World", "Dark Desert"), + ("DW Floating Island Clip Spot", "East Dark Death Mountain (Bottom)", "Dark Death Mountain Floating Island"), +] - if inverted: - yield 'Desert Palace Entrance (North)' - yield 'Desert Palace Entrance (West)' - yield 'Agahnims Tower' - yield 'Hyrule Castle Entrance (West)' - yield 'Hyrule Castle Entrance (East)' - else: - yield 'Dark Death Mountain Ledge (West)' - yield 'Dark Death Mountain Ledge (East)' - yield 'Mimic Cave' - yield 'Desert Palace Entrance (East)' +open_boots_clip_exits_dw = [ + ("Dark Death Mountain Descent", "West Dark Death Mountain (Bottom)", "West Dark World"), + ("Ganons Tower Ascent", "West Dark Death Mountain (Bottom)", "Dark Death Mountain (Top)"), + ("Dark Death Mountain Glitched Bridge", "West Dark Death Mountain (Bottom)", "Dark Death Mountain (Top)"), + ("Turtle Rock (Top) Clip Spot", "Dark Death Mountain (Top)", "Turtle Rock (Top)"), +] + boots_clip_exits_dw + +inverted_boots_clip_exits_dw = [ + ("Dark Desert Teleporter Clip Spot", "Dark Desert", "Dark Desert Ledge") +] + boots_clip_exits_dw -def get_boots_clip_exits_lw(inverted = False): - """ - Special Light World region exits that require boots clips. - """ - - yield ('Bat Cave River Clip Spot', 'Light World', 'Bat Cave Ledge') - yield ('Light World DMA Clip Spot', 'Light World', 'West Death Mountain (Bottom)') - yield ('Hera Ascent', 'West Death Mountain (Bottom)', 'West Death Mountain (Top)') - yield ('Death Mountain Return Ledge Clip Spot', 'Light World', 'Death Mountain Return Ledge') - yield ('Death Mountain Entrance Clip Spot', 'Light World', 'Death Mountain Entrance') - yield ('Death Mountain Glitched Bridge', 'West Death Mountain (Bottom)', 'East Death Mountain (Top)') - yield ('Zora Descent Clip Spot', 'East Death Mountain (Top)', 'Zoras Domain') - yield ('Desert Northern Cliffs', 'Light World', 'Desert Northern Cliffs') - yield ('Desert Ledge Dropdown', 'Desert Northern Cliffs', 'Desert Ledge') - yield ('Desert Palace Entrance Dropdown', 'Desert Northern Cliffs', 'Desert Palace Entrance (North) Spot') - yield ('Lake Hylia Island Clip Spot', 'Light World', 'Lake Hylia Island') - yield ('Death Mountain Descent', 'West Death Mountain (Bottom)', 'Light World') - yield ('Kings Grave Clip Spot', 'West Death Mountain (Bottom)', 'Kings Grave Area') - - if not inverted: - yield ('Graveyard Ledge Clip Spot', 'West Death Mountain (Bottom)', 'Graveyard Ledge') - yield ('Desert Ledge (Northeast) Dropdown', 'Desert Northern Cliffs', 'Desert Checkerboard Ledge') - yield ('Spectacle Rock Clip Spot', 'West Death Mountain (Top)', 'Spectacle Rock') - yield ('Bombos Tablet Clip Spot', 'Light World', 'Bombos Tablet Ledge') - yield ('Floating Island Clip Spot', 'East Death Mountain (Top)', 'Death Mountain Floating Island') - yield ('Cave 45 Clip Spot', 'Light World', 'Cave 45 Ledge') +# Dark World drop-down ledges that require glitched speed. +glitched_speed_drops_dw = [ + ("Dark Death Mountain Ledge Clip Spot", "Dark Death Mountain (Top)", "Dark Death Mountain Ledge") +] -def get_boots_clip_exits_dw(inverted): - """ - Special Dark World region exits that require boots clips. - """ +# Out of bounds transitions using the mirror +mirror_clip_spots_dw = [ + ("Dark Death Mountain Bunny Descent Mirror Spot", "West Dark Death Mountain (Bottom)", "West Dark World"), + ( + "Dark Death Mountain Bunny Mirror To East Jump", + "West Dark Death Mountain (Bottom)", + "East Dark Death Mountain (Bottom)", + ), + ("Desert East Mirror Clip", "Dark Desert", "Desert Palace Mouth"), +] - yield ('Dark World DMA Clip Spot', 'West Dark World', 'West Dark Death Mountain (Bottom)') - yield ('Bumper Cave Ledge Clip Spot', 'West Dark World', 'Bumper Cave Ledge') - yield ('Bumper Cave Entrance Clip Spot', 'West Dark World', 'Bumper Cave Entrance') - yield ('Catfish Descent', 'Dark Death Mountain (Top)', 'Catfish Area') - yield ('Hammer Pegs River Clip Spot', 'East Dark World', 'Hammer Peg Area') - yield ('Dark Lake Hylia Ledge Clip Spot', 'East Dark World', 'Southeast Dark World') - yield ('Dark Desert Cliffs Clip Spot', 'South Dark World', 'Dark Desert') - yield ('DW Floating Island Clip Spot', 'East Dark Death Mountain (Bottom)', 'Dark Death Mountain Floating Island') +# Mirror shenanigans placing a mirror portal with a broken camera +mirror_offset_spots_dw = [("Dark Death Mountain Offset Mirror", "West Dark Death Mountain (Bottom)", "East Dark World")] - if not inverted: - yield ('Dark Death Mountain Descent', 'West Dark Death Mountain (Bottom)', 'West Dark World') - yield ('Ganons Tower Ascent', 'West Dark Death Mountain (Bottom)', 'Dark Death Mountain (Top)') # This only gets you to the GT entrance - yield ('Dark Death Mountain Glitched Bridge', 'West Dark Death Mountain (Bottom)', 'Dark Death Mountain (Top)') - yield ('Turtle Rock (Top) Clip Spot', 'Dark Death Mountain (Top)', 'Turtle Rock (Top)') - else: - yield ('Dark Desert Teleporter Clip Spot', 'Dark Desert', 'Dark Desert Ledge') +# Mirror shenanigans placing a mirror portal with a broken camera - -def get_glitched_speed_drops_dw(inverted = False): - """ - Dark World drop-down ledges that require glitched speed. - """ - yield ('Dark Death Mountain Ledge Clip Spot', 'Dark Death Mountain (Top)', 'Dark Death Mountain Ledge') - - -def get_mirror_clip_spots_dw(): - """ - Out of bounds transitions using the mirror - """ - yield ('Dark Death Mountain Bunny Descent Mirror Spot', 'West Dark Death Mountain (Bottom)', 'West Dark World') - yield ('Dark Death Mountain Bunny Mirror To East Jump', 'West Dark Death Mountain (Bottom)', 'East Dark Death Mountain (Bottom)') - yield ('Desert East Mirror Clip', 'Dark Desert', 'Desert Palace Mouth') - - -def get_mirror_offset_spots_dw(): - """ - Mirror shenanigans placing a mirror portal with a broken camera - """ - yield ('Dark Death Mountain Offset Mirror', 'West Dark Death Mountain (Bottom)', 'East Dark World') - - -def get_mirror_offset_spots_lw(player): - """ - Mirror shenanigans placing a mirror portal with a broken camera - """ - yield ('Death Mountain Offset Mirror', 'West Death Mountain (Bottom)', 'Light World') - yield ('Death Mountain Uncle Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Secret Entrance Area') - yield ('Death Mountain Castle Ledge Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Ledge') +mirror_offset_spots_lw = [ + ("Death Mountain Offset Mirror", "West Death Mountain (Bottom)", "Light World"), + ("Death Mountain Uncle Offset Mirror", "West Death Mountain (Bottom)", "Hyrule Castle Secret Entrance Area"), + ("Death Mountain Castle Ledge Offset Mirror", "West Death Mountain (Bottom)", "Hyrule Castle Ledge"), +] def create_owg_connections(world, player): """ Add OWG transitions to player's world without logic """ - create_no_logic_connections(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted')) - create_no_logic_connections(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted')) - - # Glitched speed drops. - create_no_logic_connections(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted')) - - # Mirror clip spots. - if world.mode[player] != 'inverted': - create_no_logic_connections(player, world, get_mirror_clip_spots_dw()) - create_no_logic_connections(player, world, get_mirror_offset_spots_dw()) + if world.mode[player] == "inverted": + connections = ( + inverted_boots_clip_exits_dw + + inverted_boots_clip_exits_lw + + glitched_speed_drops_dw + + mirror_offset_spots_lw + ) else: - create_no_logic_connections(player, world, get_mirror_offset_spots_lw(player)) + connections = ( + open_boots_clip_exits_dw + + open_boots_clip_exits_lw + + glitched_speed_drops_dw + + mirror_clip_spots_dw + + mirror_offset_spots_dw + ) + + create_no_logic_connections(player, world, connections) def overworld_glitches_rules(world, player): - # Boots-accessible locations. - set_owg_rules(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_lw(player)) - set_owg_rules(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_dw(player)) + inverted = world.mode[player] == "inverted" + # Boots-accessible locations. + set_owg_rules( + player, + world, + inverted_boots_clip_exits_lw if inverted else open_boots_clip_exits_lw, + lambda state: state.can_boots_clip_lw(player), + ) + set_owg_rules( + player, + world, + inverted_boots_clip_exits_dw if inverted else open_boots_clip_exits_dw, + lambda state: state.can_boots_clip_dw(player), + ) # Glitched speed drops. - set_owg_rules(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted'), lambda state: state.can_get_glitched_speed_dw(player)) + set_owg_rules( + player, + world, + glitched_speed_drops_dw, + lambda state: state.can_get_glitched_speed_dw(player), + ) # Dark Death Mountain Ledge Clip Spot also accessible with mirror. - if world.mode[player] != 'inverted': - add_alternate_rule(world.get_entrance('Dark Death Mountain Ledge Clip Spot', player), lambda state: state.has_Mirror(player)) + if not inverted: + add_alternate_rule( + world.get_entrance("Dark Death Mountain Ledge Clip Spot", player), lambda state: state.has_Mirror(player) + ) # Mirror clip spots. - if world.mode[player] != 'inverted': - set_owg_rules(player, world, get_mirror_clip_spots_dw(), lambda state: state.has_Mirror(player)) - set_owg_rules(player, world, get_mirror_offset_spots_dw(), lambda state: state.has_Mirror(player) and state.can_boots_clip_lw(player)) + if inverted: + set_owg_rules( + player, + world, + mirror_offset_spots_lw, + lambda state: state.has_Mirror(player) and state.can_boots_clip_dw(player), + ) else: - set_owg_rules(player, world, get_mirror_offset_spots_lw(player), lambda state: state.has_Mirror(player) and state.can_boots_clip_dw(player)) - + set_owg_rules(player, world, mirror_clip_spots_dw, lambda state: state.has_Mirror(player)) + set_owg_rules( + player, + world, + mirror_offset_spots_dw, + lambda state: state.has_Mirror(player) and state.can_boots_clip_lw(player), + ) + # Regions that require the boots and some other stuff. - if world.mode[player] != 'inverted': - world.get_entrance('Turtle Rock Teleporter', player).access_rule = lambda state: (state.can_boots_clip_lw(player) or state.can_lift_heavy_rocks(player)) and state.has('Hammer', player) - add_alternate_rule(world.get_entrance('Waterfall Fairy Access', player), lambda state: state.has_Pearl(player) or state.has_Boots(player)) # assumes access to Waterwalk ability (boots case) - else: - add_alternate_rule(world.get_entrance('Waterfall Fairy Access', player), lambda state: state.has_Pearl(player)) + if not inverted: + world.get_entrance("Turtle Rock Teleporter", player).access_rule = lambda state: ( + state.can_boots_clip_lw(player) or state.can_lift_heavy_rocks(player) + ) and state.has("Hammer", player) - world.get_entrance('Dark Desert Teleporter', player).access_rule = lambda state: (state.can_flute(player) or state.can_boots_clip_dw(player)) and state.can_lift_heavy_rocks(player) - add_alternate_rule(world.get_entrance('Dark Witch Rock (North)', player), lambda state: state.can_boots_clip_dw(player)) - add_alternate_rule(world.get_entrance('Broken Bridge Pass (Top)', player), lambda state: state.can_boots_clip_dw(player)) - add_alternate_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.can_boots_clip_lw(player)) # assumes access to Waterwalk ability + add_alternate_rule( + world.get_entrance("Waterfall Fairy Access", player), + lambda state: state.has_Pearl(player) or state.has_Boots(player), + ) # assumes access to Waterwalk ability (boots case) + else: + add_alternate_rule(world.get_entrance("Waterfall Fairy Access", player), lambda state: state.has_Pearl(player)) + + world.get_entrance("Dark Desert Teleporter", player).access_rule = lambda state: ( + state.can_flute(player) or state.can_boots_clip_dw(player) + ) and state.can_lift_heavy_rocks(player) + + add_alternate_rule( + world.get_entrance("Dark Witch Rock (North)", player), lambda state: state.can_boots_clip_dw(player) + ) + add_alternate_rule( + world.get_entrance("Broken Bridge Pass (Top)", player), lambda state: state.can_boots_clip_dw(player) + ) + add_alternate_rule( + world.get_location("Zora's Ledge", player), lambda state: state.can_boots_clip_lw(player) + ) # assumes access to Waterwalk ability + + # This is doable even with bad enemies + add_alternate_rule(world.get_location("Hobo", player), lambda state: state.can_boots_clip_lw(player)) + def add_alternate_rule(entrance, rule): diff --git a/Rules.py b/Rules.py index 6e1f40f8..94100773 100644 --- a/Rules.py +++ b/Rules.py @@ -1905,7 +1905,7 @@ def set_bunny_rules(world, player, inverted): # bunny revival accessible. if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type != RegionType.Dungeon \ - and (location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations()) \ + and (location is None or location.name not in OverworldGlitchRules.superbunny_accessible_locations) \ and not is_link(region): return lambda state: state.has_Pearl(player) else: @@ -1935,7 +1935,7 @@ def set_bunny_rules(world, player, inverted): if not is_link(new_region): if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: - if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): + if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue if entrance.name in drop_dungeon_entrances: lobby = entrance.connected_region @@ -1949,21 +1949,21 @@ def set_bunny_rules(world, player, inverted): possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance)) continue elif region.type == RegionType.Cave and new_region.type != RegionType.Cave: - if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): + if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue - if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions(): + if region.name in OverworldGlitchRules.sword_required_superbunny_mirror_regions: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance)) - elif region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions(): + elif region.name in OverworldGlitchRules.boots_required_superbunny_mirror_regions: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance)) - elif location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations(): - if location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations(): + elif location and location.name in OverworldGlitchRules.superbunny_accessible_locations: + if location.name in OverworldGlitchRules.boots_required_superbunny_mirror_locations: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance)) elif region.name == 'Kakariko Well (top)': possible_options.append(path_to_access_rule(new_path, entrance)) else: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player)], entrance)) continue - elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations(): + elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.superbunny_accessible_locations: possible_options.append(path_to_access_rule(new_path, entrance)) else: continue From 15558250da200dea19593f4a39185060744d68e9 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:43:55 +0100 Subject: [PATCH 082/158] Change Maze race to collect location rather than reach region - Brother West is not reachable from the clip --- OverworldGlitchRules.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index ba44ba25..9ee11a29 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -130,7 +130,6 @@ inverted_boots_clip_exits_lw = [ ("Lake Hylia Island Clip Spot", "Light World", "Lake Hylia Island"), ("Death Mountain Descent", "West Death Mountain (Bottom)", "Light World"), ("Kings Grave Clip Spot", "West Death Mountain (Bottom)", "Kings Grave Area"), - ("Maze Race Clip Spot", "Light World", "Maze Race Ledge"), ] open_boots_clip_exits_lw = [ @@ -291,6 +290,10 @@ def overworld_glitches_rules(world, player): world.get_location("Zora's Ledge", player), lambda state: state.can_boots_clip_lw(player) ) # assumes access to Waterwalk ability + add_alternate_rule( + world.get_location('Maze Race', player), lambda state: state.can_boots_clip_lw(player) + ) + # This is doable even with bad enemies add_alternate_rule(world.get_location("Hobo", player), lambda state: state.can_boots_clip_lw(player)) From 7f5fb1645360cda83078f270a62b7c0709a49d44 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:44:48 +0100 Subject: [PATCH 083/158] Refactor hmg key logic --- Rules.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Rules.py b/Rules.py index 94100773..f4de86c2 100644 --- a/Rules.py +++ b/Rules.py @@ -737,6 +737,9 @@ def global_rules(world, player): set_rule(world.get_entrance('GT Crystal Circles to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_blunt_weapon(player) or state.has('Cane of Byrna', player)) # or state.has_beam_sword(player) add_key_logic_rules(world, player) + + if world.logic[player] == 'hybridglitches': + add_hmg_key_logic_rules(world, player) # End of door rando rules. if world.restrict_boss_items[player] != 'none': @@ -2142,6 +2145,11 @@ bunny_impassible_if_trapped = { 'GT Speed Torch WN', 'Ice Lobby SE' } +def add_hmg_key_logic_rules(world, player): + for toh_loc in world.key_logic[player]['Tower of Hera'].bk_restricted: + set_always_allow(world.get_location(toh_loc.name, player), allow_big_key_in_big_chest('Big Key (Tower of Hera)', player)) + set_always_allow(world.get_location('Swamp Palace - Entrance', player), allow_big_key_in_big_chest('Big Key (Swamp Palace)', player)) + def add_key_logic_rules(world, player): key_logic = world.key_logic[player] @@ -2159,9 +2167,6 @@ def add_key_logic_rules(world, player): add_rule(dep.entrance, eval_func(door_name, d_name, player)) for location in d_logic.bk_restricted: if not location.forced_item: - # Skip BK restricted locations in hybrid glitches. Bad, but necessary for now. - if world.logic[player] == 'hybridglitches' and d_name in ['Tower of Hera', 'Swamp Palace']: - continue forbid_item(location, d_logic.bk_name, player) for location in d_logic.sm_restricted: forbid_item(location, d_logic.small_key_name, player) From 6a41dff98b55b009a1eb1806cefc8240c5dee0e8 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:45:29 +0100 Subject: [PATCH 084/158] Raise error on doors + hmg --- Main.py | 4 ++++ resources/app/cli/lang/en.json | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Main.py b/Main.py index b273d922..99b257e2 100644 --- a/Main.py +++ b/Main.py @@ -79,6 +79,10 @@ def main(args, seed=None, fish=None): seed = customized.determine_seed(seed) seeded = True customized.adjust_args(args) + for i in zip(args.logic.values(), args.door_shuffle.values()): + if i[0] == 'hybridglitches' and i[1] != 'vanilla': + raise RuntimeError(BabelFish().translate("cli","cli","hybridglitches.door.shuffle")) + # print(args) world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.custom, args.customitemarray, args.hints) diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 3cd6aa19..1f0defb9 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -55,7 +55,8 @@ "building.collection.spheres": "Building up collection spheres", "building.calculating.spheres": "Calculated sphere %i, containing %i of %i progress items.", "building.final.spheres": "Calculated final sphere %i, containing %i of %i progress items.", - "old.python.version": "Door Rando may have issues with python versions earlier than 3.7. Detected version: %s" + "old.python.version": "Door Rando may have issues with python versions earlier than 3.7. Detected version: %s", + "hybridglitches.door.shuffle": "Hybrid Major Glitches is not currently compatible with Door Shuffle." }, "help": { "lang": [ "App Language, if available, defaults to English" ], From 6510968401ce9754f5b05964aab66901976f90e8 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:48:12 +0100 Subject: [PATCH 085/158] Support bunny pocket for SW back and voo hammer house --- BaseClasses.py | 39 +++++++++++++++++++++++++++++++++++++++ OverworldGlitchRules.py | 10 +++++++--- Rules.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index cff79a25..fe1f3c96 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -550,6 +550,42 @@ class CollectionState(object): self.placing_items = None # self.trace = None + def can_reach_from(self, spot, start, player=None): + old_state = self.copy() + # old_state.path = {old_state.world.get_region(start, player)} + old_state.stale[player] = False + old_state.reachable_regions[player] = dict() + old_state.blocked_connections[player] = dict() + rrp = old_state.reachable_regions[player] + bc = old_state.blocked_connections[player] + + # init on first call - this can't be done on construction since the regions don't exist yet + start = self.world.get_region(start, player) + if start in self.reachable_regions[player]: + rrp[start] = self.reachable_regions[player][start] + for conn in start.exits: + bc[conn] = self.blocked_connections[player][conn] + else: + rrp[start] = CrystalBarrier.Orange + for conn in start.exits: + bc[conn] = CrystalBarrier.Orange + + queue = deque(old_state.blocked_connections[player].items()) + + old_state.traverse_world(queue, rrp, bc, player) + if old_state.world.key_logic_algorithm[player] == 'default': + unresolved_events = [x for y in old_state.reachable_regions[player] for x in y.locations + if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement) + and x not in old_state.locations_checked and x.can_reach(old_state)] + unresolved_events = old_state._do_not_flood_the_keys(unresolved_events) + if len(unresolved_events) == 0: + old_state.check_key_doors_in_dungeons(rrp, player) + + if self.world.get_region(spot, player) in rrp: + return True + else: + return False + def update_reachable_regions(self, player): self.stale[player] = False rrp = self.reachable_regions[player] @@ -1274,6 +1310,9 @@ class CollectionState(object): def can_superbunny_mirror_with_sword(self, player): return self.has_Mirror(player) and self.has_sword(player) + + def can_bunny_pocket(self, player): + return self.has_Boots(player) and (self.has_Mirror(player) or self.has_bottle(player)) def collect(self, item, event=False, location=None): if location: diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index 9ee11a29..7e03b64c 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -19,7 +19,6 @@ boots_required_superbunny_mirror_locations = [ # Entrances that can't be superbunny-mirrored into. invalid_mirror_bunny_entrances = [ - "Skull Woods Final Section", "Hype Cave", "Bonk Fairy (Dark)", "Thieves Town", @@ -107,7 +106,7 @@ inverted_non_mandatory_exits = [ "Hyrule Castle Entrance (East)", ] + non_mandatory_exits -open_non_mandatory_exits_ = [ +open_non_mandatory_exits = [ "Dark Death Mountain Ledge (West)", "Dark Death Mountain Ledge (East)", "Mimic Cave", @@ -296,7 +295,12 @@ def overworld_glitches_rules(world, player): # This is doable even with bad enemies add_alternate_rule(world.get_location("Hobo", player), lambda state: state.can_boots_clip_lw(player)) - + + # Bunny pocket + if not inverted: + add_alternate_rule(world.get_entrance("Skull Woods Final Section", player), lambda state: state.can_bunny_pocket(player) and state.has("Fire Rod", player)) + add_alternate_rule(world.get_entrance("Dark World Shop", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player)) + def add_alternate_rule(entrance, rule): diff --git a/Rules.py b/Rules.py index f4de86c2..7e414424 100644 --- a/Rules.py +++ b/Rules.py @@ -1902,6 +1902,34 @@ def set_bunny_rules(world, player, inverted): return region.is_dark_world else: return region.is_light_world + + # Is it possible to do bunny pocket here + def can_bunny_pocket_skull_woods(world, player): + # return world.get_entrance( + # "Skull Woods Second Section Door (West)", player + # ).connected_region.type != RegionType.Dungeon and ( + # not world.state.can_reach_from("Skull Woods Forest (West)", "Light World", 1) + # or not world.state.can_reach_from("Light World", "Skull Woods Forest (West)", 1) + # ) + return world.get_entrance( + "Skull Woods Second Section Door (West)", player + ).connected_region.type == RegionType.Dungeon or ( + world.state.can_reach_from("Skull Woods Forest (West)", "Light World", 1) + and world.state.can_reach_from("Light World", "Skull Woods Forest (West)", 1) + ) + + def can_bunny_pocket_voo_shop(world, player): + # return world.get_entrance( + # "Dark World Shop", player + # ).connected_region.type != RegionType.Dungeon and ( + # not world.state.can_reach_from("West Dark World", "Light World", 1) + # or not world.state.can_reach_from("Light World", "West Dark World", 1) + # ) + return ( + world.state.can_reach_from("West Dark World", "Light World", 1) + and world.state.can_reach_from("Light World", "West Dark World", 1) + ) + def get_rule_to_add(region, location=None, connecting_entrance=None): # In OWG, a location can potentially be superbunny-mirror accessible or @@ -1940,6 +1968,10 @@ def set_bunny_rules(world, player, inverted): if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue + # Is this a bunny pocketable entrance? + if entrance.name == 'Skull Woods Final Section' and not can_bunny_pocket_skull_woods(world, player) or \ + entrance.name == 'Dark World Shop' and not can_bunny_pocket_voo_shop(world, player): + continue if entrance.name in drop_dungeon_entrances: lobby = entrance.connected_region else: @@ -1954,6 +1986,9 @@ def set_bunny_rules(world, player, inverted): elif region.type == RegionType.Cave and new_region.type != RegionType.Cave: if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue + if entrance.name == 'Skull Woods Final Section' and not can_bunny_pocket_skull_woods(world, player) or \ + entrance.name == 'Dark World Shop' and not can_bunny_pocket_voo_shop(world, player): + continue if region.name in OverworldGlitchRules.sword_required_superbunny_mirror_regions: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance)) elif region.name in OverworldGlitchRules.boots_required_superbunny_mirror_regions: From a4a523ce4cc64ce3ea8512f74adabbc8d2adcf73 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:49:15 +0100 Subject: [PATCH 086/158] Small cleanup --- EntranceShuffle.py | 7 ++++--- source/overworld/EntranceShuffle2.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index a1e15991..020e95a7 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1106,7 +1106,8 @@ def connect_random(world, exitlist, targetlist, player, two_way=False): def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): # Keeps track of entrances that cannot be used to access each exit / cave - if world.mode[player] == 'inverted': + inverted = world.mode[player] == 'inverted' + if inverted: invalid_connections = Inverted_Must_Exit_Invalid_Connections.copy() else: invalid_connections = Must_Exit_Invalid_Connections.copy() @@ -1114,7 +1115,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: import OverworldGlitchRules - for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'): + for entrance in OverworldGlitchRules.inverted_non_mandatory_exits if inverted else OverworldGlitchRules.open_non_mandatory_exits: invalid_connections[entrance] = set() if entrance in must_be_exits: must_be_exits.remove(entrance) @@ -1125,7 +1126,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): random.shuffle(caves) # Handle inverted Aga Tower - if it depends on connections, then so does Hyrule Castle Ledge - if world.mode[player] == 'inverted': + if inverted: for entrance in invalid_connections: if world.get_entrance(entrance, player).connected_region == world.get_region('Agahnims Tower Portal', player): for exit in invalid_connections[entrance]: diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 9847e472..1d36e43c 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -959,7 +959,7 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): if avail.world.logic[avail.player] in ['owglitches', 'hybridglitches', 'nologic']: import OverworldGlitchRules - for entrance in OverworldGlitchRules.get_non_mandatory_exits(avail.inverted): + for entrance in OverworldGlitchRules.inverted_non_mandatory_exits if avail.inverted else OverworldGlitchRules.open_non_mandatory_exits: invalid_connections[entrance] = set() if entrance in must_exit: must_exit.remove(entrance) From e1a2e1bb5f40e0ebcd88b430186fecb8b2ef2de4 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:49:55 +0100 Subject: [PATCH 087/158] Only return lobbies rather than dungeon regions --- Rules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index 7e414424..f204e7ac 100644 --- a/Rules.py +++ b/Rules.py @@ -1975,7 +1975,8 @@ def set_bunny_rules(world, player, inverted): if entrance.name in drop_dungeon_entrances: lobby = entrance.connected_region else: - lobby = next(exit.connected_region for exit in current.exits if exit.connected_region.type == RegionType.Dungeon) + portal_regions = [world.get_region(reg, player) for reg in region.dungeon.regions if reg.endswith('Portal')] + lobby = next(reg.connected_region for portal_reg in portal_regions for reg in portal_reg.exits if reg.name.startswith('Enter ')) if lobby.name in bunny_revivable_entrances: possible_options.append(path_to_access_rule(new_path, entrance)) elif lobby.name in superbunny_revivable_entrances: From 6d79e48ab0436daae86e8b26304aa72c3a8b0d51 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:51:42 +0100 Subject: [PATCH 088/158] Don't explore dungeons with a single exit for bunny logic --- Rules.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Rules.py b/Rules.py index f204e7ac..3e1aa061 100644 --- a/Rules.py +++ b/Rules.py @@ -1860,6 +1860,8 @@ def set_inverted_big_bomb_rules(world, player): def set_bunny_rules(world, player, inverted): # regions for the exits of multi-entrace caves/drops that bunny cannot pass # Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing. + all_single_exit_dungeons = ['Eastern Palace', 'Tower of Hera', 'Castle Tower', 'Palace of Darkness', 'Swamp Palace', 'Thieves Town', 'Ice Palace', 'Misery Mire', 'Ganons Tower'] + hmg_single_exit_dungeons = [d for d in all_single_exit_dungeons if d not in ['Tower of Hera', 'Misery Mire', 'Thieves Town']] bunny_impassable_caves = ['Bumper Cave (top)', 'Bumper Cave (bottom)', 'Two Brothers House', 'Hookshot Cave (Middle)', 'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)'] bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', @@ -1961,6 +1963,13 @@ def set_bunny_rules(world, player, inverted): new_region = entrance.parent_region if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_region in seen: continue + # We don't need to navigate through single entrance dungeons. HMG has more multi-entrance dungeons + if (region.type == RegionType.Dungeon and new_region.type == RegionType.Dungeon): + if ( + world.logic[player] == 'hybridglitches' and new_region.dungeon != None and new_region.dungeon.name in hmg_single_exit_dungeons) or ( + world.logic[player] not in ['hybridglitches', 'nologic'] and new_region.dungeon != None and new_region.dungeon.name in all_single_exit_dungeons + ): + continue new_path = path + [entrance.access_rule] new_seen = seen.union({new_region}) if not is_link(new_region): From fce43eb2899454dc0257569b22d6eef88d89d5eb Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:55:51 +0100 Subject: [PATCH 089/158] HMG tests --- test/suite/hmg/bunny_pocket_sw.yaml | 34 ++++++++++++++++++++++ test/suite/hmg/fireless_ice.yaml | 17 +++++++++++ test/suite/hmg/hera_from_mire.yaml | 26 +++++++++++++++++ test/suite/hmg/moon_pearl_locs.yaml | 44 +++++++++++++++++++++++++++++ test/suite/hmg/pearlless_sw.yaml | 25 ++++++++++++++++ test/suite/hmg/swamp_from_mire.yaml | 30 ++++++++++++++++++++ 6 files changed, 176 insertions(+) create mode 100644 test/suite/hmg/bunny_pocket_sw.yaml create mode 100644 test/suite/hmg/fireless_ice.yaml create mode 100644 test/suite/hmg/hera_from_mire.yaml create mode 100644 test/suite/hmg/moon_pearl_locs.yaml create mode 100644 test/suite/hmg/pearlless_sw.yaml create mode 100644 test/suite/hmg/swamp_from_mire.yaml diff --git a/test/suite/hmg/bunny_pocket_sw.yaml b/test/suite/hmg/bunny_pocket_sw.yaml new file mode 100644 index 00000000..2b263db2 --- /dev/null +++ b/test/suite/hmg/bunny_pocket_sw.yaml @@ -0,0 +1,34 @@ +meta: + players: 1 + +settings: + 1: + logic: hybridglitches + shuffle: crossed +start_inventory: + 1: + - Flippers + - Pegasus Boots + - Progressive Sword + - Hammer + - Progressive Glove + - Progressive Glove + - Fire Rod + - Book of Mudora + - Bottle + - Magic Mirror + - Lamp +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Pyramid Fairy - Left: True +entrances: + 1: + entrances: + Skull Woods Final Section: Pyramid Fairy + two-way: + Thieves Town: Two Brothers House Exit (West) + Spectacle Rock Cave (Bottom): Two Brothers House Exit (East) + diff --git a/test/suite/hmg/fireless_ice.yaml b/test/suite/hmg/fireless_ice.yaml new file mode 100644 index 00000000..79b31f59 --- /dev/null +++ b/test/suite/hmg/fireless_ice.yaml @@ -0,0 +1,17 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Hammer + - Progressive Glove + - Progressive Glove +placements: + 1: + Ice Palace - Map Chest: Bombos + Ice Palace - Iced T Room: Fire Rod diff --git a/test/suite/hmg/hera_from_mire.yaml b/test/suite/hmg/hera_from_mire.yaml new file mode 100644 index 00000000..3e7d0c49 --- /dev/null +++ b/test/suite/hmg/hera_from_mire.yaml @@ -0,0 +1,26 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Big Key (Tower of Hera) + locations: + Tower of Hera - Big Key Chest: True + Tower of Hera - Basement Cage: True + Tower of Hera - Map Chest: True + Tower of Hera - Compass Chest: True + Tower of Hera - Big Chest: True + Tower of Hera - Boss: True \ No newline at end of file diff --git a/test/suite/hmg/moon_pearl_locs.yaml b/test/suite/hmg/moon_pearl_locs.yaml new file mode 100644 index 00000000..f5d06745 --- /dev/null +++ b/test/suite/hmg/moon_pearl_locs.yaml @@ -0,0 +1,44 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Palace of Darkness - Shooter Room: True + Palace of Darkness - The Arena - Bridge: True + Palace of Darkness - Stalfos Basement: True + Palace of Darkness - Big Key Chest: True + Palace of Darkness - The Arena - Ledge: True + Palace of Darkness - Map Chest: True + Palace of Darkness - Compass Chest: True + Palace of Darkness - Dark Basement - Left: True + Palace of Darkness - Dark Basement - Right: True + Palace of Darkness - Dark Maze - Top: True + Palace of Darkness - Dark Maze - Bottom: True + Palace of Darkness - Big Chest: True + Palace of Darkness - Harmless Hellway: True + Palace of Darkness - Boss: True + Bombos Tablet: True + C-Shaped House: True + Pyramid Fairy - Left: True + Swamp Palace - Entrance: False + Thieves' Town - Map Chest: False + + + + diff --git a/test/suite/hmg/pearlless_sw.yaml b/test/suite/hmg/pearlless_sw.yaml new file mode 100644 index 00000000..c26ce1d9 --- /dev/null +++ b/test/suite/hmg/pearlless_sw.yaml @@ -0,0 +1,25 @@ +meta: + players: 1 + +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Pegasus Boots + - Progressive Sword + - Hammer + - Progressive Glove + - Progressive Glove + - Fire Rod + - Book of Mudora + - Bottle + - Magic Mirror + - Lamp +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Skull Woods - Bridge Room: True diff --git a/test/suite/hmg/swamp_from_mire.yaml b/test/suite/hmg/swamp_from_mire.yaml new file mode 100644 index 00000000..5892f07c --- /dev/null +++ b/test/suite/hmg/swamp_from_mire.yaml @@ -0,0 +1,30 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Small Key (Swamp Palace) + locations: + Swamp Palace - Entrance: True + Swamp Palace - Map Chest: True + Swamp Palace - Big Chest: True + Swamp Palace - Compass Chest: True + Swamp Palace - West Chest: True + Swamp Palace - Big Key Chest: True + Swamp Palace - Flooded Room - Left: True + Swamp Palace - Flooded Room - Right: True + Swamp Palace - Waterfall Room: True + Swamp Palace - Boss: True \ No newline at end of file From 80c8c189a8253bde6f5850a6c482d87c2a1237ee Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Tue, 12 Dec 2023 18:56:21 +0100 Subject: [PATCH 090/158] Minor HMG changes --- UnderworldGlitchRules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 98eb6626..1b6b276b 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -156,6 +156,7 @@ def underworld_glitches_rules(world, player): Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") + Rules.add_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") mire_to_hera = world.get_entrance("Mire to Hera Clip", player) mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) @@ -206,14 +207,14 @@ def underworld_glitches_rules(world, player): def gt_rule(state): return (state.has("Moon Pearl", player) or inverted) and rule_map.get( - world.get_entrance(("Ganons Tower" if not inverted else "Inverted Ganons Tower"), player).connected_region.name, lambda state: False)(state) + world.get_entrance(("Ganons Tower"), player).connected_region.name, lambda state: False)(state) def mirrorless_moat_rule(state): return state.can_reach("Old Man S&Q", "Entrance", player) and mire_clip(state) and (hera_rule(state) or gt_rule(state)) Rules.add_rule(world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or") - # Thieves -> Hera + # Thieves -> Desert Rules.add_rule(world.get_entrance("Thieves to Desert Clip", player), lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player)) dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert West Portal", "Desert Palace Exit (West)") dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert South Portal", "Desert Palace Exit (South)") From 9fb7cf9f719be31a7f3986652ce71a3480700bca Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sun, 17 Dec 2023 09:45:22 +0100 Subject: [PATCH 091/158] Refactor UW Glitches --- README.md | 33 ++++++ UnderworldGlitchRules.py | 231 +++++++++++++++++++++++---------------- 2 files changed, 171 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 24e79807..dc1d3f25 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ See https://alttpr.com/ for more details on the normal randomizer. 7. [Standard Changes](#standard-changes) 8. [Game Options](#game-options) 9. [Generation Setup & Miscellaneous](#generation-setup--miscellaneous) + 10. [Glitched Logic](#glitched-logic) ## Setup and Installation @@ -597,4 +598,36 @@ Can be used to set a seed number to generate. Using the same seed with same sett Use to batch generate multiple seeds with same settings. If a seed number is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with the same seed number given will produce the same 10 (different) roms each time). +## Glitched Logic + +Overworld glitches, Hybrid Major Glitches (HMG) and No Logic are currently supported. + +### Overworld Glitches +_Support added by qadan and compiling_ + +Overworld Glitches logic includes (but is not limited to) the following: +* Overworld teleports and clips to reach various items/entrances +* Use of superbunny to obtain items and/or bonk open entrances +* Use of mirror to access Desert Palace East Entrance +* Use of bunny pocket to access the Back of Skull Woods and VOO Hammer house entrances + + +### Hybrid Major Glitches +_Support added by Muffins (ported from work by Espeon)_. + +**Not currently compatible with Door Shuffle** + +Hybrid Major Glitches logic includes the following: +* All Overworld Glitches logic +* Kikiskip to access PoD wihtout MP or DW access +* IP Lobby clip to skip fire requirement +* Traversal between Mire -> Hera -> Swamp +* Stealing SK from Mire to open SP +* Using the Mire big key to open Hera doors and big chest +* Traversal between TT -> Desert +* Traveral between Spec rock upper -> Spec rock mid +* Traveral between Paradox lower -> Paradox mid + upper + + + diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 1b6b276b..7ddfa792 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -2,92 +2,71 @@ from BaseClasses import Entrance import Rules from OverworldGlitchRules import create_no_logic_connections +kikiskip_spots = [("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal")] -def get_kikiskip_spots(): - """ - Spectacle Rock Cave (Bottom) -> Palace of Darkness Exit, a.k.a. Kiki Skip - """ - yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal") +mireheraswamp_spots = [ + ("Mire to Hera Clip", "Mire Torches Top", "Hera Portal"), + ("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal"), +] +icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop")] -def get_mireheraswamp_spots(): - """ - "Mire Torches Top -> Tower of Hera Exit, a.k.a. Mire to Hera Clip - "Mire Torches Top -> Swamp Palace Exit, a.k.a. Hera to Swamp Clip - """ +thievesdesert_spots = [ + ("Thieves to Desert Clip", "Thieves Attic", "Desert West Portal"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert South Portal"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert East Portal"), +] - yield ("Mire to Hera Clip", "Mire Torches Top", "Hera Portal") - yield ("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal") +specrock_spots = [("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave (Top)")] - -def get_icepalace_spots(): - """ - "Ice Palace Exit -> Ice Palace Exit, a.k.a. Ice Palace Clip - """ - yield ("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop") - - -def get_thievesdesert_spots(): - """ - "Thieves' Town -> Desert Palace , a.k.a. Thieves to Desert Clip - Accessing any of the exits will be in logic because of the ability to dungeon bunny revive - """ - yield ("Thieves to Desert Clip", "Thieves Attic", "Desert West Portal") - yield ("Thieves to Desert Clip", "Thieves Attic", "Desert South Portal") - yield ("Thieves to Desert Clip", "Thieves Attic", "Desert East Portal") - - -def get_specrock_spots(): - """ - "Spectacle Rock Cave (Peak) -> Spectacle Rock Cave (Top), a.k.a. Spectacle Rock Cave Clip - """ - yield ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave (Top)") - - -def get_paradox_spots(): - """ - "Paradox Cave Front -> Paradox Cave Chest Area, a.k.a. Paradox Cave Teleport (dash citrus, 1f right, teleport up) - """ - yield ("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area") +paradox_spots = [("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area")] # We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions -def get_kikiskip_connectors(world, player): - yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", world.get_entrance("Palace of Darkness Exit", player).connected_region) +kikiskip_connectors = [("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Exit")] -def get_mireheraswamp_connectors(world, player): - yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Tower of Hera Exit", player).connected_region) - yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Swamp Palace Exit", player).connected_region) +mireheraswamp_connectors = [ + ("Mire to Hera Clip", "Mire Torches Top", "Tower of Hera Exit"), + ("Mire to Hera Clip", "Mire Torches Top", "Swamp Palace Exit"), +] -def get_thievesdesert_connectors(world, player): - yield ("Thieves to Desert Clip", "Thieves Attic", world.get_entrance("Desert Palace Exit (West)", player).connected_region) - yield ("Thieves to Desert Clip", "Thieves Attic", world.get_entrance("Desert Palace Exit (South)", player).connected_region) - yield ("Thieves to Desert Clip", "Thieves Attic", world.get_entrance("Desert Palace Exit (East)", player).connected_region) - -def get_specrock_connectors(world, player): - yield ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", world.get_entrance("Spectacle Rock Cave Exit (Top)", player).connected_region) - yield ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", world.get_entrance("Spectacle Rock Cave Exit", player).connected_region) +thievesdesert_connectors = [ + ("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (West)"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (South)"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (East)"), +] +specrock_connectors = [ + ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave Exit (Top)"), + ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave Exit"), +] # Create connections between dungeons/locations def create_hybridmajor_connections(world, player): - create_no_logic_connections(player, world, get_kikiskip_spots()) - create_no_logic_connections(player, world, get_mireheraswamp_spots()) - create_no_logic_connections(player, world, get_icepalace_spots()) - create_no_logic_connections(player, world, get_thievesdesert_spots()) - create_no_logic_connections(player, world, get_specrock_spots()) - create_no_logic_connections(player, world, get_paradox_spots()) + for spots in [ + kikiskip_spots, + mireheraswamp_spots, + icepalace_spots, + thievesdesert_spots, + specrock_spots, + paradox_spots, + ]: + create_no_logic_connections(player, world, spots) # Turn dungeons into connectors def create_hybridmajor_connectors(world, player): - create_no_logic_connections(player, world, get_kikiskip_connectors(world, player)) - create_no_logic_connections(player, world, get_mireheraswamp_connectors(world, player)) - create_no_logic_connections(player, world, get_thievesdesert_connectors(world, player)) - create_no_logic_connections(player, world, get_specrock_connectors(world, player)) + for connectors in [ + kikiskip_connectors, + mireheraswamp_connectors, + thievesdesert_connectors, + specrock_connectors, + ]: + new_connectors = [(connector[0], connector[1], world.get_entrance(connector[2], player).connected_region) for connector in connectors] + create_no_logic_connections(player, world, new_connectors) # For some entrances, we need to fake having pearl, because we're in fake DW/LW. @@ -121,7 +100,12 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du Rules.add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) elif dungeon_entrance.name == "Agahnims Tower": - Rules.add_rule(clip, lambda state: state.has("Cape", player) or state.has_beam_sword(player) or state.has("Beat Agahnim 1", player)) + Rules.add_rule( + clip, + lambda state: state.has("Cape", player) + or state.has_beam_sword(player) + or state.has("Beat Agahnim 1", player), + ) # Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally. Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) @@ -137,10 +121,12 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du def underworld_glitches_rules(world, player): # Ice Palace Entrance Clip, needs bombs or cane of somaria to exit bomb drop room - Rules.add_rule(world.get_entrance("Ice Bomb Drop SE", player), - lambda state: state.can_dash_clip(world.get_region("Ice Lobby", player), player) and - (state.can_use_bombs(player) or state.has('Cane of Somaria', player)), - combine="or") + Rules.add_rule( + world.get_entrance("Ice Bomb Drop SE", player), + lambda state: state.can_dash_clip(world.get_region("Ice Lobby", player), player) + and (state.can_use_bombs(player) or state.has("Cane of Somaria", player)), + combine="or", + ) # Kiki Skip kks = world.get_entrance("Kiki Skip", player) @@ -149,15 +135,26 @@ def underworld_glitches_rules(world, player): # Mire -> Hera -> Swamp def mire_clip(state): - return state.can_reach("Mire Torches Top", "Region", player) and state.can_dash_clip(world.get_region("Mire Torches Top", player), player) + return state.can_reach("Mire Torches Top", "Region", player) and state.can_dash_clip( + world.get_region("Mire Torches Top", player), player + ) def hera_clip(state): - return state.can_reach("Hera 4F", "Region", player) and state.can_dash_clip(world.get_region("Hera 4F", player), player) - + return state.can_reach("Hera 4F", "Region", player) and state.can_dash_clip( + world.get_region("Hera 4F", player), player + ) + + Rules.add_rule( + world.get_entrance("Hera Startile Corner NW", player), + lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), + combine="or", + ) + Rules.add_rule( + world.get_location("Tower of Hera - Big Chest", player), + lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), + combine="or", + ) - Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") - Rules.add_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or") - mire_to_hera = world.get_entrance("Mire to Hera Clip", player) mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) Rules.set_rule(mire_to_hera, mire_clip) @@ -165,7 +162,7 @@ def underworld_glitches_rules(world, player): # Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit") - dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") + dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots # Flippers required for all of these doors to prevent locks when flooding for door in [ @@ -181,10 +178,18 @@ def underworld_glitches_rules(world, player): "Swamp Waterway NW", "Swamp T SW", ]: - Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or") + Rules.add_rule( + world.get_entrance(door, player), + lambda state: mire_clip(state) + and state.has("Small Key (Misery Mire)", player, count=6) + and state.has("Flippers", player), + combine="or", + ) # Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has('Flippers', player), combine="or") - Rules.add_rule(world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or") + Rules.add_rule( + world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or" + ) # Build the rule for SP moat. # We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT. @@ -203,30 +208,70 @@ def underworld_glitches_rules(world, player): inverted = world.mode[player] == "inverted" def hera_rule(state): - return (state.has("Moon Pearl", player) or not inverted) and rule_map.get(world.get_entrance("Tower of Hera", player).connected_region.name, lambda state: False)(state) + return (state.has("Moon Pearl", player) or not inverted) and rule_map.get( + world.get_entrance("Tower of Hera", player).connected_region.name, lambda state: False + )(state) def gt_rule(state): return (state.has("Moon Pearl", player) or inverted) and rule_map.get( - world.get_entrance(("Ganons Tower"), player).connected_region.name, lambda state: False)(state) + world.get_entrance(("Ganons Tower"), player).connected_region.name, lambda state: False + )(state) def mirrorless_moat_rule(state): - return state.can_reach("Old Man S&Q", "Entrance", player) and mire_clip(state) and (hera_rule(state) or gt_rule(state)) + return ( + state.can_reach("Old Man S&Q", "Entrance", player) + and mire_clip(state) + and (hera_rule(state) or gt_rule(state)) + ) - Rules.add_rule(world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or") + Rules.add_rule( + world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or" + ) # Thieves -> Desert - Rules.add_rule(world.get_entrance("Thieves to Desert Clip", player), lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player)) - dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert West Portal", "Desert Palace Exit (West)") - dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert South Portal", "Desert Palace Exit (South)") - dungeon_reentry_rules(world, player, world.get_entrance("Thieves to Desert Clip", player), "Desert East Portal", "Desert Palace Exit (East)") + Rules.add_rule( + world.get_entrance("Thieves to Desert Clip", player), + lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player), + ) + dungeon_reentry_rules( + world, + player, + world.get_entrance("Thieves to Desert Clip", player), + "Desert West Portal", + "Desert Palace Exit (West)", + ) + dungeon_reentry_rules( + world, + player, + world.get_entrance("Thieves to Desert Clip", player), + "Desert South Portal", + "Desert Palace Exit (South)", + ) + dungeon_reentry_rules( + world, + player, + world.get_entrance("Thieves to Desert Clip", player), + "Desert East Portal", + "Desert Palace Exit (East)", + ) # Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up - paradox_left_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Middle'] + paradox_left_chests = ["Paradox Cave Lower - Far Left", "Paradox Cave Lower - Left", "Paradox Cave Lower - Middle"] for location in paradox_left_chests: - Rules.add_rule(world.get_location(location, player), lambda state: state.can_dash_clip(world.get_location(location, player).parent_region, player), 'or') - + Rules.add_rule( + world.get_location(location, player), + lambda state: state.can_dash_clip(world.get_location(location, player).parent_region, player), + "or", + ) + # Collecting right chests in Paradox Cave using a dash clip on left side -> dash citrus, 1f right, teleport up, then hitting the switch - paradox_right_chests = ['Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right'] + paradox_right_chests = ["Paradox Cave Lower - Right", "Paradox Cave Lower - Far Right"] for location in paradox_right_chests: - Rules.add_rule(world.get_location(location, player), lambda state: (state.can_dash_clip(world.get_location(location, player).parent_region, player) and state.can_hit_crystal(player)), 'or') - + Rules.add_rule( + world.get_location(location, player), + lambda state: ( + state.can_dash_clip(world.get_location(location, player).parent_region, player) + and state.can_hit_crystal(player) + ), + "or", + ) From 22c4dcdfd393d19d9846d8ecc33386b4c9e2ba22 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sun, 17 Dec 2023 14:03:04 +0100 Subject: [PATCH 092/158] More test updates + disable bunny check skip --- Rules.py | 12 ++++----- ..._sw.yaml => entrance_bunny_pocket_sw.yaml} | 2 +- .../hmg/inverted_inaccessible_desert.yaml | 26 ++++++++++++++++++ test/suite/hmg/inverted_moon_pearl_locs.yaml | 27 +++++++++++++++++++ test/suite/hmg/moon_pearl_locs.yaml | 6 ++++- test/suite/hmg/swamp_from_mire.yaml | 13 +++++++++ 6 files changed, 78 insertions(+), 8 deletions(-) rename test/suite/hmg/{bunny_pocket_sw.yaml => entrance_bunny_pocket_sw.yaml} (88%) create mode 100644 test/suite/hmg/inverted_inaccessible_desert.yaml create mode 100644 test/suite/hmg/inverted_moon_pearl_locs.yaml diff --git a/Rules.py b/Rules.py index 3e1aa061..03bcaba6 100644 --- a/Rules.py +++ b/Rules.py @@ -1964,12 +1964,12 @@ def set_bunny_rules(world, player, inverted): if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_region in seen: continue # We don't need to navigate through single entrance dungeons. HMG has more multi-entrance dungeons - if (region.type == RegionType.Dungeon and new_region.type == RegionType.Dungeon): - if ( - world.logic[player] == 'hybridglitches' and new_region.dungeon != None and new_region.dungeon.name in hmg_single_exit_dungeons) or ( - world.logic[player] not in ['hybridglitches', 'nologic'] and new_region.dungeon != None and new_region.dungeon.name in all_single_exit_dungeons - ): - continue + # if (region.type == RegionType.Dungeon and new_region.type == RegionType.Dungeon): + # if ( + # world.logic[player] == 'hybridglitches' and new_region.dungeon != None and new_region.dungeon.name in hmg_single_exit_dungeons) or ( + # world.logic[player] not in ['hybridglitches', 'nologic'] and new_region.dungeon != None and new_region.dungeon.name in all_single_exit_dungeons + # ): + # continue new_path = path + [entrance.access_rule] new_seen = seen.union({new_region}) if not is_link(new_region): diff --git a/test/suite/hmg/bunny_pocket_sw.yaml b/test/suite/hmg/entrance_bunny_pocket_sw.yaml similarity index 88% rename from test/suite/hmg/bunny_pocket_sw.yaml rename to test/suite/hmg/entrance_bunny_pocket_sw.yaml index 2b263db2..48f5021a 100644 --- a/test/suite/hmg/bunny_pocket_sw.yaml +++ b/test/suite/hmg/entrance_bunny_pocket_sw.yaml @@ -30,5 +30,5 @@ entrances: Skull Woods Final Section: Pyramid Fairy two-way: Thieves Town: Two Brothers House Exit (West) - Spectacle Rock Cave (Bottom): Two Brothers House Exit (East) + Skull Woods Second Section Door (West): Two Brothers House Exit (East) diff --git a/test/suite/hmg/inverted_inaccessible_desert.yaml b/test/suite/hmg/inverted_inaccessible_desert.yaml new file mode 100644 index 00000000..bb8ad8aa --- /dev/null +++ b/test/suite/hmg/inverted_inaccessible_desert.yaml @@ -0,0 +1,26 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches + mode: inverted + shuffle: crossed + +start_inventory: + 1: + - Pegasus Boots + - Progressive Sword + - Hammer + - Fire Rod +placements: + 1: + Desert Palace - Boss: Moon Pearl +entrances: + 1: + two-way: + Skull Woods Final Section: Desert Palace Exit (West) + Skull Woods Second Section Door (West): Desert Palace Exit (East) + Thieves Town: Thieves Town Exit + Hyrule Castle Entrance (East): Desert Palace Exit (South) + Hyrule Castle Entrance (West): Desert Palace Exit (North) + diff --git a/test/suite/hmg/inverted_moon_pearl_locs.yaml b/test/suite/hmg/inverted_moon_pearl_locs.yaml new file mode 100644 index 00000000..65d23371 --- /dev/null +++ b/test/suite/hmg/inverted_moon_pearl_locs.yaml @@ -0,0 +1,27 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches + mode: inverted +start_inventory: + 1: + - Flippers + - Progressive Sword + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Desert Palace - Big Chest: True + Eastern Palace - Big Chest: True + + + + diff --git a/test/suite/hmg/moon_pearl_locs.yaml b/test/suite/hmg/moon_pearl_locs.yaml index f5d06745..a18d53d1 100644 --- a/test/suite/hmg/moon_pearl_locs.yaml +++ b/test/suite/hmg/moon_pearl_locs.yaml @@ -5,8 +5,10 @@ settings: logic: hybridglitches start_inventory: 1: + - Pegasus Boots - Flippers - - Moon Pearl + - Fire Rod + - Book of Mudora - Progressive Sword - Progressive Sword - Lamp @@ -19,6 +21,8 @@ advanced_placements: - type: Verification item: Moon Pearl locations: + Skull Woods - Compass Chest: True + Skull Woods - Bridge Room: True Palace of Darkness - Shooter Room: True Palace of Darkness - The Arena - Bridge: True Palace of Darkness - Stalfos Basement: True diff --git a/test/suite/hmg/swamp_from_mire.yaml b/test/suite/hmg/swamp_from_mire.yaml index 5892f07c..f3873909 100644 --- a/test/suite/hmg/swamp_from_mire.yaml +++ b/test/suite/hmg/swamp_from_mire.yaml @@ -17,6 +17,19 @@ advanced_placements: 1: - type: Verification item: Small Key (Swamp Palace) + locations: + Swamp Palace - Entrance: True + Swamp Palace - Map Chest: True + Swamp Palace - Big Chest: True + Swamp Palace - Compass Chest: True + Swamp Palace - West Chest: True + Swamp Palace - Big Key Chest: True + Swamp Palace - Flooded Room - Left: True + Swamp Palace - Flooded Room - Right: True + Swamp Palace - Waterfall Room: True + Swamp Palace - Boss: True + - type: Verification + item: Big Key (Swamp Palace) locations: Swamp Palace - Entrance: True Swamp Palace - Map Chest: True From 81ace17889c057f37ae8d29689a451b6219fed24 Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Sun, 17 Dec 2023 21:02:47 +0100 Subject: [PATCH 093/158] More tests fixes --- test/suite/hmg/entrance_bunny_pocket_sw.yaml | 2 +- test/suite/hmg/inverted_moon_pearl_locs.yaml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/suite/hmg/entrance_bunny_pocket_sw.yaml b/test/suite/hmg/entrance_bunny_pocket_sw.yaml index 48f5021a..eba7743d 100644 --- a/test/suite/hmg/entrance_bunny_pocket_sw.yaml +++ b/test/suite/hmg/entrance_bunny_pocket_sw.yaml @@ -29,6 +29,6 @@ entrances: entrances: Skull Woods Final Section: Pyramid Fairy two-way: - Thieves Town: Two Brothers House Exit (West) + Chicken House: Two Brothers House Exit (West) Skull Woods Second Section Door (West): Two Brothers House Exit (East) diff --git a/test/suite/hmg/inverted_moon_pearl_locs.yaml b/test/suite/hmg/inverted_moon_pearl_locs.yaml index 65d23371..9ae1a9e1 100644 --- a/test/suite/hmg/inverted_moon_pearl_locs.yaml +++ b/test/suite/hmg/inverted_moon_pearl_locs.yaml @@ -9,6 +9,7 @@ start_inventory: - Flippers - Progressive Sword - Progressive Sword + - Book of Mudora - Lamp - Magic Mirror - Ether @@ -19,8 +20,11 @@ advanced_placements: - type: Verification item: Moon Pearl locations: + Tower of Hera - Big Chest: True Desert Palace - Big Chest: True Eastern Palace - Big Chest: True + Bombos Tablet: True + Cave 45: True From 36c8b5aaa653ad1218afea6263686aae5e5f765c Mon Sep 17 00:00:00 2001 From: KrisDavie Date: Mon, 18 Dec 2023 20:46:18 +0100 Subject: [PATCH 094/158] Code cleanup and more tests --- Main.py | 1 - README.md | 10 +++---- Rules.py | 19 ------------- test/suite/hmg/flippers_locked_flippers.yaml | 20 ++++++++++++++ test/suite/hmg/flippers_wraps.yaml | 29 ++++++++++++++++++++ 5 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 test/suite/hmg/flippers_locked_flippers.yaml create mode 100644 test/suite/hmg/flippers_wraps.yaml diff --git a/Main.py b/Main.py index 99b257e2..ad817ec2 100644 --- a/Main.py +++ b/Main.py @@ -82,7 +82,6 @@ def main(args, seed=None, fish=None): for i in zip(args.logic.values(), args.door_shuffle.values()): if i[0] == 'hybridglitches' and i[1] != 'vanilla': raise RuntimeError(BabelFish().translate("cli","cli","hybridglitches.door.shuffle")) - # print(args) world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.custom, args.customitemarray, args.hints) diff --git a/README.md b/README.md index dc1d3f25..cac13120 100644 --- a/README.md +++ b/README.md @@ -621,13 +621,11 @@ Hybrid Major Glitches logic includes the following: * All Overworld Glitches logic * Kikiskip to access PoD wihtout MP or DW access * IP Lobby clip to skip fire requirement +* Traversal between TT -> Desert +* Traversal between Spec rock upper -> Spec rock mid +* Traversal between Paradox lower -> Paradox mid + upper * Traversal between Mire -> Hera -> Swamp * Stealing SK from Mire to open SP * Using the Mire big key to open Hera doors and big chest -* Traversal between TT -> Desert -* Traveral between Spec rock upper -> Spec rock mid -* Traveral between Paradox lower -> Paradox mid + upper - - - +All traversals mentioned are considered connectors in entrance shuffle diff --git a/Rules.py b/Rules.py index 03bcaba6..deb809a4 100644 --- a/Rules.py +++ b/Rules.py @@ -1907,12 +1907,6 @@ def set_bunny_rules(world, player, inverted): # Is it possible to do bunny pocket here def can_bunny_pocket_skull_woods(world, player): - # return world.get_entrance( - # "Skull Woods Second Section Door (West)", player - # ).connected_region.type != RegionType.Dungeon and ( - # not world.state.can_reach_from("Skull Woods Forest (West)", "Light World", 1) - # or not world.state.can_reach_from("Light World", "Skull Woods Forest (West)", 1) - # ) return world.get_entrance( "Skull Woods Second Section Door (West)", player ).connected_region.type == RegionType.Dungeon or ( @@ -1921,12 +1915,6 @@ def set_bunny_rules(world, player, inverted): ) def can_bunny_pocket_voo_shop(world, player): - # return world.get_entrance( - # "Dark World Shop", player - # ).connected_region.type != RegionType.Dungeon and ( - # not world.state.can_reach_from("West Dark World", "Light World", 1) - # or not world.state.can_reach_from("Light World", "West Dark World", 1) - # ) return ( world.state.can_reach_from("West Dark World", "Light World", 1) and world.state.can_reach_from("Light World", "West Dark World", 1) @@ -1963,13 +1951,6 @@ def set_bunny_rules(world, player, inverted): new_region = entrance.parent_region if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_region in seen: continue - # We don't need to navigate through single entrance dungeons. HMG has more multi-entrance dungeons - # if (region.type == RegionType.Dungeon and new_region.type == RegionType.Dungeon): - # if ( - # world.logic[player] == 'hybridglitches' and new_region.dungeon != None and new_region.dungeon.name in hmg_single_exit_dungeons) or ( - # world.logic[player] not in ['hybridglitches', 'nologic'] and new_region.dungeon != None and new_region.dungeon.name in all_single_exit_dungeons - # ): - # continue new_path = path + [entrance.access_rule] new_seen = seen.union({new_region}) if not is_link(new_region): diff --git a/test/suite/hmg/flippers_locked_flippers.yaml b/test/suite/hmg/flippers_locked_flippers.yaml new file mode 100644 index 00000000..be71e07b --- /dev/null +++ b/test/suite/hmg/flippers_locked_flippers.yaml @@ -0,0 +1,20 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Pegasus Boots + - Moon Pearl + - Progressive Sword +advanced_placements: + 1: + - type: Verification + item: Flippers + locations: + Zora's Ledge: True + Hobo: True + Ice Palace - Boss: True + Swamp Palace - Entrance: False + diff --git a/test/suite/hmg/flippers_wraps.yaml b/test/suite/hmg/flippers_wraps.yaml new file mode 100644 index 00000000..3418b4ec --- /dev/null +++ b/test/suite/hmg/flippers_wraps.yaml @@ -0,0 +1,29 @@ +meta: + players: 1 +settings: + 1: + logic: owglitches +start_inventory: + 1: + - Pegasus Boots + - Moon Pearl + - Progressive Sword + - Flippers +placements: + 1: + Peg Cave: Magic Mirror +advanced_placements: + 1: + - type: Verification + item: Hammer + locations: + Link's House: True + Magic Bat: False +advanced_placements: + 1: + - type: Verification + item: Progressive Glove + locations: + Link's House: True + Ice Palace - Freezor Chest: False + From 81e5aff923a5487079c956e02b2f03a66a058e2e Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 28 Dec 2023 10:51:16 -0700 Subject: [PATCH 095/158] feat(logic): Hybrid major glitches - HMG fix(enemizer): enemy bans --- Main.py | 2 +- RELEASENOTES.md | 3 +++ source/enemizer/enemy_deny.yaml | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index ad817ec2..966dae39 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.3.0.8' +version_number = '1.4.0.0' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1f6f4747..fdc4dd0a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.0.0v + * Initial support for HMG (Thanks Muffins!) + * Enemizer: enemy bans * 1.3.0.9v * ER: New Swapped ER mode borrowed from OWR * ER: fixed a generation error where TR chooses all "must-exits" diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 46842052..d5dbddfa 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -195,7 +195,7 @@ UwGeneralDeny: - [ 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, 3, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper", "Statue"]] #"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" @@ -347,6 +347,7 @@ UwGeneralDeny: - [ 0x00d8, 8, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Kill Room 1 - Red Eyegore" - [ 0x00d9, 1, [ "RollerHorizontalRight" ] ] #"Eastern Palace - Dodgeball - Green Eyegore 1" - [ 0x00db, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00db, 3, [ "Bumper" ] ] # Okay in vanilla - [ 0x00dc, 2, [ "AntiFairyCircle", "BigSpike", "Bumper" ] ] - [ 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" From 337ace1fa37f729b96bb31de27b72f5ed23e8339 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 28 Dec 2023 17:22:36 -0700 Subject: [PATCH 096/158] fix(generation): reduce memory usage for bunny walk calculations fix(key logic): make partial the default --- BaseClasses.py | 2 +- CLI.py | 2 +- RELEASENOTES.md | 2 ++ Rules.py | 7 +++++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index fe1f3c96..73882607 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -148,7 +148,7 @@ class World(object): set_player_attr('door_self_loops', False) set_player_attr('door_type_mode', 'original') set_player_attr('trap_door_mode', 'optional') - set_player_attr('key_logic_algorithm', 'default') + set_player_attr('key_logic_algorithm', 'partial') set_player_attr('aga_randomness', True) set_player_attr('shopsanity', False) diff --git a/CLI.py b/CLI.py index 38af7c52..1066b845 100644 --- a/CLI.py +++ b/CLI.py @@ -216,7 +216,7 @@ def parse_settings(): 'intensity': 2, 'door_type_mode': 'original', 'trap_door_mode': 'optional', - 'key_logic_algorithm': 'default', + 'key_logic_algorithm': 'partial', 'decoupledoors': False, 'door_self_loops': False, 'experimental': False, diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fdc4dd0a..933f5b87 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -143,6 +143,8 @@ These are now independent of retro mode and have three options: None, Random, an * 1.4.0.0v * Initial support for HMG (Thanks Muffins!) + * Generation: fix for bunny walk logic taking up too much memory + * Key Logic: Partial is now the new default * Enemizer: enemy bans * 1.3.0.9v * ER: New Swapped ER mode borrowed from OWR diff --git a/Rules.py b/Rules.py index deb809a4..f5977389 100644 --- a/Rules.py +++ b/Rules.py @@ -1945,14 +1945,16 @@ def set_bunny_rules(world, player, inverted): # a) being able to reach it, and # b) being able to access all entrances from there to `region` queue = deque([(region, [], {region})]) + seen_sets = set([frozenset({region})]) while queue: (current, path, seen) = queue.popleft() for entrance in current.entrances: new_region = entrance.parent_region - if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_region in seen: + new_seen = seen.union({new_region}) + if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_seen in seen_sets: continue new_path = path + [entrance.access_rule] - new_seen = seen.union({new_region}) + seen_sets.add(frozenset(new_seen)) if not is_link(new_region): if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: @@ -1997,6 +1999,7 @@ def set_bunny_rules(world, player, inverted): else: continue if is_bunny(new_region): + # todo: if not owg or hmg and entrance is in bunny_impassible_doors, then skip this nonsense? queue.append((new_region, new_path, new_seen)) else: # we have reached pure light world, so we have a new possible option From 846423fc38e78f69acba2a62a871bae6cc44d98d Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 2 Jan 2024 17:09:08 -0700 Subject: [PATCH 097/158] fix: fix up some vanilla key logic fix: fix tile pattern --- DoorShuffle.py | 22 +++++++++++++++++++--- Main.py | 6 +++++- RELEASENOTES.md | 3 +++ source/enemizer/TilePattern.py | 2 +- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 4a48cd18..9b057f61 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -7,7 +7,7 @@ from typing import DefaultDict, Dict, List from itertools import chain from BaseClasses import RegionType, Region, Door, DoorType, Sector, CrystalBarrier, DungeonInfo, dungeon_keys -from BaseClasses import PotFlags, LocationType, Direction +from BaseClasses import PotFlags, LocationType, Direction, KeyRuleType from Doors import reset_portals from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts from Dungeons import dungeon_bigs, dungeon_hints @@ -261,8 +261,24 @@ def vanilla_key_logic(world, player): world.key_logic[player][builder.name] = key_layout.key_logic world.key_layout[player][builder.name] = key_layout log_key_logic(builder.name, key_layout.key_logic) - # if world.shuffle[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]: - # validate_vanilla_key_logic(world, player) + # special adjustments for vanilla + if world.mode[player] != 'standard': + # adjust hc doors + def adjust_hc_door(door_rule): + if door_rule.new_rules[KeyRuleType.WorstCase] == 3: + door_rule.new_rules[KeyRuleType.WorstCase] = 2 + door_rule.small_key_num = 2 + + rules = world.key_logic[player]['Hyrule Castle'].door_rules + adjust_hc_door(rules['Sewers Secret Room Key Door S']) + adjust_hc_door(rules['Hyrule Dungeon Map Room Key Door S']) + adjust_hc_door(rules['Sewers Dark Cross Key Door N']) + # adjust pod front door + pod_front = world.key_logic[player]['Palace of Darkness'].door_rules['PoD Middle Cage N'] + if pod_front.new_rules[KeyRuleType.WorstCase] == 6: + pod_front.new_rules[KeyRuleType.WorstCase] = 1 + pod_front.small_key_num = 1 + # gt logic? I'm unsure it needs adjusting def validate_vanilla_reservation(dungeon, world, player): diff --git a/Main.py b/Main.py index 966dae39..85fd02ce 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.0.0' +version_number = '1.4.0.1' version_branch = '-v' __version__ = f'{version_number}{version_branch}' @@ -487,6 +487,10 @@ def copy_world(world): ret.intensity = world.intensity.copy() ret.decoupledoors = world.decoupledoors.copy() ret.door_self_loops = world.door_self_loops.copy() + ret.door_type_mode = world.door_type_mode.copy() + ret.trap_door_mode = world.trap_door_mode.copy() + ret.key_logic_algorithm = world.key_logic_algorithm.copy() + ret.aga_randomness = world.aga_randomness.copy() ret.experimental = world.experimental.copy() ret.shopsanity = world.shopsanity.copy() ret.dropshuffle = world.dropshuffle.copy() diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 933f5b87..436e53be 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.0.1v + * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door + * Generation: Fix a broken tile pattern * 1.4.0.0v * Initial support for HMG (Thanks Muffins!) * Generation: fix for bunny walk logic taking up too much memory diff --git a/source/enemizer/TilePattern.py b/source/enemizer/TilePattern.py index 5dc00020..a452c547 100644 --- a/source/enemizer/TilePattern.py +++ b/source/enemizer/TilePattern.py @@ -90,7 +90,7 @@ tile_patterns = [ (7, 2), (8, 5), (1, 3), (1, 5), (4, 2), (3, 5), (4, 6), (8, 4), (5, 6)]), ('creeper face', [(3, 7), (4, 5), (5, 4), (5, 6), (3, 3), (2, 2), (3, 2), (7, 3), (6, 7), (4, 6), (6, 3), (7, 2), (2, 3), (4, 4), (3, 6), (6, 2), (6, 6), (5, 5)]), - ('fast', [7, 7]) + ('fast', [(7, 7)]) ] From 3d6432af957bd35e0a2abff146fa0572a7a545f7 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 2 Jan 2024 17:26:17 -0700 Subject: [PATCH 098/158] fix: enemy ban --- source/enemizer/enemy_deny.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index d5dbddfa..8983ed66 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -288,7 +288,8 @@ UwGeneralDeny: - [ 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" + - [ 0x00b3, 3, ["AntiFairyCircle", "Bumper" ]] #"Misery Mire - Spike Room - Yomo Medusa" + - [ 0x00b3, 4, ["AntiFairyCircle", "Bumper"]] - [ 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" From 3f5eafb74f41507b46c32d66726f1575b8de54aa Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 2 Jan 2024 18:01:54 -0700 Subject: [PATCH 099/158] fix: muradahla typo fix: remove warp in inverted after aga1 defeated fix: shopsanity repeatable item bug --- RELEASENOTES.md | 3 +++ Rom.py | 5 +++-- data/base2current.bps | Bin 117546 -> 117551 bytes source/enemizer/OwEnemyList.py | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 436e53be..bd95dd40 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -144,6 +144,9 @@ These are now independent of retro mode and have three options: None, Random, an * 1.4.0.1v * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door * Generation: Fix a broken tile pattern + * Inverted: Castle warp should not appear after defeating Aga 1 + * Murahdahla: Should not disappear after Aga 1. May fix other subtle issues. + * Shopsanity: Buying multiple of an item in the potion shop should no longer increase item count. * 1.4.0.0v * Initial support for HMG (Thanks Muffins!) * Generation: fix for bunny walk logic taking up too much memory diff --git a/Rom.py b/Rom.py index 1f533b5d..376e3695 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '4d1f3e36e316077823a3e2eb5359ca17' +RANDOMIZERBASEHASH = '65b433946c72953780d82cdc0bb8fe8e' class JsonRom(object): @@ -2483,7 +2483,8 @@ def set_inverted_mode(world, player, rom): write_int16(rom, 0xDBA71 + 2 * 0x35, 0x06A4) if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: rom.write_byte(0xDBB73 + 0x35, 0x36) - rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp + # rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp + del world.data_tables[player].ow_enemy_table[0xab][5] if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: write_int16(rom, 0x15AEE + 2 * 0x37, 0x0010) # pyramid exit to new hc area rom.write_byte(0x15B8C + 0x37, 0x1B) diff --git a/data/base2current.bps b/data/base2current.bps index bce1390a3bf9807351fb15408bc4ff3547866f09..82ba7e59286cfe2e14a4d035c37e95edf38084d6 100644 GIT binary patch delta 1106 zcmWks4NOyK6z%QfwLn3{2^OuiHqwg9@6aJX3Q;mg$!w{v6~@K_PP7i06Eg_HYqc8> zS6}c$K3KyFt!OJ_EPIPD7|9shvNYKy$>tW=;?!A{^3!HI=cerKac^>R&b>J~$<4jo zEgk8W_7lWJc_B*>N5oKr8p!V=WAD3z7y8usXqRLU)U#cZ7=SuCCL9s8l|&sZ@r z!=6pr4AP7T?%*VSmi=;dH9!wiD+*#R3Ztc(>{KtZhZW}lRh2wt>SOfz%#Ms&EHb~JV67V2qi>rujfuu-@uYr2HQ4W80pU`A z9E3~!%OPCkS3o%5ABENimC%9;1)M@vK^xSfTfw)gqjKA=YTR;#NqRaTeYqtwkQXQk zln0ImS^^{Gk6KqV0{m+kNx}6+?ba06)@!J6i$IXgn!?za`cpVPv)!t}2d`N*uIMoo zzg1AeQp4D2&7)R)0u9gRtv_fCjV=($>Wdv0EKK9W)?tEh)$a=p`XU@+u@4*s;WQxG z+&XatgVN^`p$e7EX%ido`L?I^e$87($N5w~)H}|PPM~CDn=1nox<2<3>_tiQ+uR95wp#G<-);;)Bf?U(@GNC(g2Rt>voTGgQ{d2RNOpl)(WpnZN0@E06mo782xe z9RHdExlqlm)PN31*kT{5;A*unm?V}YVp%a9f)cK=7;>Xw2mV9{8=;h&(m~V8s5WD# zor*Ww%v@zPJQhc8%eLDmmLmhVzXtT0D8-vzTFuLyM(#5jN+QUbno*C8d**;ya>Xkz zdTCjk=iKYGHLg28Y0RU;{~(~H3X9jObd_Pc3%a635wb{*vIrspA(SFd@1`oLi}o) zX;Y?vGu(duMmNK_HK7C!44i*D+FhfJnHZE2_ z7<~^PFZJG6^AFUg9!#m#m(Vwl-f7%}MP+BfTsE?ON29N3Of*i5eTE6Ov(Fm|T#k1k zaC)yCxb@yR;Ih0b@P#?uUJ!H1!Uiw$Nt|v@CD17R@jzSpN$QTkqk#+e#sEi7sTK3a zsrY&0gnZI^qg&5~Xs%!BLu;vRO`E3V&ZV4g7@Y{zKn3y#YK)C*Tg)0_KXcLtsA5Q|vs z1`9#-~uDwn$%aL?jMI9)kn_cgsptEbd!3|d7 zSd~386hF5SGSHs6#N>$kW)rUd2Gb9b}fOWNicIz5RL#LXgZmHb^NFPjvP*fY#Dpn;=;=M_e zmh1cr9;9;&Q~GaxM!FGGB0=OC%*bXq!aL zri6luh)$0?lRi#*_>HN2uHpJi#t2_b9tc^tq(8$2c>3-VmFtkH3P4{cu9 zxkx$Y*o^c-vt% Date: Tue, 2 Jan 2024 18:14:31 -0700 Subject: [PATCH 100/158] fix: vanilla key logic adjustment (dropshuffle must be none for reduction) --- DoorShuffle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 9b057f61..143f8497 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -262,7 +262,7 @@ def vanilla_key_logic(world, player): world.key_layout[player][builder.name] = key_layout log_key_logic(builder.name, key_layout.key_logic) # special adjustments for vanilla - if world.mode[player] != 'standard': + if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none': # adjust hc doors def adjust_hc_door(door_rule): if door_rule.new_rules[KeyRuleType.WorstCase] == 3: From 37fd1ab5ef8904b871dc4a5b68b4ca3989df8038 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 3 Jan 2024 11:58:17 -0700 Subject: [PATCH 101/158] fix: Aga1 defeatable in rain state for glitched mode fix: Terrorpin AI code excised --- RELEASENOTES.md | 3 +++ Rom.py | 6 ++++-- data/base2current.bps | Bin 117551 -> 117530 bytes 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bd95dd40..5d8f02b2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.0v + * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes + * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? * 1.4.0.1v * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door * Generation: Fix a broken tile pattern diff --git a/Rom.py b/Rom.py index 376e3695..b4ed05f4 100644 --- a/Rom.py +++ b/Rom.py @@ -40,7 +40,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '65b433946c72953780d82cdc0bb8fe8e' +RANDOMIZERBASEHASH = '3640741f6e51a98a0d2962f6bc03636a' class JsonRom(object): @@ -1300,7 +1300,9 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill - rom.write_byte(0x180358, 0x01 if (world.logic[player] in ['owglitches', 'nologic']) else 0x00) + glitches_enabled = world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] + rom.write_byte(0x180358, 0x01 if glitches_enabled else 0x00) + rom.write_byte(0x18008B, 0x01 if glitches_enabled else 0x00) # remove shield from uncle rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) diff --git a/data/base2current.bps b/data/base2current.bps index 82ba7e59286cfe2e14a4d035c37e95edf38084d6..89a5327e66be132ab6071f7ca07a42a72efe78f2 100644 GIT binary patch delta 2796 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 Date: Thu, 4 Jan 2024 11:50:09 -0700 Subject: [PATCH 102/158] fix: typo --- ItemList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ItemList.py b/ItemList.py index 39584262..f8cf2941 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1179,7 +1179,7 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer pool.extend(['Nothing'] * nothings) start_inventory = [x for x in world.precollected_items if x.player == player] - if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name !=' Pegasus Boots' for x in start_inventory): + if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory): precollected_items.append('Pegasus Boots') if 'Pegasus Boots' in pool: pool.remove('Pegasus Boots') From 4201d5d0673f040d7e0cbf8992394f61c8b41cce Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 4 Jan 2024 11:57:52 -0700 Subject: [PATCH 103/158] feat: Laser bridge logic accounts for item functionality for Byrna --- BaseClasses.py | 3 ++- RELEASENOTES.md | 1 + Rules.py | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 73882607..384ce313 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1262,7 +1262,8 @@ class CollectionState(object): return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) def can_avoid_lasers(self, player): - return self.has('Mirror Shield', player) or self.has('Cane of Byrna', player) or self.has('Cape', player) + return (self.has('Mirror Shield', player) or self.has('Cape', player) + or (self.has('Cane of Byrna', player) and self.world.difficulty_adjustments[player] not in ['hard', 'expert'])) def is_not_bunny(self, region, player): if self.has_Pearl(player): diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 5d8f02b2..a127018c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -143,6 +143,7 @@ These are now independent of retro mode and have three options: None, Random, an * 1.4.1.0v * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes + * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? * 1.4.0.1v * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door diff --git a/Rules.py b/Rules.py index f5977389..9abc4887 100644 --- a/Rules.py +++ b/Rules.py @@ -504,10 +504,10 @@ def global_rules(world, player): set_rule(location, lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Final Abyss Balcony Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Final Abyss Ledge Path', player), lambda state: state.has('Cane of Somaria', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.can_avoid_lasers(player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.can_avoid_lasers(player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.can_avoid_lasers(player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.can_avoid_lasers(player)) set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player)) From 744fcd52551c4f71ad424d668501bda3a0ece21a Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 4 Jan 2024 12:56:34 -0700 Subject: [PATCH 104/158] feat: More strict requirment for some bosses on ice --- Bosses.py | 109 +++++++++++++++++++++++++++++++++++++++--------- RELEASENOTES.md | 16 ++++++- 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/Bosses.py b/Bosses.py index 6373fb5c..d42579c8 100644 --- a/Bosses.py +++ b/Bosses.py @@ -5,11 +5,12 @@ from BaseClasses import Boss, FillError from source.enemizer.Bossmizer import boss_adjust -def BossFactory(boss, player): +def BossFactory(boss, player, on_ice=False): if boss is None: return None if boss in boss_table: - enemizer_name, defeat_rule = boss_table[boss] + enemizer_name, normal_defeat_rule, ice_defeat_rule = boss_table[boss] + defeat_rule = ice_defeat_rule if on_ice else normal_defeat_rule return Boss(boss, enemizer_name, defeat_rule, player) logging.getLogger('').error('Unknown Boss: %s', boss) @@ -42,16 +43,21 @@ def MoldormDefeatRule(state, player): def HelmasaurKingDefeatRule(state, player): return (state.has('Hammer', player) or state.can_use_bombs(player)) and (state.has_sword(player) or state.can_shoot_arrows(player)) + +def IceHelmasaurKingDefeatRule(state, player): + return state.can_use_bombs(player) and (state.has_sword(player) or state.can_shoot_arrows(player)) + + def ArrghusDefeatRule(state, player): if not state.has('Hookshot', player): return False - # TODO: ideally we would have a check for bow and silvers, which combined with the - # hookshot is enough. This is not coded yet because the silvers that only work in pyramid feature - # makes this complicated if state.has_blunt_weapon(player): return True - return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or #assuming mostly gitting two puff with one shot + if state.can_shoot_arrows(player) and state.has('Silver Arrows', player) and state.world.difficulty_adjustments[player] not in ['hard', 'expert']: + return True + + return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or # assuming mostly getting two puffs with one shot (state.has('Ice Rod', player) and state.can_use_bombs(player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16)))) @@ -65,9 +71,27 @@ def MothulaDefeatRule(state, player): (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) ) + def BlindDefeatRule(state, player): return state.has_blunt_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player) + +def IceBlindDefeatRule(state, player): + return ( + ( + # weapon + state.has_beam_sword(player) or + state.has('Cane of Somaria', player) or + (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) + ) and + ( + # protection + state.has('Red Shield', player) or + (state.has('Cane of Byrna', player) and state.world.difficulty_adjustments[player] not in ['hard', 'expert']) + ) + ) + + def KholdstareDefeatRule(state, player): return ( ( @@ -91,9 +115,39 @@ def KholdstareDefeatRule(state, player): ) ) + +def IceKholdstareDefeatRule(state, player): + return ( + ( + state.has('Fire Rod', player) or + ( + state.has('Bombos', player) and + # FIXME: the following only actually works for the vanilla location for swordless + (state.has_sword(player) or state.world.swords[player] == 'swordless') + ) + ) and + ( + state.has_beam_sword(player) or + (state.has('Fire Rod', player) and state.can_extend_magic(player, 20)) or + # FIXME: this actually only works for the vanilla location for swordless + ( + state.has('Fire Rod', player) and + state.has('Bombos', player) and + (state.has_sword(player) or state.world.swords[player] == 'swordless') and + state.can_extend_magic(player, 16) + ) + ) + ) + + def VitreousDefeatRule(state, player): return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_blunt_weapon(player) + +def IceVitreousDefeatRule(state, player): + return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_beam_sword(player) + + def TrinexxDefeatRule(state, player): if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)): return False @@ -103,24 +157,36 @@ def TrinexxDefeatRule(state, player): (state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or (state.has_sword(player) and state.can_extend_magic(player, 32))) + +def IceTrinexxDefeatRule(state, player): + if not (state.has('Fire Rod', player) and state.has('Ice Rod', player) and state.has_Boots(player)): + return False + return (state.has('Golden Sword', player) or + (state.has('Tempered Sword', player) and state.can_extend_magic(player, 16)) or + ((state.has('Hammer', player) or + state.has('Master Sword', player)) and state.can_extend_magic(player, 32))) # rod spam rule + + def AgahnimDefeatRule(state, player): return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player) + boss_table = { - 'Armos Knights': ('Armos', ArmosKnightsDefeatRule), - 'Lanmolas': ('Lanmola', LanmolasDefeatRule), - 'Moldorm': ('Moldorm', MoldormDefeatRule), - 'Helmasaur King': ('Helmasaur', HelmasaurKingDefeatRule), - 'Arrghus': ('Arrghus', ArrghusDefeatRule), - 'Mothula': ('Mothula', MothulaDefeatRule), - 'Blind': ('Blind', BlindDefeatRule), - 'Kholdstare': ('Kholdstare', KholdstareDefeatRule), - 'Vitreous': ('Vitreous', VitreousDefeatRule), - 'Trinexx': ('Trinexx', TrinexxDefeatRule), - 'Agahnim': ('Agahnim', AgahnimDefeatRule), - 'Agahnim2': ('Agahnim2', AgahnimDefeatRule) + 'Armos Knights': ('Armos', ArmosKnightsDefeatRule, ArmosKnightsDefeatRule), + 'Lanmolas': ('Lanmola', LanmolasDefeatRule, LanmolasDefeatRule), + 'Moldorm': ('Moldorm', MoldormDefeatRule, MoldormDefeatRule), + 'Helmasaur King': ('Helmasaur', HelmasaurKingDefeatRule, IceHelmasaurKingDefeatRule), + 'Arrghus': ('Arrghus', ArrghusDefeatRule, ArrghusDefeatRule), + 'Mothula': ('Mothula', MothulaDefeatRule, MothulaDefeatRule), + 'Blind': ('Blind', BlindDefeatRule, IceBlindDefeatRule), + 'Kholdstare': ('Kholdstare', KholdstareDefeatRule, IceKholdstareDefeatRule), + 'Vitreous': ('Vitreous', VitreousDefeatRule, IceVitreousDefeatRule), + 'Trinexx': ('Trinexx', TrinexxDefeatRule, IceTrinexxDefeatRule), + 'Agahnim': ('Agahnim', AgahnimDefeatRule, AgahnimDefeatRule), + 'Agahnim2': ('Agahnim2', AgahnimDefeatRule, AgahnimDefeatRule) } + def can_place_boss(world, player, boss, dungeon_name, level=None): if world.swords[player] in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace': return False @@ -133,6 +199,11 @@ def can_place_boss(world, player, boss, dungeon_name, level=None): if boss in ["Blind"]: return False + # no Trinexx on Ice in doors without doing some health modelling + if world.doorShuffle[player] != 'vanilla' and boss == 'Trinexx': + if dungeon_name == 'Ganons Tower' and level == 'bottom': + return False + if dungeon_name == 'Tower of Hera' and boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]: return False @@ -248,4 +319,4 @@ def place_boss(boss, level, loc, loc_text, world, player): loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0] loc_text = loc + ' (' + level + ')' logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) - world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player) + world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player, level == 'bottom') diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a127018c..9e79ae6d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -142,8 +142,22 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes * 1.4.1.0v - * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes + * Logic: New logic for some bosses on ice + * Helmasaur on Ice: Bombs for mask, sword or arrows for 2nd phase + * Blind on Ice: Beam sword, Somaria, or Byrna plus magic extension for damage. Red shield or Byrna for protection. + * Kholdstare on Ice: Three options (after cracking the shell) + * Beam sword + * Fire Rod with 1.5 magic extensions + * Fire Rod & Bombos & any Sword & 1 Magic Extension + * Vitreous on Ice: Arrows and Bombs or a Beam Sword + * Trinexx on Ice: Boots always required for dodging. Damage options: + * Gold sword + * Tempered with magic extension + * Hammer or Master Sword with 3 magic extensions (Rod spam for elemental heads, non ideal for last phase) + * Trinexx on Ice forbidden in doors seeds until we can model some health requirements. Low health Trinexx still isn't realistically feasible (bascially playing OHKO) + * Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert + * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? * 1.4.0.1v * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door From 0222b09aab3fdad50e5504bcde17e029189b1c2d Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 4 Jan 2024 13:00:12 -0700 Subject: [PATCH 105/158] feat: Restore Lanmo/Trinexx as possible lobbies --- Doors.py | 12 ++++++------ RELEASENOTES.md | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Doors.py b/Doors.py index ad83b67f..4c9611fc 100644 --- a/Doors.py +++ b/Doors.py @@ -250,7 +250,7 @@ def create_doors(world, player): create_door(player, 'Desert Tiles 2 NE', Intr).dir(No, 0x43, Right, High).small_key().pos(1), create_door(player, 'Desert Wall Slide SE', Intr).dir(So, 0x43, Right, High).small_key().pos(1), create_door(player, 'Desert Wall Slide NW', Nrml).dir(No, 0x43, Left, High).big_key().pos(0).no_entrance(), - create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), problem with enemizer + create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00), # Hera create_door(player, 'Hera Lobby S', Nrml).dir(So, 0x77, Mid, Low).pos(0).portal(Z, 0x22, 1), @@ -1074,7 +1074,7 @@ def create_doors(world, player): create_door(player, 'TR Final Abyss Balcony Path', Lgcl), create_door(player, 'TR Final Abyss Ledge Path', Lgcl), create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0), - create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), -enemizer doesn't work + create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00), create_door(player, 'GT Lobby S', Nrml).dir(So, 0x0c, Mid, High).pos(0).portal(Z, 0x22), create_door(player, 'GT Lobby Left Down Stairs', Sprl).dir(Dn, 0x0c, 1, HTL).ss(A, 0x0f, 0x80), @@ -1512,8 +1512,8 @@ def create_doors(world, player): world.get_door('TR Big Chest Entrance SE', player).passage = False world.get_door('Sewers Secret Room Key Door S', player).dungeonLink = 'Hyrule Castle' world.get_door('Desert Cannonball S', player).dead_end() - # world.get_door('Desert Boss SW', player).dead_end() - # world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace' + world.get_door('Desert Boss SW', player).dead_end() + world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace' world.get_door('Skull 1 Lobby S', player).dungeonLink = 'Skull Woods' world.get_door('Skull Map Room SE', player).dungeonLink = 'Skull Woods' world.get_door('Skull Spike Corner SW', player).dungeonLink = 'Skull Woods' @@ -1523,8 +1523,8 @@ def create_doors(world, player): world.get_door('Mire Right Bridge SE', player).dead_end(allowPassage=True) world.get_door('TR Roller Room SW', player).dead_end() world.get_door('TR Tile Room SE', player).dead_end() - # world.get_door('TR Boss SW', player).dead_end() - # world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock' + world.get_door('TR Boss SW', player).dead_end() + world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock' world.get_door('GT Petting Zoo SE', player).dead_end() world.get_door('GT DMs Room SW', player).dead_end() world.get_door("GT Bob\'s Room SE", player).passage = False diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9e79ae6d..1aace9bb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -158,6 +158,7 @@ These are now independent of retro mode and have three options: None, Random, an * Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes + * Generation: Trinexx and Lanmolas room allowed as lobbies in intensity 3 (works with enemizer now) * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? * 1.4.0.1v * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door From 62162b6fbc6c69a5134d95b5d1384ae7170aaf97 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 4 Jan 2024 14:54:30 -0700 Subject: [PATCH 106/158] fix: maiden deleted instead of replaced with ped tex fix: thief damage to player is not randomized --- RELEASENOTES.md | 1 + source/enemizer/Bossmizer.py | 4 ++-- source/enemizer/Enemizer.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1aace9bb..427aed81 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -157,6 +157,7 @@ These are now independent of retro mode and have three options: None, Random, an * Trinexx on Ice forbidden in doors seeds until we can model some health requirements. Low health Trinexx still isn't realistically feasible (bascially playing OHKO) * Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert + * Enemzier Damage: Thief damage to player is not randomized * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes * Generation: Trinexx and Lanmolas room allowed as lobbies in intensity 3 (works with enemizer now) * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py index 39d190a4..398cba89 100644 --- a/source/enemizer/Bossmizer.py +++ b/source/enemizer/Bossmizer.py @@ -178,8 +178,8 @@ def boss_writes(world, player, rom): remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xF95) if boss.name != 'Blind' and dungeon.name == 'Thieves Town' and level is None: rom.write_byte(snes_to_pc(0x368101), 1) # set blind boss door flag - # maiden becomes a random invisible enemy - data_tables.uw_enemy_table.room_map[0x45][0].kind = EnemySprite.PedestalPlaque + # maiden is deleted + del data_tables.uw_enemy_table.room_map[0x45][0] if not arrghus_can_swim and water_tiles_on: remove_water_tiles(data_tables) diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index f4139fd0..74af5a44 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -422,7 +422,7 @@ skip_sprites = { 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 + EnemySprite.PositionTarget, EnemySprite.Boulders, EnemySprite.Thief } From 2c3e47d2f67c888cb628e74828140c2c29e386ef Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 5 Jan 2024 12:11:56 -0700 Subject: [PATCH 107/158] feat: enemizer damage rework --- RELEASENOTES.md | 8 +++++--- source/enemizer/Enemizer.py | 34 ++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 427aed81..c9446323 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -152,12 +152,14 @@ These are now independent of retro mode and have three options: None, Random, an * Vitreous on Ice: Arrows and Bombs or a Beam Sword * Trinexx on Ice: Boots always required for dodging. Damage options: * Gold sword - * Tempered with magic extension - * Hammer or Master Sword with 3 magic extensions (Rod spam for elemental heads, non ideal for last phase) + * Tempered sword with magic extension + * Hammer or Master sword with 3 magic extensions (Rod spam for elemental heads, non-ideal weapon for last phase) * Trinexx on Ice forbidden in doors seeds until we can model some health requirements. Low health Trinexx still isn't realistically feasible (bascially playing OHKO) * Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert - * Enemzier Damage: Thief damage to player is not randomized + * Enemizer Damage Rework: + * Shuffled: Actually shuffles the damage groups in the table instead of picking random numbers and reducing for mails from there. Enemies will still be assigned to a damage group randomly. + * There will always be at least one group which does no damage. The thief will always be in that group. Ganon always has his own group. * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes * Generation: Trinexx and Lanmolas room allowed as lobbies in intensity 3 (works with enemizer now) * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index 74af5a44..b4715268 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -422,7 +422,7 @@ skip_sprites = { 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, EnemySprite.Thief + EnemySprite.PositionTarget, EnemySprite.Boulders } @@ -474,21 +474,39 @@ def randomize_enemies(world, player): stat.damage = stats[EnemySprite.GreenEyegoreMimic].damage # these share data elif sprite == EnemySprite.RedMimic: stat.damage = stats[EnemySprite.RedEyegoreMimic].damage # these share data + elif sprite == EnemySprite.Thief: # always group 0 for 0 damage + stat.damage = 0 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 + original_table = [ + (0x02, 0x01, 0x01), + (0x04, 0x04, 0x04), + (0x00, 0x00, 0x00), + (0x08, 0x04, 0x02), + (0x08, 0x08, 0x08), + (0x10, 0x08, 0x04), + (0x20, 0x10, 0x08), + (0x20, 0x18, 0x10), + (0x18, 0x10, 0x08), + (0x40, 0x30, 0x18)] 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) + if i == 0: # group 0 will always be 0 for thieves + green_mail, blue_mail, red_mail = 0, 0, 0 + del original_table[2] else: - blue_mail = (green_mail * 3) // 4 - red_mail = (green_mail * 3) // 8 + if world.enemy_damage[player] == 'random': + green_mail = random.randint(0, 64) + if world.enemy_damage[player] == 'random': + blue_mail = random.randint(0, 64) + red_mail = random.randint(0, 64) + else: + idx = random.randint(0, len(original_table)-1) + green_mail, blue_mail, red_mail = original_table[idx] + del original_table[idx] world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail] From 0b7b82d02794fdd5bc052decaa8b1ea3fd575d88 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 12 Jun 2023 14:50:33 -0500 Subject: [PATCH 108/158] Split overworld model up by screen --- BaseClasses.py | 19 +- DoorShuffle.py | 8 +- DungeonGenerator.py | 4 +- ER_hint_reference.txt | 4 +- EntranceShuffle.py | 325 ++------ Fill.py | 2 +- ItemList.py | 6 +- Main.py | 3 +- OverworldShuffle.py | 1035 +++++++++++++++++++++++++- Plando.py | 2 + PotShuffle.py | 12 +- Regions.py | 320 +++++--- Rom.py | 4 +- Rules.py | 425 +++++++---- Utils.py | 21 + source/item/District.py | 8 +- source/overworld/EntranceShuffle2.py | 288 ++----- test/inverted/TestInverted.py | 2 + test/inverted_owg/TestInvertedOWG.py | 2 + test/owg/TestVanillaOWG.py | 2 + test/vanilla/TestVanilla.py | 2 + 21 files changed, 1678 insertions(+), 816 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 384ce313..ab4f4158 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1135,11 +1135,11 @@ class CollectionState(object): def can_lift_rocks(self, player): return self.has('Power Glove', player) or self.has('Titans Mitts', player) - - def can_bomb_clip(self, region, player: int) -> bool: + + def can_bomb_clip(self, region, player: int) -> bool: return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) and self.can_use_bombs(player) - - def can_dash_clip(self, region, player: int) -> bool: + + def can_dash_clip(self, region, player: int) -> bool: return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) def has_bottle(self, player): @@ -1254,7 +1254,7 @@ class CollectionState(object): def can_flute(self, player): if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player): return False # can't flute in rain state - lw = self.world.get_region('Light World', player) + lw = self.world.get_region('Kakariko Village', player) return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self) and self.is_not_bunny(lw, player)) @@ -1311,7 +1311,7 @@ class CollectionState(object): def can_superbunny_mirror_with_sword(self, player): return self.has_Mirror(player) and self.has_sword(player) - + def can_bunny_pocket(self, player): return self.has_Boots(player) and (self.has_Mirror(player) or self.has_bottle(player)) @@ -1456,6 +1456,12 @@ class CollectionState(object): raise RuntimeError('Cannot parse %s.' % item) +@unique +class Terrain(Enum): + Land = 0 + Water = 1 + + @unique class RegionType(Enum): Menu = 0 @@ -1484,6 +1490,7 @@ class Region(object): self.is_light_world = False # will be set aftermaking connections. self.is_dark_world = False self.spot_type = 'Region' + self.terrain = None self.hint_text = hint self.recursion_count = 0 self.player = player diff --git a/DoorShuffle.py b/DoorShuffle.py index 143f8497..bf646a45 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -478,7 +478,7 @@ def choose_portals(world, player): if portal_region.type == RegionType.LightWorld: world.get_portal(portal, player).light_world = True if name in world.inaccessible_regions[player] or (hc_flag and portal != 'Hyrule Castle South'): - name_key = 'Desert Ledge' if name == 'Desert Palace Entrance (North) Spot' else name + name_key = 'Desert Ledge' if name == 'Desert Ledge Keep' else name region_map[name_key].append(portal) inaccessible_portals.append(portal) else: @@ -631,7 +631,7 @@ def analyze_portals(world, player): if portal_region.type == RegionType.LightWorld: world.get_portal(portal, player).light_world = True if name in world.inaccessible_regions[player]: - name_key = 'Desert Ledge' if name == 'Desert Palace Entrance (North) Spot' else name + name_key = 'Desert Ledge' if name == 'Desert Ledge Keep' else name region_map[name_key].append(portal) inaccessible_portals.append(portal) else: @@ -3368,7 +3368,7 @@ def find_accessible_entrances(world, player, builder): hc_std = True start_regions = ['Hyrule Castle Courtyard'] elif world.mode[player] != 'inverted': - start_regions = ['Links House', 'Sanctuary', 'East Dark World'] + start_regions = ['Links House', 'Sanctuary', 'Pyramid Area'] else: start_regions = ['Links House', 'Dark Sanctuary Hint', 'Hyrule Castle Ledge'] regs = convert_regions(start_regions, world, player) @@ -3390,7 +3390,7 @@ def find_accessible_entrances(world, player, builder): if connect not in queue and connect not in visited_regions: queue.append(connect) for ext in next_region.exits: - if hc_std and ext.name in ['Hyrule Castle Main Gate (North)', 'Castle Gate Teleporter', 'Hyrule Castle Ledge Drop']: # just skip it + if hc_std and ext.name in ['Hyrule Castle Main Gate (North)', 'Castle Gate Teleporter (Inner)', 'Hyrule Castle Ledge Drop']: # just skip it continue connect = ext.connected_region if connect is None or ext.door and ext.door.blocked: diff --git a/DungeonGenerator.py b/DungeonGenerator.py index ed0a7b78..5f34fbf2 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -1538,8 +1538,8 @@ def calc_allowance_and_dead_ends(builder, connections_tuple, world, player): if entrance in connections.keys(): enabling_region = connections[entrance] check_list = list(potentials[enabling_region]) - if enabling_region.name in ['Desert Ledge', 'Desert Palace Entrance (North) Spot']: - alternate = 'Desert Palace Entrance (North) Spot' if enabling_region.name == 'Desert Ledge' else 'Desert Ledge' + if enabling_region.name in ['Desert Ledge', 'Desert Ledge Keep']: + alternate = 'Desert Ledge Keep' if enabling_region.name == 'Desert Ledge' else 'Desert Ledge' if world.get_region(alternate, player) in potentials: check_list.extend(potentials[world.get_region(alternate, player)]) connecting_entrances = [x for x in check_list if x != entrance and x not in dead_entrances and x not in drop_entrances_allowance] diff --git a/ER_hint_reference.txt b/ER_hint_reference.txt index 018dfb3a..2752961a 100644 --- a/ER_hint_reference.txt +++ b/ER_hint_reference.txt @@ -188,7 +188,7 @@ Dark World Shop: The hammer sealed building Red Shield Shop: The fenced in building Mire Shed: The western hut in the mire East Dark World Hint: The dark cave near the eastmost portal -Dark Desert Hint: The cave east of the mire +Mire Hint: The cave east of the mire Spike Cave: The ledge cave on west dark DM Palace of Darkness Hint: The building south of Kiki Dark Lake Hylia Ledge Spike Cave: The rock SE dark Lake Hylia @@ -200,7 +200,7 @@ Hype Cave: The cave south of the old bomb shop Brewery: The Village of Outcasts building with no door Dark Lake Hylia Ledge Hint: The open cave SE dark Lake Hylia Chest Game: The westmost building in the Village of Outcasts -Dark Desert Fairy: The eastern hut in the mire +Mire Fairy: The eastern hut in the mire Dark Lake Hylia Ledge Fairy: The sealed cave SE dark Lake Hylia Fortune Teller (Dark): The building NE the Village of Outcasts Sanctuary: Sanctuary diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 020e95a7..95cf696f 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -20,13 +20,6 @@ def link_entrances(world, player): for exitname, regionname in mandatory_connections: connect_simple(world, exitname, regionname, player) - if not invFlag: - for exitname, regionname in open_mandatory_connections: - connect_simple(world, exitname, regionname, player) - else: - for exitname, regionname in inverted_mandatory_connections: - connect_simple(world, exitname, regionname, player) - connect_custom(world, player) # if we do not shuffle, set default connections @@ -1579,7 +1572,7 @@ DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Red Shield Shop', 'Mire Shed', 'East Dark World Hint', - 'Dark Desert Hint', + 'Mire Hint', 'Spike Cave', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', @@ -1592,7 +1585,7 @@ DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Brewery', 'Dark Lake Hylia Ledge Hint', 'Chest Game', - 'Dark Desert Fairy', + 'Mire Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Hammer Peg Cave'] @@ -1657,8 +1650,8 @@ Bomb_Shop_Single_Cave_Doors = ['Waterfall of Wishing', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', @@ -1723,8 +1716,8 @@ Single_Cave_Targets = ['Blinds Hideout', 'Dark Lumberjack Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Healer Fairy', + 'Mire Hint', + 'Mire Healer Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Healer Fairy', @@ -1867,7 +1860,7 @@ Inverted_DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Red Shield Shop', 'Mire Shed', 'East Dark World Hint', - 'Dark Desert Hint', + 'Mire Hint', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Death Mountain Shop', @@ -1879,7 +1872,7 @@ Inverted_DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Brewery', 'Dark Lake Hylia Ledge Hint', 'Chest Game', - 'Dark Desert Fairy', + 'Mire Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Hammer Peg Cave'] @@ -1914,8 +1907,8 @@ Inverted_Bomb_Shop_Single_Cave_Doors = ['Waterfall of Wishing', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Bumper Cave (Top)', @@ -2010,8 +2003,8 @@ Inverted_Single_Cave_Targets = ['Blinds Hideout', 'Dark Lumberjack Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Healer Fairy', + 'Mire Hint', + 'Mire Healer Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Healer Fairy', @@ -2067,9 +2060,7 @@ Inverted_Must_Exit_Invalid_Connections = defaultdict(set, { # these are connections that cannot be shuffled and always exist. # They link together separate parts of the world we need to divide into regions -mandatory_connections = [('Links House S&Q', 'Links House'), - - # underworld +mandatory_connections = [# underworld ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), ('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'), @@ -2116,212 +2107,18 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), ('Bumper Cave Bottom to Top', 'Bumper Cave (top)'), ('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'), - ('Ganon Drop', 'Bottom of Pyramid'), - - # water entry - ('Waterfall Fairy Access', 'Zora Waterfall Entryway'), - ('Zora Waterfall Water Drop', 'Lake Hylia Water'), - ('Light World Water Drop', 'Lake Hylia Water'), - ('Potion Shop Water Drop', 'Lake Hylia Water'), - ('Northeast Light World Water Drop', 'Lake Hylia Water'), - ('Lake Hylia Central Island Water Drop', 'Lake Hylia Water'), - - ('West Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Northeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Catfish Water Drop', 'Dark Lake Hylia Water'), - ('East Dark World Water Drop', 'Dark Lake Hylia Water'), - ('South Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Southeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Ice Palace Leave Water Drop', 'Dark Lake Hylia Water'), - - # water exit - ('Light World Pier', 'Light World'), # there are several piers in-game, only one needs to be modeled - ('Potion Shop Pier', 'Potion Shop Area'), - ('Hobo Pier', 'Hobo Bridge'), - ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), - ('Lake Hylia Whirlpool', 'Northeast Light World'), - - ('Northeast Dark World Pier', 'Northeast Dark World'), - ('East Dark World Pier', 'East Dark World'), - ('Southeast Dark World Pier', 'Southeast Dark World'), - - # terrain - ('Master Sword Meadow', 'Master Sword Meadow'), - ('DM Hammer Bridge (West)', 'East Death Mountain (Top)'), - ('DM Hammer Bridge (East)', 'West Death Mountain (Top)'), - ('DM Broken Bridge (West)', 'East Death Mountain (Bottom)'), - ('DM Broken Bridge (East)', 'West Death Mountain (Bottom)'), - ('Fairy Ascension Rocks', 'Fairy Ascension Plateau'), - ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), - ('Zoras Domain', 'Zoras Domain'), - ('Kings Grave Rocks (Outer)', 'Kings Grave Area'), - ('Kings Grave Rocks (Inner)', 'Light World'), - ('Potion Shop Rock (South)', 'Northeast Light World'), - ('Potion Shop Rock (North)', 'Potion Shop Area'), - ('Kakariko Southwest Bush (North)', 'Bomb Hut Area'), - ('Kakariko Southwest Bush (South)', 'Light World'), - ('Kakariko Yard Bush (North)', 'Light World'), - ('Kakariko Yard Bush (South)', 'Bush Covered Lawn'), - ('Hyrule Castle Courtyard Bush (North)', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Secret Entrance Area'), - ('Hyrule Castle Main Gate', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Main Gate (North)', 'Light World'), - ('Wooden Bridge Bush (North)', 'Light World'), - ('Wooden Bridge Bush (South)', 'Potion Shop Area'), - ('Bat Cave Ledge Peg', 'Bat Cave Ledge'), - ('Bat Cave Ledge Peg (East)', 'Light World'), - ('Desert Statue Move', 'Desert Palace Stairs'), - ('Desert Ledge Rocks (Outer)', 'Desert Palace Entrance (North) Spot'), - ('Desert Ledge Rocks (Inner)', 'Desert Ledge'), - - ('Skull Woods Forest', 'Skull Woods Forest'), - ('East Dark Death Mountain Bushes', 'East Dark Death Mountain (Bushes)'), - ('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'), - ('Dark Witch Rock (North)', 'Northeast Dark World'), - ('Dark Witch Rock (South)', 'Catfish Area'), - ('Grassy Lawn Pegs (Top)', 'West Dark World'), - ('Grassy Lawn Pegs (Bottom)', 'Dark Grassy Lawn'), - ('West Dark World Gap', 'West Dark World'), - ('Broken Bridge Pass (Top)', 'East Dark World'), - ('Broken Bridge Pass (Bottom)', 'Northeast Dark World'), - ('Dark Graveyard Bush (South)', 'Dark Graveyard North'), - ('Dark Graveyard Bush (North)', 'West Dark World'), - ('Peg Area Rocks (Left)', 'Hammer Peg Area'), - ('Peg Area Rocks (Right)', 'West Dark World'), - ('Village of Outcasts Heavy Rock', 'West Dark World'), - ('Hammer Bridge Pegs (North)', 'South Dark World'), - ('Hammer Bridge Pegs (South)', 'East Dark World'), - ('Ice Island To East Pier', 'East Dark World'), - - # ledge drops - ('Spectacle Rock Drop', 'West Death Mountain (Top)'), - ('Death Mountain Drop', 'West Death Mountain (Bottom)'), - ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), - ('Fairy Ascension Ledge Access', 'Fairy Ascension Ledge'), - ('East Death Mountain Drop', 'East Death Mountain (Bottom)'), - ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), - ('Fairy Ascension Drop', 'East Death Mountain (Bottom)'), - ('Death Mountain Entrance Drop', 'Light World'), - ('Death Mountain Return Ledge Drop', 'Light World'), - ('Graveyard Ledge Drop', 'Light World'), - ('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Ledge Drop', 'Light World'), - ('Maze Race Ledge Drop', 'Light World'), - ('Desert Ledge Drop', 'Light World'), - ('Desert Palace Mouth Drop', 'Light World'), - ('Checkerboard Ledge Drop', 'Light World'), - ('Desert Teleporter Drop', 'Light World'), - ('Cave 45 Ledge Drop', 'Light World'), - - ('Dark Death Mountain Drop (West)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Drop (East)', 'East Dark Death Mountain (Bottom)'), - ('Floating Island Drop', 'Dark Death Mountain (Top)'), - ('Turtle Rock Drop', 'Dark Death Mountain (Top)'), - ('Bumper Cave Entrance Drop', 'West Dark World'), - ('Bumper Cave Ledge Drop', 'West Dark World'), - ('Pyramid Drop', 'East Dark World'), - ('Village of Outcasts Drop', 'South Dark World'), - ('Dark Desert Drop', 'Dark Desert') + ('Ganon Drop', 'Bottom of Pyramid') ] -open_mandatory_connections = [('Sanctuary S&Q', 'Sanctuary'), - ('Old Man S&Q', 'Old Man House'), - ('Other World S&Q', 'East Dark World'), - - # flute - ('Flute Spot 1', 'West Death Mountain (Bottom)'), - ('Flute Spot 2', 'Potion Shop Area'), - ('Flute Spot 3', 'Light World'), - ('Flute Spot 4', 'Light World'), - ('Flute Spot 5', 'Light World'), - ('Flute Spot 6', 'Desert Teleporter Ledge'), - ('Flute Spot 7', 'Light World'), - ('Flute Spot 8', 'Light World'), - ('LW Flute', 'Flute Sky'), - ('NWLW Flute', 'Flute Sky'), - ('ZLW Flute', 'Flute Sky'), - ('DM Flute', 'Flute Sky'), - ('EDM Flute', 'Flute Sky'), - - # portals - ('Death Mountain Teleporter', 'West Dark Death Mountain (Bottom)'), - ('East Death Mountain Teleporter', 'East Dark Death Mountain (Bottom)'), - ('Turtle Rock Teleporter', 'Turtle Rock (Top)'), - ('Kakariko Teleporter', 'West Dark World'), - ('Castle Gate Teleporter', 'East Dark World'), - ('East Hyrule Teleporter', 'East Dark World'), - ('South Hyrule Teleporter', 'South Dark World'), - ('Desert Teleporter', 'Dark Desert'), - ('Lake Hylia Teleporter', 'Dark Lake Hylia Central Island') - ] - -inverted_mandatory_connections = [('Sanctuary S&Q', 'Dark Sanctuary Hint'), - ('Old Man S&Q', 'West Dark Death Mountain (Bottom)'), - ('Other World S&Q', 'Hyrule Castle Ledge'), - - # flute - ('Flute Spot 1', 'West Dark Death Mountain (Bottom)'), - ('Flute Spot 2', 'Northeast Dark World'), - ('Flute Spot 3', 'West Dark World'), - ('Flute Spot 4', 'South Dark World'), - ('Flute Spot 5', 'East Dark World'), - ('Flute Spot 6', 'Dark Desert Ledge'), - ('Flute Spot 7', 'South Dark World'), - ('Flute Spot 8', 'Southeast Dark World'), - ('DDM Flute', 'Flute Sky'), - ('NEDW Flute', 'Flute Sky'), - ('WDW Flute', 'Flute Sky'), - ('SDW Flute', 'Flute Sky'), - ('EDW Flute', 'Flute Sky'), - ('DD Flute', 'Flute Sky'), - ('DLHL Flute', 'Flute Sky'), - ('EDDM Flute', 'Flute Sky'), - ('Dark Grassy Lawn Flute', 'Flute Sky'), - ('Hammer Peg Area Flute', 'Flute Sky'), - - # modified terrain - ('Spectacle Rock Approach', 'Spectacle Rock'), - ('Spectacle Rock Leave', 'West Death Mountain (Top)'), - ('Floating Island Bridge (West)', 'East Death Mountain (Top)'), - ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), - ('Graveyard Ladder (Top)', 'Light World'), - ('Graveyard Ladder (Bottom)', 'Graveyard Ledge'), - ('Mimic Cave Ledge Access', 'Mimic Cave Ledge'), - ('Mimic Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Checkerboard Ledge Approach', 'Desert Checkerboard Ledge'), - ('Checkerboard Ledge Leave', 'Light World'), - ('Cave 45 Approach', 'Cave 45 Ledge'), - ('Cave 45 Leave', 'Light World'), - ('Lake Hylia Island Pier', 'Lake Hylia Island'), - ('Bombos Tablet Ladder (Top)', 'Light World'), - ('Bombos Tablet Ladder (Bottom)', 'Bombos Tablet Ledge'), - ('Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Ladder (Bottom)', 'Dark Death Mountain (Top)'), - ('Turtle Rock Tail Drop', 'Turtle Rock (Top)'), - ('Ice Palace Approach', 'Dark Lake Hylia Central Island'), - - # portals - ('Dark Death Mountain Teleporter (West)', 'West Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Bottom)', 'East Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Top)', 'East Death Mountain (Top)'), - ('West Dark World Teleporter', 'Light World'), - ('Post Aga Teleporter', 'Light World'), - ('East Dark World Teleporter', 'Light World'), - ('South Dark World Teleporter', 'Light World'), - ('Dark Desert Teleporter', 'Light World'), - ('Dark Lake Hylia Teleporter', 'Lake Hylia Central Island') - ] - # non-shuffled entrance links default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Lost Woods Hideout Drop', 'Lost Woods Hideout (top)'), ('Lost Woods Hideout Stump', 'Lost Woods Hideout (bottom)'), - ('Lost Woods Hideout Exit', 'Light World'), + ('Lost Woods Hideout Exit', 'Lost Woods East Area'), ('Lumberjack House', 'Lumberjack House'), ('Lumberjack Tree Tree', 'Lumberjack Tree (top)'), ('Lumberjack Tree Cave', 'Lumberjack Tree (bottom)'), - ('Lumberjack Tree Exit', 'Light World'), + ('Lumberjack Tree Exit', 'Lumberjack Area'), ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave (right)'), ('Death Mountain Return Cave Exit (East)', 'West Death Mountain (Bottom)'), ('Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Peak)'), @@ -2354,22 +2151,22 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Fortune Teller (Light)', 'Fortune Teller (Light)'), ('Bonk Rock Cave', 'Bonk Rock Cave'), ('Sanctuary', 'Sanctuary Portal'), - ('Sanctuary Exit', 'Light World'), + ('Sanctuary Exit', 'Sanctuary Area'), ('Sanctuary Grave', 'Sewer Drop'), ('Graveyard Cave', 'Graveyard Cave'), ('Kings Grave', 'Kings Grave'), ('North Fairy Cave Drop', 'North Fairy Cave'), ('North Fairy Cave', 'North Fairy Cave'), - ('North Fairy Cave Exit', 'Light World'), + ('North Fairy Cave Exit', 'River Bend Area'), ('Potion Shop', 'Potion Shop'), ('Kakariko Well Drop', 'Kakariko Well (top)'), ('Kakariko Well Cave', 'Kakariko Well (bottom)'), - ('Kakariko Well Exit', 'Light World'), + ('Kakariko Well Exit', 'Kakariko Village'), ('Blinds Hideout', 'Blinds Hideout'), ('Elder House (West)', 'Elder House'), ('Elder House (East)', 'Elder House'), - ('Elder House Exit (West)', 'Light World'), - ('Elder House Exit (East)', 'Light World'), + ('Elder House Exit (West)', 'Kakariko Village'), + ('Elder House Exit (East)', 'Kakariko Village'), ('Snitch Lady (West)', 'Snitch Lady (West)'), ('Snitch Lady (East)', 'Snitch Lady (East)'), ('Bush Covered House', 'Bush Covered House'), @@ -2381,16 +2178,16 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Tavern (Front)', 'Tavern (Front)'), ('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance'), ('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance'), - ('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Area'), + ('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Courtyard Northeast'), ('Sahasrahlas Hut', 'Sahasrahlas Hut'), ('Blacksmiths Hut', 'Blacksmiths Hut'), ('Bat Cave Drop', 'Bat Cave (right)'), ('Bat Cave Cave', 'Bat Cave (left)'), - ('Bat Cave Exit', 'Light World'), + ('Bat Cave Exit', 'Blacksmith Area'), ('Two Brothers House (West)', 'Two Brothers House'), ('Two Brothers House Exit (West)', 'Maze Race Ledge'), ('Two Brothers House (East)', 'Two Brothers House'), - ('Two Brothers House Exit (East)', 'Light World'), + ('Two Brothers House Exit (East)', 'Kakariko Suburb Area'), ('Library', 'Library'), ('Kakariko Gamble Game', 'Kakariko Gamble Game'), ('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), @@ -2416,9 +2213,9 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Hookshot Cave Back Exit', 'Dark Death Mountain Floating Island'), ('Hookshot Cave Back Entrance', 'Hookshot Cave (Back)'), ('Hookshot Cave', 'Hookshot Cave (Front)'), - ('Hookshot Cave Front Exit', 'Dark Death Mountain (Top)'), + ('Hookshot Cave Front Exit', 'East Dark Death Mountain (Top)'), ('Superbunny Cave (Top)', 'Superbunny Cave (Top)'), - ('Superbunny Cave Exit (Top)', 'Dark Death Mountain (Top)'), + ('Superbunny Cave Exit (Top)', 'East Dark Death Mountain (Top)'), ('Superbunny Cave (Bottom)', 'Superbunny Cave (Bottom)'), ('Superbunny Cave Exit (Bottom)', 'East Dark Death Mountain (Bottom)'), ('Dark Death Mountain Shop', 'Dark Death Mountain Shop'), @@ -2438,8 +2235,8 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'), ('East Dark World Hint', 'East Dark World Hint'), ('Mire Shed', 'Mire Shed'), - ('Dark Desert Fairy', 'Dark Desert Healer Fairy'), - ('Dark Desert Hint', 'Dark Desert Hint'), + ('Mire Fairy', 'Mire Healer Fairy'), + ('Mire Hint', 'Mire Hint'), ('Hype Cave', 'Hype Cave'), ('Dark Lake Hylia Shop', 'Dark Lake Hylia Shop'), ('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy'), @@ -2448,18 +2245,18 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ] open_default_connections = [('Links House', 'Links House'), - ('Links House Exit', 'Light World'), + ('Links House Exit', 'Links House Area'), ('Big Bomb Shop', 'Big Bomb Shop'), ('Old Man Cave (West)', 'Old Man Cave Ledge'), ('Old Man Cave (East)', 'Old Man Cave (East)'), - ('Old Man Cave Exit (West)', 'Light World'), + ('Old Man Cave Exit (West)', 'Mountain Pass Entry'), ('Old Man Cave Exit (East)', 'West Death Mountain (Bottom)'), ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave (left)'), - ('Death Mountain Return Cave Exit (West)', 'Death Mountain Return Ledge'), + ('Death Mountain Return Cave Exit (West)', 'Mountain Pass Ledge'), ('Bumper Cave (Bottom)', 'Bumper Cave (bottom)'), ('Bumper Cave (Top)', 'Bumper Cave (top)'), ('Bumper Cave Exit (Top)', 'Bumper Cave Ledge'), - ('Bumper Cave Exit (Bottom)', 'West Dark World'), + ('Bumper Cave Exit (Bottom)', 'Bumper Cave Entry'), ('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy'), ('Pyramid Hole', 'Pyramid'), ('Pyramid Entrance', 'Bottom of Pyramid'), @@ -2467,19 +2264,19 @@ open_default_connections = [('Links House', 'Links House'), ] inverted_default_connections = [('Links House', 'Big Bomb Shop'), - ('Links House Exit', 'South Dark World'), + ('Links House Exit', 'Big Bomb Shop Area'), ('Big Bomb Shop', 'Links House'), - ('Dark Sanctuary Hint Exit', 'West Dark World'), + ('Dark Sanctuary Hint Exit', 'Dark Chapel Area'), ('Old Man Cave (West)', 'Bumper Cave (bottom)'), ('Old Man Cave (East)', 'Death Mountain Return Cave (left)'), - ('Old Man Cave Exit (West)', 'West Dark World'), + ('Old Man Cave Exit (West)', 'Bumper Cave Entry'), ('Old Man Cave Exit (East)', 'West Dark Death Mountain (Bottom)'), ('Death Mountain Return Cave (West)', 'Bumper Cave (top)'), ('Death Mountain Return Cave Exit (West)', 'West Death Mountain (Bottom)'), ('Bumper Cave (Bottom)', 'Old Man Cave Ledge'), ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), - ('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'), - ('Bumper Cave Exit (Bottom)', 'Light World'), + ('Bumper Cave Exit (Top)', 'Mountain Pass Ledge'), + ('Bumper Cave Exit (Bottom)', 'Mountain Pass Entry'), ('Dark Death Mountain Fairy', 'Old Man Cave (East)'), ('Inverted Pyramid Hole', 'Pyramid'), ('Inverted Pyramid Entrance', 'Bottom of Pyramid'), @@ -2497,19 +2294,19 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Desert Palace Entrance (West)', 'Desert West Portal'), ('Desert Palace Entrance (North)', 'Desert Back Portal'), ('Desert Palace Entrance (East)', 'Desert East Portal'), - ('Desert Palace Exit (South)', 'Desert Palace Stairs'), + ('Desert Palace Exit (South)', 'Desert Stairs'), ('Desert Palace Exit (West)', 'Desert Ledge'), - ('Desert Palace Exit (East)', 'Desert Palace Mouth'), - ('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'), + ('Desert Palace Exit (East)', 'Desert Mouth'), + ('Desert Palace Exit (North)', 'Desert Ledge Keep'), ('Eastern Palace', 'Eastern Portal'), - ('Eastern Palace Exit', 'Light World'), + ('Eastern Palace Exit', 'Eastern Palace Area'), ('Tower of Hera', 'Hera Portal'), ('Tower of Hera Exit', 'West Death Mountain (Top)'), ('Palace of Darkness', 'Palace of Darkness Portal'), - ('Palace of Darkness Exit', 'East Dark World'), + ('Palace of Darkness Exit', 'Palace of Darkness Area'), ('Swamp Palace', 'Swamp Portal'), # requires additional patch for flooding moat if moved - ('Swamp Palace Exit', 'South Dark World'), + ('Swamp Palace Exit', 'Swamp Area'), ('Skull Woods First Section Hole (East)', 'Skull Pinball'), ('Skull Woods First Section Hole (West)', 'Skull Left Drop'), ('Skull Woods First Section Hole (North)', 'Skull Pot Circle'), @@ -2523,13 +2320,13 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Skull Woods Final Section', 'Skull 3 Portal'), ('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'), ('Thieves Town', 'Thieves Town Portal'), - ('Thieves Town Exit', 'West Dark World'), + ('Thieves Town Exit', 'Village of Outcasts'), ('Ice Palace', 'Ice Portal'), - ('Ice Palace Exit', 'Dark Lake Hylia Central Island'), + ('Ice Palace Exit', 'Ice Palace Area'), ('Misery Mire', 'Mire Portal'), - ('Misery Mire Exit', 'Dark Desert'), + ('Misery Mire Exit', 'Mire Area'), ('Turtle Rock', 'Turtle Rock Main Portal'), - ('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'), + ('Turtle Rock Exit (Front)', 'Turtle Rock Area'), ('Dark Death Mountain Ledge (West)', 'Turtle Rock Lazy Eyes Portal'), ('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'), ('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'), @@ -2541,23 +2338,23 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle open_default_dungeon_connections = [('Agahnims Tower', 'Agahnims Tower Portal'), ('Agahnims Tower Exit', 'Hyrule Castle Ledge'), ('Ganons Tower', 'Ganons Tower Portal'), - ('Ganons Tower Exit', 'Dark Death Mountain (Top)') + ('Ganons Tower Exit', 'West Dark Death Mountain (Top)') ] inverted_default_dungeon_connections = [('Agahnims Tower', 'Ganons Tower Portal'), - ('Agahnims Tower Exit', 'Dark Death Mountain (Top)'), + ('Agahnims Tower Exit', 'West Dark Death Mountain (Top)'), ('Ganons Tower', 'Agahnims Tower Portal'), ('Ganons Tower Exit', 'Hyrule Castle Ledge') ] indirect_connections = { - 'Turtle Rock (Top)': 'Turtle Rock', - 'East Dark World': 'Pyramid Fairy', + 'Turtle Rock Ledge': 'Turtle Rock', + 'Pyramid Area': 'Pyramid Fairy', 'Big Bomb Shop': 'Pyramid Fairy', - 'Dark Desert': 'Pyramid Fairy', - 'West Dark World': 'Pyramid Fairy', - 'South Dark World': 'Pyramid Fairy', - 'Light World': 'Pyramid Fairy', + 'Mire Area': 'Pyramid Fairy', + #'West Dark World': 'Pyramid Fairy', + 'Big Bomb Shop Area': 'Pyramid Fairy', + #'Light World': 'Pyramid Fairy', 'Old Man Cave (East)': 'Old Man S&Q' } # format: @@ -2698,8 +2495,8 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Dark Potion Shop': (0x6E, (0x010f, 0x56, 0x080e, 0x04f4, 0x0c66, 0x0548, 0x0cd8, 0x0563, 0x0ce3, 0x0a, 0xf6, 0x0000, 0x0000)), 'Archery Game': (0x58, (0x0111, 0x69, 0x069e, 0x0ac4, 0x02ea, 0x0b18, 0x0368, 0x0b33, 0x036f, 0x0a, 0xf6, 0x09AC, 0x0000)), 'Mire Shed': (0x5E, (0x010d, 0x70, 0x0384, 0x0c69, 0x001e, 0x0cb6, 0x0098, 0x0cd6, 0x00a3, 0x07, 0xf9, 0x0000, 0x0000)), - 'Dark Desert Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), - 'Dark Desert Fairy': (0x55, (0x0115, 0x70, 0x03a8, 0x0c6a, 0x013a, 0x0cb7, 0x01b8, 0x0cd7, 0x01bf, 0x06, 0xfa, 0x0000, 0x0000)), + 'Mire Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), + 'Mire Fairy': (0x55, (0x0115, 0x70, 0x03a8, 0x0c6a, 0x013a, 0x0cb7, 0x01b8, 0x0cd7, 0x01bf, 0x06, 0xfa, 0x0000, 0x0000)), 'Spike Cave': (0x40, (0x0117, 0x43, 0x0ed4, 0x01e4, 0x08aa, 0x0236, 0x0928, 0x0253, 0x092f, 0x0a, 0xf6, 0x0000, 0x0000)), 'Dark Death Mountain Shop': (0x6D, (0x0112, 0x45, 0x0ee0, 0x01e3, 0x0d00, 0x0236, 0x0daa, 0x0252, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000)), 'Dark Death Mountain Fairy': (0x6F, (0x0115, 0x43, 0x1400, 0x0294, 0x0600, 0x02e8, 0x0678, 0x0303, 0x0685, 0x0a, 0xf6, 0x0000, 0x0000)), @@ -2785,7 +2582,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Desert Healer Fairy': 0x5E, 'Dark Lake Hylia Healer Fairy': 0x5E, 'Dark Lake Hylia Ledge Healer Fairy': 0x5E, - 'Dark Desert Healer Fairy': 0x5E, + 'Mire Healer Fairy': 0x5E, 'Dark Death Mountain Healer Fairy': 0x5E, 'Fortune Teller (Light)': 0x65, 'Lake Hylia Fortune Teller': 0x65, @@ -2840,7 +2637,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Fortune Teller (Dark)': 0x66, 'Archery Game': 0x59, 'Mire Shed': 0x5F, - 'Dark Desert Hint': 0x62, + 'Mire Hint': 0x62, 'Spike Cave': 0x41, 'Mimic Cave': 0x4F, 'Kakariko Well (top)': 0x80, @@ -2976,8 +2773,8 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d), 'Dark Potion Shop': (0xc80, 0x4c0), 'Archery Game': (0x2f0, 0xaf0), 'Mire Shed': (0x060, 0xc90), - 'Dark Desert Hint': (0x2e0, 0xd00), - 'Dark Desert Fairy': (0x1c0, 0xc90), + 'Mire Hint': (0x2e0, 0xd00), + 'Mire Fairy': (0x1c0, 0xc90), 'Spike Cave': (0x860, 0x180), 'Dark Death Mountain Shop': (0xd80, 0x180), 'Dark Death Mountain Fairy': (0x620, 0x2c0), diff --git a/Fill.py b/Fill.py index 49b9f113..6ac6b3bb 100644 --- a/Fill.py +++ b/Fill.py @@ -932,7 +932,7 @@ def balance_money_progression(world): checked_locations = [] for player in range(1, world.players+1): kiki_payable = state.prog_items[('Moon Pearl', player)] > 0 or world.mode[player] == 'inverted' - if kiki_payable and world.get_region('East Dark World', player) in state.reachable_regions[player]: + if kiki_payable and world.get_region('Palace of Darkness Area', player) in state.reachable_regions[player]: if not kiki_paid[player]: kiki_check[player] = True sphere_costs[player] += 110 diff --git a/ItemList.py b/ItemList.py index f8cf2941..7b8172d5 100644 --- a/ItemList.py +++ b/ItemList.py @@ -199,7 +199,7 @@ def generate_itempool(world, player): world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) if world.goal[player] in ['triforcehunt', 'trinity']: - region = world.get_region('Light World', player) + region = world.get_region('Hyrule Castle Courtyard', player) loc = Location(player, "Murahdahla", parent=region) region.locations.append(loc) world.dynamic_locations.append(loc) @@ -476,11 +476,11 @@ take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', 'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Lumberjack House', 'Bonk Fairy (Light)', 'Bonk Fairy (Dark)', 'Lake Hylia Healer Fairy', 'Light Hype Fairy', 'Desert Healer Fairy', - 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Dark Desert Healer Fairy', + 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Mire Healer Fairy', 'Dark Death Mountain Healer Fairy', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Kakariko Gamble Game', '50 Rupee Cave', 'Lost Woods Gamble', 'Hookshot Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint', - 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint'] + 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Mire Hint'] fixed_take_anys = [ 'Desert Healer Fairy', 'Light Hype Fairy', 'Dark Death Mountain Healer Fairy', diff --git a/Main.py b/Main.py index 85fd02ce..b12cdf42 100644 --- a/Main.py +++ b/Main.py @@ -15,7 +15,7 @@ from KeyDoorShuffle import validate_key_placement from OverworldGlitchRules import create_owg_connections from PotShuffle import shuffle_pots, shuffle_pot_switches from Regions import create_regions, create_shops, mark_light_dark_world_regions, create_dungeon_regions, adjust_locations -from OverworldShuffle import create_dynamic_exits +from OverworldShuffle import link_overworld, create_dynamic_exits from EntranceShuffle import link_entrances from Rom import patch_rom, patch_race_rom, apply_rom_settings, LocalRom, JsonRom, get_hash_string from Doors import create_doors @@ -246,6 +246,7 @@ def main(args, seed=None, fish=None): logger.info(world.fish.translate("cli","cli","shuffling.world")) for player in range(1, world.players + 1): + link_overworld(world, player) create_dynamic_exits(world, player) if world.experimental[player] or world.shuffle[player] in ['lite', 'lean', 'swapped'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()): link_entrances_new(world, player) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 3793f272..303aeed2 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1,4 +1,41 @@ -from BaseClasses import RegionType, Entrance +from BaseClasses import RegionType, Terrain, Entrance +from Utils import bidict +import logging + +def link_overworld(world, player): + # setup mandatory connections + for exitname, regionname in mandatory_connections: + connect_simple(world, exitname, regionname, player) + + # apply tile logical connections + if not world.mode[player] == 'inverted': + for exitname, regionname in open_mandatory_connections: + connect_simple(world, exitname, regionname, player) + else: + for exitname, regionname in inverted_mandatory_connections: + connect_simple(world, exitname, regionname, player) + + for forward_edge, back_edge in default_connections: + connect_two_way(world, forward_edge, back_edge, player) + + +def connect_simple(world, exitname, regionname, player): + world.get_entrance(exitname, player).connect(world.get_region(regionname, player)) + +def connect_two_way(world, edgename1, edgename2, player): + edge1 = world.get_entrance(edgename1, player) + edge2 = world.get_entrance(edgename2, player) + edge1.connect(edge2.parent_region) + edge2.connect(edge1.parent_region) + +def create_flute_exits(world, player): + for region in (r for r in world.regions if r.player == player and r.terrain == Terrain.Land and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']): + if region.type == (RegionType.LightWorld if world.mode[player] != 'inverted' else RegionType.DarkWorld): + exitname = 'Flute From ' + region.name + exit = Entrance(region.player, exitname, region) + exit.spot_type = 'Flute' + exit.connect(world.get_region('Flute Sky', player)) + region.exits.append(exit) def get_mirror_exit_name(from_region, to_region): if from_region in mirror_connections and to_region in mirror_connections[from_region]: @@ -21,8 +58,7 @@ def create_mirror_exits(world, player): exit = Entrance(region.player, exitname, region) exit.spot_type = 'Mirror' to_region = world.get_region(region_dest_name, player) - # if region.terrain == Terrain.Water or to_region.terrain == Terrain.Water: - if region.name == 'Dark Lake Hylia Water': # TODO: Uncomment line above when Terrain type is modeled + if region.terrain == Terrain.Water or to_region.terrain == Terrain.Water: exit.access_rule = lambda state: state.has('Flippers', player) and state.has_Pearl(player) and state.has_Mirror(player) else: exit.access_rule = lambda state: state.has_Mirror(player) @@ -32,80 +68,997 @@ def create_mirror_exits(world, player): mirror_exits.add(exitname) def create_dynamic_exits(world, player): + create_flute_exits(world, player) create_mirror_exits(world, player) world.initialize_regions() -mirror_connections = { - 'Skull Woods Forest (West)': ['Light World'], +# these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions +mandatory_connections = [ + ('Links House S&Q', 'Links House'), - 'West Dark Death Mountain (Bottom)': ['Spectacle Rock'], - 'Dark Death Mountain (Top)': ['East Death Mountain (Top)'], + # Intra-tile OW Connections + ('Lost Woods Bush (West)', 'Lost Woods East Area'), #pearl + ('Lost Woods Bush (East)', 'Lost Woods West Area'), #pearl + ('West Death Mountain Drop', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Ledge Drop', 'West Death Mountain (Top)'), + ('DM Hammer Bridge (West)', 'East Death Mountain (Top East)'), #hammer + ('DM Hammer Bridge (East)', 'East Death Mountain (Top West)'), #hammer + ('EDM To Spiral Ledge Drop', 'Spiral Cave Ledge'), + ('EDM To Fairy Ledge Drop', 'Fairy Ascension Ledge'), + ('EDM Ledge Drop', 'East Death Mountain (Bottom)'), + ('Spiral Ledge Drop', 'East Death Mountain (Bottom)'), + ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), + ('Fairy Ascension Plateau Ledge Drop', 'East Death Mountain (Bottom)'), + ('Fairy Ascension Rocks (Inner)', 'East Death Mountain (Bottom)'), #mitts + ('Fairy Ascension Rocks (Outer)', 'Fairy Ascension Plateau'), #mitts + ('DM Broken Bridge (West)', 'East Death Mountain (Bottom)'), #hookshot + ('DM Broken Bridge (East)', 'East Death Mountain (Bottom Left)'), #hookshot + ('TR Pegs Ledge Entry', 'Death Mountain TR Pegs Ledge'), #mitts + ('TR Pegs Ledge Leave', 'Death Mountain TR Pegs Area'), #mitts + ('TR Pegs Ledge Drop', 'Death Mountain TR Pegs Area'), + ('Mountain Pass Rock (Outer)', 'Mountain Pass Entry'), #glove + ('Mountain Pass Rock (Inner)', 'Mountain Pass Area'), #glove + ('Mountain Pass Entry Ledge Drop', 'Mountain Pass Area'), + ('Mountain Pass Ledge Drop', 'Mountain Pass Area'), + ('Zora Waterfall Landing', 'Zora Waterfall Area'), + ('Zora Waterfall Water Drop', 'Zora Waterfall Water'), #flippers + ('Zora Waterfall Water Entry', 'Zora Waterfall Water'), #flippers + ('Zora Waterfall Approach', 'Zora Waterfall Entryway'), #flippers + ('Lost Woods Pass Hammer (North)', 'Lost Woods Pass Portal Area'), #hammer + ('Lost Woods Pass Hammer (South)', 'Lost Woods Pass East Top Area'), #hammer + ('Lost Woods Pass Rock (North)', 'Lost Woods Pass East Bottom Area'), #mitts + ('Lost Woods Pass Rock (South)', 'Lost Woods Pass Portal Area'), #mitts + ('Bonk Rock Ledge Drop', 'Sanctuary Area'), + ('Graveyard Ledge Drop', 'Graveyard Area'), + ('Kings Grave Rocks (Outer)', 'Kings Grave Area'), #mitts + ('Kings Grave Rocks (Inner)', 'Graveyard Area'), #mitts + ('River Bend Water Drop', 'River Bend Water'), #flippers + ('River Bend East Water Drop', 'River Bend Water'), #flippers + ('River Bend West Pier', 'River Bend Area'), + ('River Bend East Pier', 'River Bend East Bank'), + ('Potion Shop Water Drop', 'Potion Shop Water'), #flippers + ('Potion Shop Northeast Water Drop', 'Potion Shop Water'), #flippers + ('Potion Shop Rock (South)', 'Potion Shop Northeast'), #glove + ('Potion Shop Rock (North)', 'Potion Shop Area'), #glove + ('Zora Approach Water Drop', 'Zora Approach Water'), #flippers + ('Zora Approach Rocks (West)', 'Zora Approach Ledge'), #mitts/boots + ('Zora Approach Rocks (East)', 'Zora Approach Area'), #mitts/boots + ('Zora Approach Bottom Ledge Drop', 'Zora Approach Ledge'), + ('Zora Approach Ledge Drop', 'Zora Approach Area'), + ('Kakariko Southwest Bush (North)', 'Kakariko Southwest'), #pearl + ('Kakariko Southwest Bush (South)', 'Kakariko Village'), #pearl + ('Kakariko Yard Bush (South)', 'Kakariko Bush Yard'), #pearl + ('Kakariko Yard Bush (North)', 'Kakariko Village'), #pearl + ('Hyrule Castle Southwest Bush (North)', 'Hyrule Castle Southwest'), #pearl + ('Hyrule Castle Southwest Bush (South)', 'Hyrule Castle Area'), #pearl + ('Hyrule Castle Courtyard Bush (North)', 'Hyrule Castle Courtyard'), #pearl + ('Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Courtyard Northeast'), #pearl + ('Hyrule Castle Main Gate (South)', 'Hyrule Castle Courtyard'), #aga+mirror + ('Hyrule Castle Main Gate (North)', 'Hyrule Castle Area'), #aga+mirror + ('Hyrule Castle Ledge Drop', 'Hyrule Castle Area'), + ('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'), + ('Hyrule Castle East Rock (Inner)', 'Hyrule Castle East Entry'), #glove + ('Hyrule Castle East Rock (Outer)', 'Hyrule Castle Area'), #glove + ('Wooden Bridge Bush (South)', 'Wooden Bridge Northeast'), #pearl + ('Wooden Bridge Bush (North)', 'Wooden Bridge Area'), #pearl + ('Wooden Bridge Water Drop', 'Wooden Bridge Water'), #flippers + ('Wooden Bridge Northeast Water Drop', 'Wooden Bridge Water'), #flippers + ('Blacksmith Ledge Peg (West)', 'Blacksmith Ledge'), #hammer + ('Blacksmith Ledge Peg (East)', 'Blacksmith Area'), #hammer + ('Maze Race Game', 'Maze Race Prize'), #pearl + ('Maze Race Ledge Drop', 'Maze Race Area'), + ('Stone Bridge (Southbound)', 'Stone Bridge South Area'), + ('Stone Bridge (Northbound)', 'Stone Bridge North Area'), + ('Desert Statue Move', 'Desert Stairs'), #book + ('Desert Ledge Drop', 'Desert Area'), + ('Desert Ledge Rocks (Outer)', 'Desert Ledge Keep'), #glove + ('Desert Ledge Rocks (Inner)', 'Desert Ledge'), #glove + ('Checkerboard Ledge Drop', 'Desert Area'), + ('Desert Mouth Drop', 'Desert Area'), + ('Desert Teleporter Drop', 'Desert Area'), + ('Bombos Tablet Drop', 'Desert Area'), + ('Flute Boy Bush (North)', 'Flute Boy Approach Area'), #pearl + ('Flute Boy Bush (South)', 'Flute Boy Bush Entry'), #pearl + ('Cave 45 Ledge Drop', 'Flute Boy Approach Area'), + ('C Whirlpool Water Entry', 'C Whirlpool Water'), #flippers + ('C Whirlpool Landing', 'C Whirlpool Area'), + ('C Whirlpool Rock (Bottom)', 'C Whirlpool Outer Area'), #glove + ('C Whirlpool Rock (Top)', 'C Whirlpool Area'), #glove + ('C Whirlpool Pegs (Outer)', 'C Whirlpool Portal Area'), #hammer + ('C Whirlpool Pegs (Inner)', 'C Whirlpool Area'), #hammer + ('Statues Water Entry', 'Statues Water'), #flippers + ('Statues Landing', 'Statues Area'), + ('Lake Hylia Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia South Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Northeast Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Central Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Island Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), + ('Lake Hylia West Pier', 'Lake Hylia Northwest Bank'), + ('Lake Hylia East Pier', 'Lake Hylia Northeast Bank'), + ('Lake Hylia Water D Approach', 'Lake Hylia Water D'), + ('Lake Hylia Water D Leave', 'Lake Hylia Water'), #flippers + ('Ice Cave Water Drop', 'Ice Cave Water'), #flippers + ('Ice Cave Pier', 'Ice Cave Area'), + ('Desert Pass Ledge Drop', 'Desert Pass Area'), + ('Desert Pass Rocks (North)', 'Desert Pass Southeast'), #glove + ('Desert Pass Rocks (South)', 'Desert Pass Area'), #glove + ('Octoballoon Water Drop', 'Octoballoon Water'), #flippers + ('Octoballoon Waterfall Water Drop', 'Octoballoon Water'), #flippers + ('Octoballoon Pier', 'Octoballoon Area'), + + ('Skull Woods Rock (West)', 'Skull Woods Forest'), #glove + ('Skull Woods Rock (East)', 'Skull Woods Portal Entry'), #glove + ('Skull Woods Forgotten Bush (West)', 'Skull Woods Forgotten Path (Northeast)'), #pearl + ('Skull Woods Forgotten Bush (East)', 'Skull Woods Forgotten Path (Southwest)'), #pearl + ('West Dark Death Mountain Drop', 'West Dark Death Mountain (Bottom)'), + ('GT Approach', 'GT Stairs'), + ('GT Leave', 'West Dark Death Mountain (Top)'), + ('Floating Island Drop', 'East Dark Death Mountain (Top)'), + ('East Dark Death Mountain Drop', 'East Dark Death Mountain (Bottom)'), + ('East Dark Death Mountain Bushes', 'East Dark Death Mountain (Bushes)'), + ('Turtle Rock Ledge Drop', 'Turtle Rock Area'), + ('Bumper Cave Rock (Outer)', 'Bumper Cave Entry'), #glove + ('Bumper Cave Rock (Inner)', 'Bumper Cave Area'), #glove + ('Bumper Cave Ledge Drop', 'Bumper Cave Area'), + ('Bumper Cave Entry Drop', 'Bumper Cave Area'), + ('Skull Woods Pass Bush Row (West)', 'Skull Woods Pass East Top Area'), #pearl + ('Skull Woods Pass Bush Row (East)', 'Skull Woods Pass West Area'), #pearl + ('Skull Woods Pass Bush (North)', 'Skull Woods Pass Portal Area'), #pearl + ('Skull Woods Pass Bush (South)', 'Skull Woods Pass East Top Area'), #pearl + ('Skull Woods Pass Rock (North)', 'Skull Woods Pass East Bottom Area'), #mitts + ('Skull Woods Pass Rock (South)', 'Skull Woods Pass Portal Area'), #mitts + ('Dark Graveyard Bush (South)', 'Dark Graveyard North'), #pearl + ('Dark Graveyard Bush (North)', 'Dark Graveyard Area'), #pearl + ('Qirn Jump Water Drop', 'Qirn Jump Water'), #flippers + ('Qirn Jump East Water Drop', 'Qirn Jump Water'), #flippers + ('Qirn Jump Pier', 'Qirn Jump East Bank'), + ('Dark Witch Water Drop', 'Dark Witch Water'), #flippers + ('Dark Witch Northeast Water Drop', 'Dark Witch Water'), #flippers + ('Dark Witch Rock (North)', 'Dark Witch Area'), #glove + ('Dark Witch Rock (South)', 'Dark Witch Northeast'), #glove + ('Catfish Approach Water Drop', 'Catfish Approach Water'), #flippers + ('Catfish Approach Rocks (West)', 'Catfish Approach Ledge'), #mitts/boots + ('Catfish Approach Rocks (East)', 'Catfish Approach Area'), #mitts/boots + ('Catfish Approach Bottom Ledge Drop', 'Catfish Approach Ledge'), + ('Catfish Approach Ledge Drop', 'Catfish Approach Area'), + ('Bush Yard Pegs (Outer)', 'Village of Outcasts Bush Yard'), #hammer + ('Bush Yard Pegs (Inner)', 'Village of Outcasts'), #hammer + ('Shield Shop Fence Drop (Outer)', 'Shield Shop Fence'), + ('Shield Shop Fence Drop (Inner)', 'Shield Shop Area'), + ('Pyramid Exit Ledge Drop', 'Pyramid Area'), + ('Broken Bridge Hammer Rock (South)', 'Broken Bridge Northeast'), #hammer/glove + ('Broken Bridge Hammer Rock (North)', 'Broken Bridge Area'), #hammer/glove + ('Broken Bridge Hookshot Gap', 'Broken Bridge West'), #hookshot + ('Broken Bridge Water Drop', 'Broken Bridge Water'), #flippers + ('Broken Bridge Northeast Water Drop', 'Broken Bridge Water'), #flippers + ('Broken Bridge West Water Drop', 'Broken Bridge Water'), #flippers + ('Peg Area Rocks (West)', 'Hammer Pegs Area'), #mitts + ('Peg Area Rocks (East)', 'Hammer Pegs Entry'), #mitts + ('Dig Game To Ledge Drop', 'Dig Game Ledge'), #mitts + ('Dig Game Ledge Drop', 'Dig Game Area'), + ('Frog Ledge Drop', 'Archery Game Area'), + ('Frog Rock (Inner)', 'Frog Area'), #mitts + ('Frog Rock (Outer)', 'Frog Prison'), #mitts + ('Archery Game Rock (North)', 'Archery Game Area'), #mitts + ('Archery Game Rock (South)', 'Frog Area'), #mitts + ('Hammer Bridge Pegs (North)', 'Hammer Bridge South Area'), #hammer + ('Hammer Bridge Pegs (South)', 'Hammer Bridge North Area'), #hammer + ('Hammer Bridge Water Drop', 'Hammer Bridge Water'), #flippers + ('Hammer Bridge Pier', 'Hammer Bridge North Area'), + ('Mire Teleporter Ledge Drop', 'Mire Area'), + ('Stumpy Approach Bush (North)', 'Stumpy Approach Area'), #pearl + ('Stumpy Approach Bush (South)', 'Stumpy Approach Bush Entry'), #pearl + ('Dark C Whirlpool Water Entry', 'Dark C Whirlpool Water'), #flippers + ('Dark C Whirlpool Landing', 'Dark C Whirlpool Area'), + ('Dark C Whirlpool Rock (Bottom)', 'Dark C Whirlpool Outer Area'), #glove + ('Dark C Whirlpool Rock (Top)', 'Dark C Whirlpool Area'), #glove + ('Dark C Whirlpool Pegs (Outer)', 'Dark C Whirlpool Portal Area'), #hammer + ('Dark C Whirlpool Pegs (Inner)', 'Dark C Whirlpool Area'), #hammer + ('Hype Cave Water Entry', 'Hype Cave Water'), #flippers + ('Hype Cave Landing', 'Hype Cave Area'), + ('Ice Lake Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Northeast Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Southwest Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Southeast Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Iceberg Water Entry', 'Ice Lake Water'), #flippers + ('Ice Lake Northeast Pier', 'Ice Lake Northeast Bank'), + ('Shopping Mall Water Drop', 'Shopping Mall Water'), #flippers + ('Shopping Mall Pier', 'Shopping Mall Area'), + ('Bomber Corner Water Drop', 'Bomber Corner Water'), #flippers + ('Bomber Corner Waterfall Water Drop', 'Bomber Corner Water'), #flippers + ('Bomber Corner Pier', 'Bomber Corner Area'), + + # OWG In-Bounds Connections + ('Ice Lake Northeast Pier Hop', 'Ice Lake Northeast Bank'), + ('Ice Lake Iceberg Bomb Jump', 'Ice Lake Iceberg'), + + # OWG Connections + ('Lake Hylia Island FAWT Ledge Drop', 'Lake Hylia Island'), + ('Stone Bridge EC Cliff Water Drop', 'Stone Bridge Water'), #fake flipper + ('C Whirlpool Portal Cliff Ledge Drop', 'C Whirlpool Portal Area'), + ('Checkerboard Cliff Ledge Drop', 'Desert Checkerboard Ledge'), + ('Cave 45 Cliff Ledge Drop', 'Cave 45 Ledge'), + + ('Ice Lake Iceberg FAWT Ledge Drop', 'Ice Lake Iceberg'), + ('Hammer Bridge EC Cliff Water Drop', 'Hammer Bridge Water'), #fake flipper + ('Dark C Whirlpool Portal Cliff Ledge Drop', 'Dark C Whirlpool Portal Area'), + ('Mire Cliff Ledge Drop', 'Mire Area'), + ('Stumpy Approach Cliff Ledge Drop', 'Stumpy Approach Area') +] + +open_mandatory_connections = [ + ('Sanctuary S&Q', 'Sanctuary'), + ('Old Man S&Q', 'Old Man House'), + ('Other World S&Q', 'Pyramid Area'), + + # flute + ('Flute Spot 1', 'West Death Mountain (Bottom)'), + ('Flute Spot 2', 'Potion Shop Area'), + ('Flute Spot 3', 'Kakariko Village'), + ('Flute Spot 4', 'Links House Area'), + ('Flute Spot 5', 'Eastern Nook Area'), + ('Flute Spot 6', 'Desert Teleporter Ledge'), + ('Flute Spot 7', 'Dam Area'), + ('Flute Spot 8', 'Octoballoon Area'), + + # portals + ('West Death Mountain Teleporter', 'West Dark Death Mountain (Bottom)'), + ('East Death Mountain Teleporter', 'East Dark Death Mountain (Bottom)'), + ('TR Pegs Teleporter', 'Turtle Rock Ledge'), + ('Kakariko Teleporter', 'Skull Woods Pass Portal Area'), + ('Castle Gate Teleporter', 'Pyramid Area'), + ('Castle Gate Teleporter (Inner)', 'Pyramid Area'), + ('East Hyrule Teleporter', 'Darkness Nook Area'), + ('South Hyrule Teleporter', 'Dark C Whirlpool Portal Area'), + ('Desert Teleporter', 'Mire Teleporter Ledge'), + ('Lake Hylia Teleporter', 'Ice Palace Area'), + + # OWG connections + ('Mirror To Bombos Tablet Ledge', 'Bombos Tablet Ledge') +] + +inverted_mandatory_connections = [ + ('Sanctuary S&Q', 'Dark Sanctuary Hint'), + ('Old Man S&Q', 'West Dark Death Mountain (Bottom)'), + ('Other World S&Q', 'Hyrule Castle Ledge'), + + # flute + ('Flute Spot 1', 'West Dark Death Mountain (Bottom)'), + ('Flute Spot 2', 'Dark Witch Area'), + ('Flute Spot 3', 'Village of Outcasts'), + ('Flute Spot 4', 'Big Bomb Shop Area'), + ('Flute Spot 5', 'Darkness Nook Area'), + ('Flute Spot 6', 'Mire Teleporter Ledge'), + ('Flute Spot 7', 'Swamp Area'), + ('Flute Spot 8', 'Bomber Corner Area'), + + # modified terrain + ('Spectacle Rock Approach', 'Spectacle Rock Ledge'), + ('Spectacle Rock Leave', 'West Death Mountain (Top)'), + ('Floating Island Bridge (West)', 'East Death Mountain (Top East)'), + ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), + ('Graveyard Ladder (Top)', 'Graveyard Area'), + ('Graveyard Ladder (Bottom)', 'Graveyard Ledge'), + ('EDM To Mimic Ledge Drop', 'Mimic Cave Ledge'), + ('Mimic Ledge Drop', 'East Death Mountain (Bottom)'), + ('Checkerboard Ledge Approach', 'Desert Checkerboard Ledge'), + ('Checkerboard Ledge Leave', 'Desert Area'), + ('Cave 45 Approach', 'Cave 45 Ledge'), + ('Cave 45 Leave', 'Flute Boy Approach Area'), + ('Lake Hylia Island Pier', 'Lake Hylia Island'), + ('Desert Pass Ladder (North)', 'Desert Pass Area'), + ('Desert Pass Ladder (South)', 'Desert Pass Ledge'), + ('Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain (Bottom)'), + ('Dark Death Mountain Ladder (Bottom)', 'West Dark Death Mountain (Top)'), + ('Turtle Rock Tail Ledge Drop', 'Turtle Rock Ledge'), + ('Ice Palace Approach', 'Ice Palace Area'), + ('Ice Palace Leave', 'Ice Lake Iceberg'), + + # portals + ('Dark Death Mountain Teleporter (West)', 'West Death Mountain (Bottom)'), + ('East Dark Death Mountain Teleporter', 'East Death Mountain (Bottom)'), + ('Turtle Rock Teleporter', 'Death Mountain TR Pegs Ledge'), + ('West Dark World Teleporter', 'Lost Woods Pass Portal Area'), + ('Post Aga Teleporter', 'Hyrule Castle Area'), + ('East Dark World Teleporter', 'Eastern Nook Area'), + ('South Dark World Teleporter', 'C Whirlpool Portal Area'), + ('Mire Teleporter', 'Desert Teleporter Ledge'), + ('Ice Lake Teleporter', 'Lake Hylia Central Island') +] + +# non shuffled overworld +default_connections = [ + ('Lost Woods NW', 'Master Sword Meadow SC'), + ('Lost Woods SW', 'Lost Woods Pass NW'), + ('Lost Woods SC', 'Lost Woods Pass NE'), + ('Lost Woods SE', 'Kakariko Fortune NE'), + ('Lost Woods EN', 'Lumberjack WN'), + ('Lumberjack SW', 'Mountain Pass NW'), + ('Mountain Pass SE', 'Kakariko Pond NE'), + ('Zora Waterfall NE', 'Zoras Domain SW'), + ('Lost Woods Pass SW', 'Kakariko NW'), + ('Lost Woods Pass SE', 'Kakariko NC'), + ('Kakariko Fortune SC', 'Kakariko NE'), + ('Kakariko Fortune EN', 'Kakariko Pond WN'), + ('Kakariko Fortune ES', 'Kakariko Pond WS'), + ('Kakariko Pond SW', 'Forgotten Forest NW'), + ('Kakariko Pond SE', 'Forgotten Forest NE'), + ('Kakariko Pond EN', 'Sanctuary WN'), + ('Kakariko Pond ES', 'Sanctuary WS'), + ('Forgotten Forest ES', 'Hyrule Castle WN'), + ('Sanctuary EC', 'Graveyard WC'), + ('Graveyard EC', 'River Bend WC'), + ('River Bend SW', 'Wooden Bridge NW'), + ('River Bend SC', 'Wooden Bridge NC'), + ('River Bend SE', 'Wooden Bridge NE'), + ('River Bend EN', 'Potion Shop WN'), + ('River Bend EC', 'Potion Shop WC'), + ('River Bend ES', 'Potion Shop WS'), + ('Potion Shop EN', 'Zora Approach WN'), + ('Potion Shop EC', 'Zora Approach WC'), + ('Zora Approach NE', 'Zora Waterfall SE'), + ('Kakariko SE', 'Kakariko Suburb NE'), + ('Kakariko ES', 'Blacksmith WS'), + ('Hyrule Castle SW', 'Central Bonk Rocks NW'), + ('Hyrule Castle SE', 'Links House NE'), + ('Hyrule Castle ES', 'Sand Dunes WN'), + ('Wooden Bridge SW', 'Sand Dunes NW'), + ('Sand Dunes SC', 'Stone Bridge NC'), + ('Eastern Palace SW', 'Tree Line NW'), + ('Eastern Palace SE', 'Eastern Nook NE'), + ('Maze Race ES', 'Kakariko Suburb WS'), + ('Kakariko Suburb ES', 'Flute Boy WS'), + ('Flute Boy SW', 'Flute Boy Pass NW'), + ('Flute Boy SC', 'Flute Boy Pass NC'), + ('Flute Boy Pass EC', 'C Whirlpool WC'), + ('C Whirlpool NW', 'Central Bonk Rocks SW'), + ('C Whirlpool SC', 'Dam NC'), + ('C Whirlpool EN', 'Statues WN'), + ('C Whirlpool EC', 'Statues WC'), + ('C Whirlpool ES', 'Statues WS'), + ('Central Bonk Rocks EN', 'Links House WN'), + ('Central Bonk Rocks EC', 'Links House WC'), + ('Central Bonk Rocks ES', 'Links House WS'), + ('Links House SC', 'Statues NC'), + ('Links House ES', 'Stone Bridge WS'), + ('Stone Bridge SC', 'Lake Hylia NW'), + ('Stone Bridge EN', 'Tree Line WN'), + ('Stone Bridge EC', 'Tree Line WC'), + ('Stone Bridge WC', 'Hobo EC'), + ('Tree Line SC', 'Lake Hylia NC'), + ('Tree Line SE', 'Lake Hylia NE'), + ('Desert EC', 'Desert Pass WC'), + ('Desert ES', 'Desert Pass WS'), + ('Desert Pass EC', 'Dam WC'), + ('Desert Pass ES', 'Dam WS'), + ('Dam EC', 'South Pass WC'), + ('Statues SC', 'South Pass NC'), + ('South Pass ES', 'Lake Hylia WS'), + ('Lake Hylia EC', 'Octoballoon WC'), + ('Lake Hylia ES', 'Octoballoon WS'), + ('Octoballoon NW', 'Ice Cave SW'), + ('Octoballoon NE', 'Ice Cave SE'), + ('West Death Mountain EN', 'East Death Mountain WN'), + ('West Death Mountain ES', 'East Death Mountain WS'), + ('East Death Mountain EN', 'Death Mountain TR Pegs WN'), + + ('Skull Woods SW', 'Skull Woods Pass NW'), + ('Skull Woods SC', 'Skull Woods Pass NE'), + ('Skull Woods SE', 'Dark Fortune NE'), + ('Skull Woods EN', 'Dark Lumberjack WN'), + ('Dark Lumberjack SW', 'Bumper Cave NW'), + ('Bumper Cave SE', 'Outcast Pond NE'), + ('Skull Woods Pass SW', 'Village of Outcasts NW'), + ('Skull Woods Pass SE', 'Village of Outcasts NC'), + ('Dark Fortune SC', 'Village of Outcasts NE'), + ('Dark Fortune EN', 'Outcast Pond WN'), + ('Dark Fortune ES', 'Outcast Pond WS'), + ('Outcast Pond SW', 'Shield Shop NW'), + ('Outcast Pond SE', 'Shield Shop NE'), + ('Outcast Pond EN', 'Dark Chapel WN'), + ('Outcast Pond ES', 'Dark Chapel WS'), + ('Dark Chapel EC', 'Dark Graveyard WC'), + ('Dark Graveyard EC', 'Qirn Jump WC'), + ('Qirn Jump SW', 'Broken Bridge NW'), + ('Qirn Jump SC', 'Broken Bridge NC'), + ('Qirn Jump SE', 'Broken Bridge NE'), + ('Qirn Jump EN', 'Dark Witch WN'), + ('Qirn Jump EC', 'Dark Witch WC'), + ('Qirn Jump ES', 'Dark Witch WS'), + ('Dark Witch EN', 'Catfish Approach WN'), + ('Dark Witch EC', 'Catfish Approach WC'), + ('Catfish Approach NE', 'Catfish SE'), + ('Village of Outcasts SE', 'Frog NE'), + ('Village of Outcasts ES', 'Hammer Pegs WS'), + ('Pyramid SW', 'Dark Bonk Rocks NW'), + ('Pyramid SE', 'Big Bomb Shop NE'), + ('Pyramid ES', 'Dark Dunes WN'), + ('Broken Bridge SW', 'Dark Dunes NW'), + ('Dark Dunes SC', 'Hammer Bridge NC'), + ('Palace of Darkness SW', 'Dark Tree Line NW'), + ('Palace of Darkness SE', 'Palace of Darkness Nook NE'), + ('Dig Game EC', 'Frog WC'), + ('Dig Game ES', 'Frog WS'), + ('Frog ES', 'Stumpy WS'), + ('Stumpy SW', 'Stumpy Approach NW'), + ('Stumpy SC', 'Stumpy Approach NC'), + ('Stumpy Approach EC', 'Dark C Whirlpool WC'), + ('Dark C Whirlpool NW', 'Dark Bonk Rocks SW'), + ('Dark C Whirlpool SC', 'Swamp NC'), + ('Dark C Whirlpool EN', 'Hype Cave WN'), + ('Dark C Whirlpool EC', 'Hype Cave WC'), + ('Dark C Whirlpool ES', 'Hype Cave WS'), + ('Dark Bonk Rocks EN', 'Big Bomb Shop WN'), + ('Dark Bonk Rocks EC', 'Big Bomb Shop WC'), + ('Dark Bonk Rocks ES', 'Big Bomb Shop WS'), + ('Big Bomb Shop SC', 'Hype Cave NC'), + ('Big Bomb Shop ES', 'Hammer Bridge WS'), + ('Hammer Bridge SC', 'Ice Lake NW'), + ('Hammer Bridge EN', 'Dark Tree Line WN'), + ('Hammer Bridge EC', 'Dark Tree Line WC'), + ('Dark Tree Line SC', 'Ice Lake NC'), + ('Dark Tree Line SE', 'Ice Lake NE'), + ('Swamp Nook EC', 'Swamp WC'), + ('Swamp Nook ES', 'Swamp WS'), + ('Swamp EC', 'Dark South Pass WC'), + ('Hype Cave SC', 'Dark South Pass NC'), + ('Dark South Pass ES', 'Ice Lake WS'), + ('Ice Lake EC', 'Bomber Corner WC'), + ('Ice Lake ES', 'Bomber Corner WS'), + ('Bomber Corner NW', 'Shopping Mall SW'), + ('Bomber Corner NE', 'Shopping Mall SE'), + ('West Dark Death Mountain EN', 'East Dark Death Mountain WN'), + ('West Dark Death Mountain ES', 'East Dark Death Mountain WS'), + ('East Dark Death Mountain EN', 'Turtle Rock WN'), + + # whirlpool connections + ('C Whirlpool', 'River Bend Whirlpool'), + ('Lake Hylia Whirlpool', 'Zora Whirlpool'), + ('Kakariko Pond Whirlpool', 'Octoballoon Whirlpool'), + ('Qirn Jump Whirlpool', 'Bomber Corner Whirlpool') +] + +mirror_connections = { + 'Skull Woods Forest': ['Lost Woods East Area'], + 'Skull Woods Portal Entry': ['Lost Woods West Area'], + 'Skull Woods Forest (West)': ['Lost Woods West Area'], + 'Skull Woods Forgotten Path (Southwest)': ['Lost Woods West Area'], + 'Skull Woods Forgotten Path (Northeast)': ['Lost Woods East Area', 'Lost Woods West Area'], + + 'Dark Lumberjack Area': ['Lumberjack Area'], + + 'West Dark Death Mountain (Top)': ['West Death Mountain (Top)'], + 'West Dark Death Mountain (Bottom)': ['Spectacle Rock Ledge'], 'Dark Death Mountain Floating Island': ['Death Mountain Floating Island'], + 'East Dark Death Mountain (Top)': ['East Death Mountain (Top West)', 'East Death Mountain (Top East)'], 'Dark Death Mountain Ledge': ['Spiral Cave Ledge', 'Mimic Cave Ledge'], 'Dark Death Mountain Isolated Ledge': ['Fairy Ascension Ledge'], 'East Dark Death Mountain (Bushes)': ['Fairy Ascension Plateau'], - 'East Dark Death Mountain (Bottom)': ['East Death Mountain (Bottom)'], - + 'East Dark Death Mountain (Bottom Left)': ['East Death Mountain (Bottom Left)'], + + 'Turtle Rock Area': ['Death Mountain TR Pegs Area'], + + 'Bumper Cave Area': ['Mountain Pass Area'], + 'Bumper Cave Entry': ['Mountain Pass Entry'], + 'Bumper Cave Ledge': ['Mountain Pass Ledge'], + + 'Catfish Area': ['Zora Waterfall Area'], + + 'Skull Woods Pass West Area': ['Lost Woods Pass West Area'], + 'Skull Woods Pass East Top Area': ['Lost Woods Pass East Top Area'], + 'Skull Woods Pass Portal Area': ['Lost Woods Pass Portal Area'], + 'Skull Woods Pass East Bottom Area': ['Lost Woods Pass East Bottom Area'], + + 'Dark Fortune Area': ['Kakariko Fortune Area'], + + 'Outcast Pond Area': ['Kakariko Pond Area'], + + 'Dark Chapel Area': ['Sanctuary Area', 'Bonk Rock Ledge'], + + 'Dark Graveyard Area': ['Graveyard Area'], 'Dark Graveyard North': ['Graveyard Ledge', 'Kings Grave Area'], - 'Bumper Cave Ledge': ['Death Mountain Return Ledge'], - 'Bumper Cave Entrance': ['Death Mountain Entrance'], + 'Qirn Jump Area': ['River Bend Area'], + 'Qirn Jump East Bank': ['River Bend East Bank'], - 'Northeast Dark World': ['Potion Shop Area'], + 'Dark Witch Area': ['Potion Shop Area'], + 'Dark Witch Northeast': ['Potion Shop Northeast'], - 'Dark Grassy Lawn': ['Bush Covered Lawn'], + 'Catfish Approach Area': ['Zora Approach Area'], + 'Catfish Approach Ledge': ['Zora Approach Ledge'], - 'Hammer Peg Area': ['Bat Cave Ledge'], + 'Village of Outcasts': ['Kakariko Village'], + 'Village of Outcasts Bush Yard': ['Kakariko Village'], - 'East Dark World': ['Hyrule Castle Ledge', 'Hyrule Castle Courtyard'], + 'Shield Shop Area': ['Forgotten Forest Area'], + 'Shield Shop Fence': ['Forgotten Forest Area'], - 'Dark Desert': ['Desert Ledge', 'Desert Checkerboard Ledge', 'Desert Palace Stairs', 'Desert Palace Entrance (North) Spot'], + 'Pyramid Area': ['Hyrule Castle Ledge', 'Hyrule Castle Courtyard', 'Hyrule Castle Area', 'Hyrule Castle East Entry'], + 'Pyramid Exit Ledge': ['Hyrule Castle Courtyard'], + 'Pyramid Pass': ['Hyrule Castle Area'], - 'South Dark World': ['Maze Race Ledge', 'Cave 45 Ledge', 'Bombos Tablet Ledge'], + 'Broken Bridge Area': ['Wooden Bridge Area'], + 'Broken Bridge Northeast': ['Wooden Bridge Area'], + 'Broken Bridge West': ['Wooden Bridge Area'], - 'Dark Lake Hylia Water': ['Lake Hylia Island'], - 'Dark Lake Hylia Central Island': ['Lake Hylia Central Island'], + 'Palace of Darkness Area': ['Eastern Palace Area'], - 'Southeast Dark World': ['Light World'], + 'Hammer Pegs Area': ['Blacksmith Area', 'Blacksmith Ledge'], + 'Hammer Pegs Entry': ['Blacksmith Area'], + + 'Dark Dunes Area': ['Sand Dunes Area'], + + 'Dig Game Area': ['Maze Race Ledge'], + 'Dig Game Ledge': ['Maze Race Ledge'], + + 'Frog Area': ['Kakariko Suburb Area'], + 'Archery Game Area': ['Kakariko Suburb Area'], + + 'Stumpy Area': ['Flute Boy Area'], + 'Stumpy Pass': ['Flute Boy Pass'], + + 'Dark Bonk Rocks Area': ['Central Bonk Rocks Area'], + + 'Big Bomb Shop Area': ['Links House Area'], + + 'Hammer Bridge North Area': ['Stone Bridge North Area'], + 'Hammer Bridge South Area': ['Stone Bridge South Area'], + 'Hammer Bridge Water': ['Stone Bridge Water'], + + 'Dark Tree Line Area': ['Tree Line Area'], + + 'Darkness Nook Area': ['Eastern Nook Area'], + + 'Mire Area': ['Desert Area', 'Desert Ledge', 'Desert Checkerboard Ledge', 'Desert Stairs', 'Desert Ledge Keep'], + + 'Stumpy Approach Area': ['Cave 45 Ledge'], + 'Stumpy Approach Bush Entry': ['Flute Boy Bush Entry'], + + 'Dark C Whirlpool Area': ['C Whirlpool Area'], + 'Dark C Whirlpool Outer Area': ['C Whirlpool Outer Area'], + + 'Hype Cave Area': ['Statues Area'], + + 'Ice Lake Northwest Bank': ['Lake Hylia Northwest Bank'], + 'Ice Lake Northeast Bank': ['Lake Hylia Northeast Bank'], + 'Ice Lake Southwest Ledge': ['Lake Hylia South Shore'], + 'Ice Lake Southeast Ledge': ['Lake Hylia South Shore'], + 'Ice Lake Water': ['Lake Hylia Island'], + 'Ice Palace Area': ['Lake Hylia Central Island'], + 'Ice Lake Iceberg': ['Lake Hylia Water', 'Lake Hylia Water D'], #first one needs flippers + + 'Shopping Mall Area': ['Ice Cave Area'], + + 'Swamp Nook Area': ['Desert Pass Area', 'Desert Pass Ledge'], + + 'Swamp Area': ['Dam Area'], + + 'Dark South Pass Area': ['South Pass Area'], + + 'Bomber Corner Area': ['Octoballoon Area'], - 'Light World': ['Skull Woods Forest (West)', 'West Dark World', 'Hammer Peg Area', 'East Dark World', 'South Dark World', 'Dark Desert', 'Southeast Dark World'], + 'Lost Woods West Area': ['Skull Woods Forest (West)', 'Skull Woods Forgotten Path (Southwest)', 'Skull Woods Portal Entry'], + #'Lost Woods West Area': ['Skull Woods Forgotten Path (Northeast)'], # technically yes, but we dont need it + 'Lost Woods East Area': ['Skull Woods Forgotten Path (Northeast)', 'Skull Woods Forest'], - 'West Death Mountain (Top)': ['Dark Death Mountain (Top)'], + 'Lumberjack Area': ['Dark Lumberjack Area'], + + 'West Death Mountain (Top)': ['West Dark Death Mountain (Top)'], + 'Spectacle Rock Ledge': ['West Dark Death Mountain (Bottom)'], 'West Death Mountain (Bottom)': ['West Dark Death Mountain (Bottom)'], - 'East Death Mountain (Top)': ['Dark Death Mountain (Top)'], - 'Death Mountain Floating Island': ['Dark Death Mountain Floating Island'], + 'East Death Mountain (Top West)': ['East Dark Death Mountain (Top)'], + 'East Death Mountain (Top East)': ['East Dark Death Mountain (Top)'], 'Spiral Cave Ledge': ['Dark Death Mountain Ledge'], 'Mimic Cave Ledge': ['Dark Death Mountain Ledge'], 'Fairy Ascension Ledge': ['Dark Death Mountain Isolated Ledge'], + 'Fairy Ascension Plateau': ['East Dark Death Mountain (Bottom)'], + 'East Death Mountain (Bottom Left)': ['East Dark Death Mountain (Bottom Left)'], 'East Death Mountain (Bottom)': ['East Dark Death Mountain (Bottom)'], + 'Death Mountain Floating Island': ['Dark Death Mountain Floating Island'], - 'Death Mountain Return Ledge': ['Bumper Cave Ledge'], - 'Death Mountain Entrance': ['Bumper Cave Entrance'], + 'Death Mountain TR Pegs Area': ['Turtle Rock Area'], + 'Death Mountain TR Pegs Ledge': ['Turtle Rock Ledge'], - 'Northeast Light World': ['Catfish Area'], + 'Mountain Pass Area': ['Bumper Cave Area'], + 'Mountain Pass Entry': ['Bumper Cave Entry'], + 'Mountain Pass Ledge': ['Bumper Cave Ledge'], - 'Graveyard Ledge': ['West Dark World'], - 'Kings Grave Area': ['West Dark World'], + 'Zora Waterfall Area': ['Catfish Area'], - 'Potion Shop Area': ['Northeast Dark World'], + 'Lost Woods Pass West Area': ['Skull Woods Pass West Area'], + 'Lost Woods Pass East Top Area': ['Skull Woods Pass East Top Area'], + 'Lost Woods Pass Portal Area': ['Skull Woods Pass Portal Area'], + 'Lost Woods Pass East Bottom Area': ['Skull Woods Pass East Bottom Area'], - 'Bush Covered Lawn': ['Dark Grassy Lawn'], - 'Bomb Hut Area': ['West Dark World'], + 'Kakariko Fortune Area': ['Dark Fortune Area'], - 'Hyrule Castle Secret Entrance Area': ['East Dark World'], + 'Kakariko Pond Area': ['Outcast Pond Area'], - 'Maze Race Ledge': ['South Dark World'], + 'Sanctuary Area': ['Dark Chapel Area'], + 'Bonk Rock Ledge': ['Dark Chapel Area'], - 'Cave 45 Ledge': ['South Dark World'], + 'Graveyard Area': ['Dark Graveyard Area'], + 'Graveyard Ledge': ['Dark Graveyard Area'], + 'Kings Grave Area': ['Dark Graveyard Area'], - 'Desert Palace Stairs': ['Dark Desert'], - 'Desert Ledge': ['Dark Desert'], - 'Desert Palace Entrance (North) Spot': ['Dark Desert'], - 'Desert Checkerboard Ledge': ['Dark Desert'], + 'River Bend Area': ['Qirn Jump Area'], + 'River Bend East Bank': ['Qirn Jump East Bank'], - 'Lake Hylia Central Island': ['Dark Lake Hylia Central Island'] -} \ No newline at end of file + 'Potion Shop Area': ['Dark Witch Area'], + 'Potion Shop Northeast': ['Dark Witch Northeast'], + + 'Zora Approach Area': ['Catfish Approach Area'], + 'Zora Approach Ledge': ['Catfish Approach Ledge'], + + 'Kakariko Village': ['Village of Outcasts'], + 'Kakariko Southwest': ['Village of Outcasts'], + 'Kakariko Bush Yard': ['Village of Outcasts Bush Yard'], + + 'Forgotten Forest Area': ['Shield Shop Area'], + + 'Hyrule Castle Area': ['Pyramid Area', 'Pyramid Pass'], + 'Hyrule Castle Southwest': ['Pyramid Pass'], + 'Hyrule Castle Courtyard': ['Pyramid Area'], + 'Hyrule Castle Courtyard Northeast': ['Pyramid Area'], + 'Hyrule Castle Ledge': ['Pyramid Area'], + 'Hyrule Castle East Entry': ['Pyramid Area'], + + 'Wooden Bridge Area': ['Broken Bridge Area', 'Broken Bridge West'], + 'Wooden Bridge Northeast': ['Broken Bridge Northeast'], + + 'Eastern Palace Area': ['Palace of Darkness Area'], + + 'Blacksmith Area': ['Hammer Pegs Area', 'Hammer Pegs Entry'], + + 'Sand Dunes Area': ['Dark Dunes Area'], + + 'Maze Race Area': ['Dig Game Area'], + 'Maze Race Ledge': ['Dig Game Ledge'], + + 'Kakariko Suburb Area': ['Frog Area', 'Frog Prison', 'Archery Game Area'], + + 'Flute Boy Area': ['Stumpy Area'], + 'Flute Boy Pass': ['Stumpy Pass'], + + 'Central Bonk Rocks Area': ['Dark Bonk Rocks Area'], + + 'Links House Area': ['Big Bomb Shop Area'], + + 'Stone Bridge North Area': ['Hammer Bridge North Area'], + 'Stone Bridge South Area': ['Hammer Bridge South Area'], + 'Stone Bridge Water': ['Hammer Bridge Water'], + + 'Tree Line Area': ['Dark Tree Line Area'], + + 'Eastern Nook Area': ['Darkness Nook Area'], + + 'Desert Area': ['Mire Area'], + 'Desert Ledge': ['Mire Area'], + 'Desert Ledge Keep': ['Mire Area'], + 'Desert Checkerboard Ledge': ['Mire Area'], + 'Desert Stairs': ['Mire Area'], + + 'Flute Boy Approach Area': ['Stumpy Approach Area'], + 'Cave 45 Ledge': ['Stumpy Approach Area'], + 'Flute Boy Bush Entry': ['Stumpy Approach Bush Entry'], + + 'C Whirlpool Area': ['Dark C Whirlpool Area'], + 'C Whirlpool Outer Area': ['Dark C Whirlpool Outer Area'], + + 'Statues Area': ['Hype Cave Area'], + + 'Lake Hylia Northwest Bank': ['Ice Lake Northwest Bank'], + 'Lake Hylia South Shore': ['Ice Lake Southwest Ledge', 'Ice Lake Southeast Ledge'], + 'Lake Hylia Northeast Bank': ['Ice Lake Northeast Bank'], + 'Lake Hylia Central Island': ['Ice Palace Area'], + 'Lake Hylia Water D': ['Ice Lake Iceberg'], + + 'Ice Cave Area': ['Shopping Mall Area'], + + 'Desert Pass Area': ['Swamp Nook Area'], + 'Desert Pass Southeast': ['Swamp Nook Area'], + 'Desert Pass Ledge': ['Swamp Nook Area'], + + 'Dam Area': ['Swamp Area'], + + 'South Pass Area': ['Dark South Pass Area'], + + 'Octoballoon Area': ['Bomber Corner Area'] +} + +OWTileRegions = bidict({ + 'Lost Woods West Area': 0x00, + 'Lost Woods East Area': 0x00, + + 'Lumberjack Area': 0x02, + + 'West Death Mountain (Top)': 0x03, + 'Spectacle Rock Ledge': 0x03, + 'West Death Mountain (Bottom)': 0x03, + + 'East Death Mountain (Top West)': 0x05, + 'East Death Mountain (Top East)': 0x05, + 'Spiral Cave Ledge': 0x05, + 'Mimic Cave Ledge': 0x05, + 'Fairy Ascension Ledge': 0x05, + 'Fairy Ascension Plateau': 0x05, + 'East Death Mountain (Bottom Left)': 0x05, + 'East Death Mountain (Bottom)': 0x05, + 'Death Mountain Floating Island': 0x05, + + 'Death Mountain TR Pegs Area': 0x07, + 'Death Mountain TR Pegs Ledge': 0x07, + + 'Mountain Pass Area': 0x0a, + 'Mountain Pass Entry': 0x0a, + 'Mountain Pass Ledge': 0x0a, + + 'Zora Waterfall Area': 0x0f, + 'Zora Waterfall Water': 0x0f, + 'Zora Waterfall Entryway': 0x0f, + + 'Lost Woods Pass West Area': 0x10, + 'Lost Woods Pass East Top Area': 0x10, + 'Lost Woods Pass Portal Area': 0x10, + 'Lost Woods Pass East Bottom Area': 0x10, + + 'Kakariko Fortune Area': 0x11, + + 'Kakariko Pond Area': 0x12, + + 'Sanctuary Area': 0x13, + 'Bonk Rock Ledge': 0x13, + + 'Graveyard Area': 0x14, + 'Graveyard Ledge': 0x14, + 'Kings Grave Area': 0x14, + + 'River Bend Area': 0x15, + 'River Bend East Bank': 0x15, + 'River Bend Water': 0x15, + + 'Potion Shop Area': 0x16, + 'Potion Shop Northeast': 0x16, + 'Potion Shop Water': 0x16, + + 'Zora Approach Area': 0x17, + 'Zora Approach Ledge': 0x17, + 'Zora Approach Water': 0x17, + + 'Kakariko Village': 0x18, + 'Kakariko Southwest': 0x18, + 'Kakariko Bush Yard': 0x18, + + 'Forgotten Forest Area': 0x1a, + + 'Hyrule Castle Area': 0x1b, + 'Hyrule Castle Southwest': 0x1b, + 'Hyrule Castle Courtyard': 0x1b, + 'Hyrule Castle Courtyard Northeast': 0x1b, + 'Hyrule Castle Ledge': 0x1b, + 'Hyrule Castle East Entry': 0x1b, + 'Hyrule Castle Water': 0x1b, + + 'Wooden Bridge Area': 0x1d, + 'Wooden Bridge Northeast': 0x1d, + 'Wooden Bridge Water': 0x1d, + + 'Eastern Palace Area': 0x1e, + + 'Blacksmith Area': 0x22, + 'Blacksmith Ledge': 0x22, + + 'Sand Dunes Area': 0x25, + + 'Maze Race Area': 0x28, + 'Maze Race Ledge': 0x28, + 'Maze Race Prize': 0x28, + + 'Kakariko Suburb Area': 0x29, + + 'Flute Boy Area': 0x2a, + 'Flute Boy Pass': 0x2a, + + 'Central Bonk Rocks Area': 0x2b, + + 'Links House Area': 0x2c, + + 'Stone Bridge North Area': 0x2d, + 'Stone Bridge South Area': 0x2d, + 'Stone Bridge Water': 0x2d, + + 'Tree Line Area': 0x2e, + 'Tree Line Water': 0x2e, + + 'Eastern Nook Area': 0x2f, + + 'Desert Area': 0x30, + 'Desert Ledge': 0x30, + 'Desert Ledge Keep': 0x30, + 'Desert Checkerboard Ledge': 0x30, + 'Desert Stairs': 0x30, + 'Desert Mouth': 0x30, + 'Desert Teleporter Ledge': 0x30, + 'Bombos Tablet Ledge': 0x30, + + 'Flute Boy Approach Area': 0x32, + 'Flute Boy Bush Entry': 0x32, + 'Cave 45 Ledge': 0x32, + + 'C Whirlpool Area': 0x33, + 'C Whirlpool Portal Area': 0x33, + 'C Whirlpool Water': 0x33, + 'C Whirlpool Outer Area': 0x33, + + 'Statues Area': 0x34, + 'Statues Water': 0x34, + + 'Lake Hylia Northwest Bank': 0x35, + 'Lake Hylia Northeast Bank': 0x35, + 'Lake Hylia South Shore': 0x35, + 'Lake Hylia Central Island': 0x35, + 'Lake Hylia Island': 0x35, + 'Lake Hylia Water': 0x35, + 'Lake Hylia Water D': 0x35, + + 'Ice Cave Area': 0x37, + 'Ice Cave Water': 0x37, + + 'Desert Pass Area': 0x3a, + 'Desert Pass Southeast': 0x3a, + 'Desert Pass Ledge': 0x3a, + + 'Dam Area': 0x3b, + + 'South Pass Area': 0x3c, + + 'Octoballoon Area': 0x3f, + 'Octoballoon Water': 0x3f, + 'Octoballoon Water Ledge': 0x3f, + + 'Skull Woods Forest': 0x40, + 'Skull Woods Portal Entry': 0x40, + 'Skull Woods Forest (West)': 0x40, + 'Skull Woods Forgotten Path (Southwest)': 0x40, + 'Skull Woods Forgotten Path (Northeast)': 0x40, + + 'Dark Lumberjack Area': 0x42, + + 'West Dark Death Mountain (Top)': 0x43, + 'GT Stairs': 0x43, + 'West Dark Death Mountain (Bottom)': 0x43, + + 'East Dark Death Mountain (Top)': 0x45, + 'East Dark Death Mountain (Bottom Left)': 0x45, + 'East Dark Death Mountain (Bottom)': 0x45, + 'East Dark Death Mountain (Bushes)': 0x45, + 'Dark Death Mountain Ledge': 0x45, + 'Dark Death Mountain Isolated Ledge': 0x45, + 'Dark Death Mountain Floating Island': 0x45, + + 'Turtle Rock Area': 0x47, + 'Turtle Rock Ledge': 0x47, + + 'Bumper Cave Area': 0x4a, + 'Bumper Cave Entry': 0x4a, + 'Bumper Cave Ledge': 0x4a, + + 'Catfish Area': 0x4f, + + 'Skull Woods Pass West Area': 0x50, + 'Skull Woods Pass East Top Area': 0x50, + 'Skull Woods Pass Portal Area': 0x50, + 'Skull Woods Pass East Bottom Area': 0x50, + + 'Dark Fortune Area': 0x51, + + 'Outcast Pond Area': 0x52, + + 'Dark Chapel Area': 0x53, + + 'Dark Graveyard Area': 0x54, + 'Dark Graveyard North': 0x54, + + 'Qirn Jump Area': 0x55, + 'Qirn Jump East Bank': 0x55, + 'Qirn Jump Water': 0x55, + + 'Dark Witch Area': 0x56, + 'Dark Witch Northeast': 0x56, + 'Dark Witch Water': 0x56, + + 'Catfish Approach Area': 0x57, + 'Catfish Approach Ledge': 0x57, + 'Catfish Approach Water': 0x57, + + 'Village of Outcasts': 0x58, + 'Village of Outcasts Bush Yard': 0x58, + + 'Shield Shop Area': 0x5a, + 'Shield Shop Fence': 0x5a, + + 'Pyramid Area': 0x5b, + 'Pyramid Exit Ledge': 0x5b, + 'Pyramid Pass': 0x5b, + 'Pyramid Water': 0x5b, + + 'Broken Bridge Area': 0x5d, + 'Broken Bridge Northeast': 0x5d, + 'Broken Bridge West': 0x5d, + 'Broken Bridge Water': 0x5d, + + 'Palace of Darkness Area': 0x5e, + + 'Hammer Pegs Area': 0x62, + 'Hammer Pegs Entry': 0x62, + + 'Dark Dunes Area': 0x65, + + 'Dig Game Area': 0x68, + 'Dig Game Ledge': 0x68, + + 'Frog Area': 0x69, + 'Frog Prison': 0x69, + 'Archery Game Area': 0x69, + + 'Stumpy Area': 0x6a, + 'Stumpy Pass': 0x6a, + + 'Dark Bonk Rocks Area': 0x6b, + + 'Big Bomb Shop Area': 0x6c, + + 'Hammer Bridge North Area': 0x6d, + 'Hammer Bridge South Area': 0x6d, + 'Hammer Bridge Water': 0x6d, + + 'Dark Tree Line Area': 0x6e, + 'Dark Tree Line Water': 0x6e, + + 'Darkness Nook Area': 0x6f, + + 'Mire Area': 0x70, + 'Mire Teleporter Ledge': 0x70, + + 'Stumpy Approach Area': 0x72, + 'Stumpy Approach Bush Entry': 0x72, + + 'Dark C Whirlpool Area': 0x73, + 'Dark C Whirlpool Portal Area': 0x73, + 'Dark C Whirlpool Water': 0x73, + 'Dark C Whirlpool Outer Area': 0x73, + + 'Hype Cave Area': 0x74, + 'Hype Cave Water': 0x74, + + 'Ice Lake Northwest Bank': 0x75, + 'Ice Lake Northeast Bank': 0x75, + 'Ice Lake Southwest Ledge': 0x75, + 'Ice Lake Southeast Ledge': 0x75, + 'Ice Lake Water': 0x75, + 'Ice Lake Iceberg': 0x75, + 'Ice Palace Area': 0x75, + + 'Shopping Mall Area': 0x77, + 'Shopping Mall Water': 0x77, + + 'Swamp Nook Area': 0x7a, + + 'Swamp Area': 0x7b, + + 'Dark South Pass Area': 0x7c, + + 'Bomber Corner Area': 0x7f, + 'Bomber Corner Water': 0x7f, + 'Bomber Corner Water Ledge': 0x7f, + + 'Master Sword Meadow': 0x80, + 'Hobo Bridge': 0x80, + + 'Zoras Domain': 0x81 +}) \ No newline at end of file diff --git a/Plando.py b/Plando.py index 4374b706..1af62926 100755 --- a/Plando.py +++ b/Plando.py @@ -9,6 +9,7 @@ import sys from BaseClasses import World from Regions import create_regions +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit from Rom import patch_rom, LocalRom, write_string_to_rom, apply_rom_settings, get_sprite_from_name from Rules import set_rules @@ -42,6 +43,7 @@ def main(args): create_regions(world, 1) create_dungeons(world, 1) + link_overworld(world, 1) link_entrances(world, 1) logger.info('Calculating Access Rules.') diff --git a/PotShuffle.py b/PotShuffle.py index 4ad16554..c7164565 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -788,12 +788,12 @@ vanilla_pots = { 0x108: [Pot(166, 19, PotItem.Chicken, 'Chicken House', obj=RoomObject(0x03EFA9, [0x4F, 0x9F, 0xFA]))], 0x10C: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy', obj=RoomObject(0x03F329, [0xB3, 0x73, 0xFA]))], # note: these addresses got moved thanks to waterfall fairy edit - 0x114: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F79A, [0xBB, 0x23, 0xFA])), - Pot(96, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F79D, [0xC3, 0x23, 0xFA])), - Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x2B, 0xFA])), - Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x2B, 0xFA])), - Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x53, 0xFA])), - Pot(96, 10, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x53, 0xFA]))], + 0x114: [Pot(92, 4, PotItem.Heart, 'Mire Hint', obj=RoomObject(0x03F79A, [0xBB, 0x23, 0xFA])), + Pot(96, 4, PotItem.Heart, 'Mire Hint', obj=RoomObject(0x03F79D, [0xC3, 0x23, 0xFA])), + Pot(92, 5, PotItem.Bomb, 'Mire Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x2B, 0xFA])), + Pot(96, 5, PotItem.Bomb, 'Mire Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x2B, 0xFA])), + Pot(92, 10, PotItem.FiveArrows, 'Mire Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x53, 0xFA])), + Pot(96, 10, PotItem.Heart, 'Mire Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x53, 0xFA]))], 0x117: [Pot(138, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB2, [0x17, 0x1F, 0xFA])), # 0x38A -> 38A Pot(142, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB8, [0x1F, 0x1F, 0xFA])), Pot(166, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC1, [0x4F, 0x1F, 0xFA])), diff --git a/Regions.py b/Regions.py index fc8bb98e..6cb44847 100644 --- a/Regions.py +++ b/Regions.py @@ -1,6 +1,6 @@ import collections from Items import ItemFactory -from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType, LocationType, PotItem, PotFlags +from BaseClasses import Region, Location, Entrance, RegionType, Terrain, Shop, ShopType, LocationType, PotItem, PotFlags from PotShuffle import key_drop_data, vanilla_pots, choose_pots, PotSecretTable from source.dungeon.EnemyList import setup_enemy_locations, enemy_names @@ -10,109 +10,235 @@ def create_regions(world, player): world.regions += [ create_menu_region(player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q', 'Other World S&Q']), create_menu_region(player, 'Flute Sky', None, ['Flute Spot 1', 'Flute Spot 2', 'Flute Spot 3', 'Flute Spot 4', - 'Flute Spot 5', 'Flute Spot 6', 'Flute Spot 7', 'Flute Spot 8']), - create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'], - ['Lost Woods Gamble', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Lumberjack House', - 'Fortune Teller (Light)', 'Bonk Rock Cave', 'Sanctuary', 'Sanctuary Grave', 'North Fairy Cave Drop', 'North Fairy Cave', - 'Kakariko Well Drop', 'Kakariko Well Cave', 'Blinds Hideout', 'Elder House (West)', 'Elder House (East)', - 'Snitch Lady (West)', 'Snitch Lady (East)', 'Chicken House', 'Sick Kids House', 'Kakariko Shop', 'Tavern North', 'Tavern (Front)', - 'Hyrule Castle Secret Entrance Drop', 'Sahasrahlas Hut', 'Eastern Palace', 'Blacksmiths Hut', 'Bat Cave Cave', - 'Library', 'Two Brothers House (East)', 'Kakariko Gamble Game', 'Bonk Fairy (Light)', 'Links House', 'Lake Hylia Fairy', 'Long Fairy Cave', - 'Aginahs Cave', 'Light Hype Fairy', 'Lake Hylia Fortune Teller', 'Lake Hylia Shop', 'Mini Moldorm Cave', - 'Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Desert Fairy', '50 Rupee Cave', 'Dam', - - 'Master Sword Meadow', 'Death Mountain Entrance Rock', 'Graveyard Ladder (Bottom)', 'Kings Grave Rocks (Outer)', - 'Wooden Bridge Bush (South)', 'Kakariko Southwest Bush (North)', 'Kakariko Yard Bush (South)', 'Hyrule Castle Main Gate', 'Bat Cave Ledge Peg', - 'Light World Water Drop', 'Desert Statue Move', 'Checkerboard Ledge Approach', 'Cave 45 Approach', 'Bombos Tablet Ladder (Bottom)', - - 'Kakariko Teleporter', 'Castle Gate Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'LW Flute']), - create_lw_region(player, 'West Death Mountain (Top)', ['Ether Tablet'], ['Tower of Hera', 'Death Mountain Drop', 'Spectacle Rock Approach', - 'DM Hammer Bridge (West)']), + 'Flute Spot 5', 'Flute Spot 6', 'Flute Spot 7', 'Flute Spot 8']), + create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal'], ['Master Sword Meadow SC']), + create_lw_region(player, 'Lost Woods West Area', None, ['Lost Woods Bush (West)', 'Lost Woods NW', 'Lost Woods SW', 'Lost Woods SC']), + create_lw_region(player, 'Lost Woods East Area', ['Mushroom'], ['Lost Woods Gamble', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lost Woods Bush (East)', 'Lost Woods SE', 'Lost Woods EN']), + create_lw_region(player, 'Lumberjack Area', None, ['Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Lumberjack House', 'Lumberjack WN', 'Lumberjack SW']), + create_lw_region(player, 'West Death Mountain (Top)', ['Ether Tablet'], ['Tower of Hera', 'Spectacle Rock Approach', 'West Death Mountain Drop', 'West Death Mountain EN']), + create_lw_region(player, 'Spectacle Rock Ledge', ['Spectacle Rock'], ['Spectacle Rock Leave', 'Spectacle Rock Ledge Drop']), create_lw_region(player, 'West Death Mountain (Bottom)', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', - 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', - 'DM Broken Bridge (West)', 'Death Mountain Teleporter', 'DM Flute']), - create_lw_region(player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop', 'Spectacle Rock Leave']), - create_lw_region(player, 'East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'DM Hammer Bridge (East)', 'Floating Island Bridge (East)', - 'Spiral Cave Ledge Access', 'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access', 'East Death Mountain Drop', - 'Turtle Rock Teleporter']), + 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', + 'West Death Mountain Teleporter', 'West Death Mountain ES']), + create_lw_region(player, 'East Death Mountain (Top West)', None, ['DM Hammer Bridge (West)', 'East Death Mountain WN']), + create_lw_region(player, 'East Death Mountain (Top East)', None, ['Paradox Cave (Top)', 'DM Hammer Bridge (East)', 'Floating Island Bridge (East)', + 'EDM To Spiral Ledge Drop', 'EDM To Fairy Ledge Drop', 'EDM To Mimic Ledge Drop', 'EDM Ledge Drop', 'East Death Mountain EN']), create_lw_region(player, 'Death Mountain Floating Island', ['Floating Island'], ['Floating Island Bridge (West)']), - create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']), - create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Cave Ledge Drop']), + create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Ledge Drop']), + create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Ledge Drop']), create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Cave (Top)', 'Fairy Ascension Ledge Drop']), + create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Cave (Bottom)', 'Fairy Ascension Rocks (Inner)', 'Fairy Ascension Plateau Ledge Drop']), + create_lw_region(player, 'East Death Mountain (Bottom Left)', None, ['DM Broken Bridge (West)', 'East Death Mountain WS']), create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Spiral Cave (Bottom)', 'Hookshot Fairy', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', - 'DM Broken Bridge (East)', 'Fairy Ascension Rocks', 'East Death Mountain Teleporter', 'EDM Flute']), - create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Cave (Bottom)', 'Fairy Ascension Drop']), - create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Cave (West)', 'Death Mountain Return Ledge Drop'], 'a ledge in the foothills'), - create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']), - create_lw_region(player, 'Northeast Light World', None, ['Zoras Domain', 'Potion Shop Rock (North)', 'Northeast Light World Water Drop']), - create_lw_region(player, 'Zora Waterfall Entryway', None, ['Waterfall of Wishing', 'Zora Waterfall Water Drop', 'ZLW Flute']), + 'DM Broken Bridge (East)', 'Fairy Ascension Rocks (Outer)', 'East Death Mountain Teleporter']), + create_lw_region(player, 'Death Mountain TR Pegs Area', None, ['TR Pegs Ledge Entry', 'Death Mountain TR Pegs WN']), + create_lw_region(player, 'Death Mountain TR Pegs Ledge', None, ['TR Pegs Ledge Leave', 'TR Pegs Ledge Drop', 'TR Pegs Teleporter']), + create_lw_region(player, 'Mountain Pass Area', None, ['Mountain Pass Rock (Outer)', 'Mountain Pass NW', 'Mountain Pass SE']), + create_lw_region(player, 'Mountain Pass Ledge', None, ['Death Mountain Return Cave (West)', 'Mountain Pass Ledge Drop'], 'a ledge in the foothills'), + create_lw_region(player, 'Mountain Pass Entry', None, ['Old Man Cave (West)', 'Mountain Pass Rock (Inner)', 'Mountain Pass Entry Ledge Drop']), + create_lw_region(player, 'Zora Waterfall Area', None, ['Zora Waterfall Water Entry', 'Zora Waterfall SE', 'Zora Waterfall NE']), + create_lw_region(player, 'Zora Waterfall Water', None, ['Zora Waterfall Approach', 'Zora Waterfall Landing', 'Zora Whirlpool'], 'Light World', Terrain.Water), + create_lw_region(player, 'Zora Waterfall Entryway', None, ['Waterfall of Wishing', 'Zora Waterfall Water Drop']), + create_lw_region(player, 'Zoras Domain', ['King Zora', 'Zora\'s Ledge'], ['Zoras Domain SW']), + create_lw_region(player, 'Lost Woods Pass West Area', None, ['Lost Woods Pass NW', 'Lost Woods Pass SW']), + create_lw_region(player, 'Lost Woods Pass East Top Area', None, ['Lost Woods Pass Hammer (North)', 'Lost Woods Pass NE']), + create_lw_region(player, 'Lost Woods Pass Portal Area', None, ['Lost Woods Pass Hammer (South)', 'Lost Woods Pass Rock (North)', 'Kakariko Teleporter']), + create_lw_region(player, 'Lost Woods Pass East Bottom Area', None, ['Lost Woods Pass Rock (South)', 'Lost Woods Pass SE']), + create_lw_region(player, 'Kakariko Fortune Area', None, ['Fortune Teller (Light)', 'Kakariko Fortune NE', 'Kakariko Fortune EN', 'Kakariko Fortune ES', 'Kakariko Fortune SC']), + create_lw_region(player, 'Kakariko Pond Area', None, ['Kakariko Pond Whirlpool', 'Kakariko Pond NE', 'Kakariko Pond WN', 'Kakariko Pond WS', + 'Kakariko Pond SW', 'Kakariko Pond SE', 'Kakariko Pond EN', 'Kakariko Pond ES']), + create_lw_region(player, 'Sanctuary Area', None, ['Sanctuary', 'Sanctuary WS', 'Sanctuary EC']), + create_lw_region(player, 'Bonk Rock Ledge', None, ['Bonk Rock Cave', 'Bonk Rock Ledge Drop', 'Sanctuary WN']), + create_lw_region(player, 'Graveyard Area', None, ['Sanctuary Grave', 'Kings Grave Rocks (Outer)', 'Graveyard Ladder (Bottom)', 'Graveyard WC', 'Graveyard EC']), create_lw_region(player, 'Graveyard Ledge', None, ['Graveyard Cave', 'Graveyard Ledge Drop', 'Graveyard Ladder (Top)']), create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Rocks (Inner)']), - create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Wooden Bridge Bush (North)', 'Potion Shop Rock (South)', 'Potion Shop Water Drop', 'NWLW Flute']), - create_lw_region(player, 'Bush Covered Lawn', None, ['Bush Covered House', 'Kakariko Yard Bush (North)']), - create_lw_region(player, 'Bomb Hut Area', None, ['Light World Bomb Hut', 'Kakariko Southwest Bush (South)']), - create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Entrance (South)', 'Inverted Pyramid Entrance', 'Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Main Gate (North)']), - create_lw_region(player, 'Hyrule Castle Secret Entrance Area', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Courtyard Bush (North)']), + create_lw_region(player, 'River Bend Area', None, ['North Fairy Cave Drop', 'North Fairy Cave', 'River Bend Water Drop', 'River Bend WC', 'River Bend SW']), + create_lw_region(player, 'River Bend East Bank', None, ['River Bend East Water Drop', 'River Bend SE', 'River Bend EC', 'River Bend ES']), + create_lw_region(player, 'River Bend Water', None, ['River Bend West Pier', 'River Bend East Pier', 'River Bend Whirlpool', 'River Bend EN', 'River Bend SC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Potion Shop Water Drop', 'Potion Shop Rock (South)', 'Potion Shop WC', 'Potion Shop WS']), + create_lw_region(player, 'Potion Shop Northeast', None, ['Potion Shop Northeast Water Drop', 'Potion Shop Rock (North)', 'Potion Shop EC']), + create_lw_region(player, 'Potion Shop Water', None, ['Potion Shop WN', 'Potion Shop EN'], 'Light World', Terrain.Water), + create_lw_region(player, 'Zora Approach Area', None, ['Zora Approach Rocks (West)', 'Zora Approach Bottom Ledge Drop', 'Zora Approach Water Drop', 'Zora Approach WC']), + create_lw_region(player, 'Zora Approach Ledge', None, ['Zora Approach Rocks (East)', 'Zora Approach Ledge Drop', 'Zora Approach NE']), + create_lw_region(player, 'Zora Approach Water', None, ['Zora Approach WN'], 'Light World', Terrain.Water), + create_lw_region(player, 'Kakariko Village', ['Bottle Merchant'], ['Kakariko Well Drop', 'Kakariko Well Cave', 'Blinds Hideout', 'Elder House (West)', 'Elder House (East)', + 'Snitch Lady (West)', 'Snitch Lady (East)', 'Chicken House', 'Sick Kids House', 'Kakariko Shop', 'Tavern (Front)', 'Tavern North', + 'Kakariko Southwest Bush (North)', 'Kakariko Yard Bush (South)', 'Kakariko NW', 'Kakariko NC', 'Kakariko NE', 'Kakariko ES', 'Kakariko SE']), + create_lw_region(player, 'Kakariko Southwest', None, ['Light World Bomb Hut', 'Kakariko Southwest Bush (South)']), + create_lw_region(player, 'Kakariko Bush Yard', None, ['Bush Covered House', 'Kakariko Yard Bush (North)']), + create_lw_region(player, 'Forgotten Forest Area', None, ['Forgotten Forest NW', 'Forgotten Forest NE', 'Forgotten Forest ES']), + create_lw_region(player, 'Hyrule Castle Area', None, ['Hyrule Castle Secret Entrance Drop', 'Hyrule Castle East Rock (Inner)', 'Hyrule Castle Southwest Bush (North)', + 'Hyrule Castle Main Gate (South)', 'Castle Gate Teleporter', 'Hyrule Castle WN', 'Hyrule Castle SE']), + create_lw_region(player, 'Hyrule Castle Southwest', None, ['Hyrule Castle Southwest Bush (South)', 'Hyrule Castle SW']), + create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Entrance (South)', 'Inverted Pyramid Entrance', 'Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Main Gate (North)', 'Castle Gate Teleporter (Inner)']), + create_lw_region(player, 'Hyrule Castle Courtyard Northeast', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Courtyard Bush (North)']), create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Entrance (East)', 'Inverted Pyramid Hole', - 'Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Ledge Drop'], 'the castle rampart'), - create_lw_region(player, 'Bat Cave Ledge', None, ['Bat Cave Drop', 'Bat Cave Ledge Peg (East)']), - create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)', 'Maze Race Ledge Drop'], 'a race against time'), - create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), - create_lw_region(player, 'Desert Palace Mouth', None, ['Desert Palace Entrance (East)', 'Desert Palace Mouth Drop'], 'a sandy vista'), + 'Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Ledge Drop'], 'the castle rampart'), + create_lw_region(player, 'Hyrule Castle East Entry', None, ['Hyrule Castle East Rock (Outer)', 'Hyrule Castle ES']), + create_lw_region(player, 'Hyrule Castle Water', None, [], 'Light World', Terrain.Water), + create_lw_region(player, 'Wooden Bridge Area', None, ['Wooden Bridge Bush (South)', 'Wooden Bridge Water Drop', 'Wooden Bridge NW', 'Wooden Bridge SW']), + create_lw_region(player, 'Wooden Bridge Northeast', None, ['Wooden Bridge Bush (North)', 'Wooden Bridge Northeast Water Drop', 'Wooden Bridge NE']), + create_lw_region(player, 'Wooden Bridge Water', None, ['Wooden Bridge NC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Eastern Palace Area', None, ['Sahasrahlas Hut', 'Eastern Palace', 'Eastern Palace SW', 'Eastern Palace SE']), + create_lw_region(player, 'Blacksmith Area', None, ['Blacksmiths Hut', 'Bat Cave Cave', 'Blacksmith Ledge Peg (West)', 'Blacksmith WS']), + create_lw_region(player, 'Blacksmith Ledge', None, ['Bat Cave Drop', 'Blacksmith Ledge Peg (East)']), + create_lw_region(player, 'Sand Dunes Area', None, ['Sand Dunes NW', 'Sand Dunes WN', 'Sand Dunes SC']), + create_lw_region(player, 'Maze Race Area', None, ['Maze Race ES']), + create_lw_region(player, 'Maze Race Ledge', None, ['Two Brothers House (West)', 'Maze Race Game'], 'a race against time'), + create_lw_region(player, 'Maze Race Prize', ['Maze Race'], ['Maze Race Ledge Drop']), # this is a separate region to make OWG item get possible without allowing the Entrance access + create_lw_region(player, 'Kakariko Suburb Area', None, ['Library', 'Two Brothers House (East)', 'Kakariko Gamble Game', 'Kakariko Suburb NE', 'Kakariko Suburb WS', 'Kakariko Suburb ES']), + create_lw_region(player, 'Flute Boy Area', ['Flute Spot'], ['Flute Boy SC']), + create_lw_region(player, 'Flute Boy Pass', None, ['Flute Boy WS', 'Flute Boy SW']), + create_lw_region(player, 'Central Bonk Rocks Area', None, ['Bonk Fairy (Light)', 'Central Bonk Rocks NW', 'Central Bonk Rocks SW', + 'Central Bonk Rocks EN', 'Central Bonk Rocks EC', 'Central Bonk Rocks ES']), + create_lw_region(player, 'Links House Area', None, ['Links House', 'Links House NE', 'Links House WN', 'Links House WC', 'Links House WS', 'Links House SC', 'Links House ES']), + create_lw_region(player, 'Stone Bridge North Area', None, ['Stone Bridge (Southbound)', 'Stone Bridge NC', 'Stone Bridge EN']), + create_lw_region(player, 'Stone Bridge South Area', None, ['Stone Bridge (Northbound)', 'Stone Bridge WS', 'Stone Bridge SC']), + create_lw_region(player, 'Stone Bridge Water', None, ['Stone Bridge WC', 'Stone Bridge EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Hobo Bridge', ['Hobo'], ['Hobo EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Central Cliffs', None, ['Lake Hylia Island FAWT Ledge Drop', 'Stone Bridge EC Cliff Water Drop', 'C Whirlpool Portal Cliff Ledge Drop']), + create_lw_region(player, 'Tree Line Area', None, ['Lake Hylia Fairy', 'Tree Line WN', 'Tree Line NW', 'Tree Line SE']), + create_lw_region(player, 'Tree Line Water', None, ['Tree Line WC', 'Tree Line SC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Eastern Nook Area', None, ['Long Fairy Cave', 'East Hyrule Teleporter', 'Eastern Nook NE']), + create_lw_region(player, 'Desert Area', None, ['Aginahs Cave', 'Desert Statue Move', 'Checkerboard Ledge Approach', 'Desert ES']), create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (West)', 'Desert Ledge Rocks (Outer)', 'Desert Ledge Drop'], 'the desert ledge'), - create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Rocks (Inner)'], 'the desert ledge'), + create_lw_region(player, 'Desert Ledge Keep', None, ['Desert Palace Entrance (North)', 'Desert Ledge Rocks (Inner)'], 'the desert ledge'), create_lw_region(player, 'Desert Checkerboard Ledge', None, ['Checkerboard Cave', 'Checkerboard Ledge Drop', 'Checkerboard Ledge Leave']), + create_lw_region(player, 'Desert Stairs', None, ['Desert Palace Entrance (South)']), + create_lw_region(player, 'Desert Mouth', None, ['Desert Palace Entrance (East)', 'Desert Mouth Drop'], 'a sandy vista'), create_lw_region(player, 'Desert Teleporter Ledge', None, ['Desert Teleporter Drop', 'Desert Teleporter']), - create_lw_region(player, 'Bombos Tablet Ledge', ['Bombos Tablet'], ['Bombos Tablet Ladder (Top)']), - create_lw_region(player, 'Desert Northern Cliffs'), + create_lw_region(player, 'Desert Northern Cliffs', None, ['Checkerboard Cliff Ledge Drop', 'Cave 45 Cliff Ledge Drop']), + create_lw_region(player, 'Bombos Tablet Ledge', ['Bombos Tablet'], ['Bombos Tablet Drop', 'Desert EC']), + create_lw_region(player, 'Flute Boy Approach Area', None, ['Flute Boy Bush (South)', 'Cave 45 Approach', 'Flute Boy Pass NW', 'Flute Boy Pass EC']), + create_lw_region(player, 'Flute Boy Bush Entry', None, ['Flute Boy Bush (North)', 'Flute Boy Pass NC']), create_lw_region(player, 'Cave 45 Ledge', None, ['Cave 45', 'Cave 45 Ledge Drop', 'Cave 45 Leave']), - create_lw_region(player, 'Lake Hylia Water', None, ['Light World Pier', 'Potion Shop Pier', 'Hobo Pier', 'Lake Hylia Island Pier', 'Lake Hylia Central Island Pier', 'Lake Hylia Whirlpool', 'Waterfall Fairy Access']), - create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), - create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Water Drop', 'Lake Hylia Teleporter']), - create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']), - create_lw_region(player, 'Hobo Bridge', ['Hobo']), - create_lw_region(player, 'Zoras Domain', ['King Zora', 'Zora\'s Ledge']), + create_lw_region(player, 'C Whirlpool Area', None, ['C Whirlpool Rock (Bottom)', 'C Whirlpool Pegs (Outer)', 'C Whirlpool Water Entry', 'C Whirlpool EN', 'C Whirlpool ES', 'C Whirlpool SC']), + create_lw_region(player, 'C Whirlpool Portal Area', None, ['C Whirlpool Pegs (Inner)', 'South Hyrule Teleporter']), + create_lw_region(player, 'C Whirlpool Water', None, ['C Whirlpool Landing', 'C Whirlpool', 'C Whirlpool EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'C Whirlpool Outer Area', None, ['C Whirlpool Rock (Top)', 'C Whirlpool WC', 'C Whirlpool NW']), + create_lw_region(player, 'Statues Area', None, ['Light Hype Fairy', 'Statues Water Entry', 'Statues NC', 'Statues WN', 'Statues WS', 'Statues SC']), + create_lw_region(player, 'Statues Water', None, ['Statues Landing', 'Statues WC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Lake Hylia Northwest Bank', None, ['Lake Hylia Fortune Teller', 'Lake Hylia Shop', 'Lake Hylia Water Drop', 'Lake Hylia NW']), + create_lw_region(player, 'Lake Hylia Northeast Bank', None, ['Lake Hylia Northeast Water Drop', 'Lake Hylia NE']), + create_lw_region(player, 'Lake Hylia South Shore', None, ['Mini Moldorm Cave', 'Lake Hylia South Water Drop', 'Lake Hylia WS', 'Lake Hylia ES']), + create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Water Drop', 'Lake Hylia Teleporter']), + create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island'], ['Lake Hylia Island Water Drop']), + create_lw_region(player, 'Lake Hylia Water', None, ['Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia West Pier', 'Lake Hylia East Pier', + 'Lake Hylia Water D Approach', 'Lake Hylia Whirlpool', 'Lake Hylia NC', 'Lake Hylia EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Lake Hylia Water D', None, ['Lake Hylia Water D Leave']), + create_lw_region(player, 'Ice Cave Area', None, ['Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Cave Water Drop', 'Ice Cave SE']), + create_lw_region(player, 'Ice Cave Water', None, ['Ice Cave Pier', 'Ice Cave SW'], 'Light World', Terrain.Water), + create_lw_region(player, 'Desert Pass Area', ['Purple Chest'], ['Desert Fairy', '50 Rupee Cave', 'Desert Pass Ladder (South)', 'Desert Pass Rocks (North)', 'Desert Pass WS', 'Desert Pass EC']), + create_lw_region(player, 'Desert Pass Southeast', None, ['Desert Pass Rocks (South)', 'Desert Pass ES']), + create_lw_region(player, 'Desert Pass Ledge', None, ['Desert Pass Ladder (North)', 'Desert Pass Ledge Drop', 'Desert Pass WC']), + create_lw_region(player, 'Dam Area', ['Sunken Treasure'], ['Dam', 'Dam WC', 'Dam WS', 'Dam NC', 'Dam EC']), + create_lw_region(player, 'South Pass Area', None, ['South Pass WC', 'South Pass NC', 'South Pass ES']), + create_lw_region(player, 'Octoballoon Area', None, ['Octoballoon Water Drop', 'Octoballoon WS', 'Octoballoon NE']), + create_lw_region(player, 'Octoballoon Water', None, ['Octoballoon Pier', 'Octoballoon Whirlpool', 'Octoballoon WC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Octoballoon Water Ledge', None, ['Octoballoon Waterfall Water Drop', 'Octoballoon NW'], 'Light World', Terrain.Water), + create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', + 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Rock (East)', 'Skull Woods SE']), + create_dw_region(player, 'Skull Woods Portal Entry', None, ['Skull Woods Rock (West)', 'Skull Woods SC']), + create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section'], 'a deep, dark forest'), + create_dw_region(player, 'Skull Woods Forgotten Path (Southwest)', None, ['Skull Woods Forgotten Bush (West)', 'Skull Woods SW']), + create_dw_region(player, 'Skull Woods Forgotten Path (Northeast)', None, ['Skull Woods Forgotten Bush (East)', 'Skull Woods EN']), + create_dw_region(player, 'Dark Lumberjack Area', None, ['Dark Lumberjack Shop', 'Dark Lumberjack WN', 'Dark Lumberjack SW']), + create_dw_region(player, 'West Dark Death Mountain (Top)', None, ['GT Approach', 'Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain Drop', 'West Dark Death Mountain EN']), + create_dw_region(player, 'GT Stairs', None, ['Ganons Tower', 'GT Leave']), create_dw_region(player, 'West Dark Death Mountain (Bottom)', None, ['Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Ladder (Bottom)', - 'Dark Death Mountain Teleporter (West)', 'DDM Flute']), - create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Ganons Tower', 'Hookshot Cave', 'Superbunny Cave (Top)', 'Turtle Rock', 'Dark Death Mountain Drop (West)', - 'Dark Death Mountain Drop (East)', 'Dark Death Mountain Ladder (Top)', 'Turtle Rock Tail Drop']), - create_dw_region(player, 'Dark Death Mountain Floating Island', None, ['Hookshot Cave Back Entrance', 'Floating Island Drop'], 'a dark island'), + 'Dark Death Mountain Teleporter (West)', 'West Dark Death Mountain ES']), + create_dw_region(player, 'East Dark Death Mountain (Top)', None, ['Superbunny Cave (Top)', 'Hookshot Cave', 'East Dark Death Mountain Drop', 'East Dark Death Mountain WN', 'East Dark Death Mountain EN']), + create_dw_region(player, 'Dark Death Mountain Floating Island', None, ['Hookshot Cave Back Entrance', 'Floating Island Drop'], 'a dark floating island'), create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)'], 'a dark ledge'), create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Turtle Rock Isolated Ledge Entrance'], 'a dark vista'), - create_dw_region(player, 'East Dark Death Mountain (Bottom)', None, ['East Dark Death Mountain Bushes', 'Superbunny Cave (Bottom)', 'Dark Death Mountain Shop', - 'East Dark Death Mountain Teleporter (Bottom)', 'EDDM Flute']), + create_dw_region(player, 'East Dark Death Mountain (Bottom)', None, ['Superbunny Cave (Bottom)', 'Dark Death Mountain Shop', 'East Dark Death Mountain Bushes', 'East Dark Death Mountain Teleporter']), create_dw_region(player, 'East Dark Death Mountain (Bushes)', None, []), - create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop', 'East Dark Death Mountain Teleporter (Top)']), - create_dw_region(player, 'West Dark World', ['Frog'], ['Dark Lumberjack Shop', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Chest Game', 'Thieves Town', - 'C-Shaped House', 'Brewery', 'Red Shield Shop', 'Skull Woods Forest', 'Bumper Cave Entrance Rock', - 'West Dark World Water Drop', 'Grassy Lawn Pegs (Bottom)', 'Peg Area Rocks (Left)', 'Village of Outcasts Drop', - 'West Dark World Teleporter', 'WDW Flute', 'Dark Graveyard Bush (South)']), - create_dw_region(player, 'Dark Graveyard North', None, ['Dark Graveyard Bush (North)']), - create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', - 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']), - create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section'], 'a deep, dark forest'), + create_dw_region(player, 'East Dark Death Mountain (Bottom Left)', None, ['East Dark Death Mountain WS']), + create_dw_region(player, 'Turtle Rock Area', None, ['Turtle Rock', 'Turtle Rock Tail Ledge Drop', 'Turtle Rock WN']), + create_dw_region(player, 'Turtle Rock Ledge', None, ['Turtle Rock Ledge Drop', 'Turtle Rock Teleporter']), + create_dw_region(player, 'Bumper Cave Area', None, ['Bumper Cave Rock (Outer)', 'Bumper Cave NW', 'Bumper Cave SE']), create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave (Top)', 'Bumper Cave Ledge Drop'], 'a ledge with an item'), - create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']), - create_dw_region(player, 'Dark Grassy Lawn', None, ['Dark World Shop', 'Grassy Lawn Pegs (Top)', 'Dark Grassy Lawn Flute']), - create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (Right)', 'Hammer Peg Area Flute']), - create_dw_region(player, 'Northeast Dark World', None, ['Dark Potion Shop', 'Northeast Dark World Water Drop', 'Dark Witch Rock (South)', 'West Dark World Gap', - 'Broken Bridge Pass (Top)', 'NEDW Flute']), - create_dw_region(player, 'Catfish Area', ['Catfish'], ['Dark Witch Rock (North)', 'Catfish Water Drop']), - create_dw_region(player, 'East Dark World', ['Pyramid'], ['Pyramid Hole', 'Pyramid Fairy', 'Palace of Darkness Hint', 'Palace of Darkness', 'Dark Lake Hylia Fairy', - 'East Dark World Hint', 'Broken Bridge Pass (Bottom)', 'East Dark World Water Drop', 'Hammer Bridge Pegs (North)', - 'East Dark World Teleporter', 'EDW Flute']), - create_dw_region(player, 'Pyramid Exit Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']), - create_dw_region(player, 'Dark Desert', None, ['Mire Shed', 'Misery Mire', 'Dark Desert Fairy', 'Dark Desert Hint', 'DD Flute']), - create_dw_region(player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']), - create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Archery Game', 'Bonk Fairy (Dark)', 'Big Bomb Shop', 'Hype Cave', 'Dark Lake Hylia Shop', 'Swamp Palace', - 'Village of Outcasts Heavy Rock', 'Hammer Bridge Pegs (South)', 'South Dark World Water Drop', 'Post Aga Teleporter', - 'South Dark World Teleporter', 'SDW Flute']), - create_dw_region(player, 'Dark Lake Hylia Water', None, ['Northeast Dark World Pier', 'East Dark World Pier', 'Southeast Dark World Pier', 'Ice Palace Approach']), - create_dw_region(player, 'Dark Lake Hylia Central Island', None, ['Ice Palace', 'Ice Palace Leave Water Drop', 'Ice Island To East Pier', - 'Dark Lake Hylia Teleporter']), - create_dw_region(player, 'Southeast Dark World', None, ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Southeast Dark World Water Drop', 'DLHL Flute']), + create_dw_region(player, 'Bumper Cave Entry', None, ['Bumper Cave (Bottom)', 'Bumper Cave Rock (Inner)', 'Bumper Cave Entry Drop']), + create_dw_region(player, 'Catfish Area', ['Catfish'], ['Catfish SE']), + create_dw_region(player, 'Skull Woods Pass West Area', None, ['Skull Woods Pass Bush Row (West)', 'Skull Woods Pass NW', 'Skull Woods Pass SW']), + create_dw_region(player, 'Skull Woods Pass East Top Area', None, ['Skull Woods Pass Bush Row (East)', 'Skull Woods Pass Bush (North)', 'Skull Woods Pass NE']), + create_dw_region(player, 'Skull Woods Pass Portal Area', None, ['Skull Woods Pass Bush (South)', 'Skull Woods Pass Rock (North)', 'West Dark World Teleporter']), + create_dw_region(player, 'Skull Woods Pass East Bottom Area', None, ['Skull Woods Pass Rock (South)', 'Skull Woods Pass SE']), + create_dw_region(player, 'Dark Fortune Area', None, ['Fortune Teller (Dark)', 'Dark Fortune NE', 'Dark Fortune EN', 'Dark Fortune ES', 'Dark Fortune SC']), + create_dw_region(player, 'Outcast Pond Area', None, ['Outcast Pond NE', 'Outcast Pond WN', 'Outcast Pond WS', 'Outcast Pond SW', 'Outcast Pond SE', 'Outcast Pond EN', 'Outcast Pond ES']), + create_dw_region(player, 'Dark Chapel Area', None, ['Dark Sanctuary Hint', 'Dark Chapel WN', 'Dark Chapel WS', 'Dark Chapel EC']), + create_dw_region(player, 'Dark Graveyard Area', None, ['Dark Graveyard Bush (South)', 'Dark Graveyard WC', 'Dark Graveyard EC']), + create_dw_region(player, 'Dark Graveyard North', None, ['Dark Graveyard Bush (North)']), + create_dw_region(player, 'Qirn Jump Area', None, ['Qirn Jump Water Drop', 'Qirn Jump WC', 'Qirn Jump SW']), + create_dw_region(player, 'Qirn Jump East Bank', None, ['Qirn Jump East Water Drop', 'Qirn Jump SE', 'Qirn Jump EC', 'Qirn Jump ES']), + create_dw_region(player, 'Qirn Jump Water', None, ['Qirn Jump Pier', 'Qirn Jump Whirlpool', 'Qirn Jump EN', 'Qirn Jump SC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Dark Witch Area', None, ['Dark Potion Shop', 'Dark Witch Water Drop', 'Dark Witch Rock (South)', 'Dark Witch WC', 'Dark Witch WS']), + create_dw_region(player, 'Dark Witch Northeast', None, ['Dark Witch Northeast Water Drop', 'Dark Witch Rock (North)', 'Dark Witch EC']), + create_dw_region(player, 'Dark Witch Water', None, ['Dark Witch WN', 'Dark Witch EN'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Catfish Approach Area', None, ['Catfish Approach Rocks (West)', 'Catfish Approach Bottom Ledge Drop', 'Catfish Approach Water Drop', 'Catfish Approach WC']), + create_dw_region(player, 'Catfish Approach Ledge', None, ['Catfish Approach Rocks (East)', 'Catfish Approach Ledge Drop', 'Catfish Approach NE']), + create_dw_region(player, 'Catfish Approach Water', None, ['Catfish Approach WN'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Village of Outcasts', None, ['Chest Game', 'Thieves Town', 'C-Shaped House', 'Brewery', 'Bush Yard Pegs (Outer)', + 'Village of Outcasts NW', 'Village of Outcasts NC', 'Village of Outcasts NE', 'Village of Outcasts ES', 'Village of Outcasts SE']), + create_dw_region(player, 'Village of Outcasts Bush Yard', None, ['Dark World Shop', 'Bush Yard Pegs (Inner)']), + create_dw_region(player, 'Shield Shop Area', None, ['Shield Shop Fence Drop (Outer)', 'Shield Shop NW', 'Shield Shop NE']), + create_dw_region(player, 'Shield Shop Fence', None, ['Red Shield Shop', 'Shield Shop Fence Drop (Inner)']), + create_dw_region(player, 'Pyramid Area', ['Pyramid'], ['Pyramid Hole', 'Pyramid Fairy', 'Pyramid ES']), + create_dw_region(player, 'Pyramid Exit Ledge', None, ['Pyramid Entrance', 'Pyramid Exit Ledge Drop']), + create_dw_region(player, 'Pyramid Pass', None, ['Post Aga Teleporter', 'Pyramid SW', 'Pyramid SE']), + create_dw_region(player, 'Pyramid Water', None, [], 'Dark World', Terrain.Water), + create_dw_region(player, 'Broken Bridge Area', None, ['Broken Bridge Hammer Rock (South)', 'Broken Bridge Water Drop', 'Broken Bridge SW']), + create_dw_region(player, 'Broken Bridge Northeast', None, ['Broken Bridge Hammer Rock (North)', 'Broken Bridge Hookshot Gap', 'Broken Bridge Northeast Water Drop', 'Broken Bridge NE']), + create_dw_region(player, 'Broken Bridge West', None, ['Broken Bridge West Water Drop', 'Broken Bridge NW']), + create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE']), + create_dw_region(player, 'Hammer Pegs Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (East)']), + create_dw_region(player, 'Hammer Pegs Entry', None, ['Peg Area Rocks (West)', 'Hammer Pegs WS']), + create_dw_region(player, 'Dark Dunes Area', None, ['Dark Dunes NW', 'Dark Dunes WN', 'Dark Dunes SC']), + create_dw_region(player, 'Dig Game Area', ['Digging Game'], ['Dig Game To Ledge Drop', 'Dig Game ES']), + create_dw_region(player, 'Dig Game Ledge', None, ['Dig Game Ledge Drop', 'Dig Game EC']), + create_dw_region(player, 'Frog Area', None, ['Frog Ledge Drop', 'Frog Rock (Outer)', 'Archery Game Rock (North)', 'Frog NE']), + create_dw_region(player, 'Frog Prison', ['Frog'], ['Frog Rock (Inner)']), + create_dw_region(player, 'Archery Game Area', None, ['Archery Game', 'Archery Game Rock (South)', 'Frog WC', 'Frog WS', 'Frog ES']), + create_dw_region(player, 'Stumpy Area', ['Stumpy'], ['Stumpy SC']), + create_dw_region(player, 'Stumpy Pass', None, ['Stumpy WS', 'Stumpy SW']), + create_dw_region(player, 'Dark Bonk Rocks Area', None, ['Bonk Fairy (Dark)', 'Dark Bonk Rocks NW', 'Dark Bonk Rocks SW', 'Dark Bonk Rocks EN', 'Dark Bonk Rocks EC', 'Dark Bonk Rocks ES']), + create_dw_region(player, 'Big Bomb Shop Area', None, ['Big Bomb Shop', 'Big Bomb Shop NE', 'Big Bomb Shop WN', 'Big Bomb Shop WC', 'Big Bomb Shop WS', 'Big Bomb Shop SC', 'Big Bomb Shop ES']), + create_dw_region(player, 'Hammer Bridge North Area', None, ['Hammer Bridge Pegs (North)', 'Hammer Bridge Water Drop', 'Hammer Bridge NC', 'Hammer Bridge EN']), + create_dw_region(player, 'Hammer Bridge South Area', None, ['Hammer Bridge Pegs (South)', 'Hammer Bridge WS', 'Hammer Bridge SC']), + create_dw_region(player, 'Hammer Bridge Water', None, ['Hammer Bridge Pier', 'Hammer Bridge EC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Dark Central Cliffs', None, ['Ice Lake Iceberg FAWT Ledge Drop', 'Hammer Bridge EC Cliff Water Drop', 'Dark C Whirlpool Portal Cliff Ledge Drop']), + create_dw_region(player, 'Dark Tree Line Area', None, ['Dark Lake Hylia Fairy', 'Dark Tree Line WN', 'Dark Tree Line NW', 'Dark Tree Line SE']), + create_dw_region(player, 'Dark Tree Line Water', None, ['Dark Tree Line WC', 'Dark Tree Line SC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Darkness Nook Area', None, ['East Dark World Hint', 'East Dark World Teleporter', 'Palace of Darkness Nook NE']), + create_dw_region(player, 'Mire Area', None, ['Mire Shed', 'Misery Mire', 'Mire Fairy', 'Mire Hint']), + create_dw_region(player, 'Mire Teleporter Ledge', None, ['Mire Teleporter Ledge Drop', 'Mire Teleporter']), + create_dw_region(player, 'Mire Northern Cliffs', None, ['Mire Cliff Ledge Drop', 'Stumpy Approach Cliff Ledge Drop', 'Mirror To Bombos Tablet Ledge']), + create_dw_region(player, 'Stumpy Approach Area', None, ['Stumpy Approach Bush (South)', 'Stumpy Approach NW', 'Stumpy Approach EC']), + create_dw_region(player, 'Stumpy Approach Bush Entry', None, ['Stumpy Approach Bush (North)', 'Stumpy Approach NC']), + create_dw_region(player, 'Dark C Whirlpool Area', None, ['Dark C Whirlpool Rock (Bottom)', 'Dark C Whirlpool Pegs (Outer)', 'Dark C Whirlpool Water Entry', + 'Dark C Whirlpool EN', 'Dark C Whirlpool ES', 'Dark C Whirlpool SC']), + create_dw_region(player, 'Dark C Whirlpool Portal Area', None, ['Dark C Whirlpool Pegs (Inner)', 'South Dark World Teleporter']), + create_dw_region(player, 'Dark C Whirlpool Water', None, ['Dark C Whirlpool Landing', 'Dark C Whirlpool EC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Dark C Whirlpool Outer Area', None, ['Dark C Whirlpool Rock (Top)', 'Dark C Whirlpool WC', 'Dark C Whirlpool NW']), + create_dw_region(player, 'Hype Cave Area', None, ['Hype Cave', 'Hype Cave Water Entry', 'Hype Cave NC', 'Hype Cave WN', 'Hype Cave WS', 'Hype Cave SC']), + create_dw_region(player, 'Hype Cave Water', None, ['Hype Cave Landing', 'Hype Cave WC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Ice Lake Northwest Bank', None, ['Dark Lake Hylia Shop', 'Ice Lake Water Drop', 'Ice Lake NW']), + create_dw_region(player, 'Ice Lake Northeast Bank', None, ['Ice Lake Northeast Water Drop', 'Ice Lake Iceberg Bomb Jump', 'Ice Lake NE']), + create_dw_region(player, 'Ice Lake Southwest Ledge', None, ['Ice Lake Southwest Water Drop', 'Ice Lake WS']), + create_dw_region(player, 'Ice Lake Southeast Ledge', None, ['Ice Lake Southeast Water Drop', 'Ice Lake ES']), + create_dw_region(player, 'Ice Lake Water', None, ['Ice Lake Northeast Pier', 'Ice Lake NC', 'Ice Lake EC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Ice Lake Iceberg', None, ['Ice Palace Approach', 'Ice Lake Iceberg Water Entry', 'Ice Lake Northeast Pier Hop', 'Ice Lake Teleporter']), + create_dw_region(player, 'Ice Palace Area', None, ['Ice Palace', 'Ice Palace Leave']), + create_dw_region(player, 'Shopping Mall Area', None, ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Shopping Mall Water Drop', 'Shopping Mall SE']), + create_dw_region(player, 'Shopping Mall Water', None, ['Shopping Mall Pier', 'Shopping Mall SW'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Swamp Nook Area', None, ['Swamp Nook EC', 'Swamp Nook ES']), + create_dw_region(player, 'Swamp Area', None, ['Swamp Palace', 'Swamp WC', 'Swamp WS', 'Swamp NC', 'Swamp EC']), + create_dw_region(player, 'Dark South Pass Area', None, ['Dark South Pass WC', 'Dark South Pass NC', 'Dark South Pass ES']), + create_dw_region(player, 'Bomber Corner Area', None, ['Bomber Corner Water Drop', 'Bomber Corner WS', 'Bomber Corner NE']), + create_dw_region(player, 'Bomber Corner Water', None, ['Bomber Corner Pier', 'Bomber Corner Whirlpool', 'Bomber Corner WC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Bomber Corner Water Ledge', None, ['Bomber Corner Waterfall Water Drop', 'Bomber Corner NW'], 'Dark World', Terrain.Water), create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'), create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']), @@ -236,8 +362,8 @@ def create_regions(world, player): create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'East Dark World Hint', 'a storyteller'), create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), - create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'), - create_cave_region(player, 'Dark Desert Hint', 'a storyteller'), + create_cave_region(player, 'Mire Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Mire Hint', 'a storyteller'), create_cave_region(player, 'Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left', 'Hype Cave - Bottom', 'Hype Cave - Generous Guy']), create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop', ['Dark Lake Hylia Shop - Left', 'Dark Lake Hylia Shop - Middle', 'Dark Lake Hylia Shop - Right']), @@ -946,12 +1072,16 @@ def create_menu_region(player, name, locations=None, exits=None): return _create_region(player, name, RegionType.Menu, 'Menu', locations, exits) -def create_lw_region(player, name, locations=None, exits=None, hint='Light World'): - return _create_region(player, name, RegionType.LightWorld, hint, locations, exits) +def create_lw_region(player, name, locations=None, exits=None, hint='Light World', terrain=Terrain.Land): + region = _create_region(player, name, RegionType.LightWorld, hint, locations, exits) + region.terrain = terrain + return region -def create_dw_region(player, name, locations=None, exits=None, hint='Dark World'): - return _create_region(player, name, RegionType.DarkWorld, hint, locations, exits) +def create_dw_region(player, name, locations=None, exits=None, hint='Dark World', terrain=Terrain.Land): + region = _create_region(player, name, RegionType.DarkWorld, hint, locations, exits) + region.terrain = terrain + return region def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None): @@ -988,7 +1118,7 @@ def mark_light_dark_world_regions(world, player): current = queue.popleft() current.is_light_world = True for exit in current.exits: - if exit.connected_region is None or exit.connected_region.type == RegionType.DarkWorld: # todo: remove none check + if exit.connected_region is None or exit.connected_region.type == RegionType.DarkWorld: # Don't venture into the dark world continue if exit.connected_region not in seen: @@ -1090,7 +1220,7 @@ def adjust_locations(world, player): index += 1 setup_enemy_locations(world, player) # unreal events: - for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Dark Blacksmith Ruins', 'Frog', 'Missing Smith', 'Floodgate', + for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Frog', 'Missing Smith', 'Dark Blacksmith Ruins', 'Floodgate', 'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Attic Cracked Floor', 'Suspicious Maiden', 'Revealing Light', 'Ice Block Drop', 'Zelda Pickup', 'Zelda Drop Off', 'Skull Star Tile']: location = world.get_location_unsafe(l, player) diff --git a/Rom.py b/Rom.py index b4ed05f4..fd904442 100644 --- a/Rom.py +++ b/Rom.py @@ -2662,12 +2662,12 @@ OtherEntrances = {'Lake Hylia Fairy': 'A cave NE of Lake Hylia', 'Dark Lake Hylia Fairy': 'The cave NE dark Lake Hylia', 'Dark Death Mountain Fairy': 'The SW cave on dark DM', 'East Dark World Hint': 'The dark cave near the eastmost portal', - 'Dark Desert Hint': 'The cave east of the mire', + 'Mire Hint': 'The cave east of the mire', 'Palace of Darkness Hint': 'The building south of Kiki', 'Dark Lake Hylia Ledge Spike Cave': 'The rock SE dark Lake Hylia', 'Archery Game': 'The old archery game', 'Dark Lake Hylia Ledge Hint': 'The open cave SE dark Lake Hylia', - 'Dark Desert Fairy': 'The eastern hut in the mire', + 'Mire Fairy': 'The eastern hut in the mire', 'Dark Lake Hylia Ledge Fairy': 'The sealed cave SE dark Lake Hylia', 'Fortune Teller (Dark)': 'The building NE the Village of Outcasts', 'Dark Sanctuary Hint': 'The dark sanctuary cave' diff --git a/Rules.py b/Rules.py index 9abc4887..317a99c7 100644 --- a/Rules.py +++ b/Rules.py @@ -193,6 +193,10 @@ def global_rules(world, player): for exit in world.get_region('Menu', player).exits: exit.hide_path = True + # s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached + set_rule(world.get_entrance('Old Man S&Q', player), lambda state: state.can_reach('Old Man', 'Location', player)) + set_rule(world.get_entrance('Other World S&Q', player), lambda state: state.has_Mirror(player) and state.has('Beat Agahnim 1', player)) + # flute rules set_rule(world.get_entrance('Flute Spot 1', player), lambda state: state.can_flute(player)) set_rule(world.get_entrance('Flute Spot 2', player), lambda state: state.can_flute(player)) @@ -203,31 +207,26 @@ def global_rules(world, player): set_rule(world.get_entrance('Flute Spot 7', player), lambda state: state.can_flute(player)) set_rule(world.get_entrance('Flute Spot 8', player), lambda state: state.can_flute(player)) - # s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached - set_rule(world.get_entrance('Old Man S&Q', player), lambda state: state.can_reach('Old Man', 'Location', player)) - set_rule(world.get_entrance('Other World S&Q', player), lambda state: state.has_Mirror(player) and state.has('Beat Agahnim 1', player)) - # overworld location rules set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player)) - set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) - set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) + set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith + set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) - - set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith - set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest + set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) + set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) - # underworld rules + # underworld location rules + set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has_Mirror(player)) # can erase block - overridden in noglitches set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and state.can_reach('Potion Shop Area', 'Region', player)) set_rule(world.get_location('Sick Kid', player), lambda state: state.has_bottle(player)) - set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player)) - set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) - set_rule(world.get_location('Library', player), lambda state: state.has_Boots(player)) set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player)) + set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player)) + set_rule(world.get_location('Library', player), lambda state: state.has_Boots(player)) set_rule(world.get_location('Spike Cave', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or @@ -235,6 +234,9 @@ def global_rules(world, player): (state.can_extend_magic(player, 12, True) or (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4)))))) ) + + # underworld rules + set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has_Mirror(player)) # can erase block - overridden in noglitches set_rule(world.get_entrance('Hookshot Cave Bonk Path', player), lambda state: state.has('Hookshot', player) or state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Hookshot Cave Hook Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Bumper Cave Bottom to Top', player), lambda state: state.has('Cape', player)) @@ -245,37 +247,93 @@ def global_rules(world, player): set_rule(world.get_entrance('DM Hammer Bridge (East)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('DM Broken Bridge (West)', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('DM Broken Bridge (East)', player), lambda state: state.has('Hookshot', player)) - set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Fairy Ascension Rocks (Inner)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Fairy Ascension Rocks (Outer)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('TR Pegs Ledge Entry', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('TR Pegs Ledge Leave', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Mountain Pass Rock (Outer)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Mountain Pass Rock (Inner)', player), lambda state: state.can_lift_rocks(player)) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. - set_rule(world.get_entrance('Waterfall Fairy Access', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Waterfall Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Waterfall Approach', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lost Woods Pass Hammer (North)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lost Woods Pass Hammer (South)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lost Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Lost Woods Pass Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Kakariko Pond Whirlpool', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Kings Grave Rocks (Outer)', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Kings Grave Rocks (Inner)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('River Bend Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Potion Shop Rock (North)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Potion Shop Rock (South)', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Bat Cave Ledge Peg', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Bat Cave Ledge Peg (East)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Zora Approach Rocks (West)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Zora Approach Rocks (East)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Hyrule Castle East Rock (Inner)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Hyrule Castle East Rock (Outer)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Wooden Bridge Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Wooden Bridge Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Blacksmith Ledge Peg (West)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Blacksmith Ledge Peg (East)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Desert Statue Move', player), lambda state: state.has('Book of Mudora', player)) set_rule(world.get_entrance('Desert Ledge Rocks (Outer)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Desert Ledge Rocks (Inner)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('C Whirlpool Rock (Bottom)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('C Whirlpool Rock (Top)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('C Whirlpool Pegs (Outer)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('C Whirlpool Pegs (Inner)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lake Hylia Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Central Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Island Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Water D Leave', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Cave Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Desert Pass Rocks (North)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Desert Pass Rocks (South)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Skull Woods Rock (West)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Skull Woods Rock (East)', player), lambda state: state.can_lift_rocks(player)) + # this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off? + set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player)) + set_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Skull Woods Pass Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Qirn Jump Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Dark Witch Rock (North)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Dark Witch Rock (South)', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Grassy Lawn Pegs (Bottom)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Grassy Lawn Pegs (Top)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Broken Bridge Pass (Bottom)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player) or state.has('Flippers', player)) - set_rule(world.get_entrance('Broken Bridge Pass (Top)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player)) - set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has('Hookshot', player)) - set_rule(world.get_entrance('Peg Area Rocks (Left)', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Peg Area Rocks (Right)', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Catfish Approach Rocks (West)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Catfish Approach Rocks (East)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Bush Yard Pegs (Outer)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Bush Yard Pegs (Inner)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Broken Bridge Hammer Rock (South)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player)) + set_rule(world.get_entrance('Broken Bridge Hammer Rock (North)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player)) + set_rule(world.get_entrance('Broken Bridge Hookshot Gap', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Broken Bridge Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Broken Bridge Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Broken Bridge West Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Peg Area Rocks (West)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Peg Area Rocks (East)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Dig Game To Ledge Drop', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Frog Rock (Inner)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Frog Rock (Outer)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Archery Game Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Archery Game Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Hammer Bridge Pegs (North)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Hammer Bridge Pegs (South)', player), lambda state: state.has('Hammer', player)) - - # this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off? - set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: state.has_Pearl(player) and - (state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player))) + set_rule(world.get_entrance('Hammer Bridge Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark C Whirlpool Rock (Bottom)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Dark C Whirlpool Rock (Top)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Dark C Whirlpool Pegs (Outer)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Dark C Whirlpool Pegs (Inner)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Ice Lake Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Iceberg Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), lambda state: state.can_use_bombs(player)) + set_rule(world.get_entrance('Shopping Mall Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) # entrance rules # Caution: If king's grave is relaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it) @@ -295,11 +353,14 @@ def global_rules(world, player): set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock Ledge', 'Region', player)) # sword required to cast magic (!) if not world.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player)) - set_rule(world.get_entrance('Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + set_rule(world.get_entrance('GT Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + set_rule(world.get_entrance('GT Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + else: + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) # Start of door rando rules # TODO: Do these need to flag off when door rando is off? - some of them, yes @@ -759,8 +820,7 @@ def global_rules(world, player): add_mc_rule('Agahnim 1') add_mc_rule('Agahnim 2') - add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player) and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times if world.goal[player] != 'ganonhunt': add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player)) @@ -789,13 +849,16 @@ def bomb_rules(world, player): bombable_items = ['Chicken House', 'Aginah\'s Cave', 'Graveyard Cave', 'Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left', 'Hype Cave - Bottom'] for location in bonkable_items: - add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + add_bunny_rule(world.get_location(location, player), player) for location in bombable_items: add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player)) + add_bunny_rule(world.get_location(location, player), 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)) + add_bunny_rule(world.get_location(location, player), 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'] @@ -891,7 +954,7 @@ def pot_rules(world, player): (state.has('Cane of Byrna', player) and (state.can_extend_magic(player, 12, True) or (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4))))))) - for l in world.get_region('Dark Desert Hint', player).locations: + for l in world.get_region('Mire Hint', player).locations: if l.type == LocationType.Pot: add_rule(l, lambda state: state.can_use_bombs(player)) for l in world.get_region('Palace of Darkness Hint', player).locations: @@ -938,31 +1001,36 @@ def drop_rules(world, player): def ow_inverted_rules(world, player): if world.mode[player] != 'inverted': set_rule(world.get_entrance('East Death Mountain Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('TR Pegs Teleporter', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) # bunny cannot lift bushes set_rule(world.get_entrance('Castle Gate Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Castle Gate Teleporter (Inner)', player), lambda state: state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('Desert Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Lake Hylia Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Hyrule Castle Main Gate', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Hyrule Castle Main Gate (South)', player), lambda state: state.has_Mirror(player)) set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) - set_rule(world.get_entrance('Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or state.has('Beat Agahnim 2', player)) + set_rule(world.get_entrance('Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or world.goal[player] == 'trinity' or state.has('Beat Agahnim 2', player)) + set_rule(world.get_entrance('Mirror To Bombos Tablet Ledge', player), lambda state: state.has_Mirror(player)) # OWG else: - set_rule(world.get_entrance('East Dark Death Mountain Teleporter (Top)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player) and state.has_Pearl(player)) # bunny cannot use hammer - set_rule(world.get_entrance('East Dark Death Mountain Teleporter (Bottom)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_entrance('East Dark Death Mountain Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('West Dark World Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) set_rule(world.get_entrance('Post Aga Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer - set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Mire Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Ice Lake Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + + set_rule(world.get_entrance('TR Pegs Ledge Drop', player), lambda state: state.has('Hammer', player)) # inverted 1.0 + set_rule(world.get_entrance('Pyramid Exit Ledge Drop', player), lambda state: state.has('Hammer', player)) # inverted 1.0 set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) and (state.has_Pearl(player) or state.has('Beat Agahnim 1', player)) - or (state.can_reach('Light World', 'Region', player) and state.has_Mirror(player))) # Need LW access using Mirror or Portal + or (state.can_reach('Kakariko Suburb Area', 'Region', player) and state.has_Mirror(player))) # Need LW access using Mirror or Portal set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or state.has('Beat Agahnim 2', player)) @@ -972,10 +1040,12 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_location('Zora\'s Ledge', player), player) add_bunny_rule(world.get_location('Maze Race', player), player) add_bunny_rule(world.get_location('Flute Spot', player), player) + add_bunny_rule(world.get_location('Catfish', player), player) # entrances add_bunny_rule(world.get_entrance('Lost Woods Hideout Drop', player), player) add_bunny_rule(world.get_entrance('Lumberjack Tree Tree', player), player) + add_bunny_rule(world.get_entrance('Waterfall of Wishing', player), player) add_bunny_rule(world.get_entrance('Bonk Rock Cave', player), player) add_bunny_rule(world.get_entrance('Sanctuary Grave', player), player) add_bunny_rule(world.get_entrance('Kings Grave', player), player) @@ -999,48 +1069,144 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), player) # terrain + add_bunny_rule(world.get_entrance('Lost Woods Bush (West)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Bush (East)', player), player) add_bunny_rule(world.get_entrance('DM Hammer Bridge (West)', player), player) add_bunny_rule(world.get_entrance('DM Hammer Bridge (East)', player), player) add_bunny_rule(world.get_entrance('DM Broken Bridge (West)', player), player) add_bunny_rule(world.get_entrance('DM Broken Bridge (East)', player), player) - add_bunny_rule(world.get_entrance('Fairy Ascension Rocks', player), player) - add_bunny_rule(world.get_entrance('Death Mountain Entrance Rock', player), player) - add_bunny_rule(world.get_entrance('Waterfall Fairy Access', player), player) - add_bunny_rule(world.get_entrance('Graveyard Ladder (Top)', player), player) - add_bunny_rule(world.get_entrance('Graveyard Ladder (Bottom)', player), player) + add_bunny_rule(world.get_entrance('Fairy Ascension Rocks (Inner)', player), player) + add_bunny_rule(world.get_entrance('Fairy Ascension Rocks (Outer)', player), player) + add_bunny_rule(world.get_entrance('TR Pegs Ledge Entry', player), player) + add_bunny_rule(world.get_entrance('TR Pegs Ledge Leave', player), player) + add_bunny_rule(world.get_entrance('TR Pegs Ledge Drop', player), player) # inverted 1.0 + add_bunny_rule(world.get_entrance('Mountain Pass Rock (Outer)', player), player) + add_bunny_rule(world.get_entrance('Mountain Pass Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Zora Waterfall Water Drop', player), player) + add_bunny_rule(world.get_entrance('Zora Waterfall Water Entry', player), player) + add_bunny_rule(world.get_entrance('Zora Waterfall Approach', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Hammer (North)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Hammer (South)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Rock (South)', player), player) + add_bunny_rule(world.get_entrance('Kakariko Pond Whirlpool', player), player) add_bunny_rule(world.get_entrance('Kings Grave Rocks (Outer)', player), player) add_bunny_rule(world.get_entrance('Kings Grave Rocks (Inner)', player), player) + add_bunny_rule(world.get_entrance('Graveyard Ladder (Top)', player), player) + add_bunny_rule(world.get_entrance('Graveyard Ladder (Bottom)', player), player) + add_bunny_rule(world.get_entrance('River Bend Water Drop', player), player) + add_bunny_rule(world.get_entrance('River Bend East Water Drop', player), player) + add_bunny_rule(world.get_entrance('Potion Shop Water Drop', player), player) + add_bunny_rule(world.get_entrance('Potion Shop Northeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Potion Shop Rock (North)', player), player) add_bunny_rule(world.get_entrance('Potion Shop Rock (South)', player), player) - add_bunny_rule(world.get_entrance('Kakariko Yard Bush (North)', player), player) - add_bunny_rule(world.get_entrance('Kakariko Yard Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Zora Approach Water Drop', player), player) + add_bunny_rule(world.get_entrance('Zora Approach Rocks (West)', player), player) + add_bunny_rule(world.get_entrance('Zora Approach Rocks (East)', player), player) add_bunny_rule(world.get_entrance('Kakariko Southwest Bush (North)', player), player) add_bunny_rule(world.get_entrance('Kakariko Southwest Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Kakariko Yard Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Kakariko Yard Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle Southwest Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle Southwest Bush (South)', player), player) add_bunny_rule(world.get_entrance('Hyrule Castle Courtyard Bush (North)', player), player) add_bunny_rule(world.get_entrance('Hyrule Castle Courtyard Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle East Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle East Rock (Outer)', player), player) add_bunny_rule(world.get_entrance('Wooden Bridge Bush (North)', player), player) add_bunny_rule(world.get_entrance('Wooden Bridge Bush (South)', player), player) - add_bunny_rule(world.get_entrance('Bat Cave Ledge Peg', player), player) - add_bunny_rule(world.get_entrance('Bat Cave Ledge Peg (East)', player), player) + add_bunny_rule(world.get_entrance('Wooden Bridge Water Drop', player), player) + add_bunny_rule(world.get_entrance('Wooden Bridge Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Blacksmith Ledge Peg (West)', player), player) + add_bunny_rule(world.get_entrance('Blacksmith Ledge Peg (East)', player), player) + add_bunny_rule(world.get_entrance('Maze Race Game', player), player) add_bunny_rule(world.get_entrance('Desert Ledge Rocks (Outer)', player), player) add_bunny_rule(world.get_entrance('Desert Ledge Rocks (Inner)', player), player) + add_bunny_rule(world.get_entrance('Flute Boy Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Flute Boy Bush (South)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Water Entry', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Rock (Bottom)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Rock (Top)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Pegs (Outer)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Pegs (Inner)', player), player) + add_bunny_rule(world.get_entrance('Statues Water Entry', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia South Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Central Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Island Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Water D Leave', player), player) + add_bunny_rule(world.get_entrance('Ice Cave Water Drop', player), player) + add_bunny_rule(world.get_entrance('Desert Pass Rocks (North)', player), player) + add_bunny_rule(world.get_entrance('Desert Pass Rocks (South)', player), player) + add_bunny_rule(world.get_entrance('Octoballoon Water Drop', player), player) + add_bunny_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Rock (West)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Rock (East)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Forgotten Bush (West)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Forgotten Bush (East)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) add_bunny_rule(world.get_entrance('East Dark Death Mountain Bushes', player), player) - add_bunny_rule(world.get_entrance('Bumper Cave Entrance Rock', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Ledge Drop', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (West)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (East)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Rock (South)', player), player) add_bunny_rule(world.get_entrance('Dark Graveyard Bush (South)', player), player) add_bunny_rule(world.get_entrance('Dark Graveyard Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Qirn Jump Water Drop', player), player) + add_bunny_rule(world.get_entrance('Qirn Jump East Water Drop', player), player) + add_bunny_rule(world.get_entrance('Dark Witch Water Drop', player), player) + add_bunny_rule(world.get_entrance('Dark Witch Northeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Dark Witch Rock (North)', player), player) add_bunny_rule(world.get_entrance('Dark Witch Rock (South)', player), player) - add_bunny_rule(world.get_entrance('Grassy Lawn Pegs (Bottom)', player), player) - add_bunny_rule(world.get_entrance('Grassy Lawn Pegs (Top)', player), player) - add_bunny_rule(world.get_entrance('Broken Bridge Pass (Bottom)', player), player) - add_bunny_rule(world.get_entrance('Broken Bridge Pass (Top)', player), player) - add_bunny_rule(world.get_entrance('West Dark World Gap', player), player) - add_bunny_rule(world.get_entrance('Peg Area Rocks (Left)', player), player) - add_bunny_rule(world.get_entrance('Peg Area Rocks (Right)', player), player) - add_bunny_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), player) + add_bunny_rule(world.get_entrance('Catfish Approach Water Drop', player), player) + add_bunny_rule(world.get_entrance('Catfish Approach Rocks (West)', player), player) + add_bunny_rule(world.get_entrance('Catfish Approach Rocks (East)', player), player) + add_bunny_rule(world.get_entrance('Bush Yard Pegs (Outer)', player), player) + add_bunny_rule(world.get_entrance('Bush Yard Pegs (Inner)', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Hammer Rock (South)', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Hammer Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Hookshot Gap', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Water Drop', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge West Water Drop', player), player) + add_bunny_rule(world.get_entrance('Peg Area Rocks (West)', player), player) + add_bunny_rule(world.get_entrance('Peg Area Rocks (East)', player), player) + add_bunny_rule(world.get_entrance('Dig Game To Ledge Drop', player), player) + add_bunny_rule(world.get_entrance('Frog Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Frog Rock (Outer)', player), player) + add_bunny_rule(world.get_entrance('Archery Game Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Archery Game Rock (South)', player), player) add_bunny_rule(world.get_entrance('Hammer Bridge Pegs (North)', player), player) add_bunny_rule(world.get_entrance('Hammer Bridge Pegs (South)', player), player) + add_bunny_rule(world.get_entrance('Hammer Bridge Water Drop', player), player) + add_bunny_rule(world.get_entrance('Stumpy Approach Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Stumpy Approach Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Water Entry', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Rock (Bottom)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Rock (Top)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Pegs (Outer)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Pegs (Inner)', player), player) + add_bunny_rule(world.get_entrance('Hype Cave Water Entry', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Iceberg Water Entry', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), player) + add_bunny_rule(world.get_entrance('Shopping Mall Water Drop', player), player) + add_bunny_rule(world.get_entrance('Bomber Corner Water Drop', player), player) + add_bunny_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), player) + + # OWG rules + add_bunny_rule(world.get_entrance('Stone Bridge EC Cliff Water Drop', player), player) + add_bunny_rule(world.get_entrance('Hammer Bridge EC Cliff Water Drop', player), player) if not world.is_atgt_swapped(player): add_bunny_rule(world.get_entrance('Agahnims Tower', player), player) @@ -1051,31 +1217,23 @@ def ow_bunny_rules(world, player): def no_glitches_rules(world, player): - set_rule(world.get_entrance('Light World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Zora Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Potion Shop Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Northeast Light World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Lake Hylia Central Island Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('West Dark World Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('South Dark World Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('East Dark World Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Northeast Dark World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Southeast Dark World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Catfish Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Ice Palace Leave Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('River Bend East Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Potion Shop Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Potion Shop Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Approach Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('C Whirlpool Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Statues Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia South Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Octoballoon Water Drop', player), lambda state: state.has('Flippers', player)) - add_bunny_rule(world.get_entrance('Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Zora Waterfall Water Drop', player), player) - add_bunny_rule(world.get_entrance('Potion Shop Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Lake Hylia Central Island Water Drop', player), player) - add_bunny_rule(world.get_entrance('West Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('South Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('East Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Southeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Catfish Water Drop', player), player) - add_bunny_rule(world.get_entrance('Ice Palace Leave Water Drop', player), player) + set_rule(world.get_entrance('Qirn Jump East Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Witch Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Witch Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Catfish Approach Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark C Whirlpool Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Hype Cave Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Bomber Corner Water Drop', player), lambda state: state.has('Flippers', player)) # todo: move some dungeon rules to no glicthes logic - see these for examples # add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player)) @@ -1084,24 +1242,29 @@ def no_glitches_rules(world, player): # for location in DMs_room_chests: # add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override + set_rule(world.get_entrance('Ice Lake Northeast Pier Hop', player), lambda state: False) forbid_bomb_jump_requirements(world, player) add_conditional_lamps(world, player) def fake_flipper_rules(world, player): - set_rule(world.get_entrance('Light World Water Drop', player), lambda state: True) + set_rule(world.get_entrance('River Bend East Water Drop', player), lambda state: True) set_rule(world.get_entrance('Potion Shop Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Northeast Light World Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Northeast Dark World Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Southeast Dark World Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Catfish Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Potion Shop Northeast Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Zora Approach Water Drop', player), lambda state: True) + set_rule(world.get_entrance('C Whirlpool Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Statues Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Lake Hylia South Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Octoballoon Water Drop', player), lambda state: True) - add_bunny_rule(world.get_entrance('Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Potion Shop Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Southeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Catfish Water Drop', player), player) + set_rule(world.get_entrance('Qirn Jump East Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Dark Witch Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Dark Witch Northeast Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Catfish Approach Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Dark C Whirlpool Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Hype Cave Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Bomber Corner Water Drop', player), lambda state: True) def forbid_bomb_jump_requirements(world, player): @@ -1109,7 +1272,7 @@ def forbid_bomb_jump_requirements(world, player): for location in DMs_room_chests: add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False) - set_rule(world.get_entrance('Ice Island To East Pier', player), lambda state: False) + set_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), lambda state: False) # Light cones in standard depend on which world we actually are in, not which one the location would normally be @@ -1218,7 +1381,7 @@ def swordless_rules(world, player): set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock Ledge', 'Region', player)) # sword not required to use medallion for opening in swordless (!) set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) if world.mode[player] != 'inverted': @@ -1330,8 +1493,6 @@ def standard_rules(world, player): entrance = world.get_portal(portal_name, player).door.entrance set_rule(entrance, lambda state: state.has('Zelda Delivered', player)) set_rule(world.get_entrance('Sanctuary Exit', player), lambda state: state.has('Zelda Delivered', player)) - set_rule(world.get_entrance('Hyrule Castle Ledge Drop', player), lambda state: state.has('Zelda Delivered', player)) - set_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has('Zelda Delivered', player)) # zelda should be saved before agahnim is in play add_rule(world.get_location('Agahnim 1', player), lambda state: state.has('Zelda Delivered', player)) @@ -1381,24 +1542,8 @@ def standard_rules(world, player): set_rule(world.get_location('Zelda Drop Off', player), lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list)) - for location in ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest']: - add_rule(world.get_location(location, player), lambda state: state.has('Zelda Delivered', player)) - - for entrance in ['Blinds Hideout', 'Kings Grave Rocks (Outer)', 'Dam', 'Tavern North', 'Chicken House', - 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave', 'Blacksmiths Hut', - 'Bat Cave Ledge Peg', 'Bat Cave Cave', 'Sick Kids House', 'Wooden Bridge Bush (South)', - 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lumberjack Tree Tree', - 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Light World Water Drop', - 'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)', 'Desert Statue Move', - 'Eastern Palace', 'Master Sword Meadow', 'Sanctuary', 'Sanctuary Grave', - 'Death Mountain Entrance Rock', 'Light World Water Drop', 'East Hyrule Teleporter', - 'South Hyrule Teleporter', 'Kakariko Teleporter', 'Elder House (East)', 'Elder House (West)', - 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', - 'Snitch Lady (West)', 'Tavern (Front)', 'Kakariko Yard Bush (South)', 'Kakariko Southwest Bush (North)', - 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Lake Hylia Shop', - 'Waterfall of Wishing', 'Hyrule Castle Main Gate', '50 Rupee Cave', 'Bonk Fairy (Light)', - 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', - 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Castle Gate Teleporter']: + for entrance in ['Links House SC', 'Links House ES', 'Central Bonk Rocks SW', 'Hyrule Castle WN', 'Hyrule Castle ES', + 'Bonk Fairy (Light)', 'Hyrule Castle Main Gate (South)', 'Hyrule Castle Main Gate (North)', 'Hyrule Castle Ledge Drop']: add_rule(world.get_entrance(entrance, player), lambda state: state.has('Zelda Delivered', player)) # don't allow bombs to get past here before zelda is rescued @@ -1483,8 +1628,8 @@ def set_big_bomb_rules(world, player): 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Misery Mire'] Northern_DW_entrances = ['Brewery', 'C-Shaped House', @@ -1549,7 +1694,7 @@ def set_big_bomb_rules(world, player): 'Desert Palace Entrance (South)', 'Checkerboard Cave'] - set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) + set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Pyramid Area', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) # crossing peg bridge starting from the southern dark world def cross_peg_bridge(state): @@ -1630,7 +1775,7 @@ def set_big_bomb_rules(world, player): # 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes # 2. flute then basic routes # -> (Mire access and M) or Flute) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Dark Desert', 'Region', player) and state.has_Mirror(player)) or state.can_flute(player)) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Mire Area', 'Region', player) and state.has_Mirror(player)) or state.can_flute(player)) and basic_routes(state)) elif bombshop_entrance.name == 'Old Man Cave (West)': # 1. Lift rock then basic_routes # 2. flute then basic_routes @@ -1640,12 +1785,12 @@ def set_big_bomb_rules(world, player): # 1. flute then basic routes # 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge # -> (Flute or (M and P and West Dark World access) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Village of Outcasts Area', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) elif bombshop_entrance.name in Mirror_from_SDW_entrances: # 1. flute then basic routes # 2. (has South dark world access) use existing mirror spot, mirror again off ledge # -> (Flute or (M and South Dark World access) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('South Dark World', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Big Bomb Shop Area', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) elif bombshop_entrance.name == 'Dark Potion Shop': # 1. walk down by lifting rock: needs gloves and pearl` # 2. walk down by hammering peg: needs hammer and pearl @@ -1772,8 +1917,8 @@ def set_inverted_big_bomb_rules(world, player): 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Misery Mire', 'Red Shield Shop'] LW_bush_entrances = ['Bush Covered House', @@ -1784,7 +1929,7 @@ def set_inverted_big_bomb_rules(world, player): 'Spectacle Rock Cave (Bottom)'] set_rule(world.get_entrance('Pyramid Fairy', player), - lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) + lambda state: state.can_reach('Pyramid Area', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) # Key for below abbreviations: # P = pearl @@ -1804,25 +1949,25 @@ def set_inverted_big_bomb_rules(world, player): elif bombshop_entrance.name in Northern_DW_entrances: # You can just fly with the Flute, you can take a long walk with Mitts and Hammer, # or you can leave a Mirror portal nearby and then walk to the castle to Mirror again. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name in Southern_DW_entrances: # This is the same as north DW without the Mitts rock present. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Hammer', player) or state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Hammer', player) or state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name in Isolated_DW_entrances: # There's just no way to escape these places with the bomb and no Flute. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player)) elif bombshop_entrance.name in LW_walkable_entrances: # You can fly with the flute, or leave a mirror portal and walk through the light world - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name in LW_bush_entrances: # These entrances are behind bushes in LW so you need either Pearl or the tools to solve NDW bomb shop locations. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and (state.can_flute(player) or state.has_Pearl(player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)))) elif bombshop_entrance.name == 'Dark World Shop': # This is mostly the same as NDW but the Mirror path requires the Pearl, or using the Hammer - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player) and (state.has_Pearl(player) or state.has('Hammer', player)))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player) and (state.has_Pearl(player) or state.has('Hammer', player)))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': # This is mostly the same as NDW but the Mirror path requires being able to lift a rock. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_lift_rocks(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_lift_rocks(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name == 'Old Man Cave (West)': # The three paths back are Mirror and DW walk, Mirror and Flute, or LW walk and then Mirror. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and ((state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.can_lift_rocks(player) and state.has_Pearl(player)) or state.can_flute(player))) @@ -1904,8 +2049,8 @@ def set_bunny_rules(world, player, inverted): return region.is_dark_world else: return region.is_light_world - - # Is it possible to do bunny pocket here + + # Is it possible to do bunny pocket here def can_bunny_pocket_skull_woods(world, player): return world.get_entrance( "Skull Woods Second Section Door (West)", player @@ -1913,7 +2058,7 @@ def set_bunny_rules(world, player, inverted): world.state.can_reach_from("Skull Woods Forest (West)", "Light World", 1) and world.state.can_reach_from("Light World", "Skull Woods Forest (West)", 1) ) - + def can_bunny_pocket_voo_shop(world, player): return ( world.state.can_reach_from("West Dark World", "Light World", 1) @@ -2021,7 +2166,7 @@ def set_bunny_rules(world, player, inverted): for ent_name in bunny_impassible_doors: bunny_exit = world.get_entrance(ent_name, player) - if is_bunny(bunny_exit.parent_region): + if bunny_exit.connected_region and is_bunny(bunny_exit.parent_region): add_rule(bunny_exit, get_rule_to_add(bunny_exit.parent_region)) for ent_name in bunny_impassible_if_trapped: diff --git a/Utils.py b/Utils.py index c32adbd8..50ab1b84 100644 --- a/Utils.py +++ b/Utils.py @@ -743,6 +743,27 @@ def load_cached_yaml(path_list): return data +class bidict(dict): + def __init__(self, *args, **kwargs): + super(bidict, self).__init__(*args, **kwargs) + self.inverse = {} + for key, value in self.items(): + self.inverse.setdefault(value,[]).append(key) + + def __setitem__(self, key, value): + if key in self: + self.inverse[self[key]].remove(key) + super(bidict, self).__setitem__(key, value) + self.inverse.setdefault(value,[]).append(key) + + def __delitem__(self, key): + value = self[key] + self.inverse.setdefault(value,[]).remove(key) + if value in self.inverse and not self.inverse[value]: + del self.inverse[value] + super(bidict, self).__delitem__(key) + + if __name__ == '__main__': # make_new_base2current() # read_entrance_data(old_rom=sys.argv[1]) diff --git a/source/item/District.py b/source/item/District.py index cdfa7bbf..bd482ba4 100644 --- a/source/item/District.py +++ b/source/item/District.py @@ -75,7 +75,7 @@ def create_district_helper(world, player): 'Fortune Teller (Dark)', 'Dark World Shop', 'Dark Lumberjack Shop', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] - mire_entrances = ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy'] + mire_entrances = ['Misery Mire', 'Mire Shed', 'Mire Hint', 'Mire Fairy'] ddm_entrances = ['Turtle Rock', 'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', 'Hookshot Cave', 'Hookshot Cave Back Entrance', 'Ganons Tower', 'Spike Cave', @@ -150,10 +150,10 @@ def find_reachable_locations(state, player): return check_set -inaccessible_regions_std = {'Desert Palace Mouth', 'Bumper Cave Ledge', 'Skull Woods Forest (West)', +inaccessible_regions_std = {'Desert Mouth', 'Bumper Cave Ledge', 'Skull Woods Forest (West)', 'Dark Death Mountain Ledge', 'Dark Death Mountain Isolated Ledge', 'Dark Death Mountain Floating Island'} -inaccessible_regions_inv = {'Desert Palace Mouth', 'Maze Race Ledge', 'Desert Ledge', - 'Desert Palace Entrance (North) Spot', 'Hyrule Castle Ledge', 'Death Mountain Return Ledge'} +inaccessible_regions_inv = {'Desert Mouth', 'Maze Race Ledge', 'Desert Ledge', + 'Desert Ledge Keep', 'Hyrule Castle Ledge', 'Mountain Pass Ledge'} diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 1d36e43c..2a80d82d 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -77,12 +77,6 @@ def link_entrances_new(world, player): # setup mandatory connections for exit_name, region_name in mandatory_connections: connect_simple(world, exit_name, region_name, player) - if not avail_pool.inverted: - for exit_name, region_name in open_mandatory_connections: - connect_simple(world, exit_name, region_name, player) - else: - for exit_name, region_name in inverted_mandatory_connections: - connect_simple(world, exit_name, region_name, player) connect_custom(avail_pool, world, player) @@ -1435,7 +1429,7 @@ modes = { 'fixed_non_items': { 'special': 'vanilla', 'condition': '', - 'entrances': ['Dark Death Mountain Fairy', 'Dark Desert Fairy', 'Archery Game', + 'entrances': ['Dark Death Mountain Fairy', 'Mire Fairy', 'Archery Game', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Shop', 'East Dark World Hint', 'Kakariko Gamble Game', 'Good Bee Cave', @@ -1456,7 +1450,7 @@ modes = { 'entrances': ['Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint'] + 'Mire Hint'] }, 'item_caves': { # shuffles shops/pottery if they weren't fixed in the last steps @@ -1471,7 +1465,7 @@ modes = { 'Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint', + 'Mire Hint', 'Links House', 'Tavern North'] }, 'old_man_cave': { # have to do old man cave first so lw dungeon don't use up everything @@ -1511,7 +1505,7 @@ modes = { 'fixed_non_items': { 'special': 'vanilla', 'condition': '', - 'entrances': ['Dark Death Mountain Fairy', 'Dark Desert Fairy', 'Archery Game', + 'entrances': ['Dark Death Mountain Fairy', 'Mire Fairy', 'Archery Game', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Shop', 'East Dark World Hint', 'Kakariko Gamble Game', 'Good Bee Cave', @@ -1532,7 +1526,7 @@ modes = { 'entrances': ['Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint'] + 'Mire Hint'] }, 'item_caves': { # shuffles shops/pottery if they weren't fixed in the last steps @@ -1547,7 +1541,7 @@ modes = { 'Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint', + 'Mire Hint', 'Links House', 'Tavern North'] # inverted links house gets substituted } } @@ -1836,7 +1830,7 @@ entrance_map = { single_entrance_map = { 'Mimic Cave': 'Mimic Cave', 'Dark Death Mountain Fairy': 'Dark Death Mountain Healer Fairy', 'Dark Death Mountain Shop': 'Dark Death Mountain Shop', 'Spike Cave': 'Spike Cave', - 'Dark Desert Fairy': 'Dark Desert Healer Fairy', 'Dark Desert Hint': 'Dark Desert Hint', 'Mire Shed': 'Mire Shed', + 'Mire Fairy': 'Mire Healer Fairy', 'Mire Hint': 'Mire Hint', 'Mire Shed': 'Mire Shed', 'Archery Game': 'Archery Game', 'Dark Potion Shop': 'Dark Potion Shop', 'Dark Lumberjack Shop': 'Dark Lumberjack Shop', 'Dark World Shop': 'Village of Outcasts Shop', 'Fortune Teller (Dark)': 'Fortune Teller (Dark)', 'Dark Sanctuary Hint': 'Dark Sanctuary Hint', @@ -1876,12 +1870,12 @@ default_dw = { 'Turtle Rock Ledge Exit (East)', 'Turtle Rock Isolated Ledge Exit', 'Bumper Cave Exit (Top)', 'Bumper Cave Exit (Bottom)', 'Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)', 'Hookshot Cave Front Exit', 'Hookshot Cave Back Exit', 'Ganons Tower Exit', 'Pyramid Exit', 'Bonk Fairy (Dark)', - 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Dark Desert Healer Fairy', + 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Mire Healer Fairy', 'Dark Death Mountain Healer Fairy', 'Dark Death Mountain Shop', 'Pyramid Fairy', 'East Dark World Hint', 'Palace of Darkness Hint', 'Village of Outcasts Shop', 'Dark Lake Hylia Shop', 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', 'Brewery', 'C-Shaped House', 'Chest Game', 'Hammer Peg Cave', - 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Archery Game', 'Mire Shed', 'Dark Desert Hint', + 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Archery Game', 'Mire Shed', 'Mire Hint', 'Spike Cave', 'Skull Back Drop', 'Skull Left Drop', 'Skull Pinball', 'Skull Pot Circle', 'Pyramid' } @@ -1934,10 +1928,10 @@ DW_Entrances = ['Bumper Cave (Bottom)', 'Superbunny Cave (Top)', 'Superbunny Ca 'Turtle Rock Isolated Ledge Entrance', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance', 'Bonk Fairy (Dark)', 'Dark Sanctuary Hint', 'Dark Lake Hylia Fairy', 'C-Shaped House', 'Big Bomb Shop', 'Dark Death Mountain Fairy', 'Dark Lake Hylia Shop', 'Dark World Shop', 'Red Shield Shop', 'Mire Shed', - 'East Dark World Hint', 'Dark Desert Hint', 'Spike Cave', 'Palace of Darkness Hint', + 'East Dark World Hint', 'Mire Hint', 'Spike Cave', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Death Mountain Shop', 'Dark Potion Shop', 'Pyramid Fairy', 'Archery Game', 'Dark Lumberjack Shop', 'Hype Cave', 'Brewery', - 'Dark Lake Hylia Ledge Hint', 'Chest Game', 'Dark Desert Fairy', 'Dark Lake Hylia Ledge Fairy', + 'Dark Lake Hylia Ledge Hint', 'Chest Game', 'Mire Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Hammer Peg Cave', 'Pyramid Entrance', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)', 'Ganons Tower'] @@ -2071,8 +2065,8 @@ Bomb_Shop_Options = [ 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', 'Bonk Fairy (Dark)', 'Brewery', 'C-Shaped House', 'Chest Game', 'Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Shop', - 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Dark Desert Hint', - 'Dark Desert Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', + 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', 'Big Bomb Shop', 'Dark Lake Hylia Shop', 'Bumper Cave (Top)', 'Links House', 'Hyrule Castle Entrance (South)', 'Misery Mire', 'Thieves Town', 'Bumper Cave (Bottom)', 'Swamp Palace', 'Hyrule Castle Secret Entrance Stairs', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', @@ -2093,8 +2087,8 @@ Inverted_Bomb_Shop_Options = [ 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', 'Bonk Fairy (Dark)', 'Brewery', 'C-Shaped House', 'Chest Game', 'Hammer Peg Cave', 'Red Shield Shop', 'Fortune Teller (Dark)', 'Dark World Shop', - 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Dark Desert Hint', - 'Dark Desert Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', + 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', 'Dark Lake Hylia Shop', 'Bumper Cave (Top)', 'Hyrule Castle Entrance (South)', 'Misery Mire', 'Thieves Town', 'Bumper Cave (Bottom)', 'Swamp Palace', 'Hyrule Castle Secret Entrance Stairs', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', @@ -2114,9 +2108,7 @@ Forbidden_Swap_Entrances = {'Old Man Cave (East)', 'Blacksmiths Hut', 'Big Bomb # these are connections that cannot be shuffled and always exist. # They link together separate parts of the world we need to divide into regions -mandatory_connections = [('Links House S&Q', 'Links House'), - - # underworld +mandatory_connections = [# underworld ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), ('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'), @@ -2163,203 +2155,9 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), ('Bumper Cave Bottom to Top', 'Bumper Cave (top)'), ('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'), - ('Ganon Drop', 'Bottom of Pyramid'), - - # water entry - ('Waterfall Fairy Access', 'Zora Waterfall Entryway'), - ('Zora Waterfall Water Drop', 'Lake Hylia Water'), - ('Light World Water Drop', 'Lake Hylia Water'), - ('Potion Shop Water Drop', 'Lake Hylia Water'), - ('Northeast Light World Water Drop', 'Lake Hylia Water'), - ('Lake Hylia Central Island Water Drop', 'Lake Hylia Water'), - - ('West Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Northeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Catfish Water Drop', 'Dark Lake Hylia Water'), - ('East Dark World Water Drop', 'Dark Lake Hylia Water'), - ('South Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Southeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Ice Palace Leave Water Drop', 'Dark Lake Hylia Water'), - - # water exit - ('Light World Pier', 'Light World'), # there are several piers in-game, only one needs to be modeled - ('Potion Shop Pier', 'Potion Shop Area'), - ('Hobo Pier', 'Hobo Bridge'), - ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), - ('Lake Hylia Whirlpool', 'Northeast Light World'), - - ('Northeast Dark World Pier', 'Northeast Dark World'), - ('East Dark World Pier', 'East Dark World'), - ('Southeast Dark World Pier', 'Southeast Dark World'), - - # terrain - ('Master Sword Meadow', 'Master Sword Meadow'), - ('DM Hammer Bridge (West)', 'East Death Mountain (Top)'), - ('DM Hammer Bridge (East)', 'West Death Mountain (Top)'), - ('DM Broken Bridge (West)', 'East Death Mountain (Bottom)'), - ('DM Broken Bridge (East)', 'West Death Mountain (Bottom)'), - ('Fairy Ascension Rocks', 'Fairy Ascension Plateau'), - ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), - ('Zoras Domain', 'Zoras Domain'), - ('Kings Grave Rocks (Outer)', 'Kings Grave Area'), - ('Kings Grave Rocks (Inner)', 'Light World'), - ('Potion Shop Rock (South)', 'Northeast Light World'), - ('Potion Shop Rock (North)', 'Potion Shop Area'), - ('Kakariko Southwest Bush (North)', 'Bomb Hut Area'), - ('Kakariko Southwest Bush (South)', 'Light World'), - ('Kakariko Yard Bush (North)', 'Light World'), - ('Kakariko Yard Bush (South)', 'Bush Covered Lawn'), - ('Hyrule Castle Courtyard Bush (North)', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Secret Entrance Area'), - ('Hyrule Castle Main Gate', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Main Gate (North)', 'Light World'), - ('Wooden Bridge Bush (North)', 'Light World'), - ('Wooden Bridge Bush (South)', 'Potion Shop Area'), - ('Bat Cave Ledge Peg', 'Bat Cave Ledge'), - ('Bat Cave Ledge Peg (East)', 'Light World'), - ('Desert Statue Move', 'Desert Palace Stairs'), - ('Desert Ledge Rocks (Outer)', 'Desert Palace Entrance (North) Spot'), - ('Desert Ledge Rocks (Inner)', 'Desert Ledge'), - - ('Skull Woods Forest', 'Skull Woods Forest'), - ('East Dark Death Mountain Bushes', 'East Dark Death Mountain (Bushes)'), - ('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'), - ('Dark Witch Rock (North)', 'Northeast Dark World'), - ('Dark Witch Rock (South)', 'Catfish Area'), - ('Grassy Lawn Pegs (Top)', 'West Dark World'), - ('Grassy Lawn Pegs (Bottom)', 'Dark Grassy Lawn'), - ('West Dark World Gap', 'West Dark World'), - ('Dark Graveyard Bush (South)', 'Dark Graveyard North'), - ('Dark Graveyard Bush (North)', 'West Dark World'), - ('Broken Bridge Pass (Top)', 'East Dark World'), - ('Broken Bridge Pass (Bottom)', 'Northeast Dark World'), - ('Peg Area Rocks (Left)', 'Hammer Peg Area'), - ('Peg Area Rocks (Right)', 'West Dark World'), - ('Village of Outcasts Heavy Rock', 'West Dark World'), - ('Hammer Bridge Pegs (North)', 'South Dark World'), - ('Hammer Bridge Pegs (South)', 'East Dark World'), - ('Ice Island To East Pier', 'East Dark World'), - - # ledge drops - ('Spectacle Rock Drop', 'West Death Mountain (Top)'), - ('Death Mountain Drop', 'West Death Mountain (Bottom)'), - ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), - ('Fairy Ascension Ledge Access', 'Fairy Ascension Ledge'), - ('East Death Mountain Drop', 'East Death Mountain (Bottom)'), - ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), - ('Fairy Ascension Drop', 'East Death Mountain (Bottom)'), - ('Death Mountain Entrance Drop', 'Light World'), - ('Death Mountain Return Ledge Drop', 'Light World'), - ('Graveyard Ledge Drop', 'Light World'), - ('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Ledge Drop', 'Light World'), - ('Maze Race Ledge Drop', 'Light World'), - ('Desert Ledge Drop', 'Light World'), - ('Desert Palace Mouth Drop', 'Light World'), - ('Checkerboard Ledge Drop', 'Light World'), - ('Desert Teleporter Drop', 'Light World'), - ('Cave 45 Ledge Drop', 'Light World'), - - ('Dark Death Mountain Drop (West)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Drop (East)', 'East Dark Death Mountain (Bottom)'), - ('Floating Island Drop', 'Dark Death Mountain (Top)'), - ('Turtle Rock Drop', 'Dark Death Mountain (Top)'), - ('Bumper Cave Entrance Drop', 'West Dark World'), - ('Bumper Cave Ledge Drop', 'West Dark World'), - ('Pyramid Drop', 'East Dark World'), - ('Village of Outcasts Drop', 'South Dark World'), - ('Dark Desert Drop', 'Dark Desert') + ('Ganon Drop', 'Bottom of Pyramid') ] -open_mandatory_connections = [('Sanctuary S&Q', 'Sanctuary'), - ('Old Man S&Q', 'Old Man House'), - ('Other World S&Q', 'East Dark World'), - - # flute - ('Flute Spot 1', 'West Death Mountain (Bottom)'), - ('Flute Spot 2', 'Potion Shop Area'), - ('Flute Spot 3', 'Light World'), - ('Flute Spot 4', 'Light World'), - ('Flute Spot 5', 'Light World'), - ('Flute Spot 6', 'Desert Teleporter Ledge'), - ('Flute Spot 7', 'Light World'), - ('Flute Spot 8', 'Light World'), - ('LW Flute', 'Flute Sky'), - ('NWLW Flute', 'Flute Sky'), - ('ZLW Flute', 'Flute Sky'), - ('DM Flute', 'Flute Sky'), - ('EDM Flute', 'Flute Sky'), - - # portals - ('Death Mountain Teleporter', 'West Dark Death Mountain (Bottom)'), - ('East Death Mountain Teleporter', 'East Dark Death Mountain (Bottom)'), - ('Turtle Rock Teleporter', 'Turtle Rock (Top)'), - ('Kakariko Teleporter', 'West Dark World'), - ('Castle Gate Teleporter', 'East Dark World'), - ('East Hyrule Teleporter', 'East Dark World'), - ('South Hyrule Teleporter', 'South Dark World'), - ('Desert Teleporter', 'Dark Desert'), - ('Lake Hylia Teleporter', 'Dark Lake Hylia Central Island') - ] - -inverted_mandatory_connections = [('Sanctuary S&Q', 'Dark Sanctuary Hint'), - ('Old Man S&Q', 'West Dark Death Mountain (Bottom)'), - ('Other World S&Q', 'Hyrule Castle Ledge'), - - # flute - ('Flute Spot 1', 'West Dark Death Mountain (Bottom)'), - ('Flute Spot 2', 'Northeast Dark World'), - ('Flute Spot 3', 'West Dark World'), - ('Flute Spot 4', 'South Dark World'), - ('Flute Spot 5', 'East Dark World'), - ('Flute Spot 6', 'Dark Desert Ledge'), - ('Flute Spot 7', 'South Dark World'), - ('Flute Spot 8', 'Southeast Dark World'), - ('DDM Flute', 'Flute Sky'), - ('NEDW Flute', 'Flute Sky'), - ('WDW Flute', 'Flute Sky'), - ('SDW Flute', 'Flute Sky'), - ('EDW Flute', 'Flute Sky'), - ('DD Flute', 'Flute Sky'), - ('DLHL Flute', 'Flute Sky'), - ('EDDM Flute', 'Flute Sky'), - ('Dark Grassy Lawn Flute', 'Flute Sky'), - ('Hammer Peg Area Flute', 'Flute Sky'), - - # modified terrain - ('Spectacle Rock Approach', 'Spectacle Rock'), - ('Spectacle Rock Leave', 'West Death Mountain (Top)'), - ('Floating Island Bridge (West)', 'East Death Mountain (Top)'), - ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), - ('Graveyard Ladder (Top)', 'Light World'), - ('Graveyard Ladder (Bottom)', 'Graveyard Ledge'), - ('Mimic Cave Ledge Access', 'Mimic Cave Ledge'), - ('Mimic Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Checkerboard Ledge Approach', 'Desert Checkerboard Ledge'), - ('Checkerboard Ledge Leave', 'Light World'), - ('Cave 45 Approach', 'Cave 45 Ledge'), - ('Cave 45 Leave', 'Light World'), - ('Lake Hylia Island Pier', 'Lake Hylia Island'), - ('Bombos Tablet Ladder (Top)', 'Light World'), - ('Bombos Tablet Ladder (Bottom)', 'Bombos Tablet Ledge'), - ('Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Ladder (Bottom)', 'Dark Death Mountain (Top)'), - ('Turtle Rock Tail Drop', 'Turtle Rock (Top)'), - ('Ice Palace Approach', 'Dark Lake Hylia Central Island'), - - # portals - ('Dark Death Mountain Teleporter (West)', 'West Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Bottom)', 'East Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Top)', 'East Death Mountain (Top)'), - ('West Dark World Teleporter', 'Light World'), - ('Post Aga Teleporter', 'Light World'), - ('East Dark World Teleporter', 'Light World'), - ('South Dark World Teleporter', 'Light World'), - ('Dark Desert Teleporter', 'Light World'), - ('Dark Lake Hylia Teleporter', 'Lake Hylia Central Island') - ] - # non-shuffled entrance links default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', 'Lost Woods Hideout Drop': 'Lost Woods Hideout (top)', @@ -2485,8 +2283,8 @@ default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', 'Dark Lake Hylia Fairy': 'Dark Lake Hylia Healer Fairy', 'East Dark World Hint': 'East Dark World Hint', 'Mire Shed': 'Mire Shed', - 'Dark Desert Fairy': 'Dark Desert Healer Fairy', - 'Dark Desert Hint': 'Dark Desert Hint', + 'Mire Fairy': 'Mire Healer Fairy', + 'Mire Hint': 'Mire Hint', 'Hype Cave': 'Hype Cave', 'Dark Lake Hylia Shop': 'Dark Lake Hylia Shop', 'Dark Lake Hylia Ledge Fairy': 'Dark Lake Hylia Ledge Healer Fairy', @@ -2544,19 +2342,19 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Desert Palace Entrance (West)', 'Desert West Portal'), ('Desert Palace Entrance (North)', 'Desert Back Portal'), ('Desert Palace Entrance (East)', 'Desert East Portal'), - ('Desert Palace Exit (South)', 'Desert Palace Stairs'), + ('Desert Palace Exit (South)', 'Desert Stairs'), ('Desert Palace Exit (West)', 'Desert Ledge'), - ('Desert Palace Exit (East)', 'Desert Palace Mouth'), - ('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'), + ('Desert Palace Exit (East)', 'Desert Mouth'), + ('Desert Palace Exit (North)', 'Desert Ledge Keep'), ('Eastern Palace', 'Eastern Portal'), - ('Eastern Palace Exit', 'Light World'), + ('Eastern Palace Exit', 'Eastern Palace Area'), ('Tower of Hera', 'Hera Portal'), ('Tower of Hera Exit', 'West Death Mountain (Top)'), ('Palace of Darkness', 'Palace of Darkness Portal'), - ('Palace of Darkness Exit', 'East Dark World'), + ('Palace of Darkness Exit', 'Palace of Darkness Area'), ('Swamp Palace', 'Swamp Portal'), # requires additional patch for flooding moat if moved - ('Swamp Palace Exit', 'South Dark World'), + ('Swamp Palace Exit', 'Swamp Area'), ('Skull Woods First Section Hole (East)', 'Skull Pinball'), ('Skull Woods First Section Hole (West)', 'Skull Left Drop'), ('Skull Woods First Section Hole (North)', 'Skull Pot Circle'), @@ -2570,13 +2368,13 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Skull Woods Final Section', 'Skull 3 Portal'), ('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'), ('Thieves Town', 'Thieves Town Portal'), - ('Thieves Town Exit', 'West Dark World'), + ('Thieves Town Exit', 'Village of Outcasts'), ('Ice Palace', 'Ice Portal'), - ('Ice Palace Exit', 'Dark Lake Hylia Central Island'), + ('Ice Palace Exit', 'Ice Palace Area'), ('Misery Mire', 'Mire Portal'), - ('Misery Mire Exit', 'Dark Desert'), + ('Misery Mire Exit', 'Mire Area'), ('Turtle Rock', 'Turtle Rock Main Portal'), - ('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'), + ('Turtle Rock Exit (Front)', 'Turtle Rock Area'), ('Dark Death Mountain Ledge (West)', 'Turtle Rock Lazy Eyes Portal'), ('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'), ('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'), @@ -2588,23 +2386,23 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle open_default_dungeon_connections = [('Agahnims Tower', 'Agahnims Tower Portal'), ('Agahnims Tower Exit', 'Hyrule Castle Ledge'), ('Ganons Tower', 'Ganons Tower Portal'), - ('Ganons Tower Exit', 'Dark Death Mountain (Top)') + ('Ganons Tower Exit', 'West Dark Death Mountain (Top)') ] inverted_default_dungeon_connections = [('Agahnims Tower', 'Ganons Tower Portal'), - ('Agahnims Tower Exit', 'Dark Death Mountain (Top)'), + ('Agahnims Tower Exit', 'West Dark Death Mountain (Top)'), ('Ganons Tower', 'Agahnims Tower Portal'), ('Ganons Tower Exit', 'Hyrule Castle Ledge') ] indirect_connections = { - 'Turtle Rock (Top)': 'Turtle Rock', - 'East Dark World': 'Pyramid Fairy', + 'Turtle Rock Ledge': 'Turtle Rock', + 'Pyramid Area': 'Pyramid Fairy', 'Big Bomb Shop': 'Pyramid Fairy', - 'Dark Desert': 'Pyramid Fairy', - 'West Dark World': 'Pyramid Fairy', - 'South Dark World': 'Pyramid Fairy', - 'Light World': 'Pyramid Fairy', + 'Mire Area': 'Pyramid Fairy', + #'West Dark World': 'Pyramid Fairy', + 'Big Bomb Shop Area': 'Pyramid Fairy', + #'Light World': 'Pyramid Fairy', 'Old Man Cave (East)': 'Old Man S&Q' } # format: @@ -2745,8 +2543,8 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Dark Potion Shop': (0x6E, (0x010f, 0x56, 0x080e, 0x04f4, 0x0c66, 0x0548, 0x0cd8, 0x0563, 0x0ce3, 0x0a, 0xf6, 0x0000, 0x0000)), 'Archery Game': (0x58, (0x0111, 0x69, 0x069e, 0x0ac4, 0x02ea, 0x0b18, 0x0368, 0x0b33, 0x036f, 0x0a, 0xf6, 0x09AC, 0x0000)), 'Mire Shed': (0x5E, (0x010d, 0x70, 0x0384, 0x0c69, 0x001e, 0x0cb6, 0x0098, 0x0cd6, 0x00a3, 0x07, 0xf9, 0x0000, 0x0000)), - 'Dark Desert Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), - 'Dark Desert Fairy': (0x55, (0x0115, 0x70, 0x03a8, 0x0c6a, 0x013a, 0x0cb7, 0x01b8, 0x0cd7, 0x01bf, 0x06, 0xfa, 0x0000, 0x0000)), + 'Mire Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), + 'Mire Fairy': (0x55, (0x0115, 0x70, 0x03a8, 0x0c6a, 0x013a, 0x0cb7, 0x01b8, 0x0cd7, 0x01bf, 0x06, 0xfa, 0x0000, 0x0000)), 'Spike Cave': (0x40, (0x0117, 0x43, 0x0ed4, 0x01e4, 0x08aa, 0x0236, 0x0928, 0x0253, 0x092f, 0x0a, 0xf6, 0x0000, 0x0000)), 'Dark Death Mountain Shop': (0x6D, (0x0112, 0x45, 0x0ee0, 0x01e3, 0x0d00, 0x0236, 0x0daa, 0x0252, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000)), 'Dark Death Mountain Fairy': (0x6F, (0x0115, 0x43, 0x1400, 0x0294, 0x0600, 0x02e8, 0x0678, 0x0303, 0x0685, 0x0a, 0xf6, 0x0000, 0x0000)), @@ -2832,7 +2630,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Desert Healer Fairy': 0x5E, 'Dark Lake Hylia Healer Fairy': 0x5E, 'Dark Lake Hylia Ledge Healer Fairy': 0x5E, - 'Dark Desert Healer Fairy': 0x5E, + 'Mire Healer Fairy': 0x5E, 'Dark Death Mountain Healer Fairy': 0x5E, 'Fortune Teller (Light)': 0x65, 'Lake Hylia Fortune Teller': 0x65, @@ -2887,7 +2685,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Fortune Teller (Dark)': 0x66, 'Archery Game': 0x59, 'Mire Shed': 0x5F, - 'Dark Desert Hint': 0x62, + 'Mire Hint': 0x62, 'Spike Cave': 0x41, 'Mimic Cave': 0x4F, 'Kakariko Well (top)': 0x80, @@ -3023,8 +2821,8 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d), 'Dark Potion Shop': (0xc80, 0x4c0), 'Archery Game': (0x2f0, 0xaf0), 'Mire Shed': (0x060, 0xc90), - 'Dark Desert Hint': (0x2e0, 0xd00), - 'Dark Desert Fairy': (0x1c0, 0xc90), + 'Mire Hint': (0x2e0, 0xd00), + 'Mire Fairy': (0x1c0, 0xc90), 'Spike Cave': (0x860, 0x180), 'Dark Death Mountain Shop': (0xd80, 0x180), 'Dark Death Mountain Fairy': (0x620, 0x2c0), diff --git a/test/inverted/TestInverted.py b/test/inverted/TestInverted.py index 75c945e6..cc3eb68a 100644 --- a/test/inverted/TestInverted.py +++ b/test/inverted/TestInverted.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import generate_itempool, difficulties from Items import ItemFactory @@ -23,6 +24,7 @@ class TestInverted(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) generate_itempool(self.world, 1) diff --git a/test/inverted_owg/TestInvertedOWG.py b/test/inverted_owg/TestInvertedOWG.py index 2453d7ab..9c584c25 100644 --- a/test/inverted_owg/TestInvertedOWG.py +++ b/test/inverted_owg/TestInvertedOWG.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import generate_itempool, difficulties from Items import ItemFactory @@ -24,6 +25,7 @@ class TestInvertedOWG(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) create_owg_connections(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) diff --git a/test/owg/TestVanillaOWG.py b/test/owg/TestVanillaOWG.py index c5bd18d0..8c245125 100644 --- a/test/owg/TestVanillaOWG.py +++ b/test/owg/TestVanillaOWG.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import difficulties, generate_itempool from Items import ItemFactory @@ -24,6 +25,7 @@ class TestVanillaOWG(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) create_owg_connections(self.world, 1) diff --git a/test/vanilla/TestVanilla.py b/test/vanilla/TestVanilla.py index 80bbc7b9..f1bd23f2 100644 --- a/test/vanilla/TestVanilla.py +++ b/test/vanilla/TestVanilla.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import difficulties, generate_itempool from Items import ItemFactory @@ -23,6 +24,7 @@ class TestVanilla(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) generate_itempool(self.world, 1) From 4246439c6120fad5b17ed79277d6f8dd3bb4c37f Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 17 Jul 2023 00:10:15 -0500 Subject: [PATCH 109/158] Fixed issue with flipper rules not getting pearl rules added --- Rules.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Rules.py b/Rules.py index 317a99c7..136c433d 100644 --- a/Rules.py +++ b/Rules.py @@ -31,18 +31,6 @@ def set_rules(world, player): if world.swords[player] == 'swordless': swordless_rules(world, player) - ow_bunny_rules(world, player) - - if world.mode[player] == 'standard': - standard_rules(world, player) - else: - misc_key_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) elif world.logic[player] == 'minorglitches': @@ -59,6 +47,18 @@ def set_rules(world, player): else: raise NotImplementedError('Not implemented yet') + ow_bunny_rules(world, player) + + if world.mode[player] == 'standard': + standard_rules(world, player) + else: + misc_key_rules(world, player) + + bomb_rules(world, player) + pot_rules(world, player) + drop_rules(world, player) + challenge_room_rules(world, player) + if world.goal[player] == 'dungeons': # require all dungeons to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has('Beat Agahnim 1', player) and state.has('Beat Agahnim 2', player) and state.has_crystals(7, player)) From ce4179abb9d425e1477dad45c6ba42a4064d09a7 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 3 Jan 2024 16:24:35 -0700 Subject: [PATCH 110/158] feat: updated maze race/hobo clips refactor: bunny pocket assumes a connector/dungeon_revive entrance is available, for now fix: mirrorless_moat_rule no longer allows flippers in swamp fix: problems from merge --- EntranceShuffle.py | 2 +- OverworldGlitchRules.py | 148 +++++++++++---------------- Rules.py | 30 ++---- UnderworldGlitchRules.py | 1 + source/overworld/EntranceShuffle2.py | 2 +- test/NewTestSuite.py | 2 +- 6 files changed, 73 insertions(+), 112 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 95cf696f..84caaaf1 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2146,7 +2146,7 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Paradox Cave (Top)', 'Paradox Cave'), ('Paradox Cave Exit (Bottom)', 'East Death Mountain (Bottom)'), ('Paradox Cave Exit (Middle)', 'East Death Mountain (Bottom)'), - ('Paradox Cave Exit (Top)', 'East Death Mountain (Top)'), + ('Paradox Cave Exit (Top)', 'East Death Mountain (Top East)'), ('Waterfall of Wishing', 'Waterfall of Wishing'), ('Fortune Teller (Light)', 'Fortune Teller (Light)'), ('Bonk Rock Cave', 'Bonk Rock Cave'), diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index 7e03b64c..aab2ddda 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -115,81 +115,77 @@ open_non_mandatory_exits = [ # Special Light World region exits that require boots clips. - -inverted_boots_clip_exits_lw = [ - ("Light World DMA Clip Spot", "Light World", "West Death Mountain (Bottom)"), - ("Hera Ascent", "West Death Mountain (Bottom)", "West Death Mountain (Top)"), - ("Death Mountain Return Ledge Clip Spot", "Light World", "Death Mountain Return Ledge"), - ("Death Mountain Entrance Clip Spot", "Light World", "Death Mountain Entrance"), - ("Death Mountain Glitched Bridge", "West Death Mountain (Bottom)", "East Death Mountain (Top)"), - ("Zora Descent Clip Spot", "East Death Mountain (Top)", "Zoras Domain"), - ("Desert Northern Cliffs", "Light World", "Desert Northern Cliffs"), - ("Desert Ledge Dropdown", "Desert Northern Cliffs", "Desert Ledge"), - ("Desert Palace Entrance Dropdown", "Desert Northern Cliffs", "Desert Palace Entrance (North) Spot"), - ("Lake Hylia Island Clip Spot", "Light World", "Lake Hylia Island"), - ("Death Mountain Descent", "West Death Mountain (Bottom)", "Light World"), - ("Kings Grave Clip Spot", "West Death Mountain (Bottom)", "Kings Grave Area"), +boots_clip_exits_lw = [ + ('Lumberjack DMA Clip', 'Lumberjack Area', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Clip', 'West Death Mountain (Top)', 'Spectacle Rock Ledge'), + ('Hera Ascent Clip', 'West Death Mountain (Bottom)', 'West Death Mountain (Top)'), + ('Death Mountain Glitched Bridge Clip', 'West Death Mountain (Bottom)', 'East Death Mountain (Top East)'), + ('Sanctuary DMD Clip', 'West Death Mountain (Bottom)', 'Sanctuary Area'), + ('Graveyard Ledge Clip', 'West Death Mountain (Bottom)', 'Graveyard Ledge'), + ('Kings Grave Clip', 'West Death Mountain (Bottom)', 'Kings Grave Area'), + ('Floating Island Clip', 'East Death Mountain (Top East)', 'Death Mountain Floating Island'), + ('Zora DMD Clip', 'Death Mountain TR Pegs Area', 'Zoras Domain'), + ('TR Pegs Ledge Clip', 'Death Mountain TR Pegs Area', 'Death Mountain TR Pegs Ledge'), + ('Mountain Pass Ledge Clip', 'Mountain Pass Area', 'Mountain Pass Ledge'), + ('Mountain Pass Entry Clip', 'Kakariko Pond Area', 'Mountain Pass Entry'), + ('Bat Cave River Clip', 'Blacksmith Area', 'Blacksmith Ledge'), + ('Desert Keep Clip', 'Maze Race Area', 'Desert Ledge Keep'), + ('Desert Ledge Clip', 'Maze Race Area', 'Desert Ledge'), + ('Maze Race Prize Clip', 'Maze Race Area', 'Maze Race Prize'), + ('Stone Bridge To Cliff Clip', 'Stone Bridge South Area', 'Central Cliffs'), + ('Hobo Clip', 'Stone Bridge South Area', 'Stone Bridge Water'), + ('Bombos Tablet Clip', 'Desert Area', 'Bombos Tablet Ledge'), + ('Desert Teleporter Clip', 'Desert Area', 'Desert Teleporter Ledge'), + ('Cave 45 Clip', 'Flute Boy Approach Area', 'Cave 45 Ledge'), + ('Desert Northern Cliffs Clip', 'Flute Boy Approach Area', 'Desert Northern Cliffs') ] -open_boots_clip_exits_lw = [ - ("Graveyard Ledge Clip Spot", "West Death Mountain (Bottom)", "Graveyard Ledge"), - ("Desert Ledge (Northeast) Dropdown", "Desert Northern Cliffs", "Desert Checkerboard Ledge"), - ("Spectacle Rock Clip Spot", "West Death Mountain (Top)", "Spectacle Rock"), - ("Bombos Tablet Clip Spot", "Light World", "Bombos Tablet Ledge"), - ("Floating Island Clip Spot", "East Death Mountain (Top)", "Death Mountain Floating Island"), - ("Cave 45 Clip Spot", "Light World", "Cave 45 Ledge"), -] + inverted_boots_clip_exits_lw - # Special Dark World region exits that require boots clips. boots_clip_exits_dw = [ - ("Dark World DMA Clip Spot", "West Dark World", "West Dark Death Mountain (Bottom)"), - ("Bumper Cave Ledge Clip Spot", "West Dark World", "Bumper Cave Ledge"), - ("Bumper Cave Entrance Clip Spot", "West Dark World", "Bumper Cave Entrance"), - ("Catfish Descent", "Dark Death Mountain (Top)", "Catfish Area"), - ("Hammer Pegs River Clip Spot", "East Dark World", "Hammer Peg Area"), - ("Dark Lake Hylia Ledge Clip Spot", "East Dark World", "Southeast Dark World"), - ("Dark Desert Cliffs Clip Spot", "South Dark World", "Dark Desert"), - ("DW Floating Island Clip Spot", "East Dark Death Mountain (Bottom)", "Dark Death Mountain Floating Island"), + ('Dark World DMA Clip', 'Dark Lumberjack Area', 'West Dark Death Mountain (Bottom)'), + ('Dark Death Mountain Descent', 'West Dark Death Mountain (Bottom)', 'Dark Chapel Area'), + ('Ganons Tower Ascent', 'West Dark Death Mountain (Bottom)', 'GT Stairs'), # This only gets you to the GT entrance + ('Dark Death Mountain Glitched Bridge', 'West Dark Death Mountain (Bottom)', 'East Dark Death Mountain (Top)'), + ('DW Floating Island Clip', 'East Dark Death Mountain (Bottom)', 'Dark Death Mountain Floating Island'), + ('Turtle Rock (Top) Clip', 'Turtle Rock Area', 'Turtle Rock Ledge'), + ('Catfish DMD', 'Turtle Rock Area', 'Catfish Area'), + ('Bumper Cave Ledge Clip', 'Bumper Cave Area', 'Bumper Cave Ledge'), + ('Bumper Cave Entry Clip', 'Outcast Pond Area', 'Bumper Cave Entry'), + ('Broken Bridge Hammer Rock Skip Clip', 'Qirn Jump East Bank', 'Broken Bridge Area'), + ('Dark Witch Rock Skip Clip', 'Dark Witch Area', 'Dark Witch Northeast'), + ('Hammer Pegs River Clip', 'Dark Dunes Area', 'Hammer Pegs Area'), + ('Hammer Bridge To Cliff Clip', 'Hammer Bridge South Area', 'Dark Central Cliffs'), + ('Mire Cliffs Clip', 'Stumpy Approach Area', 'Mire Northern Cliffs'), + ('Dark Lake Hylia Ledge Clip', 'Darkness Nook Area', 'Shopping Mall Area'), + ('Mire Teleporter Clip', 'Mire Area', 'Mire Teleporter Ledge') ] -open_boots_clip_exits_dw = [ - ("Dark Death Mountain Descent", "West Dark Death Mountain (Bottom)", "West Dark World"), - ("Ganons Tower Ascent", "West Dark Death Mountain (Bottom)", "Dark Death Mountain (Top)"), - ("Dark Death Mountain Glitched Bridge", "West Dark Death Mountain (Bottom)", "Dark Death Mountain (Top)"), - ("Turtle Rock (Top) Clip Spot", "Dark Death Mountain (Top)", "Turtle Rock (Top)"), -] + boots_clip_exits_dw - -inverted_boots_clip_exits_dw = [ - ("Dark Desert Teleporter Clip Spot", "Dark Desert", "Dark Desert Ledge") -] + boots_clip_exits_dw - # Dark World drop-down ledges that require glitched speed. glitched_speed_drops_dw = [ - ("Dark Death Mountain Ledge Clip Spot", "Dark Death Mountain (Top)", "Dark Death Mountain Ledge") + ('Dark Death Mountain Ledge Clip', 'East Dark Death Mountain (Top)', 'Dark Death Mountain Ledge') ] # Out of bounds transitions using the mirror mirror_clip_spots_dw = [ - ("Dark Death Mountain Bunny Descent Mirror Spot", "West Dark Death Mountain (Bottom)", "West Dark World"), + ('Bunny DMD Mirror Spot', 'West Dark Death Mountain (Bottom)', 'Qirn Jump Area'), ( - "Dark Death Mountain Bunny Mirror To East Jump", - "West Dark Death Mountain (Bottom)", - "East Dark Death Mountain (Bottom)", + 'Dark Death Mountain Bunny Mirror To East Jump', + 'West Dark Death Mountain (Bottom)', + 'East Dark Death Mountain (Bottom)', ), - ("Desert East Mirror Clip", "Dark Desert", "Desert Palace Mouth"), + ('Desert East Mirror Clip', 'Mire Area', 'Desert Mouth'), ] # Mirror shenanigans placing a mirror portal with a broken camera -mirror_offset_spots_dw = [("Dark Death Mountain Offset Mirror", "West Dark Death Mountain (Bottom)", "East Dark World")] +mirror_offset_spots_dw = [('Dark Death Mountain Offset Mirror', 'West Dark Death Mountain (Bottom)', 'Pyramid Area')] # Mirror shenanigans placing a mirror portal with a broken camera - mirror_offset_spots_lw = [ - ("Death Mountain Offset Mirror", "West Death Mountain (Bottom)", "Light World"), - ("Death Mountain Uncle Offset Mirror", "West Death Mountain (Bottom)", "Hyrule Castle Secret Entrance Area"), - ("Death Mountain Castle Ledge Offset Mirror", "West Death Mountain (Bottom)", "Hyrule Castle Ledge"), + ('Death Mountain Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Area'), + ('Death Mountain Uncle Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Courtyard Northeast'), + ('Death Mountain Castle Ledge Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Ledge'), ] @@ -199,20 +195,19 @@ def create_owg_connections(world, player): """ if world.mode[player] == "inverted": connections = ( - inverted_boots_clip_exits_dw - + inverted_boots_clip_exits_lw + boots_clip_exits_dw + + boots_clip_exits_lw + glitched_speed_drops_dw + mirror_offset_spots_lw ) else: connections = ( - open_boots_clip_exits_dw - + open_boots_clip_exits_lw + boots_clip_exits_dw + + boots_clip_exits_lw + glitched_speed_drops_dw + mirror_clip_spots_dw + mirror_offset_spots_dw ) - create_no_logic_connections(player, world, connections) @@ -223,13 +218,13 @@ def overworld_glitches_rules(world, player): set_owg_rules( player, world, - inverted_boots_clip_exits_lw if inverted else open_boots_clip_exits_lw, + boots_clip_exits_lw, lambda state: state.can_boots_clip_lw(player), ) set_owg_rules( player, world, - inverted_boots_clip_exits_dw if inverted else open_boots_clip_exits_dw, + boots_clip_exits_dw, lambda state: state.can_boots_clip_dw(player), ) # Glitched speed drops. @@ -239,10 +234,10 @@ def overworld_glitches_rules(world, player): glitched_speed_drops_dw, lambda state: state.can_get_glitched_speed_dw(player), ) - # Dark Death Mountain Ledge Clip Spot also accessible with mirror. + # Dark Death Mountain Ledge Clip also accessible with mirror. if not inverted: add_alternate_rule( - world.get_entrance("Dark Death Mountain Ledge Clip Spot", player), lambda state: state.has_Mirror(player) + world.get_entrance('Dark Death Mountain Ledge Clip', player), lambda state: state.has_Mirror(player) ) # Mirror clip spots. @@ -264,45 +259,26 @@ def overworld_glitches_rules(world, player): # Regions that require the boots and some other stuff. if not inverted: - world.get_entrance("Turtle Rock Teleporter", player).access_rule = lambda state: ( - state.can_boots_clip_lw(player) or state.can_lift_heavy_rocks(player) - ) and state.has("Hammer", player) - add_alternate_rule( - world.get_entrance("Waterfall Fairy Access", player), + world.get_entrance('Zora Waterfall Approach', player), lambda state: state.has_Pearl(player) or state.has_Boots(player), ) # assumes access to Waterwalk ability (boots case) else: - add_alternate_rule(world.get_entrance("Waterfall Fairy Access", player), lambda state: state.has_Pearl(player)) + add_alternate_rule( + world.get_entrance('Zora Waterfall Approach', player), + lambda state: state.has_Pearl(player) + ) - world.get_entrance("Dark Desert Teleporter", player).access_rule = lambda state: ( - state.can_flute(player) or state.can_boots_clip_dw(player) - ) and state.can_lift_heavy_rocks(player) - - add_alternate_rule( - world.get_entrance("Dark Witch Rock (North)", player), lambda state: state.can_boots_clip_dw(player) - ) - add_alternate_rule( - world.get_entrance("Broken Bridge Pass (Top)", player), lambda state: state.can_boots_clip_dw(player) - ) add_alternate_rule( world.get_location("Zora's Ledge", player), lambda state: state.can_boots_clip_lw(player) ) # assumes access to Waterwalk ability - add_alternate_rule( - world.get_location('Maze Race', player), lambda state: state.can_boots_clip_lw(player) - ) - - # This is doable even with bad enemies - add_alternate_rule(world.get_location("Hobo", player), lambda state: state.can_boots_clip_lw(player)) - # Bunny pocket if not inverted: add_alternate_rule(world.get_entrance("Skull Woods Final Section", player), lambda state: state.can_bunny_pocket(player) and state.has("Fire Rod", player)) add_alternate_rule(world.get_entrance("Dark World Shop", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player)) - def add_alternate_rule(entrance, rule): old_rule = entrance.access_rule entrance.access_rule = lambda state: old_rule(state) or rule(state) diff --git a/Rules.py b/Rules.py index 136c433d..d125d765 100644 --- a/Rules.py +++ b/Rules.py @@ -84,6 +84,9 @@ def set_rules(world, player): if not world.swamp_patch_required[player]: add_rule(world.get_entrance('Swamp Lobby Moat', player), lambda state: state.has_Mirror(player)) + if world.logic[player] in ['owglitches', 'hybridglitches']: + overworld_glitches_rules(world, player) + set_bunny_rules(world, player, world.mode[player] == 'inverted') # These rules go here because the overwrite/add to some of the above rules @@ -2050,22 +2053,6 @@ def set_bunny_rules(world, player, inverted): else: return region.is_light_world - # Is it possible to do bunny pocket here - def can_bunny_pocket_skull_woods(world, player): - return world.get_entrance( - "Skull Woods Second Section Door (West)", player - ).connected_region.type == RegionType.Dungeon or ( - world.state.can_reach_from("Skull Woods Forest (West)", "Light World", 1) - and world.state.can_reach_from("Light World", "Skull Woods Forest (West)", 1) - ) - - def can_bunny_pocket_voo_shop(world, player): - return ( - world.state.can_reach_from("West Dark World", "Light World", 1) - and world.state.can_reach_from("Light World", "West Dark World", 1) - ) - - def get_rule_to_add(region, location=None, connecting_entrance=None): # In OWG, a location can potentially be superbunny-mirror accessible or # bunny revival accessible. @@ -2105,10 +2092,8 @@ def set_bunny_rules(world, player, inverted): if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue - # Is this a bunny pocketable entrance? - if entrance.name == 'Skull Woods Final Section' and not can_bunny_pocket_skull_woods(world, player) or \ - entrance.name == 'Dark World Shop' and not can_bunny_pocket_voo_shop(world, player): - continue + # todo - Is this a bunny pocketable entrance? + # Is there an entrance reachable to arm bunny pocket? For now, assume there is if entrance.name in drop_dungeon_entrances: lobby = entrance.connected_region else: @@ -2124,9 +2109,8 @@ def set_bunny_rules(world, player, inverted): elif region.type == RegionType.Cave and new_region.type != RegionType.Cave: if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue - if entrance.name == 'Skull Woods Final Section' and not can_bunny_pocket_skull_woods(world, player) or \ - entrance.name == 'Dark World Shop' and not can_bunny_pocket_voo_shop(world, player): - continue + # todo - Is this a bunny pocketable entrance? + # Is there an entrance reachable to arm bunny pocket? For now, assume there is if region.name in OverworldGlitchRules.sword_required_superbunny_mirror_regions: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance)) elif region.name in OverworldGlitchRules.boots_required_superbunny_mirror_regions: diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 7ddfa792..c01e242f 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -220,6 +220,7 @@ def underworld_glitches_rules(world, player): def mirrorless_moat_rule(state): return ( state.can_reach("Old Man S&Q", "Entrance", player) + and state.has("Flippers", player) and mire_clip(state) and (hera_rule(state) or gt_rule(state)) ) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 2a80d82d..dcaf2daa 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -2194,7 +2194,7 @@ default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', 'Paradox Cave (Top)': 'Paradox Cave', 'Paradox Cave Exit (Bottom)': 'East Death Mountain (Bottom)', 'Paradox Cave Exit (Middle)': 'East Death Mountain (Bottom)', - 'Paradox Cave Exit (Top)': 'East Death Mountain (Top)', + 'Paradox Cave Exit (Top)': 'East Death Mountain (Top East)', 'Waterfall of Wishing': 'Waterfall of Wishing', 'Fortune Teller (Light)': 'Fortune Teller (Light)', 'Bonk Rock Cave': 'Bonk Rock Cave', diff --git a/test/NewTestSuite.py b/test/NewTestSuite.py index aca4e796..8e7b9e1c 100644 --- a/test/NewTestSuite.py +++ b/test/NewTestSuite.py @@ -28,7 +28,7 @@ def main(args=None): def test(test_name: str, command: str, test_file: str): tests[test_name] = [command] - base_command = f"python3.8 DungeonRandomizer.py --suppress_rom --suppress_spoiler" + base_command = f"python3 DungeonRandomizer.py --suppress_rom --suppress_spoiler" def gen_seed(): task_command = base_command + " " + command From ec65999a265607a20fdd18e41885feb81307e19a Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 3 Jan 2024 16:41:59 -0700 Subject: [PATCH 111/158] fix: missing import --- source/overworld/EntranceShuffle2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index dcaf2daa..c6d69bf4 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -3,6 +3,7 @@ import logging import copy from collections import defaultdict +from BaseClasses import RegionType class EntrancePool(object): From d2095b4293d88e2d804373866c30a82e8e59ddd8 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 4 Jan 2024 11:51:32 -0700 Subject: [PATCH 112/158] fix: merge issue --- Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index d125d765..a216f06c 100644 --- a/Rules.py +++ b/Rules.py @@ -823,7 +823,7 @@ def global_rules(world, player): add_mc_rule('Agahnim 1') add_mc_rule('Agahnim 2') - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player) + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times if world.goal[player] != 'ganonhunt': add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player)) From d612709a76f12a78943ca9bb1becc9ffe32d295e Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 5 Jan 2024 15:29:44 -0700 Subject: [PATCH 113/158] chore: bump version --- Main.py | 2 +- RELEASENOTES.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index b12cdf42..0eb96e90 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.0.1' +version_number = '1.4.1.0' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c9446323..9a2d6e1e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -142,6 +142,7 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes * 1.4.1.0v + * World Model Refactor: The overworld has been split up by screen, brings OR and DR a bit closer together in the model sense. A few OWG clips have been rewritten to fit into this new logic better. * Logic: New logic for some bosses on ice * Helmasaur on Ice: Bombs for mask, sword or arrows for 2nd phase * Blind on Ice: Beam sword, Somaria, or Byrna plus magic extension for damage. Red shield or Byrna for protection. @@ -156,7 +157,7 @@ These are now independent of retro mode and have three options: None, Random, an * Hammer or Master sword with 3 magic extensions (Rod spam for elemental heads, non-ideal weapon for last phase) * Trinexx on Ice forbidden in doors seeds until we can model some health requirements. Low health Trinexx still isn't realistically feasible (bascially playing OHKO) * Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert - * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert + * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert * Enemizer Damage Rework: * Shuffled: Actually shuffles the damage groups in the table instead of picking random numbers and reducing for mails from there. Enemies will still be assigned to a damage group randomly. * There will always be at least one group which does no damage. The thief will always be in that group. Ganon always has his own group. From 5ebebb2dae582058f247cb6b87695e4c599200c0 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 5 Jan 2024 17:15:48 -0700 Subject: [PATCH 114/158] fix: moon pearl paths respect blocked doors --- Main.py | 2 +- RELEASENOTES.md | 2 ++ Rules.py | 9 ++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Main.py b/Main.py index 0eb96e90..4ee65203 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.0' +version_number = '1.4.1.1' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9a2d6e1e..e4b70ab6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,8 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.1v + * Logic: Moon pearl logic respects blocked doors * 1.4.1.0v * World Model Refactor: The overworld has been split up by screen, brings OR and DR a bit closer together in the model sense. A few OWG clips have been rewritten to fit into this new logic better. * Logic: New logic for some bosses on ice diff --git a/Rules.py b/Rules.py index a216f06c..eca022d7 100644 --- a/Rules.py +++ b/Rules.py @@ -2076,16 +2076,19 @@ def set_bunny_rules(world, player, inverted): # for each such entrance a new option is added that consist of: # a) being able to reach it, and # b) being able to access all entrances from there to `region` - queue = deque([(region, [], {region})]) + queue = deque([(region, [], {region}, [region])]) seen_sets = set([frozenset({region})]) while queue: - (current, path, seen) = queue.popleft() + (current, path, seen, region_path) = queue.popleft() for entrance in current.entrances: + if entrance.door and entrance.door.blocked: + continue new_region = entrance.parent_region new_seen = seen.union({new_region}) if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_seen in seen_sets: continue new_path = path + [entrance.access_rule] + new_region_path = region_path + [new_region] seen_sets.add(frozenset(new_seen)) if not is_link(new_region): if world.logic[player] in ['owglitches', 'hybridglitches']: @@ -2129,7 +2132,7 @@ def set_bunny_rules(world, player, inverted): continue if is_bunny(new_region): # todo: if not owg or hmg and entrance is in bunny_impassible_doors, then skip this nonsense? - queue.append((new_region, new_path, new_seen)) + queue.append((new_region, new_path, new_seen, new_region_path)) else: # we have reached pure light world, so we have a new possible option possible_options.append(path_to_access_rule(new_path, entrance)) From 971a5c3963bbe53a4134ff406452ac32f98851fc Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 8 Jan 2024 13:21:35 -0700 Subject: [PATCH 115/158] fix: no need for menu map check if ER is vanilla --- Rom.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rom.py b/Rom.py index fd904442..ad9d41f8 100644 --- a/Rom.py +++ b/Rom.py @@ -1250,12 +1250,12 @@ def patch_rom(world, rom, player, team, is_mystery=False): # b - Big Key # a - Small Key # - enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'none' + enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla' rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00) | (0x02 if world.bigkeyshuffle[player] else 0x00) | (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00) - | (0x08 if world.compassshuffle[player] else 0x00) # free roaming items in menu - | (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon + | (0x08 if world.compassshuffle[player] else 0x00) # free roaming items in menu + | (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon # Map reveals reveal_bytes = { From c5789891c0f01b266aed69dc48285d851d751078 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 8 Jan 2024 13:22:56 -0700 Subject: [PATCH 116/158] fix: reorder boss assignment, since GT bottom is more restricted now --- Bosses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bosses.py b/Bosses.py index d42579c8..f60489a2 100644 --- a/Bosses.py +++ b/Bosses.py @@ -223,6 +223,7 @@ def place_bosses(world, player): ['Tower of Hera', None], ['Skull Woods', None], ['Ganons Tower', 'middle'], + ['Ganons Tower', 'bottom'], ['Eastern Palace', None], ['Desert Palace', None], ['Palace of Darkness', None], @@ -231,7 +232,6 @@ def place_bosses(world, player): ['Ice Palace', None], ['Misery Mire', None], ['Turtle Rock', None], - ['Ganons Tower', 'bottom'], ] all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons From cacef076bb0fba51349dc5ddde558a10b160811f Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 8 Jan 2024 13:30:11 -0700 Subject: [PATCH 117/158] fix: vanilla key logic doesn't apply with universal keys --- DoorShuffle.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index bf646a45..b75e4df4 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -262,23 +262,24 @@ def vanilla_key_logic(world, player): world.key_layout[player][builder.name] = key_layout log_key_logic(builder.name, key_layout.key_logic) # special adjustments for vanilla - if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none': - # adjust hc doors - def adjust_hc_door(door_rule): - if door_rule.new_rules[KeyRuleType.WorstCase] == 3: - door_rule.new_rules[KeyRuleType.WorstCase] = 2 - door_rule.small_key_num = 2 + if world.keyshuffle[player] != 'universal': + if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none' : + # adjust hc doors + def adjust_hc_door(door_rule): + if door_rule.new_rules[KeyRuleType.WorstCase] == 3: + door_rule.new_rules[KeyRuleType.WorstCase] = 2 + door_rule.small_key_num = 2 - rules = world.key_logic[player]['Hyrule Castle'].door_rules - adjust_hc_door(rules['Sewers Secret Room Key Door S']) - adjust_hc_door(rules['Hyrule Dungeon Map Room Key Door S']) - adjust_hc_door(rules['Sewers Dark Cross Key Door N']) - # adjust pod front door - pod_front = world.key_logic[player]['Palace of Darkness'].door_rules['PoD Middle Cage N'] - if pod_front.new_rules[KeyRuleType.WorstCase] == 6: - pod_front.new_rules[KeyRuleType.WorstCase] = 1 - pod_front.small_key_num = 1 - # gt logic? I'm unsure it needs adjusting + rules = world.key_logic[player]['Hyrule Castle'].door_rules + adjust_hc_door(rules['Sewers Secret Room Key Door S']) + adjust_hc_door(rules['Hyrule Dungeon Map Room Key Door S']) + adjust_hc_door(rules['Sewers Dark Cross Key Door N']) + # adjust pod front door + pod_front = world.key_logic[player]['Palace of Darkness'].door_rules['PoD Middle Cage N'] + if pod_front.new_rules[KeyRuleType.WorstCase] == 6: + pod_front.new_rules[KeyRuleType.WorstCase] = 1 + pod_front.small_key_num = 1 + # gt logic? I'm unsure it needs adjusting def validate_vanilla_reservation(dungeon, world, player): From da80cb9d033a72fbe88a99394851ad589a48d538 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 16 Jan 2024 16:07:52 -0700 Subject: [PATCH 118/158] fix: cap fairy in different item pools should use the bee trap id --- Rom.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index ad9d41f8..fa3fed6d 100644 --- a/Rom.py +++ b/Rom.py @@ -5,6 +5,8 @@ import json import hashlib import logging import os + +import Items import RaceRandom as random import struct import sys @@ -1484,8 +1486,8 @@ def write_custom_shops(rom, world, player): loc_item = ItemFactory(item['item'], player) if (not world.shopsanity[player] and shop.region.name == 'Capacity Upgrade' and world.difficulty[player] != 'normal'): - # really should be 5A instead of B0 -- surprise!!! - item_id, price, replace, replace_price, item_max = 0xB0, [0, 0], 0xFF, [0, 0], 1 + # it's a BeeTrap -- surprise!!! + item_id, price, replace, replace_price, item_max = Items.item_table['Bee Trap'][3], [0, 0], 0xFF, [0, 0], 1 else: item_id = loc_item.code price = int16_as_bytes(item['price']) From 87f072e0701d875adb52c413482074f6df9ec1bf Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 16 Jan 2024 16:08:17 -0700 Subject: [PATCH 119/158] fix: possible generation issue for old man cave in swapped ER --- source/overworld/EntranceShuffle2.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index c6d69bf4..c15115b6 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1001,6 +1001,15 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): if not avail.swapped or (combine_map[exit] not in candidate and not any(e for e in must_exit if combine_map[e] in candidate)): #maybe someday allow these, but we need to disallow mutual locks in Swapped candidates.append(candidate) cave = random.choice(candidates) + + if avail.swapped and len(candidates) > 1 and not avail.inverted: + DM_Connector_Prefixes = ['Spectacle Rock Cave', 'Old Man House', 'Death Mountain Return'] + if any(p for p in DM_Connector_Prefixes if p in cave[0]): # if chosen cave is a DM connector + remain = [p for p in DM_Connector_Prefixes if len([e for e in entrances if p in e]) > 0] # gets remaining DM caves left in pool + if len(remain) == 1: # guarantee that old man rescue cave can still be placed + candidates.remove(cave) + cave = random.choice(candidates) + if cave is None: raise RuntimeError('No more caves left. Should not happen!') From be8a3a6fa7c6c5d6d47b71b5da9da0d77f1be9c2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 16 Jan 2024 16:13:38 -0700 Subject: [PATCH 120/158] fix: ban problematic roller --- source/enemizer/enemy_deny.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 8983ed66..bea935bf 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -244,6 +244,7 @@ UwGeneralDeny: - [ 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" + - [0x0099, 8, ["RollerHorizontalLeft", "RollerHorizontalRight"]] - [ 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, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] From 8d841b025279c782591d66044318d595a30c0ec5 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 17 Jan 2024 09:12:01 -0700 Subject: [PATCH 121/158] feat: rom performance chore: bump version and release notes --- Main.py | 2 +- RELEASENOTES.md | 8 ++++++++ Rom.py | 2 +- data/base2current.bps | Bin 117530 -> 117539 bytes 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index 4ee65203..4b47c02c 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.1' +version_number = '1.4.1.2' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e4b70ab6..3f7d2f0f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,14 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.2v + * Expert/Hard Item Pool: Capacity fairy no longer gives out free crystal + * Vanilla door + Universal Keys: Generation fixed + * Boss Shuffle: Generation fixed (thanks Codemann for easy solution) + * Vanilla ER: No need for ability to check prizes on keysanity menu + * Swapped ER: Possible generation issue fixed (thanks Codemann) + * Enemizer: Roller ban + * Performance: Faster text boxes. Thanks Kan! * 1.4.1.1v * Logic: Moon pearl logic respects blocked doors * 1.4.1.0v diff --git a/Rom.py b/Rom.py index fa3fed6d..816587ff 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '3640741f6e51a98a0d2962f6bc03636a' +RANDOMIZERBASEHASH = '80edd5ce3d17003614a11eb6b68ae848' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 89a5327e66be132ab6071f7ca07a42a72efe78f2..da6b37aa2f081b728baec7ae139b358d30b9fc22 100644 GIT binary patch delta 75 zcmV-R0JQ&_mItGj2cQE4p;TV0vjhXIKM`Pnfe46^fFhXHq)32)oH&NFx%mp2)8k!G%gq*VtS<-761SM From c8386122b75cba6c6f6f4ec83c7ff96b5887cf5c Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 17 Jan 2024 16:24:40 -0700 Subject: [PATCH 122/158] fix: restore correct enemizer flags --- Main.py | 2 +- RELEASENOTES.md | 3 +++ Rom.py | 2 +- data/base2current.bps | Bin 117539 -> 117538 bytes 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index 4b47c02c..bb231ae4 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.2' +version_number = '1.4.1.3' version_branch = '-v' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3f7d2f0f..cc5a0dc9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.3v + * Enemizer: Raven/Murderdactyls using the correct damage table + * Enemzier: Boss drops only center when boss shuffle is on * 1.4.1.2v * Expert/Hard Item Pool: Capacity fairy no longer gives out free crystal * Vanilla door + Universal Keys: Generation fixed diff --git a/Rom.py b/Rom.py index 816587ff..ea11e06b 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '80edd5ce3d17003614a11eb6b68ae848' +RANDOMIZERBASEHASH = 'd63715be60a548e080dc84ae588307e5' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index da6b37aa2f081b728baec7ae139b358d30b9fc22..22fd6589399deb99ffbc392c629cf2176cc677f1 100644 GIT binary patch delta 1255 zcmWNPeN2;g5XXPl0;QBJf zE%5AG&J7;_tDRaapMRVW%~t9b2T;Xa6C`plFBk(Z^a(%oh8I>|@FWkK80X3~cxV;K zA^-}lijX8XVRhrpr>#&Lc9toN7=edORAekXwH8KR^nj}dr2=j-G0J%O*1BIAkq%>w zPO}|;wf>>G%!3GP!_hDouvvSL_3|LIZb#L?jw*;&dA2KBuV)Fgnzn4|DQq5s7~k@` zS_QE$TNK;97ln4oqU){C@!k*wgB<6FK$Z}G@@6k-`F?v1^$2S>`K3+tuPJHnB}BdZ zH%hXXij;Sl1fX3+?;qM5uMe7p@-$8N^`WFw8Eabef+uF{vN?x# zeuj_7;DDec#+PQ%9NgxBWQi=ukD5-D-?onkJtbwZuX05yxpm zS(UL(MQVu~hF9j(&TVKJhgFb_zl=i^Nb&7Bd(NOSG+m_t&-HD*B1xb6)cYpSdc7WEau(;S={kRe|np+gtf3N zyU0FY>Ut9Nw<+bAlxjmdlJe#)@myNZvP_Mso=|fUUbq9f0lA+j=$Jj|&0cp5(64nVl*Uabw$p&i(HcoPvc1N!%2oUZ#IH(&Y!`34h zGXd11v!W5%!3NoM&o!popb-9Q$n~x`LTO|LHHwa$-m&I$f*WeMB3CVcILXz<20cOhxhhSEW84Wpr# zt1tANGZ*iQrTVwrw{U0T`Riwy!^@eBiA1`5%VA_;vsQ delta 1238 zcmWNPYfO`86vv-K$j4dL;tXF*m%EIhgkz11|LP_XNRTR*%RD5708xQJL`A z>MdRgps>oKQ@r@6G`C;2LZyUZs#cD`V@A4a9lWp>ue$0B{RX)VhL{BTMzC1RpWHs(SIk@{ItjQ5Dp*2l6%fi*Fw$);ax?xBN zRyCfH5l5g~;0SW_9YVL~ozL;XFhoK$J|6~g(u%Kc_mGzF3md4f#Ap^}j`y!tp6el` zWZy585nV+6vx(&pz78N#_g$|UCz?#g4BsuL@c82@S z&-u*Goq0t13OPj^BM8S5Wfr}hCu4W= z0}wbUz6(153bAUG8?y(`k3wXG($}AMx=9_elP3hfHEQOCv%=ZlDHGt>D3l8_6!#69 z0iBk-C)X6X!43&taRPs8uD}XAoB|R4VFx*6;Zg@&lGbc}(_OaYtZ`xQtKwJ1y~-tL z8AaSfOqV{?XOnE=ykI1hA7W86x6ux|`jh>}12!70ww8az)ot5Yon z)btt7jX}Bh;e=qih34TlC!`9OM=Yi0`s24IE)3=$xn+sMi%u9%J5=$aiyv~12~x#U zoz4}Y^re_{^zZKTOm>xks^ylz^QPh!OF^@_-@L5ZC=|vVj5*jlWZ8smE(r6I)O|ut z1$f;BDJYn}^OcxqKBy-Qfa4zMiiW?wCKK8tv6PN@l@ z>gbM?UGoF$xwMI8nFezcp_ctqbkx(Fey4;o3gk^eY4Cc*u->1DiL=#6yvdhDZ`pv=Q;-kY z=$L|al3LF&sx2ifPrb1ylse<3Iz!rtY4su_wd-A^k(iHQ&@_CpGeIVz&I`84J1xR% zy{(MAB$OIXCTT8{ED}Z{iG5eT)|?T;xp$GgA&<#Cv1f6F$1DA4drJKc<2zDMUjNgc z9{#(jl+#QH@#!>#dQ~QE=Zf&ZX;=e+7=Is9wx(AOsT&v-?W_N~`;w)6?>ee~&piu^ zk}u!9$S4CtXiJ<8^S)BQeE=Wn21S{=CPM7h6#*1Uc>wK-t!V5x;4a0h_o3Ry_2=g5 I-0E)g|J1AT)&Kwi From ba6f9e049322e99fd38e80ee25e6d70ff5e4a4be Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 25 Jan 2024 11:02:50 -0700 Subject: [PATCH 123/158] feat: Documentation update --- README.md | 264 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 149 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index cac13120..fd71ed19 100644 --- a/README.md +++ b/README.md @@ -15,29 +15,34 @@ See https://alttpr.com/ for more details on the normal randomizer. 4. [Door Type Shuffle](#door-type-shuffle) 5. [Trap Door Removal](#trap-door-removal) 6. [Key Logic Algorithm](#key-logic-algorithm) + 7. [Dungeon Items](#dungeon-items) 7. [Decouple Doors](#decouple-doors) - 8. [Pottery](#pottery) - 9. [Small Key Shuffle](#small-key-shuffle) - 10. [Shuffle Enemy Key Drops](#shuffle-enemy-key-drops) - 11. [Experimental Features](#experimental-features) - 12. [Crossed Dungeon Specific Settings](#crossed-dungeon-specific-settings) - 2. [Item Randomization Changes](#item-randomization) + 1. [Allow Self-Looping Spiral Stairs](#allow-self-looping-spiral-stairs) + 8. [Experimental Features](#experimental-features) + 9. [Crossed Dungeon Specific Settings](#crossed-dungeon-specific-settings) + 2. [Pool Expansions](#pool-expansions) + 1. [Pottery](#pottery) + 2. [Small Key Shuffle](#small-key-shuffle) + 3. [Shuffle Enemy Drops](#shuffle-enemy-drops) + 4. [Take Any Caves](#take-any-caves) + 3. [Item Randomization Changes](#item-randomization) 1. [New "Items"](#new-items) 2. [Shopsanity](#shopsanity) 3. [Logic Level](#logic-level) 4. [Goal](#goal) 5. [Item Sorting](#item-sorting) 6. [Forbidden Boss Items](#forbidden-boss-items) - 3. [Customizer](#customizer) - 4. [Entrance Randomization](#entrance-randomization) + 4. [Customizer](#customizer) + 5. [Entrance Randomization](#entrance-randomization) 1. [Shuffle Links House](#shuffle-links-house) 2. [Overworld Map](#overworld-map) - 5. [Enemizer](#enemizer) - 6. [Retro Changes](#retro-changes) - 7. [Standard Changes](#standard-changes) - 8. [Game Options](#game-options) - 9. [Generation Setup & Miscellaneous](#generation-setup--miscellaneous) - 10. [Glitched Logic](#glitched-logic) + 6. [Enemizer](#enemizer) + 7. [Glitched Logic](#glitched-logic) + 8. [Retro Changes](#retro-changes) + 8. [Standard Changes](#standard-changes) + 9. [Game Options](#game-options) + 10. [Generation Setup & Miscellaneous](#generation-setup--miscellaneous) + ## Setup and Installation @@ -128,13 +133,6 @@ CLI: `--doorShuffle [vanilla|basic|partitioned|crossed]` * Level 2 - Same as Level 1 plus open edges and both types of straight staircases are shuffled. * Level 3 - Same as Level 2 plus Dungeon Lobbies are shuffled -### Key Drop Shuffle (Legacy) (--keydropshuffle) - -Adds 33 new locations to the randomization pool. The 32 small keys found under pots and dropped by enemies and the Big -Key drop location are added to the pool. The keys normally found there are added to the item pool. Retro adds -32 generic keys to the pool instead. This has been can be controlled more granularly with the [Pottery](#pottery) and -[Shuffle Enemy Key Drops](#shuffle-enemy-key-drops) - ### Door Type Shuffle Four options here, and all of them only take effect if Dungeon Door Shuffle is not vanilla: @@ -161,11 +159,23 @@ In all cases, that the trap door near the mire cutscene chest (Mire Warping Pool CLI: `--trap_door_mode [vanilla|optional|boss|oneway]` +### Dungeon Items + +#### Small Keys + +* In Dungeon: Small keys are restricted to their own dungeon +* Randomized: Small keys to dungeon can be found anywhere +* Universal: Small keys are not specific to their own dungeon, can be found anywhere, and there will be at least one shop that sells keys. Hearkens back to the original Legend of Zelda. + +CLI: `--keyshuffle [none|wild|universal]` + +All other dungeon items can be restricted to their own dungeon or shuffled in the general pool. This includes maps, compasses and Big Keys. + ### Key Logic Algorithm Determines how small key door logic works. -* Default: Current key logic. Assumes worse case usage, placement checks, but assumes you can't get to a chest until you have sufficient keys. (May assume items are unreachable) +* ~~Default: Current key logic. Assumes worse case usage, placement checks, but assumes you can't get to a chest until you have sufficient keys. (May assume items are unreachable)~~ (Not recommended to use this setting) * Partial Protection: Assumes you always have full inventory and worse case usage. This should account for dark room and bunny revival glitches. * Strict: For those would like to glitch and be protected from yourselves. Small keys door require all small keys to be available to be in logic. @@ -177,12 +187,47 @@ This is similar to insanity mode in ER where door entrances and exits are not pa CLI: `--decoupledoors` -### Self-Looping Spiral Stairs +### Allow Self-Looping Spiral Stairs If enabled, spiral stairs are allowed to lead to themselves. CLI: `--door_self_loops` +### Experimental Features + +You will start as a bunny if your spawn point is in the dark world. CLI: `--experimental` + +### Crossed Dungeon Specific Settings + +#### Dungeon Chest Counters + +* Auto - picks an appropriate setting based on other settings. Generally will be pickup if the number of item in a dungeon changes. +* On - Dungeon counters on hud always displayed +* Off - Dungeon counters on hud never displayer +* On Compass Pickup - Dungeons with a compass item will display the counter once the compass is found. Dungeons without a compass item will display always unless the number of items in the dungeon is completely vanilla. + +CLI: `--dungeon_counters` from `auto, on, off, pickup` + +#### Mixed Travel (--mixed_travel value) + +Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump, two sections of a supertile that are +otherwise unconnected logically can be reached using these glitches. To prevent the player from unintentionally changing +dungeons while doing these tricks, you may use one of the following options: + +* Prevent (default): + Rails are added the 3 spots to prevent these tricks. This setting is recommend for those learning crossed dungeon mode to learn what is dangerous and what is not. No logic seeds ignore this setting. +* Allow: The rooms are left alone and it is up to the discretion of the player whether to use these tricks or not. +* Force: The two disjointed sections are forced to be in the same dungeon but the glitches are never logically required to complete that game. + +#### Standardize Palettes (--standardize_palettes) +No effect if door shuffle is not on crossed + +* Standardize (default): Rooms in the same dungeon have their palettes changed to match. Hyrule Castle is split between Sewer and HC palette. + Rooms adjacent to sanctuary get their coloring to match the Sanctuary's original palette. +* Original: Rooms/supertiles keep their original palettes. + +## Pool Expansions + ### Pottery New pottery option that control which pots (and large blocks) are in the locations pool: @@ -214,55 +259,30 @@ CLI `--colorizepots` This continues to works the same by shuffling all pots on a supertile. It works with the lottery option as well to move the switches to any valid pot on the supertile regardless of the pots chosen in the pottery mode. This may increase the number of pot locations slightly depending on the mode. -### Small Key Shuffle +### Shuffle Enemy Drops -There are three options now available: +Option that controls whether enemies that drop items are randomized or not. -* In Dungeon: The small key will be in their own dungeon -* Randomized: Small keys can be shuffled outside their own dungeon -* Universal: Retro keys without the other options +* None: Special enemies drop keys normally +* Keys: Enemies that drop keys are added to the randomization pool. Includes the Hyrule Castle Big Key. Universal adds + generic keys to the pool instead. +* Underworld: Enemies in the underworld are added to the randomization pool. Vanilla drops from the various drop tables are added to the item pool. In caves and in dungeons while you have the compass, a blue square will indicate if there are any enemies on the supertile that still have an available drop in the dungeon. Certain enemies do have logical requirements. In particular, the Red Bari requires Fire Rod or Bombos to collect it's drop. More information in hte [Enemizer](#enemizer) section. -CLI: `--keyshuffle [none|wild|universal]` +CLI: `--dropshuffle [none|keys|underworld]` -### Shuffle Enemy Key Drops +### Enable Key Drop Shuffle (Legacy) -Enemies that drop keys can have their drop shuffled into the pool. This is the one part of the keydropshuffle option. -See the pottery option for more options involving pots. +This sets Pottery to "Key Pots" and Shuffle Enemy Drop to "Keys" unless those option have already been changed. -CLI: `--dropshuffle` +### Take Any Caves -### Experimental Features +These are now independent of retro mode and have three options: None, Random, and Fixed. None disables the caves. Random works as take-any caves did before. Fixed means that the take any caves replace specific fairy caves in the pool and will be at those entrances unless ER is turned on (then they can be shuffled wherever). The fixed entrances are: -You will start as a bunny if your spawn point is in the dark world. CLI: `--experimental` - -### Crossed Dungeon Specific Settings - -#### Dungeon Chest Counters - -* Auto - picks an appropriate setting based on other settings. Generally will be pickup if the number of item in a dungeon changes. -* On - Dungeon counters on hud always displayed -* Off - Dungeon counters on hud never displayer -* On Compass Pickup - Dungeons with a compass item will display the counter once the compass is found. Dungeons without a compass item will display always unless the number of items in the dungeon is completely vanilla. - -CLI: `--dungeon_counters` from `auto, on, off, pickup` - -#### Mixed Travel (--mixed_travel value) - -Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump, two sections of a supertile that are -otherwise unconnected logically can be reached using these glitches. To prevent the player from unintentionally changing -dungeons while doing these tricks, you may use one of the following options: - -* Prevent (default): -Rails are added the 3 spots to prevent these tricks. This setting is recommend for those learning crossed dungeon mode to learn what is dangerous and what is not. No logic seeds ignore this setting. -* Allow: The rooms are left alone and it is up to the discretion of the player whether to use these tricks or not. -* Force: The two disjointed sections are forced to be in the same dungeon but the glitches are never logically required to complete that game. - -#### Standardize Palettes (--standardize_palettes) -No effect if door shuffle is not on crossed - -* Standardize (default): Rooms in the same dungeon have their palettes changed to match. Hyrule Castle is split between Sewer and HC palette. -Rooms adjacent to sanctuary get their coloring to match the Sanctuary's original palette. -* Original: Rooms/supertiles keep their original palettes. +* Desert Healer Fairy +* Swamp Healer Fairy (aka Light Hype Cave) +* Dark Death Mountain Healer Fairy +* Dark Lake Hylia Ledge Healer Fairy (aka Shopping Mall Bomb) +* Bonk Fairy (Dark) ## Item Randomization @@ -400,12 +420,6 @@ Arrow Capacity upgrades are now replaced by Rupees wherever it might end up. The Ten Arrows and 5 randomly selected Small Hearts or Blue Shields are replaced by the quiver item (represented by the Single Arrow in game.) 5 Red Potion refills are replaced by the Universal small key. It is assured that at least one shop sells Universal Small Keys. The quiver may thus not be found in shops. The quiver and small keys retain their original base price, but may be discounted. -### Logic Level - -Overworld Glitches is now supported. - -CLI: `--logic owglitches` - ### Goal New supported goals: @@ -499,7 +513,9 @@ Please see [Customizer documentation](docs/Customizer.md) on how to create custo ### New Modes -Lite and Lean ER is now available when Experimental Features are turned on. (todo: bring over documenation of these modes) +* Lite: Non item entrances are vanilla. +* Lean +* Swapped: Entrances are swapped with each other ### Shuffle Links House @@ -523,16 +539,42 @@ If you do not shuffle the compass or map outside of the dungeon, the non-shuffle CLI ```--overworld_map [default|compass|map]``` - ## Enemizer +Enemizer has been incorporated into the generator adn no longer requires an external program. However, there are differences. + +A document hightlighting the major changes: [Enemizer in DR](https://docs.google.com/document/d/1iwY7Gy50DR3SsdXVaLFIbx4xRBqo9a-e1_jAl5LMCX8/edit?usp=sharing) + +### Enemy Shuffle + +Shuffling enemies is different as there are places that certain enemies are not allowed to go. This is known as the enemy ban list, and is updated regularly as report of poor enemy placement occurs. Poor enemy placement included Bumpers, Statues, Beamos or other enemies that block your path with or without certain items. Other disallowed placements include unavoidable damage and glitches. Thieves are unkillable, but restricted to the overworld and even then, are banned from narrow locations. + +### Enemy Damage + +The shuffled setting actually shuffled the damage table unlike the process from the enemizer that simply randomizes values and lowers damage for armor upgrades. + ### Boss Shuffle: Unique -At least one boss each of the prize bosses will be present guarding the prizes. GT bosses can be anything. +Same as before, with some exceptions: -### Blind Note +* Trinexx is not allowed on the GT basement when DR is enabled to avoid certain low percentage kills. Future work on health logic should make this reasonable. +* In general, some of the harder bosses in the GT basement now have some concessions in the logic to make low precentage requirements less likely and onerous. -If bosses are shuffled and Blind is chosen to be the boss of Thieves Town, then bombing the attic and delivering the maiden is still required. +New variant Unique: At least one boss each of the prize bosses will be present guarding the prizes. GT bosses can be anything. + +Blind Note: If bosses are shuffled and Blind is chosen to be the boss of Thieves Town, then bombing the attic and delivering the maiden is still required. + +### Enemy Health + +Same as beofre but health is taken into account for challenge rooms if magic or ammo is required + +### Enemy Logic + +This version of the enemizer can account for logical access to both challenge room and item or key drops when enemies are shuffled. (See [Enemy Drop Shuffle](#shuffle-enemy-drops)). The enemies with particularly special logic are: Red Mimics, Red Eyegores, Terrorpins, Deadrocks, & Lynels. When bombbag is enabled Stalfos Knights, and Buzzblobs have advanced logic. For enemy drops, in particular, Red Baris must be killed with either the Fire Rod or Bombos to acquire the drop. These are in effect unless a different option is chosen: + +* Forbid special enemies: These special enemies are disallowed from drops and challenge rooms. +* Item drops may have special enemies: Challenge rooms will not have these special enemies, but item drops may. +* Allow special enemies anywhere: Both challenge rooms and enemy drops can have these special requirements occur. ## Standard Changes @@ -540,19 +582,42 @@ When dungeon door shuffle is on, the Sanctuary is guaranteed to be behind the Th ## Retro Changes -Retro can be partially enabled: see Small Key Shuffle and Bow Mode. Retro checkbox or Retro mode still enable all 3 options. +Retro can be partially enabled: see Small Key Shuffle, Bow Mode, and Take Any Caves. Enable Retro button enables all 3 options. -New supported option: +## Glitched Logic -### Take Any Caves +Overworld glitches, Hybrid Major Glitches (HMG) and No Logic are currently supported. -These are now independent of retro mode and have three options: None, Random, and Fixed. None disables the caves. Random works as take-any caves did before. Fixed means that the take any caves replace specific fairy caves in the pool and will be at those entrances unless ER is turned on (then they can be shuffled wherever). The fixed entrances are: +CLI: `--logic [noglitches|owglitches|hybridglitches|nologic]` + +### Overworld Glitches +_Support added by qadan and compiling_ + +Overworld Glitches logic includes (but is not limited to) the following: +* Overworld teleports and clips to reach various items/entrances +* Use of superbunny to obtain items and/or bonk open entrances +* Use of mirror to access Desert Palace East Entrance +* Use of bunny pocket to access the Back of Skull Woods and VOO Hammer house entrances + + +### Hybrid Major Glitches +_Support added by Muffins (ported from work by Espeon)_. + +**Not currently compatible with Door Shuffle** + +Hybrid Major Glitches logic includes the following: +* All Overworld Glitches logic +* Kikiskip to access PoD wihtout MP or DW access +* IP Lobby clip to skip fire requirement +* Traversal between TT -> Desert +* Traversal between Spec rock upper -> Spec rock mid +* Traversal between Paradox lower -> Paradox mid + upper +* Traversal between Mire -> Hera -> Swamp +* Stealing SK from Mire to open SP +* Using the Mire big key to open Hera doors and big chest + +All traversals mentioned are considered connectors in entrance shuffle -* Desert Healer Fairy -* Swamp Healer Fairy (aka Light Hype Cave) -* Dark Death Mountain Healer Fairy -* Dark Lake Hylia Ledge Healer Fairy (aka Shopping Mall Bomb) -* Bonk Fairy (Dark) ## Game Options @@ -598,34 +663,3 @@ Can be used to set a seed number to generate. Using the same seed with same sett Use to batch generate multiple seeds with same settings. If a seed number is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with the same seed number given will produce the same 10 (different) roms each time). -## Glitched Logic - -Overworld glitches, Hybrid Major Glitches (HMG) and No Logic are currently supported. - -### Overworld Glitches -_Support added by qadan and compiling_ - -Overworld Glitches logic includes (but is not limited to) the following: -* Overworld teleports and clips to reach various items/entrances -* Use of superbunny to obtain items and/or bonk open entrances -* Use of mirror to access Desert Palace East Entrance -* Use of bunny pocket to access the Back of Skull Woods and VOO Hammer house entrances - - -### Hybrid Major Glitches -_Support added by Muffins (ported from work by Espeon)_. - -**Not currently compatible with Door Shuffle** - -Hybrid Major Glitches logic includes the following: -* All Overworld Glitches logic -* Kikiskip to access PoD wihtout MP or DW access -* IP Lobby clip to skip fire requirement -* Traversal between TT -> Desert -* Traversal between Spec rock upper -> Spec rock mid -* Traversal between Paradox lower -> Paradox mid + upper -* Traversal between Mire -> Hera -> Swamp -* Stealing SK from Mire to open SP -* Using the Mire big key to open Hera doors and big chest - -All traversals mentioned are considered connectors in entrance shuffle From a6e96720a34f25bf240f56bf29d6dcce53554a0c Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 26 Jan 2024 13:48:34 -0700 Subject: [PATCH 124/158] fix: castle warp gate fix: potential swamola issue fix --- Main.py | 2 +- RELEASENOTES.md | 3 +++ Rom.py | 3 +-- data/base2current.bps | Bin 117538 -> 117552 bytes source/enemizer/OwEnemyList.py | 4 ++-- source/rom/DataTables.py | 42 +++++++++++++++++++++++++++++---- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Main.py b/Main.py index 83af5048..11589008 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.3' +version_number = '1.4.1.4' version_branch = '-u' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c668042b..cd256c63 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.4u + * Inverted: Castle warp should not appear after defeating Aga 1 + * Enemzier: Fixed a crash with cached sprites Zora/Swamola * 1.4.1.3v * Enemizer: Raven/Murderdactyls using the correct damage table * Enemzier: Boss drops only center when boss shuffle is on diff --git a/Rom.py b/Rom.py index ea11e06b..000b4da4 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'd63715be60a548e080dc84ae588307e5' +RANDOMIZERBASEHASH = 'd47bab6e3c7de77295fcdc0b0ab84a9d' class JsonRom(object): @@ -2474,7 +2474,6 @@ def set_inverted_mode(world, player, rom): rom.write_byte(snes_to_pc(0x00D0e8), 0xE0) rom.write_byte(snes_to_pc(0x00D1c7), 0x00) write_int16(rom, snes_to_pc(0x1BE8DA), 0x39AD) - rom.write_byte(0xF6E58, 0x80) # no whirlpool under castle gate rom.write_bytes(0x0086E, [0x5C, 0x00, 0xA0, 0xA1]) # TR tail rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warps under rocks rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) diff --git a/data/base2current.bps b/data/base2current.bps index 22fd6589399deb99ffbc392c629cf2176cc677f1..fdad6e462c0fdc8a2b5bba2b8de782f652ab615a 100644 GIT binary patch delta 988 zcmWNPYfO`O7{&h&Ewo%(I}i#*x)#{j>HvuX(zz+J3pc?bQA8qc;J|^aEMc&L@^296 z+fgokZ6BpVw{_x%!>R;|YoO3_p9ZpIm+S*hw`7ozKteL-0>0~~b53$ToaA@DHVf{U z1z+;`!+F_vhxoiFGT-aWH@XpaH7W}J8WPB(g2FZq477tLev3smCyagc)s z+s7eNMgoIq?*^$SF0xGUTa#`@vLsm=n6pISg;-kJSR76aIXsz{0@kM zsI>_zT<|%l@QDkO-~{fRfUCR3na}#m_-&^3qP60+;(c&t#0gjjh`Kwj65l{}>-nLb>2lLI};E z?_;hT_DlB3c2a9d&G1ZziD#F+LP{YpU;L`gD8z9$j~r=Ux6v~=sn zD7TCA;iT)5b$lC-OoMz+a9kK2BOKxE9LH^NYn#1)%Kohd@8Kd)#(1r``9WD`5cZTox>_ybg!a=xzbF-k7)m#XqSREWhyEn*x zWZT};K+^}^(Uh%0-W-&MWS{)O5Jcp}-DM&Ia)}shIaoUf*-(TNbC4Lb!%s??Qo`}H zxrJfW9jDS8(>|HkZn7$u!9$vf72+^>9va_!Cq9_EJDidCJ0&*;Z5?4{6x4IMQycD@wn+nd`mb3X{@7Ai6-X`h_-G!& z1iX|Z-jWQr=DncfFm3@--z}&b)i$vjdaL1j|5e+?qC{$V?&XDrDc9~@VTmM^cE&m} w>#}z1$B4J|4%Hj_7D8O2N-@Rovtrt(tZ42zy^)AF7NAzxeY@N~T{g1(KP|PGEdT%j delta 1003 zcmWMkZA_C_7`;ymwovE_Eg-Fw0v+2B1R|eZK?+L*5lk2}Lbs7kpa_#1odPS$eS>IQ zR=~b1SIVH9WjJ9Q&c*<%P-wAyY_9y7n}3-5%2*^R3CWzB`mVp8b53%Sb8?=WgPd!F zoKM(67pkdAM-cm0F?);}p&!M?!2?H#G?5!PsmVWt!dEmaGYw|xjLa+;b2MbWSO|;V zz1rzsE!-@J-=~#MIKm2l;Nwy`>#Q5yZdqt38JmOqP(^g}AcY!Yod=1yIW!E9Y@>GM zLK}#DZqaWF)&=VmE5mH!n}>$*!kRU+e#&S-&(U=?QMq8>uH^7AY&${Dy5Iv4k!=@b zf{G+Az}3C&2cL`|54vpeAKR+ks-0*Gx=gF(_lPp(o~Z~GA$OkzVWdJCcI`Rx#{ygg z1!-P{%0QOcMMx6n6@TZJmKw~#otLOQdpRCHbCUQMp`O#UPl!u!IjMC+oZOI+1Z*^V_)VdhsG9#C|Bx;wln3IVfY>|`vrFE7 z6c1OBJ03`aJo3;3DG)_?OYk16RZTjUKq}|8O1aEGEV;@RM|+z6;Wf&g5u3hZw|6F$}ZKZ0(HvCjQfw ze=Z7B@z&g`&8e&a>|q$X%hrRiy^h?t3zadI^=Wu6j%jB$^8K+4_c6Ck-=_ER2;U27 zASX&M>e|nYLp;KHY z59&s?mSiGZh0d;wv`Fmkb4q6W1=lAA=>6(wZ0^X`51|7n21$^s>X5-!C<}b=4tj!q zLYv5?3NUiIg@*@obzfVaAsu@B&!A%WFT?KywBdJS0+~0^5aQ4ZJSwao>z7|L>+L7A zD*~q23hXx86U~&L7J509h|Cko8pJ{b$zFrpgUXf}s*Bd*N!`ceSM8c(>Dcrx!84bE~!5s!X+ IV^tUVKheORIRF3v diff --git a/source/enemizer/OwEnemyList.py b/source/enemizer/OwEnemyList.py index 39f2b150..ffbf79d4 100644 --- a/source/enemizer/OwEnemyList.py +++ b/source/enemizer/OwEnemyList.py @@ -521,7 +521,7 @@ def init_vanilla_sprites_ow(): create_sprite(0x1a, EnemySprite.GreenGuard, 0x0C, 0x0E, '', 0x09D0A3) create_sprite(0x1a, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D0A6, fix=True) create_sprite(0x1a, EnemySprite.BlueRupee, 0x17, 0x17, '', 0x09D0A9) - # create_sprite(0x1a, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D0AC) + 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) @@ -843,7 +843,7 @@ 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, fix=True) - # create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421) + create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421) create_sprite(0xaa, EnemySprite.UsainBolt, 0x0F, 0x18, '', 0x09D421) # was 0x09D424 # Screen1B_2: create_sprite(0xab, EnemySprite.Wiseman, 0x19, 0x12, '', 0x09D425) diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index c818dfc4..6c732d29 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -1,6 +1,6 @@ from collections import defaultdict -from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes, load_cached_yaml +from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes, load_cached_yaml, pc_to_snes from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats, EnemySprite from source.dungeon.EnemyList import sprite_translation @@ -94,9 +94,7 @@ class DataTables: # _00FA81 is LW normal # _00FAC1 is LW post-aga # _00FB01 is DW - 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()) + self.write_ow_sprite_data_to_rom(rom) for sprite, stats in self.enemy_stats.items(): # write health to rom if stats.health is not None: @@ -132,6 +130,42 @@ class DataTables: 0x0F, 0x01, 0x0F, 0x0F, 0x11, 0x0F, 0x0F, 0x03 ]) + def write_ow_sprite_data_to_rom(self, rom): + max_per_state = {0: 0x40, 1: 0x90, 2: 0x8D} # dropped max on state 2 to steal space for a couple extra sprites (Murahdahla) + pointer_address = snes_to_pc(0x09C881) + data_pointer = snes_to_pc(0x09CB3B) # was originally 0x09CB41 - stealing space for a couple extra sprites (Murahdahla) + empty_pointer = pc_to_snes(data_pointer) & 0xFFFF + rom.write_byte(data_pointer, 0xff) + cached_dark_world = {} + data_pointer += 1 + for state in range(0, 3): + if state > 0: # move pointer to next section + pointer_address += max_per_state[state-1] * 2 + for screen in range(0, max_per_state[state]): + internal_screen_id = screen + if state == 0: + internal_screen_id += 0x200 + if state == 2 and screen < 0x40: + internal_screen_id += 0x90 + if internal_screen_id not in self.ow_enemy_table: # has no sprites + rom.write_bytes(pointer_address + screen * 2, int16_as_bytes(empty_pointer)) + else: + if state == 2 and screen >= 0x40: # state 2 uses state 1 pointer for screens >= 0x40 + rom.write_bytes(pointer_address + screen * 2, cached_dark_world[screen]) + # the sprites are already written out + else: + data_address = pc_to_snes(data_pointer) & 0xFFFF + ref = int16_as_bytes(data_address) + if screen >= 40: + cached_dark_world[screen] = ref + rom.write_bytes(pointer_address + screen * 2, ref) + for sprite in self.ow_enemy_table[internal_screen_id]: + data = sprite.sprite_data() + rom.write_bytes(data_pointer, data) + data_pointer += len(data) + rom.write_byte(data_pointer, 0xff) + data_pointer += 1 + special_health_table = { EnemySprite.Octorok: (0x068F76, 0x068F77), From ef778b7125f2dcd1cd9681cef69b58b61d64b05a Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 29 Jan 2024 10:06:04 -0700 Subject: [PATCH 125/158] fix: logic for dynamic doors --- DoorShuffle.py | 7 ++++++- RELEASENOTES.md | 1 + Rules.py | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index b75e4df4..c961dee7 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -2437,6 +2437,11 @@ def change_door_to_trap(d, world, player): if d.entrance.connected_region is not None and d.blocked: d.entrance.connected_region.entrances.remove(d.entrance) d.entrance.connected_region = None + if d.dependents: + for dep in d.dependents: + if dep.entrance.connected_region is not None: + dep.entrance.connected_region.remove(dep.entrance) + dep.entrance.connected_region = None trap_door_exceptions = { @@ -2448,7 +2453,7 @@ trap_door_exceptions = { 'GT Torch Cross WN', 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Torches WN', 'PoD Lobby N', 'PoD Middle Cage S', 'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Eastern Pot Switch WN', 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Eastern Dark Square Key Door WN', 'Eastern Lobby NW', - 'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Desert Back Lobby S', 'Desert West S', + 'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Ice Cross Right ES', 'Desert Back Lobby S', 'Desert West S', 'Desert West Lobby ES', 'Mire Hidden Shooters SE', 'Mire Hidden Shooters ES', 'Mire Hidden Shooters WS', 'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS', 'GT Conveyor Cross EN', } diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cd256c63..22b92568 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -142,6 +142,7 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes * 1.4.1.4u + * Logic: Fixed logic bugs surrounding dynammic doors missing logic from big keys and other door types * Inverted: Castle warp should not appear after defeating Aga 1 * Enemzier: Fixed a crash with cached sprites Zora/Swamola * 1.4.1.3v diff --git a/Rules.py b/Rules.py index 41705faf..7fd58e10 100644 --- a/Rules.py +++ b/Rules.py @@ -895,8 +895,14 @@ def bomb_rules(world, player): for door in doors_to_bomb_check: if door.kind(world) in [DoorKind.Dashable]: add_rule(door.entrance, lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + if door.dependents: + for dep in door.dependents: + add_rule(dep.entrance, lambda state: state.can_use_bombs(player) or state.has_Boots(player)) elif door.kind(world) in [DoorKind.Bombable]: add_rule(door.entrance, lambda state: state.can_use_bombs(player)) + if door.dependents: + for dep in door.dependents: + add_rule(dep.entrance, lambda state: state.can_use_bombs(player)) def challenge_room_rules(world, player): @@ -2335,6 +2341,9 @@ def add_key_logic_rules(world, player): forbid_item(location, d_logic.small_key_name, player) for door in d_logic.bk_doors: add_rule(world.get_entrance(door.name, player), create_rule(d_logic.bk_name, player)) + if door.dependents: + for dep in door.dependents: + add_rule(dep.entrance, create_rule(d_logic.bk_name, player)) for chest in d_logic.bk_chests: big_chest = world.get_location(chest.name, player) add_rule(big_chest, create_rule(d_logic.bk_name, player)) From 4281457139eff39ded5d639c7fd1768bc888fcaa Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 31 Jan 2024 11:26:10 -0700 Subject: [PATCH 126/158] fix: eliminate writing a stray byte when the sprite array is empty --- source/rom/DataTables.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index 6c732d29..c9c3c700 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -131,6 +131,9 @@ class DataTables: ]) def write_ow_sprite_data_to_rom(self, rom): + # calculate how big this table is going to be? + # bytes = sum(1+len(x)*3 for x in self.ow_enemy_table.values() if len(x) > 0)+1 + # ending_byte = 0x09CB3B + bytes max_per_state = {0: 0x40, 1: 0x90, 2: 0x8D} # dropped max on state 2 to steal space for a couple extra sprites (Murahdahla) pointer_address = snes_to_pc(0x09C881) data_pointer = snes_to_pc(0x09CB3B) # was originally 0x09CB41 - stealing space for a couple extra sprites (Murahdahla) @@ -150,10 +153,10 @@ class DataTables: if internal_screen_id not in self.ow_enemy_table: # has no sprites rom.write_bytes(pointer_address + screen * 2, int16_as_bytes(empty_pointer)) else: - if state == 2 and screen >= 0x40: # state 2 uses state 1 pointer for screens >= 0x40 + if state == 2 and screen >= 0x40: # state 2 uses state 1 pointer for screens >= 0x40 rom.write_bytes(pointer_address + screen * 2, cached_dark_world[screen]) # the sprites are already written out - else: + elif len(self.ow_enemy_table[internal_screen_id]) > 0: data_address = pc_to_snes(data_pointer) & 0xFFFF ref = int16_as_bytes(data_address) if screen >= 40: From d70c9c87f5dfa57ecf4e2021973c96a3b26722e2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 31 Jan 2024 11:31:59 -0700 Subject: [PATCH 127/158] fix (hmg): remove the two placed swamp palace keys from the pool --- ItemList.py | 2 ++ Main.py | 2 +- RELEASENOTES.md | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ItemList.py b/ItemList.py index 7b8172d5..236b88d7 100644 --- a/ItemList.py +++ b/ItemList.py @@ -291,6 +291,8 @@ def generate_itempool(world, player): # In HMG force swamp smalls in pots to allow getting out of swamp palace placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' + pool.remove('Small Key (Swamp Palace)') + pool.remove('Small Key (Swamp Palace)') start_inventory = list(world.precollected_items) for item in precollected_items: diff --git a/Main.py b/Main.py index 11589008..580a6552 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.4' +version_number = '1.4.1.5' version_branch = '-u' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 22b92568..b119b654 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,9 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.5u + * Major Fix: Problem with Ganon's Room sprites + * HMG: Remove extra Swamp Smalls in the pool due to pottery settings * 1.4.1.4u * Logic: Fixed logic bugs surrounding dynammic doors missing logic from big keys and other door types * Inverted: Castle warp should not appear after defeating Aga 1 From c139a971c478026763491cae5cab0df3681dda32 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 31 Jan 2024 11:54:50 -0700 Subject: [PATCH 128/158] fix (enemizer): enemy bans --- RELEASENOTES.md | 1 + source/enemizer/enemy_deny.yaml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b119b654..915d7664 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -144,6 +144,7 @@ These are now independent of retro mode and have three options: None, Random, an * 1.4.1.5u * Major Fix: Problem with Ganon's Room sprites * HMG: Remove extra Swamp Smalls in the pool due to pottery settings + * Enemizer: Couple enemy bans * 1.4.1.4u * Logic: Fixed logic bugs surrounding dynammic doors missing logic from big keys and other door types * Inverted: Castle warp should not appear after defeating Aga 1 diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index bea935bf..e5536857 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -67,6 +67,7 @@ UwGeneralDeny: - [ 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, 5, ["AntiFairyCircle", "Bumper"]] - [ 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" @@ -125,7 +126,7 @@ UwGeneralDeny: - [ 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" + - [ 0x0053, 7, ["Beamos", "AntiFairyCircle", "Bumper", "RollerVerticalUp", "RollerVerticalDown"]] #"Desert Palace - Bridge - Popo 5" - [ 0x0055, 1, [ "RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Secret Passage Exit - Green Knife Guard 1" - [ 0x0057, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x0057, 1, ["Statue"]] # Statue switch issues From 646fc95143d15904d255cb09ef4c495bc848e499 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 7 Feb 2024 11:51:22 -0700 Subject: [PATCH 129/158] fix(enemizer): more enemy bans --- source/enemizer/enemy_deny.yaml | 96 +++++++++++++++++---------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index e5536857..7c378cd3 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -17,9 +17,9 @@ UwGeneralDeny: - [ 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, 0, [ "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit"] ] #"Swamp Palace - Pool - Zol 1" + - [ 0x0016, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Zol 2" + - [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"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" @@ -52,7 +52,7 @@ UwGeneralDeny: - [ 0x0028, 4, [ "RollerVerticalUp" ] ] #"Swamp Palace - Entrance Ledge - Spike Trap" - [ 0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"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, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ]] + - [ 0x002a, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "RollerHorizontalRight", "RollerHorizontalLeft"]] - [ 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" @@ -91,12 +91,12 @@ UwGeneralDeny: - [ 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" + - [ 0x0042, 0, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 1" + - [ 0x0042, 1, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 2" + - [ 0x0042, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 3" + - [ 0x0042, 3, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 4" + - [ 0x0042, 4, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 5" + - [ 0x0042, 5, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"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" @@ -140,8 +140,8 @@ UwGeneralDeny: - [ 0x0057, 10, ["Statue"]] # Statue switch issues - [ 0x0057, 11, ["Statue"]] # Statue switch issues - [ 0x0057, 12, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike", "SpikeBlock", "Statue"]] #"Skull Woods - Big Key Room - Gibdo 6" - - [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 1" - - [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 2" + - [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 1" + - [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 2" - [ 0x0058, 0, ["Statue"]] - [ 0x0058, 1, ["Statue"]] - [ 0x0058, 2, ["Statue"]] @@ -157,7 +157,7 @@ UwGeneralDeny: - [ 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" + - [ 0x0060, 0, [ "RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper", "Beamos" ] ] #"Hyrule Castle - West - Blue Guard" - [ 0x0062, 0, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Hyrule Castle - East - Blue Guard" - [ 0x0064, 2, [ "Bumper" , "Beamos" ] ] #"Thieves' Town - Attic Hall Left - Keese 2" - [ 0x0064, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots @@ -171,7 +171,7 @@ UwGeneralDeny: - [ 0x0067, 2, ["Bumper"]] #"Skull Woods - Firebar Pits - Blue Bari 2" - [ 0x0067, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 1" - [ 0x0067, 4, [ "AntiFairyCircle", "Bumper" ]] - - [ 0x0067, 5, [ "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 3" + - [ 0x0067, 5, ["RollerVerticalDown", "Beamos"]] #"Skull Woods - Firebar Pits - Hardhat Beetle 3" - [ 0x0067, 6, [ "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 4" - [ 0x0067, 7, [ "Beamos", "AntiFairyCircle", "Bumper", "BunnyBeam" ] ] #"Skull Woods - Firebar Pits - Fire Bar (Clockwise)" - [ 0x006a, 0, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 1" @@ -402,10 +402,10 @@ OwGeneralDeny: - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x5e, 0, ["Gibo"]] # kiki eating Gibo - - [0x5e, 1, ["Gibo"]] # kiki eating Gibo + - [0x5e, 1, ["Gibo", "RollerVerticalUp", "RollerVerticalDown"]] # kiki eating Gibo - [0x5e, 2, ["Gibo"]] # kiki eating Gibo - [0x5e, 3, ["Gibo"]] # kiki eating Gibo - - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo + - [0x5e, 4, ["RollerVerticalUp", "RollerVerticalDown", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo - [0x5e, 5, ["Gibo"]] # kiki eating Gibo - [0x5e, 6, ["Gibo"]] # kiki eating Gibo - [0x5e, 7, ["Gibo"]] # kiki eating Gibo @@ -439,94 +439,97 @@ UwEnemyDrop: - [0x00b0, 8, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay # the following are not allowed at certain pits (or on conveyors near pits) # because they despawned or clipped away or immediately fell, etc - - [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0044, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 8, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0049, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x007b, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x007b, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - - [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0095, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - - [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - [0x0095, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] - - [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x00af, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Bumper", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] # wizzrobe despawn issues - on pots/blocks - too close to some object @@ -581,7 +584,6 @@ UwEnemyDrop: - [0x009f, 5, ["Wizzrobe", "Stal"]] - [0x00a1, 1, ["Wizzrobe"]] - [0x00aa, 5, ["Wizzrobe"]] - - [0x00af, 0, ["Wizzrobe", "Stal"]] - [0x00b0, 1, ["Wizzrobe"]] - [0x00b0, 2, ["Wizzrobe"]] - [0x00b2, 4, ["Wizzrobe"]] From d03d21425b3c6dcadd657b0c42009cb8a0daab59 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 7 Feb 2024 12:49:50 -0700 Subject: [PATCH 130/158] fix(ow map): red/blue pendant colors --- Items.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Items.py b/Items.py index 1bbda012..73c9d196 100644 --- a/Items.py +++ b/Items.py @@ -61,8 +61,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'), 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x37, 0x08], 999, None, None, None, None, None, None, None), - 'Red Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None), - 'Blue Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x39, 0x0a], 999, None, None, None, None, None, None, None), + 'Red Pendant': (True, False, 'Crystal', [0x02, 0x32, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None), + 'Blue Pendant': (True, False, 'Crystal', [0x01, 0x34, 0x60, 0x00, 0x69, 0x39, 0x0a], 999, None, None, None, None, None, None, None), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), 'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), 'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), From 21c3618c01e02fbdf82e28aec58e9b4b6207a08c Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 7 Feb 2024 13:12:49 -0700 Subject: [PATCH 131/158] fix(enemizer): random tutorial guards in south kak --- source/rom/DataTables.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index c9c3c700..9713ef80 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -150,7 +150,8 @@ class DataTables: internal_screen_id += 0x200 if state == 2 and screen < 0x40: internal_screen_id += 0x90 - if internal_screen_id not in self.ow_enemy_table: # has no sprites + # has no sprites + if internal_screen_id not in self.ow_enemy_table or len(self.ow_enemy_table[internal_screen_id]) == 0: rom.write_bytes(pointer_address + screen * 2, int16_as_bytes(empty_pointer)) else: if state == 2 and screen >= 0x40: # state 2 uses state 1 pointer for screens >= 0x40 From a7e0b85226e9bcb597d78ca6fd0631301a63fd94 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 7 Feb 2024 15:21:13 -0700 Subject: [PATCH 132/158] fix(rom): baserom update --- Main.py | 2 +- RELEASENOTES.md | 8 +++++++- Rom.py | 2 +- data/base2current.bps | Bin 117552 -> 117516 bytes 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Main.py b/Main.py index 580a6552..59f627d8 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.5' +version_number = '1.4.1.6' version_branch = '-u' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 915d7664..2c09883b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -139,8 +139,14 @@ These are now independent of retro mode and have three options: None, Random, an * Dark Lake Hylia Ledge Healer Fairy (aka Shopping Mall Bomb) * Bonk Fairy (Dark) -# Bug Fixes and Notes +# Patch Notes +* 1.4.1.6u + * Difficulty: Fixed some issues around item caps not being respected + * Enemezier: Tutorial guards remove from South Kakariko + * Map: Pendant colors fixed + * Minor rom code cleanup + * Enemizer: Hovers added to problematic pool near pits. Some other bans * 1.4.1.5u * Major Fix: Problem with Ganon's Room sprites * HMG: Remove extra Swamp Smalls in the pool due to pottery settings diff --git a/Rom.py b/Rom.py index 000b4da4..471ac8f7 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'd47bab6e3c7de77295fcdc0b0ab84a9d' +RANDOMIZERBASEHASH = '03e2ae451d5abadd905b1a4da283ab5a' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index fdad6e462c0fdc8a2b5bba2b8de782f652ab615a..75dbf238992fb94482f96254c8c44675a82f67c3 100644 GIT binary patch delta 9406 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 From ad611f9ddaa579366123a4dd96f5c9eb0b1a88b2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 21 Feb 2024 12:27:32 -0700 Subject: [PATCH 133/158] fix(gui): problem with customitemarray being empty --- source/gui/bottom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 96d7880e..b9fdd9b2 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -76,8 +76,9 @@ def bottom_frame(self, parent, args=None): for k,v in vars(parse_cli(cliargs)).items(): if k not in vars(guiargs): setattr(guiargs, k, v) - elif type(v) is dict: # use same settings for every player - setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, len(v) + 1)}) + elif type(v) is dict: # use same settings for every player + players = guiargs.multi if len(v) == 0 else len(v) + setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, players + 1)}) argsDump = vars(guiargs) needEnemizer = False From 49b190c489b2b7cdfcc458eb38b83ec8cf32c6ec Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 26 Feb 2024 16:02:35 -0700 Subject: [PATCH 134/158] fix: more enemy bans fix: custom item pool aborts if player is missing --- ItemList.py | 2 +- source/enemizer/enemy_deny.yaml | 43 ++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/ItemList.py b/ItemList.py index 236b88d7..5d7e3228 100644 --- a/ItemList.py +++ b/ItemList.py @@ -265,7 +265,7 @@ def generate_itempool(world, player): # set up item pool skip_pool_adjustments = False - if world.customizer and world.customizer.get_item_pool(): + if world.customizer and world.customizer.get_item_pool() and player in world.customizer.get_item_pool(): (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = make_customizer_pool(world, player) skip_pool_adjustments = True elif world.custom: diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index 7c378cd3..b55e90a4 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -49,7 +49,11 @@ UwGeneralDeny: - [ 0x0027, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 3" - [ 0x0027, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "RollerVerticalDown", "RollerVerticalUp"]] - [ 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" + - [0x0028, 0, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x0028, 1, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x0028, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x0028, 3, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x0028, 4, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora", "RollerVerticalUp"]] #"Swamp Palace - Entrance Ledge - Spike Trap" - [ 0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"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, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "RollerHorizontalRight", "RollerHorizontalLeft"]] @@ -83,6 +87,10 @@ UwGeneralDeny: - [ 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, 8, ["Wizzrobe"]] + - [0x003e, 9, ["Wizzrobe"]] + - [0x003e, 10, ["Wizzrobe"]] + - [0x003e, 11, ["Wizzrobe"]] - [ 0x003f, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 1" - [ 0x003f, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 2" - [ 0x003f, 4, [ "Wizzrobe", "Statue", "Bumper", "BigSpike", "AntiFairyCircle"]] # Wizzrobes can't spawn on pots @@ -196,7 +204,7 @@ UwGeneralDeny: - [ 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", "Statue"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)" + - [ 0x007c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue"]] #"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" @@ -231,7 +239,7 @@ UwGeneralDeny: - [ 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" - - [ 0x008e, 2, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots + - [0x008e, 2, ["Wizzrobe", "Statue", "FirebarCW", "FirebarCCW"]] # Wizzrobes can't spawn on pots - [ 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" @@ -322,13 +330,13 @@ UwGeneralDeny: - [ 0x00cb, 11, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots - [ 0x00cc, 8, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) - [ 0x00cc, 12, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) - - [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper" ] ] #"Ice Palace - Over Boss - top - Red Bari 1" + - [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper", "Chainchomp"]] #"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" - - [ 0x00ce, 4, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - - [ 0x00ce, 5, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - - [ 0x00ce, 6, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] - - [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 3, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Chainchomp"]] #"Ice Palace - Over Boss - top - Statue" + - [0x00ce, 4, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]] + - [0x00ce, 5, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]] + - [0x00ce, 6, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]] + - [0x00ce, 7, ["RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW", "Chainchomp"]] - [ 0x00d0, 0, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - [ 0x00d0, 1, [ "AntiFairyCircle", "BigSpike", "Bumper"]] - [ 0x00d0, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] @@ -337,7 +345,16 @@ UwGeneralDeny: - [ 0x00d0, 7, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - [ 0x00d0, 9, [ "AntiFairyCircle", "BigSpike", "Bumper"]] - [ 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" + - [0x00d2, 0, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 1, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 2, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 3, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 4, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 5, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 6, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 7, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] + - [0x00d2, 8, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Mire 2 - Popo BL" + - [0x00d2, 9, ["Raven", "Poe", "GreenZirro", "BlueZirro", "Swamola", "Zora"]] - [ 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" @@ -385,6 +402,8 @@ UwGeneralDeny: - [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] - [0x010b, 6, ["RollerHorizontalRight"]] + - [0x010c, 4, ["AntiFairyCircle"]] + - [0x010c, 5, ["AntiFairyCircle"]] - [0x010c, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] - [0x010c, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] OwGeneralDeny: @@ -401,6 +420,8 @@ OwGeneralDeny: - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x55, 6, ["BigSpike"]] + - [0x57, 5, ["RollerVerticalUp", "RollerVerticalDown"]] - [0x5e, 0, ["Gibo"]] # kiki eating Gibo - [0x5e, 1, ["Gibo", "RollerVerticalUp", "RollerVerticalDown"]] # kiki eating Gibo - [0x5e, 2, ["Gibo"]] # kiki eating Gibo @@ -614,7 +635,7 @@ UwEnemyDrop: - [0x010c, 5, ["Wizzrobe"]] # other mimic cave spots are in the rail section - # enemies that have problems with conveyors + # enemies that have problems with conveyors - add wizzrobes here - for collision? - [0x003b, 0, ["GreenMimic", "RedMimic"]] - [0x003b, 1, ["GreenMimic", "RedMimic"]] - [0x003b, 2, ["GreenMimic", "RedMimic"]] From 6d0debd987496caae3eb9602d38f496b44e8d372 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 1 Mar 2024 10:06:15 -0700 Subject: [PATCH 135/158] fix(enemizer): remove mimic exception --- Main.py | 2 +- RELEASENOTES.md | 4 ++++ source/enemizer/Enemizer.py | 5 +---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Main.py b/Main.py index 59f627d8..160ec9c6 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.6' +version_number = '1.4.1.7' version_branch = '-u' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2c09883b..1be11831 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,10 @@ These are now independent of retro mode and have three options: None, Random, an # Patch Notes +* 1.4.1.7u + * Some bugs around Triforce Pieces smoothed out + * Enemizer: No exception for mimics/eyegores in vanilla rooms if enemy logic is turned to off + * Enemizer: Various enemy bans * 1.4.1.6u * Difficulty: Fixed some issues around item caps not being respected * Enemezier: Tutorial guards remove from South Kakariko diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py index b4715268..65009fdd 100644 --- a/source/enemizer/Enemizer.py +++ b/source/enemizer/Enemizer.py @@ -362,14 +362,11 @@ def determine_forbidden(forbid, room_id, drop_flag=False): forbidden_set = set() if forbid: forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob, - EnemySprite.Lynel}) + EnemySprite.Lynel, EnemySprite.RedEyegoreMimic, EnemySprite.RedMimic}) if drop_flag: forbidden_set.add(EnemySprite.RedBari) # requires FireRod to Drop # else: Not yet able to protect triggers, would change default GT tile room behavior # forbidden_set.add(EnemySprite.AntiFairy) # can't drop anyway - if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla rooms - forbidden_set.add(EnemySprite.RedEyegoreMimic) - forbidden_set.add(EnemySprite.RedMimic) return forbidden_set From feb6bd475f56768b6b98b80acd0fb5915ff712a2 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 1 Mar 2024 14:10:55 -0600 Subject: [PATCH 136/158] Implemented New Item GFX System --- Rom.py | 2 +- asm/owrando.asm | 15 +++++++-------- data/base2current.bps | Bin 131587 -> 131690 bytes 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Rom.py b/Rom.py index 4741f457..b1c57e45 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '184ebc6920dd7b6ba985827017bfd9c0' +RANDOMIZERBASEHASH = '030a90f43980701de227341dd1a78a50' class JsonRom(object): diff --git a/asm/owrando.asm b/asm/owrando.asm index 2b9eab12..4c8a5082 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -446,6 +446,7 @@ OWBonkDropSparkle: LDA.l OWFlags+1 : AND.b #!FLAG_OW_BONKDROP : BEQ .nosparkle LDA.w $0E90,X : BEQ .nosparkle LDA.w SprRedrawFlag,X : BNE .nosparkle + LDA.b GameMode : CMP.b #$0E : BEQ .nosparkle JSL Sprite_SpawnSparkleGarnish ; move sparkle down 1 tile PHX : TYX : PLY @@ -594,15 +595,13 @@ OWBonkDrops: PLX : BEQ + : LDA.b #$00 : STA.w SpriteAITable,Y : BRA .return ; S = FlagBitmask, X (row + 2) + PHA - LDA.b #$EB : STA.l MiniGameTime + LDA.b #$EB JSL Sprite_SpawnDynamically+15 ; +15 to skip finding a new slot, use existing sprite - LDA.b #$01 : STA.w SprRedrawFlag,Y - - PLA - JSL AttemptItemSubstitution - STA.w SpriteItemType,Y - STA.w SpriteID,Y + PLA : STA.w SprSourceItemId, Y + PHX : TYX : PLY + JSL RequestStandingItemVRAMSlot + PHY : TXY : PLX ; affects the rate the item moves in the Y/X direction LDA.b #$00 : STA.w SpriteVelocityY,Y @@ -667,7 +666,7 @@ OWBonkDropCollected: RTS } -; A = SpriteID, Y = Sprite Slot Index, X = free/overwritten +; A = SprItemReceipt, Y = Sprite Slot Index, X = free/overwritten OWBonkSpritePrep: { STA.w SpriteTypeTable,Y diff --git a/data/base2current.bps b/data/base2current.bps index 2586d90992263029a389cb82aa9f9a4d30a6ea2c..cfd411380a568d450c8baad2d3cede9f3e602b63 100644 GIT binary patch delta 19531 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 delta 19717 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 z zoUxUd9;ciyLv=N57D0{Kh!NHoBMr#|bABPhd>7#Fx+y$Ml^UL<2y^z54a0dzS{s9H5t~?moNPoYJDlzXjs;vIhMIB- z&}t;?wsi@|X;C`dwyx!uSBm)WEAr!T{A4`O8^kz&mt075&7NAzg&A5lOTwiBaD_LB z^WU>hqImtIy|G^z>Mxgc^jBn{s=PU7|G&f*?-rH%0Aa?FDm-k&z&!W}7QJU!YF1Bs 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 From 46b8dff43aca24b035d9f36b8fb574932598e15f Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 1 Mar 2024 14:11:28 -0600 Subject: [PATCH 137/158] Adding Doors Async League yamls --- .../async_doors_league/S4_Ganonhunt.yaml | 70 +++++++++++++++++++ .../S4_Underworld_Enemies.yaml | 66 +++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 docs/presets/async_doors_league/S4_Ganonhunt.yaml create mode 100644 docs/presets/async_doors_league/S4_Underworld_Enemies.yaml diff --git a/docs/presets/async_doors_league/S4_Ganonhunt.yaml b/docs/presets/async_doors_league/S4_Ganonhunt.yaml new file mode 100644 index 00000000..777f4165 --- /dev/null +++ b/docs/presets/async_doors_league/S4_Ganonhunt.yaml @@ -0,0 +1,70 @@ +meta: + algorithm: balanced + players: 1 + race: true + user_notes: '' +placements: + 1: + Desert Palace - Boss: Triforce Piece + Eastern Palace - Boss: Triforce Piece + Ice Palace - Boss: Triforce Piece + Misery Mire - Boss: Triforce Piece + Palace of Darkness - Boss: Triforce Piece + Skull Woods - Boss: Triforce Piece + Swamp Palace - Boss: Triforce Piece + Thieves' Town - Boss: Triforce Piece + Tower of Hera - Boss: Triforce Piece + Turtle Rock - Boss: Triforce Piece +settings: + 1: + accessibility: locations + aga_randomness: true + any_enemy_logic: allow_all + beemizer: 0 + bigkeyshuffle: true + bombbag: false + boss_shuffle: none + bow_mode: progressive + collection_rate: true + compassshuffle: true + crystals_ganon: '7' + crystals_gt: random + decoupledoors: false + difficulty: normal + door_self_loops: false + door_shuffle: crossed + door_type_mode: big + dropshuffle: keys + dungeon_counters: 'on' + enemy_damage: default + enemy_health: default + enemy_shuffle: none + experimental: true + flute_mode: normal + goal: ganonhunt + hints: true + intensity: 3 + item_functionality: normal + key_logic_algorithm: partial + keyshuffle: wild + logic: noglitches + mapshuffle: true + mixed_travel: prevent + mode: open + openpyramid: auto + overworld_map: default + pottery: keys + pseudoboots: true + shopsanity: true + shuffle: crossed + shufflelinks: true + shufflepots: false + shuffletavern: true + standardize_palettes: standardize + swords: random + take_any: none + trap_door_mode: boss + triforce_goal: 8 + triforce_pool: 10 +start_inventory: + 1: [] diff --git a/docs/presets/async_doors_league/S4_Underworld_Enemies.yaml b/docs/presets/async_doors_league/S4_Underworld_Enemies.yaml new file mode 100644 index 00000000..6aa66eae --- /dev/null +++ b/docs/presets/async_doors_league/S4_Underworld_Enemies.yaml @@ -0,0 +1,66 @@ +start_inventory: + 1: + - Compass (Eastern Palace) + - Compass (Desert Palace) + - Compass (Tower of Hera) + - Compass (Agahnims Tower) + - Compass (Palace of Darkness) + - Compass (Swamp Palace) + - Compass (Skull Woods) + - Compass (Thieves Town) + - Compass (Ice Palace) + - Compass (Misery Mire) + - Compass (Turtle Rock) + - Compass (Ganons Tower) + - Compass (Escape) +settings: + 1: + accessibility: locations + aga_randomness: true + any_enemy_logic: allow_all + beemizer: '0' + bigkeyshuffle: 1 + bombbag: 0 + boss_shuffle: none + bow_mode: progressive + collection_rate: 1 + compassshuffle: 1 + crystals_ganon: '7' + crystals_gt: random + decoupledoors: 0 + difficulty: normal + door_self_loops: 0 + door_shuffle: crossed + door_type_mode: big + dropshuffle: underworld + dungeon_counters: 'on' + enemy_damage: default + enemy_health: default + enemy_shuffle: none + experimental: 1 + flute_mode: normal + goal: crystals + hints: 1 + intensity: 3 + item_functionality: normal + key_logic_algorithm: partial + keyshuffle: wild + logic: noglitches + mapshuffle: 1 + mixed_travel: prevent + mode: open + openpyramid: auto + overworld_map: default + pottery: keys + pseudoboots: 1 + shopsanity: 1 + shuffle: crossed + shufflelinks: 1 + shufflepots: 0 + shuffletavern: 1 + standardize_palettes: standardize + swords: assured + take_any: none + trap_door_mode: boss + triforce_goal: 0 + triforce_pool: 0 From da80fd45e419da1034e8dba8ea0b5939d21cd84b Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 11 Mar 2024 16:49:43 -0600 Subject: [PATCH 138/158] fix(owg+hmg): eg allowed to be armed --- Rom.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rom.py b/Rom.py index 471ac8f7..97b4e401 100644 --- a/Rom.py +++ b/Rom.py @@ -571,7 +571,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): dr_flags |= DROptions.OriginalPalettes if world.experimental[player]: dr_flags |= DROptions.DarkWorld_Spawns - if world.logic[player] != 'nologic': + if world.logic[player] not in ['owglitches', 'hybridglitches', 'nologic']: dr_flags |= DROptions.Fix_EG if world.door_type_mode[player] in ['big', 'all', 'chaos']: dr_flags |= DROptions.BigKeyDoor_Shuffle @@ -1299,10 +1299,10 @@ def patch_rom(world, rom, player, team, is_mystery=False): digging_game_rng = random.randint(1, 30) # set rng for digging game rom.write_byte(0x180020, digging_game_rng) rom.write_byte(0xEFD95, digging_game_rng) - rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills - rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix - rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill glitches_enabled = world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] + rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills + rom.write_byte(0x1800A4, 0x01 if not glitches_enabled else 0x00) # enable POD EG fix + rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill rom.write_byte(0x180358, 0x01 if glitches_enabled else 0x00) rom.write_byte(0x18008B, 0x01 if glitches_enabled else 0x00) From e737a138cf676260b7f24d2a66c1b9385607fe0a Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 11 Mar 2024 16:58:12 -0600 Subject: [PATCH 139/158] feat: new tiles for dungeon indicators build: test for lynel logic --- Rom.py | 2 +- data/base2current.bps | Bin 117516 -> 117498 bytes test/suite/enemy_logic/lynel_test.yaml | 29 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/suite/enemy_logic/lynel_test.yaml diff --git a/Rom.py b/Rom.py index 97b4e401..eb05877a 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '03e2ae451d5abadd905b1a4da283ab5a' +RANDOMIZERBASEHASH = '66890fefe34bb5878422322c5f952aa5' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 75dbf238992fb94482f96254c8c44675a82f67c3..62b711089c6e6f47e13d200cc91e8fc53398e650 100644 GIT binary patch delta 1134 zcmWlYYitx%6oBW>&b?Ehkk;}DtQJaYtVlH00BWojMjl3EDY#bR4`_JA7{VXZTFSCJ zvoo{Pscp8~b$eSjWDA?v+7{bV%9>W#rcK)TNW^Hm5v44KpfO~N!N6vn+~e)Pd(ZiD z&UfyktxL{F(_2?(OW5ngTU-#m(pWAbg8VqQ25sTJT+@#IV+-%q3KcG95Zd->` z>;t8HDG{E56j*}-NA91043A+E0O(UHwP_3eNY!Q%f6|?*rbm1#t%{5;uJ+SXi0<;lkW zc)6;jV?H&U=DO*`r4`aNUK4It2bh<*>X28ekiI0-wUVau8lp zz=Po+M|PBhvnabgJjT+1&;#7yTQ}ej;&X*zz^k&pH~xNt(PPzz$Vzh<}7xcAYbW! zVEryej$@8U%2!!D!DgA)Yi6)AkTw6tg^UH*b!Ir){6+rGNs~WA#1n^@%eW78h!sN&XqOJ;UAo)Cdawr3_0DoA?GS@oy}(U zW_pE;xq(R8>;&uF%Lan&L^x+eiE!CCLwFC95ZhTY)GLVfJkbjXzRJF1KA$~B%$s4WbgiRV4EX=GjTFzDgD*T6m#(ql%anvM?X~6Upf1IqGvG9vu58c<5FgajQ04f9I>m3m}kp3uQH2>-YJ_ClKFs`_pt6@ z^N74`L+RR5dfd}@!7S#&mn9y4`^MHLSxxR=Oq0gE0~mBXqW}N^ delta 1201 zcmWkuYitx%6rMXf_byM{f)COH(gIQlXe0znAX*(klz=ISnwVH2VEh3E{ve^t?wy^- z?f~6sS<5V}xFrR)>{37qU0u>HDM=xqkssP(c@2aRW6TzUq02Jf<0a?EJ?FdMIo~-q zH>cR@-Mim=TJ%VpH}BimMLcW&3Ha*Ins*?V4z{ZBjGMipddT+A8fv=uJXkEpT2#ov zrL^>ZtmPv2Lg^9v@}UtJfnfl!pDJoPXdku&YQHjjD6xIkkhO!_i>PuwelO{mImopy zR_tn;oJ6g4)G`x)CcjdYT>6fggs%9rWZ;OcE9&`o6h)2Bp=Jg$?H+m{0~WVTd=<6F z(NmpDy8TOP-!kJ6k6ux_;?tGH;Y6D?SINv%tQb$anr@)h6l&|%x?proLr44ktsRNV zpj}H7yJ*6vC8p@gV0@~Pd>+xr=p&JBO?I7<5P}n}jYdME%1Cs*ZdC@~sG#pQc5Qh$ z&Lh70R@bh_PpoMqo-?%{g&$%)!jj!Bf_wn=s!K_P)4;gJEEfZR^DlAQai+% z10rpa;Z_;+g^w!)Rl%IDtl5H2^rED_5>geCuV@y13w#gMImgA$0Q5A&;#cq6cgXL6S{~Jq;vwPJtqD>^zS!e>`BO}M*WT}@OV`sEySjfxylrhM=sLHFQ z1m&4rT_fbf6-0azVE@YQS>$R}F3xl1ZYGBdLbrAU3(f~lo8Xw`j+mxH8pxI zF}sOb>LmkglP^$H!*)lBKG}=-sB^sHCf@)T57m0T-|VxFh3=#gK?34?Z4^j+i5%{ z`a_~Wz(T(@^)OC7>`5wG}d?hY@_y#A8?$UxO-Tc$jO>P58N z-f1HK7vIiYHPOwn(xPIt(N4Fb!HFenX}sYRY6YWP>Zr+=MQ(xJx*t6rwXK%w&=Xx? kH{0mZ_-PAow*m}86zpXsnMe$6m_O?F=XBm5YZk8mA6hzN3IG5A diff --git a/test/suite/enemy_logic/lynel_test.yaml b/test/suite/enemy_logic/lynel_test.yaml new file mode 100644 index 00000000..9c779af5 --- /dev/null +++ b/test/suite/enemy_logic/lynel_test.yaml @@ -0,0 +1,29 @@ +meta: + players: 1 +settings: + 1: + mode: open + enemy_shuffle: shuffled + dropshuffle: underworld +enemies: + 1: + Underworld: + 0x55: + 1: Lynel +placements: # all the ways to kill a Lynel are Flipper locked + 1: + Waterfall Fairy - Left: Progressive Sword + Waterfall Fairy - Right: Progressive Sword + Hobo: Progressive Sword + Swamp Palace - Map Chest: Progressive Bow + Swamp Palace - Compass Chest: Progressive Bow + Zora's Ledge: Hammer +advanced_placements: + 1: + - type: Verification + item: Flippers + locations: + 'Hyrule Castle Secret Entrance Enemy #2': False # can't kill the lynel + Link's Uncle: True + + From d5d55b80171e63ef06c8317ac79cb4895ced2951 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 11 Mar 2024 17:03:40 -0600 Subject: [PATCH 140/158] fix(enemizer): any trinexx outside of TR should disable ice breath now --- source/enemizer/Bossmizer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py index 398cba89..85b8a3c2 100644 --- a/source/enemizer/Bossmizer.py +++ b/source/enemizer/Bossmizer.py @@ -166,14 +166,14 @@ def boss_writes(world, player, rom): data_tables.room_headers[room_id].byte_0 = 0x60 data_tables.room_headers[room_id].effect = 4 # $2E, $98, $FF (original shell) + # disable trinexx ice breath with No-ops if there's a trinexx anywhere outside TR + rom.write_bytes(snes_to_pc(0x09B37E), [0xEA, 0xEA, 0xEA, 0xEA]) if boss.name == 'Kholdstare' and (dungeon.name != 'Ice Palace' or level is not None): add_shell_to_boss_room(data_tables, dungeon.name, level, 0xF95) data_tables.room_headers[room_id].byte_0 = 0xE0 data_tables.room_headers[room_id].effect = 1 if boss.name != 'Trinexx' and dungeon.name == 'Turtle Rock' and level is None: remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xFF2) - # disable trinexx ice breath with No-ops - rom.write_bytes(snes_to_pc(0x09B37E), [0xEA, 0xEA, 0xEA, 0xEA]) if boss.name != 'Kholdstare' and dungeon.name == 'Ice Palace' and level is None: remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xF95) if boss.name != 'Blind' and dungeon.name == 'Thieves Town' and level is None: From 4629285e7e41773e7bacea7c782eb5f2c43b8e40 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 14 Mar 2024 12:54:19 -0600 Subject: [PATCH 141/158] fix(dropshuffle): minor drops do not increase counter if in the wrong dungeon --- Main.py | 2 +- RELEASENOTES.md | 6 ++++++ Rom.py | 2 +- data/base2current.bps | Bin 117498 -> 117491 bytes 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index 160ec9c6..4538bab4 100644 --- a/Main.py +++ b/Main.py @@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.4.1.7' +version_number = '1.4.1.8' version_branch = '-u' __version__ = f'{version_number}{version_branch}' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1be11831..2f7f9103 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -141,6 +141,12 @@ These are now independent of retro mode and have three options: None, Random, an # Patch Notes +* 1.4.1.8u + * HUD: New dungeon indicators based on common abbreviations + * OWG+HMG: EG is allowed to be armed + * Drop Shuffle: Fixed an issue with minor drops counting for the wrong dungeon + * Enemizer: Trinexx ice breath should be properly disabled if Trinexx is located outside of Turtle Rock + * Enemizer: Enemy bans * 1.4.1.7u * Some bugs around Triforce Pieces smoothed out * Enemizer: No exception for mimics/eyegores in vanilla rooms if enemy logic is turned to off diff --git a/Rom.py b/Rom.py index eb05877a..fba37983 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '66890fefe34bb5878422322c5f952aa5' +RANDOMIZERBASEHASH = '67b9ede4928dac94a650e9e9396ef44a' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 62b711089c6e6f47e13d200cc91e8fc53398e650..2c405d81b1fbf77c1277b1196a8966a11e127452 100644 GIT binary patch delta 613 zcmV-r0-F8$l?U^c2e1MI1bc{tcasJKf&pc-i336h0dcd;3ilEZ+qbBz9b}A+ii5W5 zh=~ld(j9vN0VT6e9|Q#paJ8uF>Y6!H;gyz?T_hnHp|_~82kPqT>gs?ABHp&B8X_>5 zr=XQ0%#)WSi2*y4AtmS)PPC|afRQ4`v#9EHfQf>wpj?27x3eB5Q3D7{xTxy5jUrov zcPY1bDFJ~w0Z_O3VgXzM0kfA@WC2wcO@TlMeuWOdUYr-(vpAm|e3WA|%Vt@K10MI1bvqplA1t7DRq-6mn z1wLG#lUJ9|WdVvHE{c@^@DhLtyOSA-r2y~;;xKypsR3(~R*REuRFKC+mw9FZD*-u| zrDg$00YaDVW&te$e3u+&0V5T}w)7N$TsqQ*Ya-FOsL2D+3P`6ZmsDo~HUWp1i)R5f z0eP3fX8|$`a894;@DEC=nB94o{$~Mh9N4$3U?B3hB3<^KFac|&4ao@5E3I+^&>@$5 zAD4(|0f02exTu?}i=4rYCIaFx`fC70AVk1KP)^`Zqfld?vq@-`ffcucnZh8ckGX&o zw}i36AgMo>JZS+s0(?f7cxeG49|=Y6!H;gz0~T_hnHskf-G2kPqT>gs?ABIdTJ8X_>5 zr=XQ0)RUJai2*~CAtmS)Rwpj?27zq1}DQ3D84xTxy5jUr=% zcPY1bDFJ~w0a&;BVgXzM0lSx0WC2wcb$~zzeuWOdU|NT)fMO=kf% z0gso2X8|<;;J2$_Ao{l=UG|+Y0c)iV$q3LZ zt#Sj Date: Thu, 14 Mar 2024 13:02:49 -0600 Subject: [PATCH 142/158] fix(enemizer): enemy ban --- source/enemizer/enemy_deny.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml index b55e90a4..b3d94127 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -160,6 +160,7 @@ UwGeneralDeny: - [ 0x0058, 8, ["Statue"]] - [ 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, 5, ["RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] - [ 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)" From 929bcb3de454c6fc5cb60e9b9a1a52122c99e00a Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 16 Mar 2024 01:43:57 -0500 Subject: [PATCH 143/158] Banned quiet heart beep SFX in shuffle --- source/classes/SFX.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/classes/SFX.py b/source/classes/SFX.py index 2998aacc..3a5012f6 100644 --- a/source/classes/SFX.py +++ b/source/classes/SFX.py @@ -492,7 +492,7 @@ sfx_instrument_changes = [ SFXInstrumentChange(0x02, 0x28, 0x11, [0x1A9638], type=[0x10, 0x11, 0x12]), SFXInstrumentChange(0x02, 0x29, 0x01, [0x1A97F0], type=Sf), # accompanied $29+3B SFXInstrumentChange(0x02, 0x2A, 0x0C, [0x1A93E4], type=Be, inc=[0x08, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x16]), - SFXInstrumentChange(0x02, 0x2B, 0x0E, [0x1A93A3], type=Me, ban=[0x08, 0x09, 0x0D], inc=[0x03, 0x11, 0x17]), + SFXInstrumentChange(0x02, 0x2B, 0x0E, [0x1A93A3], type=Me, ban=[0x08, 0x09, 0x0D], inc=[0x03, 0x17]), SFXInstrumentChange(0x02, 0x2C, 0x00, [0x1A938A, 0x1A9398], type=Sh, ban=[0x13]), # chained $2C->3A SFXInstrumentChange(0x02, 0x2C, 0x17, [0x1A9393, 0x1A939D], type=Me|Sh, ban=[0x0E], inc=[0x03, 0x11, 0x17]), # chained $2C->3A SFXInstrumentChange(0x02, 0x2D, 0x0F, [0x1A9457], type=Me, ban=[0x09], inc=[0x03, 0x17]), From dd75273b83b9ffc03a74e4b92a6624e90e53ea84 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 17 Mar 2024 04:13:55 -0500 Subject: [PATCH 144/158] Fixed Pyramid Exit inaccessible issue in District ER --- EntranceShuffle.py | 2 +- source/overworld/EntranceShuffle2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 516b0aad..c374ec15 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1511,7 +1511,7 @@ def connect_inaccessible_regions(world, lw_entrances, dw_entrances, caves, playe } for region_name in world.inaccessible_regions[player]: if (world.logic[player] in ['noglitches', 'minorglitches'] and region_name in glitch_regions) \ - or (region_name == 'Pyramid Exit Ledge' and (world.shuffle[player] != 'insanity' or world.is_tile_swapped(0x1b, player))) \ + or (region_name == 'Pyramid Exit Ledge' and (world.shuffle[player] not in ['district', 'insanity'] or world.is_tile_swapped(0x1b, player))) \ or (region_name == 'Spiral Mimic Ledge Extend' and not world.is_tile_swapped(0x05, player)): # removing irrelevant and resolved regions inaccessible_regions.remove(region_name) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 2042ade8..54c58417 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -910,7 +910,7 @@ def must_exits_helper(avail): } for region_name in avail.world.inaccessible_regions[avail.player]: if (avail.world.logic[avail.player] in ['noglitches', 'minorglitches'] and region_name in glitch_regions) \ - or (region_name == 'Pyramid Exit Ledge' and (avail.world.shuffle[avail.player] != 'insanity' or avail.world.is_tile_swapped(0x1b, avail.player))) \ + or (region_name == 'Pyramid Exit Ledge' and (avail.keep_drops_together or avail.world.is_tile_swapped(0x1b, avail.player))) \ or (region_name == 'Spiral Mimic Ledge Extend' and not avail.world.is_tile_swapped(0x05, avail.player)): # removing irrelevant and resolved regions inaccessible_regions.remove(region_name) From 856deda2a9a1d07e68f1d4b99fe38e7b871a2a48 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 17 Mar 2024 04:30:57 -0500 Subject: [PATCH 145/158] Updated Image Links in README --- README.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ad343b01..96275e2d 100644 --- a/README.md +++ b/README.md @@ -13,16 +13,8 @@ This is a very new mode of LTTPR so the tools and info is very limited. - CodeTracker, an [EmoTracker](https://emotracker.net) package for LTTPR - There is an [OW OWG Reference Sheet](https://zelda.codemann8.com/images/shared/ow-owg-reference-sheet.png) that shows all the in-logic places where boots/mirror clips and fake flippers are expected from the player. -# Known Issues -(Updated 2021-11-06) - ### If you want to playtest this, know these things: - If you fake flipper, beware of transitioning south. You could end up at the top of the waterfall in the southeast of either world. If you mistakenly drop down, it is important to know that altho the game may appear as frozen for a bit of time, the game is simply scrolling Link back to the original point of water entry. Upon "landing", you'll be able to S+Q properly. Falling from the waterfall is avoidable but it is super easy to do as it is super close to the transition. -- In Crossed OW, there are some interesting bunny swimming situations that can occur, these ARE in logic currently, just be careful and avoid taking a hit from an enemy. - -### Known bugs: -- Screens that loop on itself and also have free-standing items, the sprites are duplicated and can cause item duplication -- When OWG are performed to enter mega-tile screens (large OW screens), there is a small chance that an incorrect VRAM reference value causes the map graphics to offset in increments of 16 pixels # Feedback and Bug Reports @@ -137,7 +129,7 @@ Note: These changes do impact the logic. If you use `CodeTracker`, these Inverte Only settings specifically added by this Overworld Shuffle fork are found here. All door and entrance randomizer settings are supported. See their [readme](https://github.com/Aerinon/ALttPDoorRandomizer/blob/master/README.md) ## Overworld Layout Shuffle (--ow_shuffle) -OW Edge Transitions are shuffled to create new world layouts. A brief visual representation of this can be viewed [here](https://media.discordapp.net/attachments/783989090017738753/857299555183362078/ow-modes.gif). (This graphic also includes combinations of Crossed and Tile Flip) +OW Edge Transitions are shuffled to create new world layouts. A brief visual representation of this can be viewed [here](https://cdn.discordapp.com/attachments/783989090017738753/857299555183362078/ow-modes.gif?ex=66044c6d&is=65f1d76d&hm=b43ebd1b22e0a86c7caf3153c217ba14174bb92a2b168c15fe8e75364b5c8c15&). (This graphic also includes combinations of Crossed and Tile Flip) ### Vanilla @@ -197,7 +189,7 @@ Being that this uses concepts from Inverted, it will be important to review the During gameplay: - When on the OW, there will be an L or D in the upper left corner, indicating which world you are currently in. Mirroring still works the same, you must be in the DW to mirror to the LW. - - When doing a map check (pressing X while on the OW), the tiles shown will reflect the flipped tiles. This means that dungeon prizes will show the prizes for the dungeons that are now part of that world, beware of Desert/Mire and Eastern/PoD. Here is an image showing the difference of appearance when tiles are flipped on the [map check](https://media.discordapp.net/attachments/783989090017738753/970646558049714196/lttp-lw-mapcheck.gif) screen. + - When doing a map check (pressing X while on the OW), the tiles shown will reflect the flipped tiles. This means that dungeon prizes will show the prizes for the dungeons that are now part of that world, beware of Desert/Mire and Eastern/PoD. Here is an image showing the difference of appearance when tiles are flipped on the [map check](https://cdn.discordapp.com/attachments/783989090017738753/970646558049714196/lttp-lw-mapcheck.gif?ex=66015e8d&is=65eee98d&hm=b4d97c52d6aed593f0e6ec54924696ba969ce11109ce5ba1291b50a8a3e2dac8&) screen. Note: Tiles are put into Tile Groups (see `Terminology`) that must be shuffled together when certain settings are enabled. For instance, if ER is disabled, then any tiles that have a connector cave that leads to a different tile, then those tiles must flip together. @@ -256,7 +248,7 @@ This adds 42 new item locations to the game. These bonk locations are limited to - Some screens are coded to change the "alternate tree color", some of them are strange (just how the vanilla game does it) - Rocks and statues are unable to be made to have a different color -Here is a map that shows all the [Bonk Locations](https://cdn.discordapp.com/attachments/1105770688649895968/1105770806769877072/bonkdrops.png?width=1399&height=702). FYI, the numbers indicate how many bonk items there. The stars with a green square are all Bonk Locations that are unlocked after you kill Aga 1. +Here is a map that shows all the [Bonk Locations](https://cdn.discordapp.com/attachments/1105770688649895968/1105770806769877072/bonkdrops.png?ex=6603d650&is=65f16150&hm=3576367abd636ba7723ef30e87a4bc407c5e1eb9a8be325e90b1e22c04c58401&). FYI, the numbers indicate how many bonk items there. The stars with a green square are all Bonk Locations that are unlocked after you kill Aga 1. As far as map trackers, Bonk Locations are supported on `CodeTracker` when the Bonk Drops option is enabled. From c2280b439e74d7c9bfc85603d8b29f760a8a1aab Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 17 Mar 2024 05:17:57 -0500 Subject: [PATCH 146/158] Adding documentation regarding Swapped and District ER modes --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 96275e2d..afdd4201 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,20 @@ This mode groups entrances into types and shuffles them freely within those grou Both Lite and Lean ER modes are supported in `CodeTracker`, showing only the entrances that are shuffled. +### Swapped + +This mostly follows the rules of Crossed ER, but instead of entrances being randomly placed individually, random pairs of two entrances are chosen to swap with each other. This creates a unique experience where checking one entrance gives you information about another entrance, which can influence your future routing. + +If there is an odd amount of entrances being shuffled in the pool, the last remaining entrance will be vanilla. All other entrances are guaranteed to be non-vanilla. + +Here is a [Swapped ER Reference Sheet](http://zelda.codemann8.com/images/shared/swapped-reference-sheet.png) that shows some of the caves/houses that are less familiar to most people. + +### District + +This is an entrance shuffle that only shuffles entrances within their respective `Districts` (See Below). Also, dropdowns and the entrance that a dropdown normally exits are NOT kept together; this also means that the Skull Woods entrances are no longer guaranteed to lead to Skull Woods. Also, since there is no district that can span multiple worlds, this is NOT a cross-world entrance mode. + +Districts are a concept originally conceived by Aerinon in the Door Randomizer, where parts of the OW are split into areas and given a name. Here is a [District Map](https://cdn.discordapp.com/attachments/783989090017738753/1194615705027477534/districts.png?ex=66040e12&is=65f19912&hm=9ef382f004f7013e018f0b04d0bc98727f87b8b5da6499f34dbcf6e14bb0ac90&) showing how they are split up. + # Command Line Options ``` From 967bd799004daa2c497491dfbc0054231addf911 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 20 Mar 2024 17:48:43 -0500 Subject: [PATCH 147/158] Various GFX fixes and merge stability updates --- Rom.py | 2 +- asm/owrando.asm | 7 ++++--- data/base2current.bps | Bin 131690 -> 132084 bytes 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Rom.py b/Rom.py index cced7fc7..e2d04757 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '030a90f43980701de227341dd1a78a50' +RANDOMIZERBASEHASH = '001235f09be679140ef2f940b8b7f5c2' class JsonRom(object): diff --git a/asm/owrando.asm b/asm/owrando.asm index 4c8a5082..0283980e 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -306,9 +306,9 @@ OWFluteCancel: { lda.l OWFlags+1 : and.b #$01 : bne + jsl FluteMenu_LoadTransport : rtl - + lda.l HexToDecDigit4 : cmp.b #$01 : beq + + + lda.w RandoOverworldTargetEdge : bne + jsl FluteMenu_LoadTransport - + lda.b #$00 : sta.l HexToDecDigit4 : rtl + + stz.w RandoOverworldTargetEdge : rtl } OWFluteCancel2: { @@ -317,7 +317,7 @@ OWFluteCancel2: + inc.w SubModuleInterface lda.l OWFlags+1 : and.b #$01 : beq + lda.b Joy1B_All : cmp.b #$40 : bne + - lda.b #$01 : sta.l HexToDecDigit4 + lda.b #$01 : sta.w RandoOverworldTargetEdge + rtl } OWSmithAccept: @@ -587,6 +587,7 @@ OWBonkDrops: .increment_collection REP #$20 LDA.l TotalItemCounter : INC : STA.l TotalItemCounter + INC.w UpdateHUDFlag SEP #$20 + BRA .return diff --git a/data/base2current.bps b/data/base2current.bps index cfd411380a568d450c8baad2d3cede9f3e602b63..a79604a921dffd9601f65d56d194217c944fb08d 100644 GIT binary patch delta 14080 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 aY+%ulF^xu7!1@XZ9*VXFj!M1s+y4V!bZ!Cw delta 13773 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 From 67e4515bb707a5cf612c3f61b9c49eb616dda9da Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 20 Mar 2024 18:04:17 -0500 Subject: [PATCH 148/158] Fix corruption issue caused by bad collection rate address --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index e2d04757..6c796884 100644 --- a/Rom.py +++ b/Rom.py @@ -891,7 +891,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(cr_pc+0x1e, 0xEE) # slash rom.write_byte(cr_pc+0x1f, thousands_bot) # modify stat config - stat_address = 0x2397B2 # 0x23B969 - old + stat_address = 0x23978C stat_pc = snes_to_pc(stat_address) rom.write_byte(stat_pc, 0xa9) # change to pos 21 (from b1) rom.write_byte(stat_pc+2, 0xc0) # change to 12 bits (from a0) From db45906ba6f458d71c5d8742a89292b38df7fdb7 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 20 Mar 2024 18:04:45 -0500 Subject: [PATCH 149/158] Use new bombbag item gfx if bombbag mode --- Rom.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rom.py b/Rom.py index 6c796884..361729b8 100644 --- a/Rom.py +++ b/Rom.py @@ -1080,6 +1080,11 @@ def patch_rom(world, rom, player, team, is_mystery=False): 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel ]) + # item GFX changes + if world.bombbag[player]: + rom.write_byte(snes_to_pc(0x22C8A4), 0xE0) # use new bomb bag gfx + rom.write_byte(snes_to_pc(0x22BD52), 0x02) + # set Fountain bottle exchange items rom.write_byte(0x348FF, ItemFactory(world.bottle_refills[player][0], player).code) rom.write_byte(0x3493B, ItemFactory(world.bottle_refills[player][1], player).code) From f671c5ed52e09a53fabbfbfe0f0f08b2447d10a3 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 20 Mar 2024 18:05:40 -0500 Subject: [PATCH 150/158] Minor TF room text alignment --- Text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Text.py b/Text.py index d0011ff7..885af7cc 100644 --- a/Text.py +++ b/Text.py @@ -106,7 +106,7 @@ Triforce_texts = [ " Whelp…\n that just\n happened", " Oh hey…\n it's you", "\n Wheeeeee!!", - " Time for\n another one?", + " Time for\n another one?", " And\n\n scene", "\n GOT EM!!", "\n THE VALUUUE!!!", From 9eedbff0e58279d2bc7d0b2944ff74f237dbf601 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 21 Mar 2024 05:39:17 -0500 Subject: [PATCH 151/158] Fixed issue with GFX not transferring to VRAM in some cases --- Rom.py | 2 +- data/base2current.bps | Bin 132084 -> 132103 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 73e48a30..dcbafd93 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '001235f09be679140ef2f940b8b7f5c2' +RANDOMIZERBASEHASH = '0b3b7a09b48e024c8e48d6c6768ca59a' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index a79604a921dffd9601f65d56d194217c944fb08d..674039c4b876dab77cf246f1ca398f46085df897 100644 GIT binary patch delta 489 zcmVAeRJfm8k`(th|6EsjR(# zAGZx{m8V>8m8lH8fCH&Ky@d$9fRj;|2Z#Y#0cDqAhyfE!BfPK<@D9KO@CvOtz!+%) z0F61ozygpltvA3p=>hLPYo!~|1gQ^z0;wO33qot98kSWcpqdm()+Y^v4SSL@fHNwVx3h@> zYXk#KDWR7kjsY|SKrw^2SdIbb1qU`Ep`UGIgSSSM0XI+y6#=S=L^x!tPPc~S0dp1& fG+COSPXU#}OaYapgAM`rcLAT5dxilb0i2hfh5<+coR{Z@ z0dy)!qXDe&2bu8)lZlDi!Z5Q*QJkWN zjW@u+)~5nLpPfO#IOz}ojX|@Jfj^gmsXxG}HNb@k;vi%Pmp7Nihyf=78kg;e0ZR;L zUKN0m{Qv*|Lc3&_M2P`KH!lPYfVVCO4S=r!g5^lsq7I(%2g(ro`e Date: Fri, 22 Mar 2024 05:19:10 -0500 Subject: [PATCH 152/158] Fix SP key placement/removal issue in HMG --- ItemList.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ItemList.py b/ItemList.py index 526f14a0..263acba8 100644 --- a/ItemList.py +++ b/ItemList.py @@ -327,13 +327,6 @@ def generate_itempool(world, player): for _ in range(0, amt): pool.append('Rupees (20)') - if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: - # In HMG force swamp smalls in pots to allow getting out of swamp palace - placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' - placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' - pool.remove('Small Key (Swamp Palace)') - pool.remove('Small Key (Swamp Palace)') - start_inventory = list(world.precollected_items) for item in precollected_items: world.push_precollected(ItemFactory(item, player)) @@ -410,6 +403,13 @@ def generate_itempool(world, player): or (item.map and world.mapshuffle[player]) or (item.compass and world.compassshuffle[player]))]) + if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: + # In HMG force swamp smalls in pots to allow getting out of swamp palace + placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' + placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' + world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player)) + world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player)) + # logic has some branches where having 4 hearts is one possible requirement (of several alternatives) # rather than making all hearts/heart pieces progression items (which slows down generation considerably) # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode) From da749e4a11ff9dbe34874a0d6faf7d86df2edafb Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 22 Mar 2024 05:24:10 -0500 Subject: [PATCH 153/158] Fix generation issues in HMG --- Main.py | 8 ++++---- Rules.py | 2 +- UnderworldGlitchRules.py | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Main.py b/Main.py index 010a5798..1d32b778 100644 --- a/Main.py +++ b/Main.py @@ -598,10 +598,10 @@ def copy_world(world): update_world_regions(ret, player) if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'): create_owg_connections(ret, player) - if world.logic[player] in ('nologic', 'hybridglitches'): - create_hybridmajor_connections(ret, player) create_dynamic_exits(ret, player) create_dungeon_regions(ret, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(ret, player) create_owedges(ret, player) create_shops(ret, player) #create_doors(ret, player) @@ -795,10 +795,10 @@ def copy_world_premature(world, player): update_world_regions(ret, player) if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'): create_owg_connections(ret, player) - if world.logic[player] in ('nologic', 'hybridglitches'): - create_hybridmajor_connections(ret, player) create_dynamic_exits(ret, player) create_dungeon_regions(ret, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(ret, player) create_owedges(ret, player) create_shops(ret, player) create_doors(ret, player) diff --git a/Rules.py b/Rules.py index 8184c3e6..c7a9d706 100644 --- a/Rules.py +++ b/Rules.py @@ -846,7 +846,7 @@ def global_rules(world, player): add_key_logic_rules(world, player) - if world.logic[player] == 'hybridglitches': + if world.logic[player] == 'hybridglitches' and not world.is_copied_world: add_hmg_key_logic_rules(world, player) # End of door rando rules. diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index c01e242f..7510b138 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -66,6 +66,7 @@ def create_hybridmajor_connectors(world, player): specrock_connectors, ]: new_connectors = [(connector[0], connector[1], world.get_entrance(connector[2], player).connected_region) for connector in connectors] + new_connectors = [c for c in new_connectors if c[2] is not None] create_no_logic_connections(player, world, new_connectors) From 6d02d3da7a033ad6bef17d8429e7c7bba7cd6e3e Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 22 Mar 2024 06:35:10 -0500 Subject: [PATCH 154/158] Fix SP key placement/removal issue in HMG --- ItemList.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ItemList.py b/ItemList.py index 263acba8..16130fc9 100644 --- a/ItemList.py +++ b/ItemList.py @@ -405,8 +405,14 @@ def generate_itempool(world, player): if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: # In HMG force swamp smalls in pots to allow getting out of swamp palace - placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' - placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' + loc = world.get_location('Swamp Palace - Trench 1 Pot Key', player) + world.push_item(loc, ItemFactory('Small Key (Swamp Palace)', player), False) + loc.event = True + loc.locked = True + loc = world.get_location('Swamp Palace - Pot Row Pot Key', player) + world.push_item(loc, ItemFactory('Small Key (Swamp Palace)', player), False) + loc.event = True + loc.locked = True world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player)) world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player)) From 3ff5e3f6850f7dd3215d635f5fbb9c011cd4b8c0 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 24 Mar 2024 18:58:10 -0500 Subject: [PATCH 155/158] Changed bottle vendor to give key for fish prize in Universal keys --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index dcbafd93..9a638eb8 100644 --- a/Rom.py +++ b/Rom.py @@ -1403,7 +1403,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): # rupees replace arrows under pots for original and enemizer code rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) rom.write_byte(snes_to_pc(0x36837D), 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) - if world.bow_mode[player].startswith('retro'): + if world.bow_mode[player].startswith('retro') or world.keyshuffle[player] == 'universal': rom.write_byte(0x30052, 0xE4 if world.keyshuffle[player] == 'universal' else 0xDB) # replace arrows in fish prize from bottle merchant rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows From 9a7ea165e68dd35e5c76557f32f39974e46e71a9 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 25 Mar 2024 21:50:26 -0500 Subject: [PATCH 156/158] Added to SFX shuffle ban list --- source/classes/SFX.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/classes/SFX.py b/source/classes/SFX.py index 3a5012f6..1cc85f16 100644 --- a/source/classes/SFX.py +++ b/source/classes/SFX.py @@ -472,9 +472,9 @@ sfx_instrument_changes = [ SFXInstrumentChange(0x02, 0x17, 0x10, [0x1A96F5], ban=[0x00, 0x01, 0x06, 0x0C, 0x18]), SFXInstrumentChange(0x02, 0x18, 0x10, [0x1A9707], ban=[0x00, 0x01, 0x06, 0x0C, 0x18]), SFXInstrumentChange(0x02, 0x19, 0x10, [0x1A971F], ban=[0x00, 0x01, 0x06, 0x0C, 0x18]), - SFXInstrumentChange(0x02, 0x1A, 0x01, [0x1A96C7], type=Am, ban=[0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]), - SFXInstrumentChange(0x02, 0x1B, 0x11, [0x1A96B8], type=Am, ban=[0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]), - SFXInstrumentChange(0x02, 0x1C, 0x11, [0x1A96B2], type=Am, ban=[0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]), + SFXInstrumentChange(0x02, 0x1A, 0x01, [0x1A96C7], type=Am, ban=[0x00, 0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]), + SFXInstrumentChange(0x02, 0x1B, 0x11, [0x1A96B8], type=Am, ban=[0x00, 0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]), + SFXInstrumentChange(0x02, 0x1C, 0x11, [0x1A96B2], type=Am, ban=[0x00, 0x06, 0x13], inc=[0x08, 0x0F, 0x10, 0x12, 0x15, 0x17]), SFXInstrumentChange(0x02, 0x1D, 0x16, [0x1A966C], ban=[0x09, 0x10, 0x11, 0x13, 0x15, 0x18]), SFXInstrumentChange(0x02, 0x1E, 0x01, [0x1A9928], type=Lg, ban=[0x06]), SFXInstrumentChange(0x02, 0x1F, 0x02, [0x1A969A], type=Me|Be|Hd, ban=[0x09]), From 46565126fd0e129255cd24ca03768f2e93c7c32c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 25 Mar 2024 21:52:46 -0500 Subject: [PATCH 157/158] Various GFX updates to base ROM --- Rom.py | 2 +- asm/owrando.asm | 1 + data/base2current.bps | Bin 132103 -> 132072 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 9a638eb8..b6a39375 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '0b3b7a09b48e024c8e48d6c6768ca59a' +RANDOMIZERBASEHASH = '168c7d6e330a134e7565baedab79aa15' class JsonRom(object): diff --git a/asm/owrando.asm b/asm/owrando.asm index 0283980e..ee1afb53 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -447,6 +447,7 @@ OWBonkDropSparkle: LDA.w $0E90,X : BEQ .nosparkle LDA.w SprRedrawFlag,X : BNE .nosparkle LDA.b GameMode : CMP.b #$0E : BEQ .nosparkle + LDA.b LinkState : CMP.b #$08 : BCC + : CMP.b #$0A+1 : BCS + : BRA .nosparkle : + ; skip if we're mid-medallion JSL Sprite_SpawnSparkleGarnish ; move sparkle down 1 tile PHX : TYX : PLY diff --git a/data/base2current.bps b/data/base2current.bps index 674039c4b876dab77cf246f1ca398f46085df897..65e2169a50a1e09db4111c00824e8e0d6a6595b4 100644 GIT binary patch delta 11304 zcmX|n30PA{_je`0YV5PTp&QM zfd~OHp;03Uf_5{l*0i;?rLC=66;hY@D%#S8Z_@91{^4QHoH=*qPG-)W)YMA;n$xyaRiUg+1=w8f$)8~$iss>6d28Bf)gmPX>pQ#DaC zDm%BDL3XF0!}h5TTHJE@M_H~MU9-;yhmkAiQqUQTx>iQCK>9xO8QIVxJtn@)CP|5z zBqL!pk%=C0c7q3KId=xvX^^&LkP48fKA`i{9;nchf4clH`x3s7m~^>NWy$P+mDdhDINq>IZt%x zL(98{6p;84O>=Gnm(jP*@outS`k9zGI+g+%BAJL-i9B62AQttwtOK6N+cl1V?lwK3 zAOy9o7XRzjgu0}~Vp%Ct5c|KUEg513K^pR1&$Gb>gS%%R080&bymkXnW|%$xAOJn6 z%jY|=6_xlV;ibIpI~T|iYr;klkG4$s9C)BOzqH+9m+<12lknr#5YZnU*V_7P8mbB3 z8r)dF`+{uCklJFFUK~=Ree!a$_{>@24=f@Y>m*H4t4OuyUXdpx#kFKH8xv2wpu=6b zCl}Tc0Tr!!J4xOUp2akA|5r89gM$3yJSX0wYpV&z8rmF8tuE><`{kx|BHHBd6}+^Lu9aS@q0tUK z@;hCNwq4AJt!v1;GGclct(OrAi1LpaU(!RLCnZ;;o?p;Dq><<)SE@v`^gjB*KY~lt z&?f#f={JZQ5aVB^q4O;8rlN_6e^uO~kPu%Fsi_nX(JcirA1w;jd!prlvH&MgxY zam!%pMQJc`vbp#W*1$8;B(x~71*}3Z0y~|o)p)A)Q}7BpHKW#`NMc+K-6FMg(!a@w z)C)A9lo1cBX$DC)eo#!xLP;rBqE|uq{29FX5&&MH#^4O_3_S@Bb^f!OR=cpU_MO+@ z6(3LVK|M+$^89*xP{;yUORe1i&!FRZxR=s~1z+RjV#O;XyckrKLLHDqP})3_Sz z8v8PBWm{^|^n7>|@BLGC8YHD{q!d4T5)T!KK8>Qlzi2~rynWL(`lm)ILeVq9pXkeI z+s|V_pj+B30YiFRENLGv)E9# z>lg9)!$+coEapQk@dx5hoHV)S8=5+3SCS^BvNNO~Hxk}gXuUmjDOcojpR`!o6hs%K zEx$DqiSpL0)gN2R&(@;T6T^kuENaU>Q8h9Db9{KtE29GOZcD$QhpLG(BW*v;r!302wYWdo?30mTak zhipf1ZbCE2H9SrrnOJ+aCr%O?a7aNMtfTWjHb#rtP|TLeQ=x2u ziq4~DL|IXbk4Q#*UPs^UBffi}u5`>Q`GueUNMNy8(ntj{^^w|gm-LaHkr8cmG_`#< zl9?y-H6N3*S=dWvBJtc1A+4f2HXlHd&~Wf{wnRpR;FllXOgG zK{79)TtQ54p!1SSt8k3hBv$lvqBtKDGELl&U<(4Rj)wt6w8Bt!{ z>LeBugVdx8q~&Hc(WRoHTcoJj&B(r}Aa0?Ud08M3eK3yznFi&&S0I2pKtCg)8+I74 z-?U&S3Z3w#$Di)V+dOCIqUG=u?_h* zJvemiOWHz{$_7%I3m@c`-=;04i`yUa3482^ESCHw`RRlV&0p9G<{N%r_*EpRMSc4M ze4iRlKHH0RKc;6EwaauItVYEIJq2I=%# zx=n$XzLw6#8}3vhBchP$Q22OWEuLfp*;q+R?<(0bKMzQWUntas><&)^y(sZ;5;%=^ z9i9QsqjQI2Km+>v@Ywh$1AT^s*ps_9H2WQBIQu(sdRR?p(9n@&hx1g#`*d27^t>VV z=u!Z#7*2ff6aYQia{L6ih<-Uf0_LM{Pqcx3sPLpOK&bX)BrdO?YzNoTu2cJ5uwSDb zeVQA{rb;o4en!rH^ysRWxNaDIS5bv~?vNjJAFePZw?@mz1&`K? zu1=K7hS78RRiVGal-N28k;@;6u1>F)VV2XHQcvdmAG_AZuE1=>e^UF_xO#FWW+(l} zMzto@lkZ{H_doW0E5Dvx^?&Sr8~Z+Hz2vr$D_i~LR1{q;M%BO8moDeiQ%t2V%1Q*U+d-H+t8 z5!mJ%YlFZPbgy>moW1V1cEkRtM!L~5u75Tr(H{NFG1=+SzZH{qkN#senMc3OCiCn+ zYm<5QU$V(O`%NgbZaUL*5h|+N6X&u2rZi?!EMm5N=rgfGJSol)yX2tn`eS=3r5$k#1^da09{txbhYZC=L#w7GsFGb9=*>OGwP zRMB?MbqvPPkTL`5>j~zhNOZS;iC6T@o81u3e#qB}q22`bi8_R~plQl^Ns(tw`HV_d zmgKC7XQNNfnt+WiQ0Mcg_n0UgP3(>@m_S!@yVQJEh3m zshQR-&lZ}-%yvZZh*+RzWWM1wqdg4=quN^@@@r1Up1WRqCofxwhiz}^2PQp!6!|o0 z87BRm>};WxmDPlJjq({&M`R|mIY6~}gbLYQ{HK|zU1SQ! zQ6sc^O8I8D!RGR6D{B;TMwL574p5-mk}ZtlSSOf(nU&RZ&cwuI?3{^(iK5lSHiNvH zBjd(3Rl8Z8`s&XiM_Ki+Zk2EP+7Y1&1v}6}Rh)2J(vynBdMOawQ-al>+|K7{CPZGe}mAIj1fGniDyp1$f-JpBIF)^d_; z^%1Qg$BUY__&rcnHoJ((R2_>Hv)WpVNRL(*as}y3)~pU~Xf1DMlX5#!ycWGw@n#sb zR@QMi<#>I}jMK9re|0>Om8T{Ul`pw&U+W^&TA_$Se1 zdZ{pGaZ%Nd$Y;{5cCC`Xu84G3?mc0WjjhNQ)+BIBI|sFl-%A%SZtvfbEmY0hf0Hb2 z^?%!F=Juz)(94&}A4_v^dn}3G+ws`c|6aCmf(jQ}hJ0DJ(AJHbwfFIDbA&EGTWEIF zZOj%17_@Rs20KE_ZcnhXRcMUf2uje#vqxrf(tk7f9Jzl9@7>_qQu(HAp_)N6C`L|^ z7Vn82|8hHIo0uvekp}M<=8lhcFU(DM7iSawb!1MM-3EenS&WR{Id1}enBUS{9^%(f@aRI zPF5EG*7=)5_>*_$eC>Jq{Q2|x^HTZqz|DXrW6GPT^=62?FGR7DXjxqA` zDl5mv#s=4Bi0w6e(`G%d8BmMRq?SP5%%CE&1G$|+%g~yZWx_r0S(K~#$Z?NEMq#IN zZ^b7W*m>DpU4ft%SAMRc1yw=gVcBab|KkhF6kpiM@3>&|#Moa6GN=XqP`uzma}mjD zb_;;Y1wpV6_vin7q0(%yR3eo!eETY2g! zJAWnJwdR1&P+Hp@Z~zsxg-v$4Mt6S9z7q`fR^|BB;w}@%>PG}#qXpVT z@l9&hZKf)u;&7$YiJiddWU-~tY*&!`zd8y3-IdC> z1-UQkB!g@7s}6+1Z&_CR)@ZcTx8m2ddL-JRc4 z^H2SI32O&qKrMv~>-~q5Mycpi|8|-^Y+VCgY%g;_Gv`400z6Y6LEmC0Qhol!iGpkA)O=DK~whUdMGUfa+m_MA0(IVZ~e~INMUKa}b_%5arvjcyi z+r+K}GX?ws^ym2?5RH60C$i__3rbCL4_H~ZOAXQ0c1jfVkoio74$kG{* z7{DGrD~kgU-C(pXoSG*XQs|Mmb|L^ zDa*>1&q7j_){3P>D;RHD@h?ZVjAm97Ca8S7ml6&Q%vbH+ne|7X)+6Tp7krE$_ z${WbhM|nmWj(I0FyHA9-!!>PDEQX26F#GoZg0Z|cWd8q7y=@( zF~OZ>)T%3T8ZC6hnzywta#WBpi2Ob$Q_#K~#MdX`oPLeI#x64lQnBcbex+YauT6ue zW(%o3kctTTq0w2FY@2B^6^*uZ#rU@L*2HIa%J)f-hX+LVZt(U5`F#8>zBROVaR3NI zx(|KAJk~Ox^g}T-1iGzb;FDgHEu1-1F00n6#0;${88V?X%L_gGZ~|xd1_sO&9YD^8 zU=WPr4XfFkZ)Sy|GQ$esg?=$S!Hu6@8eSrncHzZa~fR%{lx&O_HP zhw}E{7TA_>E`L9Ic{vCyN3O@X%7<0A!G&}3!juI*F)dW4~8=URJ}Hb&`s>P>Y< zdw;4(PmTY32YC>c7`K5e^wOA$O?1MQ2(TNizOvH)dT!qprI>N9w0~budlX<;eW`Sh z^I@bX!|aM~U5OJ8YD(LqQq3+lxtTj!(C{f+%Mgqfs9iqW4Fx7UtEVZ}>SkJNbr+!pObo?t)ZJp4DvgVE`-3H2UOTyVGkT-jx6%jyxbfXaD)q$#zy zb7-iAk@|2gk$fvW7tX(4w)i9Snx7DNqYg-fkr94Vu&2A^NHZKOWy%892cfzw6-0;wfXo-2Bs^b$*PZ(UPh;mclJZ3=|Fw)YKmB$MPd^mDJj{l^^>MdS@M)|(4{ zN4b6GfSY#(x?4Efa+60v84hV(C?#@ONJ@9VE<_Bp=-N`S6V+bZIwj}_y6VR+Gv`?X zFf-M!bET(60vO@@hm0uvfmW`ZH6qiv%bay{lSfojCj237xXD%u(4_th;Ej&=`}wSS zWAe}f$ksYQ#_dVK%92e44HMA})Y%^k7Nf!bkR0wTs4$IZm>rZ0{QlSvy%+)_qn{>b zO2`{-=jeXMPxM1KqCo<)2kGU5^sj^T-XH0If27BLqy;v!ij67|+(=O8`fAPG8ds+1 zvskojVByjQ?7#j>WB*0+9HB^gNc7ENQO&f}$9^y@sV)buIQW#bdo9TRdM3n1WIhaa zdEOrpek+~Kop|li0ME?9{>e1X%Jj+0tKS~b|5YmbZ6E@qBB$%o>=Q0zI+}U?FbF~V z>s!D&m)r@d{4*-WRMlWPV@GO_&&gYy@ifeLGl{t{TRb;sL(?3k^opLdQaTFGzd%pxG{a#p1?MOL2o-9Z?T{C_? z^>PVD>0(GxglIKvT^Dozv$Zkz<=q_80oFZcSQw#<&B^4l(<+4_`qxAjc3~SHg@Z{* z{D|+lMbn*^F{n#;p|{Sop&r&Ls=cWDQ3TFS9y}V4QS?6_`Qg&*xAouQC-R7GAx=@b)vm(FKG6r-XpclWzf{DoQaWJq$vmZaQo3mO%MWC=J&s`!m zNE(0DoqCP!^{I|ki2pP_EF@pj81bgiXNQ|SqBUikMCz)Gq>9VxWyCK(sZ~lcNG?K& zsO)JRwqQL^4H%-@GbCglPc^(8@&kZ@94+zSs3F<181Pp1(^?=KW)dSv{`(6MhYFv? zE#A9T(ybM!QaOeEd-wSM_xa23H?Qfev$q8`4-=@Foj8_WG#CC^27k0Umc7|R9S)Do zcAc>tt6f7Zry#EY=c1nd-tUn0*(BzHuTjYJXzxuI8ksQu24Blyl{yBK+O`#voxaeJp_F`yIX z^i(9;^m4mXn7vfY)1t(mIh!QVEqh&P^&eEaqK~a1OI+-~!6~h%hAWo0mX?wN zWdyH8rY#gZqZ6g%UPO`Jl<)K}mOvf`fBP5%Qea)pfAvVeg1_lE{nGysK1Y7)V}9>% zcm;>hlvhcPfjhrY4B}WOpW1_tzRCof(MPYs@F5<4W!bbWu!JP2J(vG6GodT5{p7C4 zM;?=pD;{?|zVcX?V~ay@F)Gl7QT8zsGEB9(7uorjkYPAu50i`Jn@>~B@g-z9<Zm>%vYJOO?OsrAEt^F_~D! zSuCYjNQr#;oBElHenN!{O1h&47^VS+72nul)CODXRY?DO9tL<{zuv?I-=S4+GO#IC zy_xJ1dxs{jpQed7knRvBp)k;Qb5S%kls+{$sxIm!F^oP=dA$5N?zH2*rmLVTQo3cx) zfi2}PP)w2xvg}wa}y4xPn3IZ3$-UMQMgb{d=)=u|*vMC!ytUy}=Pw@HWaV zbKPTU91tiEUHmE@2(%sO?A!6+9_oHO5u5j?Z$m=8Vg~VR24$zm{u-lBEvS{A9_v>I zrq@zfp!jI4e@xPmj^f^JaakB^59@SesxjS|QR#zJ@8;XFjE#Ah@&eReP?f9f6^MDeDFmCA^k$56T4$g>0cm|hXS6$(c|BkfpKRu`aa#u10- zokf(K16KI)SN+_HQE{%Wn$*h9+fuk zA@$;{(cJ6QTrL>m&CZa>?$nTaDv3&T06*D1%d)RrQ?5>6IXFda zasmX{NfkMP7_f@cJAqN)L3KC-U%TZ~DyUS-qNO0b}0*Vj3q2jl^ z{U`SVmFo(Ez-H>CE7%!w#H9o+5|)r$Ro)D#Dx6tVP24J?brL05Dw8EETOOi)l!F_X zoECkN#yLyP?B$Jpv!zf#oVrHmetwT8@ITrpfY0r;o?>?a#>vK+NAleT|H;mZY1@>! zh=G5FP>Hyd&<(@|v6+&y_7`Z^rc|b&jB<4@sn)45ChA57wl9K&l)8mq=j6Wer}ZRP z6K`b`kBmHbkP1L1l{pS90O^!s94PVHd%{V5Ub(UIock zio&4%a-o*V5VMJ7iW>lOJcNOAaj&Ibh{UCf<+Wn56SbGx9RNa^-jgX502YE&>c;@^ zJBJ*kR!;b`i~2GU1hNul$*7lsVD*$)>n!J{&Tb|9k;5~E941)f{#wYZYW7aAta3kE zP72hkc2iV#Jb9LdH3lO-^98Np)QKRF0;U*yf9s!aP^#%KR{f@;0Jq@W_kfS6D&5Q7$!}N(DQac1vQ4JHn06<^h7ubXrWgEd&&E z=PILiAvnV3Z$DKmvtw8tRH?g*f3SbZZ!$aQJ>+ZL&HmaN8W37)P8K*}cQWpGJj&tIv`Z}E*Y>i@C zsU1}75%9ay^upR|2My2U#-J8cO-Dfo(7zYqc)sNF))jZIGk#Kt9>+6uwbfa}gW928%0V$T_kQ{K?&Db9I{6%o z*uEG>DNx!l%%mcGp)8|6kAt?0t8F)-Zhh3wAgG$^4=JJuBn3@QhpP8BOR5Vn+|A2q z-7}$BgReUa1Ud}kWT0H?SP_VDSl5TIAd46s?^D_$Fewxxy%^&&>8#I8q1K^M!l01U z8s<(aQ54J+(Da2336)fm@!uk>=I*ltE454Rt1&pO1&x&)J(b%^NmXdcmej4u90!%Qf-v6m|mK_G-0VeRiB=^v}TXX z1(L5^9`cUl*DqP4Y~47wqC3OHFmp8SR#rih0MDbwVWI4KrH$ct^cD=Zi#a+R>04R2 zV!JW6dKtGB{9AW}FVue913ipI4iLxopY({ina7KGbYa=r4%)@Yt(!Vv3yA49%=bY~ zP2h>rn)Q|MkpoVZ+}fmJCq^6e?$-3czybUJ8tid{U3XUxbk^7}uC(hm_CQv5U(aIv zi5mN>BSSW001FEAGjR69kj))1y;;MsT_!NF00WQ8osN=5XN z+F$)op{^e9D4>dec*CJLPZ&e~BdHiZkFr zm|c)_ADU~N(F5YR4u`h2Nr^EZWsGs(4X}W1=eVW2d5f{#*glRnZVUyQqS2(W3R`a;=eo1`IOUF;Gk3Z)ce(AWB10;7?o-)`wG~3IIrJt zj|2Fm{0{HE_Ry&O4U{|Etw}FdMH*xO2cp@)lY0Lx*bBBBKYNQO>_&ur)wB#$-^c>h vSDhz%K%?JZz?JDbC1vFDnQBHVBaKm_Jh0L@;~(&;J@;dj82vV4#nt}@(?8zI delta 11224 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 From f3dc2b881e7f05d5c0452f34a4a21ef315182bcd Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 25 Mar 2024 22:06:36 -0500 Subject: [PATCH 158/158] Version bump 0.4.0.0 --- CHANGELOG.md | 5 +++++ OverworldShuffle.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7044b65d..be04e335 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.0.0 +- Fully merged big base ROM changes from upstream, including FastROM +- \~Merged up to date with DR v1.4.1.8~ +- Various GFX re-implementation including new GFX for bee traps, bomb bags, etc. + ## 0.3.4.2 - Added Shuffle SFX Instruments as post-gen option - Fixed some issues with Swapped ER failing to place Old Man Cave diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 3c845a24..eaa6d98a 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType from OverworldGlitchRules import create_owg_connections from Utils import bidict -version_number = '0.3.4.2' +version_number = '0.4.0.0' # branch indicator is intentionally different across branches version_branch = '-u'