Bug Squashing

This commit is contained in:
Fabian Dill 2022-04-01 03:23:52 +02:00 committed by Fabian Dill
parent 70e3c47120
commit 199b778d2b
11 changed files with 2510 additions and 1220 deletions

1
worlds/hk/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/Resources

File diff suppressed because one or more lines are too long

377
worlds/hk/Extractor.py Normal file
View File

@ -0,0 +1,377 @@
"""
Logic Extractor designed for "Randomizer 4".
Place a Randomizer 4 compatible "Resources" folder next to this script, then run the script, to create AP data.
"""
import os
import json
import typing
import ast
import jinja2
try:
from ast import unparse
except ImportError:
# Py 3.8 and earlier compatibility module
from astunparse import unparse
from Utils import get_text_between
def put_digits_at_end(text: str) -> str:
for x in range(len(text)):
if text[0].isdigit():
text = text[1:] + text[0]
else:
break
return text
def hk_loads(file: str) -> typing.Any:
with open(file) as f:
data = f.read()
new_data = []
for row in data.split("\n"):
if not row.strip().startswith(r"//"):
new_data.append(row)
return json.loads("\n".join(new_data))
def hk_convert(text: str) -> str:
parts = text.replace("(", "( ").replace(")", " )").replace(">", " > ").replace("=", "==").split()
new_parts = []
for part in parts:
part = put_digits_at_end(part)
if part in items or part in effect_names or part in event_names or part in connectors:
new_parts.append(f"\"{part}\"")
else:
new_parts.append(part)
text = " ".join(new_parts)
result = ""
parts = text.split("$StartLocation[")
for i, part in enumerate(parts[:-1]):
result += part + "StartLocation[\""
parts[i+1] = parts[i+1].replace("]", "\"]", 1)
text = result + parts[-1]
result = ""
parts = text.split("COMBAT[")
for i, part in enumerate(parts[:-1]):
result += part + "COMBAT[\""
parts[i+1] = parts[i+1].replace("]", "\"]", 1)
text = result + parts[-1]
return text.replace("+", "and").replace("|", "or").replace("$", "").strip()
class Absorber(ast.NodeTransformer):
additional_truths = set()
additional_falses = set()
def __init__(self, truth_values, false_values):
self.truth_values = truth_values
self.truth_values |= {"True", "None", "ANY", "ITEMRANDO"}
self.false_values = false_values
self.false_values |= {"False", "NONE", "RANDOMELEVATORS"}
super(Absorber, self).__init__()
def generic_visit(self, node: ast.AST) -> ast.AST:
# Need to call super() in any case to visit child nodes of the current one.
node = super().generic_visit(node)
return node
def visit_BoolOp(self, node: ast.BoolOp) -> ast.AST:
if type(node.op) == ast.And:
if self.is_always_true(node.values[0]):
return self.visit(node.values[1])
if self.is_always_true(node.values[1]):
return self.visit(node.values[0])
if self.is_always_false(node.values[0]) or self.is_always_false(node.values[1]):
return ast.Constant(False, ctx=ast.Load())
elif type(node.op) == ast.Or:
if self.is_always_true(node.values[0]) or self.is_always_true(node.values[1]):
return ast.Constant(True, ctx=ast.Load())
if self.is_always_false(node.values[0]):
return self.visit(node.values[1])
if self.is_always_false(node.values[1]):
return self.visit(node.values[0])
return self.generic_visit(node)
def visit_Name(self, node: ast.Name) -> ast.AST:
if node.id in self.truth_values:
return ast.Constant(True, ctx=node.ctx)
if node.id in self.false_values:
return ast.Constant(False, ctx=node.ctx)
if node.id in logic_options:
return ast.Call(
func=ast.Attribute(value=ast.Name(id='state', ctx=ast.Load()), attr='_kh_option', ctx=ast.Load()),
args=[ast.Name(id="player", ctx=ast.Load()), ast.Constant(value=logic_options[node.id])], keywords=[])
if node.id in macros:
return macros[node.id].body
if node.id in region_names:
raise Exception(f"Should be event {node.id}")
# You'd think this means reach Scene/Region of that name, but is actually waypoint/event
# if node.id in region_names:
# return ast.Call(
# func=ast.Attribute(value=ast.Name(id='state', ctx=ast.Load()), attr='can_reach', ctx=ast.Load()),
# args=[ast.Constant(value=node.id),
# ast.Constant(value="Region"),
# ast.Name(id="player", ctx=ast.Load())],
# keywords=[])
return self.generic_visit(node)
def visit_Constant(self, node: ast.Constant) -> ast.AST:
if type(node.value) == str:
logic_items.add(node.value)
return ast.Call(
func=ast.Attribute(value=ast.Name(id='state', ctx=ast.Load()), attr='count', ctx=ast.Load()),
args=[ast.Constant(value=node.value), ast.Name(id="player", ctx=ast.Load())], keywords=[])
return node
def visit_Subscript(self, node: ast.Subscript) -> ast.AST:
if node.value.id == "NotchCost":
notches = [ast.Constant(value=notch.value - 1) for notch in node.slice.elts] # apparently 1-indexed
return ast.Call(
func=ast.Attribute(value=ast.Name(id='state', ctx=ast.Load()), attr='_hk_notches', ctx=ast.Load()),
args=[ast.Name(id="player", ctx=ast.Load())] + notches, keywords=[])
elif node.value.id == "StartLocation":
node.slice.value = node.slice.value.replace(" ", "_").lower()
if node.slice.value in removed_starts:
return ast.Constant(False, ctx=node.ctx)
return ast.Call(
func=ast.Attribute(value=ast.Name(id='state', ctx=ast.Load()), attr='_kh_start', ctx=ast.Load()),
args=[ast.Name(id="player", ctx=ast.Load()), node.slice], keywords=[])
elif node.value.id == "COMBAT":
return macros[unparse(node)].body
else:
name = unparse(node)
if name in self.additional_truths:
return ast.Constant(True, ctx=ast.Load())
elif name in self.additional_falses:
return ast.Constant(False, ctx=ast.Load())
elif name in macros:
# macro such as "COMBAT[White_Palace_Arenas]"
return macros[name].body
else:
# assume Entrance
entrance = unparse(node)
assert entrance in connectors, entrance
return ast.Call(
func=ast.Attribute(value=ast.Name(id='state', ctx=ast.Load()), attr='can_reach', ctx=ast.Load()),
args=[ast.Constant(value=entrance),
ast.Constant(value="Entrance"),
ast.Name(id="player", ctx=ast.Load())],
keywords=[])
return node
def is_always_true(self, node):
if isinstance(node, ast.Name) and (node.id in self.truth_values or node.id in self.additional_truths):
return True
if isinstance(node, ast.Subscript) and unparse(node) in self.additional_truths:
return True
def is_always_false(self, node):
if isinstance(node, ast.Name) and (node.id in self.false_values or node.id in self.additional_falses):
return True
if isinstance(node, ast.Subscript) and unparse(node) in self.additional_falses:
return True
def get_parser(truths: typing.Set[str] = frozenset(), falses: typing.Set[str] = frozenset()):
return Absorber(truths, falses)
def ast_parse(parser, rule_text, truths: typing.Set[str] = frozenset(), falses: typing.Set[str] = frozenset()):
tree = ast.parse(hk_convert(rule_text), mode='eval')
parser.additional_truths = truths
parser.additional_falses = falses
new_tree = parser.visit(tree)
parser.additional_truths = set()
parser.additional_truths = set()
return new_tree
world_folder = os.path.dirname(__file__)
resources_source = os.path.join(world_folder, "Resources")
data_folder = os.path.join(resources_source, "Data")
logic_folder = os.path.join(resources_source, "Logic")
logic_options: typing.Dict[str, str] = hk_loads(os.path.join(data_folder, "logic_settings.json"))
for logic_key, logic_value in logic_options.items():
logic_options[logic_key] = logic_value.split(".", 1)[-1]
del (logic_options["RANDOMELEVATORS"])
extra_pool_options: typing.List[typing.Dict[str, typing.Any]] = hk_loads(os.path.join(data_folder, "pools.json"))
pool_options: typing.Dict[str, typing.Tuple[typing.List[str], typing.List[str]]] = {}
for option in extra_pool_options:
if option["Path"] != "False":
items: typing.List[str] = []
locations: typing.List[str] = []
for pairing in option["Vanilla"]:
items.append(pairing["item"])
location_name = pairing["location"]
if any(cost_entry["term"] == "CHARMS" for cost_entry in pairing.get("costs", [])):
location_name += "_(Requires_Charms)"
locations.append(location_name)
if option["Path"]:
# basename carries over from prior entry if no Path given
basename = option["Path"].split(".", 1)[-1]
if not basename.startswith("Randomize"):
basename = "Randomize" + basename
assert len(items) == len(locations)
if items: # skip empty pools
if basename in pool_options:
pool_options[basename] = pool_options[basename][0]+items, pool_options[basename][1]+locations
else:
pool_options[basename] = items, locations
del extra_pool_options
# items
items: typing.Dict[str, typing.Dict] = hk_loads(os.path.join(data_folder, "items.json"))
logic_items: typing.Set[str] = set()
for item_name in sorted(items):
item = items[item_name]
items[item_name] = item["Pool"]
items: typing.Dict[str, str]
extra_item_data: typing.List[typing.Dict[str, typing.Any]] = hk_loads(os.path.join(logic_folder, "items.json"))
item_effects: typing.Dict[str, typing.Dict[str, int]] = {}
effect_names: typing.Set[str] = set()
for item_data in extra_item_data:
if "FalseItem" in item_data:
item_data = item_data["FalseItem"]
effects = []
if "Effect" in item_data:
effects = [item_data["Effect"]]
elif "Effects" in item_data:
effects = item_data["Effects"]
for effect in effects:
effect_names.add(effect["Term"])
effects = {effect["Term"]: effect["Value"] for effect in effects if
effect["Term"] != item_data["Name"] and effect["Term"] != "GEO"}
if effects:
item_effects[item_data["Name"]] = effects
del extra_item_data
# locations
original_locations: typing.Dict[str, typing.Dict[str, typing.Any]] = hk_loads(os.path.join(data_folder, "locations.json"))
del(original_locations["Start"]) # Starting Inventory works different in AP
locations: typing.List[str] = []
locations_in_regions: typing.Dict[str, typing.List[str]] = {}
location_to_region_lookup: typing.Dict[str, str] = {}
multi_locations: typing.Dict[str, typing.List[str]] = {}
for location_name, location_data in original_locations.items():
region_name = location_data["SceneName"]
if location_data["FlexibleCount"]:
location_names = [f"{location_name}_{count}" for count in range(1, 17)]
multi_locations[location_name] = location_names
else:
location_names = [location_name]
location_to_region_lookup.update({name: region_name for name in location_names})
locations_in_regions.setdefault(region_name, []).extend(location_names)
locations.extend(location_names)
del original_locations
# regions
region_names: typing.Set[str] = set(hk_loads(os.path.join(data_folder, "rooms.json")))
connectors_data: typing.Dict[str, typing.Dict[str, typing.Any]] = hk_loads(os.path.join(data_folder, "transitions.json"))
connectors_logic: typing.List[typing.Dict[str, typing.Any]] = hk_loads(os.path.join(logic_folder, "transitions.json"))
exits: typing.Dict[str, typing.List[str]] = {}
connectors: typing.Dict[str, str] = {}
one_ways: typing.Set[str] = set()
for connector_name, connector_data in connectors_data.items():
exits.setdefault(connector_data["SceneName"], []).append(connector_name)
connectors[connector_name] = connector_data["VanillaTarget"]
if connector_data["Sides"] != "Both":
one_ways.add(connector_name)
del connectors_data
# starts
starts: typing.Dict[str, typing.Dict[str, typing.Any]] = hk_loads(os.path.join(data_folder, "starts.json"))
# only allow always valid starts for now
removed_starts: typing.Set[str] = {name.replace(" ", "_").lower() for name, data in starts.items() if
name != "King's Pass"}
starts: typing.Dict[str, str] = {
name.replace(" ", "_").lower(): data["sceneName"] for name, data in starts.items() if name == "King's Pass"}
# logic
falses = {"MAPAREARANDO", "FULLAREARANDO"}
macros: typing.Dict[str, ast.AST] = {
}
parser = get_parser(set(), falses)
extra_macros: typing.Dict[str, str] = hk_loads(os.path.join(logic_folder, "macros.json"))
raw_location_rules: typing.List[typing.Dict[str, str]] = hk_loads(os.path.join(logic_folder, "locations.json"))
events: typing.List[typing.Dict[str, typing.Any]] = hk_loads(os.path.join(logic_folder, "waypoints.json"))
event_names: typing.Set[str] = {event["name"] for event in events}
for macro_name, rule in extra_macros.items():
if macro_name not in macros:
macro_name = put_digits_at_end(macro_name)
if macro_name in items or macro_name in effect_names:
continue
assert macro_name not in events
rule = ast_parse(parser, rule)
macros[macro_name] = rule
if macro_name.startswith("COMBAT["):
name = get_text_between(macro_name, "COMBAT[", "]")
if not "'" in name:
macros[f"COMBAT['{name}']"] = rule
macros[f'COMBAT["{name}"]'] = rule
location_rules: typing.Dict[str, str] = {}
for loc_obj in raw_location_rules:
loc_name = loc_obj["name"]
rule = loc_obj["logic"]
if rule != "ANY":
rule = ast_parse(parser, rule)
location_rules[loc_name] = unparse(rule)
location_rules["Salubra_(Requires_Charms)"] = location_rules["Salubra"]
connectors_rules: typing.Dict[str, str] = {}
for connector_obj in connectors_logic:
name = connector_obj["Name"]
rule = connector_obj["logic"]
rule = ast_parse(parser, rule)
rule = unparse(rule)
if rule != "True":
connectors_rules[name] = rule
event_rules: typing.Dict[str, str] = {}
for event in events:
rule = ast_parse(parser, event["logic"])
rule = unparse(rule)
if rule != "True":
event_rules[event["name"]] = rule
event_rules.update(connectors_rules)
connectors_rules = {}
names = sorted({"logic_options", "starts", "pool_options", "locations", "multi_locations", "location_to_region_lookup",
"event_names", "item_effects", "items", "logic_items", "region_names",
"exits", "connectors", "one_ways"})
warning = "# This module is written by Extractor.py, do not edit manually!.\n\n"
with open(os.path.join(os.path.dirname(__file__), "ExtractedData.py"), "wt") as py:
py.write(warning)
for name in names:
py.write(f"{name} = {globals()[name]}\n")
template_env: jinja2.Environment = \
jinja2.Environment(loader=jinja2.FileSystemLoader([os.path.join(os.path.dirname(__file__), "templates")]))
rules_template = template_env.get_template("RulesTemplate.pyt")
rules = rules_template.render(location_rules=location_rules, one_ways=one_ways, connectors_rules=connectors_rules,
event_rules=event_rules)
with open("Rules.py", "wt") as py:
py.write(warning)
py.write(rules)

