1use crate::block::{BV, Block};
2use crate::ck3::modif::ModifKinds;
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::modif::validate_modifs;
10use crate::report::{ErrorKey, err, warn};
11use crate::scopes::Scopes;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::trigger::validate_trigger;
15use crate::validator::Validator;
16
17#[derive(Clone, Debug)]
18pub struct CouncilPosition {}
19
20inventory::submit! {
21 ItemLoader::Normal(GameFlags::Ck3, Item::CouncilPosition, CouncilPosition::add)
22}
23
24impl CouncilPosition {
25 pub fn add(db: &mut Db, key: Token, block: Block) {
26 db.add(Item::CouncilPosition, key, block, Box::new(Self {}));
27 }
28}
29
30impl DbKind for CouncilPosition {
31 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
32 let mut vd = Validator::new(block, data);
33 let mut sc = ScopeContext::new(Scopes::Character, key);
34 sc.define_name("councillor", Scopes::Character, key);
35 sc.define_name("councillor_liege", Scopes::Character, key);
36
37 data.verify_exists(Item::Localization, key);
39 let loca = format!("{key}_possessive");
40 data.verify_exists_implied(Item::Localization, &loca, key);
41
42 let mut sc_name = ScopeContext::new(Scopes::Character, key);
43 sc_name.define_name("councillor", Scopes::Character, key);
44 vd.field_validated_sc("name", &mut sc_name, validate_desc);
45 vd.field_validated_sc("tooltip", &mut sc, validate_desc);
46
47 vd.field_item("skill", Item::Skill);
48 vd.field_validated_sc("auto_fill", &mut sc, validate_yes_no_trigger);
49 vd.field_bool("fill_from_pool");
50 vd.field_bool("is_clergy_position");
51 vd.field_validated_sc("inherit", &mut sc, validate_yes_no_trigger);
52 vd.field_validated_sc("can_fire", &mut sc, validate_yes_no_trigger);
53 vd.field_validated_sc("can_reassign", &mut sc, validate_yes_no_trigger);
54 vd.field_validated_sc("can_change_once", &mut sc, validate_yes_no_trigger);
55
56 let mut count = 0;
57 vd.multi_field_validated_block_rooted("modifier", Scopes::Character, |block, data, sc| {
58 let mut vd = Validator::new(block, data);
59 vd.field_item("name", Item::Localization);
60 vd.field_script_value("scale", sc);
61 validate_modifs(block, data, ModifKinds::Character, vd);
62 count += 1;
63 if count > 5 {
64 let msg = "no more than 5 modifier blocks can be specified here";
65 warn(ErrorKey::Validation).msg(msg).loc(block).push();
66 }
67 });
68 count = 0;
69 vd.multi_field_validated_block_rooted(
70 "council_owner_modifier",
71 Scopes::Character,
72 |block, data, sc| {
73 let mut vd = Validator::new(block, data);
74 vd.field_item("name", Item::Localization);
75 vd.field_script_value("scale", sc);
76 validate_modifs(block, data, ModifKinds::Character, vd);
77 count += 1;
78 if count > 5 {
79 let msg = "no more than 5 council_owner_modifier blocks can be specified here";
80 warn(ErrorKey::Validation).msg(msg).loc(block).push();
81 }
82 },
83 );
84
85 vd.field_trigger_rooted("valid_position", Tooltipped::No, Scopes::Character);
86 vd.field_trigger("valid_character", Tooltipped::No, &mut sc);
87
88 vd.field_effect_builder("on_get_position", Tooltipped::No, |key| {
89 let mut sc = ScopeContext::new(Scopes::Character, key);
90 sc.define_name("councillor_liege", Scopes::Character, key);
91 sc.define_name("swapped_position", Scopes::all(), key);
93 sc
94 });
95 vd.field_effect_builder("on_lose_position", Tooltipped::No, |key| {
96 let mut sc = ScopeContext::new(Scopes::Character, key);
97 sc.define_name("councillor_liege", Scopes::Character, key);
98 sc
99 });
100 vd.field_effect_builder("on_fired_from_position", Tooltipped::No, |key| {
101 let mut sc = ScopeContext::new(Scopes::Character, key);
102 sc.define_name("councillor_liege", Scopes::Character, key);
103 sc.define_name("new_councillor", Scopes::Character, key);
104 sc
105 });
106
107 vd.replaced_field("use_for_scheme_power", "use_for_scheme_phase_duration");
108 vd.field_bool("use_for_scheme_phase_duration");
109 vd.field_bool("use_for_scheme_resistance");
110
111 vd.field_item("portrait_animation", Item::PortraitAnimation);
112 vd.field_validated_block("barbershop_data", |block, data| {
113 let mut vd = Validator::new(block, data);
114 vd.field_list_numeric_exactly("position", 2);
115 vd.field_bool("click_to_front");
116 });
117
118 vd.field_script_value("councillor_cooldown_days", &mut sc);
121 vd.field_item("pool_character_config", Item::PoolSelector);
122 }
123}
124
125#[derive(Clone, Debug)]
126pub struct CouncilTask {}
127
128inventory::submit! {
129 ItemLoader::Normal(GameFlags::Ck3, Item::CouncilTask, CouncilTask::add)
130}
131
132impl CouncilTask {
133 pub fn add(db: &mut Db, key: Token, block: Block) {
134 db.add(Item::CouncilTask, key, block, Box::new(Self {}));
135 }
136}
137
138impl DbKind for CouncilTask {
139 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
140 let mut vd = Validator::new(block, data);
141 let mut sc = ScopeContext::new(Scopes::Character, key);
142 sc.define_name("councillor", Scopes::Character, key);
143 sc.define_name("councillor_liege", Scopes::Character, key);
144 if let Some(token) = block.get_field_value("task_type") {
145 if token.is("task_type_county") {
146 sc.define_name("province", Scopes::Province, token);
147 sc.define_name("county", Scopes::LandedTitle, token);
148 } else if token.is("task_type_court") {
149 sc.define_name("target_character", Scopes::Character, token);
150 }
151 }
152
153 vd.field_validated_value("default_task", |key, mut vd| {
154 vd.bool();
155 if vd.value().is("yes") {
156 if !block.field_value_is("task_type", "task_type_general") {
157 let msg = "`default_task` is only available for `task_type_general` tasks";
158 err(ErrorKey::Validation).msg(msg).loc(key).push();
159 }
160 if !block.field_value_is("task_progress", "task_progress_infinite") {
161 let msg = "`default_task` is only available for `task_progress_infinite` tasks";
162 err(ErrorKey::Validation).msg(msg).loc(key).push();
163 }
164 }
165 });
166 vd.field_item("position", Item::CouncilPosition);
167 vd.field_choice("task_type", &["task_type_general", "task_type_county", "task_type_court"]);
168 if block.field_value_is("task_type", "task_type_county") {
169 vd.field_choice(
170 "county_target",
171 &["all", "realm", "domain", "neighbor_land", "neighbor_land_or_water"],
172 );
173 vd.field_choice(
174 "ai_county_target",
175 &["all", "realm", "domain", "neighbor_land", "neighbor_land_or_water"],
176 );
177 if let Some(token) = block.get_field_value("county_target") {
178 if token.is("neighbor_land_or_water") {
179 let msg = "`neighbor_land_or_water` is only for `ai_county_target`";
180 warn(ErrorKey::Validation).msg(msg).loc(token).push();
181 }
182 }
183 vd.field_script_value("ai_target_score", &mut sc);
184 } else {
185 vd.ban_field("county_target", || "task_type_county");
186 vd.ban_field("ai_county_target", || "task_type_county");
187 vd.ban_field("ai_target_score", || "task_type_county");
188 }
189
190 vd.field_choice(
191 "task_progress",
192 &["task_progress_infinite", "task_progress_percentage", "task_progress_value"],
193 );
194 if block.field_value_is("task_progress", "task_progress_value") {
195 vd.field_script_value("task_current_value", &mut sc);
196 vd.field_script_value("task_max_value", &mut sc);
197 } else {
198 vd.ban_field("task_current_value", || "task_progress_value");
199 vd.ban_field("task_max_value", || "task_progress_value");
200 }
201 vd.field_bool("restart_on_finish");
202 vd.field_bool("highlight_own_realm");
203
204 vd.multi_field_validated_block_sc("asset", &mut sc, validate_asset);
205
206 vd.multi_field_validated_block_rooted(
207 "councillor_modifier",
208 Scopes::Character,
209 |block, data, sc| {
210 let mut vd = Validator::new(block, data);
211 vd.field_item("name", Item::Localization);
212 vd.field_script_value("scale", sc);
213 validate_modifs(block, data, ModifKinds::Character, vd);
214 },
215 );
216 vd.multi_field_validated_block_rooted(
217 "council_owner_modifier",
218 Scopes::Character,
219 |block, data, sc| {
220 let mut vd = Validator::new(block, data);
221 vd.field_item("name", Item::Localization);
222 vd.field_script_value("scale", sc);
223 validate_modifs(block, data, ModifKinds::Character, vd);
224 },
225 );
226 if block.field_value_is("task_type", "task_type_county") {
227 vd.multi_field_validated_block_rooted(
228 "county_modifier",
229 Scopes::Character,
230 |block, data, sc| {
231 let mut vd = Validator::new(block, data);
232 vd.field_item("name", Item::Localization);
233 vd.field_script_value("scale", sc);
234 validate_modifs(block, data, ModifKinds::County, vd);
235 },
236 );
237 } else {
238 vd.ban_field("county_modifier", || "task_type_county");
239 }
240
241 vd.field_trigger("is_shown", Tooltipped::No, &mut sc);
242 vd.field_trigger("is_valid_showing_failures_only", Tooltipped::FailuresOnly, &mut sc);
243 vd.field_effect("on_start_task", Tooltipped::Yes, &mut sc);
244 vd.field_effect("on_finish_task", Tooltipped::Yes, &mut sc);
245 vd.field_effect("on_cancel_task", Tooltipped::No, &mut sc);
246 vd.field_effect("on_monthly", Tooltipped::No, &mut sc);
247 vd.field_action("monthly_on_action", &sc);
248
249 if let Some(token) = block.get_field_value("task_type") {
250 if token.is("task_type_county") {
251 vd.field_trigger("potential_county", Tooltipped::Yes, &mut sc);
252 vd.field_trigger("valid_county", Tooltipped::Yes, &mut sc);
253 vd.field_effect("on_start_task_county", Tooltipped::Yes, &mut sc);
254 vd.field_effect("on_finish_task_county", Tooltipped::Yes, &mut sc);
255 vd.field_effect("on_cancel_task_county", Tooltipped::Yes, &mut sc);
256 vd.field_effect("on_monthly_county", Tooltipped::No, &mut sc);
257 } else {
258 vd.ban_field("potential_county", || "task_type_county");
259 vd.ban_field("valid_county", || "task_type_county");
260 vd.ban_field("on_start_task_county", || "task_type_county");
261 vd.ban_field("on_finish_task_county", || "task_type_county");
262 vd.ban_field("on_cancel_task_county", || "task_type_county");
263 vd.ban_field("on_monthly_county", || "task_type_county");
264 }
265 if token.is("task_type_court") {
266 vd.field_trigger("potential_target_court", Tooltipped::Yes, &mut sc);
267 vd.field_trigger("valid_target_court", Tooltipped::Yes, &mut sc);
268 vd.field_effect("on_start_task_court", Tooltipped::Yes, &mut sc);
269 vd.field_effect("on_finish_task_court", Tooltipped::Yes, &mut sc);
270 vd.field_effect("on_cancel_task_court", Tooltipped::No, &mut sc);
271 vd.field_effect("on_monthly_court", Tooltipped::No, &mut sc);
272 } else {
273 vd.ban_field("potential_court", || "task_type_court");
274 vd.ban_field("valid_court", || "task_type_court");
275 vd.ban_field("on_start_task_court", || "task_type_court");
276 vd.ban_field("on_finish_task_court", || "task_type_court");
277 vd.ban_field("on_cancel_task_court", || "task_type_court");
278 vd.ban_field("on_monthly_court", || "task_type_court");
279 }
280 }
281
282 if !block.field_value_is("task_progress", "task_progress_infinite")
284 || key.is("task_accept_culture")
285 {
286 vd.field_script_value("progress", &mut sc); vd.field_script_value("full_progress", &mut sc);
288 } else {
289 vd.ban_field("progress", || "task_progress_percent or task_progress_value");
290 vd.ban_field("full_progress", || "task_progress_percent or task_progress_value");
291 }
292 vd.field_item("custom_other_loc", Item::Localization);
293 vd.field_validated_sc("effect_desc", &mut sc, validate_desc);
294 vd.field_item("clone", Item::CouncilTask);
297
298 vd.field_script_value("ai_will_do", &mut sc);
300 vd.field_item("skill", Item::Skill);
301 }
302}
303
304fn validate_yes_no_trigger(bv: &BV, data: &Everything, sc: &mut ScopeContext) {
305 match bv {
306 BV::Value(token) => {
307 if !token.is("yes") && !token.is("no") {
308 err(ErrorKey::Validation).msg("expected yes or no or trigger").loc(token).push();
309 }
310 }
311 BV::Block(block) => {
312 validate_trigger(block, data, sc, Tooltipped::No);
313 }
314 }
315}
316
317fn validate_asset(block: &Block, data: &Everything, sc: &mut ScopeContext) {
318 let mut vd = Validator::new(block, data);
319 vd.field_trigger("trigger", Tooltipped::No, sc);
320 vd.field_item("icon", Item::File);
321 vd.field_item("background", Item::File);
322 vd.field_item("frame", Item::File);
323 vd.field_item("glow", Item::File);
324}