Make triforce hunt item pool fully dynamic

This commit is contained in:
Fabian Dill 2020-08-25 19:45:33 +02:00
parent 3271460c68
commit 6370f4793b
3 changed files with 34 additions and 25 deletions

View File

@ -385,7 +385,7 @@ class World(object):
return False return False
def has_beaten_game(self, state, player: Union[None, int] = None): def has_beaten_game(self, state, player: Optional[int] = None):
if player: if player:
return state.has('Triforce', player) or state.world.logic[player] == 'nologic' return state.has('Triforce', player) or state.world.logic[player] == 'nologic'
else: else:

View File

@ -43,7 +43,7 @@ Difficulty = namedtuple('Difficulty',
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivemagic', 'basicmagic', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivemagic', 'basicmagic',
'progressivesword', 'basicsword', 'progressivebow', 'basicbow', 'timedohko', 'timedother', 'progressivesword', 'basicsword', 'progressivebow', 'basicbow', 'timedohko', 'timedother',
'triforcehunt', 'universal_keys', 'universal_keys',
'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'extras', 'progressive_sword_limit', 'progressive_shield_limit',
'progressive_armor_limit', 'progressive_bottle_limit', 'progressive_armor_limit', 'progressive_bottle_limit',
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
@ -69,7 +69,6 @@ difficulties = {
basicbow=['Bow', 'Silver Bow'] * 2, basicbow=['Bow', 'Silver Bow'] * 2,
timedohko=['Green Clock'] * 25, timedohko=['Green Clock'] * 25,
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt=['Triforce Piece'] * 30,
universal_keys=['Small Key (Universal)'] * 28, universal_keys=['Small Key (Universal)'] * 28,
extras=[easyfirst15extra, easysecond15extra, easythird10extra, easyfourth5extra, easyfinal25extra], extras=[easyfirst15extra, easysecond15extra, easythird10extra, easyfourth5extra, easyfinal25extra],
progressive_sword_limit=8, progressive_sword_limit=8,
@ -98,7 +97,6 @@ difficulties = {
basicbow=['Bow', 'Silver Bow'], basicbow=['Bow', 'Silver Bow'],
timedohko=['Green Clock'] * 25, timedohko=['Green Clock'] * 25,
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt=['Triforce Piece'] * 30,
universal_keys=['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10, universal_keys=['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit=4, progressive_sword_limit=4,
@ -127,7 +125,6 @@ difficulties = {
basicbow=['Bow'] * 2, basicbow=['Bow'] * 2,
timedohko=['Green Clock'] * 25, timedohko=['Green Clock'] * 25,
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt=['Triforce Piece'] * 30,
universal_keys=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16, universal_keys=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16,
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit=3, progressive_sword_limit=3,
@ -157,7 +154,6 @@ difficulties = {
basicbow=['Bow'] * 2, basicbow=['Bow'] * 2,
timedohko=['Green Clock'] * 20 + ['Red Clock'] * 5, timedohko=['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt=['Triforce Piece'] * 30,
universal_keys=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16, universal_keys=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16,
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit=2, progressive_sword_limit=2,
@ -226,13 +222,14 @@ def generate_itempool(world, player: int):
world.get_location('Floodgate', player).locked = True world.get_location('Floodgate', player).locked = True
# set up item pool # set up item pool
additional_triforce_pieces = 0
if world.custom: if world.custom:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count,
treasure_hunt_icon) = make_custom_item_pool(world, player) treasure_hunt_icon) = make_custom_item_pool(world, player)
world.rupoor_cost = min(world.customitemarray[67], 9999) world.rupoor_cost = min(world.customitemarray[67], 9999)
else: else:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon) = get_pool_core( pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, \
world, player) treasure_hunt_icon, additional_triforce_pieces = get_pool_core(world, player)
for item in precollected_items: for item in precollected_items:
world.push_precollected(ItemFactory(item, player)) world.push_precollected(ItemFactory(item, player))
@ -299,21 +296,27 @@ def generate_itempool(world, player: int):
if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type: if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type:
choice = world.random.choices(list(beeweights[world.beemizer[item.player]].keys()), choice = world.random.choices(list(beeweights[world.beemizer[item.player]].keys()),
weights=list(beeweights[world.beemizer[item.player]].values()))[0] weights=list(beeweights[world.beemizer[item.player]].values()))[0]
return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player) return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee",
player)
return item return item
progressionitems = [item for item in items if item.advancement or item.priority or item.type] progressionitems = [item for item in items if item.advancement or item.priority or item.type]
nonprogressionitems = [beemizer(item) for item in items if not item.advancement and not item.priority and not item.type] nonprogressionitems = [beemizer(item) for item in items if
not item.advancement and not item.priority and not item.type]
world.random.shuffle(nonprogressionitems) world.random.shuffle(nonprogressionitems)
triforce_pieces = world.triforce_pieces_available[player] if additional_triforce_pieces:
if 'triforcehunt' in world.goal[player] and triforce_pieces > 30: if additional_triforce_pieces > len(nonprogressionitems):
progressionitems += [ItemFactory("Triforce Piece", player)] * (triforce_pieces - 30) raise FillError(f"Not enough non-progression items to replace with Triforce pieces found for player "
nonprogressionitems = nonprogressionitems[(triforce_pieces - 30):] f"{world.get_player_names(player)}.")
progressionitems += [ItemFactory("Triforce Piece", player)] * additional_triforce_pieces
nonprogressionitems.sort(key=lambda item: int("Heart" in item.name)) # try to keep hearts in the pool
nonprogressionitems = nonprogressionitems[additional_triforce_pieces:]
world.random.shuffle(nonprogressionitems)
# shuffle medallions # shuffle medallions
mm_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)] mm_medallion = world.random.choice(['Ether', 'Quake', 'Bombos'])
tr_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)] tr_medallion = world.random.choice(['Ether', 'Quake', 'Bombos'])
world.required_medallions[player] = (mm_medallion, tr_medallion) world.required_medallions[player] = (mm_medallion, tr_medallion)
place_bosses(world, player) place_bosses(world, player)
@ -341,7 +344,7 @@ def shuffle_shops(world, items, player: int):
shop.clear_inventory() shop.clear_inventory()
for i, item in enumerate(items): for i, item in enumerate(items):
if item.name.startswith(("Bombs", "Arrows", "Rupee", "Nothing")): if not "Heart" in item.name:
items[i] = ItemFactory(new_items.pop(), player) items[i] = ItemFactory(new_items.pop(), player)
if not new_items: if not new_items:
break break
@ -631,11 +634,12 @@ def get_pool_core(world, player: int):
pool.extend(diff.timedohko) pool.extend(diff.timedohko)
extraitems -= len(diff.timedohko) extraitems -= len(diff.timedohko)
clock_mode = 'countdown-ohko' clock_mode = 'countdown-ohko'
additional_pieces_to_place = 0
if 'triforcehunt' in goal: if 'triforcehunt' in goal:
while len(diff.triforcehunt) > world.triforce_pieces_available[player]: pieces_in_core = min(extraitems, world.triforce_pieces_available[player])
diff.triforcehunt.pop() additional_pieces_to_place = world.triforce_pieces_available[player] - pieces_in_core
pool.extend(diff.triforcehunt) pool.extend(["Triforce Piece"] * pieces_in_core)
extraitems -= len(diff.triforcehunt) extraitems -= pieces_in_core
treasure_hunt_count = world.triforce_pieces_required[player] treasure_hunt_count = world.triforce_pieces_required[player]
treasure_hunt_icon = 'Triforce Piece' treasure_hunt_icon = 'Triforce Piece'
@ -646,6 +650,8 @@ def get_pool_core(world, player: int):
elif extraitems > 0: elif extraitems > 0:
pool.extend(world.random.sample(extra, extraitems)) pool.extend(world.random.sample(extra, extraitems))
break break
else:
break
if goal == 'pedestal' and swords != 'vanilla': if goal == 'pedestal' and swords != 'vanilla':
place_item('Master Sword Pedestal', 'Triforce') place_item('Master Sword Pedestal', 'Triforce')
@ -665,7 +671,8 @@ def get_pool_core(world, player: int):
place_item(key_location, 'Small Key (Universal)') place_item(key_location, 'Small Key (Universal)')
else: else:
pool.extend(['Small Key (Universal)']) pool.extend(['Small Key (Universal)'])
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon) return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon,
additional_pieces_to_place)
def make_custom_item_pool(world, player): def make_custom_item_pool(world, player):