View File

@ -1,397 +1,20 @@
# generated by https://github.com/Berserker66/HollowKnight.RandomizerMod/blob/master/extract_data.py
# do not edit manually
from typing import Dict, Set, NamedTuple
from .ExtractedData import items, logic_items, item_effects
from .Types import HKItemData
from typing import Dict, Set
item_table = {}
item_table = \
{ '150_Geo-Resting_Grounds_Chest': HKItemData(advancement=False, id=16777336, type='Geo'),
'160_Geo-Weavers_Den_Chest': HKItemData(advancement=False, id=16777338, type='Geo'),
'1_Geo': HKItemData(advancement=False, id=16777339, type='Fake'),
'200_Geo-False_Knight_Chest': HKItemData(advancement=False, id=16777331, type='Geo'),
'380_Geo-Soul_Master_Chest': HKItemData(advancement=False, id=16777332, type='Geo'),
'620_Geo-Mantis_Lords_Chest': HKItemData(advancement=False, id=16777335, type='Geo'),
'655_Geo-Watcher_Knights_Chest': HKItemData(advancement=False, id=16777333, type='Geo'),
'80_Geo-Crystal_Peak_Chest': HKItemData(advancement=False, id=16777337, type='Geo'),
'85_Geo-Greenpath_Chest': HKItemData(advancement=False, id=16777334, type='Geo'),
'Abyss': HKItemData(advancement=True, id=0, type='Event'),
'Abyss_Shriek': HKItemData(advancement=True, id=16777236, type='Skill'),
'Ancestral_Mound': HKItemData(advancement=True, id=0, type='Event'),
'Ancient_Basin_Map': HKItemData(advancement=False, id=16777482, type='Map'),
'Arcane_Egg-Birthplace': HKItemData(advancement=False, id=16777402, type='Relic'),
'Arcane_Egg-Lifeblood_Core': HKItemData(advancement=False, id=16777400, type='Relic'),
'Arcane_Egg-Seer': HKItemData(advancement=False, id=16777399, type='Relic'),
'Arcane_Egg-Shade_Cloak': HKItemData(advancement=False, id=16777401, type='Relic'),
'Awoken_Dream_Nail': HKItemData(advancement=True, id=16777230, type='Skill'),
'Baldur_Shell': HKItemData(advancement=False, id=16777245, type='Charm'),
"Beast's_Den": HKItemData(advancement=True, id=0, type='Event'),
'Blue_Lake': HKItemData(advancement=True, id=0, type='Event'),
'Boss_Essence-Elder_Hu': HKItemData(advancement=True, id=16777418, type='Essence_Boss'),
'Boss_Essence-Failed_Champion': HKItemData(advancement=True, id=16777425, type='Essence_Boss'),
'Boss_Essence-Galien': HKItemData(advancement=True, id=16777423, type='Essence_Boss'),
'Boss_Essence-Gorb': HKItemData(advancement=True, id=16777420, type='Essence_Boss'),
'Boss_Essence-Grey_Prince_Zote': HKItemData(advancement=True, id=16777429, type='Essence_Boss'),
'Boss_Essence-Lost_Kin': HKItemData(advancement=True, id=16777427, type='Essence_Boss'),
'Boss_Essence-Markoth': HKItemData(advancement=True, id=16777424, type='Essence_Boss'),
'Boss_Essence-Marmu': HKItemData(advancement=True, id=16777421, type='Essence_Boss'),
'Boss_Essence-No_Eyes': HKItemData(advancement=True, id=16777422, type='Essence_Boss'),
'Boss_Essence-Soul_Tyrant': HKItemData(advancement=True, id=16777426, type='Essence_Boss'),
'Boss_Essence-White_Defender': HKItemData(advancement=True, id=16777428, type='Essence_Boss'),
'Boss_Essence-Xero': HKItemData(advancement=True, id=16777419, type='Essence_Boss'),
'Bottom_Left_Fungal_Wastes': HKItemData(advancement=True, id=0, type='Event'),
"Bottom_Left_Queen's_Gardens": HKItemData(advancement=True, id=0, type='Event'),
"Bottom_Right_Queen's_Gardens": HKItemData(advancement=True, id=0, type='Event'),
'Can_Stag': HKItemData(advancement=True, id=0, type='Event'),
'Cast_Off_Shell': HKItemData(advancement=True, id=0, type='Event'),
"Center_Right_Kingdom's_Edge": HKItemData(advancement=True, id=0, type='Event'),
"Central_Kingdom's_Edge": HKItemData(advancement=True, id=0, type='Event'),
'Central_Left_Waterways': HKItemData(advancement=True, id=0, type='Event'),
'Charm_Notch-Colosseum': HKItemData(advancement=False, id=16777323, type='Notch'),
'Charm_Notch-Fog_Canyon': HKItemData(advancement=False, id=16777322, type='Notch'),
'Charm_Notch-Grimm': HKItemData(advancement=False, id=16777324, type='Notch'),
'Charm_Notch-Shrumal_Ogres': HKItemData(advancement=False, id=16777321, type='Notch'),
'City_Crest': HKItemData(advancement=True, id=16777283, type='Key'),
'City_Storerooms_Stag': HKItemData(advancement=True, id=16777495, type='Stag'),
'City_of_Tears_Map': HKItemData(advancement=False, id=16777484, type='Map'),
"Collector's_Map": HKItemData(advancement=False, id=16777295, type='Key'),
'Colosseum': HKItemData(advancement=True, id=0, type='Event'),
'Crossroads': HKItemData(advancement=True, id=0, type='Event'),
'Crossroads_Map': HKItemData(advancement=False, id=16777476, type='Map'),
'Crossroads_Stag': HKItemData(advancement=True, id=16777491, type='Stag'),
'Crystal_Heart': HKItemData(advancement=True, id=16777224, type='Skill'),
'Crystal_Peak': HKItemData(advancement=True, id=0, type='Event'),
'Crystal_Peak_Map': HKItemData(advancement=False, id=16777487, type='Map'),
'Crystallized_Mound': HKItemData(advancement=True, id=0, type='Event'),
'Cyclone_Slash': HKItemData(advancement=True, id=16777237, type='Skill'),
'Dark_Deepnest': HKItemData(advancement=True, id=0, type='Event'),
'Dash_Slash': HKItemData(advancement=True, id=16777238, type='Skill'),
'Dashmaster': HKItemData(advancement=True, id=16777271, type='Charm'),
'Deep_Focus': HKItemData(advancement=False, id=16777274, type='Charm'),
'Deepnest': HKItemData(advancement=True, id=0, type='Event'),
'Deepnest_Map-Right_[Gives_Quill]': HKItemData(advancement=False, id=16777481, type='Map'),
'Deepnest_Map-Upper': HKItemData(advancement=False, id=16777480, type='Map'),
"Defender's_Crest": HKItemData(advancement=False, id=16777250, type='Charm'),
'Descending_Dark': HKItemData(advancement=True, id=16777234, type='Skill'),
'Desolate_Dive': HKItemData(advancement=True, id=16777233, type='Skill'),
'Dirtmouth': HKItemData(advancement=True, id=0, type='Event'),
'Dirtmouth_Stag': HKItemData(advancement=True, id=16777490, type='Stag'),
'Distant_Village': HKItemData(advancement=True, id=0, type='Event'),
'Distant_Village_Stag': HKItemData(advancement=True, id=16777498, type='Stag'),
'Dream_Gate': HKItemData(advancement=True, id=16777229, type='Skill'),
'Dream_Nail': HKItemData(advancement=True, id=16777228, type='Skill'),
'Dream_Wielder': HKItemData(advancement=False, id=16777270, type='Charm'),
'Dreamer': HKItemData(advancement=True, id=16777221, type='Fake'),
'Dreamshield': HKItemData(advancement=False, id=16777280, type='Charm'),
'Elegant_Key': HKItemData(advancement=True, id=16777291, type='Key'),
'Emilitia': HKItemData(advancement=True, id=0, type='Event'),
'Equipped': HKItemData(advancement=False, id=16777521, type='Fake'),
'Failed_Tramway': HKItemData(advancement=True, id=0, type='Event'),
'Far_Left_Basin': HKItemData(advancement=True, id=0, type='Event'),
'Far_Left_Waterways': HKItemData(advancement=True, id=0, type='Event'),
"Far_Queen's_Gardens": HKItemData(advancement=True, id=0, type='Event'),
'Far_Right_Deepnest': HKItemData(advancement=True, id=0, type='Event'),
'Flukenest': HKItemData(advancement=False, id=16777251, type='Charm'),
'Focus': HKItemData(advancement=True, id=16777240, type='Cursed'),
'Fog_Canyon_Map': HKItemData(advancement=False, id=16777478, type='Map'),
'Fragile_Greed': HKItemData(advancement=False, id=16777264, type='Charm'),
'Fragile_Heart': HKItemData(advancement=False, id=16777263, type='Charm'),
'Fragile_Strength': HKItemData(advancement=False, id=16777265, type='Charm'),
'Fungal_Core': HKItemData(advancement=True, id=0, type='Event'),
'Fungal_Wastes': HKItemData(advancement=True, id=0, type='Event'),
'Fungal_Wastes_Map': HKItemData(advancement=False, id=16777479, type='Map'),
'Fury_of_the_Fallen': HKItemData(advancement=False, id=16777246, type='Charm'),
'Gathering_Swarm': HKItemData(advancement=False, id=16777241, type='Charm'),
'Glowing_Womb': HKItemData(advancement=True, id=16777262, type='Charm'),
'Godtuner': HKItemData(advancement=False, id=16777294, type='Key'),
'Great_Slash': HKItemData(advancement=True, id=16777239, type='Skill'),
'Greenpath': HKItemData(advancement=True, id=0, type='Event'),
'Greenpath-QG': HKItemData(advancement=True, id=0, type='Event'),
'Greenpath_Map': HKItemData(advancement=False, id=16777477, type='Map'),
'Greenpath_Stag': HKItemData(advancement=True, id=16777492, type='Stag'),
'Grimmchild': HKItemData(advancement=True, id=16777282, type='Charm'),
'Grimmkin_Flame-Ancient_Basin': HKItemData(advancement=True, id=16777516, type='Flame'),
'Grimmkin_Flame-Brumm': HKItemData(advancement=True, id=16777518, type='Flame'),
'Grimmkin_Flame-City_Storerooms': HKItemData(advancement=True, id=16777509, type='Flame'),
'Grimmkin_Flame-Crystal_Peak': HKItemData(advancement=True, id=16777511, type='Flame'),
'Grimmkin_Flame-Fungal_Core': HKItemData(advancement=True, id=16777515, type='Flame'),
'Grimmkin_Flame-Greenpath': HKItemData(advancement=True, id=16777510, type='Flame'),
'Grimmkin_Flame-Hive': HKItemData(advancement=True, id=16777517, type='Flame'),
"Grimmkin_Flame-King's_Pass": HKItemData(advancement=True, id=16777512, type='Flame'),
"Grimmkin_Flame-Kingdom's_Edge": HKItemData(advancement=True, id=16777514, type='Flame'),
'Grimmkin_Flame-Resting_Grounds': HKItemData(advancement=True, id=16777513, type='Flame'),
'Grub-Basin_Requires_Dive': HKItemData(advancement=True, id=16777452, type='Grub'),
'Grub-Basin_Requires_Wings': HKItemData(advancement=True, id=16777451, type='Grub'),
"Grub-Beast's_Den": HKItemData(advancement=True, id=16777446, type='Grub'),
'Grub-City_of_Tears_Guarded': HKItemData(advancement=True, id=16777459, type='Grub'),
'Grub-City_of_Tears_Left': HKItemData(advancement=True, id=16777456, type='Grub'),
'Grub-Collector_1': HKItemData(advancement=True, id=16777473, type='Grub'),
'Grub-Collector_2': HKItemData(advancement=True, id=16777474, type='Grub'),
'Grub-Collector_3': HKItemData(advancement=True, id=16777475, type='Grub'),
'Grub-Crossroads_Acid': HKItemData(advancement=True, id=16777430, type='Grub'),
'Grub-Crossroads_Center': HKItemData(advancement=True, id=16777431, type='Grub'),
'Grub-Crossroads_Guarded': HKItemData(advancement=True, id=16777434, type='Grub'),
'Grub-Crossroads_Spike': HKItemData(advancement=True, id=16777433, type='Grub'),
'Grub-Crossroads_Stag': HKItemData(advancement=True, id=16777432, type='Grub'),
'Grub-Crystal_Heart': HKItemData(advancement=True, id=16777467, type='Grub'),
'Grub-Crystal_Peak_Below_Chest': HKItemData(advancement=True, id=16777462, type='Grub'),
'Grub-Crystal_Peak_Crushers': HKItemData(advancement=True, id=16777466, type='Grub'),
'Grub-Crystal_Peak_Mimic': HKItemData(advancement=True, id=16777465, type='Grub'),
'Grub-Crystal_Peak_Spike': HKItemData(advancement=True, id=16777464, type='Grub'),
'Grub-Crystallized_Mound': HKItemData(advancement=True, id=16777463, type='Grub'),
'Grub-Dark_Deepnest': HKItemData(advancement=True, id=16777445, type='Grub'),
'Grub-Deepnest_Mimic': HKItemData(advancement=True, id=16777442, type='Grub'),
'Grub-Deepnest_Nosk': HKItemData(advancement=True, id=16777443, type='Grub'),
'Grub-Deepnest_Spike': HKItemData(advancement=True, id=16777444, type='Grub'),
'Grub-Fog_Canyon': HKItemData(advancement=True, id=16777439, type='Grub'),
'Grub-Fungal_Bouncy': HKItemData(advancement=True, id=16777440, type='Grub'),
'Grub-Fungal_Spore_Shroom': HKItemData(advancement=True, id=16777441, type='Grub'),
'Grub-Greenpath_Cornifer': HKItemData(advancement=True, id=16777435, type='Grub'),
'Grub-Greenpath_Journal': HKItemData(advancement=True, id=16777436, type='Grub'),
'Grub-Greenpath_MMC': HKItemData(advancement=True, id=16777437, type='Grub'),
'Grub-Greenpath_Stag': HKItemData(advancement=True, id=16777438, type='Grub'),
'Grub-Hallownest_Crown': HKItemData(advancement=True, id=16777468, type='Grub'),
'Grub-Hive_External': HKItemData(advancement=True, id=16777449, type='Grub'),
'Grub-Hive_Internal': HKItemData(advancement=True, id=16777450, type='Grub'),
'Grub-Howling_Cliffs': HKItemData(advancement=True, id=16777469, type='Grub'),
"Grub-King's_Station": HKItemData(advancement=True, id=16777460, type='Grub'),
"Grub-Kingdom's_Edge_Camp": HKItemData(advancement=True, id=16777448, type='Grub'),
"Grub-Kingdom's_Edge_Oro": HKItemData(advancement=True, id=16777447, type='Grub'),
"Grub-Queen's_Gardens_Marmu": HKItemData(advancement=True, id=16777471, type='Grub'),
"Grub-Queen's_Gardens_Stag": HKItemData(advancement=True, id=16777470, type='Grub'),
"Grub-Queen's_Gardens_Top": HKItemData(advancement=True, id=16777472, type='Grub'),
'Grub-Resting_Grounds': HKItemData(advancement=True, id=16777461, type='Grub'),
'Grub-Soul_Sanctum': HKItemData(advancement=True, id=16777457, type='Grub'),
"Grub-Watcher's_Spire": HKItemData(advancement=True, id=16777458, type='Grub'),
'Grub-Waterways_East': HKItemData(advancement=True, id=16777454, type='Grub'),
'Grub-Waterways_Main': HKItemData(advancement=True, id=16777453, type='Grub'),
'Grub-Waterways_Requires_Tram': HKItemData(advancement=True, id=16777455, type='Grub'),
"Grubberfly's_Elegy": HKItemData(advancement=True, id=16777275, type='Charm'),
'Grubfather': HKItemData(advancement=False, id=16777519, type='Fake'),
'Grubsong': HKItemData(advancement=False, id=16777243, type='Charm'),
"Hallownest's_Crown": HKItemData(advancement=True, id=0, type='Event'),
"Hallownest_Seal-Beast's_Den": HKItemData(advancement=False, id=16777389, type='Relic'),
'Hallownest_Seal-City_Rafters': HKItemData(advancement=False, id=16777385, type='Relic'),
'Hallownest_Seal-Crossroads_Well': HKItemData(advancement=False, id=16777374, type='Relic'),
'Hallownest_Seal-Deepnest_By_Mantis_Lords': HKItemData(advancement=False, id=16777388, type='Relic'),
'Hallownest_Seal-Fog_Canyon_East': HKItemData(advancement=False, id=16777378, type='Relic'),
'Hallownest_Seal-Fog_Canyon_West': HKItemData(advancement=False, id=16777377, type='Relic'),
'Hallownest_Seal-Fungal_Wastes_Sporgs': HKItemData(advancement=False, id=16777380, type='Relic'),
'Hallownest_Seal-Greenpath': HKItemData(advancement=False, id=16777376, type='Relic'),
'Hallownest_Seal-Grubs': HKItemData(advancement=False, id=16777375, type='Relic'),
"Hallownest_Seal-King's_Station": HKItemData(advancement=False, id=16777384, type='Relic'),
'Hallownest_Seal-Mantis_Lords': HKItemData(advancement=False, id=16777381, type='Relic'),
"Hallownest_Seal-Queen's_Gardens": HKItemData(advancement=False, id=16777390, type='Relic'),
"Hallownest_Seal-Queen's_Station": HKItemData(advancement=False, id=16777379, type='Relic'),
'Hallownest_Seal-Resting_Grounds_Catacombs': HKItemData(advancement=False, id=16777383, type='Relic'),
'Hallownest_Seal-Seer': HKItemData(advancement=False, id=16777382, type='Relic'),
'Hallownest_Seal-Soul_Sanctum': HKItemData(advancement=False, id=16777386, type='Relic'),
'Hallownest_Seal-Watcher_Knight': HKItemData(advancement=False, id=16777387, type='Relic'),
'Heavy_Blow': HKItemData(advancement=False, id=16777255, type='Charm'),
'Herrah': HKItemData(advancement=True, id=16777219, type='Dreamer'),
'Hidden_Station_Stag': HKItemData(advancement=True, id=16777499, type='Stag'),
'Hive': HKItemData(advancement=True, id=0, type='Event'),
'Hiveblood': HKItemData(advancement=False, id=16777269, type='Charm'),
'Hollow Knight': HKItemData(advancement=True, id=0, type='Event'),
'Howling_Cliffs': HKItemData(advancement=True, id=0, type='Event'),
'Howling_Cliffs_Map': HKItemData(advancement=False, id=16777486, type='Map'),
'Howling_Wraiths': HKItemData(advancement=True, id=16777235, type='Skill'),
"Isma's_Grove": HKItemData(advancement=True, id=0, type='Event'),
"Isma's_Tear": HKItemData(advancement=True, id=16777227, type='Skill'),
"Joni's_Blessing": HKItemData(advancement=True, id=16777267, type='Charm'),
'Junk_Pit': HKItemData(advancement=True, id=0, type='Event'),
"King's_Brand": HKItemData(advancement=True, id=16777293, type='Key'),
"King's_Idol-Cliffs": HKItemData(advancement=False, id=16777392, type='Relic'),
"King's_Idol-Crystal_Peak": HKItemData(advancement=False, id=16777393, type='Relic'),
"King's_Idol-Deepnest": HKItemData(advancement=False, id=16777398, type='Relic'),
"King's_Idol-Dung_Defender": HKItemData(advancement=False, id=16777395, type='Relic'),
"King's_Idol-Glade_of_Hope": HKItemData(advancement=False, id=16777394, type='Relic'),
"King's_Idol-Great_Hopper": HKItemData(advancement=False, id=16777396, type='Relic'),
"King's_Idol-Grubs": HKItemData(advancement=False, id=16777391, type='Relic'),
"King's_Idol-Pale_Lurker": HKItemData(advancement=False, id=16777397, type='Relic'),
"King's_Pass": HKItemData(advancement=True, id=0, type='Event'),
"King's_Station_Stag": HKItemData(advancement=True, id=16777496, type='Stag'),
'King_Fragment': HKItemData(advancement=True, id=16777277, type='Charm'),
"Kingdom's_Edge_Map": HKItemData(advancement=False, id=16777483, type='Map'),
'Lake_of_Unn': HKItemData(advancement=True, id=0, type='Event'),
'Left_City': HKItemData(advancement=True, id=0, type='Event'),
'Left_Elevator': HKItemData(advancement=True, id=0, type='Event'),
'Left_Fog_Canyon': HKItemData(advancement=True, id=0, type='Event'),
'Lifeblood_Cocoon-Ancestral_Mound': HKItemData(advancement=False, id=16777502, type='Cocoon'),
'Lifeblood_Cocoon-Failed_Tramway': HKItemData(advancement=False, id=16777506, type='Cocoon'),
'Lifeblood_Cocoon-Fog_Canyon_West': HKItemData(advancement=False, id=16777504, type='Cocoon'),
'Lifeblood_Cocoon-Galien': HKItemData(advancement=False, id=16777507, type='Cocoon'),
'Lifeblood_Cocoon-Greenpath': HKItemData(advancement=False, id=16777503, type='Cocoon'),
"Lifeblood_Cocoon-King's_Pass": HKItemData(advancement=False, id=16777501, type='Cocoon'),
"Lifeblood_Cocoon-Kingdom's_Edge": HKItemData(advancement=False, id=16777508, type='Cocoon'),
'Lifeblood_Cocoon-Mantis_Village': HKItemData(advancement=False, id=16777505, type='Cocoon'),
'Lifeblood_Core': HKItemData(advancement=True, id=16777249, type='Charm'),
'Lifeblood_Heart': HKItemData(advancement=True, id=16777248, type='Charm'),
'Longnail': HKItemData(advancement=False, id=16777258, type='Charm'),
'Love_Key': HKItemData(advancement=True, id=16777292, type='Key'),
'Lower_Basin': HKItemData(advancement=True, id=0, type='Event'),
"Lower_King's_Station": HKItemData(advancement=True, id=0, type='Event'),
"Lower_Kingdom's_Edge": HKItemData(advancement=True, id=0, type='Event'),
'Lower_Left_Waterways': HKItemData(advancement=True, id=0, type='Event'),
'Lower_Resting_Grounds': HKItemData(advancement=True, id=0, type='Event'),
'Lower_Tram': HKItemData(advancement=True, id=0, type='Event'),
'Lumafly_Lantern': HKItemData(advancement=True, id=16777284, type='Key'),
'Lurien': HKItemData(advancement=True, id=16777217, type='Dreamer'),
'Mantis_Claw': HKItemData(advancement=True, id=16777223, type='Skill'),
'Mantis_Outskirts': HKItemData(advancement=True, id=0, type='Event'),
'Mantis_Village': HKItemData(advancement=True, id=0, type='Event'),
'Mark_of_Pride': HKItemData(advancement=True, id=16777253, type='Charm'),
'Mask_Shard-5_Grubs': HKItemData(advancement=False, id=16777301, type='Mask'),
'Mask_Shard-Bretta': HKItemData(advancement=False, id=16777311, type='Mask'),
'Mask_Shard-Brooding_Mawlek': HKItemData(advancement=False, id=16777302, type='Mask'),
'Mask_Shard-Crossroads_Goam': HKItemData(advancement=False, id=16777303, type='Mask'),
'Mask_Shard-Deepnest': HKItemData(advancement=False, id=16777306, type='Mask'),
'Mask_Shard-Enraged_Guardian': HKItemData(advancement=False, id=16777308, type='Mask'),
'Mask_Shard-Grey_Mourner': HKItemData(advancement=False, id=16777310, type='Mask'),
'Mask_Shard-Hive': HKItemData(advancement=False, id=16777309, type='Mask'),
"Mask_Shard-Queen's_Station": HKItemData(advancement=False, id=16777305, type='Mask'),
'Mask_Shard-Seer': HKItemData(advancement=False, id=16777300, type='Mask'),
'Mask_Shard-Sly1': HKItemData(advancement=False, id=16777296, type='Mask'),
'Mask_Shard-Sly2': HKItemData(advancement=False, id=16777297, type='Mask'),
'Mask_Shard-Sly3': HKItemData(advancement=False, id=16777298, type='Mask'),
'Mask_Shard-Sly4': HKItemData(advancement=False, id=16777299, type='Mask'),
'Mask_Shard-Stone_Sanctuary': HKItemData(advancement=False, id=16777304, type='Mask'),
'Mask_Shard-Waterways': HKItemData(advancement=False, id=16777307, type='Mask'),
'Mid_Basin': HKItemData(advancement=True, id=0, type='Event'),
'Monarch_Wings': HKItemData(advancement=True, id=16777225, type='Skill'),
'Monomon': HKItemData(advancement=True, id=16777218, type='Dreamer'),
'Mothwing_Cloak': HKItemData(advancement=True, id=16777222, type='Skill'),
"Nailmaster's_Glory": HKItemData(advancement=False, id=16777266, type='Charm'),
'Oro_Bench': HKItemData(advancement=True, id=0, type='Event'),
'Overgrown_Mound': HKItemData(advancement=True, id=0, type='Event'),
'Palace_Grounds': HKItemData(advancement=True, id=0, type='Event'),
'Pale_Lurker_Area': HKItemData(advancement=True, id=0, type='Event'),
'Pale_Ore-Basin': HKItemData(advancement=False, id=16777325, type='Ore'),
'Pale_Ore-Colosseum': HKItemData(advancement=False, id=16777330, type='Ore'),
'Pale_Ore-Crystal_Peak': HKItemData(advancement=False, id=16777326, type='Ore'),
'Pale_Ore-Grubs': HKItemData(advancement=False, id=16777329, type='Ore'),
'Pale_Ore-Nosk': HKItemData(advancement=False, id=16777327, type='Ore'),
'Pale_Ore-Seer': HKItemData(advancement=False, id=16777328, type='Ore'),
'Placeholder': HKItemData(advancement=False, id=16777522, type='Fake'),
'Pleasure_House': HKItemData(advancement=True, id=0, type='Event'),
"Queen's_Gardens_Map": HKItemData(advancement=False, id=16777488, type='Map'),
"Queen's_Gardens_Stag": HKItemData(advancement=True, id=16777494, type='Stag'),
"Queen's_Station": HKItemData(advancement=True, id=0, type='Event'),
"Queen's_Station_Stag": HKItemData(advancement=True, id=16777493, type='Stag'),
'Queen_Fragment': HKItemData(advancement=True, id=16777276, type='Charm'),
'Quick_Focus': HKItemData(advancement=False, id=16777247, type='Charm'),
'Quick_Slash': HKItemData(advancement=False, id=16777272, type='Charm'),
"Rancid_Egg-Beast's_Den": HKItemData(advancement=False, id=16777351, type='Egg'),
'Rancid_Egg-Blue_Lake': HKItemData(advancement=False, id=16777345, type='Egg'),
'Rancid_Egg-City_of_Tears_Left': HKItemData(advancement=False, id=16777349, type='Egg'),
'Rancid_Egg-City_of_Tears_Pleasure_House': HKItemData(advancement=False, id=16777350, type='Egg'),
'Rancid_Egg-Crystal_Peak_Dark_Room': HKItemData(advancement=False, id=16777347, type='Egg'),
'Rancid_Egg-Crystal_Peak_Dive_Entrance': HKItemData(advancement=False, id=16777346, type='Egg'),
'Rancid_Egg-Crystal_Peak_Tall_Room': HKItemData(advancement=False, id=16777348, type='Egg'),
'Rancid_Egg-Dark_Deepnest': HKItemData(advancement=False, id=16777352, type='Egg'),
'Rancid_Egg-Fungal_Core': HKItemData(advancement=False, id=16777343, type='Egg'),
'Rancid_Egg-Grubs': HKItemData(advancement=False, id=16777341, type='Egg'),
'Rancid_Egg-Near_Quick_Slash': HKItemData(advancement=False, id=16777354, type='Egg'),
"Rancid_Egg-Queen's_Gardens": HKItemData(advancement=False, id=16777344, type='Egg'),
'Rancid_Egg-Sheo': HKItemData(advancement=False, id=16777342, type='Egg'),
'Rancid_Egg-Sly': HKItemData(advancement=False, id=16777340, type='Egg'),
"Rancid_Egg-Upper_Kingdom's_Edge": HKItemData(advancement=False, id=16777355, type='Egg'),
'Rancid_Egg-Waterways_East': HKItemData(advancement=False, id=16777356, type='Egg'),
'Rancid_Egg-Waterways_Main': HKItemData(advancement=False, id=16777357, type='Egg'),
'Rancid_Egg-Waterways_West_Bluggsac': HKItemData(advancement=False, id=16777358, type='Egg'),
'Rancid_Egg-Waterways_West_Pickup': HKItemData(advancement=False, id=16777359, type='Egg'),
"Rancid_Egg-Weaver's_Den": HKItemData(advancement=False, id=16777353, type='Egg'),
'Resting_Grounds_Map': HKItemData(advancement=False, id=16777489, type='Map'),
'Resting_Grounds_Stag': HKItemData(advancement=True, id=16777497, type='Stag'),
'Right_City': HKItemData(advancement=True, id=0, type='Event'),
'Right_Elevator': HKItemData(advancement=True, id=0, type='Event'),
'Right_Fog_Canyon': HKItemData(advancement=True, id=0, type='Event'),
'Right_Waterways': HKItemData(advancement=True, id=0, type='Event'),
'Royal_Waterways_Map': HKItemData(advancement=False, id=16777485, type='Map'),
'Seer': HKItemData(advancement=False, id=16777520, type='Fake'),
'Shade_Cloak': HKItemData(advancement=True, id=16777226, type='Skill'),
'Shade_Soul': HKItemData(advancement=True, id=16777232, type='Skill'),
'Shaman_Stone': HKItemData(advancement=False, id=16777259, type='Charm'),
'Shape_of_Unn': HKItemData(advancement=False, id=16777268, type='Charm'),
'Sharp_Shadow': HKItemData(advancement=True, id=16777256, type='Charm'),
"Shopkeeper's_Key": HKItemData(advancement=True, id=16777290, type='Key'),
'Simple_Key-Basin': HKItemData(advancement=True, id=16777287, type='Key'),
'Simple_Key-City': HKItemData(advancement=True, id=16777288, type='Key'),
'Simple_Key-Lurker': HKItemData(advancement=True, id=16777289, type='Key'),
'Simple_Key-Sly': HKItemData(advancement=True, id=16777286, type='Key'),
'Soul_Catcher': HKItemData(advancement=False, id=16777260, type='Charm'),
'Soul_Eater': HKItemData(advancement=False, id=16777261, type='Charm'),
'Soul_Sanctum': HKItemData(advancement=True, id=0, type='Event'),
'Spell_Twister': HKItemData(advancement=False, id=16777273, type='Charm'),
'Spirits_Glade': HKItemData(advancement=True, id=0, type='Event'),
'Spore_Shroom': HKItemData(advancement=True, id=16777257, type='Charm'),
'Sprintmaster': HKItemData(advancement=True, id=16777279, type='Charm'),
'Stag_Nest': HKItemData(advancement=True, id=0, type='Event'),
'Stag_Nest_Stag': HKItemData(advancement=True, id=16777500, type='Stag'),
'Stalwart_Shell': HKItemData(advancement=False, id=16777244, type='Charm'),
'Steady_Body': HKItemData(advancement=False, id=16777254, type='Charm'),
'Stone_Sanctuary': HKItemData(advancement=True, id=0, type='Event'),
"Teacher's_Archives": HKItemData(advancement=True, id=0, type='Event'),
'Thorns_of_Agony': HKItemData(advancement=False, id=16777252, type='Charm'),
"Top_Kingdom's_Edge": HKItemData(advancement=True, id=0, type='Event'),
"Top_Left_Queen's_Gardens": HKItemData(advancement=True, id=0, type='Event'),
"Top_Right_Queen's_Gardens": HKItemData(advancement=True, id=0, type='Event'),
'Tower_of_Love': HKItemData(advancement=True, id=0, type='Event'),
'Tram_Pass': HKItemData(advancement=True, id=16777285, type='Key'),
'Upper_Basin': HKItemData(advancement=True, id=0, type='Event'),
'Upper_Crystal_Peak': HKItemData(advancement=True, id=0, type='Event'),
'Upper_Deepnest': HKItemData(advancement=True, id=0, type='Event'),
"Upper_King's_Station": HKItemData(advancement=True, id=0, type='Event'),
"Upper_Kingdom's_Edge": HKItemData(advancement=True, id=0, type='Event'),
'Upper_Left_Waterways': HKItemData(advancement=True, id=0, type='Event'),
'Upper_Resting_Grounds': HKItemData(advancement=True, id=0, type='Event'),
'Upper_Tram': HKItemData(advancement=True, id=0, type='Event'),
'Vengeful_Spirit': HKItemData(advancement=True, id=16777231, type='Skill'),
'Vessel_Fragment-Basin': HKItemData(advancement=False, id=16777318, type='Vessel'),
'Vessel_Fragment-City': HKItemData(advancement=False, id=16777316, type='Vessel'),
'Vessel_Fragment-Crossroads': HKItemData(advancement=False, id=16777317, type='Vessel'),
'Vessel_Fragment-Deepnest': HKItemData(advancement=False, id=16777319, type='Vessel'),
'Vessel_Fragment-Greenpath': HKItemData(advancement=False, id=16777315, type='Vessel'),
'Vessel_Fragment-Seer': HKItemData(advancement=False, id=16777314, type='Vessel'),
'Vessel_Fragment-Sly1': HKItemData(advancement=False, id=16777312, type='Vessel'),
'Vessel_Fragment-Sly2': HKItemData(advancement=False, id=16777313, type='Vessel'),
'Vessel_Fragment-Stag_Nest': HKItemData(advancement=False, id=16777320, type='Vessel'),
'Void_Heart': HKItemData(advancement=True, id=16777278, type='Charm'),
"Wanderer's_Journal-Above_Mantis_Village": HKItemData(advancement=False, id=16777364, type='Relic'),
"Wanderer's_Journal-Ancient_Basin": HKItemData(advancement=False, id=16777370, type='Relic'),
"Wanderer's_Journal-City_Storerooms": HKItemData(advancement=False, id=16777369, type='Relic'),
"Wanderer's_Journal-Cliffs": HKItemData(advancement=False, id=16777360, type='Relic'),
"Wanderer's_Journal-Crystal_Peak_Crawlers": HKItemData(advancement=False, id=16777365, type='Relic'),
"Wanderer's_Journal-Fungal_Wastes_Thorns_Gauntlet": HKItemData(advancement=False, id=16777363, type='Relic'),
"Wanderer's_Journal-Greenpath_Lower": HKItemData(advancement=False, id=16777362, type='Relic'),
"Wanderer's_Journal-Greenpath_Stag": HKItemData(advancement=False, id=16777361, type='Relic'),
"Wanderer's_Journal-King's_Station": HKItemData(advancement=False, id=16777367, type='Relic'),
"Wanderer's_Journal-Kingdom's_Edge_Camp": HKItemData(advancement=False, id=16777372, type='Relic'),
"Wanderer's_Journal-Kingdom's_Edge_Entrance": HKItemData(advancement=False, id=16777371, type='Relic'),
"Wanderer's_Journal-Kingdom's_Edge_Requires_Dive": HKItemData(advancement=False, id=16777373, type='Relic'),
"Wanderer's_Journal-Pleasure_House": HKItemData(advancement=False, id=16777368, type='Relic'),
"Wanderer's_Journal-Resting_Grounds_Catacombs": HKItemData(advancement=False, id=16777366, type='Relic'),
'Waterways_Shaft': HKItemData(advancement=True, id=0, type='Event'),
'Wayward_Compass': HKItemData(advancement=False, id=16777242, type='Charm'),
"Weaver's_Den": HKItemData(advancement=True, id=0, type='Event'),
'Weaversong': HKItemData(advancement=True, id=16777281, type='Charm'),
'Whispering_Root-Ancestral_Mound': HKItemData(advancement=True, id=16777416, type='Root'),
'Whispering_Root-City': HKItemData(advancement=True, id=16777411, type='Root'),
'Whispering_Root-Crossroads': HKItemData(advancement=True, id=16777403, type='Root'),
'Whispering_Root-Crystal_Peak': HKItemData(advancement=True, id=16777414, type='Root'),
'Whispering_Root-Deepnest': HKItemData(advancement=True, id=16777407, type='Root'),
'Whispering_Root-Greenpath': HKItemData(advancement=True, id=16777404, type='Root'),
'Whispering_Root-Hive': HKItemData(advancement=True, id=16777417, type='Root'),
'Whispering_Root-Howling_Cliffs': HKItemData(advancement=True, id=16777415, type='Root'),
'Whispering_Root-Kingdoms_Edge': HKItemData(advancement=True, id=16777409, type='Root'),
'Whispering_Root-Leg_Eater': HKItemData(advancement=True, id=16777405, type='Root'),
'Whispering_Root-Mantis_Village': HKItemData(advancement=True, id=16777406, type='Root'),
'Whispering_Root-Queens_Gardens': HKItemData(advancement=True, id=16777408, type='Root'),
'Whispering_Root-Resting_Grounds': HKItemData(advancement=True, id=16777412, type='Root'),
'Whispering_Root-Spirits_Glade': HKItemData(advancement=True, id=16777413, type='Root'),
'Whispering_Root-Waterways': HKItemData(advancement=True, id=16777410, type='Root'),
'World_Sense': HKItemData(advancement=False, id=16777220, type='Dreamer')}
lookup_id_to_name:Dict[int, str] = {data.id: item_name for item_name, data in item_table.items() if data.type != 'Event'}
lookup_type_to_names:Dict[str, Set[str]] = {}
class HKItemData(NamedTuple):
advancement: bool
id: int
type: str
for i, (item_name, item_type) in enumerate(items.items(), start=0x1000000):
item_table[item_name] = HKItemData(advancement=item_name in logic_items or item_name in item_effects,
id=i, type=item_type)
lookup_id_to_name: Dict[int, str] = {data.id: item_name for item_name, data in item_table.items()}
lookup_type_to_names: Dict[str, Set[str]] = {}
for item, item_data in item_table.items():
lookup_type_to_names.setdefault(item_data.type, set()).add(item)
lookup_type_to_names.setdefault(item_data.type, set()).add(item)

