2018-09-26 17:12:20 +00:00
import logging
import random
from BaseClasses import Boss
from Fill import FillError
2019-04-18 09:23:24 +00:00
def BossFactory ( boss , player ) :
2018-09-26 17:12:20 +00:00
if boss is None :
return None
if boss in boss_table :
enemizer_name , defeat_rule = boss_table [ boss ]
2019-04-18 09:23:24 +00:00
return Boss ( boss , enemizer_name , defeat_rule , player )
2018-09-26 17:12:20 +00:00
logging . getLogger ( ' ' ) . error ( ' Unknown Boss: %s ' , boss )
return None
2019-04-18 09:23:24 +00:00
def ArmosKnightsDefeatRule ( state , player ) :
2018-09-26 17:12:20 +00:00
# Magic amounts are probably a bit overkill
return (
2019-04-18 09:23:24 +00:00
state . has_blunt_weapon ( player ) or
( state . has ( ' Cane of Somaria ' , player ) and state . can_extend_magic ( player , 10 ) ) or
( state . has ( ' Cane of Byrna ' , player ) and state . can_extend_magic ( player , 16 ) ) or
( state . has ( ' Ice Rod ' , player ) and state . can_extend_magic ( player , 32 ) ) or
( state . has ( ' Fire Rod ' , player ) and state . can_extend_magic ( player , 32 ) ) or
state . has ( ' Blue Boomerang ' , player ) or
state . has ( ' Red Boomerang ' , player ) )
def LanmolasDefeatRule ( state , player ) :
2018-09-26 17:12:20 +00:00
# TODO: Allow the canes here?
return (
2019-04-18 09:23:24 +00:00
state . has_blunt_weapon ( player ) or
state . has ( ' Fire Rod ' , player ) or
state . has ( ' Ice Rod ' , player ) or
state . can_shoot_arrows ( player ) )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def MoldormDefeatRule ( state , player ) :
return state . has_blunt_weapon ( player )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def HelmasaurKingDefeatRule ( state , player ) :
return state . has_blunt_weapon ( player ) or state . can_shoot_arrows ( player )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def ArrghusDefeatRule ( state , player ) :
if not state . has ( ' Hookshot ' , player ) :
2018-09-26 17:12:20 +00:00
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
2019-04-18 09:23:24 +00:00
if state . has_blunt_weapon ( player ) :
2018-09-26 17:12:20 +00:00
return True
2019-04-18 09:23:24 +00:00
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
( state . has ( ' Ice Rod ' , player ) and ( state . can_shoot_arrows ( player ) or state . can_extend_magic ( player , 16 ) ) ) )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def MothulaDefeatRule ( state , player ) :
2018-09-26 17:12:20 +00:00
return (
2019-04-18 09:23:24 +00:00
state . has_blunt_weapon ( player ) or
( state . has ( ' Fire Rod ' , player ) and state . can_extend_magic ( player , 10 ) ) or
2018-09-26 17:12:20 +00:00
# TODO: Not sure how much (if any) extend magic is needed for these two, since they only apply
# to non-vanilla locations, so are harder to test, so sticking with what VT has for now:
2019-04-18 09:23:24 +00:00
( state . has ( ' Cane of Somaria ' , player ) and state . can_extend_magic ( player , 16 ) ) or
( state . has ( ' Cane of Byrna ' , player ) and state . can_extend_magic ( player , 16 ) ) or
state . can_get_good_bee ( player )
2018-09-26 17:12:20 +00:00
)
2019-04-18 09:23:24 +00:00
def BlindDefeatRule ( state , player ) :
return state . has_blunt_weapon ( player ) or state . has ( ' Cane of Somaria ' , player ) or state . has ( ' Cane of Byrna ' , player )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def KholdstareDefeatRule ( state , player ) :
2018-09-26 17:12:20 +00:00
return (
(
2019-04-18 09:23:24 +00:00
state . has ( ' Fire Rod ' , player ) or
2018-09-26 17:12:20 +00:00
(
2019-04-18 09:23:24 +00:00
state . has ( ' Bombos ' , player ) and
2019-08-04 16:32:35 +00:00
# FIXME: the following only actually works for the vanilla location for swordless
( state . has_sword ( player ) or state . world . swords == ' swordless ' )
2018-09-26 17:12:20 +00:00
)
) and
(
2019-04-18 09:23:24 +00:00
state . has_blunt_weapon ( player ) or
( state . has ( ' Fire Rod ' , player ) and state . can_extend_magic ( player , 20 ) ) or
2019-08-04 16:32:35 +00:00
# FIXME: this actually only works for the vanilla location for swordless
2018-09-26 17:12:20 +00:00
(
2019-04-18 09:23:24 +00:00
state . has ( ' Fire Rod ' , player ) and
state . has ( ' Bombos ' , player ) and
2019-08-04 16:32:35 +00:00
state . world . swords == ' swordless ' and
2019-04-18 09:23:24 +00:00
state . can_extend_magic ( player , 16 )
2018-09-26 17:12:20 +00:00
)
)
)
2019-04-18 09:23:24 +00:00
def VitreousDefeatRule ( state , player ) :
return state . can_shoot_arrows ( player ) or state . has_blunt_weapon ( player )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def TrinexxDefeatRule ( state , player ) :
if not ( state . has ( ' Fire Rod ' , player ) and state . has ( ' Ice Rod ' , player ) ) :
2018-09-26 17:12:20 +00:00
return False
2019-04-18 09:23:24 +00:00
return state . has ( ' Hammer ' , player ) or state . has_beam_sword ( player ) or ( state . has_sword ( player ) and state . can_extend_magic ( player , 32 ) )
2018-09-26 17:12:20 +00:00
2019-04-18 09:23:24 +00:00
def AgahnimDefeatRule ( state , player ) :
return state . has_sword ( player ) or state . has ( ' Hammer ' , player ) or state . has ( ' Bug Catching Net ' , player )
2018-09-26 17:12:20 +00:00
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 )
}
def can_place_boss ( world , boss , dungeon_name , level = None ) :
2019-08-04 16:32:35 +00:00
if world . swords in [ ' swordless ' ] and boss == ' Kholdstare ' and dungeon_name != ' Ice Palace ' :
2018-09-26 17:12:20 +00:00
return False
if dungeon_name == ' Ganons Tower ' and level == ' top ' :
if boss in [ " Armos Knights " , " Arrghus " , " Blind " , " Trinexx " , " Lanmolas " ] :
return False
if dungeon_name == ' Ganons Tower ' and level == ' middle ' :
if boss in [ " Blind " ] :
return False
if dungeon_name == ' Tower of Hera ' and boss in [ " Armos Knights " , " Arrghus " , " Blind " , " Trinexx " , " Lanmolas " ] :
return False
if dungeon_name == ' Skull Woods ' and boss in [ " Trinexx " ] :
return False
if boss in [ " Agahnim " , " Agahnim2 " , " Ganon " ] :
return False
return True
2019-04-18 09:23:24 +00:00
def place_bosses ( world , player ) :
2018-09-26 17:12:20 +00:00
if world . boss_shuffle == ' none ' :
return
# Most to least restrictive order
boss_locations = [
[ ' Ganons Tower ' , ' top ' ] ,
[ ' Tower of Hera ' , None ] ,
[ ' Skull Woods ' , None ] ,
[ ' Ganons Tower ' , ' middle ' ] ,
[ ' Eastern Palace ' , None ] ,
[ ' Desert Palace ' , None ] ,
[ ' Palace of Darkness ' , None ] ,
[ ' Swamp Palace ' , None ] ,
[ ' Thieves Town ' , None ] ,
[ ' 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
placeable_bosses = [ boss for boss in all_bosses if boss not in [ ' Agahnim ' , ' Agahnim2 ' , ' Ganon ' ] ]
if world . boss_shuffle in [ " basic " , " normal " ] :
# temporary hack for swordless kholdstare:
2019-08-04 16:32:35 +00:00
if world . swords == ' swordless ' :
2019-04-18 09:23:24 +00:00
world . get_dungeon ( ' Ice Palace ' , player ) . boss = BossFactory ( ' Kholdstare ' , player )
2018-09-26 17:12:20 +00:00
logging . getLogger ( ' ' ) . debug ( ' Placing boss Kholdstare at Ice Palace ' )
boss_locations . remove ( [ ' Ice Palace ' , None ] )
placeable_bosses . remove ( ' Kholdstare ' )
if world . boss_shuffle == " basic " : # vanilla bosses shuffled
bosses = placeable_bosses + [ ' Armos Knights ' , ' Lanmolas ' , ' Moldorm ' ]
else : # all bosses present, the three duplicates chosen at random
bosses = all_bosses + [ random . choice ( placeable_bosses ) for _ in range ( 3 ) ]
logging . getLogger ( ' ' ) . debug ( ' Bosses chosen %s ' , bosses )
random . shuffle ( bosses )
for [ loc , level ] in boss_locations :
loc_text = loc + ( ' ( ' + level + ' ) ' if level else ' ' )
boss = next ( ( b for b in bosses if can_place_boss ( world , b , loc , level ) ) , None )
if not boss :
raise FillError ( ' Could not place boss for location %s ' % loc_text )
bosses . remove ( boss )
logging . getLogger ( ' ' ) . debug ( ' Placing boss %s at %s ' , boss , loc_text )
2019-04-18 09:23:24 +00:00
world . get_dungeon ( loc , player ) . bosses [ level ] = BossFactory ( boss , player )
2018-09-26 17:12:20 +00:00
elif world . boss_shuffle == " chaos " : #all bosses chosen at random
for [ loc , level ] in boss_locations :
loc_text = loc + ( ' ( ' + level + ' ) ' if level else ' ' )
try :
boss = random . choice ( [ b for b in placeable_bosses if can_place_boss ( world , b , loc , level ) ] )
except IndexError :
raise FillError ( ' Could not place boss for location %s ' % loc_text )
logging . getLogger ( ' ' ) . debug ( ' Placing boss %s at %s ' , boss , loc_text )
2019-04-18 09:23:24 +00:00
world . get_dungeon ( loc , player ) . bosses [ level ] = BossFactory ( boss , player )