Core: Fill fix local logic conflict (#1271)
This commit is contained in:
parent
cde2a6e754
commit
1288f15e45
48
Fill.py
48
Fill.py
|
@ -24,7 +24,8 @@ def sweep_from_pool(base_state: CollectionState, itempool: typing.Sequence[Item]
|
||||||
|
|
||||||
def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations: typing.List[Location],
|
def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations: typing.List[Location],
|
||||||
itempool: typing.List[Item], single_player_placement: bool = False, lock: bool = False,
|
itempool: typing.List[Item], single_player_placement: bool = False, lock: bool = False,
|
||||||
swap: bool = True, on_place: typing.Optional[typing.Callable[[Location], None]] = None) -> None:
|
swap: bool = True, on_place: typing.Optional[typing.Callable[[Location], None]] = None,
|
||||||
|
allow_partial: bool = False) -> None:
|
||||||
unplaced_items: typing.List[Item] = []
|
unplaced_items: typing.List[Item] = []
|
||||||
placements: typing.List[Location] = []
|
placements: typing.List[Location] = []
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
|
||||||
if on_place:
|
if on_place:
|
||||||
on_place(spot_to_fill)
|
on_place(spot_to_fill)
|
||||||
|
|
||||||
if len(unplaced_items) > 0 and len(locations) > 0:
|
if not allow_partial and len(unplaced_items) > 0 and len(locations) > 0:
|
||||||
# There are leftover unplaceable items and locations that won't accept them
|
# There are leftover unplaceable items and locations that won't accept them
|
||||||
if world.can_beat_game():
|
if world.can_beat_game():
|
||||||
logging.warning(
|
logging.warning(
|
||||||
|
@ -252,11 +253,12 @@ def distribute_early_items(world: MultiWorld,
|
||||||
fill_locations: typing.List[Location],
|
fill_locations: typing.List[Location],
|
||||||
itempool: typing.List[Item]) -> typing.Tuple[typing.List[Location], typing.List[Item]]:
|
itempool: typing.List[Item]) -> typing.Tuple[typing.List[Location], typing.List[Item]]:
|
||||||
""" returns new fill_locations and itempool """
|
""" returns new fill_locations and itempool """
|
||||||
early_items_count: typing.Dict[typing.Tuple[str, int], int] = {}
|
early_items_count: typing.Dict[typing.Tuple[str, int], typing.List[int]] = {}
|
||||||
for player in world.player_ids:
|
for player in world.player_ids:
|
||||||
items = itertools.chain(world.early_items[player], world.local_early_items[player])
|
items = itertools.chain(world.early_items[player], world.local_early_items[player])
|
||||||
for item in items:
|
for item in items:
|
||||||
early_items_count[(item, player)] = [world.early_items[player].get(item, 0), world.local_early_items[player].get(item, 0)]
|
early_items_count[item, player] = [world.early_items[player].get(item, 0),
|
||||||
|
world.local_early_items[player].get(item, 0)]
|
||||||
if early_items_count:
|
if early_items_count:
|
||||||
early_locations: typing.List[Location] = []
|
early_locations: typing.List[Location] = []
|
||||||
early_priority_locations: typing.List[Location] = []
|
early_priority_locations: typing.List[Location] = []
|
||||||
|
@ -280,42 +282,50 @@ def distribute_early_items(world: MultiWorld,
|
||||||
for i, item in enumerate(itempool):
|
for i, item in enumerate(itempool):
|
||||||
if (item.name, item.player) in early_items_count:
|
if (item.name, item.player) in early_items_count:
|
||||||
if item.advancement:
|
if item.advancement:
|
||||||
if early_items_count[(item.name, item.player)][1]:
|
if early_items_count[item.name, item.player][1]:
|
||||||
early_local_prog_items[item.player].append(item)
|
early_local_prog_items[item.player].append(item)
|
||||||
early_items_count[(item.name, item.player)][1] -= 1
|
early_items_count[item.name, item.player][1] -= 1
|
||||||
else:
|
else:
|
||||||
early_prog_items.append(item)
|
early_prog_items.append(item)
|
||||||
early_items_count[(item.name, item.player)][0] -= 1
|
early_items_count[item.name, item.player][0] -= 1
|
||||||
else:
|
else:
|
||||||
if early_items_count[(item.name, item.player)][1]:
|
if early_items_count[item.name, item.player][1]:
|
||||||
early_local_rest_items[item.player].append(item)
|
early_local_rest_items[item.player].append(item)
|
||||||
early_items_count[(item.name, item.player)][1] -= 1
|
early_items_count[item.name, item.player][1] -= 1
|
||||||
else:
|
else:
|
||||||
early_rest_items.append(item)
|
early_rest_items.append(item)
|
||||||
early_items_count[(item.name, item.player)][0] -= 1
|
early_items_count[item.name, item.player][0] -= 1
|
||||||
item_indexes_to_remove.add(i)
|
item_indexes_to_remove.add(i)
|
||||||
if early_items_count[(item.name, item.player)] == [0, 0]:
|
if early_items_count[item.name, item.player] == [0, 0]:
|
||||||
del early_items_count[(item.name, item.player)]
|
del early_items_count[item.name, item.player]
|
||||||
if len(early_items_count) == 0:
|
if len(early_items_count) == 0:
|
||||||
break
|
break
|
||||||
itempool = [item for i, item in enumerate(itempool) if i not in item_indexes_to_remove]
|
itempool = [item for i, item in enumerate(itempool) if i not in item_indexes_to_remove]
|
||||||
for player in world.player_ids:
|
for player in world.player_ids:
|
||||||
|
player_local = early_local_rest_items[player]
|
||||||
fill_restrictive(world, base_state,
|
fill_restrictive(world, base_state,
|
||||||
[loc for loc in early_locations if loc.player == player],
|
[loc for loc in early_locations if loc.player == player],
|
||||||
early_local_rest_items[player], lock=True)
|
player_local, lock=True, allow_partial=True)
|
||||||
|
if player_local:
|
||||||
|
logging.warning(f"Could not fulfill rules of early items: {player_local}")
|
||||||
|
early_rest_items.extend(early_local_rest_items[player])
|
||||||
early_locations = [loc for loc in early_locations if not loc.item]
|
early_locations = [loc for loc in early_locations if not loc.item]
|
||||||
fill_restrictive(world, base_state, early_locations, early_rest_items, lock=True)
|
fill_restrictive(world, base_state, early_locations, early_rest_items, lock=True, allow_partial=True)
|
||||||
early_locations += early_priority_locations
|
early_locations += early_priority_locations
|
||||||
for player in world.player_ids:
|
for player in world.player_ids:
|
||||||
|
player_local = early_local_prog_items[player]
|
||||||
fill_restrictive(world, base_state,
|
fill_restrictive(world, base_state,
|
||||||
[loc for loc in early_locations if loc.player == player],
|
[loc for loc in early_locations if loc.player == player],
|
||||||
early_local_prog_items[player], lock=True)
|
player_local, lock=True, allow_partial=True)
|
||||||
|
if player_local:
|
||||||
|
logging.warning(f"Could not fulfill rules of early items: {player_local}")
|
||||||
|
early_prog_items.extend(player_local)
|
||||||
early_locations = [loc for loc in early_locations if not loc.item]
|
early_locations = [loc for loc in early_locations if not loc.item]
|
||||||
fill_restrictive(world, base_state, early_locations, early_prog_items, lock=True)
|
fill_restrictive(world, base_state, early_locations, early_prog_items, lock=True, allow_partial=True)
|
||||||
unplaced_early_items = early_rest_items + early_prog_items
|
unplaced_early_items = early_rest_items + early_prog_items
|
||||||
if unplaced_early_items:
|
if unplaced_early_items:
|
||||||
logging.warning("Ran out of early locations for early items. Failed to place "
|
logging.warning("Ran out of early locations for early items. Failed to place "
|
||||||
f"{len(unplaced_early_items)} items early.")
|
f"{unplaced_early_items} early.")
|
||||||
itempool += unplaced_early_items
|
itempool += unplaced_early_items
|
||||||
|
|
||||||
fill_locations.extend(early_locations)
|
fill_locations.extend(early_locations)
|
||||||
|
|
19
Main.py
19
Main.py
|
@ -116,19 +116,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
world.push_precollected(world.create_item(item_name, player))
|
world.push_precollected(world.create_item(item_name, player))
|
||||||
|
|
||||||
for player in world.player_ids:
|
|
||||||
if player in world.get_game_players("A Link to the Past"):
|
|
||||||
# enforce pre-defined local items.
|
|
||||||
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
|
|
||||||
world.local_items[player].value.add('Triforce Piece')
|
|
||||||
|
|
||||||
# Not possible to place pendants/crystals outside boss prizes yet.
|
|
||||||
world.non_local_items[player].value -= item_name_groups['Pendants']
|
|
||||||
world.non_local_items[player].value -= item_name_groups['Crystals']
|
|
||||||
|
|
||||||
# items can't be both local and non-local, prefer local
|
|
||||||
world.non_local_items[player].value -= world.local_items[player].value
|
|
||||||
|
|
||||||
logger.info('Creating World.')
|
logger.info('Creating World.')
|
||||||
AutoWorld.call_all(world, "create_regions")
|
AutoWorld.call_all(world, "create_regions")
|
||||||
|
|
||||||
|
@ -136,6 +123,12 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
AutoWorld.call_all(world, "create_items")
|
AutoWorld.call_all(world, "create_items")
|
||||||
|
|
||||||
logger.info('Calculating Access Rules.')
|
logger.info('Calculating Access Rules.')
|
||||||
|
|
||||||
|
for player in world.player_ids:
|
||||||
|
# items can't be both local and non-local, prefer local
|
||||||
|
world.non_local_items[player].value -= world.local_items[player].value
|
||||||
|
world.non_local_items[player].value -= set(world.local_early_items[player])
|
||||||
|
|
||||||
if world.players > 1:
|
if world.players > 1:
|
||||||
locality_rules(world)
|
locality_rules(world)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -195,6 +195,14 @@ class ALTTPWorld(World):
|
||||||
|
|
||||||
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
|
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
|
||||||
|
|
||||||
|
# enforce pre-defined local items.
|
||||||
|
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
|
||||||
|
world.local_items[player].value.add('Triforce Piece')
|
||||||
|
|
||||||
|
# Not possible to place crystals outside boss prizes yet (might as well make it consistent with pendants too).
|
||||||
|
world.non_local_items[player].value -= item_name_groups['Pendants']
|
||||||
|
world.non_local_items[player].value -= item_name_groups['Crystals']
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
player = self.player
|
player = self.player
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from Options import Choice, Range, DeathLink
|
from Options import Choice, Range, DeathLink, DefaultOnToggle
|
||||||
from .Creatures import all_creatures, Definitions
|
from .Creatures import all_creatures, Definitions
|
||||||
|
|
||||||
|
|
||||||
|
class EarlySeaglide(DefaultOnToggle):
|
||||||
|
"""Make sure 2 of the Seaglide Fragments are available in or near the Safe Shallows (Sphere 1 Locations)."""
|
||||||
|
|
||||||
|
|
||||||
class ItemPool(Choice):
|
class ItemPool(Choice):
|
||||||
"""Valuable item pool leaves all filler items in their vanilla locations and
|
"""Valuable item pool leaves all filler items in their vanilla locations and
|
||||||
creates random duplicates of important items into freed spots."""
|
creates random duplicates of important items into freed spots."""
|
||||||
|
@ -75,6 +79,7 @@ class SubnauticaDeathLink(DeathLink):
|
||||||
|
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
"early_seaglide": EarlySeaglide,
|
||||||
"item_pool": ItemPool,
|
"item_pool": ItemPool,
|
||||||
"goal": Goal,
|
"goal": Goal,
|
||||||
"creature_scans": CreatureScans,
|
"creature_scans": CreatureScans,
|
||||||
|
|
|
@ -47,7 +47,8 @@ class SubnauticaWorld(World):
|
||||||
creatures_to_scan: List[str]
|
creatures_to_scan: List[str]
|
||||||
|
|
||||||
def generate_early(self) -> None:
|
def generate_early(self) -> None:
|
||||||
self.multiworld.local_early_items[self.player]["Seaglide Fragment"] = 2
|
if self.multiworld.early_seaglide:
|
||||||
|
self.multiworld.local_early_items[self.player]["Seaglide Fragment"] = 2
|
||||||
|
|
||||||
scan_option: Options.AggressiveScanLogic = self.multiworld.creature_scan_logic[self.player]
|
scan_option: Options.AggressiveScanLogic = self.multiworld.creature_scan_logic[self.player]
|
||||||
creature_pool = scan_option.get_pool()
|
creature_pool = scan_option.get_pool()
|
||||||
|
|
Loading…
Reference in New Issue