View File

@ -1,308 +0,0 @@
# generated by https://github.com/Berserker66/HollowKnight.RandomizerMod/blob/master/extract_data.py
# do not edit manually
lookup_id_to_name = \
{ 17825793: 'Lurien',
17825794: 'Monomon',
17825795: 'Herrah',
17825796: 'World_Sense',
17825798: 'Mothwing_Cloak',
17825799: 'Mantis_Claw',
17825800: 'Crystal_Heart',
17825801: 'Monarch_Wings',
17825802: 'Shade_Cloak',
17825803: "Isma's_Tear",
17825804: 'Dream_Nail',
17825805: 'Dream_Gate',
17825806: 'Awoken_Dream_Nail',
17825807: 'Vengeful_Spirit',
17825808: 'Shade_Soul',
17825809: 'Desolate_Dive',
17825810: 'Descending_Dark',
17825811: 'Howling_Wraiths',
17825812: 'Abyss_Shriek',
17825813: 'Cyclone_Slash',
17825814: 'Dash_Slash',
17825815: 'Great_Slash',
17825816: 'Focus',
17825817: 'Gathering_Swarm',
17825818: 'Wayward_Compass',
17825819: 'Grubsong',
17825820: 'Stalwart_Shell',
17825821: 'Baldur_Shell',
17825822: 'Fury_of_the_Fallen',
17825823: 'Quick_Focus',
17825824: 'Lifeblood_Heart',
17825825: 'Lifeblood_Core',
17825826: "Defender's_Crest",
17825827: 'Flukenest',
17825828: 'Thorns_of_Agony',
17825829: 'Mark_of_Pride',
17825830: 'Steady_Body',
17825831: 'Heavy_Blow',
17825832: 'Sharp_Shadow',
17825833: 'Spore_Shroom',
17825834: 'Longnail',
17825835: 'Shaman_Stone',
17825836: 'Soul_Catcher',
17825837: 'Soul_Eater',
17825838: 'Glowing_Womb',
17825839: 'Fragile_Heart',
17825840: 'Fragile_Greed',
17825841: 'Fragile_Strength',
17825842: "Nailmaster's_Glory",
17825843: "Joni's_Blessing",
17825844: 'Shape_of_Unn',
17825845: 'Hiveblood',
17825846: 'Dream_Wielder',
17825847: 'Dashmaster',
17825848: 'Quick_Slash',
17825849: 'Spell_Twister',
17825850: 'Deep_Focus',
17825851: "Grubberfly's_Elegy",
17825852: 'Queen_Fragment',
17825853: 'King_Fragment',
17825854: 'Void_Heart',
17825855: 'Sprintmaster',
17825856: 'Dreamshield',
17825857: 'Weaversong',
17825858: 'Grimmchild',
17825859: 'City_Crest',
17825860: 'Lumafly_Lantern',
17825861: 'Tram_Pass',
17825862: 'Simple_Key-Sly',
17825863: 'Simple_Key-Basin',
17825864: 'Simple_Key-City',
17825865: 'Simple_Key-Lurker',
17825866: "Shopkeeper's_Key",
17825867: 'Elegant_Key',
17825868: 'Love_Key',
17825869: "King's_Brand",
17825870: 'Godtuner',
17825871: "Collector's_Map",
17825872: 'Mask_Shard-Sly1',
17825873: 'Mask_Shard-Sly2',
17825874: 'Mask_Shard-Sly3',
17825875: 'Mask_Shard-Sly4',
17825876: 'Mask_Shard-Seer',
17825877: 'Mask_Shard-5_Grubs',
17825878: 'Mask_Shard-Brooding_Mawlek',
17825879: 'Mask_Shard-Crossroads_Goam',
17825880: 'Mask_Shard-Stone_Sanctuary',
17825881: "Mask_Shard-Queen's_Station",
17825882: 'Mask_Shard-Deepnest',
17825883: 'Mask_Shard-Waterways',
17825884: 'Mask_Shard-Enraged_Guardian',
17825885: 'Mask_Shard-Hive',
17825886: 'Mask_Shard-Grey_Mourner',
17825887: 'Mask_Shard-Bretta',
17825888: 'Vessel_Fragment-Sly1',
17825889: 'Vessel_Fragment-Sly2',
17825890: 'Vessel_Fragment-Seer',
17825891: 'Vessel_Fragment-Greenpath',
17825892: 'Vessel_Fragment-City',
17825893: 'Vessel_Fragment-Crossroads',
17825894: 'Vessel_Fragment-Basin',
17825895: 'Vessel_Fragment-Deepnest',
17825896: 'Vessel_Fragment-Stag_Nest',
17825897: 'Charm_Notch-Shrumal_Ogres',
17825898: 'Charm_Notch-Fog_Canyon',
17825899: 'Charm_Notch-Colosseum',
17825900: 'Charm_Notch-Grimm',
17825901: 'Pale_Ore-Basin',
17825902: 'Pale_Ore-Crystal_Peak',
17825903: 'Pale_Ore-Nosk',
17825904: 'Pale_Ore-Seer',
17825905: 'Pale_Ore-Grubs',
17825906: 'Pale_Ore-Colosseum',
17825907: '200_Geo-False_Knight_Chest',
17825908: '380_Geo-Soul_Master_Chest',
17825909: '655_Geo-Watcher_Knights_Chest',
17825910: '85_Geo-Greenpath_Chest',
17825911: '620_Geo-Mantis_Lords_Chest',
17825912: '150_Geo-Resting_Grounds_Chest',
17825913: '80_Geo-Crystal_Peak_Chest',
17825914: '160_Geo-Weavers_Den_Chest',
17825916: 'Rancid_Egg-Sly',
17825917: 'Rancid_Egg-Grubs',
17825918: 'Rancid_Egg-Sheo',
17825919: 'Rancid_Egg-Fungal_Core',
17825920: "Rancid_Egg-Queen's_Gardens",
17825921: 'Rancid_Egg-Blue_Lake',
17825922: 'Rancid_Egg-Crystal_Peak_Dive_Entrance',
17825923: 'Rancid_Egg-Crystal_Peak_Dark_Room',
17825924: 'Rancid_Egg-Crystal_Peak_Tall_Room',
17825925: 'Rancid_Egg-City_of_Tears_Left',
17825926: 'Rancid_Egg-City_of_Tears_Pleasure_House',
17825927: "Rancid_Egg-Beast's_Den",
17825928: 'Rancid_Egg-Dark_Deepnest',
17825929: "Rancid_Egg-Weaver's_Den",
17825930: 'Rancid_Egg-Near_Quick_Slash',
17825931: "Rancid_Egg-Upper_Kingdom's_Edge",
17825932: 'Rancid_Egg-Waterways_East',
17825933: 'Rancid_Egg-Waterways_Main',
17825934: 'Rancid_Egg-Waterways_West_Bluggsac',
17825935: 'Rancid_Egg-Waterways_West_Pickup',
17825936: "Wanderer's_Journal-Cliffs",
17825937: "Wanderer's_Journal-Greenpath_Stag",
17825938: "Wanderer's_Journal-Greenpath_Lower",
17825939: "Wanderer's_Journal-Fungal_Wastes_Thorns_Gauntlet",
17825940: "Wanderer's_Journal-Above_Mantis_Village",
17825941: "Wanderer's_Journal-Crystal_Peak_Crawlers",
17825942: "Wanderer's_Journal-Resting_Grounds_Catacombs",
17825943: "Wanderer's_Journal-King's_Station",
17825944: "Wanderer's_Journal-Pleasure_House",
17825945: "Wanderer's_Journal-City_Storerooms",
17825946: "Wanderer's_Journal-Ancient_Basin",
17825947: "Wanderer's_Journal-Kingdom's_Edge_Entrance",
17825948: "Wanderer's_Journal-Kingdom's_Edge_Camp",
17825949: "Wanderer's_Journal-Kingdom's_Edge_Requires_Dive",
17825950: 'Hallownest_Seal-Crossroads_Well',
17825951: 'Hallownest_Seal-Grubs',
17825952: 'Hallownest_Seal-Greenpath',
17825953: 'Hallownest_Seal-Fog_Canyon_West',
17825954: 'Hallownest_Seal-Fog_Canyon_East',
17825955: "Hallownest_Seal-Queen's_Station",
17825956: 'Hallownest_Seal-Fungal_Wastes_Sporgs',
17825957: 'Hallownest_Seal-Mantis_Lords',
17825958: 'Hallownest_Seal-Seer',
17825959: 'Hallownest_Seal-Resting_Grounds_Catacombs',
17825960: "Hallownest_Seal-King's_Station",
17825961: 'Hallownest_Seal-City_Rafters',
17825962: 'Hallownest_Seal-Soul_Sanctum',
17825963: 'Hallownest_Seal-Watcher_Knight',
17825964: 'Hallownest_Seal-Deepnest_By_Mantis_Lords',
17825965: "Hallownest_Seal-Beast's_Den",
17825966: "Hallownest_Seal-Queen's_Gardens",
17825967: "King's_Idol-Grubs",
17825968: "King's_Idol-Cliffs",
17825969: "King's_Idol-Crystal_Peak",
17825970: "King's_Idol-Glade_of_Hope",
17825971: "King's_Idol-Dung_Defender",
17825972: "King's_Idol-Great_Hopper",
17825973: "King's_Idol-Pale_Lurker",
17825974: "King's_Idol-Deepnest",
17825975: 'Arcane_Egg-Seer',
17825976: 'Arcane_Egg-Lifeblood_Core',
17825977: 'Arcane_Egg-Shade_Cloak',
17825978: 'Arcane_Egg-Birthplace',
17825979: 'Whispering_Root-Crossroads',
17825980: 'Whispering_Root-Greenpath',
17825981: 'Whispering_Root-Leg_Eater',
17825982: 'Whispering_Root-Mantis_Village',
17825983: 'Whispering_Root-Deepnest',
17825984: 'Whispering_Root-Queens_Gardens',
17825985: 'Whispering_Root-Kingdoms_Edge',
17825986: 'Whispering_Root-Waterways',
17825987: 'Whispering_Root-City',
17825988: 'Whispering_Root-Resting_Grounds',
17825989: 'Whispering_Root-Spirits_Glade',
17825990: 'Whispering_Root-Crystal_Peak',
17825991: 'Whispering_Root-Howling_Cliffs',
17825992: 'Whispering_Root-Ancestral_Mound',
17825993: 'Whispering_Root-Hive',
17825994: 'Boss_Essence-Elder_Hu',
17825995: 'Boss_Essence-Xero',
17825996: 'Boss_Essence-Gorb',
17825997: 'Boss_Essence-Marmu',
17825998: 'Boss_Essence-No_Eyes',
17825999: 'Boss_Essence-Galien',
17826000: 'Boss_Essence-Markoth',
17826001: 'Boss_Essence-Failed_Champion',
17826002: 'Boss_Essence-Soul_Tyrant',
17826003: 'Boss_Essence-Lost_Kin',
17826004: 'Boss_Essence-White_Defender',
17826005: 'Boss_Essence-Grey_Prince_Zote',
17826006: 'Grub-Crossroads_Acid',
17826007: 'Grub-Crossroads_Center',
17826008: 'Grub-Crossroads_Stag',
17826009: 'Grub-Crossroads_Spike',
17826010: 'Grub-Crossroads_Guarded',
17826011: 'Grub-Greenpath_Cornifer',
17826012: 'Grub-Greenpath_Journal',
17826013: 'Grub-Greenpath_MMC',
17826014: 'Grub-Greenpath_Stag',
17826015: 'Grub-Fog_Canyon',
17826016: 'Grub-Fungal_Bouncy',
17826017: 'Grub-Fungal_Spore_Shroom',
17826018: 'Grub-Deepnest_Mimic',
17826019: 'Grub-Deepnest_Nosk',
17826020: 'Grub-Deepnest_Spike',
17826021: 'Grub-Dark_Deepnest',
17826022: "Grub-Beast's_Den",
17826023: "Grub-Kingdom's_Edge_Oro",
17826024: "Grub-Kingdom's_Edge_Camp",
17826025: 'Grub-Hive_External',
17826026: 'Grub-Hive_Internal',
17826027: 'Grub-Basin_Requires_Wings',
17826028: 'Grub-Basin_Requires_Dive',
17826029: 'Grub-Waterways_Main',
17826030: 'Grub-Waterways_East',
17826031: 'Grub-Waterways_Requires_Tram',
17826032: 'Grub-City_of_Tears_Left',
17826033: 'Grub-Soul_Sanctum',
17826034: "Grub-Watcher's_Spire",
17826035: 'Grub-City_of_Tears_Guarded',
17826036: "Grub-King's_Station",
17826037: 'Grub-Resting_Grounds',
17826038: 'Grub-Crystal_Peak_Below_Chest',
17826039: 'Grub-Crystallized_Mound',
17826040: 'Grub-Crystal_Peak_Spike',
17826041: 'Grub-Crystal_Peak_Mimic',
17826042: 'Grub-Crystal_Peak_Crushers',
17826043: 'Grub-Crystal_Heart',
17826044: 'Grub-Hallownest_Crown',
17826045: 'Grub-Howling_Cliffs',
17826046: "Grub-Queen's_Gardens_Stag",
17826047: "Grub-Queen's_Gardens_Marmu",
17826048: "Grub-Queen's_Gardens_Top",
17826049: 'Grub-Collector_1',
17826050: 'Grub-Collector_2',
17826051: 'Grub-Collector_3',
17826052: 'Crossroads_Map',
17826053: 'Greenpath_Map',
17826054: 'Fog_Canyon_Map',
17826055: 'Fungal_Wastes_Map',
17826056: 'Deepnest_Map-Upper',
17826057: 'Deepnest_Map-Right_[Gives_Quill]',
17826058: 'Ancient_Basin_Map',
17826059: "Kingdom's_Edge_Map",
17826060: 'City_of_Tears_Map',
17826061: 'Royal_Waterways_Map',
17826062: 'Howling_Cliffs_Map',
17826063: 'Crystal_Peak_Map',
17826064: "Queen's_Gardens_Map",
17826065: 'Resting_Grounds_Map',
17826066: 'Dirtmouth_Stag',
17826067: 'Crossroads_Stag',
17826068: 'Greenpath_Stag',
17826069: "Queen's_Station_Stag",
17826070: "Queen's_Gardens_Stag",
17826071: 'City_Storerooms_Stag',
17826072: "King's_Station_Stag",
17826073: 'Resting_Grounds_Stag',
17826074: 'Distant_Village_Stag',
17826075: 'Hidden_Station_Stag',
17826076: 'Stag_Nest_Stag',
17826077: "Lifeblood_Cocoon-King's_Pass",
17826078: 'Lifeblood_Cocoon-Ancestral_Mound',
17826079: 'Lifeblood_Cocoon-Greenpath',
17826080: 'Lifeblood_Cocoon-Fog_Canyon_West',
17826081: 'Lifeblood_Cocoon-Mantis_Village',
17826082: 'Lifeblood_Cocoon-Failed_Tramway',
17826083: 'Lifeblood_Cocoon-Galien',
17826084: "Lifeblood_Cocoon-Kingdom's_Edge",
17826085: 'Grimmkin_Flame-City_Storerooms',
17826086: 'Grimmkin_Flame-Greenpath',
17826087: 'Grimmkin_Flame-Crystal_Peak',
17826088: "Grimmkin_Flame-King's_Pass",
17826089: 'Grimmkin_Flame-Resting_Grounds',
17826090: "Grimmkin_Flame-Kingdom's_Edge",
17826091: 'Grimmkin_Flame-Fungal_Core',
17826092: 'Grimmkin_Flame-Ancient_Basin',
17826093: 'Grimmkin_Flame-Hive',
17826094: 'Grimmkin_Flame-Brumm'}
lookup_name_to_id = {location_name: location_id for location_id, location_name in lookup_id_to_name.items()}

