1use crate::block::{BV, Block};
2use crate::context::{ScopeContext, Temporary};
3use crate::desc::validate_desc;
4use crate::everything::Everything;
5use crate::helpers::TigerHashSet;
6use crate::item::Item;
7use crate::report::{ErrorKey, ErrorLoc, err, warn};
8use crate::scopes::Scopes;
9use crate::script_value::validate_script_value;
10use crate::token::Token;
11use crate::tooltipped::Tooltipped;
12use crate::trigger::validate_target;
13use crate::validate::{validate_color, validate_optional_duration, validate_possibly_named_color};
14use crate::validator::{Validator, ValueValidator};
15use crate::vic3::data::buildings::BuildingType;
16use crate::vic3::tables::misc::{LOBBY_FORMATION_REASON, STATE_TYPES, STRATA, TARIFF_LEVELS};
17use crate::vic3::validate::validate_treaty_article;
18
19pub fn validate_activate_production_method(
20 _key: &Token,
21 _block: &Block,
22 _data: &Everything,
23 _sc: &mut ScopeContext,
24 mut vd: Validator,
25 _tooltipped: Tooltipped,
26) {
27 vd.req_field("building_type");
28 vd.req_field("production_method");
29 vd.field_item("building_type", Item::BuildingType);
30 vd.field_item("production_method", Item::ProductionMethod);
32}
33
34pub fn validate_add_culture_modifier(
35 _key: &Token,
36 _block: &Block,
37 _data: &Everything,
38 sc: &mut ScopeContext,
39 mut vd: Validator,
40 _tooltipped: Tooltipped,
41) {
42 vd.req_field("culture");
43 vd.field_target("culture", sc, Scopes::Culture);
44 validate_optional_duration(&mut vd, sc);
45 vd.field_script_value("multiplier", sc); }
47
48pub fn validate_add_religion_sol_modifier(
49 _key: &Token,
50 _block: &Block,
51 _data: &Everything,
52 sc: &mut ScopeContext,
53 mut vd: Validator,
54 _tooltipped: Tooltipped,
55) {
56 vd.req_field("religion");
57 vd.field_target("religion", sc, Scopes::Religion);
58 validate_optional_duration(&mut vd, sc);
59 vd.field_script_value("multiplier", sc); }
61
62pub fn validate_add_enactment_modifier(
63 _key: &Token,
64 _block: &Block,
65 data: &Everything,
66 sc: &mut ScopeContext,
67 mut vd: Validator,
68 _tooltipped: Tooltipped,
69) {
70 vd.req_field("name");
71 vd.field_validated_value("name", |_key, mut vd| {
72 vd.item(Item::Modifier);
73 let value = vd.value();
74 data.validate_call(Item::Modifier, value, &Block::new(value.loc), sc);
75 });
76 vd.field_script_value_builder("multiplier", |key| sc.get_multiplier_context(key));
78}
79
80pub fn validate_add_modifier(
81 _key: &Token,
82 bv: &BV,
83 data: &Everything,
84 sc: &mut ScopeContext,
85 _tooltipped: Tooltipped,
86) {
87 match bv {
88 BV::Value(value) => {
89 data.verify_exists(Item::Modifier, value);
90 data.validate_call(Item::Modifier, value, &Block::new(value.loc), sc);
91 }
92 BV::Block(block) => {
93 let mut vd = Validator::new(block, data);
94 vd.set_case_sensitive(false);
95 vd.req_field("name");
96 vd.field_validated_value("name", |_key, mut vd| {
97 vd.item(Item::Modifier);
98 let value = vd.value();
99 data.validate_call(Item::Modifier, value, &Block::new(value.loc), sc);
100 });
101 vd.field_script_value_builder("multiplier", |key| sc.get_multiplier_context(key));
103 validate_optional_duration(&mut vd, sc);
104 vd.field_bool("is_decaying");
105 }
106 }
107}
108
109pub fn validate_add_journalentry(
110 _key: &Token,
111 _block: &Block,
112 _data: &Everything,
113 sc: &mut ScopeContext,
114 mut vd: Validator,
115 _tooltipped: Tooltipped,
116) {
117 vd.req_field("type");
118 vd.field_item("type", Item::JournalEntry);
119 vd.field_item("objective_subgoal", Item::ObjectiveSubgoal); vd.field_target("target", sc, Scopes::all());
121}
122
123pub fn validate_add_loyalists(
124 _key: &Token,
125 _block: &Block,
126 _data: &Everything,
127 sc: &mut ScopeContext,
128 mut vd: Validator,
129 _tooltipped: Tooltipped,
130) {
131 vd.req_field("value");
132 vd.field_script_value("value", sc);
133 vd.field_item_or_target("interest_group", sc, Item::InterestGroup, Scopes::InterestGroup);
134 vd.field_item_or_target("pop_type", sc, Item::PopType, Scopes::PopType);
135 vd.field_choice("strata", STRATA);
136 vd.field_item_or_target("culture", sc, Item::Culture, Scopes::Culture);
137 vd.field_item_or_target("religion", sc, Item::Religion, Scopes::Religion);
138}
139
140pub fn validate_add_technology_progress(
141 _key: &Token,
142 _block: &Block,
143 _data: &Everything,
144 _sc: &mut ScopeContext,
145 mut vd: Validator,
146 _tooltipped: Tooltipped,
147) {
148 vd.req_field("progress");
149 vd.field_numeric("progress");
150 vd.req_field("technology");
151 vd.field_item("technology", Item::Technology);
152}
153
154pub fn validate_add_war_goal(
155 _key: &Token,
156 block: &Block,
157 _data: &Everything,
158 sc: &mut ScopeContext,
159 mut vd: Validator,
160 _tooltipped: Tooltipped,
161) {
162 vd.req_field("holder");
163 vd.field_item_or_target("holder", sc, Item::Country, Scopes::Country);
164 vd.req_field("type");
165 vd.field_item("type", Item::WarGoalType);
166 vd.field_target("state", sc, Scopes::State);
167 vd.advice_field("country", "docs say `country` but it's `target_country`");
169 vd.field_target("target_country", sc, Scopes::Country);
170 vd.field_target("target_state", sc, Scopes::State);
171 vd.field_target("region", sc, Scopes::StateRegion);
172 vd.field_bool("primary_demand");
173 if let Some(goal_type) = block.get_field_value("type") {
174 if goal_type.is("enforce_treaty_article") {
175 vd.multi_field_validated_block_sc("article", sc, validate_treaty_article);
176 }
177 }
178}
179
180pub fn validate_remove_war_goal(
181 _key: &Token,
182 _block: &Block,
183 _data: &Everything,
184 sc: &mut ScopeContext,
185 mut vd: Validator,
186 _tooltipped: Tooltipped,
187) {
188 vd.req_field("who");
189 vd.field_item_or_target("who", sc, Item::Country, Scopes::Country);
190 vd.req_field("type");
191 vd.field_item("type", Item::WarGoalType);
192}
193
194pub fn validate_addremove_backers(
195 _key: &Token,
196 _block: &Block,
197 data: &Everything,
198 sc: &mut ScopeContext,
199 mut vd: Validator,
200 _tooltipped: Tooltipped,
201) {
202 for value in vd.values() {
203 if !data.item_exists(Item::Country, value.as_str()) {
204 validate_target(value, data, sc, Scopes::Country);
205 }
206 }
207}
208
209pub fn validate_call_election(
210 _key: &Token,
211 _block: &Block,
212 _data: &Everything,
213 sc: &mut ScopeContext,
214 mut vd: Validator,
215 _tooltipped: Tooltipped,
216) {
217 vd.req_field("months");
218 vd.field_script_value("months", sc);
219}
220
221pub fn validate_change_institution_investment_level(
222 _key: &Token,
223 _block: &Block,
224 _data: &Everything,
225 _sc: &mut ScopeContext,
226 mut vd: Validator,
227 _tooltipped: Tooltipped,
228) {
229 vd.req_field("institution");
230 vd.field_item("institution", Item::Institution);
231 vd.req_field("investment");
232 vd.field_integer("investment");
233}
234
235pub fn validate_set_institution_investment_level(
236 _key: &Token,
237 _block: &Block,
238 _data: &Everything,
239 _sc: &mut ScopeContext,
240 mut vd: Validator,
241 _tooltipped: Tooltipped,
242) {
243 vd.req_field("institution");
244 vd.field_item("institution", Item::Institution);
245 vd.req_field("level");
246 vd.field_integer("level");
247}
248
249pub fn validate_diplomatic_pact(
250 _key: &Token,
251 _block: &Block,
252 _data: &Everything,
253 sc: &mut ScopeContext,
254 mut vd: Validator,
255 _tooltipped: Tooltipped,
256) {
257 vd.req_field("country");
258 vd.req_field("type");
259 vd.advice_field("tcountry", "documentation says tcountry but it's just country");
260 vd.field_item_or_target("country", sc, Item::Country, Scopes::Country);
261 vd.field_item_or_target("first_state", sc, Item::StateRegion, Scopes::State);
262 vd.field_item_or_target("second_state", sc, Item::StateRegion, Scopes::State);
263 vd.field_item("type", Item::DiplomaticAction);
264}
265
266pub fn validate_country_value(
267 _key: &Token,
268 _block: &Block,
269 _data: &Everything,
270 sc: &mut ScopeContext,
271 mut vd: Validator,
272 _tooltipped: Tooltipped,
273) {
274 vd.req_field("country");
275 vd.advice_field("tcountry", "documentation says tcountry but it's just country");
276 vd.req_field("value");
277 vd.field_item_or_target("country", sc, Item::Country, Scopes::Country);
278 vd.field_script_value("value", sc);
279}
280
281pub fn validate_create_building(
282 _key: &Token,
283 block: &Block,
284 _data: &Everything,
285 sc: &mut ScopeContext,
286 mut vd: Validator,
287 _tooltipped: Tooltipped,
288) {
289 vd.req_field("building");
290 vd.field_item("building", Item::BuildingType);
291 let building = block.get_field_value("building");
292 vd.field_validated_list("activate_production_methods", |token, data| {
293 data.verify_exists(Item::ProductionMethod, token);
294 if let Some(building) = building {
295 if let Some((_, block, building_item)) =
296 data.get_item::<BuildingType>(Item::BuildingType, building.as_str())
297 {
298 building_item.validate_production_method(token, building, block, data);
299 }
300 }
301 });
302 vd.field_bool("subsidized");
303 vd.field_numeric_range("reserves", 0.0..=1.0);
304 vd.field_validated_sc("level", sc, |bv, data, sc| {
305 if let Some(token) = bv.get_value() {
306 if token.is("arable_land") {
307 return;
308 }
309 }
310 validate_script_value(bv, data, sc);
311 });
312 vd.field_validated_block("add_ownership", |block, data| {
313 let mut vd = Validator::new(block, data);
314 vd.multi_field_validated_block("country", |block, data| {
315 let mut vd = Validator::new(block, data);
316 vd.req_field("country");
317 vd.field_target("country", sc, Scopes::Country);
318 vd.req_field("levels");
319 vd.field_integer("levels");
320 });
321 vd.multi_field_validated_block("building", |block, data| {
323 let mut vd = Validator::new(block, data);
324 vd.req_field("country");
325 vd.field_target("country", sc, Scopes::Country);
326 vd.req_field("levels");
327 vd.field_integer("levels");
328 vd.req_field("type");
329 vd.field_item("type", Item::BuildingType);
330 vd.req_field("region");
331 vd.field_item("region", Item::StateRegion);
332 });
333 vd.multi_field_validated_block("company", |block, data| {
335 let mut vd = Validator::new(block, data);
336 vd.req_field("country");
337 vd.field_target("country", sc, Scopes::Country);
338 vd.req_field("type");
339 vd.field_item("type", Item::CompanyType);
340 vd.req_field("levels");
341 vd.field_integer("levels");
342 });
343 });
344}
345
346pub fn validate_create_character(
347 key: &Token,
348 block: &Block,
349 _data: &Everything,
350 sc: &mut ScopeContext,
351 mut vd: Validator,
352 _tooltipped: Tooltipped,
353) {
354 vd.field_localization("name", sc);
355 vd.field_localization("first_name", sc);
356 vd.field_localization("last_name", sc);
357 if block.has_key("name") {
358 vd.ban_field("first_name", || "characters without `name`");
359 vd.ban_field("last_name", || "characters without `name`");
360 } else if block.has_key("first_name") {
361 if !block.has_key("last_name") {
362 let msg = "character has `first_name` but no `last_name`";
363 warn(ErrorKey::Validation).msg(msg).loc(key).push();
364 }
365 } else if block.has_key("last_name") {
366 let msg = "character has `last_name` but no `first_name`";
367 warn(ErrorKey::Validation).msg(msg).loc(key).push();
368 }
369 vd.field_validated_value("culture", |_, mut vd| {
370 vd.maybe_is("primary_culture");
371 vd.item_or_target(sc, Item::Culture, Scopes::Culture);
372 });
373 vd.field_item_or_target("religion", sc, Item::Religion, Scopes::Religion);
374 vd.field_validated_value("female", |_, mut vd| {
375 vd.maybe_bool();
376 vd.target(sc, Scopes::Character);
377 });
378 vd.field_validated_value("noble", |_, mut vd| {
379 vd.maybe_bool();
380 vd.target(sc, Scopes::Character);
381 });
382 vd.field_bool("ruler");
383 vd.field_bool("heir");
384 vd.field_bool("historical");
385 vd.field_validated("age", |bv, data| {
386 match bv {
387 BV::Value(value) => {
388 let mut vd = ValueValidator::new(value, data);
390 vd.maybe_is("default");
391 vd.maybe_integer();
392 vd.target(sc, Scopes::Character);
393 }
394 BV::Block(block) => {
395 let mut vd = Validator::new(block, data);
397 vd.req_tokens_integers_exactly(2);
398 }
399 }
400 });
401 vd.field_item_or_target("ideology", sc, Item::Ideology, Scopes::Ideology);
402 vd.field_item_or_target("interest_group", sc, Item::InterestGroup, Scopes::InterestGroup);
403 vd.field_item("template", Item::CharacterTemplate);
404 vd.field_effect_rooted("on_created", Tooltipped::No, Scopes::Character);
405 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
406 sc.define_name_token(name.as_str(), Scopes::Character, name, Temporary::No);
407 }
408 vd.field_effect_rooted("trait_generation", Tooltipped::No, Scopes::Character);
409 vd.field_item_or_target("hq", sc, Item::StrategicRegion, Scopes::Hq | Scopes::StrategicRegion);
411
412 vd.field_date("birth_date");
416 vd.field_list_items("traits", Item::CharacterTrait);
417 vd.field_item("dna", Item::Dna);
418 vd.field_bool("is_general");
419 vd.field_bool("is_admiral");
420 vd.field_bool("is_agitator");
421 vd.field_bool("ig_leader");
422 vd.field_item("commander_rank", Item::CommanderRank);
423}
424
425pub fn validate_create_country(
426 _key: &Token,
427 _block: &Block,
428 _data: &Everything,
429 sc: &mut ScopeContext,
430 mut vd: Validator,
431 _tooltipped: Tooltipped,
432) {
433 vd.field_item("tag", Item::Country);
434 vd.field_target_ok_this("origin", sc, Scopes::Country);
435 vd.multi_field_target("state", sc, Scopes::State);
436 vd.multi_field_target("province", sc, Scopes::Province);
437 vd.field_effect_rooted("on_created", Tooltipped::No, Scopes::Country);
438}
439
440pub fn validate_create_dynamic_country(
441 _key: &Token,
442 block: &Block,
443 _data: &Everything,
444 sc: &mut ScopeContext,
445 mut vd: Validator,
446 _tooltipped: Tooltipped,
447) {
448 vd.field_target_ok_this("origin", sc, Scopes::Country);
449 if !block.has_key("origin") {
450 vd.req_field("country_type");
451 vd.req_field("tier");
452 vd.req_field("culture");
453 vd.req_field("religion");
454 vd.req_field("capital");
455 vd.req_field("color");
456 vd.req_field("primary_unit_color");
457 vd.req_field("secondary_unit_color");
458 vd.req_field("tertiary_unit_color");
459 }
460 vd.field_item("country_type", Item::CountryType);
461 vd.field_item("tier", Item::CountryTier);
462 vd.multi_field_target("culture", sc, Scopes::Culture);
463 vd.field_target("religion", sc, Scopes::Religion);
464 vd.field_target("capital", sc, Scopes::State);
465 vd.field_item("social_hierarchy", Item::SocialHierarchy);
466 vd.field_trigger_rooted("cede_state_trigger", Tooltipped::No, Scopes::State);
467 vd.field_validated("color", validate_possibly_named_color);
468 vd.field_validated("primary_unit_color", validate_possibly_named_color);
469 vd.field_validated("secondary_unit_color", validate_possibly_named_color);
470 vd.field_validated("tertiary_unit_color", validate_possibly_named_color);
471 vd.field_effect_rooted("on_created", Tooltipped::No, Scopes::Country);
472}
473
474pub fn validate_create_diplomatic_play(
475 _key: &Token,
476 _block: &Block,
477 _data: &Everything,
478 sc: &mut ScopeContext,
479 mut vd: Validator,
480 _tooltipped: Tooltipped,
481) {
482 vd.field_localization("name", sc);
483 vd.field_integer_range("escalation", 0..=100);
484 vd.field_bool("war");
485 vd.field_item_or_target_ok_this("initiator", sc, Item::Country, Scopes::Country);
486 vd.field_item("type", Item::DiplomaticPlay);
487 vd.advice_field(
488 "handle_annexation_as_civil_war",
489 "docs say `handle_annexation_as_civil_war` but it's `annex_as_civil_war`",
490 );
491 vd.field_bool("annex_as_civil_war");
492 for field in &["add_initiator_backers", "add_target_backers"] {
493 vd.field_validated_list(field, |token, data| {
494 let mut vd = ValueValidator::new(token, data);
495 vd.maybe_item(Item::Country);
496 vd.target(sc, Scopes::Country);
497 });
498 }
499 vd.multi_field_validated_block_sc("add_war_goal", sc, validate_war_goal);
500
501 vd.field_target("target_state", sc, Scopes::State);
504 vd.field_target("target_country", sc, Scopes::Country);
505 vd.field_target("target_region", sc, Scopes::StrategicRegion);
506}
507
508fn validate_war_goal(block: &Block, data: &Everything, sc: &mut ScopeContext) {
509 let mut vd = Validator::new(block, data);
510 vd.set_case_sensitive(false);
511 vd.field_item_or_target_ok_this("holder", sc, Item::Country, Scopes::Country);
512 vd.field_item("type", Item::WarGoalType);
513 vd.advice_field("state", "docs say `state` but it's `target_state`");
514 vd.field_target("target_state", sc, Scopes::State);
515 vd.advice_field("country", "docs say `country` but it's `target_country`");
516 vd.field_target("target_country", sc, Scopes::Country);
517 vd.advice_field("region", "docs say `region` but it's `target_region`");
518 vd.field_target("target_region", sc, Scopes::StrategicRegion);
519 vd.field_bool("primary_demand");
520 if let Some(goal_type) = block.get_field_value("type") {
521 if goal_type.is("enforce_treaty_article") {
522 vd.multi_field_validated_block_sc("article", sc, validate_treaty_article);
523 }
524 }
525}
526
527pub fn validate_create_mass_migration(
528 _key: &Token,
529 _block: &Block,
530 _data: &Everything,
531 sc: &mut ScopeContext,
532 mut vd: Validator,
533 _tooltipped: Tooltipped,
534) {
535 vd.req_field("origin");
536 vd.field_target("origin", sc, Scopes::Country);
537 vd.req_field("culture");
538 vd.field_target("culture", sc, Scopes::Culture);
539}
540
541pub fn validate_create_military_formation(
542 _key: &Token,
543 block: &Block,
544 _data: &Everything,
545 sc: &mut ScopeContext,
546 mut vd: Validator,
547 _tooltipped: Tooltipped,
548) {
549 vd.field_localization("name", sc);
550 vd.field_choice("type", &["army", "fleet"]);
551 let is_fleet = block.field_value_is("type", "fleet");
552 vd.field_target("hq_region", sc, Scopes::StrategicRegion);
553 vd.multi_field_validated_block("combat_unit", |block, data| {
554 let mut vd = Validator::new(block, data);
555 vd.field_target("type", sc, Scopes::CombatUnitType);
556 vd.field_choice("service_type", &["regular", "conscript"]);
557 if let Some(token) = vd.field_value("service_type") {
558 if is_fleet && token.is("conscript") {
559 let msg = "conscript is not applicable to fleets";
560 err(ErrorKey::Choice).msg(msg).loc(token).push();
561 }
562 }
563 vd.field_target("state_region", sc, Scopes::StateRegion);
564 vd.field_integer("count");
565 });
566 if is_fleet {
567 vd.ban_field("mobilization_options", || "armies");
568 }
569 vd.field_validated_list("mobilization_options", |token, data| {
570 let mut vd = ValueValidator::new(token, data);
571 vd.target(sc, Scopes::MobilizationOption);
572 });
573
574 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
577 sc.define_name_token(name.as_str(), Scopes::MilitaryFormation, name, Temporary::No);
578 }
579}
580
581pub fn validate_create_pop(
582 _key: &Token,
583 block: &Block,
584 _data: &Everything,
585 _sc: &mut ScopeContext,
586 mut vd: Validator,
587 _tooltipped: Tooltipped,
588) {
589 #[allow(clippy::integer_division)]
592 fn sum_fractions_warner<E: ErrorLoc>(sum_fractions: i64, loc: E) {
593 if sum_fractions != 100_000 {
594 let msg = format!(
595 "fractions should add to exactly 1, currently {}.{:05}",
596 sum_fractions / 100_000,
597 sum_fractions % 100_000,
598 );
599 warn(ErrorKey::Validation).msg(msg.trim_end_matches('0')).loc(loc).push();
600 }
601 }
602
603 vd.field_item("pop_type", Item::PopType);
604 vd.field_integer("size");
605
606 let mut available_cultures = TigerHashSet::default();
607
608 if let Some(token) = vd.field_value("culture") {
609 available_cultures.insert(token.clone());
610
611 vd.ban_field("cultures", || "pops with several cultures");
612 vd.field_item("culture", Item::Culture);
613 } else {
614 vd.field_validated_block("cultures", |block, data| {
615 let mut vd = Validator::new(block, data);
616 let mut sum_fractions = 0_i64;
617
618 vd.validate_item_key_values(Item::Culture, |key, mut vd| {
619 available_cultures.insert(key.clone());
620 vd.numeric_range(0.0..=1.0);
621
622 sum_fractions += vd.value().get_fixed_number().unwrap_or(0);
623 });
624
625 sum_fractions_warner(sum_fractions, block);
626 });
627 }
628
629 if block.has_key("religion") {
630 vd.ban_field("split_religion", || "pops without a `religion` field");
631 vd.field_item("religion", Item::Religion);
632 } else if block.has_key("split_religion") {
633 let mut used_cultures = TigerHashSet::default();
634
635 vd.multi_field_validated_block("split_religion", |block, data| {
636 let mut vd = Validator::new(block, data);
637 let mut only_one_culture = false;
638
639 vd.validate_item_key_blocks(Item::Culture, |key, block, data| {
640 if only_one_culture {
641 let msg = "split_religion should contain only one culture block";
642 err(ErrorKey::DuplicateItem).msg(msg).loc(key).push();
643 }
644 only_one_culture = true;
645
646 if !available_cultures.contains(key.as_str()) {
647 let msg = "culture being split does not appear in pop";
648 err(ErrorKey::FieldMissing).msg(msg).loc(key).push();
649 }
650
651 match used_cultures.get(key) {
652 Some(duplicate) => {
653 let msg =
654 format!("trying to split religion of culture {key} multiple times");
655 let msg_other = "first split here";
656 err(ErrorKey::DuplicateField)
657 .msg(msg)
658 .loc(key)
659 .loc_msg(duplicate, msg_other)
660 .push();
661 }
662 None => {
663 used_cultures.insert(key.clone());
664 }
665 }
666
667 let mut vd = Validator::new(block, data);
668 let mut sum_fractions = 0_i64;
669
670 vd.validate_item_key_values(Item::Religion, |_, mut vd| {
671 vd.numeric_range(0.0..=1.0);
672
673 sum_fractions += vd.value().get_fixed_number().unwrap_or(0);
674 });
675
676 sum_fractions_warner(sum_fractions, block);
677 });
678
679 if !only_one_culture {
680 let msg = "split_religion must contain one culture block";
681 err(ErrorKey::DuplicateItem).msg(msg).loc(block).push();
682 }
683 });
684 }
685}
686
687pub fn validate_create_state(
688 _key: &Token,
689 _block: &Block,
690 _data: &Everything,
691 sc: &mut ScopeContext,
692 mut vd: Validator,
693 _tooltipped: Tooltipped,
694) {
695 vd.field_target("country", sc, Scopes::Country);
698 vd.field_list_items("owned_provinces", Item::Province);
699 vd.field_choice("state_type", STATE_TYPES);
700}
701
702pub fn validate_form_government(
703 _key: &Token,
704 _block: &Block,
705 _data: &Everything,
706 sc: &mut ScopeContext,
707 mut vd: Validator,
708 _tooltipped: Tooltipped,
709) {
710 vd.field_script_value("value", sc);
711 vd.multi_field_item("interest_group_type", Item::InterestGroup);
712}
713
714pub fn validate_set_secret_goal(
715 _key: &Token,
716 _block: &Block,
717 _data: &Everything,
718 sc: &mut ScopeContext,
719 mut vd: Validator,
720 _tooltipped: Tooltipped,
721) {
722 vd.req_field("country");
723 vd.advice_field("tcountry", "documentation says tcountry but it's just country");
724 vd.req_field("secret_goal");
725 vd.field_item_or_target("country", sc, Item::Country, Scopes::Country);
726 vd.field_item("secret_goal", Item::SecretGoal);
727}
728
729pub fn validate_post_notification(
730 _key: &Token,
731 mut vd: ValueValidator,
732 sc: &mut ScopeContext,
733 _tooltipped: Tooltipped,
734) {
735 vd.item(Item::Message);
736 vd.implied_localization_sc("notification_", "_name", sc);
737 vd.implied_localization_sc("notification_", "_desc", sc);
738 vd.implied_localization_sc("notification_", "_tooltip", sc);
739}
740
741pub fn validate_progress(
742 _key: &Token,
743 _block: &Block,
744 _data: &Everything,
745 sc: &mut ScopeContext,
746 mut vd: Validator,
747 _tooltipped: Tooltipped,
748) {
749 vd.req_field("value");
750 vd.req_field("name");
751 vd.field_script_value("value", sc);
752 vd.field_item("name", Item::ScriptedProgressBar);
753}
754
755pub fn validate_join_war(
756 _key: &Token,
757 _block: &Block,
758 _data: &Everything,
759 sc: &mut ScopeContext,
760 mut vd: Validator,
761 _tooltipped: Tooltipped,
762) {
763 vd.req_field("target");
764 vd.req_field("side");
765 vd.field_target("target", sc, Scopes::Country);
766 vd.field_target("side", sc, Scopes::Country);
767}
768
769pub fn validate_create_truce(
770 _key: &Token,
771 _block: &Block,
772 _data: &Everything,
773 sc: &mut ScopeContext,
774 mut vd: Validator,
775 _tooltipped: Tooltipped,
776) {
777 vd.req_field("country");
778 vd.req_field("months");
779 vd.advice_field("tcountry", "documentation says tcountry but it's just country");
780 vd.field_target("country", sc, Scopes::Country);
781 vd.field_integer("months");
783}
784
785pub fn validate_create_power_bloc(
786 _key: &Token,
787 _block: &Block,
788 _data: &Everything,
789 sc: &mut ScopeContext,
790 mut vd: Validator,
791 _tooltipped: Tooltipped,
792) {
793 vd.req_field("name");
794 vd.req_field("map_color");
795 vd.req_field("identity");
796 vd.field_validated_sc("name", sc, validate_desc);
798 vd.field_validated_block("map_color", validate_color);
800 vd.field_item("identity", Item::PowerBlocIdentity);
801 vd.multi_field_item("principle", Item::Principle);
802 vd.multi_field_target("member", sc, Scopes::Country);
803
804 vd.field_date("founding_date");
807}
808
809pub fn validate_create_lobby(
810 _key: &Token,
811 _block: &Block,
812 _data: &Everything,
813 sc: &mut ScopeContext,
814 mut vd: Validator,
815 _tooltipped: Tooltipped,
816) {
817 vd.req_field("type");
818 vd.req_field("target");
819 vd.field_item("type", Item::PoliticalLobby);
820 vd.field_target("target", sc, Scopes::Country);
821 vd.multi_field_target("add_interest_group", sc, Scopes::InterestGroup);
822 vd.field_choice("lobby_formation_reason", LOBBY_FORMATION_REASON);
824}
825
826pub fn validate_create_movement(
827 _key: &Token,
828 _block: &Block,
829 _data: &Everything,
830 sc: &mut ScopeContext,
831 mut vd: Validator,
832 _tooltipped: Tooltipped,
833) {
834 vd.req_field("type");
835 vd.field_item("type", Item::PoliticalMovement);
836 vd.advice_field("movement_type", "docs say movement_type but it's just type");
837 vd.field_target("religion", sc, Scopes::Religion);
838 vd.field_target("culture", sc, Scopes::Culture);
839}
840
841pub fn validate_create_catalyst(
842 _key: &Token,
843 _block: &Block,
844 _data: &Everything,
845 sc: &mut ScopeContext,
846 mut vd: Validator,
847 _tooltipped: Tooltipped,
848) {
849 vd.req_field("type");
850 vd.req_field("target");
851 vd.field_item("type", Item::DiplomaticCatalyst);
852 vd.field_target("target", sc, Scopes::Country);
853}
854
855pub fn validate_change_appeasement(
856 _key: &Token,
857 _block: &Block,
858 _data: &Everything,
859 sc: &mut ScopeContext,
860 mut vd: Validator,
861 _tooltipped: Tooltipped,
862) {
863 vd.req_field("amount");
864 vd.req_field("factor");
865 vd.field_item("factor", Item::PoliticalLobbyAppeasement);
866 vd.field_script_value("amount", sc);
867}
868
869pub fn validate_pop_wealth(
871 _key: &Token,
872 _block: &Block,
873 _data: &Everything,
874 sc: &mut ScopeContext,
875 mut vd: Validator,
876 _tooltipped: Tooltipped,
877) {
878 vd.req_field("wealth_distribution");
879 vd.field_script_value("wealth_distribution", sc);
880 vd.field_bool("update_loyalties");
881}
882
883pub fn validate_kill_character(
884 _key: &Token,
885 bv: &BV,
886 data: &Everything,
887 _sc: &mut ScopeContext,
888 _tooltipped: Tooltipped,
889) {
890 match bv {
891 BV::Value(value) => {
892 let mut vd = ValueValidator::new(value, data);
894 vd.bool();
895 }
896 BV::Block(block) => {
897 let mut vd = Validator::new(block, data);
899 vd.set_case_sensitive(false);
900 vd.field_bool("value");
901 vd.field_bool("hidden");
902 }
903 }
904}
905
906pub fn validate_kill_population(
909 key: &Token,
910 _block: &Block,
911 _data: &Everything,
912 sc: &mut ScopeContext,
913 mut vd: Validator,
914 _tooltipped: Tooltipped,
915) {
916 let percent = key.is("kill_population_percent") || key.is("kill_population_percent_in_state");
917 if percent {
918 vd.field_numeric_range("percent", 0.0..=1.0);
919 } else {
920 vd.field_integer("value");
921 }
922 vd.field_target("culture", sc, Scopes::Culture);
923 vd.field_target("religion", sc, Scopes::Religion);
924 vd.field_target("interest_group", sc, Scopes::InterestGroup);
925 vd.field_item("pop_type", Item::PopType);
926 vd.field_choice("strata", STRATA);
927}
928
929pub fn validate_pop_literacy(
930 _key: &Token,
931 _block: &Block,
932 _data: &Everything,
933 sc: &mut ScopeContext,
934 mut vd: Validator,
935 _tooltipped: Tooltipped,
936) {
937 vd.field_script_value("literacy_rate", sc);
938}
939
940pub fn validate_move_partial_pop(
941 _key: &Token,
942 _block: &Block,
943 _data: &Everything,
944 sc: &mut ScopeContext,
945 mut vd: Validator,
946 _tooltipped: Tooltipped,
947) {
948 vd.req_field("state");
949 vd.field_target("state", sc, Scopes::State);
950 vd.field_script_value("population", sc);
952 vd.field_script_value("population_ratio", sc);
953}
954
955pub fn validate_set_hub_name(
956 _key: &Token,
957 _block: &Block,
958 _data: &Everything,
959 _sc: &mut ScopeContext,
960 mut vd: Validator,
961 _tooltipped: Tooltipped,
962) {
963 vd.req_field("type");
964 vd.req_field("name");
965 vd.field_choice("type", &["city", "farm", "mine", "port", "wood"]);
966 vd.field_item("name", Item::Localization);
967}
968
969pub fn validate_sort(
970 _key: &Token,
971 _block: &Block,
972 _data: &Everything,
973 sc: &mut ScopeContext,
974 mut vd: Validator,
975 _tooltipped: Tooltipped,
976) {
977 vd.req_field("name");
978 vd.req_field("order");
979 if let Some(name) = vd.field_identifier("name", "list name") {
980 sc.open_scope(Scopes::all(), name.clone());
982 vd.field_script_value("order", sc);
983 sc.close();
984 }
985}
986
987pub fn validate_owes_obligation(
988 _key: &Token,
989 _block: &Block,
990 _data: &Everything,
991 sc: &mut ScopeContext,
992 mut vd: Validator,
993 _tooltipped: Tooltipped,
994) {
995 vd.req_field("country");
996 vd.req_field("setting");
997 vd.field_target("country", sc, Scopes::Country);
998 vd.field_bool("setting");
999}
1000
1001pub fn validate_owner_of_provinces(
1002 _key: &Token,
1003 _block: &Block,
1004 _data: &Everything,
1005 sc: &mut ScopeContext,
1006 mut vd: Validator,
1007 _tooltipped: Tooltipped,
1008) {
1009 vd.req_field("country");
1010 vd.req_field("provinces");
1011 vd.field_target("country", sc, Scopes::Country);
1012 vd.field_list_items("provinces", Item::Province);
1013}
1014
1015pub fn validate_violate_sovereignty_join(
1016 _key: &Token,
1017 _block: &Block,
1018 _data: &Everything,
1019 sc: &mut ScopeContext,
1020 mut vd: Validator,
1021 _tooltipped: Tooltipped,
1022) {
1023 vd.req_field("violator");
1026 vd.req_field("target");
1027 vd.req_field("join_violator");
1028 vd.field_target("violator", sc, Scopes::Country);
1029 vd.field_target("target", sc, Scopes::Country);
1030 vd.field_bool("join_violator");
1031}
1032
1033pub fn validate_ruling_ig(
1034 _key: &Token,
1035 _block: &Block,
1036 data: &Everything,
1037 _sc: &mut ScopeContext,
1038 mut vd: Validator,
1039 _tooltipped: Tooltipped,
1040) {
1041 for token in vd.values() {
1042 data.verify_exists(Item::InterestGroup, token);
1043 }
1044}
1045
1046pub fn validate_start_tutorial(
1047 _key: &Token,
1048 _block: &Block,
1049 _data: &Everything,
1050 sc: &mut ScopeContext,
1051 mut vd: Validator,
1052 _tooltipped: Tooltipped,
1053) {
1054 vd.req_field("tutorial_lesson");
1057 vd.field_item("tutorial_lesson", Item::TutorialLesson);
1058 vd.field_target("journal_entry", sc, Scopes::JournalEntry);
1059}
1060
1061pub fn validate_audio_event(
1062 _key: &Token,
1063 _block: &Block,
1064 _data: &Everything,
1065 _sc: &mut ScopeContext,
1066 mut vd: Validator,
1067 _tooltipped: Tooltipped,
1068) {
1069 vd.field_value("persistent_object"); vd.field_value("event"); }
1072
1073pub fn validate_withdraw(
1074 _key: &Token,
1075 _block: &Block,
1076 _data: &Everything,
1077 sc: &mut ScopeContext,
1078 mut vd: Validator,
1079 _tooltipped: Tooltipped,
1080) {
1081 vd.field_target("country", sc, Scopes::Country);
1082}
1083
1084pub fn validate_create_treaty(
1085 _key: &Token,
1086 block: &Block,
1087 _data: &Everything,
1088 sc: &mut ScopeContext,
1089 mut vd: Validator,
1090 _tooltipped: Tooltipped,
1091) {
1092 vd.field_localization("name", sc);
1093 vd.req_field_one_of(&["first_country", "amends"]);
1094 vd.req_field_one_of(&["second_country", "amends"]);
1095 vd.field_target_ok_this("first_country", sc, Scopes::Country);
1096 vd.field_target_ok_this("second_country", sc, Scopes::Country);
1097 vd.field_target_ok_this("amends", sc, Scopes::Treaty);
1098
1099 vd.field_bool("is_draft");
1100 if block.has_key("amends") && !block.get_field_bool("is_draft").unwrap_or(true) {
1101 let msg = "treaties that are amendments must be drafts";
1102 let loc = block.get_key("draft").unwrap();
1104 err(ErrorKey::Validation).msg(msg).loc(loc).push();
1105 }
1106
1107 vd.field_date("entered_into_force_on");
1108 vd.field_validated_block("binding_period", |block, data| {
1109 let mut vd = Validator::new(block, data);
1110 validate_optional_duration(&mut vd, sc);
1111 });
1112
1113 if !block.has_key("amends") && !block.has_key("articles_to_create") {
1114 let msg = "treaties that are not amendments must create at least one article";
1115 err(ErrorKey::Validation).msg(msg).loc(block).push();
1116 }
1117 vd.field_validated_block("articles_to_create", |block, data| {
1118 let mut vd = Validator::new(block, data);
1119 let mut count = 0;
1120 for block in vd.blocks() {
1121 count += 1;
1122 validate_treaty_article(block, data, sc);
1123 }
1124 if count == 0 {
1125 let msg = "treaties that are not amendments must create at least one article";
1126 err(ErrorKey::Validation).msg(msg).loc(block).push();
1127 }
1128 });
1129}
1130
1131pub fn validate_tariff_level(
1132 _key: &Token,
1133 _block: &Block,
1134 _data: &Everything,
1135 sc: &mut ScopeContext,
1136 mut vd: Validator,
1137 _tooltipped: Tooltipped,
1138) {
1139 vd.field_target("goods", sc, Scopes::Goods);
1140 vd.field_choice("level", TARIFF_LEVELS);
1141}
1142
1143pub fn validate_activate_building(
1144 _key: &Token,
1145 _block: &Block,
1146 _data: &Everything,
1147 _sc: &mut ScopeContext,
1148 mut vd: Validator,
1149 _tooltipped: Tooltipped,
1150) {
1151 vd.multi_field_item("building", Item::BuildingType);
1152}
1153
1154pub fn validate_execute_event_option(
1155 _key: &Token,
1156 _block: &Block,
1157 _data: &Everything,
1158 _sc: &mut ScopeContext,
1159 mut vd: Validator,
1160 _tooltipped: Tooltipped,
1161) {
1162 vd.req_field("event");
1163 vd.req_field("option");
1164 vd.field_item("event", Item::Event);
1165 vd.field_integer_range("option", 0..);
1167}
1168
1169pub fn validate_national_awakening(
1170 _key: &Token,
1171 _block: &Block,
1172 _data: &Everything,
1173 sc: &mut ScopeContext,
1174 mut vd: Validator,
1175 _tooltipped: Tooltipped,
1176) {
1177 vd.req_field("culture");
1178 vd.req_field("months");
1179 vd.field_target("culture", sc, Scopes::Culture);
1180 vd.field_script_value("months", sc);
1181 vd.field_target("state_region", sc, Scopes::StateRegion);
1182}
1183
1184pub fn validate_add_amendment(
1185 _key: &Token,
1186 _block: &Block,
1187 _data: &Everything,
1188 sc: &mut ScopeContext,
1189 mut vd: Validator,
1190 _tooltipped: Tooltipped,
1191) {
1192 vd.req_field("type");
1193 vd.req_field("sponsor");
1194 vd.field_item("type", Item::Amendment);
1195 vd.field_target("sponsor", sc, Scopes::InterestGroup);
1196 vd.field_script_value("cooldown", sc);
1197 vd.field_script_value("timeout", sc);
1198}
1199
1200pub fn validate_spawn_entity_effect(
1201 _key: &Token,
1202 _block: &Block,
1203 _data: &Everything,
1204 sc: &mut ScopeContext,
1205 mut vd: Validator,
1206 _tooltipped: Tooltipped,
1207) {
1208 vd.req_field("name");
1209 vd.req_field("duration");
1210 vd.field_item("name", Item::Entity);
1211 vd.field_script_value("duration", sc);
1212}
1213
1214pub fn validate_teleport_to_front(
1215 _key: &Token,
1216 bv: &BV,
1217 data: &Everything,
1218 sc: &mut ScopeContext,
1219 _tooltipped: Tooltipped,
1220) {
1221 match bv {
1222 BV::Value(token) => {
1223 let mut vd = ValueValidator::new(token, data);
1224 vd.target(sc, Scopes::Front);
1225 }
1226 BV::Block(block) => {
1227 let mut vd = Validator::new(block, data);
1228 vd.field_target("front", sc, Scopes::Front);
1229 vd.field_target("base_camp", sc, Scopes::Province);
1230 }
1231 }
1232}