Customizer item_pool updates

This commit is contained in:
aerinon
2022-04-13 13:22:15 -06:00
parent 52a0e88d12
commit 833b570af6
4 changed files with 154 additions and 24 deletions

View File

@@ -1,4 +1,4 @@
from collections import namedtuple
from collections import namedtuple, defaultdict
import logging
import math
import RaceRandom as random
@@ -257,15 +257,17 @@ def generate_itempool(world, player):
world.get_location('Zelda Drop Off', player).locked = True
# set up item pool
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:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, 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)
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])
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]
@@ -311,7 +313,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):
@@ -373,7 +375,10 @@ 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, tr_medallion = None, None
@@ -403,15 +408,15 @@ def generate_itempool(world, player):
set_up_shops(world, player)
if world.retro[player]:
set_up_take_anys(world, player)
if world.dropshuffle[player]:
set_up_take_anys(world, player, skip_pool_adjustments)
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)
@@ -426,7 +431,7 @@ take_any_locations = [
'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint']
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')
@@ -450,12 +455,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])
@@ -473,7 +479,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))
@@ -1032,12 +1038,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 not world.keyshuffle[player]:
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)
@@ -1049,12 +1105,75 @@ 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']
if world.shopsanity[player]:
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion'])
if world.retro[player]:
guaranteed_items.append('Small Key (Universal)')
for item in guaranteed_items:
if item not in pool:
pool.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):
pool.append('Progressive Glove')
if world.bombbag[player]:
if 'Bomb Upgrade (+10)' not in pool:
pool.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):
pool.append('Progressive Sword')
bow_found = next((i for i in pool if i in {'Bow', 'Progressive Bow'}), None)
if not bow_found:
pool.append('Progressive Bow')
heart_found = next((i for i in pool if i in {'Boss Heart Container', 'Sanctuary Heart Container'}), None)
if heart_found is None:
pool.append('Boss Heart Container')
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))
ttl_locations = sum(1 for x in world.get_unfilled_locations(player) if '- Prize' not in x.name)
pool_size = len(get_player_dungeon_item_pool(world, player)) + len(pool)
if pool_size < ttl_locations:
amount_to_add = ttl_locations - pool_size
pool.extend(random.choices(list(filler_items.keys()), filler_items.values(), k=amount_to_add))
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 get_player_dungeon_item_pool(world, player):
return [item for dungeon in world.dungeons for item in dungeon.all_items
if dungeon.player == player and item.location is None]
# To display, count must be between 1 and 254 - larger values are not yet supported
def set_default_triforce(goal, custom_goal, custom_total):
triforce_goal, triforce_total = 0, 0

View File

@@ -154,6 +154,10 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
## Notes and Bug Fixes
#### Customizer
* Fixed up the item_pool section to skip a lot of pool manipulations. Key item will be added to the bow if not detected. Extra dungeon items can be added to the pool and will be confined to the dungeon if possible (and not shuffled). If the pool isn't full, junk items are added to the pool to fill it out.
#### Volatile
* 1.0.1.12

View File

@@ -50,20 +50,14 @@ Rom/Adjust flags like sprite, quickswap are not outputing with the print_custom_
This must be defined by player. Each player number should be listed with the appropriate pool.
Then each player can have the entire item pool defined. The name of item should be followed by the number of that item in the pool. All key items need to be listed here for now.
Then each player can have the entire item pool defined. The name of item should be followed by the number of that item in the pool. Many key items will be added to the pool if not detected.
`Bottle (Random)` is supported to randomize bottle contents according to those allowed by difficulty. Pendants and crystals are supported here.
##### Known Issues
##### Caveat
1. Dungeon items amount can be increased but not eliminated (as the amount of each dungeon item is either pre-determined or calculated by door rando) and these extra items may not be confined to the dungeon
2. Door rando removes Red Rupees from the pool to make room for extra dungeon items as needed.
3. Shopsanity appends extra shop items to the pool.
4. Beemizer runs after pool creation changing junk items into bees
5. Retro + Shopsanity adds more items to the pool
6. Retro + either of dropshuffle or pottery adds keys to the pool
7. Most pottery settings add a large amount of junk items to the pool
Dungeon items amount can be increased (but not decreased as the minimum of each dungeon item is either pre-determined or calculated by door rando) if the type of dungeon item is not shuffled then it is attempted to be placed in the dungeon. Extra item beyond dungeon capacity not be confined to the dungeon.
### placements

View File

@@ -75,6 +75,12 @@ class CustomSettings(object):
args.shopsanity[p] = get_setting(settings['shopsanity'], args.shopsanity[p])
args.dropshuffle[p] = get_setting(settings['dropshuffle'], args.dropshuffle[p])
args.pottery[p] = get_setting(settings['pottery'], args.pottery[p])
if get_setting(settings['keydropshuffle'], args.keydropshuffle[p]):
args.dropshuffle[p] = True
if args.pottery[p] == 'none':
args.pottery[p] = 'keys'
args.mixed_travel[p] = get_setting(settings['mixed_travel'], args.mixed_travel[p])
args.standardize_palettes[p] = get_setting(settings['standardize_palettes'],
args.standardize_palettes[p])
@@ -88,6 +94,13 @@ class CustomSettings(object):
args.keyshuffle[p] = get_setting(settings['keyshuffle'], args.keyshuffle[p])
args.mapshuffle[p] = get_setting(settings['mapshuffle'], args.mapshuffle[p])
args.compassshuffle[p] = get_setting(settings['compassshuffle'], args.compassshuffle[p])
if get_setting(settings['keysanity'], args.keysanity):
args.bigkeyshuffle[p] = True
args.keyshuffle[p] = True
args.mapshuffle[p] = True
args.compassshuffle[p] = True
args.shufflebosses[p] = get_setting(settings['shufflebosses'], args.shufflebosses[p])
args.shuffleenemies[p] = get_setting(settings['shuffleenemies'], args.shuffleenemies[p])
args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p])