View File

@ -242,7 +242,7 @@ def get_choice(option, root, value=None) -> typing.Any:
def handle_name(name: str): def handle_name(name: str):
return name.strip().replace(' ', '_') return name.strip().replace(' ', '_')[:16]
def roll_settings(weights): def roll_settings(weights):
@ -310,7 +310,7 @@ def roll_settings(weights):
ret.triforce_pieces_required = get_choice('triforce_pieces_required', weights, 20) ret.triforce_pieces_required = get_choice('triforce_pieces_required', weights, 20)
ret.triforce_pieces_required = min(max(1, int(ret.triforce_pieces_required)), 90) ret.triforce_pieces_required = min(max(1, int(ret.triforce_pieces_required)), 90)
ret.shop_shuffle = get_choice('shop_shuffle', weights, False) ret.shop_shuffle = get_choice('shop_shuffle', weights, '')
if not ret.shop_shuffle: if not ret.shop_shuffle:
ret.shop_shuffle = '' ret.shop_shuffle = ''
@ -413,7 +413,9 @@ def roll_settings(weights):
ret.remote_items = get_choice('remote_items', weights, False) ret.remote_items = get_choice('remote_items', weights, False)
if get_choice("local_keys", weights, "l" in dungeon_items): if get_choice("local_keys", weights, "l" in dungeon_items):
ret.local_items = item_name_groups["Small Keys"] | item_name_groups["Big Keys"] ret.local_items = item_name_groups["Small Keys"] if "s" in dungeon_items else set() \
| item_name_groups[
"Big Keys"] if "b" in dungeon_items else set()
else: else:
ret.local_items = set() ret.local_items = set()
for item_name in weights.get('local_items', []): for item_name in weights.get('local_items', []):