def ShopSlotFill(world): shop_slots: Set[Location] = { location for shop_locations in (shop.region.locations for shop in world.shops) for location in shop_locations if location.shop_slot } removed = set() for location in shop_slots: slot_num = int(location.name[-1]) - 1 shop: Shop = location.parent_region.shop if not shop.can_push_inventory( slot_num) or location.shop_slot_disabled: location.shop_slot_disabled = True removed.add(location) if removed: shop_slots -= removed if shop_slots: del shop_slots from Fill import swap_location_item # TODO: allow each game to register a blacklist to be used here? blacklist_words = {"Rupee"} blacklist_words = { item_name for item_name in item_table if any(blacklist_word in item_name for blacklist_word in blacklist_words) } blacklist_words.add("Bee") locations_per_sphere = list( list(sphere) for sphere in world.get_spheres()) # currently special care needs to be taken so that Shop.region.locations.item is identical to Shop.inventory # Potentially create Locations as needed and make inventory the only source, to prevent divergence cumu_weights = [] shops_per_sphere = [] candidates_per_sphere = [] # sort spheres into piles of valid candidates and shops for sphere in locations_per_sphere: current_shops_slots = [] current_candidates = [] shops_per_sphere.append(current_shops_slots) candidates_per_sphere.append(current_candidates) for location in sphere: if location.shop_slot: if not location.shop_slot_disabled: current_shops_slots.append(location) elif not location.locked and not location.item.name in blacklist_words: current_candidates.append(location) if cumu_weights: x = cumu_weights[-1] else: x = 0 cumu_weights.append(len(current_candidates) + x) world.random.shuffle(current_candidates) del locations_per_sphere total_spheres = len(candidates_per_sphere) for i, current_shop_slots in enumerate(shops_per_sphere): if current_shop_slots: candidate_sphere_ids = list(range(i, total_spheres)) for location in current_shop_slots: shop: Shop = location.parent_region.shop swapping_sphere_id = world.random.choices( candidate_sphere_ids, cum_weights=cumu_weights[i:])[0] swapping_sphere: list = candidates_per_sphere[ swapping_sphere_id] for c in swapping_sphere: # chosen item locations if c.item_rule(location.item) and location.item_rule( c.item): swap_location_item(c, location, check_locked=False) logger.debug( f'Swapping {c} into {location}:: {location.item}' ) break else: # This *should* never happen. But let's fail safely just in case. logger.warning( "Ran out of ShopShuffle Item candidate locations.") location.shop_slot_disabled = True continue # remove candidate swapping_sphere.remove(c) cumu_weights[swapping_sphere_id] -= 1 item_name = location.item.name if any(x in item_name for x in [ 'Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart' ]): price = world.random.randrange(1, 7) elif any(x in item_name for x in ['Arrow', 'Bomb', 'Clock']): price = world.random.randrange(2, 14) elif any(x in item_name for x in ['Small Key', 'Heart']): price = world.random.randrange(4, 28) else: price = world.random.randrange(8, 56) shop.push_inventory( int(location.name[-1]) - 1, item_name, price * 5, 1, location.item.player if location.item.player != location.player else 0)
def ShopSlotFill(world): shop_slots: Set[Location] = {location for shop_locations in (shop.region.locations for shop in world.shops) for location in shop_locations if location.shop_slot} removed = set() for location in shop_slots: slot_num = int(location.name[-1]) - 1 shop: Shop = location.parent_region.shop if not shop.can_push_inventory(slot_num) or location.shop_slot_disabled: location.shop_slot_disabled = True removed.add(location) if removed: shop_slots -= removed if shop_slots: from Fill import swap_location_item # TODO: allow each game to register a blacklist to be used here? blacklist_words = {"Rupee"} blacklist_words = {item_name for item_name in item_table if any( blacklist_word in item_name for blacklist_word in blacklist_words)} blacklist_words.add("Bee") candidates_per_sphere = list(list(sphere) for sphere in world.get_spheres()) candidate_condition = lambda location: not location.locked and \ not location.shop_slot and \ not location.item.name in blacklist_words # currently special care needs to be taken so that Shop.region.locations.item is identical to Shop.inventory # Potentially create Locations as needed and make inventory the only source, to prevent divergence cumu_weights = [] for sphere in candidates_per_sphere: if cumu_weights: x = cumu_weights[-1] else: x = 0 cumu_weights.append(len(sphere) + x) world.random.shuffle(sphere) for i, sphere in enumerate(candidates_per_sphere): current_shop_slots = [location for location in sphere if location.shop_slot and not location.shop_slot_disabled] if current_shop_slots: for location in current_shop_slots: shop: Shop = location.parent_region.shop # TODO: might need to implement trying randomly across spheres until canditates are exhausted. # As spheres may be as small as one item. swapping_sphere = world.random.choices(candidates_per_sphere[i:], cum_weights=cumu_weights[i:])[0] for c in swapping_sphere: # chosen item locations if candidate_condition(c) and c.item_rule(location.item) and location.item_rule(c.item): swap_location_item(c, location, check_locked=False) logger.debug(f'Swapping {c} into {location}:: {location.item}') break else: # This *should* never happen. But let's fail safely just in case. logger.warning("Ran out of ShopShuffle Item candidate locations.") location.shop_slot_disabled = True continue item_name = location.item.name if any(x in item_name for x in ['Single Bomb', 'Single Arrow', 'Piece of Heart']): price = world.random.randrange(1, 7) elif any(x in item_name for x in ['Arrow', 'Bomb', 'Clock']): price = world.random.randrange(2, 14) elif any(x in item_name for x in ['Compass', 'Map', 'Small Key', 'Clock', 'Heart']): price = world.random.randrange(4, 28) else: price = world.random.randrange(8, 56) price *= 5 shop.push_inventory(int(location.name[-1]) - 1, item_name, price, 1, location.item.player if location.item.player != location.player else 0)
def ShopSlotFill(world): shop_slots: Set[ALttPLocation] = { location for shop_locations in (shop.region.locations for shop in world.shops) for location in shop_locations if location.shop_slot is not None } removed = set() for location in shop_slots: shop: Shop = location.parent_region.shop if not shop.can_push_inventory( location.shop_slot) or location.shop_slot_disabled: location.shop_slot_disabled = True removed.add(location) if removed: shop_slots -= removed if shop_slots: logger.info("Filling LttP Shop Slots") del shop_slots from Fill import swap_location_item # TODO: allow each game to register a blacklist to be used here? blacklist_words = {"Rupee"} blacklist_words = { item_name for item_name in item_table if any(blacklist_word in item_name for blacklist_word in blacklist_words) } blacklist_words.add("Bee") locations_per_sphere = list( sorted(sphere, key=lambda location: location.name) for sphere in world.get_spheres()) # currently special care needs to be taken so that Shop.region.locations.item is identical to Shop.inventory # Potentially create Locations as needed and make inventory the only source, to prevent divergence cumu_weights = [] shops_per_sphere = [] candidates_per_sphere = [] # sort spheres into piles of valid candidates and shops for sphere in locations_per_sphere: current_shops_slots = [] current_candidates = [] shops_per_sphere.append(current_shops_slots) candidates_per_sphere.append(current_candidates) for location in sphere: if location.shop_slot is not None: if not location.shop_slot_disabled: current_shops_slots.append(location) elif not location.locked and not location.item.name in blacklist_words: current_candidates.append(location) if cumu_weights: x = cumu_weights[-1] else: x = 0 cumu_weights.append(len(current_candidates) + x) world.random.shuffle(current_candidates) del locations_per_sphere for i, current_shop_slots in enumerate(shops_per_sphere): if current_shop_slots: # getting all candidates and shuffling them feels cpu expensive, there may be a better method candidates = [(location, i) for i, candidates in enumerate( candidates_per_sphere[i:], start=i) for location in candidates] world.random.shuffle(candidates) for location in current_shop_slots: shop: Shop = location.parent_region.shop for index, (c, swapping_sphere_id) in enumerate( candidates): # chosen item locations if c.item_rule(location.item) and location.item_rule( c.item): swap_location_item(c, location, check_locked=False) logger.debug( f'Swapping {c} into {location}:: {location.item}' ) # remove candidate candidates_per_sphere[swapping_sphere_id].remove(c) candidates.pop(index) break else: # This *should* never happen. But let's fail safely just in case. logger.warning( "Ran out of ShopShuffle Item candidate locations.") location.shop_slot_disabled = True continue item_name = location.item.name if location.item.game != "A Link to the Past": if location.item.advancement: price = world.random.randrange(8, 56) elif location.item.useful: price = world.random.randrange(4, 28) else: price = world.random.randrange(2, 14) elif any(x in item_name for x in [ 'Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart' ]): price = world.random.randrange(1, 7) elif any(x in item_name for x in ['Arrow', 'Bomb', 'Clock']): price = world.random.randrange(2, 14) elif any(x in item_name for x in ['Small Key', 'Heart']): price = world.random.randrange(4, 28) else: price = world.random.randrange(8, 56) shop.push_inventory( location.shop_slot, item_name, min( int(price * world.shop_price_modifier[location.player] / 100) * 5, 9999), 1, location.item.player if location.item.player != location.player else 0) if 'P' in world.shop_shuffle[location.player]: price_to_funny_price( world, shop.inventory[location.shop_slot], location.player)