2017-10-15 16:16:07 +00:00
from BaseClasses import Dungeon
2018-09-26 17:12:20 +00:00
from Bosses import BossFactory
2017-10-15 19:35:45 +00:00
from Fill import fill_restrictive
2017-12-17 05:25:46 +00:00
from Items import ItemFactory
2020-10-07 17:51:46 +00:00
from Regions import lookup_boss_drops
2017-05-15 18:28:04 +00:00
2019-04-18 09:23:24 +00:00
def create_dungeons ( world , player ) :
2018-09-26 17:12:20 +00:00
def make_dungeon ( name , default_boss , dungeon_regions , big_key , small_keys , dungeon_items ) :
2020-08-20 18:13:00 +00:00
dungeon = Dungeon ( name , dungeon_regions , big_key , [ ] if world . keyshuffle [ player ] == " universal " else small_keys ,
dungeon_items , player )
2019-04-18 09:23:24 +00:00
dungeon . boss = BossFactory ( default_boss , player )
2017-10-15 16:16:07 +00:00
for region in dungeon . regions :
2019-04-18 09:23:24 +00:00
world . get_region ( region , player ) . dungeon = dungeon
2019-07-13 22:11:43 +00:00
dungeon . world = world
2017-10-15 16:16:07 +00:00
return dungeon
2020-06-24 14:22:49 +00:00
ES = make_dungeon ( ' Hyrule Castle ' , None , [ ' Hyrule Castle ' , ' Sewers ' , ' Sewer Drop ' , ' Sewers (Dark) ' , ' Sanctuary ' ] ,
None , [ ItemFactory ( ' Small Key (Hyrule Castle) ' , player ) ] ,
[ ItemFactory ( ' Map (Hyrule Castle) ' , player ) ] )
EP = make_dungeon ( ' Eastern Palace ' , ' Armos Knights ' , [ ' Eastern Palace ' ] ,
ItemFactory ( ' Big Key (Eastern Palace) ' , player ) , [ ] ,
ItemFactory ( [ ' Map (Eastern Palace) ' , ' Compass (Eastern Palace) ' ] , player ) )
2019-04-18 09:23:24 +00:00
DP = make_dungeon ( ' Desert Palace ' , ' Lanmolas ' , [ ' Desert Palace North ' , ' Desert Palace Main (Inner) ' , ' Desert Palace Main (Outer) ' , ' Desert Palace East ' ] , ItemFactory ( ' Big Key (Desert Palace) ' , player ) , [ ItemFactory ( ' Small Key (Desert Palace) ' , player ) ] , ItemFactory ( [ ' Map (Desert Palace) ' , ' Compass (Desert Palace) ' ] , player ) )
ToH = make_dungeon ( ' Tower of Hera ' , ' Moldorm ' , [ ' Tower of Hera (Bottom) ' , ' Tower of Hera (Basement) ' , ' Tower of Hera (Top) ' ] , ItemFactory ( ' Big Key (Tower of Hera) ' , player ) , [ ItemFactory ( ' Small Key (Tower of Hera) ' , player ) ] , ItemFactory ( [ ' Map (Tower of Hera) ' , ' Compass (Tower of Hera) ' ] , player ) )
PoD = make_dungeon ( ' Palace of Darkness ' , ' Helmasaur King ' , [ ' Palace of Darkness (Entrance) ' , ' Palace of Darkness (Center) ' , ' Palace of Darkness (Big Key Chest) ' , ' Palace of Darkness (Bonk Section) ' , ' Palace of Darkness (North) ' , ' Palace of Darkness (Maze) ' , ' Palace of Darkness (Harmless Hellway) ' , ' Palace of Darkness (Final Section) ' ] , ItemFactory ( ' Big Key (Palace of Darkness) ' , player ) , ItemFactory ( [ ' Small Key (Palace of Darkness) ' ] * 6 , player ) , ItemFactory ( [ ' Map (Palace of Darkness) ' , ' Compass (Palace of Darkness) ' ] , player ) )
TT = make_dungeon ( ' Thieves Town ' , ' Blind ' , [ ' Thieves Town (Entrance) ' , ' Thieves Town (Deep) ' , ' Blind Fight ' ] , ItemFactory ( ' Big Key (Thieves Town) ' , player ) , [ ItemFactory ( ' Small Key (Thieves Town) ' , player ) ] , ItemFactory ( [ ' Map (Thieves Town) ' , ' Compass (Thieves Town) ' ] , player ) )
2020-06-10 20:41:28 +00:00
SW = make_dungeon ( ' Skull Woods ' , ' Mothula ' , [ ' Skull Woods Final Section (Entrance) ' , ' Skull Woods First Section ' , ' Skull Woods Second Section ' , ' Skull Woods Second Section (Drop) ' , ' Skull Woods Final Section (Mothula) ' , ' Skull Woods First Section (Right) ' , ' Skull Woods First Section (Left) ' , ' Skull Woods First Section (Top) ' ] , ItemFactory ( ' Big Key (Skull Woods) ' , player ) , ItemFactory ( [ ' Small Key (Skull Woods) ' ] * 3 , player ) , ItemFactory ( [ ' Map (Skull Woods) ' , ' Compass (Skull Woods) ' ] , player ) )
2019-04-18 09:23:24 +00:00
SP = make_dungeon ( ' Swamp Palace ' , ' Arrghus ' , [ ' Swamp Palace (Entrance) ' , ' Swamp Palace (First Room) ' , ' Swamp Palace (Starting Area) ' , ' Swamp Palace (Center) ' , ' Swamp Palace (North) ' ] , ItemFactory ( ' Big Key (Swamp Palace) ' , player ) , [ ItemFactory ( ' Small Key (Swamp Palace) ' , player ) ] , ItemFactory ( [ ' Map (Swamp Palace) ' , ' Compass (Swamp Palace) ' ] , player ) )
IP = make_dungeon ( ' Ice Palace ' , ' Kholdstare ' , [ ' Ice Palace (Entrance) ' , ' Ice Palace (Main) ' , ' Ice Palace (East) ' , ' Ice Palace (East Top) ' , ' Ice Palace (Kholdstare) ' ] , ItemFactory ( ' Big Key (Ice Palace) ' , player ) , ItemFactory ( [ ' Small Key (Ice Palace) ' ] * 2 , player ) , ItemFactory ( [ ' Map (Ice Palace) ' , ' Compass (Ice Palace) ' ] , player ) )
MM = make_dungeon ( ' Misery Mire ' , ' Vitreous ' , [ ' Misery Mire (Entrance) ' , ' Misery Mire (Main) ' , ' Misery Mire (West) ' , ' Misery Mire (Final Area) ' , ' Misery Mire (Vitreous) ' ] , ItemFactory ( ' Big Key (Misery Mire) ' , player ) , ItemFactory ( [ ' Small Key (Misery Mire) ' ] * 3 , player ) , ItemFactory ( [ ' Map (Misery Mire) ' , ' Compass (Misery Mire) ' ] , player ) )
TR = make_dungeon ( ' Turtle Rock ' , ' Trinexx ' , [ ' Turtle Rock (Entrance) ' , ' Turtle Rock (First Section) ' , ' Turtle Rock (Chain Chomp Room) ' , ' Turtle Rock (Second Section) ' , ' Turtle Rock (Big Chest) ' , ' Turtle Rock (Crystaroller Room) ' , ' Turtle Rock (Dark Room) ' , ' Turtle Rock (Eye Bridge) ' , ' Turtle Rock (Trinexx) ' ] , ItemFactory ( ' Big Key (Turtle Rock) ' , player ) , ItemFactory ( [ ' Small Key (Turtle Rock) ' ] * 4 , player ) , ItemFactory ( [ ' Map (Turtle Rock) ' , ' Compass (Turtle Rock) ' ] , player ) )
2019-07-27 13:13:13 +00:00
2019-12-16 15:54:46 +00:00
if world . mode [ player ] != ' inverted ' :
2019-07-27 13:13:13 +00:00
AT = make_dungeon ( ' Agahnims Tower ' , ' Agahnim ' , [ ' Agahnims Tower ' , ' Agahnim 1 ' ] , None , ItemFactory ( [ ' Small Key (Agahnims Tower) ' ] * 2 , player ) , [ ] )
GT = make_dungeon ( ' Ganons Tower ' , ' Agahnim2 ' , [ ' Ganons Tower (Entrance) ' , ' Ganons Tower (Tile Room) ' , ' Ganons Tower (Compass Room) ' , ' Ganons Tower (Hookshot Room) ' , ' Ganons Tower (Map Room) ' , ' Ganons Tower (Firesnake Room) ' , ' Ganons Tower (Teleport Room) ' , ' Ganons Tower (Bottom) ' , ' Ganons Tower (Top) ' , ' Ganons Tower (Before Moldorm) ' , ' Ganons Tower (Moldorm) ' , ' Agahnim 2 ' ] , ItemFactory ( ' Big Key (Ganons Tower) ' , player ) , ItemFactory ( [ ' Small Key (Ganons Tower) ' ] * 4 , player ) , ItemFactory ( [ ' Map (Ganons Tower) ' , ' Compass (Ganons Tower) ' ] , player ) )
else :
AT = make_dungeon ( ' Inverted Agahnims Tower ' , ' Agahnim ' , [ ' Inverted Agahnims Tower ' , ' Agahnim 1 ' ] , None , ItemFactory ( [ ' Small Key (Agahnims Tower) ' ] * 2 , player ) , [ ] )
GT = make_dungeon ( ' Inverted Ganons Tower ' , ' Agahnim2 ' , [ ' Inverted Ganons Tower (Entrance) ' , ' Ganons Tower (Tile Room) ' , ' Ganons Tower (Compass Room) ' , ' Ganons Tower (Hookshot Room) ' , ' Ganons Tower (Map Room) ' , ' Ganons Tower (Firesnake Room) ' , ' Ganons Tower (Teleport Room) ' , ' Ganons Tower (Bottom) ' , ' Ganons Tower (Top) ' , ' Ganons Tower (Before Moldorm) ' , ' Ganons Tower (Moldorm) ' , ' Agahnim 2 ' ] , ItemFactory ( ' Big Key (Ganons Tower) ' , player ) , ItemFactory ( [ ' Small Key (Ganons Tower) ' ] * 4 , player ) , ItemFactory ( [ ' Map (Ganons Tower) ' , ' Compass (Ganons Tower) ' ] , player ) )
2019-04-18 09:23:24 +00:00
GT . bosses [ ' bottom ' ] = BossFactory ( ' Armos Knights ' , player )
GT . bosses [ ' middle ' ] = BossFactory ( ' Lanmolas ' , player )
GT . bosses [ ' top ' ] = BossFactory ( ' Moldorm ' , player )
world . dungeons + = [ ES , EP , DP , ToH , AT , PoD , TT , SW , SP , IP , MM , TR , GT ]
2017-10-15 16:16:07 +00:00
def fill_dungeons ( world ) :
2020-04-25 03:51:40 +00:00
#All chests on the freebes list locked behind a key in room with no other exit
2017-10-29 03:42:35 +00:00
freebes = [ ' Ganons Tower - Map Chest ' , ' Palace of Darkness - Harmless Hellway ' , ' Palace of Darkness - Big Key Chest ' , ' Turtle Rock - Big Key Chest ' ]
2017-05-15 18:28:04 +00:00
2017-06-17 12:40:37 +00:00
all_state_base = world . get_all_state ( )
2017-10-15 16:16:07 +00:00
dungeons = [ ( list ( dungeon . regions ) , dungeon . big_key , list ( dungeon . small_keys ) , list ( dungeon . dungeon_items ) ) for dungeon in world . dungeons ]
2017-07-17 20:26:15 +00:00
2017-08-05 15:51:38 +00:00
loopcnt = 0
while dungeons :
loopcnt + = 1
dungeon_regions , big_key , small_keys , dungeon_items = dungeons . pop ( 0 )
2017-05-15 18:28:04 +00:00
# this is what we need to fill
dungeon_locations = [ location for location in world . get_unfilled_locations ( ) if location . parent_region . name in dungeon_regions ]
2020-07-14 05:01:51 +00:00
world . random . shuffle ( dungeon_locations )
2017-05-15 18:28:04 +00:00
2017-06-17 12:40:37 +00:00
all_state = all_state_base . copy ( )
2017-05-15 18:28:04 +00:00
# first place big key
if big_key is not None :
bk_location = None
for location in dungeon_locations :
if location . item_rule ( big_key ) :
bk_location = location
break
if bk_location is None :
raise RuntimeError ( ' No suitable location for %s ' % big_key )
world . push_item ( bk_location , big_key , False )
2017-06-17 12:40:37 +00:00
bk_location . event = True
2019-08-04 16:32:35 +00:00
bk_location . locked = True
2017-05-15 18:28:04 +00:00
dungeon_locations . remove ( bk_location )
2017-08-05 15:51:38 +00:00
big_key = None
2017-05-15 18:28:04 +00:00
# next place small keys
2017-08-05 15:51:38 +00:00
while small_keys :
small_key = small_keys . pop ( )
2017-06-17 12:40:37 +00:00
all_state . sweep_for_events ( )
2017-05-15 18:28:04 +00:00
sk_location = None
for location in dungeon_locations :
2017-07-17 20:26:15 +00:00
if location . name in freebes or ( location . can_reach ( all_state ) and location . item_rule ( small_key ) ) :
2017-05-15 18:28:04 +00:00
sk_location = location
break
if sk_location is None :
2017-08-05 15:51:38 +00:00
# need to retry this later
small_keys . append ( small_key )
dungeons . append ( ( dungeon_regions , big_key , small_keys , dungeon_items ) )
# infinite regression protection
2019-04-18 09:23:24 +00:00
if loopcnt < ( 30 * world . players ) :
2017-08-05 15:51:38 +00:00
break
else :
raise RuntimeError ( ' No suitable location for %s ' % small_key )
2017-05-15 18:28:04 +00:00
world . push_item ( sk_location , small_key , False )
2017-06-17 12:40:37 +00:00
sk_location . event = True
2019-08-04 16:32:35 +00:00
sk_location . locked = True
2017-05-15 18:28:04 +00:00
dungeon_locations . remove ( sk_location )
2017-08-05 15:51:38 +00:00
if small_keys :
# key placement not finished, loop again
continue
2017-05-15 18:28:04 +00:00
# next place dungeon items
2019-12-13 21:37:52 +00:00
for dungeon_item in dungeon_items :
di_location = dungeon_locations . pop ( )
world . push_item ( di_location , dungeon_item , False )
2017-05-15 18:28:04 +00:00
2017-05-25 15:47:15 +00:00
2018-01-03 01:01:16 +00:00
def get_dungeon_item_pool ( world ) :
2019-12-13 21:37:52 +00:00
return [ item for dungeon in world . dungeons for item in dungeon . all_items ]
2017-05-25 15:47:15 +00:00
2020-10-07 17:51:46 +00:00
def fill_dungeons_restrictive ( world ) :
""" Places dungeon-native items into their dungeons, places nothing if everything is shuffled outside. """
restricted_players = { player for player , restricted in world . restrict_dungeon_item_on_boss . items ( ) if restricted }
locations = [ location for location in world . get_unfilled_dungeon_locations ( )
if not ( location . player in restricted_players and location . name in lookup_boss_drops ) ] # filter boss
world . random . shuffle ( locations )
2017-10-15 17:52:42 +00:00
all_state_base = world . get_all_state ( )
2019-12-13 21:37:52 +00:00
# with shuffled dungeon items they are distributed as part of the normal item pool
for item in world . get_items ( ) :
2019-12-16 20:46:47 +00:00
if ( item . smallkey and world . keyshuffle [ item . player ] ) or ( item . bigkey and world . bigkeyshuffle [ item . player ] ) :
2019-12-13 21:37:52 +00:00
all_state_base . collect ( item , True )
item . advancement = True
2019-12-16 20:46:47 +00:00
elif ( item . map and world . mapshuffle [ item . player ] ) or ( item . compass and world . compassshuffle [ item . player ] ) :
2019-12-13 21:37:52 +00:00
item . priority = True
2019-12-16 20:46:47 +00:00
dungeon_items = [ item for item in get_dungeon_item_pool ( world ) if ( ( item . smallkey and not world . keyshuffle [ item . player ] )
or ( item . bigkey and not world . bigkeyshuffle [ item . player ] )
or ( item . map and not world . mapshuffle [ item . player ] )
or ( item . compass and not world . compassshuffle [ item . player ] ) ) ]
2020-10-07 17:51:46 +00:00
if dungeon_items :
# sort in the order Big Key, Small Key, Other before placing dungeon items
sort_order = { " BigKey " : 3 , " SmallKey " : 2 }
dungeon_items . sort ( key = lambda item : sort_order . get ( item . type , 1 ) )
fill_restrictive ( world , all_state_base , locations , dungeon_items , True )
2017-10-15 17:52:42 +00:00
2017-10-29 03:42:35 +00:00
dungeon_music_addresses = { ' Eastern Palace - Prize ' : [ 0x1559A ] ,
' Desert Palace - Prize ' : [ 0x1559B , 0x1559C , 0x1559D , 0x1559E ] ,
2017-11-09 00:23:21 +00:00
' Tower of Hera - Prize ' : [ 0x155C5 , 0x1107A , 0x10B8C ] ,
2017-10-29 03:42:35 +00:00
' Palace of Darkness - Prize ' : [ 0x155B8 ] ,
' Swamp Palace - Prize ' : [ 0x155B7 ] ,
2018-09-26 21:34:15 +00:00
' Thieves \' Town - Prize ' : [ 0x155C6 ] ,
2017-10-29 03:42:35 +00:00
' Skull Woods - Prize ' : [ 0x155BA , 0x155BB , 0x155BC , 0x155BD , 0x15608 , 0x15609 , 0x1560A , 0x1560B ] ,
' Ice Palace - Prize ' : [ 0x155BF ] ,
' Misery Mire - Prize ' : [ 0x155B9 ] ,
' Turtle Rock - Prize ' : [ 0x155C7 , 0x155A7 , 0x155AA , 0x155AB ] }