Options: introduce value_determined_randomly attribute

This commit is contained in:
Berserker
2026-05-10 17:32:59 +02:00
parent 4ef1fb7630
commit 57a17fb6a5
2 changed files with 39 additions and 7 deletions
+19 -1
View File
@@ -362,6 +362,21 @@ def get_choice(option, root, value=None) -> Any:
raise RuntimeError(f"All options specified in \"{option}\" are weighted as zero.")
def get_choice_and_random(option, root, value=None) -> tuple[Any, bool]:
if option not in root:
return value, False
if type(root[option]) is list:
return random.choices(root[option])[0], len(root[option]) > 1
if type(root[option]) is not dict:
return root[option], False
if not root[option]:
return value, False
if any(root[option].values()):
return (random.choices(list(root[option].keys()), weights=list(map(int, root[option].values())))[0],
len(root[option]) > 1)
raise RuntimeError(f"All options specified in \"{option}\" are weighted as zero.")
class SafeFormatter(string.Formatter):
def get_value(self, key, args, kwargs):
if isinstance(key, int):
@@ -517,7 +532,10 @@ def handle_option(ret: argparse.Namespace, game_weights: dict, option_key: str,
if not option.supports_weighting:
player_option = option.from_any(game_weights[option_key])
else:
player_option = option.from_any(get_choice(option_key, game_weights))
result, is_weighted = get_choice_and_random(option_key, game_weights)
player_option = option.from_any(result)
if not player_option.value_determined_randomly and is_weighted:
player_option.value_determined_randomly = True
else:
player_option = option.from_any(option.default) # call the from_any here to support default "random"
setattr(ret, option_key, player_option)
+20 -6
View File
@@ -159,6 +159,7 @@ class Option(typing.Generic[T], metaclass=AssembleOptions):
value: T
default: typing.ClassVar[typing.Any] # something that __init__ will be able to convert to the correct type
visibility = Visibility.all
value_determined_randomly: bool = False # The value this option has, came from a random source
# convert option_name_long into Name Long as display_name, otherwise name_long is the result.
# Handled in get_option_name()
@@ -456,7 +457,9 @@ class Toggle(NumericOption):
@classmethod
def from_text(cls, text: str) -> Toggle:
if text == "random":
return cls(random.choice(list(cls.name_lookup)))
res = cls(random.choice(list(cls.name_lookup)))
res.value_determined_randomly = True
return res
elif text.lower() in {"off", "0", "false", "none", "null", "no", "disabled"}:
return cls(0)
elif text.lower() in {"on", "1", "true", "yes", "enabled"}:
@@ -492,7 +495,9 @@ class Choice(NumericOption):
def from_text(cls, text: str) -> Choice:
text = text.lower()
if text == "random":
return cls(random.choice(list(cls.name_lookup)))
res = cls(random.choice(list(cls.name_lookup)))
res.value_determined_randomly = True
return res
for option_name, value in cls.options.items():
if option_name == text:
return cls(value)
@@ -581,7 +586,9 @@ class TextChoice(Choice):
@classmethod
def from_text(cls, text: str) -> TextChoice:
if text.lower() == "random": # chooses a random defined option but won't use any free text options
return cls(random.choice(list(cls.name_lookup)))
res = cls(random.choice(list(cls.name_lookup)))
res.value_determined_randomly = True
return res
for option_name, value in cls.options.items():
if option_name.lower() == text.lower():
return cls(value)
@@ -636,7 +643,9 @@ class PlandoBosses(TextChoice, metaclass=BossMeta):
# set all of our text to lower case for name checking
text = text.lower()
if text == "random":
return cls(random.choice(list(cls.options.values())))
res = cls(random.choice(list(cls.options.values())))
res.value_determined_randomly = True
return res
for option_name, value in cls.options.items():
if option_name == text:
return cls(value)
@@ -655,6 +664,7 @@ class PlandoBosses(TextChoice, metaclass=BossMeta):
option_list.remove("random")
options = ";".join(option_list) + f";{shuffle}"
boss_class = cls(options)
boss_class.value_determined_randomly = True
else:
for option in option_list:
if option in cls.options:
@@ -741,7 +751,9 @@ class Range(NumericOption):
def from_text(cls, text: str) -> Range:
text = text.lower()
if text.startswith("random"):
return cls.weighted_range(text)
res = cls.weighted_range(text)
res.value_determined_randomly = True
return res
elif text == "default" and hasattr(cls, "default"):
return cls.from_any(cls.default)
elif text == "high":
@@ -1036,7 +1048,9 @@ class OptionSet(Option[typing.Set[str]], VerifyKeys):
check_text = text.lower().split(",")
if ((cls.valid_keys or cls.verify_item_name or cls.verify_location_name)
and len(check_text) == 1 and check_text[0].startswith("random")):
return cls((), check_text[0])
res = cls((), check_text[0])
res.value_determined_randomly = True
return res
return cls([option.strip() for option in text.split(",")])
@classmethod