Skip to main content

tiger_lib/vic3/data/
ai_strategies.rs

1use crate::block::Block;
2use crate::context::ScopeContext;
3use crate::db::{Db, DbKind};
4use crate::everything::Everything;
5use crate::game::GameFlags;
6use crate::item::{Item, ItemLoader};
7use crate::report::{ErrorKey, warn};
8use crate::scopes::Scopes;
9use crate::script_value::validate_script_value;
10use crate::token::Token;
11use crate::tooltipped::Tooltipped;
12use crate::validator::Validator;
13use crate::vic3::tables::misc::TREATY_ARTICLE_CATEGORIES;
14
15#[derive(Clone, Debug)]
16pub struct AiStrategy {}
17
18inventory::submit! {
19    ItemLoader::Normal(GameFlags::Vic3, Item::AiStrategy, AiStrategy::add)
20}
21
22impl AiStrategy {
23    pub fn add(db: &mut Db, key: Token, block: Block) {
24        db.add(Item::AiStrategy, key, block, Box::new(Self {}));
25    }
26}
27
28impl DbKind for AiStrategy {
29    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
30        fn sc_builder_support(key: &Token) -> ScopeContext {
31            let mut sc = ScopeContext::new(Scopes::Country, key);
32            sc.define_name("country", Scopes::Country, key);
33            sc.define_name("enemy_country", Scopes::Country, key);
34            sc.define_name("diplomatic_play_type", Scopes::DiplomaticPlayType, key);
35            sc.define_name("is_initiator", Scopes::Bool, key);
36            sc
37        }
38
39        fn sc_builder_plays(key: &Token) -> ScopeContext {
40            let mut sc = ScopeContext::new(Scopes::Country, key);
41            sc.define_name("diplomatic_play", Scopes::DiplomaticPlay, key);
42            sc
43        }
44
45        fn sc_builder_conscripts(key: &Token) -> ScopeContext {
46            let mut sc = ScopeContext::new(Scopes::Country, key);
47            sc.define_name("military_formation", Scopes::MilitaryFormation, key);
48            sc
49        }
50
51        let mut vd = Validator::new(block, data);
52
53        if !key.is("ai_strategy_default") {
54            data.verify_exists(Item::Localization, key);
55            let loca = format!("{key}_desc");
56            data.verify_exists_implied(Item::Localization, &loca, key);
57        }
58
59        vd.field_choice("type", &["administrative", "diplomatic", "political"]);
60
61        vd.field_item("icon", Item::File);
62
63        // TODO verify scope type
64        vd.field_trigger_rooted("will_form_power_bloc", Tooltipped::No, Scopes::Country);
65
66        vd.field_item("desired_tax_level", Item::Level);
67        vd.field_item("max_tax_level", Item::Level);
68        vd.field_item("min_tax_level", Item::Level);
69
70        // TODO verify scope type
71        vd.field_script_value_rooted("undesirable_infamy_level", Scopes::Country);
72        // TODO verify scope type
73        vd.field_script_value_rooted("unacceptable_infamy_level", Scopes::Country);
74        // TODO verify scope type
75        vd.field_script_value_rooted("ideological_opinion_effect_mult", Scopes::Country);
76        // TODO verify scope type
77        vd.field_script_value_rooted("revolution_aversion", Scopes::Country);
78        // TODO verify scope type
79        vd.field_script_value_builder("min_law_chance_to_pass", |key| {
80            let mut sc = ScopeContext::new(Scopes::Country, key);
81            sc.define_name("law", Scopes::Law, key);
82            sc
83        });
84        vd.field_script_value_builder("negotiation_chance", |key| {
85            let mut sc = ScopeContext::new(Scopes::Country, key);
86            sc.define_name("law", Scopes::Law, key);
87            sc.define_name("interest_group", Scopes::InterestGroup, key);
88            sc.define_name("amenability", Scopes::Value, key);
89            sc.define_name("advance_chance", Scopes::Value, key);
90            sc.define_name("stall_chance", Scopes::Value, key);
91            sc
92        });
93        // TODO verify scope type
94        vd.field_script_value_rooted("max_progressiveness", Scopes::Country);
95        // TODO verify scope type
96        vd.field_script_value_rooted("max_regressiveness", Scopes::Country);
97
98        // TODO verify scopes
99        vd.field_script_value_builder("diplomatic_play_support", sc_builder_support);
100        vd.field_script_value_no_breakdown_builder("diplomatic_play_neutrality", sc_builder_plays);
101        vd.field_script_value_no_breakdown_builder("diplomatic_play_boldness", sc_builder_plays);
102        vd.field_validated_key("wargoal_maneuvers_fraction", |key, bv, data| {
103            let mut sc = ScopeContext::new(Scopes::Country, key);
104            sc.define_name("enemy_country", Scopes::Country, key);
105            validate_script_value(bv, data, &mut sc);
106        });
107        vd.field_script_value_rooted("change_law_chance", Scopes::Country);
108
109        vd.field_list_items("pro_interest_groups", Item::InterestGroup);
110        vd.field_list_items("anti_interest_groups", Item::InterestGroup);
111        vd.field_list_items("pro_movements", Item::PoliticalMovement);
112        vd.field_list_items("anti_movements", Item::PoliticalMovement);
113        vd.field_validated_key_block("institution_scores", validate_institution_scores);
114
115        vd.field_validated_key("obligation_value", |key, bv, data| {
116            let mut sc = ScopeContext::new(Scopes::Country, key);
117            sc.define_name("target_country", Scopes::Country, key);
118            validate_script_value(bv, data, &mut sc);
119        });
120        vd.field_script_value_no_breakdown_builder("interest_group_government_weight", |key| {
121            let mut sc = ScopeContext::new(Scopes::Country, key);
122            sc.define_name("interest_group", Scopes::InterestGroup, key);
123            sc
124        });
125        vd.field_validated_key("state_value", |key, bv, data| {
126            let mut sc = ScopeContext::new(Scopes::Country, key);
127            sc.define_name("target_state", Scopes::State, key);
128            sc.define_name("target_country", Scopes::Country, key);
129            validate_script_value(bv, data, &mut sc);
130        });
131        vd.field_validated_key("treaty_port_value", |key, bv, data| {
132            let mut sc = ScopeContext::new(Scopes::Country, key);
133            sc.define_name("target_state", Scopes::State, key);
134            sc.define_name("target_country", Scopes::Country, key);
135            validate_script_value(bv, data, &mut sc);
136        });
137        vd.field_validated_key("subject_value", |key, bv, data| {
138            let mut sc = ScopeContext::new(Scopes::Country, key);
139            sc.define_name("target_country", Scopes::Country, key);
140            validate_script_value(bv, data, &mut sc);
141        });
142        vd.field_validated_key("become_subject_value", |key, bv, data| {
143            let mut sc = ScopeContext::new(Scopes::Country, key);
144            sc.define_name("overlord", Scopes::Country, key);
145            validate_script_value(bv, data, &mut sc);
146        });
147        vd.field_validated_key("recklessness", |key, bv, data| {
148            let mut sc = ScopeContext::new(Scopes::Country, key);
149            sc.define_name("target_country", Scopes::Country, key);
150            validate_script_value(bv, data, &mut sc);
151        });
152        vd.field_validated_key("aggression", |key, bv, data| {
153            let mut sc = ScopeContext::new(Scopes::Country, key);
154            sc.define_name("target_country", Scopes::Country, key);
155            validate_script_value(bv, data, &mut sc);
156        });
157        vd.field_script_value_rooted("wanted_construction_output", Scopes::Country);
158        vd.replaced_field("wanted_construction_sector_levels", "wanted_construction_output");
159        vd.field_validated_block("strategic_region_stance_type_scores", |block, data| {
160            let mut vd = Validator::new(block, data);
161            vd.unknown_fields(|key, bv| {
162                let mut sc = ScopeContext::new(Scopes::Country, key);
163                sc.define_name("target_region", Scopes::StrategicRegion, key);
164                sc.define_name("region_score", Scopes::Value, key);
165
166                data.verify_exists(Item::AiStrategicRegionStanceType, key);
167                validate_script_value(bv, data, &mut sc);
168            });
169        });
170        vd.field_script_value_rooted("max_active_stances", Scopes::Country);
171        vd.field_script_value_rooted("wanted_army_size", Scopes::Country);
172        vd.field_script_value_rooted("wanted_marines", Scopes::Country);
173        vd.field_script_value_rooted("target_marine_formation_size", Scopes::Country);
174        vd.field_script_value_rooted("wanted_navy_size", Scopes::Country);
175        vd.field_script_value_rooted("wanted_num_supply_ships", Scopes::Country);
176        vd.field_script_value_rooted("ship_construction_output_multiplier", Scopes::Country);
177
178        vd.field_validated_block("ship_group_weights", |block, data| {
179            let mut vd = Validator::new(block, data);
180            vd.unknown_fields(|key, bv| {
181                let mut sc = ScopeContext::new(Scopes::Country, key);
182                data.verify_exists(Item::ShipGroup, key);
183                validate_script_value(bv, data, &mut sc);
184            });
185        });
186        vd.field_script_value_rooted("max_ship_design_complexity", Scopes::Country);
187        vd.field_script_value_rooted("wanted_naval_fortification_levels", Scopes::Country);
188
189        vd.field_validated_key_block(
190            "combat_unit_group_weights",
191            validate_combat_unit_group_weights,
192        );
193
194        vd.field_script_value_no_breakdown_builder(
195            "conscript_battalion_ratio",
196            sc_builder_conscripts,
197        );
198
199        vd.field_script_value_no_breakdown_rooted("nationalization_desire", Scopes::Country);
200
201        vd.field_validated_key_block("building_group_weights", validate_building_group_weights);
202
203        vd.field_validated_key_block("subsidies", validate_subsidies);
204        vd.field_validated_key_block("war_subsidies", validate_subsidies);
205        vd.field_validated_key_block("goods_stances", validate_goods_stances);
206
207        vd.field_script_value_no_breakdown_rooted("colonial_interest_ratio", Scopes::Country);
208        vd.field_validated_block("liberate_country_scores", validate_liberate_country_scores);
209        vd.field_script_value_no_breakdown_builder("strategic_region_scores", |key| {
210            let mut sc = ScopeContext::new(Scopes::Country, key);
211            sc.define_name("region", Scopes::StrategicRegion, key);
212            sc
213        });
214        vd.field_validated_key_block("secret_goal_scores", validate_secret_goal_scores);
215        vd.field_validated_key_block("secret_goal_weights", validate_secret_goal_weights);
216        vd.field_validated_key_block("treaty_category_scores", validate_treaty_category_scores);
217        vd.field_validated_key_block("wargoal_scores", validate_wargoal_scores);
218        vd.field_validated_key_block("wargoal_weights", validate_wargoal_weights);
219        vd.field_validated_key_block("strait_scores", validate_strait_scores);
220        vd.field_validated_key_block("fleet_compositions", validate_fleet_compositions);
221
222        vd.field_trigger_rooted("possible", Tooltipped::No, Scopes::Country); // TODO scope type
223        vd.field_script_value_rooted("weight", Scopes::Country);
224
225        vd.field_script_value_rooted("economic_vs_government_spending_balance", Scopes::Country);
226    }
227}
228
229fn validate_institution_scores(key: &Token, block: &Block, data: &Everything) {
230    let mut vd = Validator::new(block, data);
231    let mut sc = ScopeContext::new(Scopes::Country, key); // TODO verify scope type
232    vd.unknown_fields(|key, bv| {
233        data.verify_exists(Item::Institution, key);
234        validate_script_value(bv, data, &mut sc);
235    });
236}
237
238fn validate_combat_unit_group_weights(_key: &Token, block: &Block, data: &Everything) {
239    let mut vd = Validator::new(block, data);
240    vd.unknown_fields(|key, bv| {
241        data.verify_exists(Item::CombatUnitGroup, key);
242        let mut sc = ScopeContext::new(Scopes::Country, key);
243        sc.define_name("military_formation", Scopes::MilitaryFormation, key);
244        validate_script_value(bv, data, &mut sc);
245    });
246}
247
248fn validate_building_group_weights(_key: &Token, block: &Block, data: &Everything) {
249    let mut vd = Validator::new(block, data);
250    vd.unknown_value_fields(|key, token| {
251        data.verify_exists(Item::BuildingGroup, key);
252        token.expect_number();
253    });
254}
255
256// TODO what other options?
257const SUBSIDIES_TYPES: &[&str] = &["should_have", "wants_to_have", "must_have", "nice_to_have"];
258fn validate_subsidies(_key: &Token, block: &Block, data: &Everything) {
259    let mut vd = Validator::new(block, data);
260    vd.unknown_value_fields(|key, token| {
261        data.verify_exists(Item::BuildingType, key);
262        if !SUBSIDIES_TYPES.contains(&token.as_str()) {
263            warn(ErrorKey::Validation).weak().msg("unknown subsidy type").loc(token).push();
264        }
265    });
266}
267
268fn validate_goods_stances(key: &Token, block: &Block, data: &Everything) {
269    let mut vd = Validator::new(block, data);
270    let mut sc = ScopeContext::new(Scopes::Country, key);
271    vd.unknown_block_fields(|key, block| {
272        data.verify_exists(Item::Goods, key);
273        let mut vd = Validator::new(block, data);
274        vd.req_field("stance");
275        vd.field_choice("stance", &["wants_high_supply", "wants_export", "does_not_want"]);
276        vd.field_trigger("trigger", Tooltipped::No, &mut sc);
277    });
278}
279
280fn validate_secret_goal_scores(key: &Token, block: &Block, data: &Everything) {
281    let mut vd = Validator::new(block, data);
282    let mut sc = ScopeContext::new(Scopes::Country, key);
283    sc.define_name("target_country", Scopes::Country, key);
284    vd.unknown_block_fields(|key, block| {
285        data.verify_exists(Item::SecretGoal, key);
286        let mut vd = Validator::new(block, data);
287        vd.field_trigger("trigger", Tooltipped::No, &mut sc);
288        vd.field_script_value_no_breakdown("score", &mut sc);
289    });
290}
291
292fn validate_secret_goal_weights(_key: &Token, block: &Block, data: &Everything) {
293    let mut vd = Validator::new(block, data);
294    vd.unknown_value_fields(|key, token| {
295        data.verify_exists(Item::SecretGoal, key);
296        token.expect_number();
297    });
298}
299
300fn validate_treaty_category_scores(_key: &Token, block: &Block, data: &Everything) {
301    let mut vd = Validator::new(block, data);
302    for category in TREATY_ARTICLE_CATEGORIES {
303        vd.field_script_value_rooted(category, Scopes::Country);
304    }
305    vd.field_script_value_rooted("none", Scopes::Country);
306}
307
308fn validate_wargoal_scores(key: &Token, block: &Block, data: &Everything) {
309    let mut vd = Validator::new(block, data);
310    let mut sc = ScopeContext::new(Scopes::Country, key);
311    sc.define_name("target_country", Scopes::Country, key);
312    sc.define_name("target_state", Scopes::State, key); // might not be set
313    vd.unknown_fields(|key, bv| {
314        data.verify_exists(Item::WarGoalType, key);
315        validate_script_value(bv, data, &mut sc);
316    });
317}
318
319fn validate_wargoal_weights(_key: &Token, block: &Block, data: &Everything) {
320    let mut vd = Validator::new(block, data);
321    vd.unknown_value_fields(|key, token| {
322        data.verify_exists(Item::WarGoalType, key);
323        token.expect_number();
324    });
325}
326
327fn validate_strait_scores(_key: &Token, block: &Block, data: &Everything) {
328    let mut vd = Validator::new(block, data);
329    let sc_builder = |key: &Token| {
330        let mut sc = ScopeContext::new(Scopes::Country, key);
331        sc.define_name("strait", Scopes::StraitType, key);
332        sc.define_name("first_state", Scopes::State, key);
333        sc.define_name("second_state", Scopes::State, key);
334        sc.define_name("is_sole_controller", Scopes::Bool, key);
335        sc
336    };
337    vd.field_validated_block("status_scores", |block, data| {
338        let mut vd = Validator::new(block, data);
339        vd.field_script_value_no_breakdown_builder("open", sc_builder);
340        vd.field_script_value_no_breakdown_builder("no_military", sc_builder);
341        vd.field_script_value_no_breakdown_builder("closed", sc_builder);
342    });
343    vd.field_validated_block("permissions_scores", |block, data| {
344        let mut vd = Validator::new(block, data);
345        vd.field_script_value_no_breakdown_builder("all", sc_builder);
346        vd.field_script_value_no_breakdown_builder("no_enemies", sc_builder);
347        vd.field_script_value_no_breakdown_builder("only_allies", sc_builder);
348        vd.field_script_value_no_breakdown_builder("only_controller", sc_builder);
349    });
350    vd.field_validated_block("toll_rate_scores", |block, data| {
351        let mut vd = Validator::new(block, data);
352        vd.field_script_value_no_breakdown_builder("no_tolls", sc_builder);
353        vd.field_script_value_no_breakdown_builder("low_tolls", sc_builder);
354        vd.field_script_value_no_breakdown_builder("medium_tolls", sc_builder);
355        vd.field_script_value_no_breakdown_builder("high_tolls", sc_builder);
356    });
357}
358
359fn validate_fleet_compositions(_key: &Token, block: &Block, data: &Everything) {
360    let mut vd = Validator::new(block, data);
361    for field in &["main_battle_fleet", "secondary_fleet", "raid_fleet"] {
362        vd.field_validated_block(field, |block, data| {
363            let mut vd = Validator::new(block, data);
364            vd.field_validated_block("ship_group_weights", |block, data| {
365                let mut vd = Validator::new(block, data);
366                vd.unknown_fields(|key, bv| {
367                    data.verify_exists(Item::ShipGroup, key);
368                    validate_script_value(bv, data, &mut ScopeContext::new(Scopes::Country, key));
369                });
370            });
371            vd.field_validated_block("mission_type_weights", |block, data| {
372                let mut vd = Validator::new(block, data);
373                vd.field_script_value_no_breakdown_rooted("intercept", Scopes::Country);
374                vd.field_script_value_no_breakdown_rooted("project_power", Scopes::Country);
375                vd.field_script_value_no_breakdown_rooted("blockade", Scopes::Country);
376                vd.field_script_value_no_breakdown_rooted("raid_supply", Scopes::Country);
377                vd.field_script_value_no_breakdown_rooted("protect_supply", Scopes::Country);
378                vd.field_script_value_no_breakdown_rooted("port_bombardment", Scopes::Country);
379                vd.field_script_value_no_breakdown_rooted("piracy", Scopes::Country);
380                vd.field_script_value_no_breakdown_rooted("privateer", Scopes::Country);
381                vd.field_script_value_no_breakdown_rooted("hunt_pirates", Scopes::Country);
382            });
383            vd.field_script_value_no_breakdown_rooted("min_ships_wanted", Scopes::Country);
384            vd.field_script_value_no_breakdown_rooted("num_fleets_wanted", Scopes::Country);
385            vd.field_trigger_rooted("potential", Tooltipped::No, Scopes::Country);
386            vd.field_value("replace_key"); // TODO
387        });
388    }
389}
390
391fn validate_liberate_country_scores(block: &Block, data: &Everything) {
392    let mut vd = Validator::new(block, data);
393    vd.unknown_fields(|key, bv| {
394        data.verify_exists(Item::Country, key);
395        let mut sc = ScopeContext::new(Scopes::Country, key);
396        sc.define_name("target_country", Scopes::Country, key);
397        validate_script_value(bv, data, &mut sc);
398    });
399}