Item Plando updates (#226)
* Item Plando updates Add True option for item count to place the number of that item that is in the item pool. Prioritize plando blocks by location count minus item count, so that the least flexible blocks are handled first to increase likelihood of success. True and False for Force option are coming in as bools instead of strings, so that had to be accounted for. Several other bug fixes.
This commit is contained in:
parent
c7e87bc16a
commit
219bcb3521
62
Fill.py
62
Fill.py
|
@ -409,15 +409,16 @@ def swap_location_item(location_1: Location, location_2: Location, check_locked=
|
|||
location_1.item.location = location_1
|
||||
location_2.item.location = location_2
|
||||
location_1.event, location_2.event = location_2.event, location_1.event
|
||||
|
||||
def distribute_planned(world: MultiWorld):
|
||||
def warn(warning: str, force):
|
||||
if force in ['true', 'fail', 'failure', 'none', 'false', 'warn', 'warning']:
|
||||
if force in [True, 'fail', 'failure', 'none', False, 'warn', 'warning']:
|
||||
logging.warning(f'{warning}')
|
||||
else:
|
||||
logging.debug(f'{warning}')
|
||||
|
||||
def failed(warning: str, force):
|
||||
if force in ['true', 'fail', 'failure']:
|
||||
if force in [True, 'fail', 'failure']:
|
||||
raise Exception(warning)
|
||||
else:
|
||||
warn(warning, force)
|
||||
|
@ -447,10 +448,13 @@ def distribute_planned(world: MultiWorld):
|
|||
if 'count' not in block:
|
||||
block['count'] = 1
|
||||
else:
|
||||
failed("You must specify at least one item to place items with plando.", block['forced'])
|
||||
failed("You must specify at least one item to place items with plando.", block['force'])
|
||||
continue
|
||||
if isinstance(items, dict):
|
||||
item_list = []
|
||||
for key, value in items.items():
|
||||
if value is True:
|
||||
value = world.itempool.count(world.worlds[player].create_item(key))
|
||||
item_list += [key] * value
|
||||
items = item_list
|
||||
if isinstance(items, str):
|
||||
|
@ -458,10 +462,12 @@ def distribute_planned(world: MultiWorld):
|
|||
block['items'] = items
|
||||
|
||||
locations = []
|
||||
if 'locations' in block:
|
||||
locations.extend(block['locations'])
|
||||
elif 'location' in block:
|
||||
locations.extend(block['location'])
|
||||
if 'location' in block:
|
||||
locations = block['location'] # just allow 'location' to keep old yamls compatible
|
||||
elif 'locations' in block:
|
||||
locations = block['locations']
|
||||
if isinstance(locations, str):
|
||||
locations = [locations]
|
||||
|
||||
if isinstance(locations, dict):
|
||||
location_list = []
|
||||
|
@ -471,7 +477,6 @@ def distribute_planned(world: MultiWorld):
|
|||
if isinstance(locations, str):
|
||||
locations = [locations]
|
||||
block['locations'] = locations
|
||||
c = block['count']
|
||||
|
||||
if not block['count']:
|
||||
block['count'] = (min(len(block['items']), len(block['locations'])) if len(block['locations'])
|
||||
|
@ -489,17 +494,19 @@ def distribute_planned(world: MultiWorld):
|
|||
block['count'] = len(block['items'])
|
||||
if block['count']['max'] > len(block['locations']) > 0:
|
||||
count = block['count']
|
||||
failed(f"Plando count {count} greater than locations specified for player ", block['force'])
|
||||
failed(f"Plando count {count} greater than locations specified", block['force'])
|
||||
block['count'] = len(block['locations'])
|
||||
block['count']['target'] = world.random.randint(block['count']['min'], block['count']['max'])
|
||||
c = block['count']
|
||||
|
||||
if block['count']['target'] > 0:
|
||||
plando_blocks.append(block)
|
||||
|
||||
# shuffle, but then sort blocks by number of items, so blocks with fewer items get priority
|
||||
# shuffle, but then sort blocks by number of locations minus number of items,
|
||||
# so less-flexible blocks get priority
|
||||
world.random.shuffle(plando_blocks)
|
||||
plando_blocks.sort(key=lambda block: block['count']['target'])
|
||||
plando_blocks.sort(key=lambda block: (len(block['locations']) - block['count']['target']
|
||||
if len(block['locations']) > 0
|
||||
else len(world.get_unfilled_locations(player)) - block['count']['target']))
|
||||
|
||||
for placement in plando_blocks:
|
||||
player = placement['player']
|
||||
|
@ -509,13 +516,13 @@ def distribute_planned(world: MultiWorld):
|
|||
items = placement['items']
|
||||
maxcount = placement['count']['target']
|
||||
from_pool = placement['from_pool']
|
||||
if target_world is False or world.players == 1:
|
||||
if target_world is False or world.players == 1: # target own world
|
||||
worlds = {player}
|
||||
elif target_world is True:
|
||||
elif target_world is True: # target any worlds besides own
|
||||
worlds = set(world.player_ids) - {player}
|
||||
elif target_world is None:
|
||||
elif target_world is None: # target all worlds
|
||||
worlds = set(world.player_ids)
|
||||
elif type(target_world) == list:
|
||||
elif type(target_world) == list: # list of target worlds
|
||||
worlds = []
|
||||
for listed_world in target_world:
|
||||
if listed_world not in world_name_lookup:
|
||||
|
@ -524,14 +531,14 @@ def distribute_planned(world: MultiWorld):
|
|||
continue
|
||||
worlds.append(world_name_lookup[listed_world])
|
||||
worlds = set(worlds)
|
||||
elif type(target_world) == int:
|
||||
elif type(target_world) == int: # target world by slot number
|
||||
if target_world not in range(1, world.players + 1):
|
||||
failed(
|
||||
f"Cannot place item in world {target_world} as it is not in range of (1, {world.players})",
|
||||
placement['forced'])
|
||||
placement['force'])
|
||||
continue
|
||||
worlds = {target_world}
|
||||
else: # find world by name
|
||||
else: # target world by slot name
|
||||
if target_world not in world_name_lookup:
|
||||
failed(f"Cannot place item to {target_world}'s world as that world does not exist.",
|
||||
placement['force'])
|
||||
|
@ -540,11 +547,10 @@ def distribute_planned(world: MultiWorld):
|
|||
|
||||
candidates = list(location for location in world.get_unfilled_locations_for_players(locations,
|
||||
worlds))
|
||||
|
||||
world.random.shuffle(candidates)
|
||||
world.random.shuffle(items)
|
||||
count = 0
|
||||
err = "Unknown error"
|
||||
err = []
|
||||
successful_pairs = []
|
||||
for item_name in items:
|
||||
item = world.worlds[player].create_item(item_name)
|
||||
|
@ -552,6 +558,7 @@ def distribute_planned(world: MultiWorld):
|
|||
if location in key_drop_data:
|
||||
warn(
|
||||
f"Can't place '{item_name}' at '{placement.location}', as key drop shuffle locations are not supported yet.")
|
||||
continue
|
||||
if not location.item:
|
||||
if location.item_rule(item):
|
||||
if location.can_fill(world.state, item, False):
|
||||
|
@ -560,18 +567,19 @@ def distribute_planned(world: MultiWorld):
|
|||
count = count + 1
|
||||
break
|
||||
else:
|
||||
err = f"Can't place item at {location} due to fill condition not met."
|
||||
err.append(f"Can't place item at {location} due to fill condition not met.")
|
||||
else:
|
||||
err = f"{item_name} not allowed at {location}."
|
||||
err.append(f"{item_name} not allowed at {location}.")
|
||||
else:
|
||||
err = f"Cannot place {item_name} into already filled location {location}."
|
||||
err.append(f"Cannot place {item_name} into already filled location {location}.")
|
||||
if count == maxcount:
|
||||
break
|
||||
if count < placement['count']['min']:
|
||||
err = " ".join(err)
|
||||
m = placement['count']['min']
|
||||
failed(
|
||||
f"Plando block failed to place item(s) for {world.player_name[player]}, most recent cause: {err}",
|
||||
f"Plando block failed to place {m - count} of {m} item(s) for {world.player_name[player]}, error(s): {err}",
|
||||
placement['force'])
|
||||
continue
|
||||
for (item, location) in successful_pairs:
|
||||
world.push_item(location, item, collect=False)
|
||||
location.event = True # flag location to be checked during fill
|
||||
|
@ -583,7 +591,7 @@ def distribute_planned(world: MultiWorld):
|
|||
except ValueError:
|
||||
warn(
|
||||
f"Could not remove {item} from pool for {world.player_name[player]} as it's already missing from it.",
|
||||
placement['force'])
|
||||
placement['force'])
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
|
|
|
@ -56,9 +56,10 @@ list of specific locations both in their own game or in another player's game.
|
|||
* Single Placement is when you use a plando block to place a single item at a single location.
|
||||
* `item` is the item you would like to place and `location` is the location to place it.
|
||||
* Multi Placement uses a plando block to place multiple items in multiple locations until either list is exhausted.
|
||||
* `items` defines the items to use and a number letting you place multiple of it.
|
||||
* `items` defines the items to use and a number letting you place multiple of it. You can use true instead of a number to have it use however many of that item are in your item pool.
|
||||
* `locations` is a list of possible locations those items can be placed in.
|
||||
* Using the multi placement method, placements are picked randomly.
|
||||
* Instead of a number, you can use true
|
||||
* `count` can be used to set the maximum number of items placed from the block. The default is 1 if using `item` and False if using `items`
|
||||
* If a number is used it will try to place this number of items.
|
||||
* If set to false it will try to place as many items from the block as it can.
|
||||
|
|
Loading…
Reference in New Issue