Hollow Knight updates (goals, WP/POP, etc.) (#438)
* Hollow Knight updates: - Add configurable goals (Any, THK, Siblings, Radiance) - Change base logic to require Opened_Black_Egg_Temple instead of requiring 3 dreamers. This is future-proof for transition rando, where Black Egg might not have been located yet. - Add combat logic for THK and Radiance on par with Rando4's boss logic, so itemless HK shouldn't be required. - Existing completion logic now uses Black_Egg_te - Add White Palace options (Exclude, King Fragment Only, No Path of Pain, Include) - Excluded WP may still be required for King Fragment if Charms are not randomized - Simply don't place WP locations that are excluded - Distinguish between POP locations (required for POP), WP checks ( actual item locations), WP transitions (relevant for future transition rando), and WP events (logically required to reach King Fragment) - Many transitions were listed twice. Remove duplicates. - Sort transitions by scene - For randomizable locations that have no logical significance when not randomized, simply skip adding them to the pool entirely for theoretically faster generation. * Hollow Knight updates - Support random starting geo up to 1000 geo. - Always include locations rather than dropping unrandomized "logicless" ones, as it is required to best support same-slot coop.
This commit is contained in:
parent
8c64f6221e
commit
e5a1052089
|
@ -22,3 +22,15 @@ for item, item_data in item_table.items():
|
||||||
item_name_groups = {group: lookup_type_to_names[group] for group in ("Skill", "Charm", "Mask", "Vessel",
|
item_name_groups = {group: lookup_type_to_names[group] for group in ("Skill", "Charm", "Mask", "Vessel",
|
||||||
"Relic", "Root", "Map", "Stag", "Cocoon",
|
"Relic", "Root", "Map", "Stag", "Cocoon",
|
||||||
"Soul", "DreamWarrior", "DreamBoss")}
|
"Soul", "DreamWarrior", "DreamBoss")}
|
||||||
|
|
||||||
|
directionals = ('', 'Left_', 'Right_')
|
||||||
|
|
||||||
|
item_name_groups.update({
|
||||||
|
"Dreamer": {"Herrah", "Monomon", "Lurien"},
|
||||||
|
"Cloak": {x + 'Mothwing_Cloak' for x in directionals} | {'Shade_Cloak', 'Split_Shade_Cloak'},
|
||||||
|
"Claw": {x + 'Mantis_Claw' for x in directionals},
|
||||||
|
"CDash": {x + 'Crystal_Heart' for x in directionals},
|
||||||
|
"Fragments": {"Queen_Fragment", "King_Fragment", "Void_Heart"},
|
||||||
|
})
|
||||||
|
item_name_groups['Horizontal'] = item_name_groups['Cloak'] | item_name_groups['CDash']
|
||||||
|
item_name_groups['Vertical'] = item_name_groups['Claw'] | {'Monarch_Wings'}
|
||||||
|
|
|
@ -263,6 +263,39 @@ class EggShopSlots(Range):
|
||||||
range_end = 16
|
range_end = 16
|
||||||
|
|
||||||
|
|
||||||
|
class Goal(Choice):
|
||||||
|
"""The goal required of you in order to complete your run in Archipelago."""
|
||||||
|
display_name = "Goal"
|
||||||
|
option_any = 0
|
||||||
|
option_hollowknight = 1
|
||||||
|
option_siblings = 2
|
||||||
|
option_radiance = 3
|
||||||
|
# Client support exists for this, but logic is a nightmare
|
||||||
|
# option_godhome = 4
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
|
class WhitePalace(Choice):
|
||||||
|
"""
|
||||||
|
Whether or not to include White Palace or not. Note: Even if excluded, the King Fragment check may still be
|
||||||
|
required if charms are vanilla.
|
||||||
|
"""
|
||||||
|
display_name = "White Palace"
|
||||||
|
option_exclude = 0 # No White Palace at all
|
||||||
|
option_kingfragment = 1 # Include King Fragment check only
|
||||||
|
option_nopathofpain = 2 # Exclude Path of Pain locations.
|
||||||
|
option_include = 3 # Include all White Palace locations, including Path of Pain.
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
|
class StartingGeo(Range):
|
||||||
|
"""The amount of starting geo you have."""
|
||||||
|
display_name = "Starting Geo"
|
||||||
|
range_start = 0
|
||||||
|
range_end = 1000
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
hollow_knight_options: typing.Dict[str, type(Option)] = {
|
hollow_knight_options: typing.Dict[str, type(Option)] = {
|
||||||
**hollow_knight_randomize_options,
|
**hollow_knight_randomize_options,
|
||||||
**hollow_knight_logic_options,
|
**hollow_knight_logic_options,
|
||||||
|
@ -278,4 +311,7 @@ hollow_knight_options: typing.Dict[str, type(Option)] = {
|
||||||
MinimumEggPrice.__name__: MinimumEggPrice,
|
MinimumEggPrice.__name__: MinimumEggPrice,
|
||||||
MaximumEggPrice.__name__: MaximumEggPrice,
|
MaximumEggPrice.__name__: MaximumEggPrice,
|
||||||
EggShopSlots.__name__: EggShopSlots,
|
EggShopSlots.__name__: EggShopSlots,
|
||||||
|
Goal.__name__: Goal,
|
||||||
|
WhitePalace.__name__: WhitePalace,
|
||||||
|
StartingGeo.__name__: StartingGeo,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,77 +9,80 @@ logger = logging.getLogger("Hollow Knight")
|
||||||
from .Items import item_table, lookup_type_to_names, item_name_groups
|
from .Items import item_table, lookup_type_to_names, item_name_groups
|
||||||
from .Regions import create_regions
|
from .Regions import create_regions
|
||||||
from .Rules import set_rules
|
from .Rules import set_rules
|
||||||
from .Options import hollow_knight_options, hollow_knight_randomize_options, disabled
|
from .Options import hollow_knight_options, hollow_knight_randomize_options, disabled, Goal, WhitePalace
|
||||||
from .ExtractedData import locations, starts, multi_locations, location_to_region_lookup, \
|
from .ExtractedData import locations, starts, multi_locations, location_to_region_lookup, \
|
||||||
event_names, item_effects, connectors, one_ways
|
event_names, item_effects, connectors, one_ways
|
||||||
from .Charms import names as charm_names
|
from .Charms import names as charm_names
|
||||||
|
|
||||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, RegionType, Tutorial
|
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, RegionType, LocationProgressType, Tutorial
|
||||||
from ..AutoWorld import World, LogicMixin, WebWorld
|
from ..AutoWorld import World, LogicMixin, WebWorld
|
||||||
|
|
||||||
white_palace_locations = {
|
path_of_pain_locations = {
|
||||||
"Soul_Totem-Path_of_Pain_Below_Thornskip",
|
"Soul_Totem-Path_of_Pain_Below_Thornskip",
|
||||||
"Soul_Totem-White_Palace_Final",
|
|
||||||
"Lore_Tablet-Path_of_Pain_Entrance",
|
"Lore_Tablet-Path_of_Pain_Entrance",
|
||||||
"Soul_Totem-Path_of_Pain_Left_of_Lever",
|
"Soul_Totem-Path_of_Pain_Left_of_Lever",
|
||||||
"Soul_Totem-Path_of_Pain_Hidden",
|
"Soul_Totem-Path_of_Pain_Hidden",
|
||||||
"Soul_Totem-Path_of_Pain_Entrance",
|
"Soul_Totem-Path_of_Pain_Entrance",
|
||||||
"Soul_Totem-Path_of_Pain_Final",
|
"Soul_Totem-Path_of_Pain_Final",
|
||||||
"Soul_Totem-White_Palace_Entrance",
|
|
||||||
"Soul_Totem-Path_of_Pain_Below_Lever",
|
"Soul_Totem-Path_of_Pain_Below_Lever",
|
||||||
"Lore_Tablet-Palace_Throne",
|
|
||||||
"Soul_Totem-Path_of_Pain_Second",
|
"Soul_Totem-Path_of_Pain_Second",
|
||||||
|
"Journal_Entry-Seal_of_Binding",
|
||||||
|
"Warp-Path_of_Pain_Complete",
|
||||||
|
"Defeated_Path_of_Pain_Arena",
|
||||||
|
"Completed_Path_of_Pain",
|
||||||
|
# Path of Pain transitions
|
||||||
|
"White_Palace_17[right1]", "White_Palace_17[bot1]",
|
||||||
|
"White_Palace_18[top1]", "White_Palace_18[right1]",
|
||||||
|
"White_Palace_19[left1]", "White_Palace_19[top1]",
|
||||||
|
"White_Palace_20[bot1]",
|
||||||
|
}
|
||||||
|
|
||||||
|
white_palace_transitions = {
|
||||||
|
# Event-Transitions:
|
||||||
|
# "Grubfather_2",
|
||||||
|
"White_Palace_01[left1]", "White_Palace_01[right1]", "White_Palace_01[top1]",
|
||||||
|
"White_Palace_02[left1]",
|
||||||
|
"White_Palace_03_hub[bot1]", "White_Palace_03_hub[left1]", "White_Palace_03_hub[left2]",
|
||||||
|
"White_Palace_03_hub[right1]", "White_Palace_03_hub[top1]",
|
||||||
|
"White_Palace_04[right2]", "White_Palace_04[top1]",
|
||||||
|
"White_Palace_05[left1]", "White_Palace_05[left2]", "White_Palace_05[right1]", "White_Palace_05[right2]",
|
||||||
|
"White_Palace_06[bot1]", "White_Palace_06[left1]", "White_Palace_06[top1]", "White_Palace_07[bot1]",
|
||||||
|
"White_Palace_07[top1]", "White_Palace_08[left1]", "White_Palace_08[right1]",
|
||||||
|
"White_Palace_09[right1]",
|
||||||
|
"White_Palace_11[door2]",
|
||||||
|
"White_Palace_12[bot1]", "White_Palace_12[right1]",
|
||||||
|
"White_Palace_13[left1]", "White_Palace_13[left2]", "White_Palace_13[left3]", "White_Palace_13[right1]",
|
||||||
|
"White_Palace_14[bot1]", "White_Palace_14[right1]",
|
||||||
|
"White_Palace_15[left1]", "White_Palace_15[right1]", "White_Palace_15[right2]",
|
||||||
|
"White_Palace_16[left1]", "White_Palace_16[left2]",
|
||||||
|
}
|
||||||
|
|
||||||
|
white_palace_checks = {
|
||||||
|
"Soul_Totem-White_Palace_Final",
|
||||||
|
"Soul_Totem-White_Palace_Entrance",
|
||||||
|
"Lore_Tablet-Palace_Throne",
|
||||||
"Soul_Totem-White_Palace_Left",
|
"Soul_Totem-White_Palace_Left",
|
||||||
"Lore_Tablet-Palace_Workshop",
|
"Lore_Tablet-Palace_Workshop",
|
||||||
"Soul_Totem-White_Palace_Hub",
|
"Soul_Totem-White_Palace_Hub",
|
||||||
"Journal_Entry-Seal_of_Binding",
|
"Soul_Totem-White_Palace_Right"
|
||||||
"Soul_Totem-White_Palace_Right",
|
}
|
||||||
"King_Fragment",
|
|
||||||
# Events:
|
white_palace_events = {
|
||||||
"Palace_Entrance_Lantern_Lit",
|
|
||||||
"Palace_Left_Lantern_Lit",
|
|
||||||
"Palace_Right_Lantern_Lit",
|
|
||||||
"Warp-Path_of_Pain_Complete",
|
|
||||||
"Defeated_Path_of_Pain_Arena",
|
|
||||||
"Palace_Atrium_Gates_Opened",
|
|
||||||
"Completed_Path_of_Pain",
|
|
||||||
"Warp-White_Palace_Atrium_to_Palace_Grounds",
|
|
||||||
"Warp-White_Palace_Entrance_to_Palace_Grounds",
|
|
||||||
# Event-Regions:
|
|
||||||
"White_Palace_03_hub",
|
"White_Palace_03_hub",
|
||||||
"White_Palace_13",
|
"White_Palace_13",
|
||||||
"White_Palace_01",
|
"White_Palace_01",
|
||||||
# Event-Transitions:
|
"Palace_Entrance_Lantern_Lit",
|
||||||
"White_Palace_12[bot1]", "White_Palace_12[bot1]", "White_Palace_03_hub[bot1]", "White_Palace_16[left2]",
|
"Palace_Left_Lantern_Lit",
|
||||||
"White_Palace_16[left2]", "White_Palace_11[door2]", "White_Palace_11[door2]", "White_Palace_18[top1]",
|
"Palace_Right_Lantern_Lit",
|
||||||
"White_Palace_18[top1]", "White_Palace_15[left1]", "White_Palace_15[left1]", "White_Palace_05[left2]",
|
"Palace_Atrium_Gates_Opened",
|
||||||
"White_Palace_05[left2]", "White_Palace_14[bot1]", "White_Palace_14[bot1]", "White_Palace_13[left2]",
|
"Warp-White_Palace_Atrium_to_Palace_Grounds",
|
||||||
"White_Palace_13[left2]", "White_Palace_03_hub[left1]", "White_Palace_03_hub[left1]", "White_Palace_15[right2]",
|
"Warp-White_Palace_Entrance_to_Palace_Grounds",
|
||||||
"White_Palace_15[right2]", "White_Palace_06[top1]", "White_Palace_06[top1]", "White_Palace_03_hub[bot1]",
|
|
||||||
"White_Palace_08[right1]", "White_Palace_08[right1]", "White_Palace_03_hub[right1]", "White_Palace_03_hub[right1]",
|
|
||||||
"White_Palace_01[right1]", "White_Palace_01[right1]", "White_Palace_08[left1]", "White_Palace_08[left1]",
|
|
||||||
"White_Palace_19[left1]", "White_Palace_19[left1]", "White_Palace_04[right2]", "White_Palace_04[right2]",
|
|
||||||
"White_Palace_01[left1]", "White_Palace_01[left1]", "White_Palace_17[right1]", "White_Palace_17[right1]",
|
|
||||||
"White_Palace_07[bot1]", "White_Palace_07[bot1]", "White_Palace_20[bot1]", "White_Palace_20[bot1]",
|
|
||||||
"White_Palace_03_hub[left2]", "White_Palace_03_hub[left2]", "White_Palace_18[right1]", "White_Palace_18[right1]",
|
|
||||||
"White_Palace_05[right1]", "White_Palace_05[right1]", "White_Palace_17[bot1]", "White_Palace_17[bot1]",
|
|
||||||
"White_Palace_09[right1]", "White_Palace_09[right1]", "White_Palace_16[left1]", "White_Palace_16[left1]",
|
|
||||||
"White_Palace_13[left1]", "White_Palace_13[left1]", "White_Palace_06[bot1]", "White_Palace_06[bot1]",
|
|
||||||
"White_Palace_15[right1]", "White_Palace_15[right1]", "White_Palace_06[left1]", "White_Palace_06[left1]",
|
|
||||||
"White_Palace_05[right2]", "White_Palace_05[right2]", "White_Palace_04[top1]", "White_Palace_04[top1]",
|
|
||||||
"White_Palace_19[top1]", "White_Palace_19[top1]", "White_Palace_14[right1]", "White_Palace_14[right1]",
|
|
||||||
"White_Palace_03_hub[top1]", "White_Palace_03_hub[top1]", "Grubfather_2", "White_Palace_13[left3]",
|
|
||||||
"White_Palace_13[left3]", "White_Palace_02[left1]", "White_Palace_02[left1]", "White_Palace_12[right1]",
|
|
||||||
"White_Palace_12[right1]", "White_Palace_07[top1]", "White_Palace_07[top1]", "White_Palace_05[left1]",
|
|
||||||
"White_Palace_05[left1]", "White_Palace_13[right1]", "White_Palace_13[right1]", "White_Palace_01[top1]",
|
|
||||||
"White_Palace_01[top1]",
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progression_charms = {
|
progression_charms = {
|
||||||
# Baulder Killers
|
# Baldur Killers
|
||||||
"Grubberfly's_Elegy", "Weaversong", "Glowing_Womb",
|
"Grubberfly's_Elegy", "Weaversong", "Glowing_Womb",
|
||||||
# Spore Shroom spots in fungle wastes
|
# Spore Shroom spots in fungal wastes and elsewhere
|
||||||
"Spore_Shroom",
|
"Spore_Shroom",
|
||||||
# Tuk gives egg,
|
# Tuk gives egg,
|
||||||
"Defender's_Crest",
|
"Defender's_Crest",
|
||||||
|
@ -87,6 +90,14 @@ progression_charms = {
|
||||||
"Grimmchild1", "Grimmchild2"
|
"Grimmchild1", "Grimmchild2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Vanilla placements of the following items have no impact on logic, thus we can avoid creating these items and
|
||||||
|
# locations entirely when the option to randomize them is disabled.
|
||||||
|
logicless_options = {
|
||||||
|
"RandomizeVesselFragments", "RandomizeGeoChests", "RandomizeJunkPitChests", "RandomizeRelics",
|
||||||
|
"RandomizeMaps", "RandomizeJournalEntries", "RandomizeGeoRocks", "RandomizeBossGeo",
|
||||||
|
"RandomizeLoreTablets", "RandomizeSoulTotems",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class HKWeb(WebWorld):
|
class HKWeb(WebWorld):
|
||||||
tutorials = [Tutorial(
|
tutorials = [Tutorial(
|
||||||
|
@ -125,8 +136,6 @@ class HKWorld(World):
|
||||||
charm_costs: typing.List[int]
|
charm_costs: typing.List[int]
|
||||||
data_version = 2
|
data_version = 2
|
||||||
|
|
||||||
allow_white_palace = False
|
|
||||||
|
|
||||||
def __init__(self, world, player):
|
def __init__(self, world, player):
|
||||||
super(HKWorld, self).__init__(world, player)
|
super(HKWorld, self).__init__(world, player)
|
||||||
self.created_multi_locations: typing.Dict[str, int] = Counter()
|
self.created_multi_locations: typing.Dict[str, int] = Counter()
|
||||||
|
@ -136,7 +145,7 @@ class HKWorld(World):
|
||||||
world = self.world
|
world = self.world
|
||||||
charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random)
|
charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random)
|
||||||
self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs)
|
self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs)
|
||||||
world.exclude_locations[self.player].value.update(white_palace_locations)
|
# world.exclude_locations[self.player].value.update(white_palace_locations)
|
||||||
world.local_items[self.player].value.add("Mimic_Grub")
|
world.local_items[self.player].value.add("Mimic_Grub")
|
||||||
for vendor, unit in self.shops.items():
|
for vendor, unit in self.shops.items():
|
||||||
mini = getattr(world, f"Minimum{unit}Price")[self.player]
|
mini = getattr(world, f"Minimum{unit}Price")[self.player]
|
||||||
|
@ -149,23 +158,42 @@ class HKWorld(World):
|
||||||
for option_name in disabled:
|
for option_name in disabled:
|
||||||
getattr(world, option_name)[self.player].value = 0
|
getattr(world, option_name)[self.player].value = 0
|
||||||
|
|
||||||
|
def white_palace_exclusions(self):
|
||||||
|
exclusions = set()
|
||||||
|
wp = self.world.WhitePalace[self.player]
|
||||||
|
if wp <= WhitePalace.option_nopathofpain:
|
||||||
|
exclusions.update(path_of_pain_locations)
|
||||||
|
if wp <= WhitePalace.option_kingfragment:
|
||||||
|
exclusions.update(white_palace_checks)
|
||||||
|
if wp == WhitePalace.option_exclude and self.world.RandomizeCharms[self.player]:
|
||||||
|
# Ensure KF location is still reachable if charms are non-randomized
|
||||||
|
exclusions.update(white_palace_transitions)
|
||||||
|
exclusions.update(white_palace_events)
|
||||||
|
exclusions.add("King_Fragment")
|
||||||
|
return exclusions
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
menu_region: Region = create_region(self.world, self.player, 'Menu')
|
menu_region: Region = create_region(self.world, self.player, 'Menu')
|
||||||
self.world.regions.append(menu_region)
|
self.world.regions.append(menu_region)
|
||||||
|
wp_exclusions = self.white_palace_exclusions()
|
||||||
|
|
||||||
# Link regions
|
# Link regions
|
||||||
for event_name in event_names:
|
for event_name in event_names:
|
||||||
|
if event_name in wp_exclusions:
|
||||||
|
continue
|
||||||
loc = HKLocation(self.player, event_name, None, menu_region)
|
loc = HKLocation(self.player, event_name, None, menu_region)
|
||||||
loc.place_locked_item(HKItem(event_name,
|
loc.place_locked_item(HKItem(event_name,
|
||||||
self.allow_white_palace or event_name not in white_palace_locations,
|
event_name not in wp_exclusions,
|
||||||
None, "Event", self.player))
|
None, "Event", self.player))
|
||||||
menu_region.locations.append(loc)
|
menu_region.locations.append(loc)
|
||||||
for entry_transition, exit_transition in connectors.items():
|
for entry_transition, exit_transition in connectors.items():
|
||||||
|
if entry_transition in wp_exclusions:
|
||||||
|
continue
|
||||||
if exit_transition:
|
if exit_transition:
|
||||||
# if door logic fulfilled -> award vanilla target as event
|
# if door logic fulfilled -> award vanilla target as event
|
||||||
loc = HKLocation(self.player, entry_transition, None, menu_region)
|
loc = HKLocation(self.player, entry_transition, None, menu_region)
|
||||||
loc.place_locked_item(HKItem(exit_transition,
|
loc.place_locked_item(HKItem(exit_transition,
|
||||||
self.allow_white_palace or exit_transition not in white_palace_locations,
|
exit_transition not in wp_exclusions,
|
||||||
None, "Event", self.player))
|
None, "Event", self.player))
|
||||||
menu_region.locations.append(loc)
|
menu_region.locations.append(loc)
|
||||||
|
|
||||||
|
@ -178,21 +206,26 @@ class HKWorld(World):
|
||||||
geo_replace.add("Shade_Soul")
|
geo_replace.add("Shade_Soul")
|
||||||
geo_replace.add("Descending_Dark")
|
geo_replace.add("Descending_Dark")
|
||||||
|
|
||||||
|
wp_exclusions = self.white_palace_exclusions()
|
||||||
for option_key, option in hollow_knight_randomize_options.items():
|
for option_key, option in hollow_knight_randomize_options.items():
|
||||||
if getattr(self.world, option_key)[self.player]:
|
if getattr(self.world, option_key)[self.player]:
|
||||||
for item_name, location_name in zip(option.items, option.locations):
|
for item_name, location_name in zip(option.items, option.locations):
|
||||||
|
if location_name in wp_exclusions:
|
||||||
|
continue
|
||||||
if item_name in geo_replace:
|
if item_name in geo_replace:
|
||||||
item_name = "Geo_Rock-Default"
|
item_name = "Geo_Rock-Default"
|
||||||
item = self.create_item(item_name)
|
item = self.create_item(item_name)
|
||||||
if location_name in white_palace_locations:
|
# self.create_location(location_name).place_locked_item(item)
|
||||||
self.create_location(location_name).place_locked_item(item)
|
if location_name == "Start":
|
||||||
elif location_name == "Start":
|
|
||||||
self.world.push_precollected(item)
|
self.world.push_precollected(item)
|
||||||
else:
|
else:
|
||||||
self.create_location(location_name)
|
self.create_location(location_name)
|
||||||
pool.append(item)
|
pool.append(item)
|
||||||
|
# elif option_key not in logicless_options:
|
||||||
else:
|
else:
|
||||||
for item_name, location_name in zip(option.items, option.locations):
|
for item_name, location_name in zip(option.items, option.locations):
|
||||||
|
if location_name in wp_exclusions and location_name != 'King_Fragment':
|
||||||
|
continue
|
||||||
item = self.create_item(item_name)
|
item = self.create_item(item_name)
|
||||||
if location_name == "Start":
|
if location_name == "Start":
|
||||||
self.world.push_precollected(item)
|
self.world.push_precollected(item)
|
||||||
|
@ -201,10 +234,6 @@ class HKWorld(World):
|
||||||
for i in range(self.world.EggShopSlots[self.player].value):
|
for i in range(self.world.EggShopSlots[self.player].value):
|
||||||
self.create_location("Egg_Shop")
|
self.create_location("Egg_Shop")
|
||||||
pool.append(self.create_item("Geo_Rock-Default"))
|
pool.append(self.create_item("Geo_Rock-Default"))
|
||||||
if not self.allow_white_palace:
|
|
||||||
loc = self.world.get_location("King_Fragment", self.player)
|
|
||||||
if loc.item and loc.item.name == loc.name:
|
|
||||||
loc.item.advancement = False
|
|
||||||
self.world.itempool += pool
|
self.world.itempool += pool
|
||||||
|
|
||||||
for shopname in self.shops:
|
for shopname in self.shops:
|
||||||
|
@ -222,7 +251,15 @@ class HKWorld(World):
|
||||||
world = self.world
|
world = self.world
|
||||||
player = self.player
|
player = self.player
|
||||||
if world.logic[player] != 'nologic':
|
if world.logic[player] != 'nologic':
|
||||||
world.completion_condition[player] = lambda state: state.has('DREAMER', player, 3)
|
goal = world.Goal[player]
|
||||||
|
if goal == Goal.option_siblings:
|
||||||
|
world.completion_condition[player] = lambda state: state._hk_siblings_ending(player)
|
||||||
|
elif goal == Goal.option_radiance:
|
||||||
|
world.completion_condition[player] = lambda state: state._hk_can_beat_radiance(player)
|
||||||
|
else:
|
||||||
|
# Hollow Knight or Any goal.
|
||||||
|
world.completion_condition[player] = lambda state: state._hk_can_beat_thk(player)
|
||||||
|
|
||||||
set_rules(self)
|
set_rules(self)
|
||||||
|
|
||||||
def fill_slot_data(self):
|
def fill_slot_data(self):
|
||||||
|
@ -371,3 +408,38 @@ class HKLogicMixin(LogicMixin):
|
||||||
|
|
||||||
def _hk_start(self, player, start_location: str) -> bool:
|
def _hk_start(self, player, start_location: str) -> bool:
|
||||||
return self.world.StartLocation[player] == start_location
|
return self.world.StartLocation[player] == start_location
|
||||||
|
|
||||||
|
def _hk_nail_combat(self, player: int) -> bool:
|
||||||
|
return self.has_any({'LFFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player)
|
||||||
|
|
||||||
|
def _hk_can_beat_thk(self, player: int) -> bool:
|
||||||
|
return (
|
||||||
|
self.has('Opened_Black_Egg_Temple', player)
|
||||||
|
and (self.count('FIREBALL', player) + self.count('SCREAM', player) + self.count('QUAKE', player)) > 1
|
||||||
|
and self._hk_nail_combat(player)
|
||||||
|
and (
|
||||||
|
self.has_any({'LEFTDASH', 'RIGHTDASH'}, player)
|
||||||
|
or self._hk_option(player, 'ProficientCombat')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _hk_siblings_ending(self, player: int) -> bool:
|
||||||
|
return self._hk_can_beat_thk(player) and self.has('WHITEFRAGMENT', player, 3)
|
||||||
|
|
||||||
|
def _hk_can_beat_radiance(self, player: int) -> bool:
|
||||||
|
return (
|
||||||
|
self._hk_siblings_ending(player)
|
||||||
|
and self.has('DREAMNAIL', player, 1)
|
||||||
|
and (
|
||||||
|
(self.has('LEFTCLAW', player) and self.has('RIGHTCLAW', player))
|
||||||
|
or self.has('WINGS', player)
|
||||||
|
)
|
||||||
|
and (
|
||||||
|
self.count('FIREBALL', player) + self.count('SCREAM', player)
|
||||||
|
+ self.count('QUAKE', player)
|
||||||
|
) > 1
|
||||||
|
and (
|
||||||
|
(self.has('LEFTDASH', player, 2) and self.has('RIGHTDASH', player, 2)) # Both Shade Cloaks
|
||||||
|
or (self._hk_option(player, 'ProficientCombat') and self.has('QUAKE', player)) # or Dive
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue