1use crate::block::{BV, Block};
4use crate::ck3::tables::misc::AI_TARGETS;
5use crate::context::ScopeContext;
6use crate::desc::validate_desc;
7use crate::everything::Everything;
8use crate::item::Item;
9use crate::report::{ErrorKey, err, fatal};
10use crate::scopes::Scopes;
11use crate::tooltipped::Tooltipped;
12use crate::trigger::{validate_target, validate_target_ok_this};
13use crate::validate::{validate_modifiers_with_base, validate_scope_chain};
14use crate::validator::Validator;
15
16pub fn validate_theme_background(bv: &BV, data: &Everything, sc: &mut ScopeContext) {
17 match bv {
18 BV::Value(token) => {
19 data.verify_exists(Item::EventBackground, token);
20 let block = Block::new(token.loc);
21 data.validate_call(Item::EventBackground, token, &block, sc);
22 }
23 BV::Block(block) => {
24 let mut vd = Validator::new(block, data);
25
26 vd.field_trigger("trigger", Tooltipped::No, sc);
27 if vd.field_value("event_background").is_some() {
28 let msg = "`event_background` now causes a crash. It has been replaced by `reference` since 1.9";
29 fatal(ErrorKey::Crash)
30 .msg(msg)
31 .loc(block.get_key("event_background").unwrap())
32 .push();
33 }
34 vd.req_field("reference");
35 if let Some(token) = vd.field_value("reference") {
36 data.verify_exists(Item::EventBackground, token);
37 data.validate_call(Item::EventBackground, token, block, sc);
38 }
39 }
40 }
41}
42
43pub fn validate_theme_header_background(bv: &BV, data: &Everything, sc: &mut ScopeContext) {
44 match bv {
45 BV::Value(token) => data.verify_exists(Item::File, token),
46 BV::Block(block) => {
47 let mut vd = Validator::new(block, data);
48
49 vd.field_trigger("trigger", Tooltipped::No, sc);
50 vd.req_field("reference");
51 if let Some(token) = vd.field_value("reference") {
52 data.verify_exists(Item::File, token);
53 }
54 }
55 }
56}
57
58pub fn validate_theme_icon(bv: &BV, data: &Everything, sc: &mut ScopeContext) {
59 validate_theme_header_background(bv, data, sc);
60}
61
62pub fn validate_theme_sound(block: &Block, data: &Everything, sc: &mut ScopeContext) {
63 let mut vd = Validator::new(block, data);
64
65 vd.field_trigger("trigger", Tooltipped::No, sc);
66 vd.field_item("reference", Item::Sound);
67}
68
69pub fn validate_theme_transition(block: &Block, data: &Everything, sc: &mut ScopeContext) {
70 let mut vd = Validator::new(block, data);
71
72 vd.field_trigger("trigger", Tooltipped::No, sc);
73 if let Some(token) = vd.field_value("reference") {
74 data.verify_exists(Item::EventTransition, token);
75 data.validate_call(Item::EventTransition, token, block, sc);
76 }
77}
78
79pub fn validate_theme_effect_2d(bv: &BV, data: &Everything, sc: &mut ScopeContext) {
80 match bv {
81 BV::Value(token) => data.verify_exists(Item::EventEffect2d, token),
82 BV::Block(block) => {
83 let mut vd = Validator::new(block, data);
84
85 vd.field_trigger("trigger", Tooltipped::No, sc);
86 if let Some(token) = vd.field_value("reference") {
87 data.verify_exists(Item::EventEffect2d, token);
88 }
89 }
90 }
91}
92
93pub fn validate_cost(block: &Block, data: &Everything, sc: &mut ScopeContext) {
94 let mut vd = Validator::new(block, data);
95 vd.field_script_value("barter_goods", sc);
96 vd.field_script_value("gold", sc);
97 vd.field_script_value("herd", sc);
98 vd.field_script_value("influence", sc);
99 vd.field_script_value("prestige", sc);
100 vd.field_script_value("piety", sc);
101 vd.field_script_value("renown", sc);
102 vd.field_script_value("treasury", sc);
103 vd.field_script_value("treasury_or_gold", sc);
104 vd.field_bool("round");
105}
106
107pub fn validate_traits(block: &Block, data: &Everything) {
108 let mut vd = Validator::new(block, data);
109 vd.field_validated_block("virtues", validate_virtues_sins);
110 vd.field_validated_block("sins", validate_virtues_sins);
111}
112
113pub fn validate_virtues_sins(block: &Block, data: &Everything) {
114 let mut vd = Validator::new(block, data);
117 for token in vd.values() {
118 data.verify_exists(Item::Trait, token);
119 }
120 vd.unknown_value_fields(|key, value| {
121 data.verify_exists(Item::Trait, key);
122 value.expect_number();
123 });
124 vd.unknown_block_fields(|key, block| {
125 data.verify_exists(Item::Trait, key);
126 let mut vd = Validator::new(block, data);
127 vd.field_numeric("scale");
128 vd.field_numeric("weight");
129 });
130}
131
132pub fn validate_compare_modifier(block: &Block, data: &Everything, sc: &mut ScopeContext) {
133 let mut vd = Validator::new(block, data);
134
135 sc.open_builder();
137 let mut valid_target = false;
138 vd.field_validated_value("target", |_, mut vd| {
139 valid_target = validate_scope_chain(vd.value(), data, sc, false);
140 vd.accept();
141 });
142 sc.finalize_builder();
143 if valid_target {
144 vd.field_script_value("value", sc);
145 vd.field_script_value("factor", sc);
146 } else {
147 vd.field("value");
148 vd.field("factor");
149 }
150 sc.close();
151
152 vd.multi_field_script_value("multiplier", sc);
153 vd.field_script_value("min", sc);
154 vd.field_script_value("max", sc);
155 vd.field_script_value("step", sc); vd.field_script_value("offset", sc); vd.field_validated_sc("desc", sc, validate_desc);
158 vd.field_trigger("trigger", Tooltipped::No, sc);
159}
160
161pub fn validate_opinion_modifier(block: &Block, data: &Everything, sc: &mut ScopeContext) {
162 let mut vd = Validator::new(block, data);
163 if let Some(target) = vd.field_value("who") {
164 validate_target_ok_this(target, data, sc, Scopes::Character);
165 }
166 vd.req_field("opinion_target");
167 if let Some(target) = vd.field_value("opinion_target") {
168 validate_target_ok_this(target, data, sc, Scopes::Character);
169 }
170 vd.field_script_value("multiplier", sc);
171 vd.field_validated_sc("desc", sc, validate_desc);
172 vd.field_script_value("min", sc);
173 vd.field_script_value("max", sc);
174 vd.field_script_value("step", sc); vd.field_trigger("trigger", Tooltipped::No, sc);
176}
177
178pub fn validate_ai_value_modifier(block: &Block, data: &Everything, sc: &mut ScopeContext) {
179 let mut vd = Validator::new(block, data);
180 if let Some(target) = vd.field_value("who") {
181 validate_target_ok_this(target, data, sc, Scopes::Character);
182 }
183 vd.field_validated_block("dread_modified_ai_boldness", |block, data| {
185 let mut vd = Validator::new(block, data);
186 vd.req_field("dreaded_character");
187 vd.req_field("value");
188 vd.field_target_ok_this("dreaded_character", sc, Scopes::Character);
189 vd.field_script_value("value", sc);
190 });
191 vd.field_script_value("ai_boldness", sc);
192 vd.field_script_value("ai_compassion", sc);
193 vd.field_script_value("ai_energy", sc);
194 vd.field_script_value("ai_greed", sc);
195 vd.field_script_value("ai_honor", sc);
196 vd.field_script_value("ai_rationality", sc);
197 vd.field_script_value("ai_sociability", sc);
198 vd.field_script_value("ai_vengefulness", sc);
199 vd.field_script_value("ai_zeal", sc);
200 vd.field_script_value("min", sc);
201 vd.field_script_value("max", sc);
202 vd.field_trigger("trigger", Tooltipped::No, sc);
203}
204
205pub fn validate_compatibility_modifier(block: &Block, data: &Everything, sc: &mut ScopeContext) {
206 let mut vd = Validator::new(block, data);
207 if let Some(target) = vd.field_value("who") {
208 validate_target_ok_this(target, data, sc, Scopes::Character);
209 }
210 if let Some(target) = vd.field_value("compatibility_target") {
211 validate_target_ok_this(target, data, sc, Scopes::Character);
212 }
213 vd.field_script_value("multiplier", sc);
214 vd.field_script_value("min", sc);
216 vd.field_script_value("max", sc);
217 vd.field_trigger("trigger", Tooltipped::No, sc);
218}
219
220pub fn validate_activity_modifier(block: &Block, data: &Everything, sc: &mut ScopeContext) {
221 let mut vd = Validator::new(block, data);
222 vd.field_target("object", sc, Scopes::Activity);
223 vd.field_target("target", sc, Scopes::Character);
224}
225
226pub fn validate_scheme_modifier(block: &Block, data: &Everything, sc: &mut ScopeContext) {
227 let mut vd = Validator::new(block, data);
228 vd.field_target("object", sc, Scopes::Scheme);
229 vd.field_target("target", sc, Scopes::Character);
230}
231
232pub fn validate_random_traits_list(block: &Block, data: &Everything, sc: &mut ScopeContext) {
233 let mut vd = Validator::new(block, data);
234 vd.field_script_value("count", sc);
235 vd.unknown_block_fields(|key, block| {
236 data.verify_exists(Item::Trait, key);
237 let mut vd = Validator::new(block, data);
238 vd.field_validated_block_sc("weight", sc, validate_modifiers_with_base);
239 vd.field_trigger("trigger", Tooltipped::No, sc);
240 });
241}
242
243pub fn validate_random_culture(block: &Block, data: &Everything, sc: &mut ScopeContext) {
244 let mut vd = Validator::new(block, data);
245 vd.unknown_block_fields(|key, block| {
246 validate_target(key, data, sc, Scopes::Culture);
247 let mut vd = Validator::new(block, data);
248 vd.field_validated_block_sc("weight", sc, validate_modifiers_with_base);
249 vd.field_trigger("trigger", Tooltipped::No, sc);
250 });
251}
252
253pub fn validate_random_faith(block: &Block, data: &Everything, sc: &mut ScopeContext) {
254 let mut vd = Validator::new(block, data);
255 vd.unknown_block_fields(|key, block| {
256 validate_target(key, data, sc, Scopes::Faith);
257 let mut vd = Validator::new(block, data);
258 vd.field_validated_block_sc("weight", sc, validate_modifiers_with_base);
259 vd.field_trigger("trigger", Tooltipped::No, sc);
260 });
261}
262
263pub fn validate_maa_stats(vd: &mut Validator) {
264 vd.field_numeric("pursuit");
265 vd.field_numeric("screen");
266 vd.field_numeric("damage");
267 vd.field_numeric("toughness");
268 vd.field_numeric("siege_value");
269}
270
271pub fn validate_portrait_modifier_overrides(block: &Block, data: &Everything) {
272 let mut vd = Validator::new(block, data);
273 vd.unknown_value_fields(|key, value| {
274 data.verify_exists(Item::PortraitModifierGroup, key);
275 if !data.item_has_property(Item::PortraitModifierGroup, key.as_str(), value.as_str()) {
276 let msg = format!("portrait modifier group {key} does not have the modifier {value}");
277 err(ErrorKey::MissingItem).msg(msg).loc(value).push();
278 }
279 });
280}
281
282pub fn validate_quick_trigger(block: &Block, data: &Everything) {
283 let mut vd = Validator::new(block, data);
284
285 vd.field_bool("adult");
286 vd.field_bool("attracted_to_owner");
287 vd.field_bool("owner_attracted");
288 vd.field_bool("prison");
289}
290
291pub fn validate_ai_targets(block: &Block, data: &Everything) {
292 let mut vd = Validator::new(block, data);
293
294 vd.req_field("ai_recipients");
295 vd.multi_field_choice("ai_recipients", AI_TARGETS);
296 vd.field_integer_range("max", 0..);
297 vd.field_numeric_range("chance", 0.0..=1.0);
298
299 let mut found = false;
300 for value in block.get_field_values("ai_recipients") {
301 if value.is("situation_participant_group") {
302 vd.field_item("parameter", Item::Situation);
303 found = true;
304 }
305 }
306 if !found {
307 vd.ban_field("parameter", || "`ai_recipients = situation_participant_group`");
308 }
309}