Bug Squashing
This commit is contained in:
		
							parent
							
								
									70e3c47120
								
							
						
					
					
						commit
						199b778d2b
					
				|  | @ -0,0 +1 @@ | |||
| /Resources | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -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) | ||||
|  | @ -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) | ||||
|  | @ -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()} | ||||
|  | @ -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} | ||||
|  | @ -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) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										2088
									
								
								worlds/hk/Rules.py
								
								
								
								
							
							
						
						
									
										2088
									
								
								worlds/hk/Rules.py
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,7 +0,0 @@ | |||
| import typing | ||||
| 
 | ||||
| 
 | ||||
| class HKItemData(typing.NamedTuple): | ||||
|     advancement: bool | ||||
|     id: int | ||||
|     type: str | ||||
|  | @ -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) | ||||
| 
 | ||||
|     def create_regions(self): | ||||
|         create_regions(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 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 | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| astunparse>=1.6.3; python_version <= '3.8' | ||||
		Loading…
	
		Reference in New Issue