1use crate::block::Block;
2use crate::ck3::validate::validate_cost;
3use crate::context::ScopeContext;
4use crate::db::{Db, DbKind};
5use crate::desc::validate_desc;
6use crate::everything::Everything;
7use crate::game::GameFlags;
8use crate::item::{Item, ItemLoader};
9use crate::scopes::Scopes;
10use crate::token::Token;
11use crate::tooltipped::Tooltipped;
12use crate::validate::validate_duration;
13use crate::validator::Validator;
14
15#[derive(Clone, Debug)]
16pub struct CasusBelli {}
17
18inventory::submit! {
19 ItemLoader::Normal(GameFlags::Ck3, Item::CasusBelli, CasusBelli::add)
20}
21
22impl CasusBelli {
23 pub fn add(db: &mut Db, key: Token, block: Block) {
24 db.add(Item::CasusBelli, key, block, Box::new(Self {}));
25 }
26}
27
28impl DbKind for CasusBelli {
29 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
30 data.verify_exists(Item::Localization, key);
31
32 let mut vd = Validator::new(block, data);
33 let has_claimant = block.has_key("is_allowed_claim_title");
34
35 let sc_builder = |key: &Token| {
36 let mut sc = ScopeContext::new(Scopes::CasusBelli, key);
37 sc.define_name("attacker", Scopes::Character, key);
38 sc.define_name("defender", Scopes::Character, key);
39 sc.define_name("claimant", Scopes::Character, key);
41 sc.define_list("target_titles", Scopes::LandedTitle, key);
43 if has_claimant {
44 sc.define_name("claimant", Scopes::Character, key);
45 }
46 sc
47 };
48
49 let mut sc = sc_builder(key);
50
51 vd.field_item("group", Item::CasusBelliGroup);
52 let icon = vd.field_value("icon").unwrap_or(key);
53 data.verify_icon("NGameIcons|CASUS_BELLI_TYPE_ICON_PATH", icon, ".dds");
54
55 vd.field_validated_block_rooted(
56 "attacker_ticking_warscore_delay",
57 Scopes::Character,
58 validate_duration,
59 );
60 vd.field_validated_block_rooted(
61 "defender_ticking_warscore_delay",
62 Scopes::Character,
63 validate_duration,
64 );
65 vd.field_numeric("attacker_ticking_warscore");
66 vd.field_numeric("defender_ticking_warscore");
67 vd.field_numeric_range("attacker_wargoal_percentage", 0.0..=1.0);
68 vd.field_numeric_range("defender_wargoal_percentage", 0.0..=1.0);
69 vd.field_numeric("attacker_score_from_occupation_scale");
70 vd.field_numeric("defender_score_from_occupation_scale");
71 vd.field_numeric("attacker_score_from_battles_scale");
72 vd.field_numeric("defender_score_from_battles_scale");
73 vd.field_numeric("max_attacker_score_from_battles");
74 vd.field_numeric("max_defender_score_from_battles");
75 vd.field_numeric("max_attacker_score_from_occupation");
76 vd.field_numeric("max_defender_score_from_occupation");
77 vd.field_bool("full_occupation_by_defender_gives_victory");
78 vd.field_bool("full_occupation_by_attacker_gives_victory");
79 vd.field_bool("landless_attacker_needs_armies");
80 vd.field_bool("allow_hostages");
81
82 vd.field_numeric("occupation_participation_mult");
83 vd.field_numeric("siege_participation_mult");
84 vd.field_numeric("battle_participation_mult");
85
86 vd.field_validated_block_sc("cost", &mut sc, validate_cost);
87
88 vd.field_bool("attacker_capital_gives_war_score");
89 vd.field_bool("defender_capital_gives_war_score");
90 vd.field_bool("imprisonment_by_attacker_give_war_score");
91 vd.field_bool("imprisonment_by_defender_give_war_score");
92
93 let sc_effect_builder = |key: &Token| {
94 let mut sc = sc_builder(key);
95 sc.define_name("war", Scopes::War, key);
96 sc.define_list("attackers", Scopes::LandedTitle, key);
97 sc.define_list("defenders", Scopes::LandedTitle, key);
98 sc
99 };
100
101 for field in
103 &["on_declaration", "on_victory", "on_white_peace", "on_defeat", "on_invalidated"]
104 {
105 vd.field_effect_builder(field, Tooltipped::No, sc_effect_builder);
106 }
107
108 for field in
109 &["on_victory_desc", "on_defeat_desc", "on_white_peace_desc", "on_invalidated_desc"]
110 {
111 vd.field_validated_sc(field, &mut sc, validate_desc);
112 }
113
114 vd.field_trigger("should_invalidate", Tooltipped::No, &mut sc);
115 vd.field_trigger("mutually_exclusive_titles", Tooltipped::No, &mut sc);
116 vd.field_bool("combine_into_one");
117
118 for (tooltipped, field) in &[
119 (Tooltipped::No, "allowed_for_character"),
120 (Tooltipped::Yes, "allowed_for_character_display_regardless"),
121 (Tooltipped::No, "allowed_against_character"),
122 (Tooltipped::Yes, "allowed_against_character_display_regardless"),
123 ] {
124 vd.field_trigger_builder(field, *tooltipped, |key| {
125 let mut sc = ScopeContext::new(Scopes::Character, key);
126 sc.define_name("attacker", Scopes::Character, key);
127 sc.define_name("defender", Scopes::Character, key);
128 sc.define_list("target_titles", Scopes::LandedTitle, key);
129 sc
130 });
131 }
132
133 for (tooltipped, field) in &[
134 (Tooltipped::No, "valid_to_start"),
135 (Tooltipped::Yes, "valid_to_start_display_regardless"),
136 ] {
137 vd.field_trigger_builder(field, *tooltipped, |key| {
138 let mut sc = ScopeContext::new(Scopes::Character, key);
139 sc.define_name("attacker", Scopes::Character, key);
140 sc.define_name("defender", Scopes::Character, key);
141 sc.define_name("target", Scopes::LandedTitle, key);
142 sc.define_list("target_titles", Scopes::LandedTitle, key);
143 sc
144 });
145 }
146
147 vd.field_trigger_builder("is_allowed_claim_title", Tooltipped::Yes, |key| {
148 let mut sc = ScopeContext::new(Scopes::LandedTitle, key);
149 sc.define_name("attacker", Scopes::Character, key);
150 sc.define_name("defender", Scopes::Character, key);
151 sc.define_name("claimant", Scopes::Character, key);
152 sc.define_list("target_titles", Scopes::LandedTitle, key);
153 sc
154 });
155
156 let choices = &[
157 "none",
158 "neighbor_land",
159 "neighbor_land_or_water",
160 "neighbor_land_tributary",
161 "neighbor_land_or_water_tributary",
162 "de_jure_claim",
163 "title_claim",
164 "all",
165 "claim",
167 "de_jure",
168 "independence_domain",
169 ];
170 vd.field_choice("target_titles", choices);
171 let choices = &["all", "barony", "county", "duchy", "kingdom", "empire", "hegemony"];
172 vd.field_choice("target_title_tier", choices);
173 vd.field_bool("target_de_jure_regions_above");
174 vd.field_bool("use_de_jure_wargoal_only");
175
176 vd.field_item("cb_name", Item::Localization);
178 vd.field_item("cb_name_no_target", Item::Localization);
179
180 vd.field_item("war_name", Item::Localization);
181 vd.field_item("my_war_name", Item::Localization);
182 vd.field_item("war_name_base", Item::Localization);
183 vd.field_item("my_war_name_base", Item::Localization);
184
185 vd.field_integer("truce_days"); vd.multi_field_value("ignore_effect"); let choices = &["invalidate", "inherit", "inherit_faction"];
190 vd.field_choice("on_primary_attacker_death", choices);
191 vd.field_choice("on_primary_defender_death", choices);
192 vd.field_choice("transfer_behavior", &["invalidate", "transfer"]);
193 vd.field_bool("check_attacker_inheritance_validity");
194 vd.field_bool("check_defender_inheritance_validity");
195 vd.field_bool("attacker_allies_inherit");
196 vd.field_bool("defender_allies_inherit");
197
198 vd.field_integer("interface_priority");
199 vd.field_integer("max_ai_diplo_distance_to_title");
200 vd.field_bool("ai_only_against_liege");
201 vd.field_bool("ai_only_against_neighbors");
202 vd.field_trigger_rooted("ai_can_target_all_titles", Tooltipped::No, Scopes::Character);
203 vd.field_bool("ai");
204
205 vd.field_script_value_no_breakdown_builder("ai_overlord_defensive_power_impact", |key| {
206 let mut sc = ScopeContext::new(Scopes::Character, key);
207 sc.define_name("attacker", Scopes::Character, key);
208 sc.define_name("defender", Scopes::Character, key);
209 sc.define_name("overlord", Scopes::Character, key);
210 sc
211 });
212 vd.field_bool("white_peace_possible");
213 vd.field_bool("check_all_defenders_for_ticking_war_score");
214 vd.field_bool("ticking_war_score_targets_entire_realm");
215
216 vd.field_bool("gui_attacker_faith_might_join");
217 vd.field_bool("gui_defender_faith_might_join");
218 vd.field_bool("defender_faith_can_join");
219 vd.field_bool("is_great_holy_war");
220 vd.field_bool("is_holy_war"); vd.field_bool("target_top_liege_if_outside_realm");
222 vd.field_bool("should_check_for_interface_availability");
223
224 vd.field_script_value("ai_score", &mut sc);
225 vd.field_script_value("ai_score_mult", &mut sc);
226
227 vd.field_bool("should_show_war_goal_subview");
229 }
230}
231
232#[derive(Clone, Debug)]
233pub struct CasusBelliGroup {}
234
235inventory::submit! {
236 ItemLoader::Normal(GameFlags::Ck3, Item::CasusBelliGroup, CasusBelliGroup::add)
237}
238
239impl CasusBelliGroup {
240 pub fn add(db: &mut Db, key: Token, block: Block) {
241 db.add(Item::CasusBelliGroup, key, block, Box::new(Self {}));
242 }
243}
244
245impl DbKind for CasusBelliGroup {
246 fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
247 let mut vd = Validator::new(block, data);
248 vd.field_trigger_rooted("allowed_for_character", Tooltipped::No, Scopes::Character);
249 vd.field_bool("should_check_for_interface_availability");
250 vd.field_bool("can_only_start_via_script");
251
252 vd.field_bool("debug");
255 }
256}