Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: add list/dict merging feature to triggers #2793

Merged
merged 24 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions Generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,23 @@ def roll_percentage(percentage: Union[int, float]) -> bool:

def update_weights(weights: dict, new_weights: dict, type: str, name: str) -> dict:
logging.debug(f'Applying {new_weights}')
new_options = set(new_weights) - set(weights)
weights.update(new_weights)
cleaned_weights = {}
for option in new_weights:
if option.endswith("@merge"):
option_name = option.replace("@merge", "")
new_value = new_weights[option]
if option_name in weights:
if isinstance(new_value, dict):
new_value.update(weights[option_name])
Silvris marked this conversation as resolved.
Show resolved Hide resolved
elif isinstance(new_value, list):
new_value.extend(weights[option_name])
else:
raise Exception(f"Cannot apply @merge to non-dict or list type {option_name}.")
cleaned_weights[option_name] = new_value
else:
cleaned_weights[option] = new_weights[option]
new_options = set(cleaned_weights) - set(weights)
weights.update(cleaned_weights)
if new_options:
for new_option in new_options:
logging.warning(f'{type} Suboption "{new_option}" of "{name}" did not '
Expand Down Expand Up @@ -473,6 +488,10 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
world_type = AutoWorldRegister.world_types[ret.game]
game_weights = weights[ret.game]

if any(weight.startswith("@merge") for weight in game_weights) or \
any(weight.startswith("@merge") for weight in weights):
raise Exception(f"@merge tag cannot be used outside of trigger contexts.")

if "triggers" in game_weights:
weights = roll_triggers(weights, game_weights["triggers"])
game_weights = weights[ret.game]
Expand Down
3 changes: 3 additions & 0 deletions Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ def construct_mapping(self, node, deep=False):
if key in mapping:
logging.error(f"YAML duplicates sanity check failed{key_node.start_mark}")
raise KeyError(f"Duplicate key {key} found in YAML. Already found keys: {mapping}.")
if str(key).replace("@merge", "") in mapping or str(key) + "@merge" in mapping:
logging.error(f"YAML merge duplicates sanity check failed{key_node.start_mark}")
raise KeyError(f"Equivalent key {key} found in YAML. Already found keys: {mapping}.")
mapping.add(key)
return super().construct_mapping(node, deep)

Expand Down
29 changes: 28 additions & 1 deletion worlds/generic/docs/triggers_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,31 @@ For example:
In this example (thanks to @Black-Sliver), if the `pupdunk` option is rolled, then the difficulty values will be rolled
again using the new options `normal`, `pupdunk_hard`, and `pupdunk_mystery`, and the exp modifier will be rerolled using
new weights for 150 and 200. This allows for two more triggers that will only be used for the new `pupdunk_hard`
and `pupdunk_mystery` options so that they will only be triggered on "pupdunk AND hard/mystery".
and `pupdunk_mystery` options so that they will only be triggered on "pupdunk AND hard/mystery".

Options that define a list or dict can additionally have the tag `@merge` added to the end of thier name, which applies the contents of
the activated trigger to the already present equivalents in the game options.

For example:
```yaml
Super Metroid:
start_location:
landing_site: 50
aqueduct: 50
start_hints:
- Morph Ball
triggers:
- option_category: Super Metroid
option_name: start_location
option_result: aqueduct
options:
Super Metroid:
start_hints@merge:
- Gravity Suit
```

In this example, if the `start_location` option rolls `landing_site`, only a starting hint for Morph Ball will be created.
If `aqueduct` is rolled, a starting hint for Gravity Suit will also be created alongside the hint for Morph Ball.

Note that for lists, items can only be added, not removed or replaced. For dicts, defining a value for a present key will
replace that value within the dict.
Loading