Generate: remove tag "-" (#3036)
* Generate: introduce Remove, similar to Merge * make + dict behave as + for each value --------- Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
parent
3f8c348a49
commit
860ab10b0b
29
Generate.py
29
Generate.py
|
@ -9,6 +9,7 @@ import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from typing import Any, Dict, Tuple, Union
|
from typing import Any, Dict, Tuple, Union
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
import ModuleUpdate
|
import ModuleUpdate
|
||||||
|
|
||||||
|
@ -319,18 +320,34 @@ def update_weights(weights: dict, new_weights: dict, update_type: str, name: str
|
||||||
logging.debug(f'Applying {new_weights}')
|
logging.debug(f'Applying {new_weights}')
|
||||||
cleaned_weights = {}
|
cleaned_weights = {}
|
||||||
for option in new_weights:
|
for option in new_weights:
|
||||||
option_name = option.lstrip("+")
|
option_name = option.lstrip("+-")
|
||||||
if option.startswith("+") and option_name in weights:
|
if option.startswith("+") and option_name in weights:
|
||||||
cleaned_value = weights[option_name]
|
cleaned_value = weights[option_name]
|
||||||
new_value = new_weights[option]
|
new_value = new_weights[option]
|
||||||
if isinstance(new_value, (set, dict)):
|
if isinstance(new_value, set):
|
||||||
cleaned_value.update(new_value)
|
cleaned_value.update(new_value)
|
||||||
elif isinstance(new_value, list):
|
elif isinstance(new_value, list):
|
||||||
cleaned_value.extend(new_value)
|
cleaned_value.extend(new_value)
|
||||||
|
elif isinstance(new_value, dict):
|
||||||
|
cleaned_value = dict(Counter(cleaned_value) + Counter(new_value))
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Cannot apply merge to non-dict, set, or list type {option_name},"
|
raise Exception(f"Cannot apply merge to non-dict, set, or list type {option_name},"
|
||||||
f" received {type(new_value).__name__}.")
|
f" received {type(new_value).__name__}.")
|
||||||
cleaned_weights[option_name] = cleaned_value
|
cleaned_weights[option_name] = cleaned_value
|
||||||
|
elif option.startswith("-") and option_name in weights:
|
||||||
|
cleaned_value = weights[option_name]
|
||||||
|
new_value = new_weights[option]
|
||||||
|
if isinstance(new_value, set):
|
||||||
|
cleaned_value.difference_update(new_value)
|
||||||
|
elif isinstance(new_value, list):
|
||||||
|
for element in new_value:
|
||||||
|
cleaned_value.remove(element)
|
||||||
|
elif isinstance(new_value, dict):
|
||||||
|
cleaned_value = dict(Counter(cleaned_value) - Counter(new_value))
|
||||||
|
else:
|
||||||
|
raise Exception(f"Cannot apply remove to non-dict, set, or list type {option_name},"
|
||||||
|
f" received {type(new_value).__name__}.")
|
||||||
|
cleaned_weights[option_name] = cleaned_value
|
||||||
else:
|
else:
|
||||||
cleaned_weights[option_name] = new_weights[option]
|
cleaned_weights[option_name] = new_weights[option]
|
||||||
new_options = set(cleaned_weights) - set(weights)
|
new_options = set(cleaned_weights) - set(weights)
|
||||||
|
@ -466,9 +483,11 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
|
||||||
world_type = AutoWorldRegister.world_types[ret.game]
|
world_type = AutoWorldRegister.world_types[ret.game]
|
||||||
game_weights = weights[ret.game]
|
game_weights = weights[ret.game]
|
||||||
|
|
||||||
if any(weight.startswith("+") for weight in game_weights) or \
|
for weight in chain(game_weights, weights):
|
||||||
any(weight.startswith("+") for weight in weights):
|
if weight.startswith("+"):
|
||||||
raise Exception(f"Merge tag cannot be used outside of trigger contexts.")
|
raise Exception(f"Merge tag cannot be used outside of trigger contexts. Found {weight}")
|
||||||
|
if weight.startswith("-"):
|
||||||
|
raise Exception(f"Remove tag cannot be used outside of trigger contexts. Found {weight}")
|
||||||
|
|
||||||
if "triggers" in game_weights:
|
if "triggers" in game_weights:
|
||||||
weights = roll_triggers(weights, game_weights["triggers"], valid_trigger_names)
|
weights = roll_triggers(weights, game_weights["triggers"], valid_trigger_names)
|
||||||
|
|
|
@ -31,7 +31,7 @@ class TestPlayerOptions(unittest.TestCase):
|
||||||
self.assertEqual(new_weights["list_2"], ["string_3"])
|
self.assertEqual(new_weights["list_2"], ["string_3"])
|
||||||
self.assertEqual(new_weights["list_1"], ["string", "string_2"])
|
self.assertEqual(new_weights["list_1"], ["string", "string_2"])
|
||||||
self.assertEqual(new_weights["dict_1"]["option_a"], 50)
|
self.assertEqual(new_weights["dict_1"]["option_a"], 50)
|
||||||
self.assertEqual(new_weights["dict_1"]["option_b"], 0)
|
self.assertEqual(new_weights["dict_1"]["option_b"], 50)
|
||||||
self.assertEqual(new_weights["dict_1"]["option_c"], 50)
|
self.assertEqual(new_weights["dict_1"]["option_c"], 50)
|
||||||
self.assertNotIn("option_f", new_weights["dict_2"])
|
self.assertNotIn("option_f", new_weights["dict_2"])
|
||||||
self.assertEqual(new_weights["dict_2"]["option_g"], 50)
|
self.assertEqual(new_weights["dict_2"]["option_g"], 50)
|
||||||
|
|
|
@ -123,10 +123,21 @@ again using the new options `normal`, `pupdunk_hard`, and `pupdunk_mystery`, and
|
||||||
new weights for 150 and 200. This allows for two more triggers that will only be used for the new `pupdunk_hard`
|
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, set, or dict can additionally have the character `+` added to the start of their name, which applies the contents of
|
## Adding or Removing from a List, Set, or Dict Option
|
||||||
the activated trigger to the already present equivalents in the game options.
|
|
||||||
|
List, set, and dict options can additionally have values added to or removed from itself without overriding the existing
|
||||||
|
option value by prefixing the option name in the trigger block with `+` (add) or `-` (remove). The exact behavior for
|
||||||
|
each will depend on the option type.
|
||||||
|
|
||||||
|
- For sets, `+` will add the value(s) to the set and `-` will remove any value(s) of the set. Sets do not allow
|
||||||
|
duplicates.
|
||||||
|
- For lists, `+` will add new values(s) to the list and `-` will remove the first matching values(s) it comes across.
|
||||||
|
Lists allow duplicate values.
|
||||||
|
- For dicts, `+` will add the value(s) to the given key(s) inside the dict if it exists, or add it otherwise. `-` is the
|
||||||
|
inverse operation of addition (and negative values are allowed).
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
Super Metroid:
|
Super Metroid:
|
||||||
start_location:
|
start_location:
|
||||||
|
@ -134,18 +145,21 @@ Super Metroid:
|
||||||
aqueduct: 50
|
aqueduct: 50
|
||||||
start_hints:
|
start_hints:
|
||||||
- Morph Ball
|
- Morph Ball
|
||||||
triggers:
|
start_inventory:
|
||||||
- option_category: Super Metroid
|
Power Bombs: 1
|
||||||
option_name: start_location
|
triggers:
|
||||||
option_result: aqueduct
|
- option_category: Super Metroid
|
||||||
options:
|
option_name: start_location
|
||||||
Super Metroid:
|
option_result: aqueduct
|
||||||
+start_hints:
|
options:
|
||||||
- Gravity Suit
|
Super Metroid:
|
||||||
|
+start_hints:
|
||||||
|
- Gravity Suit
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, if the `start_location` option rolls `landing_site`, only a starting hint for Morph Ball will be created.
|
In this example, if the `start_location` option rolls `landing_site`, only a starting hint for Morph Ball will be
|
||||||
If `aqueduct` is rolled, a starting hint for Gravity Suit will also be created alongside the hint for Morph Ball.
|
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
|
Note that for lists, items can only be added, not removed or replaced. For dicts, defining a value for a present key
|
||||||
replace that value within the dict.
|
will replace that value within the dict.
|
||||||
|
|
Loading…
Reference in New Issue