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 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 vd.field_script_value_rooted("undesirable_infamy_level", Scopes::Country);
72 vd.field_script_value_rooted("unacceptable_infamy_level", Scopes::Country);
74 vd.field_script_value_rooted("ideological_opinion_effect_mult", Scopes::Country);
76 vd.field_script_value_rooted("revolution_aversion", Scopes::Country);
78 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 vd.field_script_value_rooted("max_progressiveness", Scopes::Country);
95 vd.field_script_value_rooted("max_regressiveness", Scopes::Country);
97
98 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); 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); 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
256const 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); 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"); });
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}