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 if !data.item_exists(Item::Secret, token.as_str()) {
140 validate_target(token, data, sc, Scopes::Secret);
141 }
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.req_field("secondary");
523 vd.field_target("knight", sc, Scopes::Character);
524 vd.field_item("primary", Item::AccoladeType);
525 vd.field_item("secondary", Item::AccoladeType);
526 vd.field_item("name", Item::Localization);
527}
528
529pub fn validate_create_artifact(
530 key: &Token,
531 _block: &Block,
532 _data: &Everything,
533 sc: &mut ScopeContext,
534 mut vd: Validator,
535 _tooltipped: Tooltipped,
536) {
537 let caller = key.as_str().to_ascii_lowercase();
538 vd.field_validated_sc("name", sc, validate_desc);
539 vd.field_validated_sc("description", sc, validate_desc);
540 vd.field_item("rarity", Item::ArtifactRarity);
541 vd.field_item("type", Item::ArtifactType);
542 vd.multi_field_item("modifier", Item::Modifier);
543 vd.field_script_value("durability", sc);
544 vd.field_script_value("max_durability", sc);
545 vd.field_bool("decaying");
546 vd.multi_field_validated_block_sc("history", sc, validate_artifact_history);
547 vd.field_item("template", Item::ArtifactTemplate);
548 vd.field_item("visuals", Item::ArtifactVisual);
549 vd.field_bool("generate_history");
550 vd.field_script_value("quality", sc);
551 vd.field_script_value("wealth", sc);
552 vd.field_target("creator", sc, Scopes::Character);
553 vd.field_target(
554 "visuals_source",
555 sc,
556 Scopes::LandedTitle | Scopes::Dynasty | Scopes::DynastyHouse,
557 );
558
559 if caller == "create_artifact" {
560 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
561 sc.define_name_token(name.as_str(), Scopes::Artifact, name, Temporary::No);
562 }
563 vd.field_target("title_history", sc, Scopes::LandedTitle);
564 vd.field_date("title_history_date");
565 } else {
566 vd.ban_field("save_scope_as", || "`create_artifact`");
567 vd.ban_field("title_history", || "`create_artifact`");
568 vd.ban_field("title_history_date", || "`create_artifact`");
569 }
570}
571
572pub fn validate_create_character(
573 _key: &Token,
574 block: &Block,
575 data: &Everything,
576 sc: &mut ScopeContext,
577 mut vd: Validator,
578 _tooltipped: Tooltipped,
579) {
580 vd.replaced_field("save_event_target_as", "save_scope_as");
582 vd.replaced_field("save_temporary_event_target_as", "save_temporary_scope_as");
583 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
584 sc.define_name_token(name.as_str(), Scopes::Character, name, Temporary::No);
585 }
586 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
587 sc.define_name_token(name.as_str(), Scopes::Character, name, Temporary::Yes);
588 }
589
590 vd.field_validated_sc("name", sc, validate_desc);
591 vd.field_script_value("age", sc);
592 if let Some(token) = vd.field_value("gender") {
593 if !token.is("male") && !token.is("female") {
594 validate_target_ok_this(token, data, sc, Scopes::Character);
595 }
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 if !token.is("generate") && !token.is("inherit") && !token.is("none") {
627 validate_target(token, data, sc, Scopes::Dynasty);
628 }
629 }
630 vd.field_validated_value("ethnicity", |_, mut vd| {
631 vd.maybe_is("culture");
632 vd.maybe_is("mother");
633 vd.maybe_is("father");
634 vd.maybe_is("parents");
635 vd.item(Item::Ethnicity);
636 });
637 vd.field_block("ethnicities");
639 vd.field_script_value("diplomacy", sc);
640 vd.field_script_value("intrigue", sc);
641 vd.field_script_value("martial", sc);
642 vd.field_script_value("learning", sc);
643 vd.field_script_value("prowess", sc);
644 vd.field_script_value("stewardship", sc);
645 vd.field_validated_key_block("after_creation", |key, block, data| {
646 sc.open_scope(Scopes::Character, key.clone());
647 validate_effect(block, data, sc, Tooltipped::No); sc.close();
649 });
650}
651
652pub fn validate_create_character_memory(
653 key: &Token,
654 block: &Block,
655 _data: &Everything,
656 sc: &mut ScopeContext,
657 mut vd: Validator,
658 _tooltipped: Tooltipped,
659) {
660 vd.req_field("type");
661 vd.field_item("type", Item::MemoryType);
662 vd.field_validated_block("participants", |b, data| {
664 let mut vd = Validator::new(b, data);
665 let memtype = block.get_field_value("type");
666 vd.unknown_value_fields(|key, token| {
667 if let Some(memtype) = memtype {
668 if !data.item_has_property(Item::MemoryType, memtype.as_str(), key.as_str()) {
669 let msg =
670 format!("memory type `{memtype}` does not define participant `{key}`");
671 warn(ErrorKey::Validation).msg(msg).loc(key).push();
672 }
673 }
674 validate_target_ok_this(token, data, sc, Scopes::Character);
675 });
676 });
677 vd.field_validated_block_sc("duration", sc, validate_duration);
678 sc.define_name("new_memory", Scopes::CharacterMemory, key);
679}
680
681pub fn validate_create_confederation(
682 key: &Token,
683 _block: &Block,
684 _data: &Everything,
685 sc: &mut ScopeContext,
686 mut vd: Validator,
687 _tooltipped: Tooltipped,
688) {
689 vd.req_field("name");
690 vd.field_validated_sc("name", sc, validate_desc);
691 vd.field_target("type", sc, Scopes::ConfederationType);
692 vd.field_target("leader", sc, Scopes::DynastyHouse);
693 sc.define_name("new_confederation", Scopes::Confederation, key);
694}
695
696pub fn validate_create_dynamic_title(
697 key: &Token,
698 _block: &Block,
699 _data: &Everything,
700 sc: &mut ScopeContext,
701 mut vd: Validator,
702 _tooltipped: Tooltipped,
703) {
704 vd.req_field("tier");
705 vd.req_field("name");
706 vd.field_choice("tier", &["duchy", "kingdom", "empire", "hegemony"]);
707 vd.field_validated_sc("name", sc, validate_desc);
708 vd.advice_field("adjective", "changed to adj in 1.13");
709 vd.field_validated_sc("adj", sc, validate_desc);
710 vd.field_validated_sc("pre", sc, validate_desc);
711 vd.field_validated_sc("article", sc, validate_desc);
712 sc.define_name("new_title", Scopes::LandedTitle, key);
713}
714
715pub fn validate_create_nomad_title(
716 _key: &Token,
717 _block: &Block,
718 _data: &Everything,
719 sc: &mut ScopeContext,
720 mut vd: Validator,
721 _tooltipped: Tooltipped,
722) {
723 vd.field_validated_sc("name", sc, validate_desc);
724 vd.field_validated_sc("prefix", sc, validate_desc);
725 vd.field_validated_sc("adjective", sc, validate_desc);
726 vd.field_target("holder", sc, Scopes::Character);
727 vd.field_item("government", Item::GovernmentType);
728 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
729 sc.define_name_token(name.as_str(), Scopes::LandedTitle, name, Temporary::No);
730 }
731}
732
733pub fn validate_create_holy_order(
734 _key: &Token,
735 _block: &Block,
736 _data: &Everything,
737 sc: &mut ScopeContext,
738 mut vd: Validator,
739 _tooltipped: Tooltipped,
740) {
741 vd.req_field("leader");
742 vd.req_field("capital");
743 vd.field_target("leader", sc, Scopes::Character);
744 vd.field_target("capital", sc, Scopes::LandedTitle);
745 vd.field_item("name", Item::Localization);
746 vd.field_item("coat_of_arms", Item::Coa);
747 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
748 sc.define_name_token(name.as_str(), Scopes::HolyOrder, name, Temporary::No);
749 }
750 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
751 sc.define_name_token(name.as_str(), Scopes::HolyOrder, name, Temporary::Yes);
752 }
753}
754
755pub fn validate_create_title_and_vassal_change(
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("type");
764 vd.req_field("save_scope_as");
765 if let Some(history_type) = vd.field_value("type") {
766 let valid_types: Vec<_> = TITLE_HISTORY_TYPES
767 .iter()
768 .filter(|t| !BANNED_TITLE_HISTORY_TYPES.contains(t))
769 .copied()
770 .collect();
771 if BANNED_TITLE_HISTORY_TYPES.contains(&history_type.as_str()) {
772 let msg = format!(
773 "types {} cannot be used from script",
774 stringify_list(BANNED_TITLE_HISTORY_TYPES)
775 );
776 let info = format!("choose_from {}", stringify_choices(&valid_types));
777 err(ErrorKey::Choice).msg(msg).info(info).loc(history_type).push();
778 } else {
779 vd.field_choice("type", &valid_types);
780 }
781 }
782 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
783 sc.define_name_token(name.as_str(), Scopes::TitleAndVassalChange, name, Temporary::No);
784 }
785 vd.field_bool("add_claim_on_loss");
786}
787
788pub fn validate_delay_travel_plan(
789 _key: &Token,
790 _block: &Block,
791 _data: &Everything,
792 sc: &mut ScopeContext,
793 mut vd: Validator,
794 _tooltipped: Tooltipped,
795) {
796 vd.field_bool("add");
797 validate_optional_duration(&mut vd, sc);
798}
799
800pub fn validate_divide_war_chest(
801 _key: &Token,
802 _block: &Block,
803 _data: &Everything,
804 sc: &mut ScopeContext,
805 mut vd: Validator,
806 _tooltipped: Tooltipped,
807) {
808 vd.field_bool("defenders");
809 vd.field_script_value("fraction", sc);
810 vd.field_bool("gold");
811 vd.field_bool("piety");
812 vd.field_bool("prestige");
813}
814
815pub fn validate_duel(
816 key: &Token,
817 block: &Block,
818 data: &Everything,
819 sc: &mut ScopeContext,
820 mut vd: Validator,
821 tooltipped: Tooltipped,
822 special_tokens: &mut SpecialTokens,
823) -> bool {
824 vd.field_item("skill", Item::Skill);
825 vd.field_list_items("skills", Item::Skill);
826 vd.field_target("target", sc, Scopes::Character);
827 vd.field_script_value("value", sc);
828 if let Some(value) = vd.field_value("localization") {
829 validate_effect_localization(value, data, tooltipped);
830 }
831 vd.field_validated_value("challenge_variable", |_, mut vd| {
832 vd.identifier("variable name");
833 let loca = format!("{}_name", vd.value());
834 data.verify_exists_implied(Item::Localization, &loca, vd.value());
835 vd.accept();
836 });
837 vd.field_validated_list("challenge_variables", |token, data| {
838 validate_identifier(token, "variable name", Severity::Error);
839 let loca = format!("{token}_name");
840 data.verify_exists_implied(Item::Localization, &loca, token);
841 });
842 sc.define_name("duel_value", Scopes::Value, key);
843 let has_tooltip = validate_random_list(key, block, data, sc, vd, tooltipped, special_tokens);
844 if has_tooltip {
845 special_tokens.insert(key);
846 }
847 has_tooltip
848}
849
850pub fn validate_faction_start_war(
851 _key: &Token,
852 _block: &Block,
853 _data: &Everything,
854 sc: &mut ScopeContext,
855 mut vd: Validator,
856 _tooltipped: Tooltipped,
857) {
858 vd.field_target("title", sc, Scopes::LandedTitle);
859}
860
861pub fn validate_force_add_to_agent_slot(
862 _key: &Token,
863 _block: &Block,
864 _data: &Everything,
865 sc: &mut ScopeContext,
866 mut vd: Validator,
867 _tooltipped: Tooltipped,
868) {
869 vd.field_target("agent_slot", sc, Scopes::AgentSlot);
870 validate_optional_duration(&mut vd, sc);
871}
872
873pub fn validate_force_vote_as(
874 _key: &Token,
875 _block: &Block,
876 _data: &Everything,
877 sc: &mut ScopeContext,
878 mut vd: Validator,
879 _tooltipped: Tooltipped,
880) {
881 vd.field_target("target", sc, Scopes::Character);
882 validate_optional_duration(&mut vd, sc);
883}
884
885pub fn validate_imprison(
886 _key: &Token,
887 _block: &Block,
888 _data: &Everything,
889 sc: &mut ScopeContext,
890 mut vd: Validator,
891 _tooltipped: Tooltipped,
892) {
893 vd.field_target("target", sc, Scopes::Character);
894 vd.field_item("type", Item::PrisonType);
895 }
897
898pub fn validate_join_faction_forced(
899 _key: &Token,
900 _block: &Block,
901 _data: &Everything,
902 sc: &mut ScopeContext,
903 mut vd: Validator,
904 _tooltipped: Tooltipped,
905) {
906 vd.field_target("faction", sc, Scopes::Faction);
907 vd.field_target("forced_by", sc, Scopes::Character);
908 validate_optional_duration(&mut vd, sc);
909}
910
911pub fn validate_make_pregnant(
912 _key: &Token,
913 _block: &Block,
914 _data: &Everything,
915 sc: &mut ScopeContext,
916 mut vd: Validator,
917 _tooltipped: Tooltipped,
918) {
919 vd.field_target("father", sc, Scopes::Character);
920 vd.field_integer("number_of_children");
921 vd.field_bool("known_bastard");
922}
923
924pub fn validate_move_budget(
925 key: &Token,
926 _block: &Block,
927 _data: &Everything,
928 sc: &mut ScopeContext,
929 mut vd: Validator,
930 _tooltipped: Tooltipped,
931) {
932 if key.is("move_budget_gold") {
933 vd.field_script_value_no_breakdown("gold", sc);
934 } else if key.is("move_budget_treasury") {
935 vd.field_script_value_no_breakdown("treasury", sc);
936 }
937 let choices = &["budget_war_chest", "budget_reserved", "budget_short_term", "budget_long_term"];
938 vd.field_choice("from", choices);
939 vd.field_choice("to", choices);
940}
941
942pub fn validate_open_interaction_window(
943 key: &Token,
944 _block: &Block,
945 _data: &Everything,
946 sc: &mut ScopeContext,
947 mut vd: Validator,
948 _tooltipped: Tooltipped,
949) {
950 let caller = key.as_str().to_ascii_lowercase();
951 vd.req_field("interaction");
952 vd.req_field("actor");
953 vd.req_field("recipient");
954 vd.field_value("interaction"); vd.field_bool("redirect");
956 vd.field_target_ok_this("actor", sc, Scopes::Character);
957 vd.field_target_ok_this("recipient", sc, Scopes::Character);
958 vd.field_target_ok_this("secondary_actor", sc, Scopes::Character);
959 vd.field_target_ok_this("secondary_recipient", sc, Scopes::Character);
960 if caller == "open_interaction_window" {
961 vd.field_target("target_title", sc, Scopes::LandedTitle);
962 }
963 if caller == "run_interaction" {
964 vd.field_choice("execute_threshold", &["accept", "maybe", "decline"]);
965 vd.field_choice("send_threshold", &["accept", "maybe", "decline"]);
966 }
967}
968
969pub fn validate_pay_gold(
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("target");
978 vd.field_target("target", sc, Scopes::Character);
979 vd.field_script_value("gold", sc);
980 vd.field_bool("yearly_income");
982}
983
984pub fn validate_pay_income(
985 _key: &Token,
986 _block: &Block,
987 _data: &Everything,
988 sc: &mut ScopeContext,
989 mut vd: Validator,
990 _tooltipped: Tooltipped,
991) {
992 vd.req_field("target");
993 vd.field_target("target", sc, Scopes::Character);
994 validate_optional_duration(&mut vd, sc);
995}
996
997pub fn validate_current_phase_guest_subset(
998 _key: &Token,
999 _block: &Block,
1000 _data: &Everything,
1001 sc: &mut ScopeContext,
1002 mut vd: Validator,
1003 _tooltipped: Tooltipped,
1004) {
1005 vd.req_field("name");
1006 vd.req_field("target");
1007 vd.field_item("name", Item::GuestSubset);
1008 vd.field_target("target", sc, Scopes::Character);
1009}
1010
1011pub fn validate_remove_opinion(
1012 _key: &Token,
1013 _block: &Block,
1014 _data: &Everything,
1015 sc: &mut ScopeContext,
1016 mut vd: Validator,
1017 _tooltipped: Tooltipped,
1018) {
1019 vd.req_field("target");
1020 vd.req_field("modifier");
1021 vd.field_target("target", sc, Scopes::Character);
1022 vd.field_item("modifier", Item::OpinionModifier);
1023 vd.field_bool("single");
1024}
1025
1026pub fn validate_replace_court_position(
1027 _key: &Token,
1028 _block: &Block,
1029 _data: &Everything,
1030 sc: &mut ScopeContext,
1031 mut vd: Validator,
1032 _tooltipped: Tooltipped,
1033) {
1034 vd.req_field("recipient");
1035 vd.req_field("court_position");
1036 vd.field_target("recipient", sc, Scopes::Character);
1037 vd.field_target("holder", sc, Scopes::Character);
1038 vd.field_item("court_position", Item::CourtPosition);
1039}
1040
1041pub fn validate_revoke_court_position(
1042 _key: &Token,
1043 _block: &Block,
1044 _data: &Everything,
1045 sc: &mut ScopeContext,
1046 mut vd: Validator,
1047 _tooltipped: Tooltipped,
1048) {
1049 vd.req_field("court_position");
1050 vd.field_item("court_position", Item::CourtPosition);
1051 vd.field_target("recipient", sc, Scopes::Character);
1052 vd.field_target("holder", sc, Scopes::Character);
1053}
1054
1055pub fn validate_save_opinion_value(
1056 key: &Token,
1057 _block: &Block,
1058 _data: &Everything,
1059 sc: &mut ScopeContext,
1060 mut vd: Validator,
1061 _tooltipped: Tooltipped,
1062) {
1063 vd.req_field("name");
1064 vd.req_field("target");
1065 let temp =
1066 if key.is("save_temporary_opinion_value_as") { Temporary::Yes } else { Temporary::No };
1067 if let Some(name) = vd.field_value("name") {
1068 sc.define_name_token(name.as_str(), Scopes::Value, name, temp);
1069 }
1070 vd.field_target("target", sc, Scopes::Character);
1071}
1072
1073pub fn validate_scheme_freeze(
1074 _key: &Token,
1075 _block: &Block,
1076 _data: &Everything,
1077 sc: &mut ScopeContext,
1078 mut vd: Validator,
1079 _tooltipped: Tooltipped,
1080) {
1081 vd.field_item("reason", Item::Localization);
1082 validate_optional_duration(&mut vd, sc);
1083}
1084
1085pub fn validate_set_council_task(
1086 _key: &Token,
1087 _block: &Block,
1088 _data: &Everything,
1089 sc: &mut ScopeContext,
1090 mut vd: Validator,
1091 _tooltipped: Tooltipped,
1092) {
1093 vd.req_field("task_type");
1094 vd.field_item("task_type", Item::CouncilTask);
1096 vd.field_target("target", sc, Scopes::Character | Scopes::Province);
1098}
1099
1100pub fn validate_set_culture_name(
1101 _key: &Token,
1102 _block: &Block,
1103 _data: &Everything,
1104 sc: &mut ScopeContext,
1105 mut vd: Validator,
1106 _tooltipped: Tooltipped,
1107) {
1108 vd.req_field("noun");
1109 vd.field_validated_sc("noun", sc, validate_desc);
1110 vd.field_validated_sc("collective_noun", sc, validate_desc);
1111 vd.field_validated_sc("prefix", sc, validate_desc);
1112}
1113
1114pub fn validate_set_death_reason(
1115 _key: &Token,
1116 _block: &Block,
1117 _data: &Everything,
1118 sc: &mut ScopeContext,
1119 mut vd: Validator,
1120 _tooltipped: Tooltipped,
1121) {
1122 vd.req_field("death_reason");
1123 vd.field_item("death_reason", Item::DeathReason);
1124 vd.field_target("killer", sc, Scopes::Character);
1125 vd.field_target("artifact", sc, Scopes::Artifact);
1126}
1127
1128pub fn validate_set_ghw_target(
1129 key: &Token,
1130 _block: &Block,
1131 _data: &Everything,
1132 sc: &mut ScopeContext,
1133 mut vd: Validator,
1134 _tooltipped: Tooltipped,
1135) {
1136 let caller = key.as_str().to_ascii_lowercase();
1137 vd.req_field("target_character");
1138 vd.req_field("target_title");
1139 vd.field_target("target_character", sc, Scopes::Character);
1140 vd.field_target("target_title", sc, Scopes::LandedTitle);
1141 if caller == "start_great_holy_war" {
1142 vd.field_script_value("delay", sc);
1143 vd.field_target("war", sc, Scopes::War);
1144 }
1145}
1146
1147pub fn validate_set_legend_chapter(
1148 _key: &Token,
1149 _block: &Block,
1150 _data: &Everything,
1151 _sc: &mut ScopeContext,
1152 mut vd: Validator,
1153 _tooltipped: Tooltipped,
1154) {
1155 vd.field_item("name", Item::LegendChapter);
1156 vd.field_item("localization_key", Item::Localization);
1157}
1158
1159pub fn validate_set_legend_property(
1160 _key: &Token,
1161 _block: &Block,
1162 _data: &Everything,
1163 sc: &mut ScopeContext,
1164 mut vd: Validator,
1165 _tooltipped: Tooltipped,
1166) {
1167 vd.field_item("name", Item::LegendProperty);
1168 vd.field_target("target", sc, Scopes::all());
1170}
1171
1172pub fn validate_setup_cb(
1173 key: &Token,
1174 _block: &Block,
1175 _data: &Everything,
1176 sc: &mut ScopeContext,
1177 mut vd: Validator,
1178 _tooltipped: Tooltipped,
1179) {
1180 let caller = key.as_str().to_ascii_lowercase();
1181 vd.req_field("attacker");
1182 vd.req_field("defender");
1183 vd.field_target("attacker", sc, Scopes::Character);
1185 vd.field_target("defender", sc, Scopes::Character);
1186 vd.field_target("change", sc, Scopes::TitleAndVassalChange);
1187 vd.field_bool("victory");
1188 if caller == "setup_claim_cb" {
1189 vd.req_field("claimant");
1190 vd.field_target("claimant", sc, Scopes::Character);
1191 vd.field_bool("take_occupied");
1192 vd.field_bool("civil_war");
1193 vd.field_choice("titles", &["target_titles", "faction_titles"]);
1194 } else if caller == "setup_de_jure_cb" {
1195 vd.field_target("title", sc, Scopes::LandedTitle);
1196 } else if caller == "setup_invasion_cb" {
1197 vd.field_identifier("titles", "list name");
1198 vd.field_bool("take_occupied");
1199 vd.field_target("claimant", sc, Scopes::Character);
1200 }
1201 sc.define_name("cb_prestige_factor", Scopes::Value, key);
1202}
1203
1204pub fn validate_spawn_army(
1205 _key: &Token,
1206 _block: &Block,
1207 _data: &Everything,
1208 sc: &mut ScopeContext,
1209 mut vd: Validator,
1210 _tooltipped: Tooltipped,
1211) {
1212 vd.req_field("location");
1214 vd.field_script_value("levies", sc);
1215 vd.multi_field_validated_block("men_at_arms", |b, data| {
1216 let mut vd = Validator::new(b, data);
1217 vd.req_field("type");
1218 vd.field_item("type", Item::MenAtArms);
1219 vd.field_script_value("men", sc);
1220 vd.field_script_value("stacks", sc);
1221 vd.field_bool("inheritable"); });
1223 vd.field_target("location", sc, Scopes::Province);
1224 vd.field_target("origin", sc, Scopes::Province);
1225 vd.field_target("war", sc, Scopes::War);
1226 vd.field_bool("war_keep_on_attacker_victory");
1227 vd.field_bool("inheritable");
1228 vd.field_bool("uses_supply");
1229 vd.field_target("army", sc, Scopes::Army);
1230 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1231 sc.define_name_token(name.as_str(), Scopes::Army, name, Temporary::No);
1232 }
1233 if let Some(name) = vd.field_identifier("save_temporary_scope_as", "scope name") {
1234 sc.define_name_token(name.as_str(), Scopes::Army, name, Temporary::Yes);
1235 }
1236 vd.field_validated_sc("name", sc, validate_desc);
1237}
1238
1239pub fn validate_start_scheme(
1240 _key: &Token,
1241 _block: &Block,
1242 _data: &Everything,
1243 sc: &mut ScopeContext,
1244 mut vd: Validator,
1245 _tooltipped: Tooltipped,
1246) {
1247 vd.req_field("type");
1248 vd.req_field_one_of(&[
1249 "target_character",
1250 "target_title",
1251 "target_culture",
1252 "target_faith",
1253 "targets_nothing",
1254 ]);
1255 vd.field_item("type", Item::Scheme);
1256 vd.field_target("contract", sc, Scopes::TaskContract);
1257 vd.advice_field("target", "replaced with target_character in 1.13");
1258 vd.field_target("target_character", sc, Scopes::Character);
1259 vd.field_target("target_title", sc, Scopes::LandedTitle);
1260 vd.field_target("target_culture", sc, Scopes::Culture);
1261 vd.field_target("target_faith", sc, Scopes::Faith);
1262 vd.field_bool("targets_nothing");
1263 if let Some(name) = vd.field_identifier("save_scope_as", "scope name") {
1264 sc.define_name_token(name.as_str(), Scopes::Scheme, name, Temporary::No);
1265 }
1266
1267 vd.field_target("artifact", sc, Scopes::Artifact);
1271}
1272
1273pub fn validate_start_struggle(
1274 _key: &Token,
1275 _block: &Block,
1276 _data: &Everything,
1277 _sc: &mut ScopeContext,
1278 mut vd: Validator,
1279 _tooltipped: Tooltipped,
1280) {
1281 vd.req_field("struggle_type");
1282 vd.req_field("start_phase");
1283 vd.field_item("struggle_type", Item::Struggle);
1284 vd.field_item("start_phase", Item::StrugglePhase);
1285}
1286
1287pub fn validate_start_travel_plan(
1288 _key: &Token,
1289 _block: &Block,
1290 data: &Everything,
1291 sc: &mut ScopeContext,
1292 mut vd: Validator,
1293 _tooltipped: Tooltipped,
1294) {
1295 vd.req_field("destination");
1296 for token in vd.multi_field_value("destination") {
1297 validate_target(token, data, sc, Scopes::Province);
1298 }
1299 vd.field_target("travel_leader", sc, Scopes::Character);
1300 for token in vd.multi_field_value("companion") {
1301 validate_target(token, data, sc, Scopes::Character);
1302 }
1303 vd.field_bool("travel_with_domicile");
1304 vd.field_bool("can_cancel_planning");
1305 vd.field_bool("players_use_planner");
1306 vd.field_bool("return_trip");
1307 vd.field_event("on_arrival_event", sc);
1308 vd.field_action("on_arrival_on_action", sc);
1309 vd.field_event("on_start_event", sc);
1310 vd.field_action("on_start_on_action", sc);
1311 vd.field_event("on_travel_planner_cancel_event", sc);
1312 vd.field_action("on_travel_planner_cancel_on_action", sc);
1313 vd.field_choice("on_arrival_destinations", &["all", "first", "last", "all_but_last"]);
1314}
1315
1316pub fn validate_start_war(
1317 _key: &Token,
1318 _block: &Block,
1319 data: &Everything,
1320 sc: &mut ScopeContext,
1321 mut vd: Validator,
1322 _tooltipped: Tooltipped,
1323) {
1324 vd.field_item("casus_belli", Item::CasusBelli);
1325 vd.field_item("cb", Item::CasusBelli);
1326 vd.field_target("target", sc, Scopes::Character);
1327 vd.field_target_ok_this("claimant", sc, Scopes::Character);
1328 for token in vd.multi_field_value("target_title") {
1329 validate_target(token, data, sc, Scopes::LandedTitle);
1330 }
1331}
1332
1333pub fn validate_stress_impact(
1334 _key: &Token,
1335 _block: &Block,
1336 data: &Everything,
1337 sc: &mut ScopeContext,
1338 mut vd: Validator,
1339 _tooltipped: Tooltipped,
1340) {
1341 vd.field_script_value("base", sc);
1342 vd.unknown_fields(|token, bv| {
1343 data.verify_exists(Item::Trait, token);
1344 validate_non_dynamic_script_value(bv, data);
1345 });
1346}
1347
1348pub fn validate_try_create_important_action(
1349 _key: &Token,
1350 _block: &Block,
1351 data: &Everything,
1352 sc: &mut ScopeContext,
1353 mut vd: Validator,
1354 _tooltipped: Tooltipped,
1355) {
1356 vd.req_field("important_action_type");
1357 vd.field_item("important_action_type", Item::ImportantAction);
1358 vd.unknown_value_fields(|_, value| {
1359 validate_target_ok_this(value, data, sc, Scopes::all_but_none());
1360 });
1361}
1362
1363pub fn validate_try_create_suggestion(
1364 _key: &Token,
1365 _block: &Block,
1366 _data: &Everything,
1367 sc: &mut ScopeContext,
1368 mut vd: Validator,
1369 _tooltipped: Tooltipped,
1370) {
1371 vd.req_field("suggestion_type");
1372 vd.field_item("suggestion_type", Item::Suggestion);
1373 vd.field_target_ok_this("actor", sc, Scopes::Character);
1374 vd.field_target_ok_this("recipient", sc, Scopes::Character);
1375 vd.field_target_ok_this("secondary_actor", sc, Scopes::Character);
1376 vd.field_target_ok_this("secondary_recipient", sc, Scopes::Character);
1377 vd.field_target_ok_this("landed_title", sc, Scopes::LandedTitle);
1378}
1379
1380pub fn validate_contract_set_obligation_level(
1381 _key: &Token,
1382 _block: &Block,
1383 data: &Everything,
1384 sc: &mut ScopeContext,
1385 mut vd: Validator,
1386 _tooltipped: Tooltipped,
1387) {
1388 vd.req_field("type");
1389 vd.req_field("level");
1390 if let Some(token) = vd.field_value("type") {
1391 if !data.item_exists(Item::SubjectContract, token.as_str()) {
1392 validate_target(token, data, sc, Scopes::VassalContract);
1393 }
1394 }
1395 if let Some(token) = vd.field_value("level") {
1396 if !token.is_integer()
1397 && !data.item_exists(Item::SubjectContractObligationLevel, token.as_str())
1398 {
1399 validate_target(token, data, sc, Scopes::VassalObligationLevel);
1400 }
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}