from enum import Enum import re import copy from typing import List from .Config import Config, SMLogic class ItemType(Enum): Nothing = 0 MapHC = 0x7F MapEP = 0x7D MapDP = 0x7C MapTH = 0x75 MapPD = 0x79 MapSP = 0x7A MapSW = 0x77 MapTT = 0x74 MapIP = 0x76 MapMM = 0x78 MapTR = 0x73 MapGT = 0x72 CompassEP = 0x8D CompassDP = 0x8C CompassTH = 0x85 CompassPD = 0x89 CompassSP = 0x8A CompassSW = 0x87 CompassTT = 0x84 CompassIP = 0x86 CompassMM = 0x88 CompassTR = 0x83 CompassGT = 0x82 BigKeyEP = 0x9D BigKeyDP = 0x9C BigKeyTH = 0x95 BigKeyPD = 0x99 BigKeySP = 0x9A BigKeySW = 0x97 BigKeyTT = 0x94 BigKeyIP = 0x96 BigKeyMM = 0x98 BigKeyTR = 0x93 BigKeyGT = 0x92 KeyHC = 0xA0 KeyCT = 0xA4 KeyDP = 0xA3 KeyTH = 0xAA KeyPD = 0xA6 KeySP = 0xA5 KeySW = 0xA8 KeyTT = 0xAB KeyIP = 0xA9 KeyMM = 0xA7 KeyTR = 0xAC KeyGT = 0xAD Key = 0x24 Compass = 0x25 BigKey = 0x32 Map = 0x33 Something = 0x6B ProgressiveTunic = 0x60 ProgressiveShield = 0x5F ProgressiveSword = 0x5E Bow = 0x0B SilverArrows = 0x58 BlueBoomerang = 0x0C RedBoomerang = 0x2A Hookshot = 0x0A Mushroom = 0x29 Powder = 0x0D Firerod = 0x07 Icerod = 0x08 Bombos = 0x0f Ether = 0x10 Quake = 0x11 Lamp = 0x12 Hammer = 0x09 Shovel = 0x13 Flute = 0x14 Bugnet = 0x21 Book = 0x1D Bottle = 0x16 Somaria = 0x15 Byrna = 0x18 Cape = 0x19 Mirror = 0x1A Boots = 0x4B ProgressiveGlove = 0x61 Flippers = 0x1E MoonPearl = 0x1F HalfMagic = 0x4E HeartPiece = 0x17 HeartContainer = 0x3E HeartContainerRefill = 0x3F ThreeBombs = 0x28 Arrow = 0x43 TenArrows = 0x44 OneRupee = 0x34 FiveRupees = 0x35 TwentyRupees = 0x36 TwentyRupees2 = 0x47 FiftyRupees = 0x41 OneHundredRupees = 0x40 ThreeHundredRupees = 0x46 BombUpgrade5 = 0x51 BombUpgrade10 = 0x52 ArrowUpgrade5 = 0x53 ArrowUpgrade10 = 0x54 CardCrateriaL1 = 0xD0 CardCrateriaL2 = 0xD1 CardCrateriaBoss = 0xD2 CardBrinstarL1 = 0xD3 CardBrinstarL2 = 0xD4 CardBrinstarBoss = 0xD5 CardNorfairL1 = 0xD6 CardNorfairL2 = 0xD7 CardNorfairBoss = 0xD8 CardMaridiaL1 = 0xD9 CardMaridiaL2 = 0xDA CardMaridiaBoss = 0xDB CardWreckedShipL1 = 0xDC CardWreckedShipBoss = 0xDD CardLowerNorfairL1 = 0xDE CardLowerNorfairBoss = 0xDF SmMapBrinstar = 0xCA SmMapWreckedShip = 0xCB SmMapMaridia = 0xCC SmMapLowerNorfair = 0xCD Missile = 0xC2 Super = 0xC3 PowerBomb = 0xC4 Grapple = 0xB0 XRay = 0xB1 ETank = 0xC0 ReserveTank = 0xC1 Charge = 0xBB Ice = 0xBC Wave = 0xBD Spazer = 0xBE Plasma = 0xBF Varia = 0xB2 Gravity = 0xB6 Morph = 0xB4 Bombs = 0xB9 SpringBall = 0xB3 ScrewAttack = 0xB5 HiJump = 0xB7 SpaceJump = 0xB8 SpeedBooster = 0xBA BottleWithRedPotion = 0x2B BottleWithGreenPotion = 0x2C BottleWithBluePotion = 0x2D BottleWithFairy = 0x3D BottleWithBee = 0x3C BottleWithGoldBee = 0x48 RedContent = 0x2E GreenContent = 0x2F BlueContent = 0x30 BeeContent = 0x0E class Item: Name: str Type: ItemType Progression: bool dungeon = re.compile("^(BigKey|Key|Map|Compass)") bigKey = re.compile("^BigKey") key = re.compile("^Key") map = re.compile("^Map") compass = re.compile("^Compass") keycard = re.compile("^Card") smMap = re.compile("^SmMap") def IsNameDungeonItem(item_name): return Item.dungeon.match(item_name) def IsDungeonItem(self): return self.dungeon.match(self.Type.name) def IsBigKey(self): return self.bigKey.match(self.Type.name) def IsKey(self): return self.key.match(self.Type.name) def IsMap(self): return self.map.match(self.Type.name) def IsCompass(self): return self.compass.match(self.Type.name) def IsKeycard(self): return self.keycard.match(self.Type.name) def IsSmMap(self): return self.smMap.match(self.Type.name) def Is(self, type: ItemType, world): return self.Type == type and self.World == world def IsNot(self, type: ItemType, world): return not self.Is(type, world) def __init__(self, itemType: ItemType, world = None): self.Type = itemType self.World = world self.Progression = False #self.Name = itemType.GetDescription() @staticmethod def Nothing(world): return Item(ItemType.Nothing, world) @staticmethod def AddRange(itemPool, count, item): for i in range(count): itemPool.append(copy.copy(item)) @staticmethod def CreateProgressionPool(world): itemPool = [ Item(ItemType.ProgressiveShield), Item(ItemType.ProgressiveShield), Item(ItemType.ProgressiveShield), Item(ItemType.ProgressiveSword), Item(ItemType.ProgressiveSword), Item(ItemType.Bow), Item(ItemType.Hookshot), Item(ItemType.Mushroom), Item(ItemType.Powder), Item(ItemType.Firerod), Item(ItemType.Icerod), Item(ItemType.Bombos), Item(ItemType.Ether), Item(ItemType.Quake), Item(ItemType.Lamp), Item(ItemType.Hammer), Item(ItemType.Shovel), Item(ItemType.Flute), Item(ItemType.Bugnet), Item(ItemType.Book), Item(ItemType.Bottle), Item(ItemType.Somaria), Item(ItemType.Byrna), Item(ItemType.Cape), Item(ItemType.Mirror), Item(ItemType.Boots), Item(ItemType.ProgressiveGlove), Item(ItemType.ProgressiveGlove), Item(ItemType.Flippers), Item(ItemType.MoonPearl), Item(ItemType.HalfMagic), Item(ItemType.Grapple), Item(ItemType.Charge), Item(ItemType.Ice), Item(ItemType.Wave), Item(ItemType.Plasma), Item(ItemType.Varia), Item(ItemType.Gravity), Item(ItemType.Morph), Item(ItemType.Bombs), Item(ItemType.SpringBall), Item(ItemType.ScrewAttack), Item(ItemType.HiJump), Item(ItemType.SpaceJump), Item(ItemType.SpeedBooster), Item(ItemType.Missile), Item(ItemType.Super), Item(ItemType.PowerBomb), Item(ItemType.PowerBomb), Item(ItemType.ETank), Item(ItemType.ETank), Item(ItemType.ETank), Item(ItemType.ETank), Item(ItemType.ETank), Item(ItemType.ReserveTank), Item(ItemType.ReserveTank), Item(ItemType.ReserveTank), Item(ItemType.ReserveTank), ] for item in itemPool: item.Progression = True item.World = world return itemPool @staticmethod def CreateNicePool(world): itemPool = [ Item(ItemType.ProgressiveTunic), Item(ItemType.ProgressiveTunic), Item(ItemType.ProgressiveSword), Item(ItemType.ProgressiveSword), Item(ItemType.SilverArrows), Item(ItemType.BlueBoomerang), Item(ItemType.RedBoomerang), Item(ItemType.Bottle), Item(ItemType.Bottle), Item(ItemType.Bottle), Item(ItemType.HeartContainerRefill), Item(ItemType.Spazer), Item(ItemType.XRay), ] Item.AddRange(itemPool, 10, Item(ItemType.HeartContainer, world)) for item in itemPool: item.World = world return itemPool @staticmethod def CreateJunkPool(world): itemPool = [ Item(ItemType.Arrow), Item(ItemType.OneHundredRupees) ] Item.AddRange(itemPool, 24, Item(ItemType.HeartPiece)) Item.AddRange(itemPool, 8, Item(ItemType.TenArrows)) Item.AddRange(itemPool, 13, Item(ItemType.ThreeBombs)) Item.AddRange(itemPool, 4, Item(ItemType.ArrowUpgrade5)) Item.AddRange(itemPool, 4, Item(ItemType.BombUpgrade5)) Item.AddRange(itemPool, 2, Item(ItemType.OneRupee)) Item.AddRange(itemPool, 4, Item(ItemType.FiveRupees)) Item.AddRange(itemPool, 21 if world.Config.Keysanity else 28, Item(ItemType.TwentyRupees)) Item.AddRange(itemPool, 7, Item(ItemType.FiftyRupees)) Item.AddRange(itemPool, 5, Item(ItemType.ThreeHundredRupees)) Item.AddRange(itemPool, 9, Item(ItemType.ETank)) Item.AddRange(itemPool, 39, Item(ItemType.Missile)) Item.AddRange(itemPool, 15, Item(ItemType.Super)) Item.AddRange(itemPool, 8, Item(ItemType.PowerBomb)) for item in itemPool: item.World = world return itemPool # The order of the dungeon pool is significant @staticmethod def CreateDungeonPool(world): itemPool = [Item(ItemType.BigKeyGT)] Item.AddRange(itemPool, 4, Item(ItemType.KeyGT)) if (not world.Config.Keysanity): itemPool += [ Item(ItemType.MapGT), Item(ItemType.CompassGT), ] itemPool += [ Item(ItemType.BigKeyEP), Item(ItemType.BigKeyDP), Item(ItemType.BigKeyTH), Item(ItemType.BigKeyPD), Item(ItemType.BigKeySP), Item(ItemType.BigKeySW), Item(ItemType.BigKeyTT), Item(ItemType.BigKeyIP), Item(ItemType.BigKeyMM), Item(ItemType.BigKeyTR), ] Item.AddRange(itemPool, 1, Item(ItemType.KeyHC)) Item.AddRange(itemPool, 2, Item(ItemType.KeyCT)) Item.AddRange(itemPool, 1, Item(ItemType.KeyDP)) Item.AddRange(itemPool, 1, Item(ItemType.KeyTH)) Item.AddRange(itemPool, 6, Item(ItemType.KeyPD)) Item.AddRange(itemPool, 1, Item(ItemType.KeySP)) Item.AddRange(itemPool, 3, Item(ItemType.KeySW)) Item.AddRange(itemPool, 1, Item(ItemType.KeyTT)) Item.AddRange(itemPool, 2, Item(ItemType.KeyIP)) Item.AddRange(itemPool, 3, Item(ItemType.KeyMM)) Item.AddRange(itemPool, 4, Item(ItemType.KeyTR)) itemPool += [ Item(ItemType.MapEP), Item(ItemType.MapDP), Item(ItemType.MapTH), Item(ItemType.MapPD), Item(ItemType.MapSP), Item(ItemType.MapSW), Item(ItemType.MapTT), Item(ItemType.MapIP), Item(ItemType.MapMM), Item(ItemType.MapTR), ] if (not world.Config.Keysanity): itemPool += [ Item(ItemType.MapHC), Item(ItemType.CompassEP), Item(ItemType.CompassDP), Item(ItemType.CompassTH), Item(ItemType.CompassPD), Item(ItemType.CompassSP), Item(ItemType.CompassSW), Item(ItemType.CompassTT), Item(ItemType.CompassIP), Item(ItemType.CompassMM), Item(ItemType.CompassTR), ] for item in itemPool: item.World = world return itemPool @staticmethod def CreateKeycards(world): itemPool = [ Item(ItemType.CardCrateriaL1, world), Item(ItemType.CardCrateriaL2, world), Item(ItemType.CardCrateriaBoss, world), Item(ItemType.CardBrinstarL1, world), Item(ItemType.CardBrinstarL2, world), Item(ItemType.CardBrinstarBoss, world), Item(ItemType.CardNorfairL1, world), Item(ItemType.CardNorfairL2, world), Item(ItemType.CardNorfairBoss, world), Item(ItemType.CardMaridiaL1, world), Item(ItemType.CardMaridiaL2, world), Item(ItemType.CardMaridiaBoss, world), Item(ItemType.CardWreckedShipL1, world), Item(ItemType.CardWreckedShipBoss, world), Item(ItemType.CardLowerNorfairL1, world), Item(ItemType.CardLowerNorfairBoss, world), ] for item in itemPool: item.Progression = True item.World = world return itemPool @staticmethod def CreateSmMaps(world): itemPool = [ Item(ItemType.SmMapBrinstar, world), Item(ItemType.SmMapWreckedShip, world), Item(ItemType.SmMapMaridia, world), Item(ItemType.SmMapLowerNorfair, world) ] for item in itemPool: item.Progression = True item.World = world return itemPool @staticmethod def Get(items, itemType:ItemType): item = next((i for i in items if i.Type == itemType), None) if (item == None): raise Exception(f"Could not find an item of type {itemType}") return item @staticmethod def Get(items, itemType:ItemType, world): item = next((i for i in items if i.Is(itemType, world)), None) if (item == None): raise Exception(f"Could not find an item of type {itemType} in world {world.Id}") return item class Progression: BigKeyEP: bool BigKeyDP: bool BigKeyTH: bool BigKeyPD: bool BigKeySP: bool BigKeySW: bool BigKeyTT: bool BigKeyIP: bool BigKeyMM: bool BigKeyTR: bool BigKeyGT: bool KeyHC: bool KeyDP: bool KeyTH: bool KeySP: bool KeyTT: bool KeyCT: bool KeyPD: bool KeySW: bool KeyIP: bool KeyMM: bool KeyTR: bool KeyGT: bool CardCrateriaL1: bool CardCrateriaL2: bool CardCrateriaBoss: bool CardBrinstarL1: bool CardBrinstarL2: bool CardBrinstarBoss: bool CardNorfairL1: bool CardNorfairL2: bool CardNorfairBoss: bool CardMaridiaL1: bool CardMaridiaL2: bool CardMaridiaBoss: bool CardWreckedShipL1: bool CardWreckedShipBoss: bool CardLowerNorfairL1: bool CardLowerNorfairBoss: bool def CanBlockLasers(self): return self.shield >= 3 Sword: bool MasterSword: bool Bow: bool Hookshot: bool Mushroom: bool Powder: bool Firerod: bool Icerod: bool Bombos: bool Ether: bool Quake: bool Lamp: bool Hammer: bool Shovel: bool Flute: bool Book: bool Bottle: bool Somaria: bool Byrna: bool Cape: bool Mirror: bool Boots: bool Glove: bool Mitt: bool Flippers: bool MoonPearl: bool HalfMagic: bool Grapple: bool Charge: bool Ice: bool Wave: bool Plasma: bool Varia: bool Gravity: bool Morph: bool Bombs: bool SpringBall: bool ScrewAttack: bool HiJump: bool SpaceJump: bool SpeedBooster: bool Missile: bool Super: bool PowerBomb: bool TwoPowerBombs: bool ETank: int ReserveTank: int shield: int itemMapping = [ ItemType.BigKeyEP, ItemType.BigKeyDP, ItemType.BigKeyTH, ItemType.BigKeyPD, ItemType.BigKeySP, ItemType.BigKeySW, ItemType.BigKeyTT, ItemType.BigKeyIP, ItemType.BigKeyMM, ItemType.BigKeyTR, ItemType.BigKeyGT, ItemType.KeyHC, ItemType.KeyDP, ItemType.KeyTH, ItemType.KeySP, ItemType.KeyTT, ItemType.CardCrateriaL1, ItemType.CardCrateriaL2, ItemType.CardCrateriaBoss, ItemType.CardBrinstarL1, ItemType.CardBrinstarL2, ItemType.CardBrinstarBoss, ItemType.CardNorfairL1, ItemType.CardNorfairL2, ItemType.CardNorfairBoss, ItemType.CardMaridiaL1, ItemType.CardMaridiaL2, ItemType.CardMaridiaBoss, ItemType.CardWreckedShipL1, ItemType.CardWreckedShipBoss, ItemType.CardLowerNorfairL1, ItemType.CardLowerNorfairBoss, ItemType.Bow, ItemType.Hookshot, ItemType.Mushroom, ItemType.Powder, ItemType.Firerod, ItemType.Icerod, ItemType.Bombos, ItemType.Ether, ItemType.Quake, ItemType.Lamp, ItemType.Hammer, ItemType.Shovel, ItemType.Flute, ItemType.Book, ItemType.Bottle, ItemType.Somaria, ItemType.Byrna, ItemType.Cape, ItemType.Mirror, ItemType.Boots, ItemType.Flippers, ItemType.MoonPearl, ItemType.HalfMagic, ItemType.Grapple, ItemType.Charge, ItemType.Ice, ItemType.Wave, ItemType.Plasma, ItemType.Varia, ItemType.Gravity, ItemType.Morph, ItemType.Bombs, ItemType.SpringBall, ItemType.ScrewAttack, ItemType.HiJump, ItemType.SpaceJump, ItemType.SpeedBooster, ItemType.Missile, ItemType.Super, ] def __init__(self, items): for item in Progression.itemMapping: setattr(self, item.name, False) self.KeyCT = 0 self.KeyPD = 0 self.KeySW = 0 self.KeyIP = 0 self.KeyMM = 0 self.KeyTR = 0 self.KeyGT = 0 self.ETank = 0 self.ReserveTank = 0 self.shield = 0 self.MasterSword = False self.Sword = False self.Mitt = False self.Glove = False self.TwoPowerBombs = False self.PowerBomb = False self.Add(items) def Add(self, items:List[Item]): for item in items: found = item.Type in Progression.itemMapping if found: setattr(self, item.Type.name, True) continue if (item.Type == ItemType.KeyCT): self.KeyCT += 1 elif (item.Type == ItemType.KeyPD): self.KeyPD += 1 elif (item.Type == ItemType.KeySW): self.KeySW += 1 elif (item.Type == ItemType.KeyIP): self.KeyIP += 1 elif (item.Type == ItemType.KeyMM): self.KeyMM += 1 elif (item.Type == ItemType.KeyTR): self.KeyTR += 1 elif (item.Type == ItemType.KeyGT): self.KeyGT += 1 elif (item.Type == ItemType.ETank): self.ETank += 1 elif (item.Type == ItemType.ReserveTank): self.ReserveTank += 1 elif (item.Type == ItemType.KeyPD): self.shield += 1 elif (item.Type == ItemType.ProgressiveSword): self.MasterSword = self.Sword self.Sword = True elif (item.Type == ItemType.ProgressiveGlove): self.Mitt = self.Glove self.Glove = True elif (item.Type == ItemType.PowerBomb): self.TwoPowerBombs = self.PowerBomb self.PowerBomb = True def Remove(self, items:List[Item]): for item in items: found = item.Type in Progression.itemMapping if found: setattr(self, item.Type.name, False) continue if (item.Type == ItemType.KeyCT): self.KeyCT -= 1 elif (item.Type == ItemType.KeyPD): self.KeyPD -= 1 elif (item.Type == ItemType.KeySW): self.KeySW -= 1 elif (item.Type == ItemType.KeyIP): self.KeyIP -= 1 elif (item.Type == ItemType.KeyMM): self.KeyMM -= 1 elif (item.Type == ItemType.KeyTR): self.KeyTR -= 1 elif (item.Type == ItemType.KeyGT): self.KeyGT -= 1 elif (item.Type == ItemType.ETank): self.ETank -= 1 elif (item.Type == ItemType.ReserveTank): self.ReserveTank -= 1 elif (item.Type == ItemType.KeyPD): self.shield -= 1 elif (item.Type == ItemType.ProgressiveSword): self.Sword = self.MasterSword self.MasterSword = False elif (item.Type == ItemType.ProgressiveGlove): self.Glove = self.Mitt self.Mitt = False elif (item.Type == ItemType.PowerBomb): self.PowerBomb = self.TwoPowerBombs self.TwoPowerBombs = False def CanLiftLight(self): return self.Glove def CanLiftHeavy(self): return self.Mitt def CanLightTorches(self): return self.Firerod or self.Lamp def CanMeltFreezors(self): return self.Firerod or self.Bombos and self.Sword def CanExtendMagic(self, bars:int = 2): return (2 if self.HalfMagic else 1) * (2 if self.Bottle else 1) >= bars def CanKillManyEnemies(self): return self.Sword or self.Hammer or self.Bow or self.Firerod or \ self.Somaria or self.Byrna and self.CanExtendMagic() def CanAccessDeathMountainPortal(self): return (self.CanDestroyBombWalls() or self.SpeedBooster) and self.Super and self.Morph def CanAccessDarkWorldPortal(self, config: Config): if (config.SMLogic == SMLogic.Normal): return self.CardMaridiaL1 and self.CardMaridiaL2 and self.CanUsePowerBombs() and self.Super and self.Gravity and self.SpeedBooster else: return self.CardMaridiaL1 and self.CardMaridiaL2 and self.CanUsePowerBombs() and self.Super and \ (self.Charge or self.Super and self.Missile) and \ (self.Gravity or self.HiJump and self.Ice and self.Grapple) and \ (self.Ice or self.Gravity and self.SpeedBooster) def CanAccessMiseryMirePortal(self, config: Config): if (config.SMLogic == SMLogic.Normal): return (self.CardNorfairL2 or (self.SpeedBooster and self.Wave)) and self.Varia and self.Super and self.Gravity and self.SpaceJump and self.CanUsePowerBombs() else: return (self.CardNorfairL2 or self.SpeedBooster) and self.Varia and self.Super and \ (self.CanFly() or self.HiJump or self.SpeedBooster or self.CanSpringBallJump() or self.Ice) \ and (self.Gravity or self.HiJump) and self.CanUsePowerBombs() def CanIbj(self): return self.Morph and self.Bombs def CanFly(self): return self.SpaceJump or self.CanIbj() def CanUsePowerBombs(self): return self.Morph and self.PowerBomb def CanPassBombPassages(self): return self.Morph and (self.Bombs or self.PowerBomb) def CanDestroyBombWalls(self): return self.CanPassBombPassages() or self.ScrewAttack def CanSpringBallJump(self): return self.Morph and self.SpringBall def CanHellRun(self): return self.Varia or self.HasEnergyReserves(5) def HasEnergyReserves(self, amount: int): return (self.ETank + self.ReserveTank) >= amount def CanOpenRedDoors(self): return self.Missile or self.Super def CanAccessNorfairUpperPortal(self): return self.Flute or self.CanLiftLight() and self.Lamp def CanAccessNorfairLowerPortal(self): return self.Flute and self.CanLiftHeavy() def CanAccessMaridiaPortal(self, world): from .Region import RewardType if (world.Config.SMLogic == SMLogic.Normal): return self.MoonPearl and self.Flippers and \ self.Gravity and self.Morph and \ (world.CanAcquire(self, RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy()) else: return self.MoonPearl and self.Flippers and \ (self.CanSpringBallJump() or self.HiJump or self.Gravity) and self.Morph and \ (world.CanAcquire(self, RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy()) # Start of AP integration items_start_id = 84000 lookup_id_to_name = { items_start_id + enum.value : enum.name for enum in ItemType } lookup_name_to_id = { item_name : item_id for item_id, item_name in lookup_id_to_name.items() }