55
66from Options import (
77 Choice , Toggle , DefaultOnToggle , OptionSet , Range ,
8- PerGameCommonOptions , Option , VerifyKeys , StartInventory ,
8+ PerGameCommonOptions , VerifyKeys , StartInventory ,
99 is_iterable_except_str , OptionGroup , Visibility , ItemDict ,
10- Accessibility , ProgressionBalancing
10+ OptionCounter ,
1111)
1212from Utils import get_fuzzy_results
1313from BaseClasses import PlandoOptions
14- from .item import item_names , item_tables
15- from .item .item_groups import kerrigan_active_abilities , kerrigan_passives , nova_weapons , nova_gadgets
14+ from .item import item_names , item_tables , item_groups
1615from .mission_tables import (
1716 SC2Campaign , SC2Mission , lookup_name_to_mission , MissionPools , get_missions_with_any_flags_in_list ,
1817 campaign_mission_table , SC2Race , MissionFlag
@@ -700,7 +699,7 @@ class KerriganMaxActiveAbilities(Range):
700699 """
701700 display_name = "Kerrigan Maximum Active Abilities"
702701 range_start = 0
703- range_end = len (kerrigan_active_abilities )
702+ range_end = len (item_groups . kerrigan_active_abilities )
704703 default = range_end
705704
706705
@@ -711,7 +710,7 @@ class KerriganMaxPassiveAbilities(Range):
711710 """
712711 display_name = "Kerrigan Maximum Passive Abilities"
713712 range_start = 0
714- range_end = len (kerrigan_passives )
713+ range_end = len (item_groups . kerrigan_passives )
715714 default = range_end
716715
717716
@@ -829,7 +828,7 @@ class SpearOfAdunMaxAutocastAbilities(Range):
829828 """
830829 display_name = "Spear of Adun Maximum Passive Abilities"
831830 range_start = 0
832- range_end = sum (item . quantity for item_name , item in item_tables . get_full_item_list (). items () if item_name in item_tables . spear_of_adun_castable_passives )
831+ range_end = sum (item_tables . item_table [ item_name ]. quantity for item_name in item_groups . spear_of_adun_passives )
833832 default = range_end
834833
835834
@@ -883,7 +882,7 @@ class NovaMaxWeapons(Range):
883882 """
884883 display_name = "Nova Maximum Weapons"
885884 range_start = 0
886- range_end = len (nova_weapons )
885+ range_end = len (item_groups . nova_weapons )
887886 default = range_end
888887
889888
@@ -897,7 +896,7 @@ class NovaMaxGadgets(Range):
897896 """
898897 display_name = "Nova Maximum Gadgets"
899898 range_start = 0
900- range_end = len (nova_gadgets )
899+ range_end = len (item_groups . nova_gadgets )
901900 default = range_end
902901
903902
@@ -932,33 +931,48 @@ class TakeOverAIAllies(Toggle):
932931 display_name = "Take Over AI Allies"
933932
934933
935- class Sc2ItemDict (Option [ Dict [ str , int ]] , VerifyKeys , Mapping [str , int ]):
936- """A branch of ItemDict that supports item counts of 0 """
934+ class Sc2ItemDict (OptionCounter , VerifyKeys , Mapping [str , int ]):
935+ """A branch of ItemDict that supports negative item counts"""
937936 default = {}
938937 supports_weighting = False
939938 verify_item_name = True
940939 # convert_name_groups = True
941940 display_name = 'Unnamed dictionary'
942- minimum_value : int = 0
941+ # Note(phaneros): Limiting minimum to -1 means that if two triggers add -1 to the same item,
942+ # the validation fails. So give trigger people space to stack a bunch of triggers.
943+ min : int = - 1000
944+ max : int = 1000
945+ valid_keys = set (item_tables .item_table ) | set (item_groups .item_name_groups )
943946
944- def __init__ (self , value : Dict [str , int ]):
947+ def __init__ (self , value : dict [str , int ]):
945948 self .value = {key : val for key , val in value .items ()}
946949
947950 @classmethod
948- def from_any (cls , data : Union [ List [ str ], Dict [str , int ] ]) -> 'Sc2ItemDict' :
951+ def from_any (cls , data : list [ str ] | dict [str , int ]) -> 'Sc2ItemDict' :
949952 if isinstance (data , list ):
950- # This is a little default that gets us backwards compatibility with lists.
951- # It doesn't play nice with trigger merging dicts and lists together, though, so best not to advertise it overmuch.
952- data = {item : 0 for item in data }
953+ raise ValueError (
954+ f"{ cls .display_name } : Cannot convert from list. "
955+ f"Use dict syntax (no dashes, 'value: number' synax)."
956+ )
953957 if isinstance (data , dict ):
954958 for key , value in data .items ():
955959 if not isinstance (value , int ):
956- raise ValueError (f"Invalid type in '{ cls .display_name } ': element '{ key } ' maps to '{ value } ', expected an integer" )
957- if value < cls .minimum_value :
958- raise ValueError (f"Invalid value for '{ cls .display_name } ': element '{ key } ' maps to { value } , which is less than the minimum ({ cls .minimum_value } )" )
960+ raise ValueError (
961+ f"Invalid type in '{ cls .display_name } ': "
962+ f"element '{ key } ' maps to '{ value } ', expected an integer"
963+ )
964+ if value < cls .min :
965+ raise ValueError (
966+ f"Invalid value for '{ cls .display_name } ': "
967+ f"element '{ key } ' maps to { value } , which is less than the minimum ({ cls .min } )"
968+ )
969+ if value > cls .max :
970+ raise ValueError (f"Invalid value for '{ cls .display_name } ': "
971+ f"element '{ key } ' maps to { value } , which is greater than the maximum ({ cls .max } )"
972+ )
959973 return cls (data )
960974 else :
961- raise NotImplementedError (f"Cannot Convert from non-dictionary, got { type (data )} " )
975+ raise NotImplementedError (f"{ cls . display_name } : Cannot convert from non-dictionary, got { type (data )} " )
962976
963977 def verify (self , world : Type ['World' ], player_name : str , plando_options : PlandoOptions ) -> None :
964978 """Overridden version of function from Options.VerifyKeys for a better error message"""
@@ -974,15 +988,16 @@ def verify(self, world: Type['World'], player_name: str, plando_options: PlandoO
974988 self .value = new_value
975989 for item_name in self .value :
976990 if item_name not in world .item_names :
977- from .item import item_groups
978991 picks = get_fuzzy_results (
979992 item_name ,
980993 list (world .item_names ) + list (item_groups .ItemGroupNames .get_all_group_names ()),
981994 limit = 1 ,
982995 )
983- raise Exception (f"Item { item_name } from option { self } "
984- f"is not a valid item name from { world .game } . "
985- f"Did you mean '{ picks [0 ][0 ]} ' ({ picks [0 ][1 ]} % sure)" )
996+ raise Exception (
997+ f"Item { item_name } from option { self } "
998+ f"is not a valid item name from { world .game } . "
999+ f"Did you mean '{ picks [0 ][0 ]} ' ({ picks [0 ][1 ]} % sure)"
1000+ )
9861001
9871002 def get_option_name (self , value ):
9881003 return ", " .join (f"{ key } : { v } " for key , v in value .items ())
@@ -998,25 +1013,25 @@ def __len__(self) -> int:
9981013
9991014
10001015class Sc2StartInventory (Sc2ItemDict ):
1001- """Start with these items."""
1016+ """Start with these items. Use an amount of -1 to start with all copies of an item. """
10021017 display_name = StartInventory .display_name
10031018
10041019
10051020class LockedItems (Sc2ItemDict ):
10061021 """Guarantees that these items will be unlockable, in the amount specified.
1007- Specify an amount of 0 to lock all copies of an item."""
1022+ Specify an amount of -1 to lock all copies of an item."""
10081023 display_name = "Locked Items"
10091024
10101025
10111026class ExcludedItems (Sc2ItemDict ):
10121027 """Guarantees that these items will not be unlockable, in the amount specified.
1013- Specify an amount of 0 to exclude all copies of an item."""
1028+ Specify an amount of -1 to exclude all copies of an item."""
10141029 display_name = "Excluded Items"
10151030
10161031
10171032class UnexcludedItems (Sc2ItemDict ):
10181033 """Undoes an item exclusion; useful for whitelisting or fine-tuning a category.
1019- Specify an amount of 0 to unexclude all copies of an item."""
1034+ Specify an amount of -1 to unexclude all copies of an item."""
10201035 display_name = "Unexcluded Items"
10211036
10221037
0 commit comments