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
 | 
			
		||||
from collections import Counter
 | 
			
		||||
from typing import Any, Dict, Tuple, Union
 | 
			
		||||
from itertools import chain
 | 
			
		||||
 | 
			
		||||
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}')
 | 
			
		||||
    cleaned_weights = {}
 | 
			
		||||
    for option in new_weights:
 | 
			
		||||
        option_name = option.lstrip("+")
 | 
			
		||||
        option_name = option.lstrip("+-")
 | 
			
		||||
        if option.startswith("+") and option_name in weights:
 | 
			
		||||
            cleaned_value = weights[option_name]
 | 
			
		||||
            new_value = new_weights[option]
 | 
			
		||||
            if isinstance(new_value, (set, dict)):
 | 
			
		||||
            if isinstance(new_value, set):
 | 
			
		||||
                cleaned_value.update(new_value)
 | 
			
		||||
            elif isinstance(new_value, list):
 | 
			
		||||
                cleaned_value.extend(new_value)
 | 
			
		||||
            elif isinstance(new_value, dict):
 | 
			
		||||
                cleaned_value = dict(Counter(cleaned_value) + Counter(new_value))
 | 
			
		||||
            else:
 | 
			
		||||
                raise Exception(f"Cannot apply merge to non-dict, set, or list type {option_name},"
 | 
			
		||||
                                f" received {type(new_value).__name__}.")
 | 
			
		||||
            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:
 | 
			
		||||
            cleaned_weights[option_name] = new_weights[option]
 | 
			
		||||
    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]
 | 
			
		||||
    game_weights = weights[ret.game]
 | 
			
		||||
 | 
			
		||||
    if any(weight.startswith("+") for weight in game_weights) or \
 | 
			
		||||
       any(weight.startswith("+") for weight in weights):
 | 
			
		||||
        raise Exception(f"Merge tag cannot be used outside of trigger contexts.")
 | 
			
		||||
    for weight in chain(game_weights, weights):
 | 
			
		||||
        if weight.startswith("+"):
 | 
			
		||||
            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:
 | 
			
		||||
        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_1"], ["string", "string_2"])
 | 
			
		||||
        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.assertNotIn("option_f", new_weights["dict_2"])
 | 
			
		||||
        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`
 | 
			
		||||
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
 | 
			
		||||
the activated trigger to the already present equivalents in the game options.
 | 
			
		||||
## Adding or Removing from a List, Set, or Dict Option
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
Super Metroid:
 | 
			
		||||
  start_location: 
 | 
			
		||||
| 
						 | 
				
			
			@ -134,18 +145,21 @@ Super Metroid:
 | 
			
		|||
    aqueduct: 50
 | 
			
		||||
  start_hints:
 | 
			
		||||
    - Morph Ball
 | 
			
		||||
triggers:
 | 
			
		||||
  - option_category: Super Metroid
 | 
			
		||||
    option_name: start_location
 | 
			
		||||
    option_result: aqueduct
 | 
			
		||||
    options:
 | 
			
		||||
      Super Metroid:
 | 
			
		||||
        +start_hints:
 | 
			
		||||
          - Gravity Suit
 | 
			
		||||
  start_inventory:
 | 
			
		||||
    Power Bombs: 1
 | 
			
		||||
  triggers:
 | 
			
		||||
    - option_category: Super Metroid
 | 
			
		||||
      option_name: start_location
 | 
			
		||||
      option_result: aqueduct
 | 
			
		||||
      options:
 | 
			
		||||
        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.
 | 
			
		||||
If `aqueduct` is rolled, a starting hint for Gravity Suit will also be created alongside the hint for Morph Ball.
 | 
			
		||||
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.
 | 
			
		||||
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…
	
		Reference in New Issue