tiger_lib/
scopes.rs

1//! The core [`Scopes`] type which tracks our knowledge about the types of in-game values.
2
3use std::fmt::{Display, Formatter};
4
5use bitflags::bitflags;
6
7use crate::context::ScopeContext;
8use crate::everything::Everything;
9use crate::game::Game;
10use crate::helpers::{camel_case_to_separated_words, display_choices, snake_case_to_camel_case};
11use crate::item::Item;
12use crate::report::{err, ErrorKey};
13use crate::token::Token;
14
15/// vic3 and ck3 need more than 64 bits, but the others don't.
16#[cfg(any(feature = "vic3", feature = "ck3"))]
17type ScopesBits = u128;
18#[cfg(not(any(feature = "vic3", feature = "ck3")))]
19type ScopesBits = u64;
20
21bitflags! {
22    /// This type represents our knowledge about the set of scope types that a script value can
23    /// have. In most cases it's narrowed down to a single scope type, but not always.
24    ///
25    /// The available scope types depend on the game.
26    /// They are listed in `event_scopes.log` from the game data dumps.
27    // LAST UPDATED CK3 VERSION 1.16.0
28    // LAST UPDATED VIC3 VERSION 1.8.1
29    // LAST UPDATED IR VERSION 2.0.4
30    //
31    // Each scope type gets one bitflag. In order to keep the bit count down, scope types from
32    // the different games have overlapping bitflags. Therefore, scope types from different games
33    // should be kept carefully separated.
34    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
35    #[rustfmt::skip] // having the cfg and the flag on one line is much more readable
36    pub struct Scopes: ScopesBits {
37        // Generic scope types
38        const None = 0x0000_0001;
39        const Value = 0x0000_0002;
40        const Bool = 0x0000_0004;
41        const Flag = 0x0000_0008;
42
43        // Scope types shared by multiple games
44
45        #[cfg(any(feature = "vic3", feature = "imperator"))]
46        const Color = 0x0000_0010;
47        #[cfg(any(feature = "vic3", feature = "imperator", feature = "hoi4"))]
48        const Country = 0x0000_0020;
49        const Character = 0x0000_0040;
50        #[cfg(any(feature = "ck3", feature = "vic3", feature = "imperator"))]
51        const Culture = 0x0000_0080;
52        #[cfg(any(feature = "ck3", feature = "vic3", feature = "imperator"))]
53        const Province = 0x0000_0100;
54        #[cfg(any(feature = "vic3", feature = "imperator"))]
55        const Pop = 0x0000_0200;
56        #[cfg(any(feature = "vic3", feature = "imperator"))]
57        const Party = 0x0000_0400;
58        #[cfg(any(feature = "ck3", feature = "vic3", feature = "imperator"))]
59        const Religion = 0x0000_0800;
60        #[cfg(any(feature = "vic3", feature = "imperator", feature = "hoi4"))]
61        const State = 0x0000_1000;
62        #[cfg(any(feature = "ck3", feature = "vic3", feature = "imperator"))]
63        const War = 0x0000_2000;
64        #[cfg(any(feature = "vic3", feature = "hoi4"))]
65        const StrategicRegion = 0x0000_4000;
66
67        // Scope types for CK3
68        #[cfg(feature = "ck3")] const Accolade = 0x0001_0000;
69        #[cfg(feature = "ck3")] const AccoladeType = 0x0002_0000;
70        #[cfg(feature = "ck3")] const Activity = 0x0004_0000;
71        #[cfg(feature = "ck3")] const ActivityType = 0x0008_0000;
72        #[cfg(feature = "ck3")] const Army = 0x0010_0000;
73        #[cfg(feature = "ck3")] const Artifact = 0x0020_0000;
74        #[cfg(feature = "ck3")] const CasusBelli = 0x0040_0000;
75        #[cfg(feature = "ck3")] const CharacterMemory = 0x0080_0000;
76        #[cfg(feature = "ck3")] const Combat = 0x0100_0000;
77        #[cfg(feature = "ck3")] const CombatSide = 0x0200_0000;
78        #[cfg(feature = "ck3")] const CouncilTask = 0x0400_0000;
79        #[cfg(feature = "ck3")] const CulturePillar = 0x0800_0000;
80        #[cfg(feature = "ck3")] const CultureTradition = 0x1000_0000;
81        #[cfg(feature = "ck3")] const Decision = 0x2000_0000;
82        #[cfg(feature = "ck3")] const Doctrine = 0x4000_0000;
83        #[cfg(feature = "ck3")] const Dynasty = 0x8000_0000;
84        #[cfg(feature = "ck3")] const DynastyHouse = 0x0000_0001_0000_0000;
85        #[cfg(feature = "ck3")] const Faction = 0x0000_0002_0000_0000;
86        #[cfg(feature = "ck3")] const Faith = 0x0000_0004_0000_0000;
87        #[cfg(feature = "ck3")] const GovernmentType = 0x0000_0008_0000_0000;
88        #[cfg(feature = "ck3")] const GreatHolyWar = 0x0000_0010_0000_0000;
89        #[cfg(feature = "ck3")] const HolyOrder = 0x0000_0020_0000_0000;
90        #[cfg(feature = "ck3")] const Inspiration = 0x0000_0040_0000_0000;
91        #[cfg(feature = "ck3")] const LandedTitle = 0x0000_0080_0000_0000;
92        #[cfg(feature = "ck3")] const MercenaryCompany = 0x0000_0100_0000_0000;
93        #[cfg(feature = "ck3")] const Scheme = 0x0000_0200_0000_0000;
94        #[cfg(feature = "ck3")] const Secret = 0x0000_0400_0000_0000;
95        #[cfg(feature = "ck3")] const StoryCycle = 0x0000_0800_0000_0000;
96        #[cfg(feature = "ck3")] const Struggle = 0x0000_1000_0000_0000;
97        #[cfg(feature = "ck3")] const TitleAndVassalChange = 0x0000_2000_0000_0000;
98        #[cfg(feature = "ck3")] const Trait = 0x0000_4000_0000_0000;
99        #[cfg(feature = "ck3")] const TravelPlan = 0x0000_8000_0000_0000;
100        #[cfg(feature = "ck3")] const VassalContract = 0x0001_0000_0000_0000;
101        #[cfg(feature = "ck3")] const VassalObligationLevel = 0x0002_0000_0000_0000;
102        // CK3 1.11
103        #[cfg(feature = "ck3")] const HoldingType = 0x0004_0000_0000_0000;
104        #[cfg(feature = "ck3")] const TaxSlot = 0x0008_0000_0000_0000;
105        // CK3 1.12
106        #[cfg(feature = "ck3")] const EpidemicType = 0x0010_0000_0000_0000;
107        #[cfg(feature = "ck3")] const Epidemic = 0x0020_0000_0000_0000;
108        #[cfg(feature = "ck3")] const LegendType = 0x0040_0000_0000_0000;
109        #[cfg(feature = "ck3")] const Legend = 0x0080_0000_0000_0000;
110        #[cfg(feature = "ck3")] const GeographicalRegion = 0x0100_0000_0000_0000;
111        // CK3 1.13
112        #[cfg(feature = "ck3")] const Domicile = 0x0200_0000_0000_0000;
113        #[cfg(feature = "ck3")] const AgentSlot = 0x0400_0000_0000_0000;
114        #[cfg(feature = "ck3")] const TaskContract = 0x0800_0000_0000_0000;
115        #[cfg(feature = "ck3")] const TaskContractType = 0x1000_0000_0000_0000;
116        #[cfg(feature = "ck3")] const Regiment = 0x2000_0000_0000_0000;
117        #[cfg(feature = "ck3")] const CasusBelliType = 0x4000_0000_0000_0000;
118        // CK3 1.15
119        #[cfg(feature = "ck3")] const CourtPosition = 0x8000_0000_0000_0000;
120        #[cfg(feature = "ck3")] const CourtPositionType = 0x0000_0000_0000_0001_0000_0000_0000_0000;
121        // CK3 1.16
122        #[cfg(feature = "ck3")] const Situation = 0x0000_0000_0000_0002_0000_0000_0000_0000;
123        #[cfg(feature = "ck3")] const SituationParticipantGroup = 0x0000_0000_0000_0004_0000_0000_0000_0000;
124        #[cfg(feature = "ck3")] const SituationSubRegion = 0x0000_0000_0000_0008_0000_0000_0000_0000;
125        #[cfg(feature = "ck3")] const Confederation = 0x0000_0000_0000_0010_0000_0000_0000_0000;
126
127
128        #[cfg(feature = "vic3")] const Battle = 0x0001_0000;
129        #[cfg(feature = "vic3")] const BattleSide = 0x0002_0000;
130        #[cfg(feature = "vic3")] const Building = 0x0004_0000;
131        #[cfg(feature = "vic3")] const BuildingType = 0x0008_0000;
132        #[cfg(feature = "vic3")] const CanalType = 0x0010_0000;
133        #[cfg(feature = "vic3")] const CivilWar = 0x0020_0000;
134        #[cfg(feature = "vic3")] const CulturalCommunity = 0x0040_0000;
135        #[cfg(feature = "vic3")] const NewCombatUnit = 0x0080_0000;
136        #[cfg(feature = "vic3")] const CommanderOrderType = 0x0100_0000;
137        #[cfg(feature = "vic3")] const CountryCreation = 0x0200_0000;
138        #[cfg(feature = "vic3")] const CountryDefinition = 0x0400_0000;
139        #[cfg(feature = "vic3")] const CountryFormation = 0x0800_0000;
140        #[cfg(feature = "vic3")] const Decree = 0x1000_0000;
141        #[cfg(feature = "vic3")] const DiplomaticAction = 0x2000_0000;
142        #[cfg(feature = "vic3")] const DiplomaticPact = 0x4000_0000;
143        #[cfg(feature = "vic3")] const DiplomaticPlay = 0x8000_0000;
144        #[cfg(feature = "vic3")] const DiplomaticRelations = 0x0000_0001_0000_0000;
145        #[cfg(feature = "vic3")] const Front = 0x0000_0002_0000_0000;
146        #[cfg(feature = "vic3")] const Goods = 0x0000_0004_0000_0000;
147        #[cfg(feature = "vic3")] const Hq = 0x0000_0008_0000_0000;
148        #[cfg(feature = "vic3")] const Ideology = 0x0000_0010_0000_0000;
149        #[cfg(feature = "vic3")] const Institution = 0x0000_0020_0000_0000;
150        #[cfg(feature = "vic3")] const InstitutionType = 0x0000_0040_0000_0000;
151        #[cfg(feature = "vic3")] const InterestMarker = 0x0000_0080_0000_0000;
152        #[cfg(feature = "vic3")] const InterestGroup = 0x0000_0100_0000_0000;
153        #[cfg(feature = "vic3")] const InterestGroupTrait = 0x0000_0200_0000_0000;
154        #[cfg(feature = "vic3")] const InterestGroupType = 0x0000_0400_0000_0000;
155        #[cfg(feature = "vic3")] const JournalEntry = 0x0000_0800_0000_0000;
156        #[cfg(feature = "vic3")] const Law = 0x0000_1000_0000_0000;
157        #[cfg(feature = "vic3")] const LawType = 0x0000_2000_0000_0000;
158        #[cfg(feature = "vic3")] const Market = 0x0000_4000_0000_0000;
159        #[cfg(feature = "vic3")] const MarketGoods = 0x0000_8000_0000_0000;
160        #[cfg(feature = "vic3")] const Objective = 0x0001_0000_0000_0000;
161        #[cfg(feature = "vic3")] const PoliticalMovement = 0x0002_0000_0000_0000;
162        #[cfg(feature = "vic3")] const PopType = 0x0004_0000_0000_0000;
163        #[cfg(feature = "vic3")] const ShippingLanes = 0x0008_0000_0000_0000;
164        #[cfg(feature = "vic3")] const StateRegion = 0x0010_0000_0000_0000;
165        #[cfg(feature = "vic3")] const StateTrait = 0x0020_0000_0000_0000;
166        // Available bitflag 0x0040_0000_0000_0000;
167        #[cfg(feature = "vic3")] const Technology = 0x0080_0000_0000_0000;
168        #[cfg(feature = "vic3")] const TechnologyStatus = 0x0100_0000_0000_0000;
169        #[cfg(feature = "vic3")] const Theater = 0x0200_0000_0000_0000;
170        // #[cfg(feature = "vic3")] const TradeRoute = 0x0400_0000_0000_0000;
171        #[cfg(feature = "vic3")] const CombatUnitType = 0x1000_0000_0000_0000;
172        #[cfg(feature = "vic3")] const MilitaryFormation = 0x2000_0000_0000_0000;
173        #[cfg(feature = "vic3")] const Sway = 0x4000_0000_0000_0000;
174        #[cfg(feature = "vic3")] const StateGoods = 0x8000_0000_0000_0000;
175        #[cfg(feature = "vic3")] const DiplomaticDemand = 0x0000_0000_0000_0001_0000_0000_0000_0000;
176        #[cfg(feature = "vic3")] const Company = 0x0000_0000_0000_0002_0000_0000_0000_0000;
177        #[cfg(feature = "vic3")] const CompanyType = 0x0000_0000_0000_0004_0000_0000_0000_0000;
178        #[cfg(feature = "vic3")] const TravelNode = 0x0000_0000_0000_0008_0000_0000_0000_0000;
179        #[cfg(feature = "vic3")] const TravelNodeDefinition = 0x0000_0000_0000_0010_0000_0000_0000_0000;
180        #[cfg(feature = "vic3")] const TravelConnection = 0x0000_0000_0000_0020_0000_0000_0000_0000;
181        #[cfg(feature = "vic3")] const TravelConnectionDefinition = 0x0000_0000_0000_0040_0000_0000_0000_0000;
182        #[cfg(feature = "vic3")] const Invasion = 0x0000_0000_0000_0080_0000_0000_0000_0000;
183        #[cfg(feature = "vic3")] const MobilizationOption = 0x0000_0000_0000_0100_0000_0000_0000_0000;
184        #[cfg(feature = "vic3")] const PowerBlocPrincipleGroup = 0x0000_0000_0000_0200_0000_0000_0000_0000;
185        #[cfg(feature = "vic3")] const DiplomaticPlayType = 0x0000_0000_0000_0400_0000_0000_0000_0000;
186        #[cfg(feature = "vic3")] const DiplomaticCatalyst = 0x0000_0000_0000_0800_0000_0000_0000_0000;
187        #[cfg(feature = "vic3")] const DiplomaticCatalystType = 0x0000_0000_0000_1000_0000_0000_0000_0000;
188        #[cfg(feature = "vic3")] const DiplomaticCatalystCategory = 0x0000_0000_0000_2000_0000_0000_0000_0000;
189        #[cfg(feature = "vic3")] const PoliticalLobby = 0x0000_0000_0000_4000_0000_0000_0000_0000;
190        #[cfg(feature = "vic3")] const PoliticalLobbyType = 0x0000_0000_0000_8000_0000_0000_0000_0000;
191        #[cfg(feature = "vic3")] const PoliticalLobbyAppeasement = 0x0000_0000_0001_0000_0000_0000_0000_0000;
192        #[cfg(feature = "vic3")] const PowerBloc = 0x0000_0000_0002_0000_0000_0000_0000_0000;
193        #[cfg(feature = "vic3")] const PowerBlocIdentity = 0x0000_0000_0004_0000_0000_0000_0000_0000;
194        #[cfg(feature = "vic3")] const PowerBlocPrinciple = 0x0000_0000_0008_0000_0000_0000_0000_0000;
195        #[cfg(feature = "vic3")] const HarvestCondition = 0x0000_0000_0010_0000_0000_0000_0000_0000;
196        #[cfg(feature = "vic3")] const PoliticalMovementType = 0x0000_0000_0020_0000_0000_0000_0000_0000;
197        #[cfg(feature = "vic3")] const HarvestConditionType = 0x0000_0000_0040_0000_0000_0000_0000_0000;
198        #[cfg(feature = "vic3")] const TreatyArticle = 0x0000_0000_0080_0000_0000_0000_0000_0000;
199        #[cfg(feature = "vic3")] const TreatyOptions = 0x0000_0000_0100_0000_0000_0000_0000_0000;
200        #[cfg(feature = "vic3")] const TreatyArticleOptions = 0x0000_0000_0200_0000_0000_0000_0000_0000;
201        #[cfg(feature = "vic3")] const Treaty = 0x0000_0000_0400_0000_0000_0000_0000_0000;
202        #[cfg(feature = "vic3")] const BuildingGroup = 0x0000_0000_0800_0000_0000_0000_0000_0000;
203
204        #[cfg(feature = "imperator")] const Area = 0x0001_0000;
205        #[cfg(feature = "imperator")] const CountryCulture = 0x0002_0000;
206        #[cfg(feature = "imperator")] const CultureGroup = 0x0004_0000;
207        #[cfg(feature = "imperator")] const Deity = 0x0008_0000;
208        #[cfg(feature = "imperator")] const Family = 0x0010_0000;
209        #[cfg(feature = "imperator")] const Governorship = 0x0020_0000;
210        #[cfg(feature = "imperator")] const GreatWork = 0x0040_0000;
211        #[cfg(feature = "imperator")] const Job = 0x0080_0000;
212        #[cfg(feature = "imperator")] const Legion = 0x0100_0000;
213        #[cfg(feature = "imperator")] const LevyTemplate = 0x0200_0000;
214        #[cfg(feature = "imperator")] const Region = 0x0400_0000;
215        #[cfg(feature = "imperator")] const Siege = 0x0800_0000;
216        #[cfg(feature = "imperator")] const SubUnit = 0x1000_0000;
217        #[cfg(feature = "imperator")] const Treasure = 0x2000_0000;
218        #[cfg(feature = "imperator")] const Unit = 0x4000_0000;
219
220        #[cfg(feature = "hoi4")] const Ace = 0x0001_0000;
221        #[cfg(feature = "hoi4")] const Combatant = 0x0002_0000;
222        #[cfg(feature = "hoi4")] const Division = 0x0004_0000;
223        #[cfg(feature = "hoi4")] const IndustrialOrg = 0x0008_0000;
224        #[cfg(feature = "hoi4")] const Operation = 0x0010_0000;
225        #[cfg(feature = "hoi4")] const PurchaseContract = 0x0020_0000;
226        #[cfg(feature = "hoi4")] const RaidInstance = 0x0040_0000;
227        #[cfg(feature = "hoi4")] const SpecialProject = 0x0080_0000;
228        // Available bitflag 0x0100_0000;
229        // These two "combined" ones represent the odd scopes created for events.
230        #[cfg(feature = "hoi4")] const CombinedCountryAndState = 0x0200_0000;
231        #[cfg(feature = "hoi4")] const CombinedCountryAndCharacter = 0x0400_0000;
232    }
233}
234
235// These have to be expressed a bit awkwardly because the binary operators are not `const`.
236// TODO: Scopes::all() returns a too-large set if multiple features are enabled.
237impl Scopes {
238    pub const fn non_primitive() -> Scopes {
239        Scopes::all()
240            .difference(Scopes::None.union(Scopes::Value).union(Scopes::Bool).union(Scopes::Flag))
241    }
242
243    pub const fn primitive() -> Scopes {
244        Scopes::Value.union(Scopes::Bool).union(Scopes::Flag)
245    }
246
247    pub const fn all_but_none() -> Scopes {
248        Scopes::all().difference(Scopes::None)
249    }
250
251    /// Read a scope type in string form and return it as a [`Scopes`] value.
252    pub fn from_snake_case(s: &str) -> Option<Scopes> {
253        #[cfg(feature = "ck3")]
254        if Game::is_ck3() {
255            // Deal with some exceptions to the general pattern
256            match s {
257                "ghw" => return Some(Scopes::GreatHolyWar),
258                "story" => return Some(Scopes::StoryCycle),
259                "great_holy_war" | "story_cycle" => return None,
260                _ => (),
261            }
262        }
263
264        Scopes::from_name(&snake_case_to_camel_case(s))
265    }
266
267    /// Similar to `from_snake_case`, but allows multiple scopes separated by `|`
268    /// Returns None if any of the conversions fail.
269    pub fn from_snake_case_multi(s: &str) -> Option<Scopes> {
270        let mut scopes = Scopes::empty();
271        for part in s.split('|') {
272            if let Some(scope) = Scopes::from_snake_case(part) {
273                scopes |= scope;
274            } else {
275                return None;
276            }
277        }
278        // If `scopes` is still empty then probably `s` was empty.
279        // Remember that `Scopes::empty()` is different from a bitfield containing `Scopes::None`.
280        if scopes == Scopes::empty() {
281            return None;
282        }
283        Some(scopes)
284    }
285}
286
287impl Display for Scopes {
288    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
289        if *self == Scopes::all() {
290            write!(f, "any scope")
291        } else if *self == Scopes::primitive() {
292            write!(f, "any primitive scope")
293        } else if *self == Scopes::non_primitive() {
294            write!(f, "non-primitive scope")
295        } else if *self == Scopes::all_but_none() {
296            write!(f, "any except none scope")
297        } else {
298            let mut vec = Vec::new();
299            for (name, _) in self.iter_names() {
300                vec.push(camel_case_to_separated_words(name));
301            }
302            let vec: Vec<&str> = vec.iter().map(String::as_ref).collect();
303            display_choices(f, &vec, "or")
304        }
305    }
306}
307
308/// A description of the constraints on a value with a prefix such as `var:` or `list_size:`
309#[derive(Copy, Clone, Debug, Eq, PartialEq)]
310pub enum ArgumentValue {
311    /// The value must be an expression that resolves to a scope object of the given type.
312    #[cfg(any(feature = "ck3", feature = "vic3"))]
313    Scope(Scopes),
314    /// The value must be the name of an item of the given item type.
315    Item(Item),
316    /// The value can be either a Scope or an Item
317    #[cfg(any(feature = "ck3", feature = "vic3"))]
318    ScopeOrItem(Scopes, Item),
319    /// The value can be a trait name or `trait|track`.
320    #[cfg(feature = "ck3")]
321    TraitTrack,
322    /// The value must be the name of a modif
323    #[cfg(any(feature = "vic3", feature = "imperator"))]
324    Modif,
325    /// The value must be a single word
326    #[cfg(any(feature = "vic3", feature = "ck3"))]
327    Identifier(&'static str),
328    /// The value can be anything
329    UncheckedValue,
330}
331
332/// Look up an "event link", which is a script token that looks up something related
333/// to a scope value and returns another scope value.
334///
335/// `name` is the token. `inscopes` is the known scope context of this token.
336/// `inscopes` is only used for some special-case event links whose output scope type
337/// depends on their input scope type.
338///
339/// Returns a pair of `Scopes`. The first is the scope types this token can accept as input,
340/// and the second is the scope types it may return.
341#[allow(unused_variables)] // inscopes is only used for vic3
342pub fn scope_to_scope(name: &Token, inscopes: Scopes) -> Option<(Scopes, Scopes)> {
343    let scope_to_scope = match Game::game() {
344        #[cfg(feature = "ck3")]
345        Game::Ck3 => crate::ck3::tables::targets::scope_to_scope,
346        #[cfg(feature = "vic3")]
347        Game::Vic3 => crate::vic3::tables::targets::scope_to_scope,
348        #[cfg(feature = "imperator")]
349        Game::Imperator => crate::imperator::tables::targets::scope_to_scope,
350        #[cfg(feature = "hoi4")]
351        Game::Hoi4 => crate::hoi4::tables::targets::scope_to_scope,
352    };
353    let scope_to_scope_removed = match Game::game() {
354        #[cfg(feature = "ck3")]
355        Game::Ck3 => crate::ck3::tables::targets::scope_to_scope_removed,
356        #[cfg(feature = "vic3")]
357        Game::Vic3 => crate::vic3::tables::targets::scope_to_scope_removed,
358        #[cfg(feature = "imperator")]
359        Game::Imperator => crate::imperator::tables::targets::scope_to_scope_removed,
360        #[cfg(feature = "hoi4")]
361        Game::Hoi4 => crate::hoi4::tables::targets::scope_to_scope_removed,
362    };
363
364    let name_lc = name.as_str().to_ascii_lowercase();
365    if let scopes @ Some((from, _)) = scope_to_scope(&name_lc) {
366        #[cfg(feature = "vic3")]
367        if Game::is_vic3() && name_lc == "type" {
368            // Special case for "type" because it goes from specific scope types to specific
369            // other scope types.
370            let mut outscopes = Scopes::empty();
371            if inscopes.contains(Scopes::Building) {
372                outscopes |= Scopes::BuildingType;
373            }
374            if inscopes.contains(Scopes::Company) {
375                outscopes |= Scopes::CompanyType;
376            }
377            if inscopes.contains(Scopes::DiplomaticPlay) {
378                outscopes |= Scopes::DiplomaticPlayType;
379            }
380            if inscopes.contains(Scopes::DiplomaticCatalyst) {
381                outscopes |= Scopes::DiplomaticCatalystType;
382            }
383            if inscopes.contains(Scopes::PoliticalLobby) {
384                outscopes |= Scopes::PoliticalLobbyType;
385            }
386            if inscopes.contains(Scopes::Institution) {
387                outscopes |= Scopes::InstitutionType;
388            }
389            if inscopes.contains(Scopes::InterestGroup) {
390                outscopes |= Scopes::InterestGroupType;
391            }
392            if inscopes.contains(Scopes::Law) {
393                outscopes |= Scopes::LawType;
394            }
395            if inscopes.contains(Scopes::PoliticalMovement) {
396                outscopes |= Scopes::PoliticalMovementType;
397            }
398            if inscopes.contains(Scopes::HarvestCondition) {
399                outscopes |= Scopes::HarvestConditionType;
400            }
401            if !outscopes.is_empty() {
402                return Some((from, outscopes));
403            }
404        }
405        scopes
406    } else if let Some((version, explanation)) = scope_to_scope_removed(&name_lc) {
407        let msg = format!("`{name}` was removed in {version}");
408        err(ErrorKey::Removed).strong().msg(msg).info(explanation).loc(name).push();
409        Some((Scopes::all(), Scopes::all_but_none()))
410    } else {
411        None
412    }
413}
414
415/// Look up a prefixed token that is used to look up items in the game database.
416///
417/// For example, `character:alexander_the_great` to fetch that character as a scope value.
418///
419/// Some prefixes have an input scope, and they look up something related to the input scope value.
420///
421/// Returns a pair of `Scopes` and the type of argument it accepts.
422/// The first `Scopes` is the scope types this token can accept as input, and the second one is
423/// the scope types it may return. The first will be `Scopes::None` if it needs no input.
424pub fn scope_prefix(prefix: &Token) -> Option<(Scopes, Scopes, ArgumentValue)> {
425    let scope_prefix = match Game::game() {
426        #[cfg(feature = "ck3")]
427        Game::Ck3 => crate::ck3::tables::targets::scope_prefix,
428        #[cfg(feature = "vic3")]
429        Game::Vic3 => crate::vic3::tables::targets::scope_prefix,
430        #[cfg(feature = "imperator")]
431        Game::Imperator => crate::imperator::tables::targets::scope_prefix,
432        #[cfg(feature = "hoi4")]
433        Game::Hoi4 => crate::hoi4::tables::targets::scope_prefix,
434    };
435    let prefix_lc = prefix.as_str().to_ascii_lowercase();
436    scope_prefix(&prefix_lc)
437}
438
439/// Look up a token that's an invalid target, and see if it might be missing a prefix.
440/// Return the prefix if one was found.
441///
442/// `scopes` should be a singular `Scopes` flag.
443///
444/// Example: if the token is "irish" and `scopes` is `Scopes::Culture` then return
445/// `Some("culture")` to indicate that the token should have been "culture:irish".
446pub fn needs_prefix(arg: &str, data: &Everything, scopes: Scopes) -> Option<&'static str> {
447    match Game::game() {
448        #[cfg(feature = "ck3")]
449        Game::Ck3 => crate::ck3::scopes::needs_prefix(arg, data, scopes),
450        #[cfg(feature = "vic3")]
451        Game::Vic3 => crate::vic3::scopes::needs_prefix(arg, data, scopes),
452        #[cfg(feature = "imperator")]
453        Game::Imperator => crate::imperator::scopes::needs_prefix(arg, data, scopes),
454        #[cfg(feature = "hoi4")]
455        Game::Hoi4 => crate::hoi4::scopes::needs_prefix(arg, data, scopes),
456    }
457}
458
459/// Look up an iterator, which is a script element that executes its block multiple times, once for
460/// each applicable scope value. Iterators may be builtin (the usual case) or may be scripted lists.
461///
462/// `name` is the name of the iterator, without its `any_`, `every_`, `random_` or `ordered_` prefix.
463/// `sc` is a [`ScopeContext`], only used for validating scripted lists.
464///
465/// Returns a pair of `Scopes`. The first is the scope types this token can accept as input,
466/// and the second is the scope types it may return.
467/// The first will be `Scopes::None` if it needs no input.
468pub fn scope_iterator(
469    name: &Token,
470    data: &Everything,
471    sc: &mut ScopeContext,
472) -> Option<(Scopes, Scopes)> {
473    let scope_iterator = match Game::game() {
474        #[cfg(feature = "ck3")]
475        Game::Ck3 => crate::ck3::tables::iterators::iterator,
476        #[cfg(feature = "vic3")]
477        Game::Vic3 => crate::vic3::tables::iterators::iterator,
478        #[cfg(feature = "imperator")]
479        Game::Imperator => crate::imperator::tables::iterators::iterator,
480        #[cfg(feature = "hoi4")]
481        Game::Hoi4 => crate::hoi4::tables::iterators::iterator,
482    };
483    let scope_iterator_removed = match Game::game() {
484        #[cfg(feature = "ck3")]
485        Game::Ck3 => crate::ck3::tables::iterators::iterator_removed,
486        #[cfg(feature = "vic3")]
487        Game::Vic3 => crate::vic3::tables::iterators::iterator_removed,
488        #[cfg(feature = "imperator")]
489        Game::Imperator => crate::imperator::tables::iterators::iterator_removed,
490        #[cfg(feature = "hoi4")]
491        Game::Hoi4 => crate::hoi4::tables::iterators::iterator_removed,
492    };
493
494    let name_lc = name.as_str().to_ascii_lowercase();
495    if let scopes @ Some(_) = scope_iterator(&name_lc) {
496        return scopes;
497    }
498    if let Some((version, explanation)) = scope_iterator_removed(&name_lc) {
499        let msg = format!("`{name}` iterators were removed in {version}");
500        err(ErrorKey::Removed).strong().msg(msg).info(explanation).loc(name).push();
501        return Some((Scopes::all(), Scopes::all()));
502    }
503    #[cfg(feature = "jomini")]
504    if Game::is_jomini() && data.scripted_lists.exists(name.as_str()) {
505        data.scripted_lists.validate_call(name, data, sc);
506        return data
507            .scripted_lists
508            .base(name)
509            .and_then(|base| scope_iterator(&base.as_str().to_ascii_lowercase()));
510    }
511    #[cfg(feature = "hoi4")]
512    let _ = &data; // mark parameter used
513    #[cfg(feature = "hoi4")]
514    let _ = &sc; // mark parameter used
515    None
516}