2020-03-18 15:15:32 +00:00
|
|
|
from __future__ import annotations
|
2020-10-24 04:43:35 +00:00
|
|
|
import typing
|
2021-06-08 12:15:23 +00:00
|
|
|
import random
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
|
2020-10-24 04:43:35 +00:00
|
|
|
class AssembleOptions(type):
|
2021-04-14 15:51:11 +00:00
|
|
|
def __new__(mcs, name, bases, attrs):
|
2020-10-24 04:43:35 +00:00
|
|
|
options = attrs["options"] = {}
|
|
|
|
name_lookup = attrs["name_lookup"] = {}
|
|
|
|
for base in bases:
|
2021-06-08 19:58:11 +00:00
|
|
|
if hasattr(base, "options"):
|
|
|
|
options.update(base.options)
|
|
|
|
name_lookup.update(name_lookup)
|
2020-10-24 04:43:35 +00:00
|
|
|
new_options = {name[7:].lower(): option_id for name, option_id in attrs.items() if
|
2021-03-20 23:47:17 +00:00
|
|
|
name.startswith("option_")}
|
2020-10-24 04:43:35 +00:00
|
|
|
attrs["name_lookup"].update({option_id: name for name, option_id in new_options.items()})
|
|
|
|
options.update(new_options)
|
|
|
|
|
2021-03-20 23:47:17 +00:00
|
|
|
# apply aliases, without name_lookup
|
2020-10-24 04:43:35 +00:00
|
|
|
options.update({name[6:].lower(): option_id for name, option_id in attrs.items() if
|
|
|
|
name.startswith("alias_")})
|
2021-04-14 15:51:11 +00:00
|
|
|
return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs)
|
2020-10-24 04:43:35 +00:00
|
|
|
|
|
|
|
|
2021-06-11 12:22:44 +00:00
|
|
|
class AssembleCategoryPath(type):
|
|
|
|
def __new__(mcs, name, bases, attrs):
|
|
|
|
path = []
|
|
|
|
for base in bases:
|
|
|
|
if hasattr(base, "segment"):
|
|
|
|
path += base.segment
|
|
|
|
path += attrs["segment"]
|
|
|
|
attrs["path"] = path
|
|
|
|
return super(AssembleCategoryPath, mcs).__new__(mcs, name, bases, attrs)
|
|
|
|
|
|
|
|
|
|
|
|
class RootCategory(metaclass=AssembleCategoryPath):
|
|
|
|
segment = []
|
|
|
|
|
|
|
|
|
|
|
|
class LttPCategory(RootCategory):
|
|
|
|
segment = ["A Link to the Past"]
|
|
|
|
|
|
|
|
|
|
|
|
class LttPRomCategory(LttPCategory):
|
|
|
|
segment = ["rom"]
|
|
|
|
|
|
|
|
|
|
|
|
class FactorioCategory(RootCategory):
|
|
|
|
segment = ["Factorio"]
|
|
|
|
|
|
|
|
|
|
|
|
class MinecraftCategory(RootCategory):
|
|
|
|
segment = ["Minecraft"]
|
|
|
|
|
|
|
|
|
2020-10-24 04:43:35 +00:00
|
|
|
class Option(metaclass=AssembleOptions):
|
|
|
|
value: int
|
|
|
|
name_lookup: typing.Dict[int, str]
|
2021-04-03 12:47:49 +00:00
|
|
|
default = 0
|
2020-10-24 04:43:35 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return f"{self.__class__.__name__}({self.get_option_name()})"
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
return hash(self.value)
|
|
|
|
|
|
|
|
def get_option_name(self):
|
|
|
|
return self.name_lookup[self.value]
|
|
|
|
|
|
|
|
def __int__(self):
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
def __bool__(self):
|
|
|
|
return bool(self.value)
|
|
|
|
|
2021-03-14 07:38:02 +00:00
|
|
|
@classmethod
|
|
|
|
def from_any(cls, data: typing.Any):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-10-24 04:43:35 +00:00
|
|
|
|
|
|
|
class Toggle(Option):
|
|
|
|
option_false = 0
|
|
|
|
option_true = 1
|
2021-04-03 12:47:49 +00:00
|
|
|
default = 0
|
2020-10-24 04:43:35 +00:00
|
|
|
|
|
|
|
def __init__(self, value: int):
|
|
|
|
self.value = value
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_text(cls, text: str) -> Toggle:
|
|
|
|
if text.lower() in {"off", "0", "false", "none", "null", "no"}:
|
2020-10-24 04:43:35 +00:00
|
|
|
return cls(0)
|
|
|
|
else:
|
|
|
|
return cls(1)
|
|
|
|
|
2021-03-14 07:38:02 +00:00
|
|
|
@classmethod
|
|
|
|
def from_any(cls, data: typing.Any):
|
|
|
|
if type(data) == str:
|
|
|
|
return cls.from_text(data)
|
|
|
|
else:
|
|
|
|
return cls(data)
|
|
|
|
|
2020-10-24 04:43:35 +00:00
|
|
|
def __eq__(self, other):
|
|
|
|
if isinstance(other, Toggle):
|
|
|
|
return self.value == other.value
|
|
|
|
else:
|
|
|
|
return self.value == other
|
|
|
|
|
|
|
|
def __gt__(self, other):
|
|
|
|
if isinstance(other, Toggle):
|
|
|
|
return self.value > other.value
|
2020-03-18 15:15:32 +00:00
|
|
|
else:
|
2020-10-24 04:43:35 +00:00
|
|
|
return self.value > other
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2021-03-20 23:47:17 +00:00
|
|
|
def __bool__(self):
|
|
|
|
return bool(self.value)
|
|
|
|
|
|
|
|
def __int__(self):
|
|
|
|
return int(self.value)
|
|
|
|
|
2020-10-24 04:43:35 +00:00
|
|
|
def get_option_name(self):
|
|
|
|
return bool(self.value)
|
|
|
|
|
2021-04-03 12:47:49 +00:00
|
|
|
|
2020-10-24 04:43:35 +00:00
|
|
|
class Choice(Option):
|
|
|
|
def __init__(self, value: int):
|
|
|
|
self.value: int = value
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_text(cls, text: str) -> Choice:
|
2020-10-24 04:43:35 +00:00
|
|
|
for optionname, value in cls.options.items():
|
|
|
|
if optionname == text.lower():
|
|
|
|
return cls(value)
|
2020-03-18 15:15:32 +00:00
|
|
|
raise KeyError(
|
2020-10-24 04:43:35 +00:00
|
|
|
f'Could not find option "{text}" for "{cls.__name__}", '
|
|
|
|
f'known options are {", ".join(f"{option}" for option in cls.name_lookup.values())}')
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2021-03-14 07:38:02 +00:00
|
|
|
@classmethod
|
2021-05-09 15:46:26 +00:00
|
|
|
def from_any(cls, data: typing.Any) -> Choice:
|
2021-04-08 17:53:24 +00:00
|
|
|
if type(data) == int and data in cls.options.values():
|
|
|
|
return cls(data)
|
|
|
|
return cls.from_text(str(data))
|
2021-03-14 07:38:02 +00:00
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
class Range(Option, int):
|
2021-06-08 12:15:23 +00:00
|
|
|
range_start = 0
|
|
|
|
range_end = 1
|
2021-06-08 13:39:34 +00:00
|
|
|
|
|
|
|
def __init__(self, value: int):
|
|
|
|
if value < self.range_start:
|
|
|
|
raise Exception(f"{value} is lower than minimum {self.range_start} for option {self.__class__.__name__}")
|
|
|
|
elif value > self.range_end:
|
|
|
|
raise Exception(f"{value} is higher than maximum {self.range_end} for option {self.__class__.__name__}")
|
2021-06-08 19:58:11 +00:00
|
|
|
self.value = value
|
2021-06-08 12:15:23 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_text(cls, text: str) -> Range:
|
2021-06-08 12:48:00 +00:00
|
|
|
text = text.lower()
|
|
|
|
if text.startswith("random"):
|
|
|
|
if text == "random-low":
|
|
|
|
return cls(int(round(random.triangular(cls.range_start, cls.range_end, cls.range_start), 0)))
|
|
|
|
elif text == "random-high":
|
|
|
|
return cls(int(round(random.triangular(cls.range_start, cls.range_end, cls.range_end), 0)))
|
2021-06-12 19:05:45 +00:00
|
|
|
elif text == "random-middle":
|
|
|
|
return cls(int(round(random.triangular(cls.range_start, cls.range_end), 0)))
|
2021-06-08 12:48:00 +00:00
|
|
|
else:
|
|
|
|
return cls(random.randint(cls.range_start, cls.range_end))
|
2021-06-08 13:39:34 +00:00
|
|
|
return cls(int(text))
|
2021-06-08 12:15:23 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_any(cls, data: typing.Any) -> Range:
|
|
|
|
if type(data) == int:
|
|
|
|
return cls(data)
|
|
|
|
return cls.from_text(str(data))
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
def __str__(self):
|
|
|
|
return str(self.value)
|
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2021-05-09 15:46:26 +00:00
|
|
|
class OptionNameSet(Option):
|
|
|
|
default = frozenset()
|
|
|
|
|
|
|
|
def __init__(self, value: typing.Set[str]):
|
|
|
|
self.value: typing.Set[str] = value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_text(cls, text: str) -> OptionNameSet:
|
|
|
|
return cls({option.strip() for option in text.split(",")})
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_any(cls, data: typing.Any) -> OptionNameSet:
|
|
|
|
if type(data) == set:
|
|
|
|
return cls(data)
|
|
|
|
return cls.from_text(str(data))
|
|
|
|
|
|
|
|
|
|
|
|
class OptionDict(Option):
|
|
|
|
default = {}
|
|
|
|
|
|
|
|
def __init__(self, value: typing.Dict[str, typing.Any]):
|
|
|
|
self.value: typing.Dict[str, typing.Any] = value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_any(cls, data: typing.Dict[str, typing.Any]) -> OptionDict:
|
|
|
|
if type(data) == dict:
|
|
|
|
return cls(data)
|
|
|
|
else:
|
|
|
|
raise NotImplementedError(f"Cannot Convert from non-dictionary, got {type(data)}")
|
|
|
|
|
2021-06-03 22:29:59 +00:00
|
|
|
def get_option_name(self):
|
|
|
|
return str(self.value)
|
2021-05-09 15:46:26 +00:00
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2020-03-18 15:15:32 +00:00
|
|
|
class Logic(Choice):
|
2020-10-24 04:43:35 +00:00
|
|
|
option_no_glitches = 0
|
|
|
|
option_minor_glitches = 1
|
|
|
|
option_overworld_glitches = 2
|
|
|
|
option_no_logic = 4
|
|
|
|
alias_owg = 2
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
|
2020-10-24 17:46:13 +00:00
|
|
|
class Objective(Choice):
|
|
|
|
option_crystals = 0
|
2021-03-20 23:47:17 +00:00
|
|
|
# option_pendants = 1
|
2020-10-24 17:46:13 +00:00
|
|
|
option_triforce_pieces = 2
|
2020-10-24 04:43:35 +00:00
|
|
|
option_pedestal = 3
|
2020-10-24 17:46:13 +00:00
|
|
|
option_bingo = 4
|
|
|
|
|
2021-03-20 23:47:17 +00:00
|
|
|
|
|
|
|
local_objective = Toggle # local triforce pieces, local dungeon prizes etc.
|
|
|
|
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2020-10-24 17:46:13 +00:00
|
|
|
class Goal(Choice):
|
|
|
|
option_kill_ganon = 0
|
|
|
|
option_kill_ganon_and_gt_agahnim = 1
|
|
|
|
option_hand_in = 2
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2021-03-20 23:47:17 +00:00
|
|
|
|
2020-03-18 15:15:32 +00:00
|
|
|
class Accessibility(Choice):
|
2020-10-24 04:43:35 +00:00
|
|
|
option_locations = 0
|
|
|
|
option_items = 1
|
|
|
|
option_beatable = 2
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
|
2021-06-08 12:15:23 +00:00
|
|
|
class Crystals(Range):
|
|
|
|
range_start = 0
|
|
|
|
range_end = 7
|
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
class CrystalsTower(Crystals):
|
2021-06-08 20:14:56 +00:00
|
|
|
default = 7
|
2021-06-08 19:58:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CrystalsGanon(Crystals):
|
|
|
|
default = 7
|
|
|
|
|
|
|
|
|
2021-06-08 12:15:23 +00:00
|
|
|
class TriforcePieces(Range):
|
|
|
|
range_start = 1
|
|
|
|
range_end = 90
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
class ShopItemSlots(Range):
|
2021-06-08 12:15:23 +00:00
|
|
|
range_start = 0
|
|
|
|
range_end = 30
|
2020-03-18 15:15:32 +00:00
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2020-03-18 15:15:32 +00:00
|
|
|
class WorldState(Choice):
|
2020-10-24 04:43:35 +00:00
|
|
|
option_standard = 1
|
|
|
|
option_open = 0
|
|
|
|
option_inverted = 2
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Bosses(Choice):
|
2020-10-24 04:43:35 +00:00
|
|
|
option_vanilla = 0
|
|
|
|
option_simple = 1
|
|
|
|
option_full = 2
|
|
|
|
option_chaos = 3
|
|
|
|
option_singularity = 4
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Enemies(Choice):
|
2020-10-24 04:43:35 +00:00
|
|
|
option_vanilla = 0
|
|
|
|
option_shuffled = 1
|
|
|
|
option_chaos = 2
|
2020-03-18 15:15:32 +00:00
|
|
|
|
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
alttp_options: typing.Dict[str, type(Option)] = {
|
|
|
|
"crystals_needed_for_gt": CrystalsTower,
|
|
|
|
"crystals_needed_for_ganon": CrystalsGanon,
|
|
|
|
"shop_item_slots": ShopItemSlots,
|
|
|
|
}
|
|
|
|
|
2020-03-18 15:15:32 +00:00
|
|
|
mapshuffle = Toggle
|
|
|
|
compassshuffle = Toggle
|
|
|
|
keyshuffle = Toggle
|
|
|
|
bigkeyshuffle = Toggle
|
|
|
|
hints = Toggle
|
|
|
|
|
2021-03-14 07:38:02 +00:00
|
|
|
RandomizeDreamers = Toggle
|
|
|
|
RandomizeSkills = Toggle
|
|
|
|
RandomizeCharms = Toggle
|
|
|
|
RandomizeKeys = Toggle
|
|
|
|
RandomizeGeoChests = Toggle
|
|
|
|
RandomizeMaskShards = Toggle
|
|
|
|
RandomizeVesselFragments = Toggle
|
|
|
|
RandomizeCharmNotches = Toggle
|
|
|
|
RandomizePaleOre = Toggle
|
|
|
|
RandomizeRancidEggs = Toggle
|
|
|
|
RandomizeRelics = Toggle
|
|
|
|
RandomizeMaps = Toggle
|
|
|
|
RandomizeStags = Toggle
|
|
|
|
RandomizeGrubs = Toggle
|
|
|
|
RandomizeWhisperingRoots = Toggle
|
|
|
|
RandomizeRocks = Toggle
|
|
|
|
RandomizeSoulTotems = Toggle
|
|
|
|
RandomizePalaceTotems = Toggle
|
|
|
|
RandomizeLoreTablets = Toggle
|
|
|
|
RandomizeLifebloodCocoons = Toggle
|
2021-03-20 23:47:17 +00:00
|
|
|
RandomizeFlames = Toggle
|
2021-03-14 07:38:02 +00:00
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
hollow_knight_randomize_options: typing.Dict[str, type(Option)] = {
|
2021-03-20 23:47:17 +00:00
|
|
|
"RandomizeDreamers": RandomizeDreamers,
|
|
|
|
"RandomizeSkills": RandomizeSkills,
|
|
|
|
"RandomizeCharms": RandomizeCharms,
|
|
|
|
"RandomizeKeys": RandomizeKeys,
|
|
|
|
"RandomizeGeoChests": RandomizeGeoChests,
|
|
|
|
"RandomizeMaskShards": RandomizeMaskShards,
|
|
|
|
"RandomizeVesselFragments": RandomizeVesselFragments,
|
|
|
|
"RandomizeCharmNotches": RandomizeCharmNotches,
|
|
|
|
"RandomizePaleOre": RandomizePaleOre,
|
|
|
|
"RandomizeRancidEggs": RandomizeRancidEggs,
|
|
|
|
"RandomizeRelics": RandomizeRelics,
|
|
|
|
"RandomizeMaps": RandomizeMaps,
|
|
|
|
"RandomizeStags": RandomizeStags,
|
|
|
|
"RandomizeGrubs": RandomizeGrubs,
|
|
|
|
"RandomizeWhisperingRoots": RandomizeWhisperingRoots,
|
|
|
|
"RandomizeRocks": RandomizeRocks,
|
|
|
|
"RandomizeSoulTotems": RandomizeSoulTotems,
|
|
|
|
"RandomizePalaceTotems": RandomizePalaceTotems,
|
|
|
|
"RandomizeLoreTablets": RandomizeLoreTablets,
|
|
|
|
"RandomizeLifebloodCocoons": RandomizeLifebloodCocoons,
|
|
|
|
"RandomizeFlames": RandomizeFlames
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2021-03-14 07:38:02 +00:00
|
|
|
}
|
|
|
|
|
2021-04-13 09:14:05 +00:00
|
|
|
hollow_knight_options: typing.Dict[str, type(Option)] = {**hollow_knight_randomize_options,
|
|
|
|
**hollow_knight_skip_options}
|
2021-03-14 07:38:02 +00:00
|
|
|
|
2021-04-03 12:47:49 +00:00
|
|
|
|
|
|
|
class MaxSciencePack(Choice):
|
|
|
|
option_automation_science_pack = 0
|
|
|
|
option_logistic_science_pack = 1
|
|
|
|
option_military_science_pack = 2
|
|
|
|
option_chemical_science_pack = 3
|
|
|
|
option_production_science_pack = 4
|
|
|
|
option_utility_science_pack = 5
|
|
|
|
option_space_science_pack = 6
|
|
|
|
default = 6
|
|
|
|
|
|
|
|
def get_allowed_packs(self):
|
|
|
|
return {option.replace("_", "-") for option, value in self.options.items()
|
|
|
|
if value <= self.value}
|
|
|
|
|
|
|
|
|
|
|
|
class TechCost(Choice):
|
|
|
|
option_very_easy = 0
|
|
|
|
option_easy = 1
|
|
|
|
option_kind = 2
|
|
|
|
option_normal = 3
|
|
|
|
option_hard = 4
|
|
|
|
option_very_hard = 5
|
|
|
|
option_insane = 6
|
|
|
|
default = 3
|
|
|
|
|
2021-04-13 09:14:05 +00:00
|
|
|
|
2021-04-06 19:16:25 +00:00
|
|
|
class FreeSamples(Choice):
|
|
|
|
option_none = 0
|
|
|
|
option_single_craft = 1
|
|
|
|
option_half_stack = 2
|
|
|
|
option_stack = 3
|
|
|
|
default = 3
|
2021-04-03 12:47:49 +00:00
|
|
|
|
2021-04-13 09:14:05 +00:00
|
|
|
|
2021-04-03 12:47:49 +00:00
|
|
|
class TechTreeLayout(Choice):
|
|
|
|
option_single = 0
|
2021-04-10 01:03:46 +00:00
|
|
|
option_small_diamonds = 1
|
2021-04-10 17:34:30 +00:00
|
|
|
option_medium_diamonds = 2
|
2021-06-06 18:26:40 +00:00
|
|
|
option_large_diamonds = 3
|
|
|
|
option_small_pyramids = 4
|
|
|
|
option_medium_pyramids = 5
|
|
|
|
option_large_pyramids = 6
|
|
|
|
option_small_funnels = 7
|
|
|
|
option_medium_funnels = 8
|
|
|
|
option_large_funnels = 9
|
|
|
|
option_funnels = 4
|
|
|
|
alias_pyramid = 6
|
|
|
|
alias_funnel = 9
|
2021-04-03 12:47:49 +00:00
|
|
|
default = 0
|
|
|
|
|
2021-04-13 09:14:05 +00:00
|
|
|
|
2021-04-08 17:53:24 +00:00
|
|
|
class Visibility(Choice):
|
|
|
|
option_none = 0
|
|
|
|
option_sending = 1
|
2021-04-13 10:38:39 +00:00
|
|
|
default = 1
|
2021-04-03 12:47:49 +00:00
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2021-06-06 19:11:58 +00:00
|
|
|
class RecipeTime(Choice):
|
|
|
|
option_vanilla = 0
|
|
|
|
option_fast = 1
|
|
|
|
option_normal = 2
|
|
|
|
option_slow = 4
|
2021-06-06 19:38:53 +00:00
|
|
|
option_chaos = 5
|
2021-04-13 09:14:05 +00:00
|
|
|
|
2021-06-08 13:39:34 +00:00
|
|
|
|
2021-05-09 15:46:26 +00:00
|
|
|
class FactorioStartItems(OptionDict):
|
|
|
|
default = {"burner-mining-drill": 19, "stone-furnace": 19}
|
|
|
|
|
|
|
|
|
2021-04-03 12:47:49 +00:00
|
|
|
factorio_options: typing.Dict[str, type(Option)] = {"max_science_pack": MaxSciencePack,
|
|
|
|
"tech_tree_layout": TechTreeLayout,
|
2021-04-06 19:16:25 +00:00
|
|
|
"tech_cost": TechCost,
|
2021-04-08 17:53:24 +00:00
|
|
|
"free_samples": FreeSamples,
|
2021-04-23 23:16:49 +00:00
|
|
|
"visibility": Visibility,
|
2021-05-09 15:46:26 +00:00
|
|
|
"random_tech_ingredients": Toggle,
|
2021-06-06 19:11:58 +00:00
|
|
|
"starting_items": FactorioStartItems,
|
|
|
|
"recipe_time": RecipeTime}
|
2021-05-09 15:46:26 +00:00
|
|
|
|
2021-04-03 12:47:49 +00:00
|
|
|
|
Minecraft Randomizer
Squash merge, original Commits:
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
2021-05-08 11:38:57 +00:00
|
|
|
class AdvancementGoal(Choice):
|
|
|
|
option_few = 0
|
|
|
|
option_normal = 1
|
|
|
|
option_many = 2
|
|
|
|
default = 1
|
|
|
|
|
2021-05-09 15:46:26 +00:00
|
|
|
|
|
|
|
class CombatDifficulty(Choice):
|
Minecraft Randomizer
Squash merge, original Commits:
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
2021-05-08 11:38:57 +00:00
|
|
|
option_easy = 0
|
|
|
|
option_normal = 1
|
|
|
|
option_hard = 2
|
|
|
|
default = 1
|
|
|
|
|
2021-05-09 15:46:26 +00:00
|
|
|
|
Minecraft Randomizer
Squash merge, original Commits:
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
2021-05-08 11:38:57 +00:00
|
|
|
minecraft_options: typing.Dict[str, type(Option)] = {
|
2021-05-09 15:46:26 +00:00
|
|
|
"advancement_goal": AdvancementGoal,
|
Minecraft Randomizer
Squash merge, original Commits:
* Minecraft locations, items, and generation without logic
* added id lookup for minecraft
* typing import fix in minecraft/Items.py
* fix 2
* implementing Minecraft options and hard/postgame advancement exclusion
* first logic pass (75/80)
* logic pass 2 and proper completion conditions
* added insane difficulty pool, modified method of excluding item pools for easier extension
* bump network_data_package version
* minecraft testing framework
* switch Ancient Debris to Netherite Scrap to avoid advancement triggering on receiving that item
* Testing now functions, split tests up by advancement pane, added some story tests
* Newer testing framework: every advancement gets its own function, for ease of testing
* fixed logic for The End... Again...
* changed option names to "include_hard_advancements" etc.
* village/pillager-related advancements now require can_adventure: weapon + food
* a few minecraft tests
* rename "Flint & Steel" to "Flint and Steel" for parity with in-game name
* additional MC tests
* more tests, mostly nether-related tests
* more tests, removed anvil path for Two Birds One Arrow
* include Minecraft slot data, and a world seed for each Minecraft player slot
* Added new items: ender pearls, lapis, porkchops
* All remaining Minecraft tests
* formatting of Minecraft tests and logic for better readability
* require Wither kill for Monsters Hunted
* properly removed 8 Emeralds item from item pool
* enchanting required for wither; fishing rod required for water breathing; water breathing required for elder guardian kill
* Added 12 new advancements (ported from old achievement system)
* renamed "On a Rail" for consistency with modern advancements
* tests for the new advancements
* moved slot_data generation for minecraft into worlds/minecraft/__init__.py, added logic_version to slot_data
* output minecraft options in the spoiler log
* modified advancement goal values for new advancements
* make non-native Minecraft items appear as Shovel in ALttP, and unknown-game items as Power Stars
* fixed glowstone block logic for Not Quite Nine Lives
* setup for shuffling MC structures: building ER world and shuffling regions/entrances
* ensured Nether Fortresses can't be placed in the End
* finished logic for structure randomization
* fixed nonnative items always showing up as Hammers in ALttP shops
* output minecraft structure info in the spoiler
* generate .apmc file for communication with MC client
* fixed structure rando always using the same seed
* move stuff to worlds/minecraft/Regions.py
* make output apmc file have consistent name with other files
* added minecraft bottle macro; fixed tests imports
* generalizing MC region generation
* restructured structure shuffling in preparation for structure plando
* only output structure rando info in spoiler if they are shuffled
* Force structure rando to always be off, for the stable release
* added Minecraft options to player settings
* formally added combat_difficulty as an option
* Added Ender Dragon into playthrough, cleaned up goal map
* Added new difficulties: Easy, Normal, Hard combat
* moved .apmc generation time to prevent outputs on failed generation
* updated tests for new combat logic
* Fixed bug causing generation to fail; removed Nether Fortress event since it should no longer be needed with the fix
* moved all MC-specific functions into gen_minecraft
* renamed "logic_version" to "client_version"
* bug fixes
properly flagged event locations/items with id None
moved generation back to Main.py to fix mysterious generation failures
* moved link_minecraft_regions into minecraft init, left create_regions in Main for caching
* added seed_name, player_name, client_version to apmc file
* reenabled structure shuffle
* added entrance tests for minecraft
Co-authored-by: achuang <alexander.w.chuang@gmail.com>
2021-05-08 11:38:57 +00:00
|
|
|
"combat_difficulty": CombatDifficulty,
|
|
|
|
"include_hard_advancements": Toggle,
|
|
|
|
"include_insane_advancements": Toggle,
|
|
|
|
"include_postgame_advancements": Toggle,
|
|
|
|
"shuffle_structures": Toggle
|
|
|
|
}
|
2021-04-11 16:19:47 +00:00
|
|
|
|
2021-06-08 19:58:11 +00:00
|
|
|
option_sets = (
|
|
|
|
minecraft_options,
|
|
|
|
factorio_options,
|
|
|
|
alttp_options,
|
|
|
|
hollow_knight_options
|
|
|
|
)
|
|
|
|
|
2020-03-18 15:15:32 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
test = argparse.Namespace()
|
|
|
|
test.logic = Logic.from_text("no_logic")
|
|
|
|
test.mapshuffle = mapshuffle.from_text("ON")
|
2020-10-24 04:43:35 +00:00
|
|
|
test.hints = hints.from_text('OFF')
|
|
|
|
try:
|
|
|
|
test.logic = Logic.from_text("overworld_glitches_typo")
|
|
|
|
except KeyError as e:
|
|
|
|
print(e)
|
2020-03-18 15:15:32 +00:00
|
|
|
try:
|
2020-10-24 04:43:35 +00:00
|
|
|
test.logic_owg = Logic.from_text("owg")
|
2020-03-18 15:15:32 +00:00
|
|
|
except KeyError as e:
|
|
|
|
print(e)
|
|
|
|
if test.mapshuffle:
|
|
|
|
print("Mapshuffle is on")
|
2020-10-24 04:43:35 +00:00
|
|
|
print(f"Hints are {bool(test.hints)}")
|
2020-03-18 15:15:32 +00:00
|
|
|
print(test)
|