|
|
|
|
@@ -7,22 +7,22 @@ import itertools
|
|
|
|
|
import operator
|
|
|
|
|
from collections import defaultdict, Counter
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("Hollow Knight")
|
|
|
|
|
|
|
|
|
|
from .Items import item_table, lookup_type_to_names, item_name_groups
|
|
|
|
|
from .Regions import create_regions
|
|
|
|
|
from .Items import item_table, item_name_groups
|
|
|
|
|
from .Rules import set_rules, cost_terms, _hk_can_beat_thk, _hk_siblings_ending, _hk_can_beat_radiance
|
|
|
|
|
from .Options import hollow_knight_options, hollow_knight_randomize_options, Goal, WhitePalace, CostSanity, \
|
|
|
|
|
shop_to_option, HKOptions, GrubHuntGoal
|
|
|
|
|
from .ExtractedData import locations, starts, multi_locations, location_to_region_lookup, \
|
|
|
|
|
event_names, item_effects, connectors, one_ways, vanilla_shop_costs, vanilla_location_costs
|
|
|
|
|
from .ExtractedData import locations, starts, multi_locations, event_names, item_effects, connectors, \
|
|
|
|
|
vanilla_shop_costs, vanilla_location_costs
|
|
|
|
|
from .Charms import names as charm_names
|
|
|
|
|
|
|
|
|
|
from BaseClasses import Region, Location, MultiWorld, Item, LocationProgressType, Tutorial, ItemClassification, CollectionState
|
|
|
|
|
from BaseClasses import Region, Location, MultiWorld, Item, LocationProgressType, Tutorial, ItemClassification, \
|
|
|
|
|
CollectionState
|
|
|
|
|
from worlds.AutoWorld import World, LogicMixin, WebWorld
|
|
|
|
|
|
|
|
|
|
from settings import Group, Bool
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("Hollow Knight")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HollowKnightSettings(Group):
|
|
|
|
|
class DisableMapModSpoilers(Bool):
|
|
|
|
|
@@ -231,7 +231,6 @@ class HKWorld(World):
|
|
|
|
|
def create_regions(self):
|
|
|
|
|
menu_region: Region = create_region(self.multiworld, self.player, 'Menu')
|
|
|
|
|
self.multiworld.regions.append(menu_region)
|
|
|
|
|
# wp_exclusions = self.white_palace_exclusions()
|
|
|
|
|
|
|
|
|
|
# check for any goal that godhome events are relevant to
|
|
|
|
|
all_event_names = event_names.copy()
|
|
|
|
|
@@ -241,21 +240,17 @@ class HKWorld(World):
|
|
|
|
|
|
|
|
|
|
# Link regions
|
|
|
|
|
for event_name in sorted(all_event_names):
|
|
|
|
|
#if event_name in wp_exclusions:
|
|
|
|
|
# continue
|
|
|
|
|
loc = HKLocation(self.player, event_name, None, menu_region)
|
|
|
|
|
loc.place_locked_item(HKItem(event_name,
|
|
|
|
|
True, #event_name not in wp_exclusions,
|
|
|
|
|
True,
|
|
|
|
|
None, "Event", self.player))
|
|
|
|
|
menu_region.locations.append(loc)
|
|
|
|
|
for entry_transition, exit_transition in connectors.items():
|
|
|
|
|
#if entry_transition in wp_exclusions:
|
|
|
|
|
# continue
|
|
|
|
|
if exit_transition:
|
|
|
|
|
# if door logic fulfilled -> award vanilla target as event
|
|
|
|
|
loc = HKLocation(self.player, entry_transition, None, menu_region)
|
|
|
|
|
loc.place_locked_item(HKItem(exit_transition,
|
|
|
|
|
True, #exit_transition not in wp_exclusions,
|
|
|
|
|
True,
|
|
|
|
|
None, "Event", self.player))
|
|
|
|
|
menu_region.locations.append(loc)
|
|
|
|
|
|
|
|
|
|
@@ -292,7 +287,10 @@ class HKWorld(World):
|
|
|
|
|
if item_name in junk_replace:
|
|
|
|
|
item_name = self.get_filler_item_name()
|
|
|
|
|
|
|
|
|
|
item = self.create_item(item_name) if not vanilla or location_name == "Start" or self.options.AddUnshuffledLocations else self.create_event(item_name)
|
|
|
|
|
item = (self.create_item(item_name)
|
|
|
|
|
if not vanilla or location_name == "Start" or self.options.AddUnshuffledLocations
|
|
|
|
|
else self.create_event(item_name)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if location_name == "Start":
|
|
|
|
|
if item_name in randomized_starting_items:
|
|
|
|
|
@@ -347,8 +345,8 @@ class HKWorld(World):
|
|
|
|
|
randomized = True
|
|
|
|
|
_add("Elevator_Pass", "Elevator_Pass", randomized)
|
|
|
|
|
|
|
|
|
|
for shop, locations in self.created_multi_locations.items():
|
|
|
|
|
for _ in range(len(locations), getattr(self.options, shop_to_option[shop]).value):
|
|
|
|
|
for shop, shop_locations in self.created_multi_locations.items():
|
|
|
|
|
for _ in range(len(shop_locations), getattr(self.options, shop_to_option[shop]).value):
|
|
|
|
|
self.create_location(shop)
|
|
|
|
|
unfilled_locations += 1
|
|
|
|
|
|
|
|
|
|
@@ -358,7 +356,7 @@ class HKWorld(World):
|
|
|
|
|
|
|
|
|
|
# Add additional shop items, as needed.
|
|
|
|
|
if additional_shop_items > 0:
|
|
|
|
|
shops = list(shop for shop, locations in self.created_multi_locations.items() if len(locations) < 16)
|
|
|
|
|
shops = [shop for shop, shop_locations in self.created_multi_locations.items() if len(shop_locations) < 16]
|
|
|
|
|
if not self.options.EggShopSlots: # No eggshop, so don't place items there
|
|
|
|
|
shops.remove('Egg_Shop')
|
|
|
|
|
|
|
|
|
|
@@ -380,8 +378,8 @@ class HKWorld(World):
|
|
|
|
|
self.sort_shops_by_cost()
|
|
|
|
|
|
|
|
|
|
def sort_shops_by_cost(self):
|
|
|
|
|
for shop, locations in self.created_multi_locations.items():
|
|
|
|
|
randomized_locations = list(loc for loc in locations if not loc.vanilla)
|
|
|
|
|
for shop, shop_locations in self.created_multi_locations.items():
|
|
|
|
|
randomized_locations = [loc for loc in shop_locations if not loc.vanilla]
|
|
|
|
|
prices = sorted(
|
|
|
|
|
(loc.costs for loc in randomized_locations),
|
|
|
|
|
key=lambda costs: (len(costs),) + tuple(costs.values())
|
|
|
|
|
@@ -405,7 +403,7 @@ class HKWorld(World):
|
|
|
|
|
return {k: v for k, v in weights.items() if v}
|
|
|
|
|
|
|
|
|
|
random = self.random
|
|
|
|
|
hybrid_chance = getattr(self.options, f"CostSanityHybridChance").value
|
|
|
|
|
hybrid_chance = getattr(self.options, "CostSanityHybridChance").value
|
|
|
|
|
weights = {
|
|
|
|
|
data.term: getattr(self.options, f"CostSanity{data.option}Weight").value
|
|
|
|
|
for data in cost_terms.values()
|
|
|
|
|
@@ -493,7 +491,11 @@ class HKWorld(World):
|
|
|
|
|
worlds = [world for world in multiworld.get_game_worlds(cls.game) if world.options.Goal in ["any", "grub_hunt"]]
|
|
|
|
|
if worlds:
|
|
|
|
|
grubs = [item for item in multiworld.get_items() if item.name == "Grub"]
|
|
|
|
|
all_grub_players = [world.player for world in worlds if world.options.GrubHuntGoal == GrubHuntGoal.special_range_names["all"]]
|
|
|
|
|
all_grub_players = [
|
|
|
|
|
world.player
|
|
|
|
|
for world in worlds
|
|
|
|
|
if world.options.GrubHuntGoal == GrubHuntGoal.special_range_names["all"]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if all_grub_players:
|
|
|
|
|
group_lookup = defaultdict(set)
|
|
|
|
|
@@ -668,8 +670,8 @@ class HKWorld(World):
|
|
|
|
|
):
|
|
|
|
|
spoiler_handle.write(f"\n{loc}: {loc.item} costing {loc.cost_text()}")
|
|
|
|
|
else:
|
|
|
|
|
for shop_name, locations in hk_world.created_multi_locations.items():
|
|
|
|
|
for loc in locations:
|
|
|
|
|
for shop_name, shop_locations in hk_world.created_multi_locations.items():
|
|
|
|
|
for loc in shop_locations:
|
|
|
|
|
spoiler_handle.write(f"\n{loc}: {loc.item} costing {loc.cost_text()}")
|
|
|
|
|
|
|
|
|
|
def get_multi_location_name(self, base: str, i: typing.Optional[int]) -> str:
|
|
|
|
|
|