| 
									
										
										
										
											2021-06-25 23:32:13 +02:00
										 |  |  | import typing | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2024-07-28 16:27:39 -05:00
										 |  |  | from dataclasses import dataclass, make_dataclass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | from .ExtractedData import logic_options, starts, pool_options | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | from .Rules import cost_terms | 
					
						
							| 
									
										
										
										
											2024-06-07 12:12:10 -05:00
										 |  |  | from schema import And, Schema, Optional | 
					
						
							| 
									
										
										
										
											2022-04-12 17:13:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-28 16:27:39 -05:00
										 |  |  | from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionDict, NamedRange, DeathLink, PerGameCommonOptions | 
					
						
							| 
									
										
										
										
											2022-04-12 17:13:52 +02:00
										 |  |  | from .Charms import vanilla_costs, names as charm_names | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 00:27:43 +02:00
										 |  |  | if typing.TYPE_CHECKING: | 
					
						
							|  |  |  |     # avoid import during runtime | 
					
						
							|  |  |  |     from random import Random | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     Random = typing.Any | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:32:17 +02:00
										 |  |  | locations = {"option_" + start: i for i, start in enumerate(starts)} | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | # This way the dynamic start names are picked up by the MetaClass Choice belongs to | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  | StartLocation = type("StartLocation", (Choice,), { | 
					
						
							|  |  |  |     "__module__": __name__, | 
					
						
							|  |  |  |     "auto_display_name": False, | 
					
						
							|  |  |  |     "display_name": "Start Location", | 
					
						
							|  |  |  |     "__doc__": "Choose your start location. " | 
					
						
							|  |  |  |                "This is currently only locked to King's Pass.", | 
					
						
							|  |  |  |     **locations, | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2022-04-01 03:32:17 +02:00
										 |  |  | del (locations) | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  | option_docstrings = { | 
					
						
							|  |  |  |     "RandomizeDreamers": "Allow for Dreamers to be randomized into the item pool and opens their locations for " | 
					
						
							|  |  |  |                          "randomization.", | 
					
						
							|  |  |  |     "RandomizeSkills": "Allow for Skills, such as Mantis Claw or Shade Soul, to be randomized into the item pool. " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                        "Also opens their locations\n    for receiving randomized items.", | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     "RandomizeFocus": "Removes the ability to focus and randomizes it into the item pool.", | 
					
						
							|  |  |  |     "RandomizeSwim": "Removes the ability to swim in water and randomizes it into the item pool.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "RandomizeCharms": "Allow for Charms to be randomized into the item pool and open their locations for " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                        "randomization. Includes Charms\n    sold in shops.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "RandomizeKeys": "Allow for Keys to be randomized into the item pool. Includes those sold in shops.", | 
					
						
							|  |  |  |     "RandomizeMaskShards": "Allow for Mask Shard to be randomized into the item pool and open their locations for" | 
					
						
							|  |  |  |                            " randomization.", | 
					
						
							|  |  |  |     "RandomizeVesselFragments": "Allow for Vessel Fragments to be randomized into the item pool and open their " | 
					
						
							|  |  |  |                                 "locations for randomization.", | 
					
						
							|  |  |  |     "RandomizeCharmNotches": "Allow for Charm Notches to be randomized into the item pool. " | 
					
						
							|  |  |  |                              "Includes those sold by Salubra.", | 
					
						
							|  |  |  |     "RandomizePaleOre": "Randomize Pale Ores into the item pool and open their locations for randomization.", | 
					
						
							|  |  |  |     "RandomizeGeoChests": "Allow for Geo Chests to contain randomized items, " | 
					
						
							|  |  |  |                           "as well as their Geo reward being randomized into the item pool.", | 
					
						
							|  |  |  |     "RandomizeJunkPitChests": "Randomize the contents of junk pit chests into the item pool and open their locations " | 
					
						
							|  |  |  |                               "for randomization.", | 
					
						
							|  |  |  |     "RandomizeRancidEggs": "Randomize Rancid Eggs into the item pool and open their locations for randomization", | 
					
						
							|  |  |  |     "RandomizeRelics": "Randomize Relics (King's Idol, et al.) into the item pool and open their locations for" | 
					
						
							|  |  |  |                        " randomization.", | 
					
						
							|  |  |  |     "RandomizeWhisperingRoots": "Randomize the essence rewards from Whispering Roots into the item pool. Whispering " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                                 "Roots will now grant a randomized\n    item when completed. This can be previewed by " | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |                                 "standing on the root.", | 
					
						
							|  |  |  |     "RandomizeBossEssence": "Randomize boss essence drops, such as those for defeating Warrior Dreams, into the item " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                             "pool and open their locations\n    for randomization.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "RandomizeGrubs": "Randomize Grubs into the item pool and open their locations for randomization.", | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     "RandomizeMimics": "Randomize Mimic Grubs into the item pool and open their locations for randomization.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "RandomizeMaps": "Randomize Maps into the item pool. This causes Cornifer to give you a message allowing you to see" | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                      " and buy an item\n    that is randomized into that location as well.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "RandomizeStags": "Randomize Stag Stations unlocks into the item pool as well as placing randomized items " | 
					
						
							|  |  |  |                       "on the stag station bell/toll.", | 
					
						
							|  |  |  |     "RandomizeLifebloodCocoons": "Randomize Lifeblood Cocoon grants into the item pool and open their locations" | 
					
						
							|  |  |  |                                  " for randomization.", | 
					
						
							|  |  |  |     "RandomizeGrimmkinFlames": "Randomize Grimmkin Flames into the item pool and open their locations for " | 
					
						
							|  |  |  |                                "randomization.", | 
					
						
							|  |  |  |     "RandomizeJournalEntries": "Randomize the Hunter's Journal as well as the findable journal entries into the item " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                                "pool, and open their locations\n    for randomization. Does not include journal entries " | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |                                "gained by killing enemies.", | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     "RandomizeNail": "Removes the ability to swing the nail left, right and up, and shuffles these into the item pool.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "RandomizeGeoRocks": "Randomize Geo Rock rewards into the item pool and open their locations for randomization.", | 
					
						
							|  |  |  |     "RandomizeBossGeo": "Randomize boss Geo drops into the item pool and open those locations for randomization.", | 
					
						
							|  |  |  |     "RandomizeSoulTotems": "Randomize Soul Refill items into the item pool and open the Soul Totem locations for" | 
					
						
							|  |  |  |                            " randomization.", | 
					
						
							|  |  |  |     "RandomizeLoreTablets": "Randomize Lore items into the itempool, one per Lore Tablet, and place randomized item " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                             "grants on the tablets themselves.\n    You must still read the tablet to get the item.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "PreciseMovement": "Places skips into logic which require extremely precise player movement, possibly without " | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |                        "movement skills such as\n    dash or hook.", | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     "ProficientCombat": "Places skips into logic which require proficient combat, possibly with limited items.", | 
					
						
							|  |  |  |     "BackgroundObjectPogos": "Places skips into logic for locations which are reachable via pogoing off of " | 
					
						
							|  |  |  |                              "background objects.", | 
					
						
							|  |  |  |     "EnemyPogos": "Places skips into logic for locations which are reachable via pogos off of enemies.", | 
					
						
							|  |  |  |     "ObscureSkips": "Places skips into logic which are considered obscure enough that a beginner is not expected " | 
					
						
							|  |  |  |                     "to know them.", | 
					
						
							|  |  |  |     "ShadeSkips": "Places shade skips into logic which utilize the player's shade for pogoing or damage boosting.", | 
					
						
							|  |  |  |     "InfectionSkips": "Places skips into logic which are only possible after the crossroads become infected.", | 
					
						
							|  |  |  |     "FireballSkips": "Places skips into logic which require the use of spells to reset fall speed while in mid-air.", | 
					
						
							|  |  |  |     "SpikeTunnels": "Places skips into logic which require the navigation of narrow tunnels filled with spikes.", | 
					
						
							|  |  |  |     "AcidSkips": "Places skips into logic which require crossing a pool of acid without Isma's Tear, or water if swim " | 
					
						
							|  |  |  |                  "is disabled.", | 
					
						
							|  |  |  |     "DamageBoosts": "Places skips into logic which require you to take damage from an enemy or hazard to progress.", | 
					
						
							|  |  |  |     "DangerousSkips": "Places skips into logic which contain a high risk of taking damage.", | 
					
						
							|  |  |  |     "DarkRooms": "Places skips into logic which require navigating dark rooms without the use of the Lumafly Lantern.", | 
					
						
							|  |  |  |     "ComplexSkips": "Places skips into logic which require intense setup or are obscure even beyond advanced skip " | 
					
						
							|  |  |  |                     "standards.", | 
					
						
							|  |  |  |     "DifficultSkips": "Places skips into logic which are considered more difficult than typical.", | 
					
						
							|  |  |  |     "RemoveSpellUpgrades": "Removes the second level of all spells from the item pool." | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | default_on = { | 
					
						
							|  |  |  |     "RandomizeDreamers", | 
					
						
							|  |  |  |     "RandomizeSkills", | 
					
						
							|  |  |  |     "RandomizeCharms", | 
					
						
							|  |  |  |     "RandomizeKeys", | 
					
						
							|  |  |  |     "RandomizeMaskShards", | 
					
						
							|  |  |  |     "RandomizeVesselFragments", | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     "RandomizeCharmNotches", | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     "RandomizePaleOre", | 
					
						
							| 
									
										
										
										
											2024-05-30 10:57:54 -07:00
										 |  |  |     "RandomizeRancidEggs", | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     "RandomizeRelics", | 
					
						
							|  |  |  |     "RandomizeStags", | 
					
						
							|  |  |  |     "RandomizeLifebloodCocoons" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | shop_to_option = { | 
					
						
							|  |  |  |     "Seer": "SeerRewardSlots", | 
					
						
							|  |  |  |     "Grubfather": "GrubfatherRewardSlots", | 
					
						
							|  |  |  |     "Sly": "SlyShopSlots", | 
					
						
							|  |  |  |     "Sly_(Key)": "SlyKeyShopSlots", | 
					
						
							|  |  |  |     "Iselda": "IseldaShopSlots", | 
					
						
							|  |  |  |     "Salubra": "SalubraShopSlots", | 
					
						
							|  |  |  |     "Leg_Eater": "LegEaterShopSlots", | 
					
						
							| 
									
										
										
										
											2023-07-11 04:49:40 -05:00
										 |  |  |     "Salubra_(Requires_Charms)": "SalubraCharmShopSlots", | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     "Egg_Shop": "EggShopSlots", | 
					
						
							| 
									
										
										
										
											2021-06-25 23:32:13 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  | splitter_pattern = re.compile(r'(?<!^)(?=[A-Z])') | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | for option_name, option_data in pool_options.items(): | 
					
						
							| 
									
										
										
										
											2022-04-02 20:49:27 +02:00
										 |  |  |     extra_data = {"__module__": __name__, "items": option_data[0], "locations": option_data[1]} | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     if option_name in option_docstrings: | 
					
						
							|  |  |  |         extra_data["__doc__"] = option_docstrings[option_name] | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     if option_name in default_on: | 
					
						
							|  |  |  |         option = type(option_name, (DefaultOnToggle,), extra_data) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         option = type(option_name, (Toggle,), extra_data) | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     option.display_name = splitter_pattern.sub(" ", option_name) | 
					
						
							| 
									
										
										
										
											2022-04-02 20:49:27 +02:00
										 |  |  |     globals()[option.__name__] = option | 
					
						
							|  |  |  |     hollow_knight_randomize_options[option.__name__] = option | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  | hollow_knight_logic_options: typing.Dict[str, type(Option)] = {} | 
					
						
							|  |  |  | for option_name in logic_options.values(): | 
					
						
							|  |  |  |     if option_name in hollow_knight_randomize_options: | 
					
						
							|  |  |  |         continue | 
					
						
							| 
									
										
										
										
											2022-04-05 00:13:25 +02:00
										 |  |  |     extra_data = {"__module__": __name__} | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     # some options, such as elevator pass, appear in logic_options despite explicitly being | 
					
						
							|  |  |  |     # handled below as classes. | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     if option_name in option_docstrings: | 
					
						
							|  |  |  |         extra_data["__doc__"] = option_docstrings[option_name] | 
					
						
							|  |  |  |         option = type(option_name, (Toggle,), extra_data) | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |         option.display_name = splitter_pattern.sub(" ", option_name) | 
					
						
							|  |  |  |         globals()[option.__name__] = option | 
					
						
							|  |  |  |         hollow_knight_logic_options[option.__name__] = option | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | class RandomizeElevatorPass(Toggle): | 
					
						
							|  |  |  |     """Adds an Elevator Pass item to the item pool, which is then required to use the large elevators connecting
 | 
					
						
							|  |  |  |     City of Tears to the Forgotten Crossroads and Resting Grounds."""
 | 
					
						
							|  |  |  |     display_name = "Randomize Elevator Pass" | 
					
						
							|  |  |  |     default = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SplitMothwingCloak(Toggle): | 
					
						
							|  |  |  |     """Splits the Mothwing Cloak into left- and right-only versions of the item. Randomly adds a second left or
 | 
					
						
							|  |  |  |     right Mothwing cloak item which functions as the upgrade to Shade Cloak."""
 | 
					
						
							|  |  |  |     display_name = "Split Mothwing Cloak" | 
					
						
							|  |  |  |     default = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SplitMantisClaw(Toggle): | 
					
						
							|  |  |  |     """Splits the Mantis Claw into left- and right-only versions of the item.""" | 
					
						
							|  |  |  |     display_name = "Split Mantis Claw" | 
					
						
							|  |  |  |     default = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SplitCrystalHeart(Toggle): | 
					
						
							|  |  |  |     """Splits the Crystal Heart into left- and right-only versions of the item.""" | 
					
						
							|  |  |  |     display_name = "Split Crystal Heart" | 
					
						
							|  |  |  |     default = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | class MinimumGrubPrice(Range): | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     """The minimum grub price in the range of prices that an item should cost from Grubfather.""" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Minimum Grub Price" | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 46 | 
					
						
							|  |  |  |     default = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MaximumGrubPrice(MinimumGrubPrice): | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     """The maximum grub price in the range of prices that an item should cost from Grubfather.""" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Maximum Grub Price" | 
					
						
							|  |  |  |     default = 23 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MinimumEssencePrice(Range): | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     """The minimum essence price in the range of prices that an item should cost from Seer.""" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Minimum Essence Price" | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 2800 | 
					
						
							|  |  |  |     default = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MaximumEssencePrice(MinimumEssencePrice): | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     """The maximum essence price in the range of prices that an item should cost from Seer.""" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Maximum Essence Price" | 
					
						
							|  |  |  |     default = 1400 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MinimumEggPrice(Range): | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     """The minimum rancid egg price in the range of prices that an item should cost from Jiji.
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     Only takes effect if the EggSlotShops option is greater than 0."""
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Minimum Egg Price" | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							| 
									
										
										
										
											2024-06-05 00:01:22 -05:00
										 |  |  |     range_end = 20 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     default = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MaximumEggPrice(MinimumEggPrice): | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     """The maximum rancid egg price in the range of prices that an item should cost from Jiji.
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     Only takes effect if the EggSlotShops option is greater than 0."""
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Maximum Egg Price" | 
					
						
							|  |  |  |     default = 10 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MinimumCharmPrice(Range): | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     """The minimum charm price in the range of prices that an item should cost for Salubra's shop item which also
 | 
					
						
							|  |  |  |     carry a charm cost."""
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     display_name = "Minimum Charm Requirement" | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 40 | 
					
						
							|  |  |  |     default = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MaximumCharmPrice(MinimumCharmPrice): | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     """The maximum charm price in the range of prices that an item should cost for Salubra's shop item which also
 | 
					
						
							|  |  |  |     carry a charm cost."""
 | 
					
						
							| 
									
										
										
										
											2022-07-20 21:45:01 -07:00
										 |  |  |     display_name = "Maximum Charm Requirement" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     default = 20 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | class MinimumGeoPrice(Range): | 
					
						
							|  |  |  |     """The minimum geo price for items in geo shops.""" | 
					
						
							|  |  |  |     display_name = "Minimum Geo Price" | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 200 | 
					
						
							|  |  |  |     default = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MaximumGeoPrice(Range): | 
					
						
							|  |  |  |     """The maximum geo price for items in geo shops.""" | 
					
						
							| 
									
										
										
										
											2022-07-20 21:45:01 -07:00
										 |  |  |     display_name = "Maximum Geo Price" | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 2000 | 
					
						
							|  |  |  |     default = 400 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 00:10:52 +01:00
										 |  |  | class RandomCharmCosts(NamedRange): | 
					
						
							| 
									
										
										
										
											2022-06-09 00:27:43 +02:00
										 |  |  |     """Total Notch Cost of all Charms together. Vanilla sums to 90.
 | 
					
						
							|  |  |  |     This value is distributed among all charms in a random fashion. | 
					
						
							|  |  |  |     Special Cases: | 
					
						
							| 
									
										
										
										
											2022-06-12 23:33:14 +02:00
										 |  |  |     Set to -1 or vanilla for vanilla costs. | 
					
						
							|  |  |  |     Set to -2 or shuffle to shuffle around the vanilla costs to different charms."""
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:10:13 -04:00
										 |  |  |     display_name = "Randomize Charm Notch Costs" | 
					
						
							| 
									
										
										
										
											2023-11-25 00:10:52 +01:00
										 |  |  |     range_start = 0 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     range_end = 240 | 
					
						
							|  |  |  |     default = -1 | 
					
						
							| 
									
										
										
										
											2022-04-08 19:22:50 +02:00
										 |  |  |     vanilla_costs: typing.List[int] = vanilla_costs | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     charm_count: int = len(vanilla_costs) | 
					
						
							| 
									
										
										
										
											2022-06-12 23:33:14 +02:00
										 |  |  |     special_range_names = { | 
					
						
							|  |  |  |         "vanilla": -1, | 
					
						
							|  |  |  |         "shuffle": -2 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 00:27:43 +02:00
										 |  |  |     def get_costs(self, random_source: Random) -> typing.List[int]: | 
					
						
							|  |  |  |         charms: typing.List[int] | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |         if -1 == self.value: | 
					
						
							| 
									
										
										
										
											2022-06-09 00:27:43 +02:00
										 |  |  |             return self.vanilla_costs.copy() | 
					
						
							|  |  |  |         elif -2 == self.value: | 
					
						
							|  |  |  |             charms = self.vanilla_costs.copy() | 
					
						
							|  |  |  |             random_source.shuffle(charms) | 
					
						
							|  |  |  |             return charms | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |             charms = [0] * self.charm_count | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |             for x in range(self.value): | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |                 index = random_source.randint(0, self.charm_count - 1) | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |                 while charms[index] > 5: | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |                     index = random_source.randint(0, self.charm_count - 1) | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |                 charms[index] += 1 | 
					
						
							|  |  |  |             return charms | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 17:13:52 +02:00
										 |  |  | class PlandoCharmCosts(OptionDict): | 
					
						
							|  |  |  |     """Allows setting a Charm's Notch costs directly, mapping {name: cost}.
 | 
					
						
							|  |  |  |     This is set after any random Charm Notch costs, if applicable."""
 | 
					
						
							|  |  |  |     display_name = "Charm Notch Cost Plando" | 
					
						
							|  |  |  |     valid_keys = frozenset(charm_names) | 
					
						
							| 
									
										
										
										
											2024-06-07 12:12:10 -05:00
										 |  |  |     schema = Schema({ | 
					
						
							|  |  |  |         Optional(name): And(int, lambda n: 6 >= n >= 0) for name in charm_names | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2022-04-12 17:13:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_costs(self, charm_costs: typing.List[int]) -> typing.List[int]: | 
					
						
							|  |  |  |         for name, cost in self.value.items(): | 
					
						
							|  |  |  |             charm_costs[charm_names.index(name)] = cost | 
					
						
							|  |  |  |         return charm_costs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | class SlyShopSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to the Sly Shop and a filler item to the item pool.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Sly Shop Slots" | 
					
						
							|  |  |  |     default = 8 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SlyKeyShopSlots(Range): | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |     """For each extra slot, add a location to the Sly Shop (requiring Shopkeeper's Key) and a filler item to the item
 | 
					
						
							|  |  |  |     pool."""
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Sly Key Shop Slots" | 
					
						
							|  |  |  |     default = 6 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class IseldaShopSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to the Iselda Shop and a filler item to the item pool.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Iselda Shop Slots" | 
					
						
							|  |  |  |     default = 2 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SalubraShopSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to the Salubra Shop, and a filler item to the item pool.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Salubra Shop Slots" | 
					
						
							|  |  |  |     default = 5 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SalubraCharmShopSlots(Range): | 
					
						
							| 
									
										
										
										
											2022-11-06 08:28:16 -06:00
										 |  |  |     """For each extra slot, add a location to the Salubra Shop (requiring Charms), and a filler item to the item
 | 
					
						
							|  |  |  |     pool."""
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Salubra Charm Shop Slots" | 
					
						
							|  |  |  |     default = 5 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LegEaterShopSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to the Leg Eater Shop and a filler item to the item pool.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Leg Eater Shop Slots" | 
					
						
							|  |  |  |     default = 3 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GrubfatherRewardSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to the Grubfather and a filler item to the item pool.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Grubfather Reward Slots" | 
					
						
							|  |  |  |     default = 7 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SeerRewardSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to the Seer and a filler item to the item pool.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Seer Reward Reward Slots" | 
					
						
							|  |  |  |     default = 8 | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | class EggShopSlots(Range): | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     """For each slot, add a location to the Egg Shop and a filler item to the item pool.""" | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Egg Shop Item Slots" | 
					
						
							|  |  |  |     range_end = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | class ExtraShopSlots(Range): | 
					
						
							|  |  |  |     """For each extra slot, add a location to a randomly chosen shop a filler item to the item pool.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The Egg Shop will be excluded from this list unless it has at least one item. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Shops are capped at 16 items each. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     display_name = "Additional Shop Slots" | 
					
						
							|  |  |  |     default = 0 | 
					
						
							|  |  |  |     range_end = 9 * 16  # Number of shops x max slots per shop. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Hollow Knight updates (goals, WP/POP, etc.) (#438)
* Hollow Knight updates:
- Add configurable goals (Any, THK, Siblings, Radiance)
  - Change base logic to require Opened_Black_Egg_Temple instead of
    requiring 3 dreamers.  This is future-proof for transition rando,
    where Black Egg might not have been located yet.
  - Add combat logic for THK and Radiance on par with Rando4's boss logic,
    so itemless HK shouldn't be required.
- Existing completion logic now uses Black_Egg_te
- Add White Palace options
  (Exclude, King Fragment Only, No Path of Pain, Include)
  - Excluded WP may still be required for King Fragment if Charms are
    not randomized
  - Simply don't place WP locations that are excluded
  - Distinguish between POP locations (required for POP), WP checks (
    actual item locations), WP transitions (relevant for future transition
    rando), and WP events (logically required to reach King Fragment)
  - Many transitions were listed twice.  Remove duplicates.
  - Sort transitions by scene
- For randomizable locations that have no logical significance when not
    randomized, simply skip adding them to the pool entirely for
    theoretically faster generation.
* Hollow Knight updates
  - Support random starting geo up to 1000 geo.
  - Always include locations rather than dropping unrandomized "logicless"
    ones, as it is required to best support same-slot coop.
											
										 
											2022-06-12 23:23:03 -07:00
										 |  |  | class Goal(Choice): | 
					
						
							|  |  |  |     """The goal required of you in order to complete your run in Archipelago.""" | 
					
						
							|  |  |  |     display_name = "Goal" | 
					
						
							|  |  |  |     option_any = 0 | 
					
						
							|  |  |  |     option_hollowknight = 1 | 
					
						
							|  |  |  |     option_siblings = 2 | 
					
						
							|  |  |  |     option_radiance = 3 | 
					
						
							| 
									
										
										
										
											2024-04-09 14:12:50 -05:00
										 |  |  |     option_godhome = 4 | 
					
						
							|  |  |  |     option_godhome_flower = 5 | 
					
						
							| 
									
										
										
										
											2024-08-08 13:33:13 -05:00
										 |  |  |     option_grub_hunt = 6 | 
					
						
							| 
									
										
											  
											
												Hollow Knight updates (goals, WP/POP, etc.) (#438)
* Hollow Knight updates:
- Add configurable goals (Any, THK, Siblings, Radiance)
  - Change base logic to require Opened_Black_Egg_Temple instead of
    requiring 3 dreamers.  This is future-proof for transition rando,
    where Black Egg might not have been located yet.
  - Add combat logic for THK and Radiance on par with Rando4's boss logic,
    so itemless HK shouldn't be required.
- Existing completion logic now uses Black_Egg_te
- Add White Palace options
  (Exclude, King Fragment Only, No Path of Pain, Include)
  - Excluded WP may still be required for King Fragment if Charms are
    not randomized
  - Simply don't place WP locations that are excluded
  - Distinguish between POP locations (required for POP), WP checks (
    actual item locations), WP transitions (relevant for future transition
    rando), and WP events (logically required to reach King Fragment)
  - Many transitions were listed twice.  Remove duplicates.
  - Sort transitions by scene
- For randomizable locations that have no logical significance when not
    randomized, simply skip adding them to the pool entirely for
    theoretically faster generation.
* Hollow Knight updates
  - Support random starting geo up to 1000 geo.
  - Always include locations rather than dropping unrandomized "logicless"
    ones, as it is required to best support same-slot coop.
											
										 
											2022-06-12 23:23:03 -07:00
										 |  |  |     default = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 13:33:13 -05:00
										 |  |  | class GrubHuntGoal(NamedRange): | 
					
						
							|  |  |  |     """The amount of grubs required to finish Grub Hunt.
 | 
					
						
							|  |  |  |     On 'All' any grubs from item links replacements etc. will be counted"""
 | 
					
						
							|  |  |  |     display_name = "Grub Hunt Goal" | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 46 | 
					
						
							|  |  |  |     special_range_names = {"all": -1} | 
					
						
							|  |  |  |     default = 46 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Hollow Knight updates (goals, WP/POP, etc.) (#438)
* Hollow Knight updates:
- Add configurable goals (Any, THK, Siblings, Radiance)
  - Change base logic to require Opened_Black_Egg_Temple instead of
    requiring 3 dreamers.  This is future-proof for transition rando,
    where Black Egg might not have been located yet.
  - Add combat logic for THK and Radiance on par with Rando4's boss logic,
    so itemless HK shouldn't be required.
- Existing completion logic now uses Black_Egg_te
- Add White Palace options
  (Exclude, King Fragment Only, No Path of Pain, Include)
  - Excluded WP may still be required for King Fragment if Charms are
    not randomized
  - Simply don't place WP locations that are excluded
  - Distinguish between POP locations (required for POP), WP checks (
    actual item locations), WP transitions (relevant for future transition
    rando), and WP events (logically required to reach King Fragment)
  - Many transitions were listed twice.  Remove duplicates.
  - Sort transitions by scene
- For randomizable locations that have no logical significance when not
    randomized, simply skip adding them to the pool entirely for
    theoretically faster generation.
* Hollow Knight updates
  - Support random starting geo up to 1000 geo.
  - Always include locations rather than dropping unrandomized "logicless"
    ones, as it is required to best support same-slot coop.
											
										 
											2022-06-12 23:23:03 -07:00
										 |  |  | class WhitePalace(Choice): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Whether or not to include White Palace or not.  Note: Even if excluded, the King Fragment check may still be | 
					
						
							|  |  |  |     required if charms are vanilla. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     display_name = "White Palace" | 
					
						
							|  |  |  |     option_exclude = 0  # No White Palace at all | 
					
						
							|  |  |  |     option_kingfragment = 1  # Include King Fragment check only | 
					
						
							|  |  |  |     option_nopathofpain = 2  # Exclude Path of Pain locations. | 
					
						
							|  |  |  |     option_include = 3  # Include all White Palace locations, including Path of Pain. | 
					
						
							|  |  |  |     default = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  | class ExtraPlatforms(DefaultOnToggle): | 
					
						
							|  |  |  |     """Places additional platforms to make traveling throughout Hallownest more convenient.""" | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = "Extra Platforms" | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-13 06:45:43 -05:00
										 |  |  | class AddUnshuffledLocations(Toggle): | 
					
						
							|  |  |  |     """Adds non-randomized locations to the location pool, which allows syncing
 | 
					
						
							|  |  |  |     of location state with co-op or automatic collection via collect. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Note: This will increase the number of location checks required to purchase | 
					
						
							|  |  |  |     hints to the total maximum. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = "Add Unshuffled Locations" | 
					
						
							| 
									
										
										
										
											2024-03-13 06:45:43 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  | class DeathLinkShade(Choice): | 
					
						
							|  |  |  |     """Sets whether to create a shade when you are killed by a DeathLink and how to handle your existing shade, if any.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     vanilla: DeathLink deaths function like any other death and overrides your existing shade (including geo), if any. | 
					
						
							|  |  |  |     shadeless: DeathLink deaths do not spawn shades. Your existing shade (including geo), if any, is untouched. | 
					
						
							|  |  |  |     shade: DeathLink deaths spawn a shade if you do not have an existing shade. Otherwise, it acts like shadeless. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     * This option has no effect if DeathLink is disabled. | 
					
						
							|  |  |  |     ** Self-death shade behavior is not changed; if a self-death normally creates a shade in vanilla, it will override | 
					
						
							|  |  |  |         your existing shade, if any. | 
					
						
							| 
									
										
										
										
											2022-06-27 18:05:29 -07:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  |     option_vanilla = 0 | 
					
						
							| 
									
										
										
										
											2022-06-27 18:05:29 -07:00
										 |  |  |     option_shadeless = 1 | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  |     option_shade = 2 | 
					
						
							|  |  |  |     default = 2 | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = "Deathlink Shade Handling" | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DeathLinkBreaksFragileCharms(Toggle): | 
					
						
							|  |  |  |     """Sets if fragile charms break when you are killed by a DeathLink.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     * This option has no effect if DeathLink is disabled. | 
					
						
							|  |  |  |     ** Self-death fragile charm behavior is not changed; if a self-death normally breaks fragile charms in vanilla, it | 
					
						
							|  |  |  |         will continue to do so. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = "Deathlink Breaks Fragile Charms" | 
					
						
							| 
									
										
										
										
											2022-06-27 18:05:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Hollow Knight updates (goals, WP/POP, etc.) (#438)
* Hollow Knight updates:
- Add configurable goals (Any, THK, Siblings, Radiance)
  - Change base logic to require Opened_Black_Egg_Temple instead of
    requiring 3 dreamers.  This is future-proof for transition rando,
    where Black Egg might not have been located yet.
  - Add combat logic for THK and Radiance on par with Rando4's boss logic,
    so itemless HK shouldn't be required.
- Existing completion logic now uses Black_Egg_te
- Add White Palace options
  (Exclude, King Fragment Only, No Path of Pain, Include)
  - Excluded WP may still be required for King Fragment if Charms are
    not randomized
  - Simply don't place WP locations that are excluded
  - Distinguish between POP locations (required for POP), WP checks (
    actual item locations), WP transitions (relevant for future transition
    rando), and WP events (logically required to reach King Fragment)
  - Many transitions were listed twice.  Remove duplicates.
  - Sort transitions by scene
- For randomizable locations that have no logical significance when not
    randomized, simply skip adding them to the pool entirely for
    theoretically faster generation.
* Hollow Knight updates
  - Support random starting geo up to 1000 geo.
  - Always include locations rather than dropping unrandomized "logicless"
    ones, as it is required to best support same-slot coop.
											
										 
											2022-06-12 23:23:03 -07:00
										 |  |  | class StartingGeo(Range): | 
					
						
							|  |  |  |     """The amount of starting geo you have.""" | 
					
						
							|  |  |  |     display_name = "Starting Geo" | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 1000 | 
					
						
							|  |  |  |     default = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | class CostSanity(Choice): | 
					
						
							|  |  |  |     """If enabled, most locations with costs (like stag stations) will have randomly determined costs.
 | 
					
						
							|  |  |  |     If set to shopsonly, CostSanity will only apply to shops (including Grubfather, Seer and Egg Shop). | 
					
						
							|  |  |  |     If set to notshops, CostSanity will only apply to non-shops (e.g. Stag stations and Cornifer locations) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     These costs can be in Geo (except Grubfather, Seer and Eggshop), Grubs, Charms, Essence and/or Rancid Eggs | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     option_off = 0 | 
					
						
							|  |  |  |     alias_no = 0 | 
					
						
							|  |  |  |     option_on = 1 | 
					
						
							|  |  |  |     alias_yes = 1 | 
					
						
							|  |  |  |     option_shopsonly = 2 | 
					
						
							|  |  |  |     option_notshops = 3 | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = "Costsanity" | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CostSanityHybridChance(Range): | 
					
						
							|  |  |  |     """The chance that a CostSanity cost will include two components instead of one, e.g. Grubs + Essence""" | 
					
						
							|  |  |  |     range_end = 100 | 
					
						
							|  |  |  |     default = 10 | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = "Costsanity Hybrid Chance" | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | cost_sanity_weights: typing.Dict[str, type(Option)] = {} | 
					
						
							|  |  |  | for term, cost in cost_terms.items(): | 
					
						
							|  |  |  |     option_name = f"CostSanity{cost.option}Weight" | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     display_name = f"Costsanity {cost.option} Weight" | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     extra_data = { | 
					
						
							|  |  |  |         "__module__": __name__, "range_end": 1000, | 
					
						
							|  |  |  |         "__doc__": ( | 
					
						
							|  |  |  |             f"The likelihood of Costsanity choosing a {cost.option} cost." | 
					
						
							|  |  |  |             " Chosen as a sum of all weights from other types." | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         "default": cost.weight | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if cost == 'GEO': | 
					
						
							|  |  |  |         extra_data["__doc__"] += " Geo costs will never be chosen for Grubfather, Seer, or Egg Shop." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     option = type(option_name, (Range,), extra_data) | 
					
						
							| 
									
										
										
										
											2024-03-28 08:49:19 -07:00
										 |  |  |     option.display_name = display_name | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     globals()[option.__name__] = option | 
					
						
							|  |  |  |     cost_sanity_weights[option.__name__] = option | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  | hollow_knight_options: typing.Dict[str, type(Option)] = { | 
					
						
							|  |  |  |     **hollow_knight_randomize_options, | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     RandomizeElevatorPass.__name__: RandomizeElevatorPass, | 
					
						
							| 
									
										
										
										
											2022-04-01 03:23:52 +02:00
										 |  |  |     **hollow_knight_logic_options, | 
					
						
							| 
									
										
										
										
											2022-06-27 18:05:29 -07:00
										 |  |  |     **{ | 
					
						
							|  |  |  |         option.__name__: option | 
					
						
							|  |  |  |         for option in ( | 
					
						
							| 
									
										
										
										
											2024-08-08 13:33:13 -05:00
										 |  |  |             StartLocation, Goal, GrubHuntGoal, WhitePalace, ExtraPlatforms, AddUnshuffledLocations, StartingGeo, | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  |             DeathLink, DeathLinkShade, DeathLinkBreaksFragileCharms, | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |             MinimumGeoPrice, MaximumGeoPrice, | 
					
						
							| 
									
										
										
										
											2022-06-27 18:05:29 -07:00
										 |  |  |             MinimumGrubPrice, MaximumGrubPrice, | 
					
						
							|  |  |  |             MinimumEssencePrice, MaximumEssencePrice, | 
					
						
							|  |  |  |             MinimumCharmPrice, MaximumCharmPrice, | 
					
						
							|  |  |  |             RandomCharmCosts, PlandoCharmCosts, | 
					
						
							|  |  |  |             MinimumEggPrice, MaximumEggPrice, EggShopSlots, | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |             SlyShopSlots, SlyKeyShopSlots, IseldaShopSlots, | 
					
						
							|  |  |  |             SalubraShopSlots, SalubraCharmShopSlots, | 
					
						
							|  |  |  |             LegEaterShopSlots, GrubfatherRewardSlots, | 
					
						
							|  |  |  |             SeerRewardSlots, ExtraShopSlots, | 
					
						
							|  |  |  |             SplitCrystalHeart, SplitMothwingCloak, SplitMantisClaw, | 
					
						
							| 
									
										
										
										
											2023-12-17 10:11:40 -06:00
										 |  |  |             CostSanity, CostSanityHybridChance | 
					
						
							| 
									
										
										
										
											2022-06-27 18:05:29 -07:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-07-03 08:10:10 -07:00
										 |  |  |     }, | 
					
						
							|  |  |  |     **cost_sanity_weights | 
					
						
							| 
									
										
										
										
											2021-06-25 23:32:13 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-07-28 16:27:39 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | HKOptions = make_dataclass("HKOptions", [(name, option) for name, option in hollow_knight_options.items()], bases=(PerGameCommonOptions,)) |