1use crate::block::{BV, Block};
2use crate::ck3::data::legends::LegendChronicle;
3use crate::ck3::tables::misc::{
4 BANNED_TITLE_HISTORY_TYPES, LEGEND_QUALITY, OUTBREAK_INTENSITIES, TITLE_HISTORY_TYPES,
5};
6use crate::ck3::validate::{
7 validate_random_culture, validate_random_faith, validate_random_traits_list,
8};
9use crate::context::{ScopeContext, Temporary};
10use crate::data::effect_localization::validate_effect_localization;
11use crate::desc::validate_desc;
12use crate::effect::{validate_effect, validate_effect_internal};
13use crate::effect_validation::validate_random_list;
14use crate::everything::Everything;
15use crate::helpers::{TigerHashSet, stringify_choices, stringify_list};
16use crate::item::Item;
17use crate::lowercase::Lowercase;
18use crate::report::{ErrorKey, Severity, err, warn};
19use crate::scopes::Scopes;
20use crate::script_value::{validate_non_dynamic_script_value, validate_script_value};
21use crate::special_tokens::SpecialTokens;
22use crate::token::Token;
23use crate::tooltipped::Tooltipped;
24use crate::trigger::{validate_target, validate_target_ok_this};
25use crate::validate::{
26 ListType, validate_duration, validate_identifier, validate_mandatory_duration,
27 validate_optional_duration, validate_optional_duration_int, validate_possibly_named_color,
28};
29use crate::validator::{Validator, ValueValidator};
30
31pub fn validate_add_activity_log_entry(
32 key: &Token,
33 block: &Block,
34 data: &Everything,
35 sc: &mut ScopeContext,
36 mut vd: Validator,
37 tooltipped: Tooltipped,
38 special_tokens: &mut SpecialTokens,
39) -> bool {
40 let caller = Lowercase::new(key.as_str());
41 vd.req_field("key");
42 vd.req_field("character");
43 if let Some(token) = vd.field_value("key") {
44 let loca = format!("{token}_title");
45 data.verify_exists_implied(Item::Localization, &loca, token);
46 }
47 vd.field_script_value("score", sc);
48 vd.field_validated_block("tags", |b, data| {
49 let mut vd = Validator::new(b, data);
50 vd.values(); });
52 vd.field_bool("show_in_conclusion");
53 vd.field_target("character", sc, Scopes::Character);
54 vd.field_target("target", sc, Scopes::Character);
55 vd.field_target("location", sc, Scopes::Province);
56 vd.field_target("artifact", sc, Scopes::Artifact);
57 validate_effect_internal(
59 &caller,
60 ListType::None,
61 block,
62 data,
63 sc,
64 &mut vd,
65 tooltipped,
66 special_tokens,
67 )
68}
69
70pub fn validate_add_artifact_history(
71 _key: &Token,
72 _block: &Block,
73 _data: &Everything,
74 sc: &mut ScopeContext,
75 mut vd: Validator,
76 _tooltipped: Tooltipped,
77) {
78 vd.req_field("type");
79 vd.req_field("recipient");
80 vd.field_item("type", Item::ArtifactHistory);
81 vd.field_date("date");
82 vd.field_target("actor", sc, Scopes::Character);
83 vd.field_target("recipient", sc, Scopes::Character);
84 vd.field_target("location", sc, Scopes::Province);
85}
86
87pub fn validate_add_artifact_title_history(
88 _key: &Token,
89 _block: &Block,
90 _data: &Everything,
91 sc: &mut ScopeContext,
92 mut vd: Validator,
93 _tooltipped: Tooltipped,
94) {
95 vd.req_field("target");
96 vd.req_field("date");
97 vd.field_target("target", sc, Scopes::LandedTitle);
98 vd.field_date("date");
99}
100
101pub fn validate_add_from_contribution(
102 _key: &Token,
103 _block: &Block,
104 _data: &Everything,
105 sc: &mut ScopeContext,
106 mut vd: Validator,
107 _tooltipped: Tooltipped,
108) {
109 vd.field_script_value("prestige", sc);
110 vd.field_script_value("gold", sc);
111 vd.field_script_value("piety", sc);
112 vd.field_script_value("influence", sc);
113 vd.field_script_value("merit", sc);
114 vd.field_script_value("renown", sc);
115 vd.field_script_value("treasury", sc);
116 vd.field_script_value("herd", sc);
117 vd.field_script_value("provisions", sc);
118 vd.field_script_value("barter_goods", sc);
120 vd.field_validated_block("opinion", |block, data| {
121 let mut vd = Validator::new(block, data);
122 vd.field_item("modifier", Item::OpinionModifier);
123 });
124}
125
126pub fn validate_add_hook(
127 _key: &Token,
128 _block: &Block,
129 data: &Everything,
130 sc: &mut ScopeContext,
131 mut vd: Validator,
132 _tooltipped: Tooltipped,
133) {
134 vd.req_field("type");
135 vd.req_field("target");
136 vd.field_item("type", Item::Hook);
137 vd.field_target("target", sc, Scopes::Character);
138 if let Some(token) = vd.field_value("secret")
139 && !data.item_exists(Item::Secret, token.as_str())
140 {
141 validate_target(token, data, sc, Scopes::Secret);
142 }
143 validate_optional_duration(&mut vd, sc);
144}
145
146pub fn validate_add_opinion(
147 _key: &Token,
148 _block: &Block,
149 _data: &Everything,
150 sc: &mut ScopeContext,
151 mut vd: Validator,
152 _tooltipped: Tooltipped,
153) {
154 vd.req_field("modifier");
155 vd.req_field("target");
156 vd.field_item("modifier", Item::OpinionModifier);
157 vd.field_target("target", sc, Scopes::Character);
158 vd.field_script_value("opinion", sc); validate_optional_duration(&mut vd, sc);
160}
161
162pub fn validate_add_relation_flag(
163 _key: &Token,
164 _block: &Block,
165 _data: &Everything,
166 sc: &mut ScopeContext,
167 mut vd: Validator,
168 _tooltipped: Tooltipped,
169) {
170 vd.req_field("relation");
171 vd.req_field("flag");
172 vd.req_field("target");
173 vd.field_item("relation", Item::Relation);
174 vd.field_value("flag");
176 vd.field_target("target", sc, Scopes::Character);
177}
178
179pub fn validate_scheme_cooldown(
180 _key: &Token,
181 _block: &Block,
182 _data: &Everything,
183 sc: &mut ScopeContext,
184 mut vd: Validator,
185 _tooltipped: Tooltipped,
186) {
187 vd.req_field("target");
188 vd.req_field("type");
189 vd.field_target("target", sc, Scopes::Character);
190 vd.field_item("type", Item::Scheme);
191 validate_optional_duration_int(&mut vd);
192}
193
194pub fn validate_scheme_modifier(
195 key: &Token,
196 block: &Block,
197 data: &Everything,
198 sc: &mut ScopeContext,
199 mut vd: Validator,
200 _tooltipped: Tooltipped,
201) {
202 vd.req_field("type");
203 if let Some(token) = vd.field_value("type") {
204 data.verify_exists(Item::Modifier, token);
205 data.database.validate_call(Item::Modifier, token, block, data, sc);
206 data.database.validate_property_use(Item::Modifier, token, data, key, key.as_str());
207 }
208 validate_optional_duration(&mut vd, sc);
209}
210
211pub fn validate_add_secret(
212 _key: &Token,
213 _block: &Block,
214 _data: &Everything,
215 sc: &mut ScopeContext,
216 mut vd: Validator,
217 _tooltipped: Tooltipped,
218) {
219 vd.req_field("type");
220 vd.field_item("type", Item::Secret);
221 vd.field_target("target", sc, Scopes::Character);
222 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
223 sc.define_name_token(name.as_str(), Scopes::Secret, name, Temporary::No);
224 }
225}
226
227pub fn validate_guest_subset(
228 _key: &Token,
229 _block: &Block,
230 _data: &Everything,
231 sc: &mut ScopeContext,
232 mut vd: Validator,
233 _tooltipped: Tooltipped,
234) {
235 vd.req_field("name");
236 vd.req_field("target");
237 vd.field_item("name", Item::GuestSubset);
238 vd.field_target("target", sc, Scopes::Character);
239 vd.field_item("phase", Item::ActivityPhase);
240}
241
242pub fn validate_add_trait_xp(
243 _key: &Token,
244 _block: &Block,
245 _data: &Everything,
246 sc: &mut ScopeContext,
247 mut vd: Validator,
248 _tooltipped: Tooltipped,
249) {
250 vd.req_field("trait");
251 vd.req_field("value");
252 vd.field_item_or_target("trait", sc, Item::Trait, Scopes::Trait);
254 vd.field_item("track", Item::TraitTrack);
255 vd.field_script_value("value", sc);
256}
257
258pub fn validate_add_modifier(
260 key: &Token,
261 bv: &BV,
262 data: &Everything,
263 sc: &mut ScopeContext,
264 _tooltipped: Tooltipped,
265) {
266 let caller = key.as_str().to_ascii_lowercase();
267 let visible = caller == "add_character_modifier"
268 || caller == "add_house_modifier"
269 || caller == "add_dynasty_modifier"
270 || caller == "add_county_modifier"
271 || caller == "add_house_unity_modifier"
272 || caller == "add_legend_county_modifier"
273 || caller == "add_legend_owner_modifier"
274 || caller == "add_legend_province_modifier";
275 match bv {
276 BV::Value(token) => {
277 data.verify_exists(Item::Modifier, token);
278 if visible {
279 data.verify_exists(Item::Localization, token);
280 }
281 let block = Block::new(key.loc);
282 data.database.validate_call(Item::Modifier, token, &block, data, sc);
283 data.database.validate_property_use(Item::Modifier, token, data, key, key.as_str());
284 }
285 BV::Block(block) => {
286 let mut vd = Validator::new(block, data);
287 vd.set_case_sensitive(false);
288 vd.req_field("modifier");
289 if let Some(token) = vd.field_value("modifier") {
290 data.verify_exists(Item::Modifier, token);
291 if visible && !block.has_key("desc") {
292 data.verify_exists(Item::Localization, token);
293 }
294 data.database.validate_call(Item::Modifier, token, block, data, sc);
295 data.database.validate_property_use(Item::Modifier, token, data, key, key.as_str());
296 }
297 vd.field_validated_sc("desc", sc, validate_desc);
298 vd.field_validated_sc("duration_desc", sc, validate_desc);
299 validate_optional_duration(&mut vd, sc);
300 }
301 }
302}
303
304pub fn validate_add_truce(
305 _key: &Token,
306 block: &Block,
307 _data: &Everything,
308 sc: &mut ScopeContext,
309 mut vd: Validator,
310 _tooltipped: Tooltipped,
311) {
312 vd.req_field("character");
313 vd.field_target("character", sc, Scopes::Character);
314 vd.field_bool("override");
315 vd.field_choice("result", &["white_peace", "victory", "defeat"]);
316 vd.field_item("casus_belli", Item::CasusBelli);
317 vd.field_validated_sc("name", sc, validate_desc);
318 vd.field_target("war", sc, Scopes::War);
319 validate_optional_duration(&mut vd, sc);
320 if block.has_key("war") && block.has_key("casus_belli") {
321 let msg = "cannot use both `war` and `casus_belli`";
322 err(ErrorKey::Validation).msg(msg).loc(block).push();
323 }
324}
325
326pub fn validate_add_unity(
327 _key: &Token,
328 _block: &Block,
329 _data: &Everything,
330 sc: &mut ScopeContext,
331 mut vd: Validator,
332 _tooltipped: Tooltipped,
333) {
334 vd.req_field("value");
335 vd.req_field("character");
336 vd.field_script_value("value", sc);
337 vd.field_target("character", sc, Scopes::Character);
338 vd.field_validated_sc("desc", sc, validate_desc);
339}
340
341pub fn validate_assign_council_task(
342 _key: &Token,
343 _block: &Block,
344 _data: &Everything,
345 sc: &mut ScopeContext,
346 mut vd: Validator,
347 _tooltipped: Tooltipped,
348) {
349 vd.req_field("council_task");
350 vd.req_field("target");
351 vd.field_target("council_task", sc, Scopes::CouncilTask);
352 vd.field_target("target", sc, Scopes::Character);
353 vd.field_bool("fire_on_actions");
354}
355
356pub fn validate_assign_councillor_type(
357 _key: &Token,
358 _block: &Block,
359 _data: &Everything,
360 sc: &mut ScopeContext,
361 mut vd: Validator,
362 _tooltipped: Tooltipped,
363) {
364 vd.req_field("type");
365 vd.req_field("target");
366 vd.field_item("type", Item::CouncilPosition);
367 vd.field_target("target", sc, Scopes::Character);
368 vd.field_bool("fire_on_actions");
369 vd.field_bool("remove_existing_councillor");
370}
371
372pub fn validate_battle_event(
373 _key: &Token,
374 _block: &Block,
375 data: &Everything,
376 sc: &mut ScopeContext,
377 mut vd: Validator,
378 _tooltipped: Tooltipped,
379) {
380 vd.req_field("left_portrait");
381 vd.req_field("key");
382 if let Some(token) = vd.field_value("key") {
383 let loca = format!("{token}_friendly");
384 data.verify_exists_implied(Item::Localization, &loca, token);
385 let loca = format!("{token}_enemy");
386 data.verify_exists_implied(Item::Localization, &loca, token);
387 }
388 vd.field_target("left_portrait", sc, Scopes::Character);
389 vd.field_target("right_portrait", sc, Scopes::Character);
390 vd.field_value("type"); vd.field_bool("target_right"); }
393
394pub fn validate_change_cultural_acceptance(
395 _key: &Token,
396 _block: &Block,
397 _data: &Everything,
398 sc: &mut ScopeContext,
399 mut vd: Validator,
400 _tooltipped: Tooltipped,
401) {
402 vd.req_field("target");
403 vd.req_field("value");
404 vd.field_target("target", sc, Scopes::Culture);
405 vd.field_script_value("value", sc);
406 vd.field_validated_sc("desc", sc, validate_desc);
407}
408
409pub fn validate_change_liege(
410 _key: &Token,
411 _block: &Block,
412 _data: &Everything,
413 sc: &mut ScopeContext,
414 mut vd: Validator,
415 _tooltipped: Tooltipped,
416) {
417 vd.req_field("liege");
418 vd.req_field("change");
419 vd.field_target("liege", sc, Scopes::Character);
420 vd.field_target("change", sc, Scopes::TitleAndVassalChange);
421}
422
423pub fn validate_change_struggle_phase(
424 _key: &Token,
425 bv: &BV,
426 data: &Everything,
427 _sc: &mut ScopeContext,
428 _tooltipped: Tooltipped,
429) {
430 match bv {
431 BV::Value(token) => {
432 data.verify_exists(Item::StrugglePhase, token);
433 }
434 BV::Block(block) => {
435 let mut vd = Validator::new(block, data);
436 vd.set_case_sensitive(false);
437 vd.req_field("struggle_phase");
438 vd.req_field("with_transition");
439 vd.field_item("struggle_phase", Item::StrugglePhase);
440 vd.field_bool("with_transition");
441 }
442 }
443}
444
445pub fn validate_change_struggle_phase_duration(
446 _key: &Token,
447 _block: &Block,
448 _data: &Everything,
449 sc: &mut ScopeContext,
450 mut vd: Validator,
451 _tooltipped: Tooltipped,
452) {
453 vd.req_field("duration");
454 vd.field_validated_block_sc("duration", sc, |block, data, sc| {
455 if let Some(bv) = block.get_field("points") {
456 validate_script_value(bv, data, sc);
457 } else {
458 validate_duration(block, data, sc);
459 }
460 });
461}
462
463pub fn validate_change_title_holder(
464 _key: &Token,
465 _block: &Block,
466 _data: &Everything,
467 sc: &mut ScopeContext,
468 mut vd: Validator,
469 _tooltipped: Tooltipped,
470) {
471 vd.req_field("holder");
472 vd.req_field("change");
473 vd.field_target("holder", sc, Scopes::Character);
474 vd.field_target("change", sc, Scopes::TitleAndVassalChange);
475 vd.field_bool("take_baronies");
476 vd.field_target("government_base", sc, Scopes::Character);
477}
478
479pub fn validate_change_trait_rank(
480 key: &Token,
481 _block: &Block,
482 _data: &Everything,
483 sc: &mut ScopeContext,
484 mut vd: Validator,
485 _tooltipped: Tooltipped,
486) {
487 let caller = key.as_str().to_ascii_lowercase();
488 vd.req_field("trait");
489 vd.req_field("rank");
490 vd.field_item("trait", Item::Trait);
492 vd.field_script_value("rank", sc);
493 if caller == "change_trait_rank" {
494 vd.field_script_value("max", sc);
495 }
496}
497
498pub fn validate_copy_localized_text(
499 _key: &Token,
500 _block: &Block,
501 _data: &Everything,
502 sc: &mut ScopeContext,
503 mut vd: Validator,
504 _tooltipped: Tooltipped,
505) {
506 vd.req_field("key");
507 vd.req_field("target");
508 vd.field_value("key");
509 vd.field_target("target", sc, Scopes::Character);
510}
511
512pub fn validate_create_accolade(
513 _key: &Token,
514 _block: &Block,
515 _data: &Everything,
516 sc: &mut ScopeContext,
517 mut vd: Validator,
518 _tooltipped: Tooltipped,
519) {
520 vd.req_field("knight");
521 vd.req_field("primary");
522 vd.field_target("knight", sc, Scopes::Character);
523 vd.field_item("primary", Item::AccoladeType);
524 vd.advice_field("secondary", "removed in 1.19");
525 vd.field_item("name", Item::Localization);
526}
527
528pub fn validate_create_artifact(
529 key: &Token,
530 _block: &Block,
531 _data: &Everything,
532 sc: &mut ScopeContext,
533 mut vd: Validator,
534 _tooltipped: Tooltipped,
535) {
536 let caller = key.as_str().to_ascii_lowercase();
537 vd.field_validated_sc("name", sc, validate_desc);
538 vd.field_validated_sc("description", sc, validate_desc);
539 vd.field_item("rarity", Item::ArtifactRarity);
540 vd.field_item("type", Item::ArtifactType);
541 vd.multi_field_item("modifier", Item::Modifier);
542 vd.field_script_value("durability", sc);
543 vd.field_script_value("max_durability", sc);
544 vd.field_bool("decaying");
545 vd.multi_field_validated_block_sc("history", sc, validate_artifact_history);
546 vd.field_item("template", Item::ArtifactTemplate);
547 vd.field_item("visuals", Item::ArtifactVisual);
548 vd.field_bool("generate_history");
549 vd.field_script_value("quality", sc);
550 vd.field_script_value("wealth", sc);
551 vd.field_target("creator", sc, Scopes::Character);
552 vd.field_target(
553 "visuals_source",
554 sc,
555 Scopes::LandedTitle | Scopes::Dynasty | Scopes::DynastyHouse,
556 );
557
558 if caller == "create_artifact" {
559 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
560 sc.define_name_token(name.as_str(), Scopes::Artifact, name, Temporary::No);
561 }
562 vd.field_target("title_history", sc, Scopes::LandedTitle);
563 vd.field_date("title_history_date");
564 } else {
565 vd.ban_field("save_scope_as", || "`create_artifact`");
566 vd.ban_field("title_history", || "`create_artifact`");
567 vd.ban_field("title_history_date", || "`create_artifact`");
568 }
569}
570
571pub fn validate_create_character(
572 _key: &Token,
573 block: &Block,
574 data: &Everything,
575 sc: &mut ScopeContext,
576 mut vd: Validator,
577 _tooltipped: Tooltipped,
578) {
579 vd.replaced_field("save_event_target_as", "save_scope_as");
581 vd.replaced_field("save_temporary_event_target_as", "save_temporary_scope_as");
582 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
583 sc.define_name_token(name.as_str(), Scopes::Character, name, Temporary::No);
584 }
585 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
586 sc.define_name_token(name.as_str(), Scopes::Character, name, Temporary::Yes);
587 }
588
589 vd.field_validated_sc("name", sc, validate_desc);
590 vd.field_script_value("age", sc);
591 if let Some(token) = vd.field_value("gender")
592 && !token.is("male")
593 && !token.is("female")
594 {
595 validate_target_ok_this(token, data, sc, Scopes::Character);
596 }
597 vd.field_script_value("gender_female_chance", sc);
598 vd.field_target_ok_this("opposite_gender", sc, Scopes::Character);
599 vd.multi_field_item("trait", Item::Trait);
600 vd.multi_field_validated_block_sc("random_traits_list", sc, validate_random_traits_list);
601 vd.field_bool("random_traits");
602 vd.field_script_value("health", sc);
603 vd.field_script_value("fertility", sc);
604 vd.field_target_ok_this("mother", sc, Scopes::Character);
605 vd.field_target_ok_this("father", sc, Scopes::Character);
606 vd.field_target_ok_this("real_father", sc, Scopes::Character);
607 vd.req_field_one_of(&["location", "employer"]);
608 vd.field_target_ok_this("employer", sc, Scopes::Character);
609 vd.field_target_ok_this("location", sc, Scopes::Province);
610 if let Some(token) = vd.field_value("template") {
611 data.verify_exists(Item::CharacterTemplate, token);
613 data.validate_call(Item::CharacterTemplate, token, block, sc);
614 }
615 vd.field_item("template", Item::CharacterTemplate); vd.field_target_ok_this("template_character", sc, Scopes::Character);
617 vd.field_item_or_target("faith", sc, Item::Faith, Scopes::Faith);
618 vd.field_validated_block_sc("random_faith", sc, validate_random_faith);
619 vd.field_item_or_target("random_faith_in_religion", sc, Item::Religion, Scopes::Faith);
620 vd.field_item_or_target("culture", sc, Item::Culture, Scopes::Culture);
621 vd.field_validated_block_sc("random_culture", sc, validate_random_culture);
622 vd.field_value("random_culture_in_group");
624 vd.field_item_or_target("dynasty_house", sc, Item::House, Scopes::DynastyHouse);
625 if let Some(token) = vd.field_value("dynasty")
626 && !token.is("generate")
627 && !token.is("inherit")
628 && !token.is("none")
629 {
630 validate_target(token, data, sc, Scopes::Dynasty);
631 }
632 vd.field_validated_value("ethnicity", |_, mut vd| {
633 vd.maybe_is("culture");
634 vd.maybe_is("mother");
635 vd.maybe_is("father");
636 vd.maybe_is("parents");
637 vd.item(Item::Ethnicity);
638 });
639 vd.field_block("ethnicities");
641 vd.field_script_value("diplomacy", sc);
642 vd.field_script_value("intrigue", sc);
643 vd.field_script_value("martial", sc);
644 vd.field_script_value("learning", sc);
645 vd.field_script_value("prowess", sc);
646 vd.field_script_value("stewardship", sc);
647 vd.field_validated_key_block("after_creation", |key, block, data| {
648 sc.open_scope(Scopes::Character, key.clone());
649 validate_effect(block, data, sc, Tooltipped::No); sc.close();
651 });
652}
653
654pub fn validate_create_character_memory(
655 key: &Token,
656 block: &Block,
657 _data: &Everything,
658 sc: &mut ScopeContext,
659 mut vd: Validator,
660 _tooltipped: Tooltipped,
661) {
662 vd.req_field("type");
663 vd.field_item("type", Item::MemoryType);
664 vd.field_validated_block("participants", |b, data| {
666 let mut vd = Validator::new(b, data);
667 let memtype = block.get_field_value("type");
668 vd.unknown_value_fields(|key, token| {
669 if let Some(memtype) = memtype
670 && !data.item_has_property(Item::MemoryType, memtype.as_str(), key.as_str())
671 {
672 let msg = format!("memory type `{memtype}` does not define participant `{key}`");
673 warn(ErrorKey::Validation).msg(msg).loc(key).push();
674 }
675 validate_target_ok_this(token, data, sc, Scopes::Character);
676 });
677 });
678 vd.field_validated_block_sc("duration", sc, validate_duration);
679 sc.define_name("new_memory", Scopes::CharacterMemory, key);
680}
681
682pub fn validate_create_confederation(
683 key: &Token,
684 _block: &Block,
685 _data: &Everything,
686 sc: &mut ScopeContext,
687 mut vd: Validator,
688 _tooltipped: Tooltipped,
689) {
690 vd.req_field("name");
691 vd.field_validated_sc("name", sc, validate_desc);
692 vd.field_target("type", sc, Scopes::ConfederationType);
693 vd.field_target("leader", sc, Scopes::DynastyHouse);
694 sc.define_name("new_confederation", Scopes::Confederation, key);
695}
696
697pub fn validate_create_dynamic_title(
698 key: &Token,
699 _block: &Block,
700 _data: &Everything,
701 sc: &mut ScopeContext,
702 mut vd: Validator,
703 _tooltipped: Tooltipped,
704) {
705 vd.req_field("tier");
706 vd.req_field("name");
707 vd.field_choice("tier", &["duchy", "kingdom", "empire", "hegemony"]);
708 vd.field_validated_sc("name", sc, validate_desc);
709 vd.advice_field("adjective", "changed to adj in 1.13");
710 vd.field_validated_sc("adj", sc, validate_desc);
711 vd.field_validated_sc("pre", sc, validate_desc);
712 vd.field_validated_sc("article", sc, validate_desc);
713 sc.define_name("new_title", Scopes::LandedTitle, key);
714}
715
716pub fn validate_create_nomad_title(
717 _key: &Token,
718 _block: &Block,
719 _data: &Everything,
720 sc: &mut ScopeContext,
721 mut vd: Validator,
722 _tooltipped: Tooltipped,
723) {
724 vd.field_validated_sc("name", sc, validate_desc);
725 vd.field_validated_sc("prefix", sc, validate_desc);
726 vd.field_validated_sc("adjective", sc, validate_desc);
727 vd.field_target("holder", sc, Scopes::Character);
728 vd.field_item("government", Item::GovernmentType);
729 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
730 sc.define_name_token(name.as_str(), Scopes::LandedTitle, name, Temporary::No);
731 }
732}
733
734pub fn validate_create_holy_order(
735 _key: &Token,
736 _block: &Block,
737 _data: &Everything,
738 sc: &mut ScopeContext,
739 mut vd: Validator,
740 _tooltipped: Tooltipped,
741) {
742 vd.req_field("leader");
743 vd.req_field("capital");
744 vd.field_target("leader", sc, Scopes::Character);
745 vd.field_target("capital", sc, Scopes::LandedTitle);
746 vd.field_item("name", Item::Localization);
747 vd.field_item("coat_of_arms", Item::Coa);
748 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
749 sc.define_name_token(name.as_str(), Scopes::HolyOrder, name, Temporary::No);
750 }
751 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
752 sc.define_name_token(name.as_str(), Scopes::HolyOrder, name, Temporary::Yes);
753 }
754}
755
756pub fn validate_create_title_and_vassal_change(
757 _key: &Token,
758 _block: &Block,
759 _data: &Everything,
760 sc: &mut ScopeContext,
761 mut vd: Validator,
762 _tooltipped: Tooltipped,
763) {
764 vd.req_field("type");
765 vd.req_field("save_scope_as");
766 if let Some(history_type) = vd.field_value("type") {
767 let valid_types: Vec<_> = TITLE_HISTORY_TYPES
768 .iter()
769 .filter(|t| !BANNED_TITLE_HISTORY_TYPES.contains(t))
770 .copied()
771 .collect();
772 if BANNED_TITLE_HISTORY_TYPES.contains(&history_type.as_str()) {
773 let msg = format!(
774 "types {} cannot be used from script",
775 stringify_list(BANNED_TITLE_HISTORY_TYPES)
776 );
777 let info = format!("choose_from {}", stringify_choices(&valid_types));
778 err(ErrorKey::Choice).msg(msg).info(info).loc(history_type).push();
779 } else {
780 vd.field_choice("type", &valid_types);
781 }
782 }
783 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
784 sc.define_name_token(name.as_str(), Scopes::TitleAndVassalChange, name, Temporary::No);
785 }
786 vd.field_bool("add_claim_on_loss");
787}
788
789pub fn validate_delay_travel_plan(
790 _key: &Token,
791 _block: &Block,
792 _data: &Everything,
793 sc: &mut ScopeContext,
794 mut vd: Validator,
795 _tooltipped: Tooltipped,
796) {
797 vd.field_bool("add");
798 validate_optional_duration(&mut vd, sc);
799}
800
801pub fn validate_divide_war_chest(
802 _key: &Token,
803 _block: &Block,
804 _data: &Everything,
805 sc: &mut ScopeContext,
806 mut vd: Validator,
807 _tooltipped: Tooltipped,
808) {
809 vd.field_bool("defenders");
810 vd.field_script_value("fraction", sc);
811 vd.field_bool("gold");
812 vd.field_bool("piety");
813 vd.field_bool("prestige");
814}
815
816pub fn validate_duel(
817 key: &Token,
818 block: &Block,
819 data: &Everything,
820 sc: &mut ScopeContext,
821 mut vd: Validator,
822 tooltipped: Tooltipped,
823 special_tokens: &mut SpecialTokens,
824) -> bool {
825 vd.field_item("skill", Item::Skill);
826 vd.field_list_items("skills", Item::Skill);
827 vd.field_target("target", sc, Scopes::Character);
828 vd.field_script_value("value", sc);
829 if let Some(value) = vd.field_value("localization") {
830 validate_effect_localization(value, data, tooltipped);
831 }
832 vd.field_validated_value("challenge_variable", |_, mut vd| {
833 vd.identifier("variable name");
834 let loca = format!("{}_name", vd.value());
835 data.verify_exists_implied(Item::Localization, &loca, vd.value());
836 vd.accept();
837 });
838 vd.field_validated_list("challenge_variables", |token, data| {
839 validate_identifier(token, "variable name", Severity::Error);
840 let loca = format!("{token}_name");
841 data.verify_exists_implied(Item::Localization, &loca, token);
842 });
843 sc.define_name("duel_value", Scopes::Value, key);
844 let has_tooltip = validate_random_list(key, block, data, sc, vd, tooltipped, special_tokens);
845 if has_tooltip {
846 special_tokens.insert(key);
847 }
848 has_tooltip
849}
850
851pub fn validate_faction_start_war(
852 _key: &Token,
853 _block: &Block,
854 _data: &Everything,
855 sc: &mut ScopeContext,
856 mut vd: Validator,
857 _tooltipped: Tooltipped,
858) {
859 vd.field_target("title", sc, Scopes::LandedTitle);
860}
861
862pub fn validate_force_add_to_agent_slot(
863 _key: &Token,
864 _block: &Block,
865 _data: &Everything,
866 sc: &mut ScopeContext,
867 mut vd: Validator,
868 _tooltipped: Tooltipped,
869) {
870 vd.field_target("agent_slot", sc, Scopes::AgentSlot);
871 validate_optional_duration(&mut vd, sc);
872}
873
874pub fn validate_force_vote_as(
875 _key: &Token,
876 _block: &Block,
877 _data: &Everything,
878 sc: &mut ScopeContext,
879 mut vd: Validator,
880 _tooltipped: Tooltipped,
881) {
882 vd.field_target("target", sc, Scopes::Character);
883 validate_optional_duration(&mut vd, sc);
884}
885
886pub fn validate_imprison(
887 _key: &Token,
888 _block: &Block,
889 _data: &Everything,
890 sc: &mut ScopeContext,
891 mut vd: Validator,
892 _tooltipped: Tooltipped,
893) {
894 vd.field_target("target", sc, Scopes::Character);
895 vd.field_item("type", Item::PrisonType);
896 }
898
899pub fn validate_join_faction_forced(
900 _key: &Token,
901 _block: &Block,
902 _data: &Everything,
903 sc: &mut ScopeContext,
904 mut vd: Validator,
905 _tooltipped: Tooltipped,
906) {
907 vd.field_target("faction", sc, Scopes::Faction);
908 vd.field_target("forced_by", sc, Scopes::Character);
909 validate_optional_duration(&mut vd, sc);
910}
911
912pub fn validate_make_pregnant(
913 _key: &Token,
914 _block: &Block,
915 _data: &Everything,
916 sc: &mut ScopeContext,
917 mut vd: Validator,
918 _tooltipped: Tooltipped,
919) {
920 vd.field_target("father", sc, Scopes::Character);
921 vd.field_integer("number_of_children");
922 vd.field_bool("known_bastard");
923}
924
925pub fn validate_move_budget(
926 key: &Token,
927 _block: &Block,
928 _data: &Everything,
929 sc: &mut ScopeContext,
930 mut vd: Validator,
931 _tooltipped: Tooltipped,
932) {
933 if key.is("move_budget_gold") {
934 vd.field_script_value_no_breakdown("gold", sc);
935 } else if key.is("move_budget_treasury") {
936 vd.field_script_value_no_breakdown("treasury", sc);
937 }
938 let choices = &["budget_war_chest", "budget_reserved", "budget_short_term", "budget_long_term"];
939 vd.field_choice("from", choices);
940 vd.field_choice("to", choices);
941}
942
943pub fn validate_open_interaction_window(
944 key: &Token,
945 _block: &Block,
946 _data: &Everything,
947 sc: &mut ScopeContext,
948 mut vd: Validator,
949 _tooltipped: Tooltipped,
950) {
951 let caller = key.as_str().to_ascii_lowercase();
952 vd.req_field("interaction");
953 vd.req_field("actor");
954 vd.req_field("recipient");
955 vd.field_value("interaction"); vd.field_bool("redirect");
957 vd.field_target_ok_this("actor", sc, Scopes::Character);
958 vd.field_target_ok_this("recipient", sc, Scopes::Character);
959 vd.field_target_ok_this("secondary_actor", sc, Scopes::Character);
960 vd.field_target_ok_this("secondary_recipient", sc, Scopes::Character);
961 if caller == "open_interaction_window" {
962 vd.field_target("target_title", sc, Scopes::LandedTitle);
963 }
964 if caller == "run_interaction" {
965 vd.field_choice("execute_threshold", &["accept", "maybe", "decline"]);
966 vd.field_choice("send_threshold", &["accept", "maybe", "decline"]);
967 }
968}
969
970pub fn validate_pay_gold(
971 _key: &Token,
972 _block: &Block,
973 _data: &Everything,
974 sc: &mut ScopeContext,
975 mut vd: Validator,
976 _tooltipped: Tooltipped,
977) {
978 vd.req_field("target");
979 vd.field_target("target", sc, Scopes::Character);
980 vd.field_script_value("gold", sc);
981 vd.field_bool("yearly_income");
983}
984
985pub fn validate_pay_income(
986 _key: &Token,
987 _block: &Block,
988 _data: &Everything,
989 sc: &mut ScopeContext,
990 mut vd: Validator,
991 _tooltipped: Tooltipped,
992) {
993 vd.req_field("target");
994 vd.field_target("target", sc, Scopes::Character);
995 validate_optional_duration(&mut vd, sc);
996}
997
998pub fn validate_current_phase_guest_subset(
999 _key: &Token,
1000 _block: &Block,
1001 _data: &Everything,
1002 sc: &mut ScopeContext,
1003 mut vd: Validator,
1004 _tooltipped: Tooltipped,
1005) {
1006 vd.req_field("name");
1007 vd.req_field("target");
1008 vd.field_item("name", Item::GuestSubset);
1009 vd.field_target("target", sc, Scopes::Character);
1010}
1011
1012pub fn validate_remove_opinion(
1013 _key: &Token,
1014 _block: &Block,
1015 _data: &Everything,
1016 sc: &mut ScopeContext,
1017 mut vd: Validator,
1018 _tooltipped: Tooltipped,
1019) {
1020 vd.req_field("target");
1021 vd.req_field("modifier");
1022 vd.field_target("target", sc, Scopes::Character);
1023 vd.field_item("modifier", Item::OpinionModifier);
1024 vd.field_bool("single");
1025}
1026
1027pub fn validate_replace_court_position(
1028 _key: &Token,
1029 _block: &Block,
1030 _data: &Everything,
1031 sc: &mut ScopeContext,
1032 mut vd: Validator,
1033 _tooltipped: Tooltipped,
1034) {
1035 vd.req_field("recipient");
1036 vd.req_field("court_position");
1037 vd.field_target("recipient", sc, Scopes::Character);
1038 vd.field_target("holder", sc, Scopes::Character);
1039 vd.field_item("court_position", Item::CourtPosition);
1040}
1041
1042pub fn validate_revoke_court_position(
1043 _key: &Token,
1044 _block: &Block,
1045 _data: &Everything,
1046 sc: &mut ScopeContext,
1047 mut vd: Validator,
1048 _tooltipped: Tooltipped,
1049) {
1050 vd.req_field("court_position");
1051 vd.field_item("court_position", Item::CourtPosition);
1052 vd.field_target("recipient", sc, Scopes::Character);
1053 vd.field_target("holder", sc, Scopes::Character);
1054}
1055
1056pub fn validate_save_opinion_value(
1057 key: &Token,
1058 _block: &Block,
1059 _data: &Everything,
1060 sc: &mut ScopeContext,
1061 mut vd: Validator,
1062 _tooltipped: Tooltipped,
1063) {
1064 vd.req_field("name");
1065 vd.req_field("target");
1066 let temp =
1067 if key.is("save_temporary_opinion_value_as") { Temporary::Yes } else { Temporary::No };
1068 if let Some(name) = vd.field_value("name") {
1069 sc.define_name_token(name.as_str(), Scopes::Value, name, temp);
1070 }
1071 vd.field_target("target", sc, Scopes::Character);
1072}
1073
1074pub fn validate_scheme_freeze(
1075 _key: &Token,
1076 _block: &Block,
1077 _data: &Everything,
1078 sc: &mut ScopeContext,
1079 mut vd: Validator,
1080 _tooltipped: Tooltipped,
1081) {
1082 vd.field_item("reason", Item::Localization);
1083 validate_optional_duration(&mut vd, sc);
1084}
1085
1086pub fn validate_set_council_task(
1087 _key: &Token,
1088 _block: &Block,
1089 _data: &Everything,
1090 sc: &mut ScopeContext,
1091 mut vd: Validator,
1092 _tooltipped: Tooltipped,
1093) {
1094 vd.req_field("task_type");
1095 vd.field_item("task_type", Item::CouncilTask);
1097 vd.field_target("target", sc, Scopes::Character | Scopes::Province);
1099}
1100
1101pub fn validate_set_culture_name(
1102 _key: &Token,
1103 _block: &Block,
1104 _data: &Everything,
1105 sc: &mut ScopeContext,
1106 mut vd: Validator,
1107 _tooltipped: Tooltipped,
1108) {
1109 vd.req_field("noun");
1110 vd.field_validated_sc("noun", sc, validate_desc);
1111 vd.field_validated_sc("collective_noun", sc, validate_desc);
1112 vd.field_validated_sc("prefix", sc, validate_desc);
1113}
1114
1115pub fn validate_set_death_reason(
1116 _key: &Token,
1117 _block: &Block,
1118 _data: &Everything,
1119 sc: &mut ScopeContext,
1120 mut vd: Validator,
1121 _tooltipped: Tooltipped,
1122) {
1123 vd.req_field("death_reason");
1124 vd.field_item("death_reason", Item::DeathReason);
1125 vd.field_target("killer", sc, Scopes::Character);
1126 vd.field_target("artifact", sc, Scopes::Artifact);
1127}
1128
1129pub fn validate_set_ghw_target(
1130 key: &Token,
1131 _block: &Block,
1132 _data: &Everything,
1133 sc: &mut ScopeContext,
1134 mut vd: Validator,
1135 _tooltipped: Tooltipped,
1136) {
1137 let caller = key.as_str().to_ascii_lowercase();
1138 vd.req_field("target_character");
1139 vd.req_field("target_title");
1140 vd.field_target("target_character", sc, Scopes::Character);
1141 vd.field_target("target_title", sc, Scopes::LandedTitle);
1142 if caller == "start_great_holy_war" {
1143 vd.field_script_value("delay", sc);
1144 vd.field_target("war", sc, Scopes::War);
1145 }
1146}
1147
1148pub fn validate_set_legend_chapter(
1149 _key: &Token,
1150 _block: &Block,
1151 _data: &Everything,
1152 _sc: &mut ScopeContext,
1153 mut vd: Validator,
1154 _tooltipped: Tooltipped,
1155) {
1156 vd.field_item("name", Item::LegendChapter);
1157 vd.field_item("localization_key", Item::Localization);
1158}
1159
1160pub fn validate_set_legend_property(
1161 _key: &Token,
1162 _block: &Block,
1163 _data: &Everything,
1164 sc: &mut ScopeContext,
1165 mut vd: Validator,
1166 _tooltipped: Tooltipped,
1167) {
1168 vd.field_item("name", Item::LegendProperty);
1169 vd.field_target("target", sc, Scopes::all());
1171}
1172
1173pub fn validate_setup_cb(
1174 key: &Token,
1175 _block: &Block,
1176 _data: &Everything,
1177 sc: &mut ScopeContext,
1178 mut vd: Validator,
1179 _tooltipped: Tooltipped,
1180) {
1181 let caller = key.as_str().to_ascii_lowercase();
1182 vd.req_field("attacker");
1183 vd.req_field("defender");
1184 vd.field_target("attacker", sc, Scopes::Character);
1186 vd.field_target("defender", sc, Scopes::Character);
1187 vd.field_target("change", sc, Scopes::TitleAndVassalChange);
1188 vd.field_bool("victory");
1189 if caller == "setup_claim_cb" {
1190 vd.req_field("claimant");
1191 vd.field_target("claimant", sc, Scopes::Character);
1192 vd.field_bool("take_occupied");
1193 vd.field_bool("civil_war");
1194 vd.field_choice("titles", &["target_titles", "faction_titles"]);
1195 } else if caller == "setup_de_jure_cb" {
1196 vd.field_target("title", sc, Scopes::LandedTitle);
1197 } else if caller == "setup_invasion_cb" {
1198 vd.field_identifier("titles", "list name");
1199 vd.field_bool("take_occupied");
1200 vd.field_target("claimant", sc, Scopes::Character);
1201 }
1202 sc.define_name("cb_prestige_factor", Scopes::Value, key);
1203}
1204
1205pub fn validate_spawn_army(
1206 _key: &Token,
1207 _block: &Block,
1208 _data: &Everything,
1209 sc: &mut ScopeContext,
1210 mut vd: Validator,
1211 _tooltipped: Tooltipped,
1212) {
1213 vd.req_field("location");
1215 vd.field_script_value("levies", sc);
1216 vd.multi_field_validated_block("men_at_arms", |b, data| {
1217 let mut vd = Validator::new(b, data);
1218 vd.req_field("type");
1219 vd.field_item("type", Item::MenAtArms);
1220 vd.field_script_value("men", sc);
1221 vd.field_script_value("stacks", sc);
1222 vd.field_bool("inheritable"); });
1224 vd.field_target("location", sc, Scopes::Province);
1225 vd.field_target("origin", sc, Scopes::Province);
1226 vd.field_target("war", sc, Scopes::War);
1227 vd.field_bool("war_keep_on_attacker_victory");
1228 vd.field_bool("inheritable");
1229 vd.field_bool("uses_supply");
1230 vd.field_target("army", sc, Scopes::Army);
1231 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1232 sc.define_name_token(name.as_str(), Scopes::Army, name, Temporary::No);
1233 }
1234 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
1235 sc.define_name_token(name.as_str(), Scopes::Army, name, Temporary::Yes);
1236 }
1237 vd.field_validated_sc("name", sc, validate_desc);
1238}
1239
1240pub fn validate_start_scheme(
1241 _key: &Token,
1242 _block: &Block,
1243 _data: &Everything,
1244 sc: &mut ScopeContext,
1245 mut vd: Validator,
1246 _tooltipped: Tooltipped,
1247) {
1248 vd.req_field("type");
1249 vd.req_field_one_of(&[
1250 "target_character",
1251 "target_title",
1252 "target_culture",
1253 "target_faith",
1254 "targets_nothing",
1255 ]);
1256 vd.field_item("type", Item::Scheme);
1257 vd.field_target("contract", sc, Scopes::TaskContract);
1258 vd.advice_field("target", "replaced with target_character in 1.13");
1259 vd.field_target("target_character", sc, Scopes::Character);
1260 vd.field_target("target_title", sc, Scopes::LandedTitle);
1261 vd.field_target("target_culture", sc, Scopes::Culture);
1262 vd.field_target("target_faith", sc, Scopes::Faith);
1263 vd.field_bool("targets_nothing");
1264 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1265 sc.define_name_token(name.as_str(), Scopes::Scheme, name, Temporary::No);
1266 }
1267
1268 vd.field_target("artifact", sc, Scopes::Artifact);
1272}
1273
1274pub fn validate_start_struggle(
1275 _key: &Token,
1276 _block: &Block,
1277 _data: &Everything,
1278 _sc: &mut ScopeContext,
1279 mut vd: Validator,
1280 _tooltipped: Tooltipped,
1281) {
1282 vd.req_field("struggle_type");
1283 vd.req_field("start_phase");
1284 vd.field_item("struggle_type", Item::Struggle);
1285 vd.field_item("start_phase", Item::StrugglePhase);
1286}
1287
1288pub fn validate_start_travel_plan(
1289 _key: &Token,
1290 _block: &Block,
1291 data: &Everything,
1292 sc: &mut ScopeContext,
1293 mut vd: Validator,
1294 _tooltipped: Tooltipped,
1295) {
1296 vd.req_field("destination");
1297 for token in vd.multi_field_value("destination") {
1298 validate_target(token, data, sc, Scopes::Province);
1299 }
1300 vd.field_target("travel_leader", sc, Scopes::Character);
1301 for token in vd.multi_field_value("companion") {
1302 validate_target(token, data, sc, Scopes::Character);
1303 }
1304 vd.field_bool("travel_with_domicile");
1305 vd.field_bool("can_cancel_planning");
1306 vd.field_bool("players_use_planner");
1307 vd.field_bool("return_trip");
1308 vd.field_event("on_arrival_event", sc);
1309 vd.field_action("on_arrival_on_action", sc);
1310 vd.field_event("on_start_event", sc);
1311 vd.field_action("on_start_on_action", sc);
1312 vd.field_event("on_travel_planner_cancel_event", sc);
1313 vd.field_action("on_travel_planner_cancel_on_action", sc);
1314 vd.field_choice("on_arrival_destinations", &["all", "first", "last", "all_but_last"]);
1315}
1316
1317pub fn validate_start_war(
1318 _key: &Token,
1319 _block: &Block,
1320 data: &Everything,
1321 sc: &mut ScopeContext,
1322 mut vd: Validator,
1323 _tooltipped: Tooltipped,
1324) {
1325 vd.field_item("casus_belli", Item::CasusBelli);
1326 vd.field_item("cb", Item::CasusBelli);
1327 vd.field_target("target", sc, Scopes::Character);
1328 vd.field_target_ok_this("claimant", sc, Scopes::Character);
1329 for token in vd.multi_field_value("target_title") {
1330 validate_target(token, data, sc, Scopes::LandedTitle);
1331 }
1332}
1333
1334pub fn validate_stress_impact(
1335 _key: &Token,
1336 _block: &Block,
1337 data: &Everything,
1338 sc: &mut ScopeContext,
1339 mut vd: Validator,
1340 _tooltipped: Tooltipped,
1341) {
1342 vd.field_script_value("base", sc);
1343 vd.unknown_fields(|token, bv| {
1344 data.verify_exists(Item::Trait, token);
1345 validate_non_dynamic_script_value(bv, data);
1346 });
1347}
1348
1349pub fn validate_try_create_important_action(
1350 _key: &Token,
1351 _block: &Block,
1352 data: &Everything,
1353 sc: &mut ScopeContext,
1354 mut vd: Validator,
1355 _tooltipped: Tooltipped,
1356) {
1357 vd.req_field("important_action_type");
1358 vd.field_item("important_action_type", Item::ImportantAction);
1359 vd.unknown_value_fields(|_, value| {
1360 validate_target_ok_this(value, data, sc, Scopes::all_but_none());
1361 });
1362}
1363
1364pub fn validate_try_create_suggestion(
1365 _key: &Token,
1366 _block: &Block,
1367 _data: &Everything,
1368 sc: &mut ScopeContext,
1369 mut vd: Validator,
1370 _tooltipped: Tooltipped,
1371) {
1372 vd.req_field("suggestion_type");
1373 vd.field_item("suggestion_type", Item::Suggestion);
1374 vd.field_target_ok_this("actor", sc, Scopes::Character);
1375 vd.field_target_ok_this("recipient", sc, Scopes::Character);
1376 vd.field_target_ok_this("secondary_actor", sc, Scopes::Character);
1377 vd.field_target_ok_this("secondary_recipient", sc, Scopes::Character);
1378 vd.field_target_ok_this("landed_title", sc, Scopes::LandedTitle);
1379}
1380
1381pub fn validate_contract_set_obligation_level(
1382 _key: &Token,
1383 _block: &Block,
1384 data: &Everything,
1385 sc: &mut ScopeContext,
1386 mut vd: Validator,
1387 _tooltipped: Tooltipped,
1388) {
1389 vd.req_field("type");
1390 vd.req_field("level");
1391 if let Some(token) = vd.field_value("type")
1392 && !data.item_exists(Item::SubjectContract, token.as_str())
1393 {
1394 validate_target(token, data, sc, Scopes::VassalContract);
1395 }
1396 if let Some(token) = vd.field_value("level")
1397 && !token.is_integer()
1398 && !data.item_exists(Item::SubjectContractObligationLevel, token.as_str())
1399 {
1400 validate_target(token, data, sc, Scopes::VassalObligationLevel);
1401 }
1402}
1403
1404pub fn validate_add_artifact_modifier(
1405 _key: &Token,
1406 mut vd: ValueValidator,
1407 _sc: &mut ScopeContext,
1408 _tooltipped: Tooltipped,
1409) {
1410 vd.item(Item::Modifier);
1411 }
1420
1421pub fn validate_generate_coa(
1422 _key: &Token,
1423 mut vd: ValueValidator,
1424 _sc: &mut ScopeContext,
1425 _tooltipped: Tooltipped,
1426) {
1427 vd.maybe_is("yes");
1428 vd.item(Item::CoaTemplateList);
1429}
1430
1431pub fn validate_set_coa(
1432 _key: &Token,
1433 mut vd: ValueValidator,
1434 sc: &mut ScopeContext,
1435 _tooltipped: Tooltipped,
1436) {
1437 let options = Scopes::LandedTitle | Scopes::Dynasty | Scopes::DynastyHouse;
1438 vd.item_or_target(sc, Item::Coa, options);
1439}
1440
1441pub fn validate_set_focus(
1442 _key: &Token,
1443 mut vd: ValueValidator,
1444 _sc: &mut ScopeContext,
1445 _tooltipped: Tooltipped,
1446) {
1447 vd.maybe_is("no");
1448 vd.item(Item::Focus);
1449}
1450
1451pub fn validate_set_title_name(
1452 _key: &Token,
1453 mut vd: ValueValidator,
1454 _sc: &mut ScopeContext,
1455 _tooltipped: Tooltipped,
1456) {
1457 vd.item(Item::Localization);
1458 vd.item_used_with_suffix(Item::Localization, "_adj");
1459}
1460
1461pub fn validate_activate_struggle_catalyst(
1462 _key: &Token,
1463 bv: &BV,
1464 data: &Everything,
1465 sc: &mut ScopeContext,
1466 _tooltipped: Tooltipped,
1467) {
1468 match bv {
1469 BV::Value(token) => data.verify_exists(Item::Catalyst, token),
1470 BV::Block(block) => {
1471 let mut vd = Validator::new(block, data);
1472 vd.set_case_sensitive(false);
1473 vd.req_field("catalyst");
1474 vd.req_field("character");
1475 vd.field_item("catalyst", Item::Catalyst);
1476 vd.field_target("character", sc, Scopes::Character);
1477 }
1478 }
1479}
1480
1481pub fn validate_add_character_flag(
1482 _key: &Token,
1483 bv: &BV,
1484 data: &Everything,
1485 sc: &mut ScopeContext,
1486 _tooltipped: Tooltipped,
1487) {
1488 match bv {
1489 BV::Value(_token) => (),
1490 BV::Block(block) => {
1491 let mut vd = Validator::new(block, data);
1492 vd.set_case_sensitive(false);
1493 vd.req_field("flag");
1494 vd.multi_field_value("flag");
1495 validate_optional_duration(&mut vd, sc);
1496 }
1497 }
1498}
1499
1500pub fn validate_add_dead_character_flag(
1501 _key: &Token,
1502 block: &Block,
1503 _data: &Everything,
1504 sc: &mut ScopeContext,
1505 mut vd: Validator,
1506 _tooltipped: Tooltipped,
1507) {
1508 vd.set_case_sensitive(false);
1509 vd.req_field("flag");
1510 vd.multi_field_value("flag");
1511 validate_mandatory_duration(block, &mut vd, sc);
1512}
1513
1514pub fn validate_begin_create_holding(
1515 _key: &Token,
1516 bv: &BV,
1517 data: &Everything,
1518 sc: &mut ScopeContext,
1519 _tooltipped: Tooltipped,
1520) {
1521 match bv {
1522 BV::Value(token) => data.verify_exists(Item::HoldingType, token),
1523 BV::Block(block) => {
1524 let mut vd = Validator::new(block, data);
1525 vd.set_case_sensitive(false);
1526 vd.req_field("type");
1527 vd.field_item("type", Item::HoldingType);
1528 vd.field_validated_block("refund_cost", |b, data| {
1529 let mut vd = Validator::new(b, data);
1530 vd.set_case_sensitive(false);
1531 vd.field_script_value("gold", sc);
1532 vd.field_script_value("prestige", sc);
1533 vd.field_script_value("piety", sc);
1534 });
1535 }
1536 }
1537}
1538
1539pub fn validate_change_first_name(
1540 _key: &Token,
1541 bv: &BV,
1542 data: &Everything,
1543 sc: &mut ScopeContext,
1544 _tooltipped: Tooltipped,
1545) {
1546 match bv {
1548 BV::Value(token) => {
1549 if data.item_exists(Item::Localization, token.as_str()) {
1550 data.mark_used(Item::Localization, token.as_str());
1551 } else {
1552 validate_target(token, data, sc, Scopes::Flag);
1553 }
1554 }
1555 BV::Block(block) => {
1556 let mut vd = Validator::new(block, data);
1557 vd.set_case_sensitive(false);
1558 vd.req_field("template_character");
1559 vd.field_target("template_character", sc, Scopes::Character);
1560 }
1561 }
1562}
1563
1564pub fn validate_close_view(
1565 _key: &Token,
1566 bv: &BV,
1567 data: &Everything,
1568 sc: &mut ScopeContext,
1569 _tooltipped: Tooltipped,
1570) {
1571 match bv {
1572 BV::Value(_token) => (), BV::Block(block) => {
1574 let mut vd = Validator::new(block, data);
1575 vd.set_case_sensitive(false);
1576 vd.req_field("view");
1577 vd.field_value("view"); vd.field_target("player", sc, Scopes::Character);
1579 }
1580 }
1581}
1582
1583pub fn validate_create_alliance(
1584 _key: &Token,
1585 bv: &BV,
1586 data: &Everything,
1587 sc: &mut ScopeContext,
1588 _tooltipped: Tooltipped,
1589) {
1590 match bv {
1591 BV::Value(token) => {
1592 validate_target(token, data, sc, Scopes::Character);
1593 }
1594 BV::Block(block) => {
1595 let mut vd = Validator::new(block, data);
1596 vd.set_case_sensitive(false);
1597 vd.req_field("target");
1598 vd.field_target("target", sc, Scopes::Character);
1599 vd.field_target_ok_this("allied_through_owner", sc, Scopes::Character);
1600 vd.field_target("allied_through_target", sc, Scopes::Character);
1601 }
1602 }
1603}
1604
1605pub fn validate_create_epidemic_outbreak(
1606 _key: &Token,
1607 _block: &Block,
1608 _data: &Everything,
1609 sc: &mut ScopeContext,
1610 mut vd: Validator,
1611 _tooltipped: Tooltipped,
1612) {
1613 vd.req_field("type");
1614 vd.req_field("intensity");
1615 vd.field_item("type", Item::EpidemicType);
1616 vd.field_choice("intensity", OUTBREAK_INTENSITIES);
1617 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1618 sc.define_name_token(name.as_str(), Scopes::Epidemic, name, Temporary::No);
1619 }
1620}
1621
1622pub fn validate_create_inspiration(
1623 _key: &Token,
1624 bv: &BV,
1625 data: &Everything,
1626 sc: &mut ScopeContext,
1627 _tooltipped: Tooltipped,
1628) {
1629 match bv {
1630 BV::Value(token) => data.verify_exists(Item::Inspiration, token),
1631 BV::Block(block) => {
1632 let mut vd = Validator::new(block, data);
1633 vd.set_case_sensitive(false);
1634 vd.req_field("type");
1635 vd.field_item("type", Item::Inspiration);
1636 vd.field_script_value("gold", sc);
1637 }
1638 }
1639}
1640
1641pub fn validate_create_story(
1642 _key: &Token,
1643 bv: &BV,
1644 data: &Everything,
1645 sc: &mut ScopeContext,
1646 _tooltipped: Tooltipped,
1647) {
1648 match bv {
1649 BV::Value(token) => data.verify_exists(Item::Story, token),
1650 BV::Block(block) => {
1651 let mut vd = Validator::new(block, data);
1652 vd.set_case_sensitive(false);
1653 vd.req_field("type");
1654 vd.field_item("type", Item::Story);
1655 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1656 sc.define_name_token(name.as_str(), Scopes::StoryCycle, name, Temporary::No);
1657 }
1658 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
1659 sc.define_name_token(name.as_str(), Scopes::StoryCycle, name, Temporary::Yes);
1660 }
1661 }
1662 }
1663}
1664
1665pub fn validate_death(
1666 _key: &Token,
1667 bv: &BV,
1668 data: &Everything,
1669 sc: &mut ScopeContext,
1670 _tooltipped: Tooltipped,
1671) {
1672 match bv {
1673 BV::Value(token) => {
1674 if !token.is("natural") {
1675 let msg = "expected `death = natural`";
1676 warn(ErrorKey::Validation).msg(msg).loc(token).push();
1677 }
1678 }
1679 BV::Block(block) => {
1680 let mut vd = Validator::new(block, data);
1681 vd.set_case_sensitive(false);
1682 vd.req_field("death_reason");
1683 vd.field_item("death_reason", Item::DeathReason);
1684 vd.field_target("killer", sc, Scopes::Character);
1685 vd.field_target("artifact", sc, Scopes::Artifact);
1686 }
1687 }
1688}
1689
1690pub fn validate_open_view(
1691 key: &Token,
1692 bv: &BV,
1693 data: &Everything,
1694 sc: &mut ScopeContext,
1695 _tooltipped: Tooltipped,
1696) {
1697 match bv {
1698 BV::Value(_token) => (), BV::Block(block) => {
1700 let mut vd = Validator::new(block, data);
1701 vd.set_case_sensitive(false);
1702 vd.req_field("view");
1703 vd.field_value("view"); vd.field_value("view_message"); vd.field_target("player", sc, Scopes::Character);
1706 if key.is("open_view_data") {
1707 vd.field_target("secondary_actor", sc, Scopes::Character); vd.field_target("data", sc, Scopes::all_but_none()); }
1710 }
1711 }
1712}
1713
1714pub fn validate_remove_courtier_or_guest(
1715 _key: &Token,
1716 bv: &BV,
1717 data: &Everything,
1718 sc: &mut ScopeContext,
1719 _tooltipped: Tooltipped,
1720) {
1721 match bv {
1722 BV::Value(token) => {
1723 validate_target(token, data, sc, Scopes::Character);
1724 }
1725 BV::Block(block) => {
1726 let mut vd = Validator::new(block, data);
1727 vd.set_case_sensitive(false);
1728 vd.req_field("character");
1729 vd.field_target("character", sc, Scopes::Character);
1730 vd.field_target("new_location", sc, Scopes::Province);
1731 }
1732 }
1733}
1734
1735pub fn validate_set_dead_character_variable(
1736 _key: &Token,
1737 block: &Block,
1738 _data: &Everything,
1739 sc: &mut ScopeContext,
1740 mut vd: Validator,
1741 _tooltipped: Tooltipped,
1742) {
1743 vd.req_field("name");
1744 vd.field_identifier("name", "variable name");
1745 vd.field_validated("value", |bv, data| match bv {
1746 BV::Value(token) => {
1747 validate_target_ok_this(token, data, sc, Scopes::all_but_none());
1748 }
1749 BV::Block(_) => validate_script_value(bv, data, sc),
1750 });
1751 validate_mandatory_duration(block, &mut vd, sc);
1752}
1753
1754pub fn validate_set_location(
1755 _key: &Token,
1756 bv: &BV,
1757 data: &Everything,
1758 sc: &mut ScopeContext,
1759 _tooltipped: Tooltipped,
1760) {
1761 match bv {
1762 BV::Value(token) => {
1763 validate_target(token, data, sc, Scopes::Province);
1764 }
1765 BV::Block(block) => {
1766 let mut vd = Validator::new(block, data);
1767 vd.set_case_sensitive(false);
1768 vd.req_field("location");
1769 vd.field_target("location", sc, Scopes::Province);
1770 vd.field_bool("stick_to_location");
1771 }
1772 }
1773}
1774
1775pub fn validate_set_owner(
1776 _key: &Token,
1777 bv: &BV,
1778 data: &Everything,
1779 sc: &mut ScopeContext,
1780 _tooltipped: Tooltipped,
1781) {
1782 match bv {
1783 BV::Value(token) => {
1784 validate_target(token, data, sc, Scopes::Character);
1785 }
1786 BV::Block(block) => {
1787 let mut vd = Validator::new(block, data);
1788 vd.set_case_sensitive(false);
1789 vd.req_field("target");
1790 vd.field_target("target", sc, Scopes::Character);
1791 vd.multi_field_validated_block_sc("history", sc, validate_artifact_history);
1792 vd.field_bool("generate_history");
1793 }
1794 }
1795}
1796
1797pub fn validate_set_relation(
1798 _key: &Token,
1799 bv: &BV,
1800 data: &Everything,
1801 sc: &mut ScopeContext,
1802 _tooltipped: Tooltipped,
1803) {
1804 match bv {
1805 BV::Value(token) => {
1806 validate_target(token, data, sc, Scopes::Character);
1807 }
1808 BV::Block(block) => {
1809 let mut vd = Validator::new(block, data);
1810 vd.set_case_sensitive(false);
1811 vd.req_field("target");
1812 vd.field_target("target", sc, Scopes::Character);
1815 vd.field_item("reason", Item::Localization);
1817 vd.field_item("copy_reason", Item::Relation);
1818 vd.field_target("province", sc, Scopes::Province);
1819 vd.field_target("involved_character", sc, Scopes::Character);
1820 }
1821 }
1822}
1823
1824fn validate_artifact_history(block: &Block, data: &Everything, sc: &mut ScopeContext) {
1825 let mut vd = Validator::new(block, data);
1826 vd.set_case_sensitive(false);
1827 vd.req_field("type");
1828 vd.field_item("type", Item::ArtifactHistory);
1829 vd.field_date("date");
1830 vd.field_target("actor", sc, Scopes::Character);
1831 vd.field_target("recipient", sc, Scopes::Character);
1832 vd.field_target("location", sc, Scopes::Province);
1833}
1834
1835pub fn validate_end_struggle(
1836 _value: &Token,
1837 mut vd: ValueValidator,
1838 _sc: &mut ScopeContext,
1839 _tooltipped: Tooltipped,
1840) {
1841 vd.maybe_is("yes");
1842 vd.item(Item::Localization); }
1844
1845pub fn validate_create_legend(
1846 key: &Token,
1847 _block: &Block,
1848 data: &Everything,
1849 sc: &mut ScopeContext,
1850 mut vd: Validator,
1851 _tooltipped: Tooltipped,
1852) {
1853 vd.req_field("type");
1854 vd.field_item("type", Item::LegendType);
1855 vd.req_field("quality");
1856 vd.field_choice("quality", LEGEND_QUALITY);
1857 vd.req_field("chronicle");
1858 vd.req_field("properties");
1859 vd.field_item("chronicle", Item::LegendChronicle);
1860 if let Some(chronicle_token) = vd.field_value("chronicle").cloned() {
1861 data.verify_exists(Item::LegendChronicle, &chronicle_token);
1862
1863 if let Some((_, _, chronicle)) =
1864 data.get_item::<LegendChronicle>(Item::LegendChronicle, chronicle_token.as_str())
1865 {
1866 vd.field_validated_key_block("properties", |key, block, data| {
1867 let mut found_properties = TigerHashSet::default();
1868 let mut vd = Validator::new(block, data);
1869 vd.unknown_value_fields(|key, value| {
1870 if let Some(scopes) = chronicle.properties.get(key).copied() {
1871 found_properties.insert(key.clone());
1872 validate_target(value, data, sc, scopes);
1873 } else {
1874 let msg =
1875 format!("property {key} not found in {chronicle_token} chronicle");
1876 err(ErrorKey::Validation).msg(msg).loc(key).push();
1877 }
1878 });
1879 for property in chronicle.properties.keys() {
1880 if !found_properties.contains(property) {
1881 let msg = format!("chronicle property {property} missing from properties");
1882 err(ErrorKey::Validation)
1883 .msg(msg)
1884 .loc(key)
1885 .loc_msg(property, "defined here")
1886 .push();
1887 }
1888 }
1889 });
1890 }
1891 }
1892 if key.is("create_legend") {
1894 vd.field_target("protagonist", sc, Scopes::Character);
1895 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1896 sc.define_name_token(name.as_str(), Scopes::Legend, name, Temporary::No);
1897 }
1898 }
1899}
1900
1901pub fn validate_change_maa_regiment_size(
1902 _key: &Token,
1903 bv: &BV,
1904 data: &Everything,
1905 sc: &mut ScopeContext,
1906 _tooltipped: Tooltipped,
1907) {
1908 match bv {
1909 BV::Value(_) => validate_script_value(bv, data, sc),
1910 BV::Block(block) => {
1911 let mut vd = Validator::new(block, data);
1912 vd.set_case_sensitive(false);
1913 vd.req_field("size");
1914 vd.field_script_value("size", sc);
1915 vd.field_bool("reinforce");
1916 }
1917 }
1918}
1919
1920pub fn validate_create_adventurer_title(
1921 _key: &Token,
1922 _block: &Block,
1923 _data: &Everything,
1924 sc: &mut ScopeContext,
1925 mut vd: Validator,
1926 _tooltipped: Tooltipped,
1927) {
1928 vd.req_field("name");
1929 vd.req_field("holder");
1930 vd.field_validated_sc("name", sc, validate_desc);
1931 vd.field_target("holder", sc, Scopes::Character);
1932 vd.field_validated_sc("prefix", sc, validate_desc);
1933 vd.field_validated_sc("adjective", sc, validate_desc);
1934 vd.field_item("government", Item::GovernmentType);
1935 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1936 sc.define_name_token(name.as_str(), Scopes::LandedTitle, name, Temporary::No);
1937 }
1938
1939 vd.field_validated_sc("article", sc, validate_desc);
1942}
1943
1944pub fn validate_start_best_war(
1945 _key: &Token,
1946 _block: &Block,
1947 _data: &Everything,
1948 _sc: &mut ScopeContext,
1949 mut vd: Validator,
1950 _tooltipped: Tooltipped,
1951) {
1952 fn sc_builder(key: &Token) -> ScopeContext {
1953 let mut sc = ScopeContext::new(Scopes::Character, key);
1954 sc.define_name("target_character", Scopes::Character, key);
1955 sc.define_name("target_title", Scopes::LandedTitle, key);
1956 sc.define_list("target_titles", Scopes::LandedTitle, key);
1957 sc.define_name("claimant", Scopes::Character, key);
1958 sc.define_name("casus_belli_type", Scopes::CasusBelliType, key);
1959 sc.define_name("has_hostage", Scopes::Bool, key);
1960 sc.define_name("score", Scopes::Value, key);
1961 sc
1962 }
1963
1964 vd.field_list_items("cb", Item::CasusBelli);
1965 vd.field_bool("recalculate_cb_targets");
1966 vd.field_trigger_builder("is_valid", Tooltipped::No, sc_builder);
1967 vd.field_effect_builder("on_success", Tooltipped::No, sc_builder);
1968 vd.field_effect_builder("on_failure", Tooltipped::No, sc_builder);
1969}
1970
1971pub fn validate_create_maa_regiment(
1972 _key: &Token,
1973 _block: &Block,
1974 _data: &Everything,
1975 sc: &mut ScopeContext,
1976 mut vd: Validator,
1977 _tooltipped: Tooltipped,
1978) {
1979 vd.req_field_one_of(&["type", "type_of"]);
1980 vd.field_item("type", Item::MenAtArms);
1981 vd.field_target("type_of", sc, Scopes::Regiment);
1982 vd.field_bool("check_can_recruit");
1983 vd.field_target("title", sc, Scopes::LandedTitle);
1984 vd.field_integer("size");
1985}
1986
1987pub fn validate_create_task_contract(
1988 _key: &Token,
1989 _block: &Block,
1990 _data: &Everything,
1991 sc: &mut ScopeContext,
1992 mut vd: Validator,
1993 _tooltipped: Tooltipped,
1994) {
1995 vd.req_field("task_contract_type");
1996 vd.req_field("task_contract_tier");
1997 vd.req_field("location");
1998
1999 vd.field_item("task_contract_type", Item::TaskContractType);
2000 vd.advice_field(
2001 "task_task_contract_tier",
2002 "docs say `task_task_contract_tier` but it's `task_contract_tier`",
2003 );
2004 vd.field_script_value("task_contract_tier", sc);
2005 vd.field_target("location", sc, Scopes::Province);
2006
2007 vd.field_target("task_contract_employer", sc, Scopes::Character);
2008 vd.field_target("destination", sc, Scopes::Province);
2009 vd.field_target("target", sc, Scopes::Character);
2010 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2011 sc.define_name_token(name.as_str(), Scopes::TaskContract, name, Temporary::No);
2012 }
2013
2014 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
2017 sc.define_name_token(name.as_str(), Scopes::TaskContract, name, Temporary::Yes);
2018 }
2019}
2020
2021pub fn validate_give_noble_family_title(
2022 _key: &Token,
2023 _block: &Block,
2024 _data: &Everything,
2025 sc: &mut ScopeContext,
2026 mut vd: Validator,
2027 _tooltipped: Tooltipped,
2028) {
2029 vd.field_validated_sc("name", sc, validate_desc);
2030 vd.field_choice("tier", &["county", "duchy"]);
2031 vd.field_validated_sc("article", sc, validate_desc);
2032 vd.field_item("government", Item::GovernmentType);
2033 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2034 sc.define_name_token(name.as_str(), Scopes::LandedTitle, name, Temporary::No);
2035 }
2036}
2037
2038pub fn validate_contracts_for_area(
2039 _key: &Token,
2040 _block: &Block,
2041 _data: &Everything,
2042 sc: &mut ScopeContext,
2043 mut vd: Validator,
2044 _tooltipped: Tooltipped,
2045) {
2046 vd.field_target("location", sc, Scopes::Province);
2047 vd.field_script_value("amount", sc);
2048 vd.field_list_items("group", Item::TaskContractGroup);
2049}
2050
2051pub fn validate_change_appointment_investment(
2052 _key: &Token,
2053 _block: &Block,
2054 _data: &Everything,
2055 sc: &mut ScopeContext,
2056 mut vd: Validator,
2057 _tooltipped: Tooltipped,
2058) {
2059 vd.req_field("target");
2060 vd.req_field("value");
2061 vd.field_target("target", sc, Scopes::Character);
2062 vd.field_target("investor", sc, Scopes::Character);
2063 vd.field_script_value("value", sc);
2064}
2065
2066pub fn validate_set_important_location(
2067 key: &Token,
2068 _block: &Block,
2069 _data: &Everything,
2070 sc: &mut ScopeContext,
2071 mut vd: Validator,
2072 _tooltipped: Tooltipped,
2073) {
2074 vd.field_target("title", sc, Scopes::LandedTitle);
2075 let mut sc = ScopeContext::new(Scopes::Character, key);
2085 sc.define_name("county", Scopes::LandedTitle, key);
2086 sc.define_name("title", Scopes::LandedTitle, key);
2087 sc.define_name("changed_top_liege", Scopes::Character, key);
2088
2089 vd.field_event("enter_realm_event", &mut sc);
2090 vd.field_action("enter_realm_on_action", &sc);
2091 vd.field_event("leave_realm_event", &mut sc);
2092 vd.field_action("leave_realm_on_action", &sc);
2093}
2094
2095pub fn validate_appoint_court_position(
2096 _key: &Token,
2097 _block: &Block,
2098 _data: &Everything,
2099 sc: &mut ScopeContext,
2100 mut vd: Validator,
2101 _tooltipped: Tooltipped,
2102) {
2103 vd.req_field("court_position");
2104 vd.req_field("recipient");
2105 vd.field_item_or_target("court_position", sc, Item::CourtPosition, Scopes::CourtPositionType);
2106 vd.field_target("recipient", sc, Scopes::Character);
2107}
2108
2109pub fn validate_add_takeover_phase_duration(
2110 _key: &Token,
2111 block: &Block,
2112 _data: &Everything,
2113 sc: &mut ScopeContext,
2114 mut vd: Validator,
2115 _tooltipped: Tooltipped,
2116) {
2117 vd.field_item("phase", Item::SituationPhase);
2118 validate_mandatory_duration(block, &mut vd, sc);
2119}
2120
2121pub fn validate_add_takeover_phase_points(
2122 _key: &Token,
2123 _block: &Block,
2124 _data: &Everything,
2125 sc: &mut ScopeContext,
2126 mut vd: Validator,
2127 _tooltipped: Tooltipped,
2128) {
2129 vd.field_item("phase", Item::SituationPhase);
2130 vd.field_script_value("points", sc);
2131}
2132
2133pub fn validate_change_phase(
2134 _key: &Token,
2135 bv: &BV,
2136 data: &Everything,
2137 _sc: &mut ScopeContext,
2138 _tooltipped: Tooltipped,
2139) {
2140 match bv {
2141 BV::Value(value) => {
2142 data.verify_exists(Item::SituationPhase, value);
2143 }
2144 BV::Block(block) => {
2145 let mut vd = Validator::new(block, data);
2146 vd.field_item("phase", Item::SituationPhase);
2147 }
2148 }
2149}
2150
2151pub fn validate_raze_county(
2152 _key: &Token,
2153 _block: &Block,
2154 _data: &Everything,
2155 _sc: &mut ScopeContext,
2156 mut vd: Validator,
2157 _tooltipped: Tooltipped,
2158) {
2159 vd.field_item("holding_type", Item::HoldingType);
2160 vd.field_bool("purge_secondary_holdings");
2161 vd.multi_field_item("excluded_holding_type", Item::HoldingType);
2162}
2163
2164pub fn validate_start_tributary(
2165 _key: &Token,
2166 _block: &Block,
2167 _data: &Everything,
2168 sc: &mut ScopeContext,
2169 mut vd: Validator,
2170 _tooltipped: Tooltipped,
2171) {
2172 vd.field_item("contract_group", Item::SubjectContractGroup);
2173 vd.field_target("suzerain", sc, Scopes::Character);
2174}
2175
2176pub fn validate_start_situation(
2177 _key: &Token,
2178 _block: &Block,
2179 _data: &Everything,
2180 sc: &mut ScopeContext,
2181 mut vd: Validator,
2182 _tooltipped: Tooltipped,
2183) {
2184 vd.req_field("type");
2185 vd.field_item("type", Item::Situation);
2186 vd.field_item("start_phase", Item::SituationPhase);
2187 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2188 sc.define_name_token(name.as_str(), Scopes::Situation, name, Temporary::No);
2189 }
2190 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
2191 sc.define_name_token(name.as_str(), Scopes::Situation, name, Temporary::Yes);
2192 }
2193 vd.multi_field_validated_block("sub_region", |block, data| {
2194 let mut vd = Validator::new(block, data);
2195 vd.req_field("key");
2196 vd.field_item("key", Item::SituationSubRegion);
2197 vd.field_item("start_phase", Item::SituationPhase);
2198 vd.field_list_items("geographical_regions", Item::Region);
2199 vd.field_validated("map_color", validate_possibly_named_color);
2200 });
2201}
2202
2203pub fn validate_situation_catalyst(
2204 key: &Token,
2205 bv: &BV,
2206 data: &Everything,
2207 sc: &mut ScopeContext,
2208 _tooltipped: Tooltipped,
2209) {
2210 match bv {
2211 BV::Value(value) => {
2212 data.verify_exists(Item::SituationCatalyst, value);
2213 }
2214 BV::Block(block) => {
2215 let mut vd = Validator::new(block, data);
2216 vd.req_field("catalyst");
2217 vd.field_item("catalyst", Item::SituationCatalyst);
2218 vd.field_target("character", sc, Scopes::Character);
2219 if key.is("trigger_situation_catalyst") {
2220 vd.field_target("county", sc, Scopes::Province);
2222 }
2223 }
2224 }
2225}
2226
2227pub fn validate_house_relation_level(
2228 _key: &Token,
2229 _block: &Block,
2230 _data: &Everything,
2231 sc: &mut ScopeContext,
2232 mut vd: Validator,
2233 _tooltipped: Tooltipped,
2234) {
2235 vd.field_script_value_no_breakdown("steps", sc);
2236 vd.field_validated_sc("description", sc, validate_desc);
2237 vd.field_bool("notification");
2238}
2239
2240pub fn validate_create_cadet_branch(
2241 _key: &Token,
2242 _block: &Block,
2243 _data: &Everything,
2244 sc: &mut ScopeContext,
2245 mut vd: Validator,
2246 _tooltipped: Tooltipped,
2247) {
2248 vd.field_item("prefix", Item::Localization);
2249 vd.field_validated_sc("name", sc, validate_desc);
2250 vd.field_item("coat_of_arms", Item::Coa);
2251 vd.field_bool("spread_to_descendants");
2252 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2253 sc.define_name_token(name.as_str(), Scopes::DynastyHouse, name, Temporary::No);
2254 }
2255}
2256
2257pub fn validate_create_dynasty(
2258 _key: &Token,
2259 _block: &Block,
2260 _data: &Everything,
2261 sc: &mut ScopeContext,
2262 mut vd: Validator,
2263 _tooltipped: Tooltipped,
2264) {
2265 vd.field_item("prefix", Item::Localization);
2266 vd.field_validated_sc("name", sc, validate_desc);
2267 vd.field_item("coat_of_arms", Item::Coa);
2268 vd.field_bool("spread_to_descendants");
2269 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2270 sc.define_name_token(name.as_str(), Scopes::Dynasty, name, Temporary::No);
2271 }
2272}
2273
2274pub fn validate_contribution(
2275 _key: &Token,
2276 _block: &Block,
2277 _data: &Everything,
2278 sc: &mut ScopeContext,
2279 mut vd: Validator,
2280 _tooltipped: Tooltipped,
2281) {
2282 vd.req_field("contribution");
2283 vd.field_target("contribution", sc, Scopes::ProjectContribution);
2284 vd.field_bool("cost");
2285 vd.field_bool("check_can_contribute");
2286}
2287
2288pub fn validate_situation_special_event(
2289 _key: &Token,
2290 _block: &Block,
2291 _data: &Everything,
2292 sc: &mut ScopeContext,
2293 mut vd: Validator,
2294 _tooltipped: Tooltipped,
2295) {
2296 vd.field_target("actor", sc, Scopes::Character);
2298 vd.field_item("phase", Item::SituationPhase);
2299 let mut loca_sc = sc.clone();
2300 vd.field_validated_block("variables", |block, data| {
2301 let mut vd = Validator::new(block, data);
2302 vd.unknown_value_fields(|key, value| {
2303 let scopes = validate_target_ok_this(value, data, sc, Scopes::all());
2304 loca_sc.define_name(key.as_str(), scopes, value);
2305 });
2306 });
2307 vd.field_localization("key", &mut loca_sc);
2308 loca_sc.destroy();
2309}
2310
2311pub fn validate_appointment_timeout(
2312 _key: &Token,
2313 block: &Block,
2314 _data: &Everything,
2315 sc: &mut ScopeContext,
2316 mut vd: Validator,
2317 _tooltipped: Tooltipped,
2318) {
2319 vd.field_validated_sc("desc", sc, validate_desc);
2320 validate_mandatory_duration(block, &mut vd, sc);
2321}
2322
2323pub fn validate_house_relation(
2324 _key: &Token,
2325 _block: &Block,
2326 _data: &Everything,
2327 sc: &mut ScopeContext,
2328 mut vd: Validator,
2329 _tooltipped: Tooltipped,
2330) {
2331 vd.req_field("target");
2332 vd.field_target("target", sc, Scopes::DynastyHouse);
2333 vd.field_item("type", Item::HouseRelationType);
2334 vd.field_item("level", Item::HouseRelationLevel);
2336 vd.field_validated_sc("description", sc, validate_desc);
2337 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2338 sc.define_name_token(name.as_str(), Scopes::HouseRelation, name, Temporary::No);
2339 }
2340}
2341
2342pub fn validate_set_activity_intent(
2343 _key: &Token,
2344 bv: &BV,
2345 data: &Everything,
2346 sc: &mut ScopeContext,
2347 _tooltipped: Tooltipped,
2348) {
2349 match bv {
2350 BV::Value(value) => {
2351 data.verify_exists(Item::ActivityIntent, value);
2352 }
2353 BV::Block(block) => {
2354 let mut vd = Validator::new(block, data);
2355 vd.req_field("intent");
2356 vd.field_item("intent", Item::ActivityIntent);
2357 vd.field_target("target", sc, Scopes::Character);
2358 }
2359 }
2360}
2361
2362pub fn validate_plan_great_project(
2363 _key: &Token,
2364 _block: &Block,
2365 _data: &Everything,
2366 sc: &mut ScopeContext,
2367 mut vd: Validator,
2368 _tooltipped: Tooltipped,
2369) {
2370 vd.req_field("great_project_type");
2371 vd.req_field("founder");
2372 vd.field_item("great_project_type", Item::GreatProjectType);
2373 vd.field_target("founder", sc, Scopes::Character);
2374 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2375 sc.define_name_token(name.as_str(), Scopes::GreatProject, name, Temporary::No);
2376 }
2377}
2378
2379pub fn validate_phase_duration(
2380 _key: &Token,
2381 block: &Block,
2382 _data: &Everything,
2383 sc: &mut ScopeContext,
2384 mut vd: Validator,
2385 _tooltipped: Tooltipped,
2386) {
2387 validate_mandatory_duration(block, &mut vd, sc);
2388}
2389
2390pub fn validate_send_interface(
2391 key: &Token,
2392 block: &Block,
2393 data: &Everything,
2394 sc: &mut ScopeContext,
2395 mut vd: Validator,
2396 _tooltipped: Tooltipped,
2397 special_tokens: &mut SpecialTokens,
2398) -> bool {
2399 vd.field_item("type", Item::Message);
2400 if let Some(token) = vd.field_value("goto") {
2401 let msg = "`goto` was removed from interface messages in 1.9";
2402 warn(ErrorKey::Removed).msg(msg).loc(token).push();
2403 }
2404 vd.field("title");
2407 vd.field("desc");
2408 vd.field("tooltip");
2409 vd.field("left_icon");
2410 vd.field("right_icon");
2411 vd.field("localization_values");
2412
2413 let random_is_problem = if let Some(message_type) = block.get_field_value("type") {
2414 data.item_has_property(Item::Message, message_type.as_str(), "displays_effect")
2415 } else {
2416 true
2417 };
2418 let mut st = SpecialTokens::empty();
2419 let caller = Lowercase::new(key.as_str());
2420 validate_effect_internal(
2421 &caller,
2422 ListType::None,
2423 block,
2424 data,
2425 sc,
2426 &mut vd,
2427 Tooltipped::Yes,
2428 &mut st,
2429 );
2430 for token in st.into_iter() {
2431 if token.is("random") || token.is("random_list") || token.is("duel") {
2432 if random_is_problem {
2433 warn(ErrorKey::Logic)
2434 .msg(format!("running `{token}` inside `{key}` does not work right"))
2435 .info("the message performs one roll and the execution performs another")
2436 .loc(token)
2437 .loc_msg(key, "executed inside this")
2438 .push();
2439 }
2440 } else {
2441 special_tokens.insert(token);
2442 }
2443 }
2444
2445 let mut loca_sc = sc.clone();
2448 vd.field_validated_block("localization_values", |block, data| {
2449 let mut vd = Validator::new(block, data);
2450 vd.unknown_value_fields(|key, value| {
2451 let scopes = validate_target_ok_this(value, data, sc, Scopes::all());
2452 loca_sc.define_name(key.as_str(), scopes, value);
2453 });
2454 });
2455 vd.field_validated_sc("title", &mut loca_sc, validate_desc);
2456 vd.field_validated_sc("desc", &mut loca_sc, validate_desc);
2457 vd.field_validated_sc("tooltip", &mut loca_sc, validate_desc);
2458 loca_sc.destroy();
2459 let icon_scopes = Scopes::Character
2460 | Scopes::LandedTitle
2461 | Scopes::Artifact
2462 | Scopes::Faith
2463 | Scopes::Dynasty
2464 | Scopes::DynastyHouse
2465 | Scopes::Confederation;
2466 if let Some(token) = vd.field_value("left_icon") {
2467 validate_target_ok_this(token, data, sc, icon_scopes);
2468 }
2469 if let Some(token) = vd.field_value("right_icon") {
2470 validate_target_ok_this(token, data, sc, icon_scopes);
2471 }
2472 false
2473}
2474
2475pub fn validate_impact_house_relation(
2476 _key: &Token,
2477 _block: &Block,
2478 _data: &Everything,
2479 sc: &mut ScopeContext,
2480 mut vd: Validator,
2481 _tooltipped: Tooltipped,
2482) {
2483 vd.req_field("target");
2484 vd.field_target("target", sc, Scopes::DynastyHouse);
2485 vd.field_item("type", Item::HouseRelationType);
2486 vd.field_script_value("steps", sc);
2487 vd.field_validated_sc("description", sc, validate_desc);
2488 vd.field_bool("notification");
2489 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
2490 sc.define_name_token(name.as_str(), Scopes::HouseRelation, name, Temporary::No);
2491 }
2492}
2493
2494pub fn validate_set_regnal_name(
2495 _key: &Token,
2496 bv: &BV,
2497 data: &Everything,
2498 sc: &mut ScopeContext,
2499 _tooltipped: Tooltipped,
2500) {
2501 match bv {
2502 BV::Value(value) => {
2503 data.validate_localization_sc(value.as_str(), sc);
2504 }
2505 BV::Block(block) => {
2506 let mut vd = Validator::new(block, data);
2507 vd.req_field("character");
2508 vd.field_target("character", sc, Scopes::Character);
2509 }
2510 }
2511}