2021-06-25 21:32:13 +00:00
|
|
|
import typing
|
2022-04-01 01:23:52 +00:00
|
|
|
from .ExtractedData import logic_options, starts, pool_options
|
2022-07-03 15:10:10 +00:00
|
|
|
from .Rules import cost_terms
|
2022-04-12 15:13:52 +00:00
|
|
|
|
2022-06-12 21:33:14 +00:00
|
|
|
from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionDict, SpecialRange
|
2022-04-12 15:13:52 +00:00
|
|
|
from .Charms import vanilla_costs, names as charm_names
|
2022-04-01 01:23:52 +00:00
|
|
|
|
2022-06-08 22:27:43 +00:00
|
|
|
if typing.TYPE_CHECKING:
|
|
|
|
# avoid import during runtime
|
|
|
|
from random import Random
|
|
|
|
else:
|
|
|
|
Random = typing.Any
|
|
|
|
|
2022-04-01 01:42:56 +00:00
|
|
|
|
2022-04-01 01:32:17 +00:00
|
|
|
locations = {"option_" + start: i for i, start in enumerate(starts)}
|
2022-04-01 01:23:52 +00:00
|
|
|
# This way the dynamic start names are picked up by the MetaClass Choice belongs to
|
2022-04-03 22:10:13 +00:00
|
|
|
StartLocation = type("StartLocation", (Choice,), {"__module__": __name__, "auto_display_name": False, **locations,
|
|
|
|
"__doc__": "Choose your start location. "
|
|
|
|
"This is currently only locked to King's Pass."})
|
2022-04-01 01:32:17 +00:00
|
|
|
del (locations)
|
2022-04-01 01:23:52 +00:00
|
|
|
|
2022-04-03 22:10:13 +00: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 14:28:16 +00:00
|
|
|
"Also opens their locations\n for receiving randomized items.",
|
2022-07-03 15:10:10 +00: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 22:10:13 +00:00
|
|
|
"RandomizeCharms": "Allow for Charms to be randomized into the item pool and open their locations for "
|
2022-11-06 14:28:16 +00:00
|
|
|
"randomization. Includes Charms\n sold in shops.",
|
2022-04-03 22:10:13 +00: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 14:28:16 +00:00
|
|
|
"Roots will now grant a randomized\n item when completed. This can be previewed by "
|
2022-04-03 22:10:13 +00:00
|
|
|
"standing on the root.",
|
|
|
|
"RandomizeBossEssence": "Randomize boss essence drops, such as those for defeating Warrior Dreams, into the item "
|
2022-11-06 14:28:16 +00:00
|
|
|
"pool and open their locations\n for randomization.",
|
2022-04-03 22:10:13 +00:00
|
|
|
"RandomizeGrubs": "Randomize Grubs into the item pool and open their locations for randomization.",
|
2022-07-03 15:10:10 +00:00
|
|
|
"RandomizeMimics": "Randomize Mimic Grubs into the item pool and open their locations for randomization."
|
2022-11-06 14:28:16 +00:00
|
|
|
"Mimic Grubs are always placed\n in your own game.",
|
2022-04-03 22:10:13 +00:00
|
|
|
"RandomizeMaps": "Randomize Maps into the item pool. This causes Cornifer to give you a message allowing you to see"
|
2022-11-06 14:28:16 +00:00
|
|
|
" and buy an item\n that is randomized into that location as well.",
|
2022-04-03 22:10:13 +00: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 14:28:16 +00:00
|
|
|
"pool, and open their locations\n for randomization. Does not include journal entries "
|
2022-04-03 22:10:13 +00:00
|
|
|
"gained by killing enemies.",
|
2022-07-03 15:10:10 +00:00
|
|
|
"RandomizeNail": "Removes the ability to swing the nail left, right and up, and shuffles these into the item pool.",
|
2022-04-03 22:10:13 +00: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 14:28:16 +00:00
|
|
|
"grants on the tablets themselves.\n You must still read the tablet to get the item.",
|
2022-04-03 22:10:13 +00:00
|
|
|
"PreciseMovement": "Places skips into logic which require extremely precise player movement, possibly without "
|
2022-11-06 14:28:16 +00:00
|
|
|
"movement skills such as\n dash or hook.",
|
2022-04-03 22:10:13 +00: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 01:23:52 +00:00
|
|
|
default_on = {
|
|
|
|
"RandomizeDreamers",
|
|
|
|
"RandomizeSkills",
|
|
|
|
"RandomizeCharms",
|
|
|
|
"RandomizeKeys",
|
|
|
|
"RandomizeMaskShards",
|
|
|
|
"RandomizeVesselFragments",
|
|
|
|
"RandomizePaleOre",
|
|
|
|
"RandomizeRelics"
|
|
|
|
}
|
|
|
|
|
2022-07-03 15:10:10 +00:00
|
|
|
shop_to_option = {
|
|
|
|
"Seer": "SeerRewardSlots",
|
|
|
|
"Grubfather": "GrubfatherRewardSlots",
|
|
|
|
"Sly": "SlyShopSlots",
|
|
|
|
"Sly_(Key)": "SlyKeyShopSlots",
|
|
|
|
"Iselda": "IseldaShopSlots",
|
|
|
|
"Salubra": "SalubraShopSlots",
|
|
|
|
"Leg_Eater": "LegEaterShopSlots",
|
2023-07-11 09:49:40 +00:00
|
|
|
"Salubra_(Requires_Charms)": "SalubraCharmShopSlots",
|
2022-07-03 15:10:10 +00:00
|
|
|
"Egg_Shop": "EggShopSlots",
|
2021-06-25 21:32:13 +00:00
|
|
|
}
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {}
|
|
|
|
|
|
|
|
for option_name, option_data in pool_options.items():
|
2022-04-02 18:49:27 +00:00
|
|
|
extra_data = {"__module__": __name__, "items": option_data[0], "locations": option_data[1]}
|
2022-04-03 22:10:13 +00:00
|
|
|
if option_name in option_docstrings:
|
|
|
|
extra_data["__doc__"] = option_docstrings[option_name]
|
2022-04-01 01:23:52 +00:00
|
|
|
if option_name in default_on:
|
|
|
|
option = type(option_name, (DefaultOnToggle,), extra_data)
|
|
|
|
else:
|
|
|
|
option = type(option_name, (Toggle,), extra_data)
|
2022-04-02 18:49:27 +00:00
|
|
|
globals()[option.__name__] = option
|
|
|
|
hollow_knight_randomize_options[option.__name__] = option
|
2022-04-01 01:23:52 +00:00
|
|
|
|
2022-04-03 22:10:13 +00: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-04 22:13:25 +00:00
|
|
|
extra_data = {"__module__": __name__}
|
2022-04-03 22:10:13 +00:00
|
|
|
if option_name in option_docstrings:
|
|
|
|
extra_data["__doc__"] = option_docstrings[option_name]
|
|
|
|
option = type(option_name, (Toggle,), extra_data)
|
2022-04-04 22:13:25 +00:00
|
|
|
globals()[option.__name__] = option
|
|
|
|
hollow_knight_logic_options[option.__name__] = option
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
|
2022-07-03 15:10:10 +00: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 01:23:52 +00:00
|
|
|
class MinimumGrubPrice(Range):
|
2022-04-03 22:10:13 +00:00
|
|
|
"""The minimum grub price in the range of prices that an item should cost from Grubfather."""
|
2022-04-01 01:23:52 +00:00
|
|
|
display_name = "Minimum Grub Price"
|
|
|
|
range_start = 1
|
|
|
|
range_end = 46
|
|
|
|
default = 1
|
|
|
|
|
|
|
|
|
|
|
|
class MaximumGrubPrice(MinimumGrubPrice):
|
2022-04-03 22:10:13 +00:00
|
|
|
"""The maximum grub price in the range of prices that an item should cost from Grubfather."""
|
2022-04-01 01:23:52 +00:00
|
|
|
display_name = "Maximum Grub Price"
|
|
|
|
default = 23
|
|
|
|
|
|
|
|
|
|
|
|
class MinimumEssencePrice(Range):
|
2022-04-03 22:10:13 +00:00
|
|
|
"""The minimum essence price in the range of prices that an item should cost from Seer."""
|
2022-04-01 01:23:52 +00:00
|
|
|
display_name = "Minimum Essence Price"
|
|
|
|
range_start = 1
|
|
|
|
range_end = 2800
|
|
|
|
default = 1
|
|
|
|
|
|
|
|
|
|
|
|
class MaximumEssencePrice(MinimumEssencePrice):
|
2022-04-03 22:10:13 +00:00
|
|
|
"""The maximum essence price in the range of prices that an item should cost from Seer."""
|
2022-04-01 01:23:52 +00:00
|
|
|
display_name = "Maximum Essence Price"
|
|
|
|
default = 1400
|
|
|
|
|
|
|
|
|
|
|
|
class MinimumEggPrice(Range):
|
2022-07-03 15:10:10 +00:00
|
|
|
"""The minimum rancid egg price in the range of prices that an item should cost from Jiji.
|
2022-04-03 22:10:13 +00:00
|
|
|
Only takes effect if the EggSlotShops option is greater than 0."""
|
2022-04-01 01:23:52 +00:00
|
|
|
display_name = "Minimum Egg Price"
|
|
|
|
range_start = 1
|
|
|
|
range_end = 21
|
|
|
|
default = 1
|
|
|
|
|
|
|
|
|
|
|
|
class MaximumEggPrice(MinimumEggPrice):
|
2022-07-03 15:10:10 +00:00
|
|
|
"""The maximum rancid egg price in the range of prices that an item should cost from Jiji.
|
2022-04-03 22:10:13 +00:00
|
|
|
Only takes effect if the EggSlotShops option is greater than 0."""
|
2022-04-01 01:23:52 +00:00
|
|
|
display_name = "Maximum Egg Price"
|
|
|
|
default = 10
|
|
|
|
|
|
|
|
|
|
|
|
class MinimumCharmPrice(Range):
|
2022-04-03 22:10:13 +00: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 01:23:52 +00:00
|
|
|
display_name = "Minimum Charm Requirement"
|
|
|
|
range_start = 1
|
|
|
|
range_end = 40
|
|
|
|
default = 1
|
|
|
|
|
|
|
|
|
|
|
|
class MaximumCharmPrice(MinimumCharmPrice):
|
2022-04-03 22:10:13 +00: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-21 04:45:01 +00:00
|
|
|
display_name = "Maximum Charm Requirement"
|
2022-04-01 01:23:52 +00:00
|
|
|
default = 20
|
|
|
|
|
|
|
|
|
2022-07-03 15:10:10 +00: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-21 04:45:01 +00:00
|
|
|
display_name = "Maximum Geo Price"
|
2022-07-03 15:10:10 +00:00
|
|
|
range_start = 1
|
|
|
|
range_end = 2000
|
|
|
|
default = 400
|
|
|
|
|
|
|
|
|
2022-06-12 21:33:14 +00:00
|
|
|
class RandomCharmCosts(SpecialRange):
|
2022-06-08 22:27:43 +00: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 21:33:14 +00: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 01:23:52 +00:00
|
|
|
|
2022-04-03 22:10:13 +00:00
|
|
|
display_name = "Randomize Charm Notch Costs"
|
2022-06-08 22:27:43 +00:00
|
|
|
range_start = -2
|
2022-04-01 01:23:52 +00:00
|
|
|
range_end = 240
|
|
|
|
default = -1
|
2022-04-08 17:22:50 +00:00
|
|
|
vanilla_costs: typing.List[int] = vanilla_costs
|
2022-04-01 01:23:52 +00:00
|
|
|
charm_count: int = len(vanilla_costs)
|
2022-06-12 21:33:14 +00:00
|
|
|
special_range_names = {
|
|
|
|
"vanilla": -1,
|
|
|
|
"shuffle": -2
|
|
|
|
}
|
2022-04-01 01:23:52 +00:00
|
|
|
|
2022-06-08 22:27:43 +00:00
|
|
|
def get_costs(self, random_source: Random) -> typing.List[int]:
|
|
|
|
charms: typing.List[int]
|
2022-04-01 01:23:52 +00:00
|
|
|
if -1 == self.value:
|
2022-06-08 22:27:43 +00:00
|
|
|
return self.vanilla_costs.copy()
|
|
|
|
elif -2 == self.value:
|
|
|
|
charms = self.vanilla_costs.copy()
|
|
|
|
random_source.shuffle(charms)
|
|
|
|
return charms
|
2022-04-01 01:23:52 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2022-04-12 15:13:52 +00: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)
|
|
|
|
|
|
|
|
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 15:10:10 +00: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 14:28:16 +00: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 15:10:10 +00: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 14:28:16 +00: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 15:10:10 +00: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 01:23:52 +00:00
|
|
|
class EggShopSlots(Range):
|
2022-07-03 15:10:10 +00:00
|
|
|
"""For each slot, add a location to the Egg Shop and a filler item to the item pool."""
|
2022-04-01 01:23:52 +00:00
|
|
|
|
|
|
|
display_name = "Egg Shop Item Slots"
|
|
|
|
range_end = 16
|
|
|
|
|
|
|
|
|
2022-07-03 15:10:10 +00: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-13 06:23:03 +00: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
|
|
|
|
# Client support exists for this, but logic is a nightmare
|
|
|
|
# option_godhome = 4
|
|
|
|
default = 0
|
|
|
|
|
|
|
|
|
|
|
|
class WhitePalace(Choice):
|
|
|
|
"""
|
|
|
|
Whether or not to include White Palace or not. Note: Even if excluded, the King Fragment check may still be
|
|
|
|
required if charms are vanilla.
|
|
|
|
"""
|
|
|
|
display_name = "White Palace"
|
|
|
|
option_exclude = 0 # No White Palace at all
|
|
|
|
option_kingfragment = 1 # Include King Fragment check only
|
|
|
|
option_nopathofpain = 2 # Exclude Path of Pain locations.
|
|
|
|
option_include = 3 # Include all White Palace locations, including Path of Pain.
|
|
|
|
default = 0
|
|
|
|
|
|
|
|
|
2022-06-28 01:05:29 +00:00
|
|
|
class DeathLink(Choice):
|
|
|
|
"""
|
|
|
|
When you die, everyone dies. Of course the reverse is true too.
|
|
|
|
When enabled, choose how incoming deathlinks are handled:
|
|
|
|
vanilla: DeathLink kills you and is just like any other death. RIP your previous shade and geo.
|
|
|
|
shadeless: DeathLink kills you, but no shade spawns and no geo is lost. Your previous shade, if any, is untouched.
|
|
|
|
shade: DeathLink functions like a normal death if you do not already have a shade, shadeless otherwise.
|
|
|
|
"""
|
|
|
|
option_off = 0
|
|
|
|
alias_no = 0
|
|
|
|
alias_true = 1
|
|
|
|
alias_on = 1
|
|
|
|
alias_yes = 1
|
|
|
|
option_shadeless = 1
|
|
|
|
option_vanilla = 2
|
|
|
|
option_shade = 3
|
|
|
|
|
|
|
|
|
Hollow Knight updates (goals, WP/POP, etc.) (#438)
* Hollow Knight updates:
- Add configurable goals (Any, THK, Siblings, Radiance)
- Change base logic to require Opened_Black_Egg_Temple instead of
requiring 3 dreamers. This is future-proof for transition rando,
where Black Egg might not have been located yet.
- Add combat logic for THK and Radiance on par with Rando4's boss logic,
so itemless HK shouldn't be required.
- Existing completion logic now uses Black_Egg_te
- Add White Palace options
(Exclude, King Fragment Only, No Path of Pain, Include)
- Excluded WP may still be required for King Fragment if Charms are
not randomized
- Simply don't place WP locations that are excluded
- Distinguish between POP locations (required for POP), WP checks (
actual item locations), WP transitions (relevant for future transition
rando), and WP events (logically required to reach King Fragment)
- Many transitions were listed twice. Remove duplicates.
- Sort transitions by scene
- For randomizable locations that have no logical significance when not
randomized, simply skip adding them to the pool entirely for
theoretically faster generation.
* Hollow Knight updates
- Support random starting geo up to 1000 geo.
- Always include locations rather than dropping unrandomized "logicless"
ones, as it is required to best support same-slot coop.
2022-06-13 06:23:03 +00:00
|
|
|
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 15:10:10 +00: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
|
|
|
|
display_name = "Cost Sanity"
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
cost_sanity_weights: typing.Dict[str, type(Option)] = {}
|
|
|
|
for term, cost in cost_terms.items():
|
|
|
|
option_name = f"CostSanity{cost.option}Weight"
|
|
|
|
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)
|
|
|
|
globals()[option.__name__] = option
|
|
|
|
cost_sanity_weights[option.__name__] = option
|
|
|
|
|
|
|
|
|
2022-04-01 01:23:52 +00:00
|
|
|
hollow_knight_options: typing.Dict[str, type(Option)] = {
|
|
|
|
**hollow_knight_randomize_options,
|
2022-07-03 15:10:10 +00:00
|
|
|
RandomizeElevatorPass.__name__: RandomizeElevatorPass,
|
2022-04-01 01:23:52 +00:00
|
|
|
**hollow_knight_logic_options,
|
2022-06-28 01:05:29 +00:00
|
|
|
**{
|
|
|
|
option.__name__: option
|
|
|
|
for option in (
|
|
|
|
StartLocation, Goal, WhitePalace, StartingGeo, DeathLink,
|
2022-07-03 15:10:10 +00:00
|
|
|
MinimumGeoPrice, MaximumGeoPrice,
|
2022-06-28 01:05:29 +00:00
|
|
|
MinimumGrubPrice, MaximumGrubPrice,
|
|
|
|
MinimumEssencePrice, MaximumEssencePrice,
|
|
|
|
MinimumCharmPrice, MaximumCharmPrice,
|
|
|
|
RandomCharmCosts, PlandoCharmCosts,
|
|
|
|
MinimumEggPrice, MaximumEggPrice, EggShopSlots,
|
2022-07-03 15:10:10 +00:00
|
|
|
SlyShopSlots, SlyKeyShopSlots, IseldaShopSlots,
|
|
|
|
SalubraShopSlots, SalubraCharmShopSlots,
|
|
|
|
LegEaterShopSlots, GrubfatherRewardSlots,
|
|
|
|
SeerRewardSlots, ExtraShopSlots,
|
|
|
|
SplitCrystalHeart, SplitMothwingCloak, SplitMantisClaw,
|
|
|
|
CostSanity, CostSanityHybridChance,
|
2022-06-28 01:05:29 +00:00
|
|
|
)
|
2022-07-03 15:10:10 +00:00
|
|
|
},
|
|
|
|
**cost_sanity_weights
|
2021-06-25 21:32:13 +00:00
|
|
|
}
|