1use std::fmt::Display;
6
7use bitflags::Flags;
8
9use crate::block::Block;
10use crate::everything::Everything;
11use crate::game::Game;
12#[cfg(feature = "hoi4")]
13use crate::hoi4::tables::modifs::modif_loc_hoi4;
14#[cfg(any(feature = "ck3", feature = "vic3", feature = "hoi4"))]
15use crate::item::Item;
16use crate::report::{ErrorKey, Severity, err};
17#[cfg(feature = "jomini")]
18use crate::script_value::validate_non_dynamic_script_value;
19use crate::token::Token;
20use crate::validator::Validator;
21#[cfg(feature = "vic3")]
22use crate::vic3::tables::modifs::modif_loc_vic3;
23
24pub trait ModifKinds: Display + Flags + Copy {
30 fn require(self, other: Self, token: &Token) {
31 if !self.intersects(other) {
32 let msg = format!("`{token}` is a modifier for {other} but expected {self}");
33 err(ErrorKey::Modifiers).msg(msg).loc(token).push();
34 }
35 }
36
37 fn lookup_modif(name: &Token, data: &Everything, warn: Option<Severity>) -> Option<Self>;
40}
41
42pub fn validate_modifs<'a, MK: ModifKinds>(
43 _block: &Block,
44 data: &'a Everything,
45 kinds: MK,
46 mut vd: Validator<'a>,
47) {
48 #[cfg(feature = "hoi4")]
49 vd.field_validated_block("hidden_modifier", |block, data| {
50 let mut vd = Validator::new(block, data);
51 vd.unknown_fields(|key, bv| {
53 if let Some(mk) = MK::lookup_modif(key, data, Some(Severity::Error)) {
54 kinds.require(mk, key);
55
56 let _ = &bv;
58 } else {
59 let msg = format!("unknown modifier `{key}`");
60 err(ErrorKey::UnknownField).msg(msg).loc(key).push();
61 }
62 });
63 });
64
65 #[cfg(feature = "hoi4")]
66 vd.field_item("custom_modifier_tooltip", Item::Localization);
67
68 vd.unknown_fields(|key, bv| {
69 #[cfg(feature = "hoi4")]
70 if Game::is_hoi4()
71 && (key.is("fort") || key.is("river") || data.item_exists(Item::Terrain, key.as_str()))
72 {
73 if let Some(block) = bv.expect_block() {
74 let mut vd = Validator::new(block, data);
75 vd.field_numeric("attack");
76 vd.field_numeric("movement");
77 vd.field_numeric("defence");
78 }
79 return;
80 }
81
82 if let Some(mk) = MK::lookup_modif(key, data, Some(Severity::Error)) {
83 kinds.require(mk, key);
84 if Game::is_jomini() {
85 #[cfg(feature = "jomini")]
86 validate_non_dynamic_script_value(bv, data);
87 } else {
88 let _ = &bv;
90 }
91 #[cfg(feature = "ck3")]
92 if Game::is_ck3()
93 && !key.is("health")
94 && !key.is("elderly_health")
95 && !key.is("child_health")
96 && !key.is("negate_health_penalty_add")
97 {
98 data.verify_exists(Item::ModifierFormat, key);
99 }
100 #[cfg(feature = "vic3")]
101 if Game::is_vic3() {
102 let (loca_key, loca_desc_key) = modif_loc_vic3(key, data);
104 data.verify_exists_implied(Item::Localization, &loca_key, key);
105 data.verify_exists_implied(Item::Localization, &loca_desc_key, key);
106 }
107 #[cfg(feature = "hoi4")]
108 if Game::is_hoi4() {
109 let loca_key = modif_loc_hoi4(key, data);
110 data.verify_exists_implied(Item::Localization, &loca_key, key);
111 }
112 }
113 else if !Game::is_vic3() {
115 let msg = format!("unknown modifier `{key}`");
116 err(ErrorKey::UnknownField).msg(msg).loc(key).push();
117 }
118 });
119}
120
121pub fn verify_modif_exists<MK: ModifKinds>(
122 key: &Token,
123 data: &Everything,
124 kinds: MK,
125 sev: Severity,
126) {
127 if let Some(mk) = MK::lookup_modif(key, data, Some(sev)) {
128 kinds.require(mk, key);
129 }
130 else if !Game::is_vic3() {
132 let msg = format!("unknown modifier `{key}`");
133 err(ErrorKey::UnknownField).msg(msg).loc(key).push();
134 }
135}