Files
Grinch-AP/worlds/smz3/TotalSMZ3/Item.py
Kaito Sinclaire 83cfb803a7 SMZ3: Fix forced fill behaviors (GT junk fill, initial Super/PB front fill) (#5361)
* SMZ3: Make GT fill behave like upstream SMZ3 multiworld GT fill

This means: All items local, 50% guaranteed filler, followed by possible
useful items, never progression.

* Fix item links

* SMZ3: Ensure in all cases, we remove the right item from the pool

Previously front fill would cause erratic errors on frozen, with the
cause immediately revealed by, on source, tripping the assert that was
added in #5109

* SMZ3: Truly, *properly* fix GT junk fill

After hours of diving deep into the upstream SMZ3 randomizer, it finally
behaves identically to how it does there
2025-10-03 02:05:29 +02:00

803 lines
24 KiB
Python

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.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.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() }