Main structure for various biased fills
Lots of help around correctly reserving locations
This commit is contained in:
403
Fill.py
403
Fill.py
@@ -3,176 +3,74 @@ import collections
|
||||
import itertools
|
||||
import logging
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from BaseClasses import CollectionState, FillError
|
||||
from Items import ItemFactory
|
||||
from Regions import shop_to_location_table, retro_shops
|
||||
from source.item.BiasedFill import filter_locations, classify_major_items, split_pool
|
||||
|
||||
|
||||
class FillError(RuntimeError):
|
||||
pass
|
||||
|
||||
def distribute_items_cutoff(world, cutoffrate=0.33):
|
||||
# get list of locations to fill in
|
||||
fill_locations = world.get_unfilled_locations()
|
||||
random.shuffle(fill_locations)
|
||||
|
||||
# get items to distribute
|
||||
random.shuffle(world.itempool)
|
||||
itempool = world.itempool
|
||||
|
||||
total_advancement_items = len([item for item in itempool if item.advancement])
|
||||
placed_advancement_items = 0
|
||||
|
||||
progress_done = False
|
||||
advancement_placed = False
|
||||
|
||||
# sweep once to pick up preplaced items
|
||||
world.state.sweep_for_events()
|
||||
|
||||
while itempool and fill_locations:
|
||||
candidate_item_to_place = None
|
||||
item_to_place = None
|
||||
for item in itempool:
|
||||
if advancement_placed or (progress_done and (item.advancement or item.priority)):
|
||||
item_to_place = item
|
||||
break
|
||||
if item.advancement:
|
||||
candidate_item_to_place = item
|
||||
if world.unlocks_new_location(item):
|
||||
item_to_place = item
|
||||
placed_advancement_items += 1
|
||||
break
|
||||
|
||||
if item_to_place is None:
|
||||
# check if we can reach all locations and that is why we find no new locations to place
|
||||
if not progress_done and len(world.get_reachable_locations()) == len(world.get_locations()):
|
||||
progress_done = True
|
||||
continue
|
||||
# check if we have now placed all advancement items
|
||||
if progress_done:
|
||||
advancement_placed = True
|
||||
continue
|
||||
# we might be in a situation where all new locations require multiple items to reach. If that is the case, just place any advancement item we've found and continue trying
|
||||
if candidate_item_to_place is not None:
|
||||
item_to_place = candidate_item_to_place
|
||||
placed_advancement_items += 1
|
||||
else:
|
||||
# we placed all available progress items. Maybe the game can be beaten anyway?
|
||||
if world.can_beat_game():
|
||||
logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.')
|
||||
progress_done = True
|
||||
continue
|
||||
raise FillError('No more progress items left to place.')
|
||||
|
||||
spot_to_fill = None
|
||||
for location in fill_locations if placed_advancement_items / total_advancement_items < cutoffrate else reversed(fill_locations):
|
||||
if location.can_fill(world.state, item_to_place):
|
||||
spot_to_fill = location
|
||||
break
|
||||
|
||||
if spot_to_fill is None:
|
||||
# we filled all reachable spots. Maybe the game can be beaten anyway?
|
||||
if world.can_beat_game():
|
||||
logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
|
||||
break
|
||||
raise FillError('No more spots to place %s' % item_to_place)
|
||||
|
||||
world.push_item(spot_to_fill, item_to_place, True)
|
||||
itempool.remove(item_to_place)
|
||||
fill_locations.remove(spot_to_fill)
|
||||
unplaced = [item.name for item in itempool]
|
||||
unfilled = [location.name for location in fill_locations]
|
||||
if unplaced or unfilled:
|
||||
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||
def get_dungeon_item_pool(world):
|
||||
return [item for dungeon in world.dungeons for item in dungeon.all_items]
|
||||
|
||||
|
||||
def distribute_items_staleness(world):
|
||||
# get list of locations to fill in
|
||||
fill_locations = world.get_unfilled_locations()
|
||||
random.shuffle(fill_locations)
|
||||
def promote_dungeon_items(world):
|
||||
world.itempool += get_dungeon_item_pool(world)
|
||||
|
||||
# get items to distribute
|
||||
random.shuffle(world.itempool)
|
||||
itempool = world.itempool
|
||||
for item in world.get_items():
|
||||
if item.smallkey or item.bigkey:
|
||||
item.advancement = True
|
||||
elif item.map or item.compass:
|
||||
item.priority = True
|
||||
dungeon_tracking(world)
|
||||
|
||||
progress_done = False
|
||||
advancement_placed = False
|
||||
|
||||
# sweep once to pick up preplaced items
|
||||
world.state.sweep_for_events()
|
||||
def dungeon_tracking(world):
|
||||
for dungeon in world.dungeons:
|
||||
layout = world.dungeon_layouts[dungeon.player][dungeon.name]
|
||||
layout.dungeon_items = len(dungeon.all_items)
|
||||
layout.free_items = layout.location_cnt - layout.dungeon_items
|
||||
|
||||
while itempool and fill_locations:
|
||||
candidate_item_to_place = None
|
||||
item_to_place = None
|
||||
for item in itempool:
|
||||
if advancement_placed or (progress_done and (item.advancement or item.priority)):
|
||||
item_to_place = item
|
||||
break
|
||||
if item.advancement:
|
||||
candidate_item_to_place = item
|
||||
if world.unlocks_new_location(item):
|
||||
item_to_place = item
|
||||
break
|
||||
|
||||
if item_to_place is None:
|
||||
# check if we can reach all locations and that is why we find no new locations to place
|
||||
if not progress_done and len(world.get_reachable_locations()) == len(world.get_locations()):
|
||||
progress_done = True
|
||||
continue
|
||||
# check if we have now placed all advancement items
|
||||
if progress_done:
|
||||
advancement_placed = True
|
||||
continue
|
||||
# we might be in a situation where all new locations require multiple items to reach. If that is the case, just place any advancement item we've found and continue trying
|
||||
if candidate_item_to_place is not None:
|
||||
item_to_place = candidate_item_to_place
|
||||
else:
|
||||
# we placed all available progress items. Maybe the game can be beaten anyway?
|
||||
if world.can_beat_game():
|
||||
logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.')
|
||||
progress_done = True
|
||||
continue
|
||||
raise FillError('No more progress items left to place.')
|
||||
def fill_dungeons_restrictive(world, shuffled_locations):
|
||||
dungeon_tracking(world)
|
||||
all_state_base = world.get_all_state()
|
||||
|
||||
spot_to_fill = None
|
||||
for location in fill_locations:
|
||||
# increase likelyhood of skipping a location if it has been found stale
|
||||
if not progress_done and random.randint(0, location.staleness_count) > 2:
|
||||
continue
|
||||
# for player in range(1, world.players + 1):
|
||||
# pinball_room = world.get_location('Skull Woods - Pinball Room', player)
|
||||
# if world.retro[player]:
|
||||
# world.push_item(pinball_room, ItemFactory('Small Key (Universal)', player), False)
|
||||
# else:
|
||||
# world.push_item(pinball_room, ItemFactory('Small Key (Skull Woods)', player), False)
|
||||
# pinball_room.event = True
|
||||
# pinball_room.locked = True
|
||||
# shuffled_locations.remove(pinball_room)
|
||||
|
||||
if location.can_fill(world.state, item_to_place):
|
||||
spot_to_fill = location
|
||||
break
|
||||
else:
|
||||
location.staleness_count += 1
|
||||
# with shuffled dungeon items they are distributed as part of the normal item pool
|
||||
for item in world.get_items():
|
||||
if (item.smallkey and world.keyshuffle[item.player]) or (item.bigkey and world.bigkeyshuffle[item.player]):
|
||||
item.advancement = True
|
||||
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
|
||||
item.priority = True
|
||||
|
||||
# might have skipped too many locations due to potential staleness. Do not check for staleness now to find a candidate
|
||||
if spot_to_fill is None:
|
||||
for location in fill_locations:
|
||||
if location.can_fill(world.state, item_to_place):
|
||||
spot_to_fill = location
|
||||
break
|
||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)]
|
||||
|
||||
if spot_to_fill is None:
|
||||
# we filled all reachable spots. Maybe the game can be beaten anyway?
|
||||
if world.can_beat_game():
|
||||
logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
|
||||
break
|
||||
raise FillError('No more spots to place %s' % item_to_place)
|
||||
# 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))
|
||||
|
||||
world.push_item(spot_to_fill, item_to_place, True)
|
||||
itempool.remove(item_to_place)
|
||||
fill_locations.remove(spot_to_fill)
|
||||
fill_restrictive(world, all_state_base, shuffled_locations, dungeon_items,
|
||||
keys_in_itempool={player: not world.keyshuffle[player] for player in range(1, world.players+1)},
|
||||
single_player_placement=True)
|
||||
|
||||
unplaced = [item.name for item in itempool]
|
||||
unfilled = [location.name for location in fill_locations]
|
||||
if unplaced or unfilled:
|
||||
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||
|
||||
def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool = None, single_player_placement = False):
|
||||
def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool=None, single_player_placement=False,
|
||||
reserved_items=None):
|
||||
if not reserved_items:
|
||||
reserved_items = []
|
||||
|
||||
def sweep_from_pool():
|
||||
new_state = base_state.copy()
|
||||
for item in itempool:
|
||||
for item in itempool + reserved_items:
|
||||
new_state.collect(item, True)
|
||||
new_state.sweep_for_events()
|
||||
return new_state
|
||||
@@ -201,41 +99,56 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool =
|
||||
|
||||
spot_to_fill = None
|
||||
|
||||
for location in locations:
|
||||
if item_to_place.smallkey or item_to_place.bigkey: # a better test to see if a key can go there
|
||||
location.item = item_to_place
|
||||
test_state = maximum_exploration_state.copy()
|
||||
test_state.stale[item_to_place.player] = True
|
||||
else:
|
||||
test_state = maximum_exploration_state
|
||||
if (not single_player_placement or location.player == item_to_place.player)\
|
||||
and location.can_fill(test_state, item_to_place, perform_access_check)\
|
||||
and valid_key_placement(item_to_place, location, itempool if (keys_in_itempool and keys_in_itempool[item_to_place.player]) else world.itempool, world):
|
||||
spot_to_fill = location
|
||||
item_locations = filter_locations(item_to_place, locations, world)
|
||||
for location in item_locations:
|
||||
spot_to_fill = verify_spot_to_fill(location, item_to_place, maximum_exploration_state,
|
||||
single_player_placement, perform_access_check, itempool,
|
||||
keys_in_itempool, world)
|
||||
if spot_to_fill:
|
||||
break
|
||||
if item_to_place.smallkey or item_to_place.bigkey:
|
||||
location.item = None
|
||||
|
||||
if spot_to_fill is None:
|
||||
# we filled all reachable spots. Maybe the game can be beaten anyway?
|
||||
unplaced_items.insert(0, item_to_place)
|
||||
if world.can_beat_game():
|
||||
if world.accessibility[item_to_place.player] != 'none':
|
||||
logging.getLogger('').warning('Not all items placed. Game beatable anyway. (Could not place %s)' % item_to_place)
|
||||
logging.getLogger('').warning('Not all items placed. Game beatable anyway.'
|
||||
f' (Could not place {item_to_place})')
|
||||
continue
|
||||
spot_to_fill = last_ditch_placement(item_to_place, locations, world, maximum_exploration_state,
|
||||
base_state, itempool, keys_in_itempool, single_player_placement)
|
||||
if world.algorithm in ['balanced', 'equitable']:
|
||||
spot_to_fill = last_ditch_placement(item_to_place, locations, world, maximum_exploration_state,
|
||||
base_state, itempool, keys_in_itempool,
|
||||
single_player_placement)
|
||||
if spot_to_fill is None:
|
||||
raise FillError('No more spots to place %s' % item_to_place)
|
||||
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
track_outside_keys(item_to_place, spot_to_fill, world)
|
||||
track_dungeon_items(item_to_place, spot_to_fill, world)
|
||||
locations.remove(spot_to_fill)
|
||||
spot_to_fill.event = True
|
||||
|
||||
itempool.extend(unplaced_items)
|
||||
|
||||
|
||||
def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_placement, perform_access_check,
|
||||
itempool, keys_in_itempool, world):
|
||||
if item_to_place.smallkey or item_to_place.bigkey: # a better test to see if a key can go there
|
||||
location.item = item_to_place
|
||||
test_state = max_exp_state.copy()
|
||||
test_state.stale[item_to_place.player] = True
|
||||
else:
|
||||
test_state = max_exp_state
|
||||
if not single_player_placement or location.player == item_to_place.player:
|
||||
if location.can_fill(test_state, item_to_place, perform_access_check):
|
||||
test_pool = itempool if (keys_in_itempool and keys_in_itempool[item_to_place.player]) else world.itempool
|
||||
if valid_key_placement(item_to_place, location, test_pool, world):
|
||||
if item_to_place.crystal or valid_dungeon_placement(item_to_place, location, world):
|
||||
return location
|
||||
if item_to_place.smallkey or item_to_place.bigkey:
|
||||
location.item = None
|
||||
return None
|
||||
|
||||
|
||||
def valid_key_placement(item, location, itempool, world):
|
||||
if not valid_reserved_placement(item, location, world):
|
||||
return False
|
||||
@@ -259,6 +172,17 @@ def valid_reserved_placement(item, location, world):
|
||||
return True
|
||||
|
||||
|
||||
def valid_dungeon_placement(item, location, world):
|
||||
if location.parent_region.dungeon:
|
||||
layout = world.dungeon_layouts[location.player][location.parent_region.dungeon.name]
|
||||
if not is_dungeon_item(item, world) or item.player != location.player:
|
||||
return layout.free_items > 0
|
||||
else:
|
||||
# the second half probably doesn't matter much - should always return true
|
||||
return item.dungeon == location.parent_region.dungeon.name and layout.dungeon_items > 0
|
||||
return not is_dungeon_item(item, world)
|
||||
|
||||
|
||||
def track_outside_keys(item, location, world):
|
||||
if not item.smallkey:
|
||||
return
|
||||
@@ -270,6 +194,22 @@ def track_outside_keys(item, location, world):
|
||||
world.key_logic[item.player][item_dungeon].outside_keys += 1
|
||||
|
||||
|
||||
def track_dungeon_items(item, location, world):
|
||||
if location.parent_region.dungeon and not item.crystal:
|
||||
layout = world.dungeon_layouts[location.player][location.parent_region.dungeon.name]
|
||||
if is_dungeon_item(item, world) and item.player == location.player:
|
||||
layout.dungeon_items -= 1
|
||||
else:
|
||||
layout.free_items -= 1
|
||||
|
||||
|
||||
def is_dungeon_item(item, world):
|
||||
return ((item.smallkey and not world.keyshuffle[item.player])
|
||||
or (item.bigkey and not world.bigkeyshuffle[item.player])
|
||||
or (item.compass and not world.compassshuffle[item.player])
|
||||
or (item.map and not world.mapshuffle[item.player]))
|
||||
|
||||
|
||||
def last_ditch_placement(item_to_place, locations, world, state, base_state, itempool,
|
||||
keys_in_itempool=None, single_player_placement=False):
|
||||
def location_preference(loc):
|
||||
@@ -351,6 +291,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
||||
random.shuffle(fill_locations)
|
||||
|
||||
# get items to distribute
|
||||
classify_major_items(world)
|
||||
random.shuffle(world.itempool)
|
||||
progitempool = [item for item in world.itempool if item.advancement]
|
||||
prioitempool = [item for item in world.itempool if not item.advancement and item.priority]
|
||||
@@ -379,21 +320,53 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
||||
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
|
||||
# todo: crossed
|
||||
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.keyshuffle[item.player] and world.mode[item.player] == 'standard' else 0)
|
||||
keys_in_pool = {player: world.keyshuffle[player] or world.algorithm != 'balanced' for player in range(1, world.players + 1)}
|
||||
if world.algorithm in ['balanced', 'equitable', 'vanilla_bias', 'dungeon_bias', 'entangled']:
|
||||
fill_restrictive(world, world.state, fill_locations, progitempool, keys_in_pool)
|
||||
random.shuffle(fill_locations)
|
||||
if world.algorithm == 'balanced':
|
||||
fast_fill(world, prioitempool, fill_locations)
|
||||
elif world.algorithm == 'vanilla_bias':
|
||||
fast_vanilla_fill(world, prioitempool, fill_locations)
|
||||
elif world.algorithm in ['dungeon_bias', 'entangled']:
|
||||
filtered_fill(world, prioitempool, fill_locations)
|
||||
else: # just need to ensure dungeon items still get placed in dungeons
|
||||
fast_equitable_fill(world, prioitempool, fill_locations)
|
||||
# placeholder work
|
||||
if world.algorithm == 'entangled' and world.players > 1:
|
||||
random.shuffle(fill_locations)
|
||||
placeholder_locations = filter_locations('Placeholder', fill_locations, world)
|
||||
placeholder_items = [item for item in world.itempool if item.name == 'Rupee (1)']
|
||||
for i in placeholder_items:
|
||||
restitempool.remove(i)
|
||||
for l in placeholder_locations:
|
||||
fill_locations.remove(l)
|
||||
filtered_fill(world, placeholder_items, placeholder_locations)
|
||||
else:
|
||||
primary, secondary = split_pool(progitempool, world)
|
||||
fill_restrictive(world, world.state, fill_locations, primary, keys_in_pool, False, secondary)
|
||||
random.shuffle(fill_locations)
|
||||
tertiary, quaternary = split_pool(prioitempool, world)
|
||||
prioitempool = []
|
||||
filtered_equitable_fill(world, tertiary, fill_locations)
|
||||
prioitempool += tertiary
|
||||
random.shuffle(fill_locations)
|
||||
fill_restrictive(world, world.state, fill_locations, secondary, keys_in_pool)
|
||||
random.shuffle(fill_locations)
|
||||
fast_equitable_fill(world, quaternary, fill_locations)
|
||||
prioitempool += quaternary
|
||||
|
||||
fill_restrictive(world, world.state, fill_locations, progitempool,
|
||||
keys_in_itempool={player: world.keyshuffle[player] for player in range(1, world.players + 1)})
|
||||
|
||||
random.shuffle(fill_locations)
|
||||
|
||||
fast_fill(world, prioitempool, fill_locations)
|
||||
|
||||
fast_fill(world, restitempool, fill_locations)
|
||||
if world.algorithm == 'vanilla_bias':
|
||||
fast_vanilla_fill(world, restitempool, fill_locations)
|
||||
else:
|
||||
fast_fill(world, restitempool, fill_locations)
|
||||
|
||||
unplaced = [item.name for item in prioitempool + restitempool]
|
||||
unfilled = [location.name for location in fill_locations]
|
||||
if unplaced or unfilled:
|
||||
logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled)
|
||||
|
||||
|
||||
def fast_fill(world, item_pool, fill_locations):
|
||||
while item_pool and fill_locations:
|
||||
spot_to_fill = fill_locations.pop()
|
||||
@@ -401,70 +374,48 @@ def fast_fill(world, item_pool, fill_locations):
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
|
||||
|
||||
def flood_items(world):
|
||||
# get items to distribute
|
||||
random.shuffle(world.itempool)
|
||||
itempool = world.itempool
|
||||
progress_done = False
|
||||
def filtered_fill(world, item_pool, fill_locations):
|
||||
while item_pool and fill_locations:
|
||||
item_to_place = item_pool.pop()
|
||||
item_locations = filter_locations(item_to_place, fill_locations, world)
|
||||
spot_to_fill = next(iter(item_locations))
|
||||
fill_locations.remove(spot_to_fill)
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
|
||||
# sweep once to pick up preplaced items
|
||||
world.state.sweep_for_events()
|
||||
|
||||
# fill world from top of itempool while we can
|
||||
while not progress_done:
|
||||
location_list = world.get_unfilled_locations()
|
||||
random.shuffle(location_list)
|
||||
spot_to_fill = None
|
||||
for location in location_list:
|
||||
if location.can_fill(world.state, itempool[0]):
|
||||
spot_to_fill = location
|
||||
break
|
||||
def fast_vanilla_fill(world, item_pool, fill_locations):
|
||||
while item_pool and fill_locations:
|
||||
item_to_place = item_pool.pop()
|
||||
spot_to_fill = next(iter(filter_locations(item_to_place, fill_locations, world)))
|
||||
fill_locations.remove(spot_to_fill)
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
|
||||
if spot_to_fill:
|
||||
item = itempool.pop(0)
|
||||
world.push_item(spot_to_fill, item, True)
|
||||
continue
|
||||
|
||||
# ran out of spots, check if we need to step in and correct things
|
||||
if len(world.get_reachable_locations()) == len(world.get_locations()):
|
||||
progress_done = True
|
||||
continue
|
||||
def filtered_equitable_fill(world, item_pool, fill_locations):
|
||||
while item_pool and fill_locations:
|
||||
item_to_place = item_pool.pop()
|
||||
item_locations = filter_locations(item_to_place, fill_locations, world)
|
||||
spot_to_fill = next(l for l in item_locations if valid_dungeon_placement(item_to_place, l, world))
|
||||
fill_locations.remove(spot_to_fill)
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
track_dungeon_items(item_to_place, spot_to_fill, world)
|
||||
|
||||
# need to place a progress item instead of an already placed item, find candidate
|
||||
item_to_place = None
|
||||
candidate_item_to_place = None
|
||||
for item in itempool:
|
||||
if item.advancement:
|
||||
candidate_item_to_place = item
|
||||
if world.unlocks_new_location(item):
|
||||
item_to_place = item
|
||||
break
|
||||
|
||||
# we might be in a situation where all new locations require multiple items to reach. If that is the case, just place any advancement item we've found and continue trying
|
||||
if item_to_place is None:
|
||||
if candidate_item_to_place is not None:
|
||||
item_to_place = candidate_item_to_place
|
||||
else:
|
||||
raise FillError('No more progress items left to place.')
|
||||
|
||||
# find item to replace with progress item
|
||||
location_list = world.get_reachable_locations()
|
||||
random.shuffle(location_list)
|
||||
for location in location_list:
|
||||
if location.item is not None and not location.item.advancement and not location.item.priority and not location.item.smallkey and not location.item.bigkey:
|
||||
# safe to replace
|
||||
replace_item = location.item
|
||||
replace_item.location = None
|
||||
itempool.append(replace_item)
|
||||
world.push_item(location, item_to_place, True)
|
||||
itempool.remove(item_to_place)
|
||||
break
|
||||
def fast_equitable_fill(world, item_pool, fill_locations):
|
||||
while item_pool and fill_locations:
|
||||
item_to_place = item_pool.pop()
|
||||
spot_to_fill = next(l for l in fill_locations if valid_dungeon_placement(item_to_place, l, world))
|
||||
fill_locations.remove(spot_to_fill)
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
track_dungeon_items(item_to_place, spot_to_fill, world)
|
||||
|
||||
|
||||
def lock_shop_locations(world, player):
|
||||
for shop, loc_names in shop_to_location_table.items():
|
||||
for loc in loc_names:
|
||||
world.get_location(loc, player).event = True
|
||||
# world.get_location(loc, player).event = True
|
||||
world.get_location(loc, player).locked = True
|
||||
# I don't believe these locations exist in non-shopsanity
|
||||
# if world.retro[player]:
|
||||
|
||||
Reference in New Issue
Block a user