2022-04-01 01:23:52 +00:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2021-02-21 19:17:24 +00:00
|
|
|
|
import logging
|
2022-04-01 01:23:52 +00:00
|
|
|
|
import typing
|
2022-07-03 15:10:10 +00:00
|
|
|
|
from copy import deepcopy
|
|
|
|
|
import itertools
|
|
|
|
|
import operator
|
2021-02-21 19:17:24 +00:00
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("Hollow Knight")
|
|
|
|
|
|
2022-04-03 22:15:16 +00:00
|
|
|
|
from .Items import item_table, lookup_type_to_names, item_name_groups
|
2021-02-24 05:02:51 +00:00
|
|
|
|
from .Regions import create_regions
|
2022-07-03 15:10:10 +00:00
|
|
|
|
from .Rules import set_rules, cost_terms
|
|
|
|
|
from .Options import hollow_knight_options, hollow_knight_randomize_options, Goal, WhitePalace, CostSanity, \
|
|
|
|
|
shop_to_option
|
2022-04-01 01:23:52 +00:00
|
|
|
|
from .ExtractedData import locations, starts, multi_locations, location_to_region_lookup, \
|
2022-07-03 15:10:10 +00:00
|
|
|
|
event_names, item_effects, connectors, one_ways, vanilla_shop_costs, vanilla_location_costs
|
2022-04-08 17:22:50 +00:00
|
|
|
|
from .Charms import names as charm_names
|
2021-02-21 19:17:24 +00:00
|
|
|
|
|
2023-06-25 01:47:38 +00:00
|
|
|
|
from BaseClasses import Region, Location, MultiWorld, Item, LocationProgressType, Tutorial, ItemClassification
|
|
|
|
|
from worlds.AutoWorld import World, LogicMixin, WebWorld
|
2021-02-21 19:17:24 +00:00
|
|
|
|
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
path_of_pain_locations = {
|
2022-04-01 01:23:52 +00:00
|
|
|
|
"Soul_Totem-Path_of_Pain_Below_Thornskip",
|
|
|
|
|
"Lore_Tablet-Path_of_Pain_Entrance",
|
|
|
|
|
"Soul_Totem-Path_of_Pain_Left_of_Lever",
|
|
|
|
|
"Soul_Totem-Path_of_Pain_Hidden",
|
|
|
|
|
"Soul_Totem-Path_of_Pain_Entrance",
|
|
|
|
|
"Soul_Totem-Path_of_Pain_Final",
|
|
|
|
|
"Soul_Totem-Path_of_Pain_Below_Lever",
|
|
|
|
|
"Soul_Totem-Path_of_Pain_Second",
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
"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",
|
2022-04-01 01:23:52 +00:00
|
|
|
|
"Soul_Totem-White_Palace_Left",
|
|
|
|
|
"Lore_Tablet-Palace_Workshop",
|
|
|
|
|
"Soul_Totem-White_Palace_Hub",
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
"Soul_Totem-White_Palace_Right"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
white_palace_events = {
|
|
|
|
|
"White_Palace_03_hub",
|
|
|
|
|
"White_Palace_13",
|
|
|
|
|
"White_Palace_01",
|
2022-04-01 01:23:52 +00:00
|
|
|
|
"Palace_Entrance_Lantern_Lit",
|
|
|
|
|
"Palace_Left_Lantern_Lit",
|
|
|
|
|
"Palace_Right_Lantern_Lit",
|
|
|
|
|
"Palace_Atrium_Gates_Opened",
|
|
|
|
|
"Warp-White_Palace_Atrium_to_Palace_Grounds",
|
|
|
|
|
"Warp-White_Palace_Entrance_to_Palace_Grounds",
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-05 22:41:15 +00:00
|
|
|
|
progression_charms = {
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
# Baldur Killers
|
2022-04-05 22:41:15 +00:00
|
|
|
|
"Grubberfly's_Elegy", "Weaversong", "Glowing_Womb",
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
# Spore Shroom spots in fungal wastes and elsewhere
|
2022-04-05 22:41:15 +00:00
|
|
|
|
"Spore_Shroom",
|
|
|
|
|
# Tuk gives egg,
|
|
|
|
|
"Defender's_Crest",
|
|
|
|
|
# Unlocks Grimm Troupe
|
|
|
|
|
"Grimmchild1", "Grimmchild2"
|
|
|
|
|
}
|
|
|
|
|
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
# 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",
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
# Options that affect vanilla starting items
|
|
|
|
|
randomizable_starting_items: typing.Dict[str, typing.Tuple[str, ...]] = {
|
|
|
|
|
"RandomizeFocus": ("Focus",),
|
|
|
|
|
"RandomizeSwim": ("Swim",),
|
|
|
|
|
"RandomizeNail": ('Upslash', 'Leftslash', 'Rightslash')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Shop cost types.
|
|
|
|
|
shop_cost_types: typing.Dict[str, typing.Tuple[str, ...]] = {
|
|
|
|
|
"Egg_Shop": ("RANCIDEGGS",),
|
|
|
|
|
"Grubfather": ("GRUBS",),
|
|
|
|
|
"Seer": ("ESSENCE",),
|
|
|
|
|
"Salubra_(Requires_Charms)": ("CHARMS", "GEO"),
|
|
|
|
|
"Sly": ("GEO",),
|
|
|
|
|
"Sly_(Key)": ("GEO",),
|
|
|
|
|
"Iselda": ("GEO",),
|
|
|
|
|
"Salubra": ("GEO",),
|
|
|
|
|
"Leg_Eater": ("GEO",),
|
|
|
|
|
}
|
2022-03-18 17:19:21 +00:00
|
|
|
|
|
2022-08-03 12:41:27 +00:00
|
|
|
|
|
2022-05-11 18:05:53 +00:00
|
|
|
|
class HKWeb(WebWorld):
|
|
|
|
|
tutorials = [Tutorial(
|
|
|
|
|
"Mod Setup and Use Guide",
|
|
|
|
|
"A guide to playing Hollow Knight with Archipelago.",
|
|
|
|
|
"English",
|
|
|
|
|
"setup_en.md",
|
|
|
|
|
"setup/en",
|
|
|
|
|
["Ijwu"]
|
|
|
|
|
)]
|
|
|
|
|
|
2022-08-03 12:41:27 +00:00
|
|
|
|
bug_report_page = "https://github.com/Ijwu/Archipelago.HollowKnight/issues/new?assignees=&labels=bug%2C+needs+investigation&template=bug_report.md&title="
|
|
|
|
|
|
2022-05-11 18:05:53 +00:00
|
|
|
|
|
2021-06-11 12:22:44 +00:00
|
|
|
|
class HKWorld(World):
|
2022-04-02 18:49:27 +00:00
|
|
|
|
"""Beneath the fading town of Dirtmouth sleeps a vast, ancient kingdom. Many are drawn beneath the surface,
|
|
|
|
|
searching for riches, or glory, or answers to old secrets.
|
|
|
|
|
|
2022-04-02 18:56:26 +00:00
|
|
|
|
As the enigmatic Knight, you’ll traverse the depths, unravel its mysteries and conquer its evils.
|
2022-04-02 18:49:27 +00:00
|
|
|
|
""" # from https://www.hollowknight.com
|
2021-06-11 12:22:44 +00:00
|
|
|
|
game: str = "Hollow Knight"
|
2022-08-15 21:46:59 +00:00
|
|
|
|
option_definitions = hollow_knight_options
|
2021-06-26 16:18:12 +00:00
|
|
|
|
|
2022-05-11 18:05:53 +00:00
|
|
|
|
web = HKWeb()
|
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
item_name_to_id = {name: data.id for name, data in item_table.items()}
|
|
|
|
|
location_name_to_id = {location_name: location_id for location_id, location_name in
|
|
|
|
|
enumerate(locations, start=0x1000000)}
|
2022-04-03 22:15:16 +00:00
|
|
|
|
item_name_groups = item_name_groups
|
2021-07-12 16:05:46 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
ranges: typing.Dict[str, typing.Tuple[int, int]]
|
|
|
|
|
charm_costs: typing.List[int]
|
2022-07-03 15:10:10 +00:00
|
|
|
|
cached_filler_items = {}
|
2022-04-01 01:23:52 +00:00
|
|
|
|
data_version = 2
|
|
|
|
|
|
|
|
|
|
def __init__(self, world, player):
|
|
|
|
|
super(HKWorld, self).__init__(world, player)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
self.created_multi_locations: typing.Dict[str, typing.List[HKLocation]] = {
|
|
|
|
|
location: list() for location in multi_locations
|
|
|
|
|
}
|
2022-04-01 01:23:52 +00:00
|
|
|
|
self.ranges = {}
|
2022-07-03 15:10:10 +00:00
|
|
|
|
self.created_shop_items = 0
|
|
|
|
|
self.vanilla_shop_costs = deepcopy(vanilla_shop_costs)
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
|
def generate_early(self):
|
2022-11-01 02:41:21 +00:00
|
|
|
|
world = self.multiworld
|
2022-04-12 15:13:52 +00:00
|
|
|
|
charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random)
|
|
|
|
|
self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs)
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
# world.exclude_locations[self.player].value.update(white_palace_locations)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
for term, data in cost_terms.items():
|
|
|
|
|
mini = getattr(world, f"Minimum{data.option}Price")[self.player]
|
|
|
|
|
maxi = getattr(world, f"Maximum{data.option}Price")[self.player]
|
2022-04-01 01:23:52 +00:00
|
|
|
|
# if minimum > maximum, set minimum to maximum
|
|
|
|
|
mini.value = min(mini.value, maxi.value)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
self.ranges[term] = mini.value, maxi.value
|
2022-04-03 22:23:40 +00:00
|
|
|
|
world.push_precollected(HKItem(starts[world.StartLocation[self.player].current_key],
|
2022-04-01 01:23:52 +00:00
|
|
|
|
True, None, "Event", self.player))
|
|
|
|
|
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
def white_palace_exclusions(self):
|
|
|
|
|
exclusions = set()
|
2022-11-01 02:41:21 +00:00
|
|
|
|
wp = self.multiworld.WhitePalace[self.player]
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
if wp <= WhitePalace.option_nopathofpain:
|
|
|
|
|
exclusions.update(path_of_pain_locations)
|
|
|
|
|
if wp <= WhitePalace.option_kingfragment:
|
|
|
|
|
exclusions.update(white_palace_checks)
|
2022-06-25 18:15:03 +00:00
|
|
|
|
if wp == WhitePalace.option_exclude:
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
exclusions.add("King_Fragment")
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if self.multiworld.RandomizeCharms[self.player]:
|
2022-06-25 18:15:03 +00:00
|
|
|
|
# If charms are randomized, this will be junk-filled -- so transitions and events are not progression
|
|
|
|
|
exclusions.update(white_palace_transitions)
|
|
|
|
|
exclusions.update(white_palace_events)
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
return exclusions
|
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
def create_regions(self):
|
2022-11-01 02:41:21 +00:00
|
|
|
|
menu_region: Region = create_region(self.multiworld, self.player, 'Menu')
|
|
|
|
|
self.multiworld.regions.append(menu_region)
|
2022-06-25 18:15:03 +00:00
|
|
|
|
# wp_exclusions = self.white_palace_exclusions()
|
2021-08-27 12:52:33 +00:00
|
|
|
|
|
2024-05-02 13:26:17 +00:00
|
|
|
|
# check for any goal that godhome events are relevant to
|
|
|
|
|
all_event_names = event_names.copy()
|
|
|
|
|
if self.multiworld.Goal[self.player] in [Goal.option_godhome, Goal.option_godhome_flower]:
|
|
|
|
|
from .GodhomeData import godhome_event_names
|
|
|
|
|
all_event_names.update(set(godhome_event_names))
|
|
|
|
|
|
2021-06-26 16:18:12 +00:00
|
|
|
|
# Link regions
|
2024-05-02 13:26:17 +00:00
|
|
|
|
for event_name in all_event_names:
|
2022-06-25 18:15:03 +00:00
|
|
|
|
#if event_name in wp_exclusions:
|
|
|
|
|
# continue
|
2022-04-01 01:23:52 +00:00
|
|
|
|
loc = HKLocation(self.player, event_name, None, menu_region)
|
|
|
|
|
loc.place_locked_item(HKItem(event_name,
|
2022-06-25 18:15:03 +00:00
|
|
|
|
True, #event_name not in wp_exclusions,
|
2022-04-01 01:23:52 +00:00
|
|
|
|
None, "Event", self.player))
|
|
|
|
|
menu_region.locations.append(loc)
|
|
|
|
|
for entry_transition, exit_transition in connectors.items():
|
2022-06-25 18:15:03 +00:00
|
|
|
|
#if entry_transition in wp_exclusions:
|
|
|
|
|
# continue
|
2022-04-01 01:23:52 +00:00
|
|
|
|
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,
|
2022-06-25 18:15:03 +00:00
|
|
|
|
True, #exit_transition not in wp_exclusions,
|
2022-04-01 01:23:52 +00:00
|
|
|
|
None, "Event", self.player))
|
|
|
|
|
menu_region.locations.append(loc)
|
|
|
|
|
|
|
|
|
|
def create_items(self):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
unfilled_locations = 0
|
2022-04-01 01:23:52 +00:00
|
|
|
|
# Generate item pool and associated locations (paired in HK)
|
|
|
|
|
pool: typing.List[HKItem] = []
|
2022-07-03 15:10:10 +00:00
|
|
|
|
wp_exclusions = self.white_palace_exclusions()
|
|
|
|
|
junk_replace: typing.Set[str] = set()
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if self.multiworld.RemoveSpellUpgrades[self.player]:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
junk_replace.update(("Abyss_Shriek", "Shade_Soul", "Descending_Dark"))
|
|
|
|
|
|
|
|
|
|
randomized_starting_items = set()
|
|
|
|
|
for attr, items in randomizable_starting_items.items():
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if getattr(self.multiworld, attr)[self.player]:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
randomized_starting_items.update(items)
|
|
|
|
|
|
|
|
|
|
# noinspection PyShadowingNames
|
2024-03-13 11:45:43 +00:00
|
|
|
|
def _add(item_name: str, location_name: str, randomized: bool):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
"""
|
|
|
|
|
Adds a pairing of an item and location, doing appropriate checks to see if it should be vanilla or not.
|
|
|
|
|
"""
|
|
|
|
|
nonlocal unfilled_locations
|
|
|
|
|
|
|
|
|
|
vanilla = not randomized
|
|
|
|
|
excluded = False
|
|
|
|
|
|
|
|
|
|
if not vanilla and location_name in wp_exclusions:
|
|
|
|
|
if location_name == 'King_Fragment':
|
|
|
|
|
excluded = True
|
|
|
|
|
else:
|
|
|
|
|
vanilla = True
|
|
|
|
|
|
|
|
|
|
if item_name in junk_replace:
|
|
|
|
|
item_name = self.get_filler_item_name()
|
|
|
|
|
|
2024-03-13 11:45:43 +00:00
|
|
|
|
item = self.create_item(item_name) if not vanilla or location_name == "Start" or self.multiworld.AddUnshuffledLocations[self.player] else self.create_event(item_name)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
|
|
|
|
if location_name == "Start":
|
|
|
|
|
if item_name in randomized_starting_items:
|
2022-07-25 20:19:07 +00:00
|
|
|
|
if item_name == "Focus":
|
|
|
|
|
self.create_location("Focus")
|
|
|
|
|
unfilled_locations += 1
|
2022-07-03 15:10:10 +00:00
|
|
|
|
pool.append(item)
|
|
|
|
|
else:
|
2022-11-01 02:41:21 +00:00
|
|
|
|
self.multiworld.push_precollected(item)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if vanilla:
|
|
|
|
|
location = self.create_vanilla_location(location_name, item)
|
|
|
|
|
else:
|
|
|
|
|
pool.append(item)
|
|
|
|
|
if location_name in multi_locations: # Create shop locations later.
|
|
|
|
|
return
|
|
|
|
|
location = self.create_location(location_name)
|
|
|
|
|
unfilled_locations += 1
|
|
|
|
|
if excluded:
|
|
|
|
|
location.progress_type = LocationProgressType.EXCLUDED
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
|
for option_key, option in hollow_knight_randomize_options.items():
|
2022-11-01 02:41:21 +00:00
|
|
|
|
randomized = getattr(self.multiworld, option_key)[self.player]
|
2024-03-13 11:45:43 +00:00
|
|
|
|
if all([not randomized, option_key in logicless_options, not self.multiworld.AddUnshuffledLocations[self.player]]):
|
|
|
|
|
continue
|
2022-06-25 18:15:03 +00:00
|
|
|
|
for item_name, location_name in zip(option.items, option.locations):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
if item_name in junk_replace:
|
|
|
|
|
item_name = self.get_filler_item_name()
|
2022-06-25 18:15:03 +00:00
|
|
|
|
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if (item_name == "Crystal_Heart" and self.multiworld.SplitCrystalHeart[self.player]) or \
|
|
|
|
|
(item_name == "Mothwing_Cloak" and self.multiworld.SplitMothwingCloak[self.player]):
|
2024-03-13 11:45:43 +00:00
|
|
|
|
_add("Left_" + item_name, location_name, randomized)
|
|
|
|
|
_add("Right_" + item_name, "Split_" + location_name, randomized)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
continue
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if item_name == "Mantis_Claw" and self.multiworld.SplitMantisClaw[self.player]:
|
2024-03-13 11:45:43 +00:00
|
|
|
|
_add("Left_" + item_name, "Left_" + location_name, randomized)
|
|
|
|
|
_add("Right_" + item_name, "Right_" + location_name, randomized)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
continue
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if item_name == "Shade_Cloak" and self.multiworld.SplitMothwingCloak[self.player]:
|
|
|
|
|
if self.multiworld.random.randint(0, 1):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
item_name = "Left_Mothwing_Cloak"
|
2022-04-01 01:23:52 +00:00
|
|
|
|
else:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
item_name = "Right_Mothwing_Cloak"
|
2024-03-13 11:45:43 +00:00
|
|
|
|
if item_name == "Grimmchild2" and self.multiworld.RandomizeGrimmkinFlames[self.player] and self.multiworld.RandomizeCharms[self.player]:
|
|
|
|
|
_add("Grimmchild1", location_name, randomized)
|
|
|
|
|
continue
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
2024-03-13 11:45:43 +00:00
|
|
|
|
_add(item_name, location_name, randomized)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if self.multiworld.RandomizeElevatorPass[self.player]:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
randomized = True
|
2024-03-13 11:45:43 +00:00
|
|
|
|
_add("Elevator_Pass", "Elevator_Pass", randomized)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
|
|
|
|
for shop, locations in self.created_multi_locations.items():
|
2022-11-01 02:41:21 +00:00
|
|
|
|
for _ in range(len(locations), getattr(self.multiworld, shop_to_option[shop])[self.player].value):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
loc = self.create_location(shop)
|
|
|
|
|
unfilled_locations += 1
|
|
|
|
|
|
|
|
|
|
# Balance the pool
|
|
|
|
|
item_count = len(pool)
|
2022-11-01 02:41:21 +00:00
|
|
|
|
additional_shop_items = max(item_count - unfilled_locations, self.multiworld.ExtraShopSlots[self.player].value)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
|
|
|
|
# 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)
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if not self.multiworld.EggShopSlots[self.player].value: # No eggshop, so don't place items there
|
2022-07-03 15:10:10 +00:00
|
|
|
|
shops.remove('Egg_Shop')
|
|
|
|
|
|
2022-11-15 00:57:47 +00:00
|
|
|
|
if shops:
|
|
|
|
|
for _ in range(additional_shop_items):
|
|
|
|
|
shop = self.multiworld.random.choice(shops)
|
|
|
|
|
loc = self.create_location(shop)
|
|
|
|
|
unfilled_locations += 1
|
|
|
|
|
if len(self.created_multi_locations[shop]) >= 16:
|
|
|
|
|
shops.remove(shop)
|
|
|
|
|
if not shops:
|
|
|
|
|
break
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
|
|
|
|
# Create filler items, if needed
|
|
|
|
|
if item_count < unfilled_locations:
|
|
|
|
|
pool.extend(self.create_item(self.get_filler_item_name()) for _ in range(unfilled_locations - item_count))
|
2022-11-01 02:41:21 +00:00
|
|
|
|
self.multiworld.itempool += pool
|
2022-07-03 15:10:10 +00:00
|
|
|
|
self.apply_costsanity()
|
|
|
|
|
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)
|
|
|
|
|
prices = sorted(
|
|
|
|
|
(loc.costs for loc in randomized_locations),
|
|
|
|
|
key=lambda costs: (len(costs),) + tuple(costs.values())
|
|
|
|
|
)
|
|
|
|
|
for loc, costs in zip(randomized_locations, prices):
|
|
|
|
|
loc.costs = costs
|
|
|
|
|
|
|
|
|
|
def apply_costsanity(self):
|
2022-11-01 02:41:21 +00:00
|
|
|
|
setting = self.multiworld.CostSanity[self.player].value
|
2022-07-03 15:10:10 +00:00
|
|
|
|
if not setting:
|
|
|
|
|
return # noop
|
|
|
|
|
|
|
|
|
|
def _compute_weights(weights: dict, desc: str) -> typing.Dict[str, int]:
|
|
|
|
|
if all(x == 0 for x in weights.values()):
|
|
|
|
|
logger.warning(
|
2022-11-01 02:41:21 +00:00
|
|
|
|
f"All {desc} weights were zero for {self.multiworld.player_name[self.player]}."
|
2022-07-03 15:10:10 +00:00
|
|
|
|
f" Setting them to one instead."
|
|
|
|
|
)
|
|
|
|
|
weights = {k: 1 for k in weights}
|
|
|
|
|
|
|
|
|
|
return {k: v for k, v in weights.items() if v}
|
|
|
|
|
|
2022-11-01 02:41:21 +00:00
|
|
|
|
random = self.multiworld.random
|
|
|
|
|
hybrid_chance = getattr(self.multiworld, f"CostSanityHybridChance")[self.player].value
|
2022-07-03 15:10:10 +00:00
|
|
|
|
weights = {
|
2022-11-01 02:41:21 +00:00
|
|
|
|
data.term: getattr(self.multiworld, f"CostSanity{data.option}Weight")[self.player].value
|
2022-07-03 15:10:10 +00:00
|
|
|
|
for data in cost_terms.values()
|
|
|
|
|
}
|
|
|
|
|
weights_geoless = dict(weights)
|
|
|
|
|
del weights_geoless["GEO"]
|
|
|
|
|
|
|
|
|
|
weights = _compute_weights(weights, "CostSanity")
|
|
|
|
|
weights_geoless = _compute_weights(weights_geoless, "Geoless CostSanity")
|
|
|
|
|
|
|
|
|
|
if hybrid_chance > 0:
|
|
|
|
|
if len(weights) == 1:
|
|
|
|
|
logger.warning(
|
2022-11-01 02:41:21 +00:00
|
|
|
|
f"Only one cost type is available for CostSanity in {self.multiworld.player_name[self.player]}'s world."
|
2022-07-03 15:10:10 +00:00
|
|
|
|
f" CostSanityHybridChance will not trigger."
|
|
|
|
|
)
|
|
|
|
|
if len(weights_geoless) == 1:
|
|
|
|
|
logger.warning(
|
2022-11-01 02:41:21 +00:00
|
|
|
|
f"Only one cost type is available for CostSanity in {self.multiworld.player_name[self.player]}'s world."
|
2022-07-03 15:10:10 +00:00
|
|
|
|
f" CostSanityHybridChance will not trigger in geoless locations."
|
|
|
|
|
)
|
|
|
|
|
|
2022-11-01 02:41:21 +00:00
|
|
|
|
for region in self.multiworld.get_regions(self.player):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
for location in region.locations:
|
|
|
|
|
if location.vanilla:
|
|
|
|
|
continue
|
|
|
|
|
if not location.costs:
|
|
|
|
|
continue
|
|
|
|
|
if location.name == "Vessel_Fragment-Basin":
|
|
|
|
|
continue
|
|
|
|
|
if setting == CostSanity.option_notshops and location.basename in multi_locations:
|
|
|
|
|
continue
|
|
|
|
|
if setting == CostSanity.option_shopsonly and location.basename not in multi_locations:
|
|
|
|
|
continue
|
|
|
|
|
if location.basename in {'Grubfather', 'Seer', 'Eggshop'}:
|
|
|
|
|
our_weights = dict(weights_geoless)
|
2022-06-25 18:15:03 +00:00
|
|
|
|
else:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
our_weights = dict(weights)
|
2022-06-25 18:15:03 +00:00
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
rolls = 1
|
|
|
|
|
if random.randrange(100) < hybrid_chance:
|
|
|
|
|
rolls = 2
|
|
|
|
|
|
|
|
|
|
if rolls > len(our_weights):
|
|
|
|
|
terms = list(our_weights.keys()) # Can't randomly choose cost types, using all of them.
|
|
|
|
|
else:
|
|
|
|
|
terms = []
|
|
|
|
|
for _ in range(rolls):
|
|
|
|
|
term = random.choices(list(our_weights.keys()), list(our_weights.values()))[0]
|
|
|
|
|
del our_weights[term]
|
|
|
|
|
terms.append(term)
|
2021-06-26 16:18:12 +00:00
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
location.costs = {term: random.randint(*self.ranges[term]) for term in terms}
|
|
|
|
|
location.sort_costs()
|
2022-04-03 20:05:20 +00:00
|
|
|
|
|
2021-06-26 16:18:12 +00:00
|
|
|
|
def set_rules(self):
|
2022-11-01 02:41:21 +00:00
|
|
|
|
world = self.multiworld
|
2022-04-01 01:23:52 +00:00
|
|
|
|
player = self.player
|
2023-12-13 02:11:10 +00:00
|
|
|
|
goal = world.Goal[player]
|
|
|
|
|
if goal == Goal.option_hollowknight:
|
|
|
|
|
world.completion_condition[player] = lambda state: state._hk_can_beat_thk(player)
|
|
|
|
|
elif 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)
|
2024-04-09 19:12:50 +00:00
|
|
|
|
elif goal == Goal.option_godhome:
|
|
|
|
|
world.completion_condition[player] = lambda state: state.count("Defeated_Pantheon_5", player)
|
|
|
|
|
elif goal == Goal.option_godhome_flower:
|
|
|
|
|
world.completion_condition[player] = lambda state: state.count("Godhome_Flower_Quest", player)
|
2023-12-13 02:11:10 +00:00
|
|
|
|
else:
|
|
|
|
|
# Any goal
|
|
|
|
|
world.completion_condition[player] = lambda state: state._hk_can_beat_thk(player) or state._hk_can_beat_radiance(player)
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
set_rules(self)
|
2021-06-26 16:18:12 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
def fill_slot_data(self):
|
2021-06-26 16:18:12 +00:00
|
|
|
|
slot_data = {}
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
|
options = slot_data["options"] = {}
|
2022-08-15 21:46:59 +00:00
|
|
|
|
for option_name in self.option_definitions:
|
2022-11-01 02:41:21 +00:00
|
|
|
|
option = getattr(self.multiworld, option_name)[self.player]
|
2022-04-12 15:13:52 +00:00
|
|
|
|
try:
|
|
|
|
|
optionvalue = int(option.value)
|
|
|
|
|
except TypeError:
|
|
|
|
|
pass # C# side is currently typed as dict[str, int], drop what doesn't fit
|
|
|
|
|
else:
|
|
|
|
|
options[option_name] = optionvalue
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
|
# 32 bit int
|
2023-02-02 00:14:23 +00:00
|
|
|
|
slot_data["seed"] = self.multiworld.per_slot_randoms[self.player].randint(-2147483647, 2147483646)
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
# Backwards compatibility for shop cost data (HKAP < 0.1.0)
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if not self.multiworld.CostSanity[self.player]:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
for shop, terms in shop_cost_types.items():
|
|
|
|
|
unit = cost_terms[next(iter(terms))].option
|
|
|
|
|
if unit == "Geo":
|
|
|
|
|
continue
|
|
|
|
|
slot_data[f"{unit}_costs"] = {
|
|
|
|
|
loc.name: next(iter(loc.costs.values()))
|
|
|
|
|
for loc in self.created_multi_locations[shop]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# HKAP 0.1.0 and later cost data.
|
|
|
|
|
location_costs = {}
|
2022-11-01 02:41:21 +00:00
|
|
|
|
for region in self.multiworld.get_regions(self.player):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
for location in region.locations:
|
|
|
|
|
if location.costs:
|
|
|
|
|
location_costs[location.name] = location.costs
|
|
|
|
|
slot_data["location_costs"] = location_costs
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
|
slot_data["notch_costs"] = self.charm_costs
|
|
|
|
|
|
2021-06-26 16:18:12 +00:00
|
|
|
|
return slot_data
|
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
def create_item(self, name: str) -> HKItem:
|
2021-07-12 11:54:47 +00:00
|
|
|
|
item_data = item_table[name]
|
|
|
|
|
return HKItem(name, item_data.advancement, item_data.id, item_data.type, self.player)
|
2021-02-21 19:17:24 +00:00
|
|
|
|
|
2024-03-13 11:45:43 +00:00
|
|
|
|
def create_event(self, name: str) -> HKItem:
|
|
|
|
|
item_data = item_table[name]
|
|
|
|
|
return HKItem(name, item_data.advancement, None, item_data.type, self.player)
|
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
def create_location(self, name: str, vanilla=False) -> HKLocation:
|
|
|
|
|
costs = None
|
|
|
|
|
basename = name
|
|
|
|
|
if name in shop_cost_types:
|
|
|
|
|
costs = {
|
2022-11-01 02:41:21 +00:00
|
|
|
|
term: self.multiworld.random.randint(*self.ranges[term])
|
2022-07-03 15:10:10 +00:00
|
|
|
|
for term in shop_cost_types[name]
|
|
|
|
|
}
|
|
|
|
|
elif name in vanilla_location_costs:
|
|
|
|
|
costs = vanilla_location_costs[name]
|
|
|
|
|
|
|
|
|
|
multi = self.created_multi_locations.get(name)
|
|
|
|
|
|
|
|
|
|
if multi is not None:
|
|
|
|
|
i = len(multi) + 1
|
|
|
|
|
name = f"{name}_{i}"
|
2021-07-21 16:08:15 +00:00
|
|
|
|
|
2022-11-01 02:41:21 +00:00
|
|
|
|
region = self.multiworld.get_region("Menu", self.player)
|
2024-03-13 11:45:43 +00:00
|
|
|
|
|
|
|
|
|
if vanilla and not self.multiworld.AddUnshuffledLocations[self.player]:
|
|
|
|
|
loc = HKLocation(self.player, name,
|
|
|
|
|
None, region, costs=costs, vanilla=vanilla,
|
|
|
|
|
basename=basename)
|
|
|
|
|
else:
|
|
|
|
|
loc = HKLocation(self.player, name,
|
|
|
|
|
self.location_name_to_id[name], region, costs=costs, vanilla=vanilla,
|
|
|
|
|
basename=basename)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
|
|
|
|
if multi is not None:
|
|
|
|
|
multi.append(loc)
|
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
region.locations.append(loc)
|
|
|
|
|
return loc
|
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
def create_vanilla_location(self, location: str, item: Item):
|
|
|
|
|
costs = self.vanilla_shop_costs.get((location, item.name))
|
|
|
|
|
location = self.create_location(location, vanilla=True)
|
|
|
|
|
location.place_locked_item(item)
|
|
|
|
|
if costs:
|
|
|
|
|
location.costs = costs.pop()
|
2022-07-25 20:19:07 +00:00
|
|
|
|
return location
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
def collect(self, state, item: HKItem) -> bool:
|
|
|
|
|
change = super(HKWorld, self).collect(state, item)
|
2022-04-08 19:30:38 +00:00
|
|
|
|
if change:
|
|
|
|
|
for effect_name, effect_value in item_effects.get(item.name, {}).items():
|
2023-11-02 05:41:20 +00:00
|
|
|
|
state.prog_items[item.player][effect_name] += effect_value
|
2022-07-03 15:10:10 +00:00
|
|
|
|
if item.name in {"Left_Mothwing_Cloak", "Right_Mothwing_Cloak"}:
|
2023-11-02 05:41:20 +00:00
|
|
|
|
if state.prog_items[item.player].get('RIGHTDASH', 0) and \
|
|
|
|
|
state.prog_items[item.player].get('LEFTDASH', 0):
|
|
|
|
|
(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"]) = \
|
|
|
|
|
([max(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"])] * 2)
|
2022-04-01 01:23:52 +00:00
|
|
|
|
return change
|
|
|
|
|
|
|
|
|
|
def remove(self, state, item: HKItem) -> bool:
|
|
|
|
|
change = super(HKWorld, self).remove(state, item)
|
|
|
|
|
|
2022-04-08 19:30:38 +00:00
|
|
|
|
if change:
|
|
|
|
|
for effect_name, effect_value in item_effects.get(item.name, {}).items():
|
2023-11-02 05:41:20 +00:00
|
|
|
|
if state.prog_items[item.player][effect_name] == effect_value:
|
|
|
|
|
del state.prog_items[item.player][effect_name]
|
|
|
|
|
state.prog_items[item.player][effect_name] -= effect_value
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
|
return change
|
|
|
|
|
|
2022-04-02 22:12:37 +00:00
|
|
|
|
@classmethod
|
2022-04-03 22:40:19 +00:00
|
|
|
|
def stage_write_spoiler(cls, world: MultiWorld, spoiler_handle):
|
2022-04-02 22:12:37 +00:00
|
|
|
|
hk_players = world.get_game_players(cls.game)
|
|
|
|
|
spoiler_handle.write('\n\nCharm Notches:')
|
|
|
|
|
for player in hk_players:
|
|
|
|
|
name = world.get_player_name(player)
|
|
|
|
|
spoiler_handle.write(f'\n{name}\n')
|
|
|
|
|
hk_world: HKWorld = world.worlds[player]
|
2022-04-08 17:22:50 +00:00
|
|
|
|
for charm_number, cost in enumerate(hk_world.charm_costs):
|
|
|
|
|
spoiler_handle.write(f"\n{charm_names[charm_number]}: {cost}")
|
2022-04-02 22:12:37 +00:00
|
|
|
|
|
2022-04-03 22:40:19 +00:00
|
|
|
|
spoiler_handle.write('\n\nShop Prices:')
|
|
|
|
|
for player in hk_players:
|
|
|
|
|
name = world.get_player_name(player)
|
|
|
|
|
spoiler_handle.write(f'\n{name}\n')
|
|
|
|
|
hk_world: HKWorld = world.worlds[player]
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
|
|
|
|
if world.CostSanity[player].value:
|
|
|
|
|
for loc in sorted(
|
|
|
|
|
(
|
|
|
|
|
loc for loc in itertools.chain(*(region.locations for region in world.get_regions(player)))
|
|
|
|
|
if loc.costs
|
|
|
|
|
), key=operator.attrgetter('name')
|
|
|
|
|
):
|
|
|
|
|
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:
|
|
|
|
|
spoiler_handle.write(f"\n{loc}: {loc.item} costing {loc.cost_text()}")
|
2022-04-03 22:40:19 +00:00
|
|
|
|
|
2022-04-03 20:05:20 +00:00
|
|
|
|
def get_multi_location_name(self, base: str, i: typing.Optional[int]) -> str:
|
|
|
|
|
if i is None:
|
2022-07-03 15:10:10 +00:00
|
|
|
|
i = len(self.created_multi_locations[base]) + 1
|
|
|
|
|
assert 1 <= 16, "limited number of multi location IDs reserved."
|
2022-04-03 20:05:20 +00:00
|
|
|
|
return f"{base}_{i}"
|
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
|
def get_filler_item_name(self) -> str:
|
|
|
|
|
if self.player not in self.cached_filler_items:
|
|
|
|
|
fillers = ["One_Geo", "Soul_Refill"]
|
|
|
|
|
exclusions = self.white_palace_exclusions()
|
|
|
|
|
for group in (
|
|
|
|
|
'RandomizeGeoRocks', 'RandomizeSoulTotems', 'RandomizeLoreTablets', 'RandomizeJunkPitChests',
|
|
|
|
|
'RandomizeRancidEggs'
|
|
|
|
|
):
|
2022-11-01 02:41:21 +00:00
|
|
|
|
if getattr(self.multiworld, group):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
fillers.extend(item for item in hollow_knight_randomize_options[group].items if item not in
|
|
|
|
|
exclusions)
|
|
|
|
|
self.cached_filler_items[self.player] = fillers
|
2022-11-01 02:41:21 +00:00
|
|
|
|
return self.multiworld.random.choice(self.cached_filler_items[self.player])
|
2022-07-03 15:10:10 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
2023-02-14 00:06:43 +00:00
|
|
|
|
def create_region(world: MultiWorld, player: int, name: str, location_names=None) -> Region:
|
|
|
|
|
ret = Region(name, player, world)
|
2022-04-01 01:23:52 +00:00
|
|
|
|
if location_names:
|
|
|
|
|
for location in location_names:
|
|
|
|
|
loc_id = HKWorld.location_name_to_id.get(location, None)
|
2021-02-24 05:02:51 +00:00
|
|
|
|
location = HKLocation(player, location, loc_id, ret)
|
|
|
|
|
ret.locations.append(location)
|
|
|
|
|
return ret
|
|
|
|
|
|
2021-04-01 09:40:58 +00:00
|
|
|
|
|
2021-02-21 19:17:24 +00:00
|
|
|
|
class HKLocation(Location):
|
|
|
|
|
game: str = "Hollow Knight"
|
2022-07-03 15:10:10 +00:00
|
|
|
|
costs: typing.Dict[str, int] = None
|
2022-04-01 01:23:52 +00:00
|
|
|
|
unit: typing.Optional[str] = None
|
2022-07-03 15:10:10 +00:00
|
|
|
|
vanilla = False
|
|
|
|
|
basename: str
|
|
|
|
|
|
|
|
|
|
def sort_costs(self):
|
|
|
|
|
if self.costs is None:
|
|
|
|
|
return
|
|
|
|
|
self.costs = {k: self.costs[k] for k in sorted(self.costs.keys(), key=lambda x: cost_terms[x].sort)}
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self, player: int, name: str, code=None, parent=None,
|
|
|
|
|
costs: typing.Dict[str, int] = None, vanilla: bool = False, basename: str = None
|
|
|
|
|
):
|
|
|
|
|
self.basename = basename or name
|
2022-04-01 01:23:52 +00:00
|
|
|
|
super(HKLocation, self).__init__(player, name, code if code else None, parent)
|
2022-07-03 15:10:10 +00:00
|
|
|
|
self.vanilla = vanilla
|
|
|
|
|
if costs:
|
|
|
|
|
self.costs = dict(costs)
|
|
|
|
|
self.sort_costs()
|
|
|
|
|
|
|
|
|
|
def cost_text(self, separator=" and "):
|
|
|
|
|
if self.costs is None:
|
|
|
|
|
return None
|
|
|
|
|
return separator.join(
|
|
|
|
|
f"{value} {cost_terms[term].singular if value == 1 else cost_terms[term].plural}"
|
|
|
|
|
for term, value in self.costs.items()
|
|
|
|
|
)
|
2021-02-21 19:17:24 +00:00
|
|
|
|
|
2021-04-01 09:40:58 +00:00
|
|
|
|
|
2021-02-21 19:17:24 +00:00
|
|
|
|
class HKItem(Item):
|
2021-02-22 10:18:53 +00:00
|
|
|
|
game = "Hollow Knight"
|
2022-08-05 22:49:54 +00:00
|
|
|
|
type: str
|
2021-02-22 10:18:53 +00:00
|
|
|
|
|
2022-08-05 22:49:54 +00:00
|
|
|
|
def __init__(self, name, advancement, code, type: str, player: int = None):
|
2022-04-01 01:23:52 +00:00
|
|
|
|
if name == "Mimic_Grub":
|
2022-06-17 01:23:27 +00:00
|
|
|
|
classification = ItemClassification.trap
|
2022-10-10 00:26:33 +00:00
|
|
|
|
elif type in ("Grub", "DreamWarrior", "Root", "Egg", "Dreamer"):
|
2022-06-17 01:23:27 +00:00
|
|
|
|
classification = ItemClassification.progression_skip_balancing
|
|
|
|
|
elif type == "Charm" and name not in progression_charms:
|
|
|
|
|
classification = ItemClassification.progression_skip_balancing
|
2022-07-03 15:10:10 +00:00
|
|
|
|
elif type in ("Map", "Journal"):
|
|
|
|
|
classification = ItemClassification.filler
|
2023-07-11 09:49:40 +00:00
|
|
|
|
elif type in ("Ore", "Vessel"):
|
2022-07-03 15:10:10 +00:00
|
|
|
|
classification = ItemClassification.useful
|
2022-06-17 01:23:27 +00:00
|
|
|
|
elif advancement:
|
|
|
|
|
classification = ItemClassification.progression
|
|
|
|
|
else:
|
|
|
|
|
classification = ItemClassification.filler
|
|
|
|
|
super(HKItem, self).__init__(name, classification, code if code else None, player)
|
|
|
|
|
self.type = type
|
2022-04-05 22:41:15 +00:00
|
|
|
|
|
2021-02-24 05:02:51 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
class HKLogicMixin(LogicMixin):
|
2022-11-01 02:41:21 +00:00
|
|
|
|
multiworld: MultiWorld
|
2021-02-21 19:17:24 +00:00
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
|
def _hk_notches(self, player: int, *notches: int) -> int:
|
2022-11-01 02:41:21 +00:00
|
|
|
|
return sum(self.multiworld.worlds[player].charm_costs[notch] for notch in notches)
|
2021-07-15 11:31:33 +00:00
|
|
|
|
|
2022-04-05 13:01:33 +00:00
|
|
|
|
def _hk_option(self, player: int, option_name: str) -> int:
|
2022-11-01 02:41:21 +00:00
|
|
|
|
return getattr(self.multiworld, option_name)[player].value
|
2021-07-15 11:31:33 +00:00
|
|
|
|
|
2022-04-05 13:01:33 +00:00
|
|
|
|
def _hk_start(self, player, start_location: str) -> bool:
|
2022-11-01 02:41:21 +00:00
|
|
|
|
return self.multiworld.StartLocation[player] == start_location
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
|
|
|
|
|
def _hk_nail_combat(self, player: int) -> bool:
|
2023-07-25 03:32:57 +00:00
|
|
|
|
return self.has_any({'LEFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player)
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
)
|
2023-07-11 09:49:40 +00:00
|
|
|
|
and self.has('FOCUS', player)
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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 (
|
2023-07-11 09:49:40 +00:00
|
|
|
|
self.has('Opened_Black_Egg_Temple', player)
|
|
|
|
|
and self._hk_nail_combat(player)
|
|
|
|
|
and self.has('WHITEFRAGMENT', player, 3)
|
|
|
|
|
and self.has('DREAMNAIL', player)
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
and (
|
|
|
|
|
(self.has('LEFTCLAW', player) and self.has('RIGHTCLAW', player))
|
|
|
|
|
or self.has('WINGS', player)
|
|
|
|
|
)
|
2023-07-11 09:49:40 +00:00
|
|
|
|
and (self.count('FIREBALL', player) + self.count('SCREAM', player) + self.count('QUAKE', player)) > 1
|
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.
2022-06-13 06:23:03 +00:00
|
|
|
|
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
|
|
|
|
|
)
|
|
|
|
|
)
|