Merge remote-tracking branch 'origin/OverworldShuffle' into OverworldShuffle
This commit is contained in:
612
ItemList.py
612
ItemList.py
@@ -1,4 +1,4 @@
|
||||
from collections import namedtuple
|
||||
from collections import namedtuple, defaultdict
|
||||
import logging
|
||||
import math
|
||||
import RaceRandom as random
|
||||
@@ -6,7 +6,7 @@ import RaceRandom as random
|
||||
from BaseClasses import LocationType, Region, RegionType, Shop, ShopType, Location, CollectionState, PotItem
|
||||
from EntranceShuffle import connect_entrance
|
||||
from Regions import shop_to_location_table, retro_shops, shop_table_by_location, valid_pot_location
|
||||
from Fill import FillError, fill_restrictive, fast_fill, get_dungeon_item_pool
|
||||
from Fill import FillError, fill_restrictive, get_dungeon_item_pool, track_dungeon_items, track_outside_keys
|
||||
from PotShuffle import vanilla_pots
|
||||
from Tables import bonk_prize_lookup
|
||||
from Items import ItemFactory
|
||||
@@ -39,7 +39,7 @@ Difficulty = namedtuple('Difficulty',
|
||||
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
|
||||
'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'bombs_only', 'cane_only',
|
||||
'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother',
|
||||
'rupee_bow', 'bombbag',
|
||||
'retro', 'bombbag',
|
||||
'extras', 'progressive_sword_limit', 'progressive_shield_limit',
|
||||
'progressive_armor_limit', 'progressive_bottle_limit',
|
||||
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
|
||||
@@ -65,7 +65,7 @@ difficulties = {
|
||||
basicbow = ['Bow', 'Silver Arrows'],
|
||||
timedohko = ['Green Clock'] * 25,
|
||||
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||
rupee_bow = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
|
||||
retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
|
||||
bombbag = ['Bomb Upgrade (+10)'] * 2,
|
||||
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
||||
progressive_sword_limit = 4,
|
||||
@@ -93,7 +93,7 @@ difficulties = {
|
||||
basicbow = ['Bow'] * 2,
|
||||
timedohko = ['Green Clock'] * 25,
|
||||
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||
rupee_bow = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
|
||||
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
|
||||
bombbag = ['Bomb Upgrade (+10)'] * 2,
|
||||
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
||||
progressive_sword_limit = 3,
|
||||
@@ -121,7 +121,7 @@ difficulties = {
|
||||
basicbow = ['Bow'] * 2,
|
||||
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
|
||||
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||
rupee_bow = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
|
||||
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
|
||||
bombbag = ['Bomb Upgrade (+10)'] * 2,
|
||||
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
||||
progressive_sword_limit = 2,
|
||||
@@ -188,7 +188,8 @@ def get_custom_array_key(item):
|
||||
|
||||
|
||||
def maybe_replace_item(world, player, item):
|
||||
if world.rupee_bow[player] and item in ['Single Arrow', 'Arrows (5)', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)']:
|
||||
retro_bow = world.bow_mode[player].startswith('retro')
|
||||
if retro_bow and item in ['Single Arrow', 'Arrows (5)', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)']:
|
||||
return 'Rupees (5)'
|
||||
if world.swords[player] == 'bombs' and item in ['Single Bomb', 'Bombs (3)', 'Bombs (10)', 'Bomb Upgrade (+5)', 'Bomb Upgrade (+10)']:
|
||||
return 'Small Heart'
|
||||
@@ -196,8 +197,11 @@ def maybe_replace_item(world, player, item):
|
||||
|
||||
|
||||
def generate_itempool(world, player):
|
||||
if (world.difficulty[player] not in ['normal', 'hard', 'expert'] or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'trinity', 'crystals', 'z1']
|
||||
or world.mode[player] not in ['open', 'standard', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
|
||||
if (world.difficulty[player] not in ['normal', 'hard', 'expert']
|
||||
or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'trinity', 'crystals', 'z1', 'ganonhunt', 'completionist']
|
||||
or world.mode[player] not in ['open', 'standard', 'inverted']
|
||||
or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']
|
||||
or world.progressive not in ['on', 'off', 'random']):
|
||||
raise NotImplementedError('Not supported yet')
|
||||
|
||||
if world.timer in ['ohko', 'timed-ohko']:
|
||||
@@ -266,6 +270,9 @@ def generate_itempool(world, player):
|
||||
world.push_item(world.get_location('Swamp Drain', player), ItemFactory('Drained Swamp', player), False)
|
||||
world.get_location('Swamp Drain', player).event = True
|
||||
world.get_location('Swamp Drain', player).locked = True
|
||||
world.push_item(world.get_location('Turtle Medallion Pad', player), ItemFactory('Turtle Opened', player), False)
|
||||
world.get_location('Turtle Medallion Pad', player).event = True
|
||||
world.get_location('Turtle Medallion Pad', player).locked = True
|
||||
world.push_item(world.get_location('Attic Cracked Floor', player), ItemFactory('Shining Light', player), False)
|
||||
world.get_location('Attic Cracked Floor', player).event = True
|
||||
world.get_location('Attic Cracked Floor', player).locked = True
|
||||
@@ -290,13 +297,17 @@ def generate_itempool(world, player):
|
||||
world.get_location('Zelda Drop Off', player).locked = True
|
||||
|
||||
# set up item pool
|
||||
if world.custom and player in world.customitemarray:
|
||||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray[player])
|
||||
skip_pool_adjustments = False
|
||||
if world.customizer and world.customizer.get_item_pool():
|
||||
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = make_customizer_pool(world, player)
|
||||
skip_pool_adjustments = True
|
||||
elif world.custom and player in world.customitemarray:
|
||||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world, player, world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.bombbag[player], world.customitemarray[player])
|
||||
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
|
||||
else:
|
||||
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player], world.is_tile_swapped(0x18, player))
|
||||
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world, player, world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.bombbag[player], world.doorShuffle[player], world.logic[player], world.flute_mode[player] == 'active' or world.is_tile_swapped(0x18, player))
|
||||
|
||||
if player in world.pool_adjustment.keys():
|
||||
if player in world.pool_adjustment.keys() and not skip_pool_adjustments:
|
||||
amt = world.pool_adjustment[player]
|
||||
if amt < 0:
|
||||
trash_options = [x for x in pool if x in trash_items]
|
||||
@@ -309,6 +320,7 @@ def generate_itempool(world, player):
|
||||
for _ in range(0, amt):
|
||||
pool.append('Rupees (20)')
|
||||
|
||||
start_inventory = list(world.precollected_items)
|
||||
for item in precollected_items:
|
||||
world.push_precollected(ItemFactory(item, player))
|
||||
|
||||
@@ -346,7 +358,8 @@ def generate_itempool(world, player):
|
||||
if not found_sword and world.swords[player] != 'swordless':
|
||||
found_sword = True
|
||||
possible_weapons.append(item)
|
||||
if item in ['Progressive Bow', 'Bow'] and not found_bow and not world.rupee_bow[player]:
|
||||
if (item in ['Progressive Bow', 'Bow'] and not found_bow
|
||||
and not world.bow_mode[player].startswith('retro')):
|
||||
found_bow = True
|
||||
possible_weapons.append(item)
|
||||
if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
|
||||
@@ -366,7 +379,7 @@ def generate_itempool(world, player):
|
||||
world.get_location(location, player).event = True
|
||||
world.get_location(location, player).locked = True
|
||||
|
||||
if world.shopsanity[player]:
|
||||
if world.shopsanity[player] and not skip_pool_adjustments:
|
||||
for shop in world.shops[player]:
|
||||
if shop.region.name in shop_to_location_table:
|
||||
for index, slot in enumerate(shop.inventory):
|
||||
@@ -396,13 +409,13 @@ def generate_itempool(world, player):
|
||||
world.clock_mode = clock_mode
|
||||
|
||||
goal = world.goal[player]
|
||||
if goal in ['triforcehunt', 'trinity', 'z1']:
|
||||
if goal in ['triforcehunt', 'trinity', 'z1', 'ganonhunt']:
|
||||
g, t = set_default_triforce(goal, world.treasure_hunt_count[player], world.treasure_hunt_total[player])
|
||||
world.treasure_hunt_count[player], world.treasure_hunt_total[player] = g, t
|
||||
world.treasure_hunt_icon[player] = 'Triforce Piece'
|
||||
|
||||
world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
|
||||
and ((item.smallkey and world.keyshuffle[player])
|
||||
and ((item.smallkey and world.keyshuffle[player] != 'none')
|
||||
or (item.bigkey and world.bigkeyshuffle[player])
|
||||
or (item.map and world.mapshuffle[player])
|
||||
or (item.compass and world.compassshuffle[player]))])
|
||||
@@ -411,7 +424,9 @@ def generate_itempool(world, player):
|
||||
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
|
||||
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
|
||||
if world.difficulty[player] in ['normal', 'hard'] and not (world.custom and player in world.customitemarray and world.customitemarray[player]["heartcontainer"] == 0):
|
||||
next(item for item in items if item.name == 'Boss Heart Container').advancement = True
|
||||
container = next((item for item in items if item.name == 'Boss Heart Container'), None)
|
||||
if container:
|
||||
container.advancement = True
|
||||
elif world.difficulty[player] in ['expert'] and not (world.custom and player in world.customitemarray and world.customitemarray[player]["heartpiece"] < 4):
|
||||
adv_heart_pieces = (item for item in items if item.name == 'Piece of Heart')
|
||||
for i in range(4):
|
||||
@@ -428,11 +443,33 @@ def generate_itempool(world, player):
|
||||
return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player)
|
||||
return item
|
||||
|
||||
world.itempool += [beemizer(item) for item in items]
|
||||
if not skip_pool_adjustments:
|
||||
world.itempool += [beemizer(item) for item in items]
|
||||
else:
|
||||
world.itempool += items
|
||||
|
||||
# shuffle medallions
|
||||
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
||||
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
||||
mm_medallion, tr_medallion = None, None
|
||||
if world.customizer and world.customizer.get_medallions() and player in world.customizer.get_medallions():
|
||||
medal_map = world.customizer.get_medallions()
|
||||
if player in medal_map:
|
||||
custom_medallions = medal_map[player]
|
||||
if 'Misery Mire' in custom_medallions:
|
||||
mm_medallion = custom_medallions['Misery Mire']
|
||||
if isinstance(mm_medallion, dict):
|
||||
mm_medallion = random.choices(list(mm_medallion.keys()), list(mm_medallion.values()), k=1)[0]
|
||||
if mm_medallion == 'Random':
|
||||
mm_medallion = None
|
||||
if 'Turtle Rock' in custom_medallions:
|
||||
tr_medallion = custom_medallions['Turtle Rock']
|
||||
if isinstance(tr_medallion, dict):
|
||||
tr_medallion = random.choices(list(tr_medallion.keys()), list(tr_medallion.values()), k=1)[0]
|
||||
if tr_medallion == 'Random':
|
||||
tr_medallion = None
|
||||
if not mm_medallion:
|
||||
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
||||
if not tr_medallion:
|
||||
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
||||
world.required_medallions[player] = (mm_medallion, tr_medallion)
|
||||
|
||||
# shuffle bottle refills
|
||||
@@ -446,24 +483,39 @@ def generate_itempool(world, player):
|
||||
|
||||
set_up_shops(world, player)
|
||||
|
||||
if world.retro[player]:
|
||||
set_up_take_anys(world, player)
|
||||
|
||||
if world.universal_keys[player]:
|
||||
if world.dropshuffle[player]:
|
||||
if world.take_any[player] != 'none':
|
||||
set_up_take_anys(world, player, skip_pool_adjustments)
|
||||
if world.keyshuffle[player] == 'universal':
|
||||
if world.dropshuffle[player] and not skip_pool_adjustments:
|
||||
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13
|
||||
if world.pottery[player] not in ['none', 'cave']:
|
||||
if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments:
|
||||
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19
|
||||
|
||||
create_dynamic_shop_locations(world, player)
|
||||
|
||||
if world.pottery[player] not in ['none', 'keys']:
|
||||
if world.pottery[player] not in ['none', 'keys'] and not skip_pool_adjustments:
|
||||
add_pot_contents(world, player)
|
||||
|
||||
if world.shuffle_bonk_drops[player]:
|
||||
create_dynamic_bonkdrop_locations(world, player)
|
||||
add_bonkdrop_contents(world, player)
|
||||
|
||||
# modfiy based on start inventory, if any
|
||||
modify_pool_for_start_inventory(start_inventory, world, player)
|
||||
|
||||
# increase pool if not enough items
|
||||
ttl_locations = sum(1 for x in world.get_unfilled_locations(player) if '- Prize' not in x.name)
|
||||
pool_size = count_player_dungeon_item_pool(world, player)
|
||||
pool_size += sum(1 for x in world.itempool if x.player == player)
|
||||
|
||||
if pool_size < ttl_locations:
|
||||
retro_bow = world.bow_mode[player].startswith('retro')
|
||||
amount_to_add = ttl_locations - pool_size
|
||||
filler_additions = random.choices(list(filler_items.keys()), filler_items.values(), k=amount_to_add)
|
||||
for item in filler_additions:
|
||||
item_name = 'Rupees (5)' if retro_bow and item == 'Arrows (10)' else item
|
||||
world.itempool.append(ItemFactory(item_name, player))
|
||||
|
||||
|
||||
take_any_locations = [
|
||||
'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut',
|
||||
@@ -475,8 +527,12 @@ take_any_locations = [
|
||||
'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint',
|
||||
'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint']
|
||||
|
||||
fixed_take_anys = [
|
||||
'Desert Healer Fairy', 'Swamp Healer Fairy', 'Dark Death Mountain Healer Fairy',
|
||||
'Dark Lake Hylia Ledge Healer Fairy', 'Bonk Fairy (Dark)']
|
||||
|
||||
def set_up_take_anys(world, player):
|
||||
|
||||
def set_up_take_anys(world, player, skip_adjustments=False):
|
||||
if world.mode[player] == 'inverted':
|
||||
if 'Dark Sanctuary Hint' in take_any_locations:
|
||||
take_any_locations.remove('Dark Sanctuary Hint')
|
||||
@@ -484,9 +540,12 @@ def set_up_take_anys(world, player):
|
||||
if 'Archery Game' in take_any_locations:
|
||||
take_any_locations.remove('Archery Game')
|
||||
|
||||
take_any_candidates = [x for x in take_any_locations if len(world.get_region(x, player).locations) == 0]
|
||||
|
||||
regions = random.sample(take_any_candidates, 5)
|
||||
if world.take_any[player] == 'random':
|
||||
take_any_candidates = [x for x in take_any_locations if len(world.get_region(x, player).locations) == 0]
|
||||
regions = random.sample(take_any_candidates, 5)
|
||||
elif world.take_any[player] == 'fixed':
|
||||
regions = list(fixed_take_anys)
|
||||
random.shuffle(regions)
|
||||
|
||||
old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player)
|
||||
world.regions.append(old_man_take_any)
|
||||
@@ -501,12 +560,13 @@ def set_up_take_anys(world, player):
|
||||
|
||||
sword = next((item for item in world.itempool if item.type == 'Sword' and item.player == player), None)
|
||||
if sword:
|
||||
world.itempool.append(ItemFactory('Rupees (20)', player))
|
||||
if not world.shopsanity[player]:
|
||||
world.itempool.remove(sword)
|
||||
if not skip_adjustments:
|
||||
world.itempool.append(ItemFactory('Rupees (20)', player))
|
||||
if not world.shopsanity[player]:
|
||||
world.itempool.remove(sword)
|
||||
old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True)
|
||||
else:
|
||||
if world.shopsanity[player]:
|
||||
if world.shopsanity[player] and not skip_adjustments:
|
||||
world.itempool.append(ItemFactory('Rupees (300)', player))
|
||||
old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=world.shopsanity[player])
|
||||
|
||||
@@ -524,7 +584,7 @@ def set_up_take_anys(world, player):
|
||||
world.shops[player].append(take_any.shop)
|
||||
take_any.shop.add_inventory(0, 'Blue Potion', 0, 0, create_location=world.shopsanity[player])
|
||||
take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=world.shopsanity[player])
|
||||
if world.shopsanity[player]:
|
||||
if world.shopsanity[player] and not skip_adjustments:
|
||||
world.itempool.append(ItemFactory('Blue Potion', player))
|
||||
world.itempool.append(ItemFactory('Boss Heart Container', player))
|
||||
|
||||
@@ -707,39 +767,47 @@ def fill_prizes(world, attempts=15):
|
||||
|
||||
|
||||
def set_up_shops(world, player):
|
||||
# TODO: separate universal key replacements from rupee bow replacements
|
||||
if world.retro[player] or world.goal[player] in ['z1']:
|
||||
retro_bow = world.bow_mode[player].startswith('retro')
|
||||
universal_keys = world.keyshuffle[player] == 'universal'
|
||||
if retro_bow or universal_keys:
|
||||
if world.shopsanity[player]:
|
||||
removals = [next(item for item in world.itempool if item.name == 'Arrows (10)' and item.player == player)]
|
||||
red_pots = [item for item in world.itempool if item.name == 'Red Potion' and item.player == player][:5]
|
||||
shields_n_hearts = [item for item in world.itempool if item.name in ['Blue Shield', 'Small Heart'] and item.player == player]
|
||||
removals.extend([item for item in world.itempool if item.name == 'Arrow Upgrade (+5)' and item.player == player])
|
||||
removals.extend(red_pots)
|
||||
removals.extend(random.sample(shields_n_hearts, 5))
|
||||
removals = []
|
||||
if retro_bow:
|
||||
removals = [next(item for item in world.itempool if item.name == 'Arrows (10)' and item.player == player)]
|
||||
removals.extend([item for item in world.itempool if item.name == 'Arrow Upgrade (+5)' and item.player == player])
|
||||
shields_n_hearts = [item for item in world.itempool if item.name in ['Blue Shield', 'Small Heart'] and item.player == player]
|
||||
removals.extend(random.sample(shields_n_hearts, 5))
|
||||
if universal_keys:
|
||||
red_pots = [item for item in world.itempool if item.name == 'Red Potion' and item.player == player][:5]
|
||||
removals.extend(red_pots)
|
||||
for remove in removals:
|
||||
world.itempool.remove(remove)
|
||||
for i in range(6): # replace the Arrows (10) and randomly selected hearts/blue shield
|
||||
arrow_item = ItemFactory('Single Arrow', player)
|
||||
arrow_item.advancement = True
|
||||
world.itempool.append(arrow_item)
|
||||
for i in range(5): # replace the red potions
|
||||
world.itempool.append(ItemFactory('Small Key (Universal)', player))
|
||||
world.itempool.append(ItemFactory('Rupees (50)', player)) # replaces the arrow upgrade
|
||||
if retro_bow:
|
||||
for i in range(6): # replace the Arrows (10) and randomly selected hearts/blue shield
|
||||
arrow_item = ItemFactory('Single Arrow', player)
|
||||
arrow_item.advancement = True
|
||||
world.itempool.append(arrow_item)
|
||||
world.itempool.append(ItemFactory('Rupees (50)', player)) # replaces the arrow upgrade
|
||||
if universal_keys:
|
||||
for i in range(5): # replace the red potions
|
||||
world.itempool.append(ItemFactory('Small Key (Universal)', player))
|
||||
# TODO: move hard+ mode changes for shields here, utilizing the new shops
|
||||
else:
|
||||
rss = world.get_region('Red Shield Shop', player).shop
|
||||
if not rss.locked:
|
||||
rss.custom = True
|
||||
rss.add_inventory(2, 'Single Arrow', 80)
|
||||
if retro_bow:
|
||||
rss = world.get_region('Red Shield Shop', player).shop
|
||||
if not rss.locked:
|
||||
rss.custom = True
|
||||
rss.add_inventory(2, 'Single Arrow', 80)
|
||||
rss.locked = True
|
||||
cap_shop = world.get_region('Capacity Upgrade', player).shop
|
||||
cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro
|
||||
for shop in random.sample([s for s in world.shops[player] if not s.locked and s.region.player == player], 5):
|
||||
shop.custom = True
|
||||
shop.locked = True
|
||||
if shop.inventory[2]['item'] != 'Single Arrow':
|
||||
if retro_bow:
|
||||
shop.add_inventory(0, 'Single Arrow', 80)
|
||||
shop.add_inventory(1, 'Small Key (Universal)', 100)
|
||||
rss.locked = True
|
||||
cap_shop = world.get_region('Capacity Upgrade', player).shop
|
||||
cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro
|
||||
if universal_keys:
|
||||
shop.add_inventory(1, 'Small Key (Universal)', 100)
|
||||
if world.swords[player] == 'bombs' and not world.shopsanity[player]:
|
||||
for shop in [s for s in world.shops[player] if s.region.player == player]:
|
||||
shop.custom = True
|
||||
@@ -768,10 +836,11 @@ def set_up_shops(world, player):
|
||||
|
||||
|
||||
def customize_shops(world, player):
|
||||
found_bomb_upgrade, found_arrow_upgrade = False, world.rupee_bow[player]
|
||||
retro_bow = world.bow_mode[player].startswith('retro')
|
||||
found_bomb_upgrade, found_arrow_upgrade = False, retro_bow
|
||||
possible_replacements = []
|
||||
shops_to_customize = shop_to_location_table.copy()
|
||||
if world.retro[player]:
|
||||
if world.take_any[player] != 'none':
|
||||
shops_to_customize.update(retro_shops)
|
||||
for shop_name, loc_list in shops_to_customize.items():
|
||||
shop = world.get_region(shop_name, player).shop
|
||||
@@ -794,7 +863,7 @@ def customize_shops(world, player):
|
||||
price = 0
|
||||
else:
|
||||
price = 120 if shop_name == 'Potion Shop' and item.name == 'Red Potion' else item.price
|
||||
if world.rupee_bow[player] and item.name == 'Single Arrow':
|
||||
if retro_bow and item.name == 'Single Arrow':
|
||||
price = 80
|
||||
# randomize price
|
||||
shop.add_inventory(idx, item.name, randomize_price(price), max_repeat, player=item.player)
|
||||
@@ -821,7 +890,7 @@ def customize_shops(world, player):
|
||||
if not found_arrow_upgrade and len(possible_replacements) > 0:
|
||||
choices = []
|
||||
for shop, idx, loc, item in possible_replacements:
|
||||
if item.name == 'Arrows (10)' or (item.name == 'Single Arrow' and not world.rupee_bow[player]):
|
||||
if item.name == 'Arrows (10)' or (item.name == 'Single Arrow' and not retro_bow):
|
||||
choices.append((shop, idx, loc, item))
|
||||
if len(choices) > 0:
|
||||
shop, idx, loc, item = random.choice(choices)
|
||||
@@ -965,6 +1034,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)'
|
||||
|
||||
|
||||
def add_pot_contents(world, player):
|
||||
retro_bow = world.bow_mode[player].startswith('retro')
|
||||
for super_tile, pot_list in vanilla_pots.items():
|
||||
for pot in pot_list:
|
||||
if pot.item not in [PotItem.Hole, PotItem.Key, PotItem.Switch]:
|
||||
@@ -985,13 +1055,15 @@ def add_bonkdrop_contents(world, player):
|
||||
count -= 1
|
||||
|
||||
|
||||
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic, flute_activated):
|
||||
def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords,
|
||||
bombbag, door_shuffle, logic, flute_activated):
|
||||
pool = []
|
||||
placed_items = {}
|
||||
precollected_items = []
|
||||
clock_mode = None
|
||||
if treasure_hunt_total == 0 and goal in ['triforcehunt', 'trinity', 'z1']:
|
||||
treasure_hunt_total = 30 if goal == 'triforcehunt' else 10
|
||||
if goal in ['triforcehunt', 'trinity', 'z1', 'ganonhunt']:
|
||||
if treasure_hunt_total == 0:
|
||||
treasure_hunt_total = 30 if goal in ['triforcehunt', 'ganonhunt'] else 10
|
||||
# triforce pieces max out
|
||||
triforcepool = ['Triforce Piece'] * min(treasure_hunt_total, max_goal)
|
||||
|
||||
@@ -1022,8 +1094,6 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
|
||||
place_item('Misery Mire - Boss', 'Triforce Piece')
|
||||
place_item('Turtle Rock - Boss', 'Triforce Piece')
|
||||
pool.remove('Pegasus Boots')
|
||||
pool.remove('Ocarina')
|
||||
pool.append('Ocarina (Activated)')
|
||||
|
||||
# provide boots to boots glitch dependent modes
|
||||
if logic in ['owglitches', 'nologic']:
|
||||
@@ -1038,12 +1108,8 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
|
||||
|
||||
lamps_needed_for_dark_rooms = 1
|
||||
|
||||
# insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
|
||||
if shuffle == 'insanity_legacy':
|
||||
place_item('Link\'s House', 'Magic Mirror')
|
||||
place_item('Sanctuary', 'Moon Pearl')
|
||||
else:
|
||||
pool.extend(['Magic Mirror', 'Moon Pearl'])
|
||||
# old insanity shuffle didn't have fake LW/DW logic so this used to be conditional
|
||||
pool.extend(['Magic Mirror', 'Moon Pearl'])
|
||||
|
||||
if timer == 'display':
|
||||
clock_mode = 'stopwatch'
|
||||
@@ -1084,9 +1150,9 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
|
||||
else:
|
||||
pool.extend(diff.basicarmor)
|
||||
|
||||
if want_progressives() and goal != "z1":
|
||||
if 'silvers' not in world.bow_mode[player]:
|
||||
pool.extend(['Progressive Bow'] * 2)
|
||||
elif swords != 'swordless':
|
||||
elif swords not in ['swordless', 'pseudo', 'assured_pseudo', 'swordless_hammer']:
|
||||
pool.extend(diff.basicbow)
|
||||
else:
|
||||
pool.extend(['Bow', 'Silver Arrows'])
|
||||
@@ -1141,7 +1207,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
|
||||
elif timer == 'timed-ohko':
|
||||
pool.extend(diff.timedohko)
|
||||
clock_mode = 'countdown-ohko'
|
||||
if goal in ['triforcehunt', 'trinity']:
|
||||
if goal in ['triforcehunt', 'trinity', 'ganonhunt']:
|
||||
pool.extend(triforcepool)
|
||||
|
||||
for extra in diff.extras:
|
||||
@@ -1151,13 +1217,14 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
|
||||
|
||||
if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
|
||||
place_item('Master Sword Pedestal', 'Triforce')
|
||||
if retro or goal == 'z1':
|
||||
pool = [item.replace('Single Arrow','Rupees (5)') for item in pool]
|
||||
pool = [item.replace('Arrows (10)','Rupees (5)') for item in pool]
|
||||
pool = [item.replace('Arrow Upgrade (+5)','Rupees (5)') for item in pool]
|
||||
pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool]
|
||||
pool.extend(diff.rupee_bow)
|
||||
if door_shuffle != 'vanilla': # door shuffle needs more keys for retro
|
||||
if world.bow_mode[player].startswith('retro'):
|
||||
pool = [item.replace('Single Arrow', 'Rupees (5)') for item in pool]
|
||||
pool = [item.replace('Arrows (10)', 'Rupees (5)') for item in pool]
|
||||
pool = [item.replace('Arrow Upgrade (+5)', 'Rupees (5)') for item in pool]
|
||||
pool = [item.replace('Arrow Upgrade (+10)', 'Rupees (5)') for item in pool]
|
||||
if world.keyshuffle[player] == 'universal':
|
||||
pool.extend(diff.retro)
|
||||
if door_shuffle != 'vanilla': # door shuffle needs more keys for universal keys
|
||||
replace = 'Rupees (20)' if difficulty == 'normal' else 'Rupees (5)'
|
||||
indices = [i for i, x in enumerate(pool) if x == replace]
|
||||
for i in range(0, min(10, len(indices))):
|
||||
@@ -1179,7 +1246,70 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
|
||||
return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms)
|
||||
|
||||
|
||||
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag, customitemarray):
|
||||
item_alternates = {
|
||||
# Bows
|
||||
'Progressive Bow (Alt)': ('Progressive Bow', 1),
|
||||
'Bow': ('Progressive Bow', 1),
|
||||
'Silver Arrows': ('Progressive Bow', 2),
|
||||
# Gloves
|
||||
'Power Glove': ('Progressive Glove', 1),
|
||||
'Titans Mitts': ('Progressive Glove', 2),
|
||||
# Swords
|
||||
'Sword and Shield': ('Progressive Sword', 1), # could find a way to also remove a shield, but mostly not impactful
|
||||
'Fighter Sword': ('Progressive Sword', 1),
|
||||
'Master Sword': ('Progressive Sword', 2),
|
||||
'Tempered Sword': ('Progressive Sword', 3),
|
||||
'Golden Sword': ('Progressive Sword', 4),
|
||||
# Shields
|
||||
'Blue Shield': ('Progressive Shield', 1),
|
||||
'Red Shield': ('Progressive Shield', 2),
|
||||
'Mirror Shield': ('Progressive Shield', 3),
|
||||
# Armors
|
||||
'Blue Mail': ('Progressive Armor', 1),
|
||||
'Red Mail': ('Progressive Armor', 2),
|
||||
|
||||
'Magic Upgrade (1/4)': ('Magic Upgrade (1/2)', 2),
|
||||
'Ocarina': ('Ocarina (Activated)', 1),
|
||||
'Ocarina (Activated)': ('Ocarina', 1),
|
||||
'Boss Heart Container': ('Sanctuary Heart Container', 1),
|
||||
'Sanctuary Heart Container': ('Boss Heart Container', 1),
|
||||
'Power Star': ('Triforce Piece', 1)
|
||||
}
|
||||
|
||||
|
||||
def modify_pool_for_start_inventory(start_inventory, world, player):
|
||||
# skips custom item pools - these shouldn't be adjusted
|
||||
if (world.customizer and world.customizer.get_item_pool()) or world.custom:
|
||||
return
|
||||
for item in start_inventory:
|
||||
if item.player == player:
|
||||
if item in world.itempool:
|
||||
world.itempool.remove(item)
|
||||
elif item.name in item_alternates:
|
||||
alt = item_alternates[item.name]
|
||||
i = alt[1]
|
||||
while i > 0:
|
||||
alt_item = ItemFactory([alt[0]], player)[0]
|
||||
if alt_item in world.itempool:
|
||||
world.itempool.remove(alt_item)
|
||||
i = i-1
|
||||
elif 'Bottle' in item.name:
|
||||
bottle_item = next((x for x in world.itempool if 'Bottle' in item.name and x.player == player), None)
|
||||
if bottle_item is not None:
|
||||
world.itempool.remove(bottle_item)
|
||||
if item.dungeon:
|
||||
d = world.get_dungeon(item.dungeon, item.player)
|
||||
match = next((i for i in d.all_items if i.name == item.name), None)
|
||||
if match:
|
||||
if match.map or match.compass:
|
||||
d.dungeon_items.remove(match)
|
||||
elif match.smallkey:
|
||||
d.small_keys.remove(match)
|
||||
elif match.bigkey:
|
||||
d.big_key.remove(match)
|
||||
|
||||
|
||||
def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer, goal, mode, swords, bombbag, customitemarray):
|
||||
pool = []
|
||||
placed_items = {}
|
||||
precollected_items = []
|
||||
@@ -1204,7 +1334,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
||||
customitemarray["triforce"] = total_items_to_place
|
||||
|
||||
# Triforce Pieces
|
||||
if goal in ['triforcehunt', 'trinity', 'z1']:
|
||||
if goal in ['triforcehunt', 'trinity', 'z1', 'ganonhunt']:
|
||||
g, t = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"])
|
||||
customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = g, t
|
||||
|
||||
@@ -1242,8 +1372,8 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
||||
treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], max_goal), 1)
|
||||
treasure_hunt_icon = 'Triforce Piece'
|
||||
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
|
||||
if ((customitemarray["triforcepieces"] < treasure_hunt_count) and (goal in ['triforcehunt', 'trinity', 'z1'])
|
||||
and (customitemarray["triforce"] == 0)):
|
||||
if ((customitemarray["triforcepieces"] < treasure_hunt_count)
|
||||
and (goal in ['triforcehunt', 'trinity', 'z1', 'ganonhunt']) and (customitemarray["triforce"] == 0)):
|
||||
extrapieces = treasure_hunt_count - customitemarray["triforcepieces"]
|
||||
pool.extend(['Triforce Piece'] * extrapieces)
|
||||
itemtotal = itemtotal + extrapieces
|
||||
@@ -1260,7 +1390,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
||||
itemtotal = itemtotal + 1
|
||||
|
||||
if mode == 'standard':
|
||||
if retro or goal == 'z1':
|
||||
if world.keyshuffle[player] == 'universal':
|
||||
key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
|
||||
place_item(key_location, 'Small Key (Universal)')
|
||||
pool.extend(['Small Key (Universal)'] * max((customitemarray["generickeys"] - 1), 0))
|
||||
@@ -1271,6 +1401,8 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
||||
|
||||
pool.extend(['Fighter Sword'] * customitemarray["sword1"])
|
||||
pool.extend(['Progressive Sword'] * customitemarray["progressivesword"])
|
||||
pool.extend(['Magic Mirror'] * customitemarray["mirror"])
|
||||
pool.extend(['Moon Pearl'] * customitemarray["pearl"])
|
||||
|
||||
# in z1 mode, preplace triforce pieces on bosses and boots on green pendant turn-in
|
||||
if goal == 'z1':
|
||||
@@ -1289,22 +1421,29 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
|
||||
pool.remove('Ocarina')
|
||||
pool.append('Ocarina (Activated)')
|
||||
|
||||
if shuffle == 'insanity_legacy':
|
||||
place_item('Link\'s House', 'Magic Mirror')
|
||||
place_item('Sanctuary', 'Moon Pearl')
|
||||
pool.extend(['Magic Mirror'] * max((customitemarray["mirror"] -1 ), 0))
|
||||
pool.extend(['Moon Pearl'] * max((customitemarray["pearl"] - 1), 0))
|
||||
else:
|
||||
pool.extend(['Magic Mirror'] * customitemarray["mirror"])
|
||||
pool.extend(['Moon Pearl'] * customitemarray["pearl"])
|
||||
|
||||
if retro or goal == 'z1':
|
||||
if world.keyshuffle[player] == 'universal':
|
||||
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
|
||||
if itemtotal < total_items_to_place:
|
||||
nothings = total_items_to_place - itemtotal
|
||||
# print("Placing " + str(nothings) + " Nothings")
|
||||
pool.extend(['Nothing'] * nothings)
|
||||
|
||||
start_inventory = [x for x in world.precollected_items if x.player == player]
|
||||
if not start_inventory:
|
||||
if world.logic[player] in ['owglitches', 'nologic']:
|
||||
precollected_items.append('Pegasus Boots')
|
||||
if 'Pegasus Boots' in pool:
|
||||
pool.remove('Pegasus Boots')
|
||||
pool.append('Rupees (20)')
|
||||
if world.swords[player] == 'assured':
|
||||
precollected_items.append('Progressive Sword')
|
||||
if 'Progressive Sword' in pool:
|
||||
pool.remove('Progressive Sword')
|
||||
pool.append('Rupees (50)')
|
||||
elif 'Fighter Sword' in pool:
|
||||
pool.remove('Fighter Sword')
|
||||
pool.append('Rupees (50)')
|
||||
|
||||
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms)
|
||||
|
||||
def make_customizer_pool(world, player):
|
||||
@@ -1317,12 +1456,62 @@ def make_customizer_pool(world, player):
|
||||
assert loc not in placed_items
|
||||
placed_items[loc] = item
|
||||
|
||||
dungeon_locations, dungeon_count = defaultdict(set), defaultdict(int)
|
||||
for l in world.get_unfilled_locations(player):
|
||||
if l.parent_region.dungeon:
|
||||
dungeon = l.parent_region.dungeon
|
||||
dungeon_locations[dungeon.name].add(l)
|
||||
if dungeon.name not in dungeon_count:
|
||||
d_count = 1 if dungeon.big_key else 0
|
||||
d_count += len(dungeon.small_keys) + len(dungeon.dungeon_items)
|
||||
dungeon_count[dungeon.name] = d_count
|
||||
|
||||
diff = difficulties[world.difficulty[player]]
|
||||
for item_name, amount in world.customizer.get_item_pool()[player].items():
|
||||
if isinstance(amount, int):
|
||||
if item_name == 'Bottle (Random)':
|
||||
for _ in range(amount):
|
||||
pool.append(random.choice(diff.bottles))
|
||||
elif item_name.startswith('Small Key') and item_name != 'Small Key (Universal)':
|
||||
d_item = ItemFactory(item_name, player)
|
||||
if world.keyshuffle[player] == 'none':
|
||||
d_name = d_item.dungeon
|
||||
dungeon = world.get_dungeon(d_name, player)
|
||||
target_amount = max(amount, len(dungeon.small_keys))
|
||||
additional_amount = target_amount - len(dungeon.small_keys)
|
||||
possible_fit = min(additional_amount, len(dungeon_locations[d_name])-dungeon_count[d_name])
|
||||
if possible_fit > 0:
|
||||
dungeon_count[d_name] += possible_fit
|
||||
dungeon.small_keys.extend([d_item] * amount)
|
||||
additional_amount -= possible_fit
|
||||
if additional_amount > 0:
|
||||
pool.extend([item_name] * amount)
|
||||
else:
|
||||
dungeon = world.get_dungeon(d_item.dungeon, player)
|
||||
target_amount = max(amount, len(dungeon.small_keys))
|
||||
additional_amount = target_amount - len(dungeon.small_keys)
|
||||
dungeon.small_keys.extend([d_item] * additional_amount)
|
||||
elif item_name.startswith('Big Key') or item_name.startswith('Map') or item_name.startswith('Compass'):
|
||||
d_item = ItemFactory(item_name, player)
|
||||
if ((d_item.bigkey and not world.bigkeyshuffle[player])
|
||||
or (d_item.compass and not world.compassshuffle[player])
|
||||
or (d_item.map and not world.mapshuffle[player])):
|
||||
d_name = d_item.dungeon
|
||||
dungeon = world.get_dungeon(d_name, player)
|
||||
current_amount = 1 if d_item == dungeon.big_key or d_item in dungeon.dungeon_items else 0
|
||||
additional_amount = amount - current_amount
|
||||
possible_fit = min(additional_amount, len(dungeon_locations[d_name])-dungeon_count[d_name])
|
||||
if possible_fit > 0:
|
||||
dungeon_count[d_name] += possible_fit
|
||||
dungeon.dungeon_items.extend([d_item] * amount)
|
||||
additional_amount -= possible_fit
|
||||
if additional_amount > 0:
|
||||
pool.extend([item_name] * amount)
|
||||
else:
|
||||
dungeon = world.get_dungeon(d_item.dungeon, player)
|
||||
current_amount = 1 if d_item == dungeon.big_key or d_item in dungeon.dungeon_items else 0
|
||||
additional_amount = amount - current_amount
|
||||
dungeon.dungeon_items.extend([d_item] * additional_amount)
|
||||
else:
|
||||
pool.extend([item_name] * amount)
|
||||
|
||||
@@ -1334,16 +1523,85 @@ def make_customizer_pool(world, player):
|
||||
elif timer == 'ohko':
|
||||
clock_mode = 'ohko'
|
||||
|
||||
if world.goal[player] == 'pedestal':
|
||||
if world.goal[player] in ['pedestal', 'trinity']:
|
||||
place_item('Master Sword Pedestal', 'Triforce')
|
||||
|
||||
guaranteed_items = alwaysitems + ['Magic Mirror', 'Moon Pearl']
|
||||
missing_items = []
|
||||
if world.shopsanity[player]:
|
||||
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion'])
|
||||
if world.keyshuffle[player] == 'universal':
|
||||
guaranteed_items.append('Small Key (Universal)')
|
||||
for item in guaranteed_items:
|
||||
if item not in pool:
|
||||
missing_items.append(item)
|
||||
|
||||
glove_count = sum(1 for i in pool if i == 'Progressive Glove')
|
||||
glove_count = 2 if next((i for i in pool if i == 'Titans Glove'), None) is not None else glove_count
|
||||
for i in range(glove_count, 2):
|
||||
missing_items.append('Progressive Glove')
|
||||
|
||||
if world.bombbag[player]:
|
||||
if 'Bomb Upgrade (+10)' not in pool:
|
||||
missing_items.append('Bomb Upgrade (+10)')
|
||||
|
||||
if world.swords[player] != 'swordless':
|
||||
beam_swords = {'Master Sword', 'Tempered Sword', 'Golden Sword'}
|
||||
sword_count = sum(1 for i in pool if i in 'Progressive Sword')
|
||||
sword_count = 2 if next((i for i in pool if i in beam_swords), None) is not None else sword_count
|
||||
for i in range(sword_count, 2):
|
||||
missing_items.append('Progressive Sword')
|
||||
|
||||
bow_found = next((i for i in pool if i in {'Bow', 'Progressive Bow'}), None)
|
||||
if not bow_found:
|
||||
missing_items.append('Progressive Bow')
|
||||
logging.getLogger('').warning(f'The following items are not in the custom item pool {", ".join(missing_items)}')
|
||||
|
||||
g, t = set_default_triforce(world.goal[player], world.treasure_hunt_count[player],
|
||||
world.treasure_hunt_total[player])
|
||||
if t != 0:
|
||||
pieces = sum(1 for i in pool if i == 'Triforce Piece')
|
||||
if pieces < t:
|
||||
pool.extend(['Triforce Piece'] * (t - pieces))
|
||||
|
||||
if not world.customizer.get_start_inventory():
|
||||
if world.logic[player] in ['owglitches', 'nologic']:
|
||||
precollected_items.append('Pegasus Boots')
|
||||
if 'Pegasus Boots' in pool:
|
||||
pool.remove('Pegasus Boots')
|
||||
pool.append('Rupees (20)')
|
||||
if world.swords[player] == 'assured':
|
||||
precollected_items.append('Progressive Sword')
|
||||
if 'Progressive Sword' in pool:
|
||||
pool.remove('Progressive Sword')
|
||||
pool.append('Rupees (50)')
|
||||
elif 'Fighter Sword' in pool:
|
||||
pool.remove('Fighter Sword')
|
||||
pool.append('Rupees (50)')
|
||||
|
||||
return pool, placed_items, precollected_items, clock_mode, 1
|
||||
|
||||
|
||||
filler_items = {
|
||||
'Arrows (10)': 12,
|
||||
'Bombs (3)': 16,
|
||||
'Rupees (300)': 5,
|
||||
'Rupees (100)': 1,
|
||||
'Rupees (50)': 7,
|
||||
'Rupees (20)': 28,
|
||||
'Rupees (5)': 4,
|
||||
}
|
||||
|
||||
|
||||
def count_player_dungeon_item_pool(world, player):
|
||||
return sum(1 for dungeon in world.dungeons for item in dungeon.all_items
|
||||
if dungeon.player == player and item.location is None and is_dungeon_item(item.name, world, player))
|
||||
|
||||
|
||||
# location pool doesn't support larger values at this time
|
||||
def set_default_triforce(goal, custom_goal, custom_total):
|
||||
triforce_goal, triforce_total = 0, 0
|
||||
if goal == 'triforcehunt':
|
||||
if goal in ['triforcehunt', 'ganonhunt']:
|
||||
triforce_goal, triforce_total = 20, 30
|
||||
elif goal == 'trinity':
|
||||
triforce_goal, triforce_total = 8, 10
|
||||
@@ -1360,31 +1618,127 @@ def set_default_triforce(goal, custom_goal, custom_total):
|
||||
|
||||
|
||||
# A quick test to ensure all combinations generate the correct amount of items.
|
||||
def test():
|
||||
for difficulty in ['normal', 'hard', 'expert']:
|
||||
for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity', 'z1']:
|
||||
for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
|
||||
for mode in ['open', 'standard', 'inverted', 'retro']:
|
||||
for swords in ['random', 'assured', 'swordless', 'vanilla', 'bombs', 'byrna', 'somaria', 'cane']:
|
||||
for progressive in ['on', 'off']:
|
||||
for shuffle in ['vanilla', 'full', 'crossed', 'insanity']:
|
||||
for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
|
||||
for retro in [True, False]:
|
||||
for door_shuffle in ['basic', 'crossed', 'vanilla']:
|
||||
for bombbag in [True, False]:
|
||||
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic, False)
|
||||
count = len(out[0]) + len(out[1])
|
||||
# def test():
|
||||
# for difficulty in ['normal', 'hard', 'expert']:
|
||||
# for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity']:
|
||||
# for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
|
||||
# for mode in ['open', 'standard', 'inverted', 'retro']:
|
||||
# for swords in ['random', 'assured', 'swordless', 'vanilla']:
|
||||
# for progressive in ['on', 'off']:
|
||||
# for shuffle in ['vanilla', 'full', 'crossed', 'insanity']:
|
||||
# for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
|
||||
# for retro in [True, False]:
|
||||
# for door_shuffle in ['basic', 'crossed', 'vanilla']:
|
||||
# for bombbag in [True, False]:
|
||||
# out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic, False)
|
||||
# count = len(out[0]) + len(out[1])
|
||||
|
||||
correct_count = total_items_to_place
|
||||
if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
try:
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag))
|
||||
except AssertionError as e:
|
||||
print(e)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
def fill_specific_items(world):
|
||||
if world.customizer:
|
||||
placements = world.customizer.get_placements()
|
||||
dungeon_pool = get_dungeon_item_pool(world)
|
||||
prize_pool = []
|
||||
prize_set = {'Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2',
|
||||
'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'}
|
||||
for p in range(1, world.players + 1):
|
||||
prize_pool.extend(prize_set)
|
||||
if placements:
|
||||
for player, placement_list in placements.items():
|
||||
for location, item in placement_list.items():
|
||||
loc = world.get_location(location, player)
|
||||
item_to_place, event_flag = get_item_and_event_flag(item, world, player,
|
||||
dungeon_pool, prize_set, prize_pool)
|
||||
if item_to_place:
|
||||
world.push_item(loc, item_to_place, False)
|
||||
track_outside_keys(item_to_place, loc, world)
|
||||
track_dungeon_items(item_to_place, loc, world)
|
||||
loc.event = (event_flag or item_to_place.advancement
|
||||
or item_to_place.bigkey or item_to_place.smallkey)
|
||||
advanced_placements = world.customizer.get_advanced_placements()
|
||||
if advanced_placements:
|
||||
for player, placement_list in advanced_placements.items():
|
||||
for placement in placement_list:
|
||||
if placement['type'] == 'LocationGroup':
|
||||
item = placement['item']
|
||||
item_to_place, event_flag = get_item_and_event_flag(item, world, player,
|
||||
dungeon_pool, prize_set, prize_pool)
|
||||
if not item_to_place:
|
||||
continue
|
||||
locations = placement['locations']
|
||||
handled = False
|
||||
while not handled:
|
||||
if isinstance(locations, dict):
|
||||
chosen_loc = random.choices(list(locations.keys()), list(locations.values()), k=1)[0]
|
||||
else: # if isinstance(locations, list):
|
||||
chosen_loc = random.choice(locations)
|
||||
if chosen_loc == 'Random':
|
||||
if is_dungeon_item(item_to_place.name, world, item_to_place.player):
|
||||
dungeon_pool.append(item_to_place)
|
||||
elif item_to_place.name in prize_set:
|
||||
prize_pool.append(item_to_place.name)
|
||||
else:
|
||||
world.itempool.append(item_to_place)
|
||||
else:
|
||||
loc = world.get_location(chosen_loc, player)
|
||||
if loc.item:
|
||||
continue
|
||||
world.push_item(loc, item_to_place, False)
|
||||
track_outside_keys(item_to_place, loc, world)
|
||||
track_dungeon_items(item_to_place, loc, world)
|
||||
loc.event = (event_flag or item_to_place.advancement
|
||||
or item_to_place.bigkey or item_to_place.smallkey)
|
||||
handled = True
|
||||
elif placement['type'] == 'NotLocationGroup':
|
||||
item = placement['item']
|
||||
item_parts = item.split('#')
|
||||
item_player = player if len(item_parts) < 2 else int(item_parts[1])
|
||||
item_name = item_parts[0]
|
||||
world.item_pool_config.restricted[(item_name, item_player)] = placement['locations']
|
||||
elif placement['type'] == 'PreferredLocationGroup':
|
||||
item = placement['item']
|
||||
item_parts = item.split('#')
|
||||
item_player = player if len(item_parts) < 2 else int(item_parts[1])
|
||||
item_name = item_parts[0]
|
||||
world.item_pool_config.preferred[(item_name, item_player)] = placement['locations']
|
||||
elif placement['type'] == 'Verification':
|
||||
item = placement['item']
|
||||
item_parts = item.split('#')
|
||||
item_player = player if len(item_parts) < 2 else int(item_parts[1])
|
||||
item_name = item_parts[0]
|
||||
world.item_pool_config.verify[(item_name, item_player)] = placement['locations']
|
||||
world.item_pool_config.verify_target += len(placement['locations'])
|
||||
|
||||
|
||||
def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool):
|
||||
item_parts = item.split('#')
|
||||
item_player = player if len(item_parts) < 2 else int(item_parts[1])
|
||||
item_name = item_parts[0]
|
||||
event_flag = False
|
||||
if is_dungeon_item(item_name, world, item_player):
|
||||
item_to_place = next(x for x in dungeon_pool
|
||||
if x.name == item_name and x.player == item_player)
|
||||
dungeon_pool.remove(item_to_place)
|
||||
event_flag = True
|
||||
elif item_name in prize_set:
|
||||
item_player = player # prizes must be for that player
|
||||
item_to_place = ItemFactory(item_name, item_player)
|
||||
prize_pool.remove(item_name)
|
||||
event_flag = True
|
||||
else:
|
||||
matcher = lambda x: x.name == item_name and x.player == item_player
|
||||
if item_name == 'Bottle':
|
||||
matcher = lambda x: x.name.startswith(item_name) and x.player == item_player
|
||||
item_to_place = next((x for x in world.itempool if matcher(x)), None)
|
||||
if item_to_place is None:
|
||||
return None, event_flag
|
||||
else:
|
||||
world.itempool.remove(item_to_place)
|
||||
return item_to_place, event_flag
|
||||
|
||||
|
||||
def is_dungeon_item(item, world, player):
|
||||
return ((item.startswith('Small Key') and world.keyshuffle[player] == 'none')
|
||||
or (item.startswith('Big Key') and not world.bigkeyshuffle[player])
|
||||
or (item.startswith('Compass') and not world.compassshuffle[player])
|
||||
or (item.startswith('Map') and not world.mapshuffle[player]))
|
||||
|
||||
Reference in New Issue
Block a user