View File

@ -1,39 +1,138 @@
import typing
from .ExtractedData import logic_options, starts, pool_options
from Options import Option, DefaultOnToggle, Toggle, Choice, Range
from Options import Option, DefaultOnToggle, Toggle
# This way the dynamic start names are picked up by the MetaClass Choice belongs to
StartLocation = type("StartLocation", (Choice,), {
"option_" + start: i for i, start in enumerate(starts)} | {"auto_display_name": False})
hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {
"RandomizeDreamers": DefaultOnToggle,
"RandomizeSkills": DefaultOnToggle,
"RandomizeCharms": DefaultOnToggle,
"RandomizeKeys": DefaultOnToggle,
"RandomizeGeoChests": Toggle,
"RandomizeMaskShards": DefaultOnToggle,
"RandomizeVesselFragments": DefaultOnToggle,
"RandomizeCharmNotches": Toggle,
"RandomizePaleOre": DefaultOnToggle,
"RandomizeRancidEggs": Toggle,
"RandomizeRelics": DefaultOnToggle,
"RandomizeMaps": Toggle,
"RandomizeStags": Toggle,
"RandomizeGrubs": Toggle,
"RandomizeWhisperingRoots": Toggle,
"RandomizeRocks": Toggle,
"RandomizeSoulTotems": Toggle,
"RandomizePalaceTotems": Toggle,
"RandomizeLoreTablets": Toggle,
"RandomizeLifebloodCocoons": Toggle,
"RandomizeFlames": Toggle
default_on = {
"RandomizeDreamers",
"RandomizeSkills",
"RandomizeCharms",
"RandomizeKeys",
"RandomizeMaskShards",
"RandomizeVesselFragments",
"RandomizePaleOre",
"RandomizeRelics"
}
hollow_knight_skip_options: typing.Dict[str, type(Option)] = {
"MILDSKIPS": Toggle,
"SPICYSKIPS": Toggle,
"FIREBALLSKIPS": Toggle,
"ACIDSKIPS": Toggle,
"SPIKETUNNELS": Toggle,
"DARKROOMS": Toggle,
"CURSED": Toggle,
"SHADESKIPS": Toggle,
# not supported at this time
disabled = {
"RandomizeFocus",
"RandomizeSwim",
"RandomizeMimics",
"RandomizeNail",
}
hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {}
for option_name, option_data in pool_options.items():
extra_data = {"items": option_data[0], "locations": option_data[1]}
if option_name in default_on:
option = type(option_name, (DefaultOnToggle,), extra_data)
else:
option = type(option_name, (Toggle,), extra_data)
hollow_knight_randomize_options[option_name] = option
hollow_knight_logic_options: typing.Dict[str, type(Option)] = {
option_name: Toggle for option_name in logic_options.values() if
option_name not in hollow_knight_randomize_options
and option_name != "RandomizeCharmNotches"}
class MinimumGrubPrice(Range):
display_name = "Minimum Grub Price"
range_start = 1
range_end = 46
default = 1
class MaximumGrubPrice(MinimumGrubPrice):
display_name = "Maximum Grub Price"
default = 23
class MinimumEssencePrice(Range):
display_name = "Minimum Essence Price"
range_start = 1
range_end = 2800
default = 1
class MaximumEssencePrice(MinimumEssencePrice):
display_name = "Maximum Essence Price"
default = 1400
class MinimumEggPrice(Range):
display_name = "Minimum Egg Price"
range_start = 1
range_end = 21
default = 1
class MaximumEggPrice(MinimumEggPrice):
display_name = "Maximum Egg Price"
default = 10
class MinimumCharmPrice(Range):
"""For Salubra's Charm-count based locations."""
display_name = "Minimum Charm Requirement"
range_start = 1
range_end = 40
default = 1
class MaximumCharmPrice(MinimumCharmPrice):
default = 20
class RandomCharmCosts(Range):
"""Total Cost of all Charms together. Set to -1 for vanilla costs. Vanilla sums to 90."""
display_name = "Random Charm Costs"
range_start = -1
range_end = 240
default = -1
vanilla_costs: typing.List[int] = [1, 1, 1, 2, 2, 2, 3, 2, 3, 1, 3, 1, 3, 1, 2, 2, 1, 2, 3, 2,
4, 2, 2, 2, 3, 1, 4, 2, 4, 1, 2, 3, 2, 4, 3, 5, 1, 3, 2, 2]
charm_count: int = len(vanilla_costs)
def get_costs(self, random_source) -> typing.List[int]:
if -1 == self.value:
return self.vanilla_costs
else:
charms = [0]*self.charm_count
for x in range(self.value):
index = random_source.randint(0, self.charm_count-1)
while charms[index] > 5:
index = random_source.randint(0, self.charm_count-1)
charms[index] += 1
return charms
class EggShopSlots(Range):
"""For each slot, add a location to the Egg Shop and a Geo drop to the item pool."""
display_name = "Egg Shop Item Slots"
range_end = 16
hollow_knight_options: typing.Dict[str, type(Option)] = {
**hollow_knight_randomize_options,
**hollow_knight_logic_options,
"start_location": StartLocation,
"minimum_grub_price": MinimumGrubPrice,
"maximum_grub_price": MaximumGrubPrice,
"minimum_essence_price": MinimumEssencePrice,
"maximum_essence_price": MaximumEssencePrice,
"minimum_egg_price": MinimumEggPrice,
"maximum_egg_price": MaximumEggPrice,
"minimum_charm_price": MinimumCharmPrice,
"maximum_charm_price": MaximumCharmPrice,
"random_charm_costs": RandomCharmCosts,
"egg_shop_slots": EggShopSlots,
}
hollow_knight_options: typing.Dict[str, type(Option)] = {**hollow_knight_randomize_options,
**hollow_knight_skip_options}

View File

@ -1,13 +1,25 @@
# generated by https://github.com/Berserker66/HollowKnight.RandomizerMod/blob/master/extract_data.py
# do not edit manually
from .ExtractedData import region_names, exits, connectors
def create_regions(world, player: int):
from . import create_region
from .Items import item_table
from .Locations import lookup_name_to_id
world.regions += [
create_region(world, player, 'Menu', None, ['Hollow Nest S&Q']),
create_region(world, player, 'Hollow Nest', [location for location in lookup_name_to_id] +
[item_name for item_name, item_data in item_table.items() if item_data.type == "Event"])
]
from . import create_region, HKLocation, HKItem
world.regions.append(create_region(world, player, 'Menu', None, ['Hollow Nest S&Q']))
for region in region_names:
world.regions.append(create_region(world, player, region, [],
exits.get(region, [])))
for entrance_name, exit_name in connectors.items():
if exit_name:
target_region = world.get_entrance(exit_name, player).parent_region
world.get_entrance(entrance_name, player).connect(target_region)
if not entrance_name.endswith("_R"):
# a traversable entrance puts the name of the target door "into logic".
loc = HKLocation(player, exit_name, None, target_region)
loc.place_locked_item(HKItem(exit_name,
not exit_name.startswith("White_Palace_"),
None, "Event", player))
target_region.locations.append(loc)
else:
ent = world.get_entrance(entrance_name, player)
ent.parent_region.exits.remove(ent)

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
import typing
class HKItemData(typing.NamedTuple):
advancement: bool
id: int
type: str

View File

@ -1,111 +1,262 @@
from __future__ import annotations
import logging
import typing
from collections import Counter
logger = logging.getLogger("Hollow Knight")
from .Locations import lookup_name_to_id
from .Items import item_table, lookup_type_to_names
from .Regions import create_regions
from .Rules import set_rules
from .Options import hollow_knight_options
from .Options import hollow_knight_options, hollow_knight_randomize_options
from .ExtractedData import locations, starts, multi_locations, location_to_region_lookup, \
event_names, item_effects, connectors, one_ways
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, RegionType
from ..AutoWorld import World, LogicMixin
white_palace_locations = {
"Soul_Totem-Path_of_Pain_Below_Thornskip",
"Soul_Totem-White_Palace_Final",
"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-White_Palace_Entrance",
"Soul_Totem-Path_of_Pain_Below_Lever",
"Lore_Tablet-Palace_Throne",
"Soul_Totem-Path_of_Pain_Second",
"Soul_Totem-White_Palace_Left",
"Lore_Tablet-Palace_Workshop",
"Soul_Totem-White_Palace_Hub",
"Journal_Entry-Seal_of_Binding",
"Soul_Totem-White_Palace_Right",
"King_Fragment",
# 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_13",
"White_Palace_01",
# Event-Transitions:
"White_Palace_12[bot1]", "White_Palace_12[bot1]", "White_Palace_03_hub[bot1]", "White_Palace_16[left2]",
"White_Palace_16[left2]", "White_Palace_11[door2]", "White_Palace_11[door2]", "White_Palace_18[top1]",
"White_Palace_18[top1]", "White_Palace_15[left1]", "White_Palace_15[left1]", "White_Palace_05[left2]",
"White_Palace_05[left2]", "White_Palace_14[bot1]", "White_Palace_14[bot1]", "White_Palace_13[left2]",
"White_Palace_13[left2]", "White_Palace_03_hub[left1]", "White_Palace_03_hub[left1]", "White_Palace_15[right2]",
"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]",
}
class HKWorld(World):
game: str = "Hollow Knight"
options = hollow_knight_options
item_name_to_id = {name: data.id for name, data in item_table.items() if data.type != "Event"}
location_name_to_id = lookup_name_to_id
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)}
hidden = True
ranges: typing.Dict[str, typing.Tuple[int, int]]
shops = {"Egg_Shop": "egg", "Grubfather": "grub", "Seer": "essence", "Salubra_(Requires_Charms)": "charm"}
charm_costs: typing.List[int]
data_version = 2
allow_white_palace = False
def __init__(self, world, player):
super(HKWorld, self).__init__(world, player)
self.created_multi_locations: typing.Dict[str, int] = Counter()
self.ranges = {}
def generate_early(self):
world = self.world
self.charm_costs = world.random_charm_costs[self.player].get_costs(world.random)
world.exclude_locations[self.player].value.update(white_palace_locations)
world.local_items[self.player].value.add("Mimic_Grub")
for vendor, unit in self.shops.items():
mini = getattr(world, f"minimum_{unit}_price")[self.player]
maxi = getattr(world, f"maximum_{unit}_price")[self.player]
# if minimum > maximum, set minimum to maximum
mini.value = min(mini.value, maxi.value)
self.ranges[unit] = mini.value, maxi.value
world.push_precollected(HKItem(starts[world.start_location[self.player].current_key],
True, None, "Event", self.player))
def create_regions(self):
menu_region: Region = create_region(self.world, self.player, 'Menu')
self.world.regions.append(menu_region)
def generate_basic(self):
# Link regions
self.world.get_entrance('Hollow Nest S&Q', self.player).connect(self.world.get_region('Hollow Nest', self.player))
for event_name in event_names:
loc = HKLocation(self.player, event_name, None, menu_region)
loc.place_locked_item(HKItem(event_name,
self.allow_white_palace or event_name not in white_palace_locations,
None, "Event", self.player))
menu_region.locations.append(loc)
for entry_transition, exit_transition in connectors.items():
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,
self.allow_white_palace or exit_transition not in white_palace_locations,
None, "Event", self.player))
menu_region.locations.append(loc)
# Generate item pool
pool = []
for item_name, item_data in item_table.items():
item = self.create_item(item_name)
def create_items(self):
# Generate item pool and associated locations (paired in HK)
pool: typing.List[HKItem] = []
geo_replace: typing.Set[str] = set()
if self.world.RemoveSpellUpgrades[self.player]:
geo_replace.add("Abyss_Shriek")
geo_replace.add("Shade_Soul")
geo_replace.add("Descending_Dark")
if item_data.type == "Event":
event_location = self.world.get_location(item_name, self.player)
self.world.push_item(event_location, item, collect=False)
event_location.event = True
event_location.locked = True
if item.name == "King's_Pass":
self.world.push_precollected(item)
elif item_data.type == "Cursed":
if self.world.CURSED[self.player]:
pool.append(item)
else:
# fill Focus Location with Focus and add it to start inventory as well.
event_location = self.world.get_location(item_name, self.player)
self.world.push_item(event_location, item)
event_location.event = True
event_location.locked = True
elif item_data.type == "Fake":
pass
elif item_data.type in not_shufflable_types:
location = self.world.get_location(item_name, self.player)
self.world.push_item(location, item, collect=False)
location.event = item.advancement
location.locked = True
for option_key, option in hollow_knight_randomize_options.items():
if getattr(self.world, option_key)[self.player]:
for item_name, location_name in zip(option.items, option.locations):
if item_name in geo_replace:
item_name = "Geo_Rock-Default"
if location_name in white_palace_locations:
self.create_location(location_name).place_locked_item(self.create_item(item_name))
else:
self.create_location(location_name)
pool.append(self.create_item(item_name))
else:
target = option_to_type_lookup[item.type]
shuffle_it = getattr(self.world, target)
if shuffle_it[self.player]:
pool.append(item)
else:
location = self.world.get_location(item_name, self.player)
self.world.push_item(location, item, collect=False)
location.event = item.advancement
location.locked = True
logger.debug(f"Placed {item_name} to vanilla for player {self.player}")
for item_name, location_name in zip(option.items, option.locations):
item = self.create_item(item_name)
if location_name == "Start":
self.world.push_precollected(item)
else:
self.create_location(location_name).place_locked_item(item)
for i in range(self.world.egg_shop_slots[self.player].value):
self.create_location("Egg_Shop")
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
def set_rules(self):
set_rules(self.world, self.player)
world = self.world
player = self.player
if world.logic[player] != 'nologic':
world.completion_condition[player] = lambda state: state.has('DREAMER', player, 3)
set_rules(self)
def create_regions(self):
create_regions(self.world, self.player)
def fill_slot_data(self):
def fill_slot_data(self):
slot_data = {}
options = slot_data["options"] = {}
for option_name in self.options:
option = getattr(self.world, option_name)[self.player]
slot_data[option_name] = int(option.value)
options[option_name] = int(option.value)
# 32 bit int
slot_data["seed"] = self.world.slot_seeds[self.player].randint(-2147483647, 2147483646)
for shop, unit in self.shops.items():
slot_data[f"{unit}_costs"] = {
f"{shop}_{i}":
self.world.get_location(f"{shop}_{i}", self.player).cost
for i in range(1, 1 + self.created_multi_locations[shop])
}
slot_data["notch_costs"] = self.charm_costs
return slot_data
def create_item(self, name: str) -> Item:
def create_item(self, name: str) -> HKItem:
item_data = item_table[name]
return HKItem(name, item_data.advancement, item_data.id, item_data.type, self.player)
def create_location(self, name: str) -> HKLocation:
unit = self.shops.get(name, None)
if unit:
cost = self.world.random.randint(*self.ranges[unit])
else:
cost = 0
if name in multi_locations:
self.created_multi_locations[name] += 1
name += f"_{self.created_multi_locations[name]}"
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
region = self.world.get_region("Menu", self.player)
loc = HKLocation(self.player, name, self.location_name_to_id[name], region)
if unit:
loc.unit = unit
loc.cost = cost
region.locations.append(loc)
return loc
def collect(self, state, item: HKItem) -> bool:
change = super(HKWorld, self).collect(state, item)
for effect_name, effect_value in item_effects.get(item.name, {}).items():
state.prog_items[effect_name, item.player] += effect_value
return change
def remove(self, state, item: HKItem) -> bool:
change = super(HKWorld, self).remove(state, item)
for effect_name, effect_value in item_effects.get(item.name, {}).items():
if state.prog_items[effect_name, item.player] == effect_value:
del state.prog_items[effect_name, item.player]
state.prog_items[effect_name, item.player] -= effect_value
return change
def create_region(world: MultiWorld, player: int, name: str, location_names=None, exits=None) -> Region:
ret = Region(name, RegionType.Generic, name, player)
ret.world = world
if locations:
for location in locations:
loc_id = lookup_name_to_id.get(location, 0)
if location_names:
for location in location_names:
loc_id = HKWorld.location_name_to_id.get(location, None)
location = HKLocation(player, location, loc_id, ret)
ret.locations.append(location)
if exits:
for exit in exits:
ret.exits.append(Entrance(player, exit, ret))
return ret
class HKLocation(Location):
game: str = "Hollow Knight"
cost: int = 0
unit: typing.Optional[str] = None
def __init__(self, player: int, name: str, address=None, parent=None):
super(HKLocation, self).__init__(player, name, address if address else None, parent)
def __init__(self, player: int, name: str, code=None, parent=None):
super(HKLocation, self).__init__(player, name, code if code else None, parent)
class HKItem(Item):
@ -114,51 +265,20 @@ class HKItem(Item):
def __init__(self, name, advancement, code, type, player: int = None):
super(HKItem, self).__init__(name, advancement, code if code else None, player)
self.type = type
if name == "Mimic_Grub":
self.trap = True
not_shufflable_types = {"Essence_Boss"}
class HKLogicMixin(LogicMixin):
world: MultiWorld
option_to_type_lookup = {
"Root": "RandomizeWhisperingRoots",
"Dreamer": "RandomizeDreamers",
"Geo": "RandomizeGeoChests",
"Skill": "RandomizeSkills",
"Map": "RandomizeMaps",
"Relic": "RandomizeRelics",
"Charm": "RandomizeCharms",
"Notch": "RandomizeCharmNotches",
"Key": "RandomizeKeys",
"Stag": "RandomizeStags",
"Flame": "RandomizeFlames",
"Grub": "RandomizeGrubs",
"Cocoon": "RandomizeLifebloodCocoons",
"Mask": "RandomizeMaskShards",
"Ore": "RandomizePaleOre",
"Egg": "RandomizeRancidEggs",
"Vessel": "RandomizeVesselFragments",
}
def _hk_notches(self, player: int, *notches: int) -> int:
return sum(self.world.worlds[player].charm_costs[notch] for notch in notches)
def _kh_option(self, player: int, option_name: str) -> int:
if option_name == "RandomizeCharmNotches":
return self.world.random_charm_costs[player] != -1
return getattr(self.world, option_name)[player].value
class HKLogic(LogicMixin):
# these are all wip
def _hk_has_essence(self, player: int, count: int):
return self.prog_items["Dream_Nail", player]
# return self.prog_items["Essence", player] >= count
def _hk_has_grubs(self, player: int, count: int):
found = 0
for item_name in lookup_type_to_names["Grub"]:
found += self.prog_items[item_name, player]
if found >= count:
return True
return False
def _hk_has_flames(self, player: int, count: int):
found = 0
for item_name in lookup_type_to_names["Flame"]:
found += self.prog_items[item_name, player]
if found >= count:
return True
return False
def _kh_start(self, player, start_location: str) -> bool:
return self.world.start_location[player] == start_location

View File

@ -0,0 +1 @@
astunparse>=1.6.3; python_version <= '3.8'