2023-03-21 01:26:03 +09:00
from typing import Optional
from . . locations . items import *
class OR :
__slots__ = ( ' __items ' , ' __children ' )
def __new__ ( cls , * args ) :
if True in args :
return True
return super ( ) . __new__ ( cls )
def __init__ ( self , * args ) :
self . __items = [ item for item in args if isinstance ( item , str ) ]
self . __children = [ item for item in args if type ( item ) not in ( bool , str ) and item is not None ]
assert self . __items or self . __children , args
def __repr__ ( self ) - > str :
return " or %s " % ( self . __items + self . __children )
def remove ( self , item ) - > None :
if item in self . __items :
self . __items . remove ( item )
def hasConsumableRequirement ( self ) - > bool :
for item in self . __items :
if isConsumable ( item ) :
print ( " Consumable OR requirement? %r " % self )
return True
for child in self . __children :
if child . hasConsumableRequirement ( ) :
print ( " Consumable OR requirement? %r " % self )
return True
return False
def test ( self , inventory ) - > bool :
for item in self . __items :
if item in inventory :
return True
for child in self . __children :
if child . test ( inventory ) :
return True
return False
def consume ( self , inventory ) - > bool :
for item in self . __items :
if item in inventory :
if isConsumable ( item ) :
inventory [ item ] - = 1
if inventory [ item ] == 0 :
del inventory [ item ]
inventory [ " %s _USED " % item ] = inventory . get ( " %s _USED " % item , 0 ) + 1
return True
for child in self . __children :
if child . consume ( inventory ) :
return True
return False
def getItems ( self , inventory , target_set ) - > None :
if self . test ( inventory ) :
return
for item in self . __items :
target_set . add ( item )
for child in self . __children :
child . getItems ( inventory , target_set )
def copyWithModifiedItemNames ( self , f ) - > " OR " :
return OR ( * ( f ( item ) for item in self . __items ) , * ( child . copyWithModifiedItemNames ( f ) for child in self . __children ) )
class AND :
__slots__ = ( ' __items ' , ' __children ' )
def __new__ ( cls , * args ) :
if False in args :
return False
return super ( ) . __new__ ( cls )
def __init__ ( self , * args ) :
self . __items = [ item for item in args if isinstance ( item , str ) ]
self . __children = [ item for item in args if type ( item ) not in ( bool , str ) and item is not None ]
def __repr__ ( self ) - > str :
return " and %s " % ( self . __items + self . __children )
def remove ( self , item ) - > None :
if item in self . __items :
self . __items . remove ( item )
def hasConsumableRequirement ( self ) - > bool :
for item in self . __items :
if isConsumable ( item ) :
return True
for child in self . __children :
if child . hasConsumableRequirement ( ) :
return True
return False
def test ( self , inventory ) - > bool :
for item in self . __items :
if item not in inventory :
return False
for child in self . __children :
if not child . test ( inventory ) :
return False
return True
def consume ( self , inventory ) - > bool :
for item in self . __items :
if isConsumable ( item ) :
inventory [ item ] - = 1
if inventory [ item ] == 0 :
del inventory [ item ]
inventory [ " %s _USED " % item ] = inventory . get ( " %s _USED " % item , 0 ) + 1
for child in self . __children :
if not child . consume ( inventory ) :
return False
return True
def getItems ( self , inventory , target_set ) - > None :
if self . test ( inventory ) :
return
for item in self . __items :
target_set . add ( item )
for child in self . __children :
child . getItems ( inventory , target_set )
def copyWithModifiedItemNames ( self , f ) - > " AND " :
return AND ( * ( f ( item ) for item in self . __items ) , * ( child . copyWithModifiedItemNames ( f ) for child in self . __children ) )
class COUNT :
__slots__ = ( ' __item ' , ' __amount ' )
def __init__ ( self , item : str , amount : int ) - > None :
self . __item = item
self . __amount = amount
def __repr__ ( self ) - > str :
return " < %d x %s > " % ( self . __amount , self . __item )
def hasConsumableRequirement ( self ) - > bool :
if isConsumable ( self . __item ) :
return True
return False
def test ( self , inventory ) - > bool :
return inventory . get ( self . __item , 0 ) > = self . __amount
def consume ( self , inventory ) - > None :
if isConsumable ( self . __item ) :
inventory [ self . __item ] - = self . __amount
if inventory [ self . __item ] == 0 :
del inventory [ self . __item ]
inventory [ " %s _USED " % self . __item ] = inventory . get ( " %s _USED " % self . __item , 0 ) + self . __amount
def getItems ( self , inventory , target_set ) - > None :
if self . test ( inventory ) :
return
target_set . add ( self . __item )
def copyWithModifiedItemNames ( self , f ) - > " COUNT " :
return COUNT ( f ( self . __item ) , self . __amount )
class COUNTS :
__slots__ = ( ' __items ' , ' __amount ' )
def __init__ ( self , items , amount ) :
self . __items = items
self . __amount = amount
def __repr__ ( self ) - > str :
return " < %d x %s > " % ( self . __amount , self . __items )
def hasConsumableRequirement ( self ) - > bool :
for item in self . __items :
if isConsumable ( item ) :
print ( " Consumable COUNTS requirement? %r " % ( self ) )
return True
return False
def test ( self , inventory ) - > bool :
count = 0
for item in self . __items :
count + = inventory . get ( item , 0 )
return count > = self . __amount
def consume ( self , inventory ) - > None :
for item in self . __items :
if isConsumable ( item ) :
inventory [ item ] - = self . __amount
if inventory [ item ] == 0 :
del inventory [ item ]
inventory [ " %s _USED " % item ] = inventory . get ( " %s _USED " % item , 0 ) + self . __amount
def getItems ( self , inventory , target_set ) - > None :
if self . test ( inventory ) :
return
for item in self . __items :
target_set . add ( item )
def copyWithModifiedItemNames ( self , f ) - > " COUNTS " :
return COUNTS ( [ f ( item ) for item in self . __items ] , self . __amount )
class FOUND :
__slots__ = ( ' __item ' , ' __amount ' )
def __init__ ( self , item : str , amount : int ) - > None :
self . __item = item
self . __amount = amount
def __repr__ ( self ) - > str :
return " { %d x %s } " % ( self . __amount , self . __item )
def hasConsumableRequirement ( self ) - > bool :
return False
def test ( self , inventory ) - > bool :
return inventory . get ( self . __item , 0 ) + inventory . get ( " %s _USED " % self . __item , 0 ) > = self . __amount
def consume ( self , inventory ) - > None :
pass
def getItems ( self , inventory , target_set ) - > None :
if self . test ( inventory ) :
return
target_set . add ( self . __item )
def copyWithModifiedItemNames ( self , f ) - > " FOUND " :
return FOUND ( f ( self . __item ) , self . __amount )
def hasConsumableRequirement ( requirements ) - > bool :
if isinstance ( requirements , str ) :
return isConsumable ( requirements )
if requirements is None :
return False
return requirements . hasConsumableRequirement ( )
def isConsumable ( item ) - > bool :
if item is None :
return False
#if item.startswith("RUPEES_") or item == "RUPEES":
# return True
if item . startswith ( " KEY " ) :
return True
return False
class RequirementsSettings :
def __init__ ( self , options ) :
self . bush = OR ( SWORD , MAGIC_POWDER , MAGIC_ROD , POWER_BRACELET , BOOMERANG )
self . attack = OR ( SWORD , BOMB , BOW , MAGIC_ROD , BOOMERANG )
self . attack_hookshot = OR ( SWORD , BOMB , BOW , MAGIC_ROD , BOOMERANG , HOOKSHOT ) # switches, hinox, shrouded stalfos
self . attack_hookshot_powder = OR ( SWORD , BOMB , BOW , MAGIC_ROD , BOOMERANG , HOOKSHOT , MAGIC_POWDER ) # zols, keese, moldorm
self . attack_no_bomb = OR ( SWORD , BOW , MAGIC_ROD , BOOMERANG , HOOKSHOT ) # ?
self . attack_hookshot_no_bomb = OR ( SWORD , BOW , MAGIC_ROD , BOOMERANG , HOOKSHOT ) # vire
self . attack_no_boomerang = OR ( SWORD , BOMB , BOW , MAGIC_ROD , HOOKSHOT ) # teleporting owls
self . attack_skeleton = OR ( SWORD , BOMB , BOW , BOOMERANG , HOOKSHOT ) # cannot kill skeletons with the fire rod
self . rear_attack = OR ( SWORD , BOMB ) # mimic
self . rear_attack_range = OR ( MAGIC_ROD , BOW ) # mimic
self . fire = OR ( MAGIC_POWDER , MAGIC_ROD ) # torches
self . push_hardhat = OR ( SHIELD , SWORD , HOOKSHOT , BOOMERANG )
2024-11-30 22:58:10 -05:00
self . shuffled_magnifier = TRADING_ITEM_MAGNIFYING_GLASS
2023-03-21 01:26:03 +09:00
self . boss_requirements = [
SWORD , # D1 boss
AND ( OR ( SWORD , MAGIC_ROD ) , POWER_BRACELET ) , # D2 boss
AND ( PEGASUS_BOOTS , SWORD ) , # D3 boss
AND ( FLIPPERS , OR ( SWORD , MAGIC_ROD , BOW ) ) , # D4 boss
AND ( HOOKSHOT , SWORD ) , # D5 boss
BOMB , # D6 boss
AND ( OR ( MAGIC_ROD , SWORD , HOOKSHOT ) , COUNT ( SHIELD , 2 ) ) , # D7 boss
MAGIC_ROD , # D8 boss
self . attack_hookshot_no_bomb , # D9 boss
]
self . miniboss_requirements = {
" ROLLING_BONES " : self . attack_hookshot ,
" HINOX " : self . attack_hookshot ,
" DODONGO " : BOMB ,
" CUE_BALL " : SWORD ,
" GHOMA " : OR ( BOW , HOOKSHOT ) ,
" SMASHER " : POWER_BRACELET ,
" GRIM_CREEPER " : self . attack_hookshot_no_bomb ,
" BLAINO " : SWORD ,
" AVALAUNCH " : self . attack_hookshot ,
" GIANT_BUZZ_BLOB " : MAGIC_POWDER ,
" MOBLIN_KING " : SWORD ,
" ARMOS_KNIGHT " : OR ( BOW , MAGIC_ROD , SWORD ) ,
}
# Adjust for options
2024-11-30 22:58:10 -05:00
if not options . tradequest :
self . shuffled_magnifier = True
2023-03-21 01:26:03 +09:00
if options . bowwow != ' normal ' :
# We cheat in bowwow mode, we pretend we have the sword, as bowwow can pretty much do all what the sword ca$ # Except for taking out bushes (and crystal pillars are removed)
self . bush . remove ( SWORD )
if options . logic == " casual " :
# In casual mode, remove the more complex kill methods
self . bush . remove ( MAGIC_POWDER )
self . attack_hookshot_powder . remove ( MAGIC_POWDER )
self . attack . remove ( BOMB )
self . attack_hookshot . remove ( BOMB )
self . attack_hookshot_powder . remove ( BOMB )
self . attack_no_boomerang . remove ( BOMB )
self . attack_skeleton . remove ( BOMB )
if options . logic == " hard " :
self . boss_requirements [ 3 ] = AND ( FLIPPERS , OR ( SWORD , MAGIC_ROD , BOW , BOMB ) ) # bomb angler fish
self . boss_requirements [ 6 ] = OR ( MAGIC_ROD , AND ( BOMB , BOW ) , COUNT ( SWORD , 2 ) , AND ( OR ( SWORD , HOOKSHOT , BOW ) , SHIELD ) ) # evil eagle 3 cycle magic rod / bomb arrows / l2 sword, and bow kill
if options . logic == " glitched " :
self . boss_requirements [ 3 ] = AND ( FLIPPERS , OR ( SWORD , MAGIC_ROD , BOW , BOMB ) ) # bomb angler fish
self . boss_requirements [ 6 ] = OR ( MAGIC_ROD , BOMB , BOW , HOOKSHOT , COUNT ( SWORD , 2 ) , AND ( SWORD , SHIELD ) ) # evil eagle off screen kill or 3 cycle with bombs
if options . logic == " hell " :
self . boss_requirements [ 3 ] = AND ( FLIPPERS , OR ( SWORD , MAGIC_ROD , BOW , BOMB ) ) # bomb angler fish
self . boss_requirements [ 6 ] = OR ( MAGIC_ROD , BOMB , BOW , HOOKSHOT , COUNT ( SWORD , 2 ) , AND ( SWORD , SHIELD ) ) # evil eagle off screen kill or 3 cycle with bombs
self . boss_requirements [ 7 ] = OR ( MAGIC_ROD , COUNT ( SWORD , 2 ) ) # hot head sword beams
self . miniboss_requirements [ " GIANT_BUZZ_BLOB " ] = OR ( MAGIC_POWDER , COUNT ( SWORD , 2 ) ) # use sword beams to damage buzz blob