DS3: Convert post_fill to stage_post_fill for better performance (#4122)
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
parent
62942704bd
commit
33ae68c756
|
@ -1366,7 +1366,8 @@ class DarkSouls3World(World):
|
||||||
text = "\n" + text + "\n"
|
text = "\n" + text + "\n"
|
||||||
spoiler_handle.write(text)
|
spoiler_handle.write(text)
|
||||||
|
|
||||||
def post_fill(self):
|
@classmethod
|
||||||
|
def stage_post_fill(cls, multiworld: MultiWorld):
|
||||||
"""If item smoothing is enabled, rearrange items so they scale up smoothly through the run.
|
"""If item smoothing is enabled, rearrange items so they scale up smoothly through the run.
|
||||||
|
|
||||||
This determines the approximate order a given silo of items (say, soul items) show up in the
|
This determines the approximate order a given silo of items (say, soul items) show up in the
|
||||||
|
@ -1375,106 +1376,125 @@ class DarkSouls3World(World):
|
||||||
items, later spheres get higher-level ones. Within a sphere, items in DS3 are distributed in
|
items, later spheres get higher-level ones. Within a sphere, items in DS3 are distributed in
|
||||||
region order, and then the best items in a sphere go into the multiworld.
|
region order, and then the best items in a sphere go into the multiworld.
|
||||||
"""
|
"""
|
||||||
|
ds3_worlds = [world for world in cast(List[DarkSouls3World], multiworld.get_game_worlds(cls.game)) if
|
||||||
|
world.options.smooth_upgrade_items
|
||||||
|
or world.options.smooth_soul_items
|
||||||
|
or world.options.smooth_upgraded_weapons]
|
||||||
|
if not ds3_worlds:
|
||||||
|
# No worlds need item smoothing.
|
||||||
|
return
|
||||||
|
|
||||||
locations_by_sphere = [
|
spheres_per_player: Dict[int, List[List[Location]]] = {world.player: [] for world in ds3_worlds}
|
||||||
sorted(loc for loc in sphere if loc.item.player == self.player and not loc.locked)
|
for sphere in multiworld.get_spheres():
|
||||||
for sphere in self.multiworld.get_spheres()
|
locations_per_item_player: Dict[int, List[Location]] = {player: [] for player in spheres_per_player.keys()}
|
||||||
]
|
for location in sphere:
|
||||||
|
if location.locked:
|
||||||
|
continue
|
||||||
|
item_player = location.item.player
|
||||||
|
if item_player in locations_per_item_player:
|
||||||
|
locations_per_item_player[item_player].append(location)
|
||||||
|
for player, locations in locations_per_item_player.items():
|
||||||
|
# Sort for deterministic results.
|
||||||
|
locations.sort()
|
||||||
|
spheres_per_player[player].append(locations)
|
||||||
|
|
||||||
# All items in the base game in approximately the order they appear
|
for ds3_world in ds3_worlds:
|
||||||
all_item_order: List[DS3ItemData] = [
|
locations_by_sphere = spheres_per_player[ds3_world.player]
|
||||||
item_dictionary[location.default_item_name]
|
|
||||||
for region in region_order
|
|
||||||
# Shuffle locations within each region.
|
|
||||||
for location in self._shuffle(location_tables[region])
|
|
||||||
if self._is_location_available(location)
|
|
||||||
]
|
|
||||||
|
|
||||||
# All DarkSouls3Items for this world that have been assigned anywhere, grouped by name
|
# All items in the base game in approximately the order they appear
|
||||||
full_items_by_name: Dict[str, List[DarkSouls3Item]] = defaultdict(list)
|
all_item_order: List[DS3ItemData] = [
|
||||||
for location in self.multiworld.get_filled_locations():
|
item_dictionary[location.default_item_name]
|
||||||
if location.item.player == self.player and (
|
for region in region_order
|
||||||
location.player != self.player or self._is_location_available(location)
|
# Shuffle locations within each region.
|
||||||
):
|
for location in ds3_world._shuffle(location_tables[region])
|
||||||
full_items_by_name[location.item.name].append(location.item)
|
if ds3_world._is_location_available(location)
|
||||||
|
]
|
||||||
|
|
||||||
def smooth_items(item_order: List[Union[DS3ItemData, DarkSouls3Item]]) -> None:
|
# All DarkSouls3Items for this world that have been assigned anywhere, grouped by name
|
||||||
"""Rearrange all items in item_order to match that order.
|
full_items_by_name: Dict[str, List[DarkSouls3Item]] = defaultdict(list)
|
||||||
|
for location in multiworld.get_filled_locations():
|
||||||
|
if location.item.player == ds3_world.player and (
|
||||||
|
location.player != ds3_world.player or ds3_world._is_location_available(location)
|
||||||
|
):
|
||||||
|
full_items_by_name[location.item.name].append(location.item)
|
||||||
|
|
||||||
Note: this requires that item_order exactly matches the number of placed items from this
|
def smooth_items(item_order: List[Union[DS3ItemData, DarkSouls3Item]]) -> None:
|
||||||
world matching the given names.
|
"""Rearrange all items in item_order to match that order.
|
||||||
"""
|
|
||||||
|
|
||||||
# Convert items to full DarkSouls3Items.
|
Note: this requires that item_order exactly matches the number of placed items from this
|
||||||
converted_item_order: List[DarkSouls3Item] = [
|
world matching the given names.
|
||||||
item for item in (
|
"""
|
||||||
(
|
|
||||||
# full_items_by_name won't contain DLC items if the DLC is disabled.
|
# Convert items to full DarkSouls3Items.
|
||||||
(full_items_by_name[item.name] or [None]).pop(0)
|
converted_item_order: List[DarkSouls3Item] = [
|
||||||
if isinstance(item, DS3ItemData) else item
|
item for item in (
|
||||||
|
(
|
||||||
|
# full_items_by_name won't contain DLC items if the DLC is disabled.
|
||||||
|
(full_items_by_name[item.name] or [None]).pop(0)
|
||||||
|
if isinstance(item, DS3ItemData) else item
|
||||||
|
)
|
||||||
|
for item in item_order
|
||||||
)
|
)
|
||||||
for item in item_order
|
# Never re-order event items, because they weren't randomized in the first place.
|
||||||
)
|
if item and item.code is not None
|
||||||
# Never re-order event items, because they weren't randomized in the first place.
|
]
|
||||||
if item and item.code is not None
|
|
||||||
]
|
|
||||||
|
|
||||||
names = {item.name for item in converted_item_order}
|
names = {item.name for item in converted_item_order}
|
||||||
|
|
||||||
all_matching_locations = [
|
all_matching_locations = [
|
||||||
loc
|
loc
|
||||||
for sphere in locations_by_sphere
|
for sphere in locations_by_sphere
|
||||||
for loc in sphere
|
for loc in sphere
|
||||||
if loc.item.name in names
|
if loc.item.name in names
|
||||||
]
|
]
|
||||||
|
|
||||||
# It's expected that there may be more total items than there are matching locations if
|
# It's expected that there may be more total items than there are matching locations if
|
||||||
# the player has chosen a more limited accessibility option, since the matching
|
# the player has chosen a more limited accessibility option, since the matching
|
||||||
# locations *only* include items in the spheres of accessibility.
|
# locations *only* include items in the spheres of accessibility.
|
||||||
if len(converted_item_order) < len(all_matching_locations):
|
if len(converted_item_order) < len(all_matching_locations):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"DS3 bug: there are {len(all_matching_locations)} locations that can " +
|
f"DS3 bug: there are {len(all_matching_locations)} locations that can " +
|
||||||
f"contain smoothed items, but only {len(converted_item_order)} items to smooth."
|
f"contain smoothed items, but only {len(converted_item_order)} items to smooth."
|
||||||
)
|
)
|
||||||
|
|
||||||
for sphere in locations_by_sphere:
|
for sphere in locations_by_sphere:
|
||||||
locations = [loc for loc in sphere if loc.item.name in names]
|
locations = [loc for loc in sphere if loc.item.name in names]
|
||||||
|
|
||||||
# Check the game, not the player, because we know how to sort within regions for DS3
|
# Check the game, not the player, because we know how to sort within regions for DS3
|
||||||
offworld = self._shuffle([loc for loc in locations if loc.game != "Dark Souls III"])
|
offworld = ds3_world._shuffle([loc for loc in locations if loc.game != "Dark Souls III"])
|
||||||
onworld = sorted((loc for loc in locations if loc.game == "Dark Souls III"),
|
onworld = sorted((loc for loc in locations if loc.game == "Dark Souls III"),
|
||||||
key=lambda loc: loc.data.region_value)
|
key=lambda loc: loc.data.region_value)
|
||||||
|
|
||||||
# Give offworld regions the last (best) items within a given sphere
|
# Give offworld regions the last (best) items within a given sphere
|
||||||
for location in onworld + offworld:
|
for location in onworld + offworld:
|
||||||
new_item = self._pop_item(location, converted_item_order)
|
new_item = ds3_world._pop_item(location, converted_item_order)
|
||||||
location.item = new_item
|
location.item = new_item
|
||||||
new_item.location = location
|
new_item.location = location
|
||||||
|
|
||||||
if self.options.smooth_upgrade_items:
|
if ds3_world.options.smooth_upgrade_items:
|
||||||
base_names = {
|
base_names = {
|
||||||
"Titanite Shard", "Large Titanite Shard", "Titanite Chunk", "Titanite Slab",
|
"Titanite Shard", "Large Titanite Shard", "Titanite Chunk", "Titanite Slab",
|
||||||
"Titanite Scale", "Twinkling Titanite", "Farron Coal", "Sage's Coal", "Giant's Coal",
|
"Titanite Scale", "Twinkling Titanite", "Farron Coal", "Sage's Coal", "Giant's Coal",
|
||||||
"Profaned Coal"
|
"Profaned Coal"
|
||||||
}
|
}
|
||||||
smooth_items([item for item in all_item_order if item.base_name in base_names])
|
smooth_items([item for item in all_item_order if item.base_name in base_names])
|
||||||
|
|
||||||
if self.options.smooth_soul_items:
|
if ds3_world.options.smooth_soul_items:
|
||||||
smooth_items([
|
smooth_items([
|
||||||
item for item in all_item_order
|
item for item in all_item_order
|
||||||
if item.souls and item.classification != ItemClassification.progression
|
if item.souls and item.classification != ItemClassification.progression
|
||||||
])
|
])
|
||||||
|
|
||||||
if self.options.smooth_upgraded_weapons:
|
if ds3_world.options.smooth_upgraded_weapons:
|
||||||
upgraded_weapons = [
|
upgraded_weapons = [
|
||||||
location.item
|
location.item
|
||||||
for location in self.multiworld.get_filled_locations()
|
for location in multiworld.get_filled_locations()
|
||||||
if location.item.player == self.player
|
if location.item.player == ds3_world.player
|
||||||
and location.item.level and location.item.level > 0
|
and location.item.level and location.item.level > 0
|
||||||
and location.item.classification != ItemClassification.progression
|
and location.item.classification != ItemClassification.progression
|
||||||
]
|
]
|
||||||
upgraded_weapons.sort(key=lambda item: item.level)
|
upgraded_weapons.sort(key=lambda item: item.level)
|
||||||
smooth_items(upgraded_weapons)
|
smooth_items(upgraded_weapons)
|
||||||
|
|
||||||
def _shuffle(self, seq: Sequence) -> List:
|
def _shuffle(self, seq: Sequence) -> List:
|
||||||
"""Returns a shuffled copy of a sequence."""
|
"""Returns a shuffled copy of a sequence."""
|
||||||
|
|
Loading…
Reference in New Issue