1use crate::block::{BV, Block};
2use crate::ck3::modif::ModifKinds;
3use crate::ck3::validate::validate_cost;
4use crate::context::ScopeContext;
5use crate::db::{Db, DbKind};
6use crate::desc::validate_desc;
7use crate::everything::Everything;
8use crate::game::GameFlags;
9use crate::item::{Item, ItemLoader};
10use crate::modif::validate_modifs;
11use crate::scopes::Scopes;
12use crate::script_value::validate_non_dynamic_script_value;
13use crate::token::Token;
14use crate::tooltipped::Tooltipped;
15use crate::validate::validate_modifiers_with_base;
16use crate::validator::Validator;
17
18#[derive(Clone, Debug)]
19pub struct Building {
20 is_upgrade: bool,
21}
22
23inventory::submit! {
24 ItemLoader::Normal(GameFlags::Ck3, Item::Building, Building::add)
25}
26
27impl Building {
28 pub fn new() -> Self {
29 Self { is_upgrade: false }
30 }
31
32 pub fn add(db: &mut Db, key: Token, block: Block) {
33 db.add(Item::Building, key, block, Box::new(Self::new()));
34 }
35
36 pub fn finalize(db: &mut Db) {
37 let mut upgrades = Vec::new();
38 for (_, block) in db.iter_key_block(Item::Building) {
39 if let Some(token) = block.get_field_value("next_building") {
40 upgrades.push(token.to_string());
41 }
42 }
43 for upgrade in upgrades {
44 db.set_property(Item::Building, &upgrade, "is_upgrade");
45 }
46 }
47}
48
49impl DbKind for Building {
50 fn add_subitems(&self, key: &Token, block: &Block, db: &mut Db) {
51 for token in block.get_field_values("flag") {
52 db.add_flag(Item::BuildingFlag, token.clone());
53 }
54 if block.field_value_is("type", "special") {
55 db.add_flag(Item::SpecialBuilding, key.clone());
56 }
57 }
58
59 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
60 fn sc_builder(key: &Token) -> ScopeContext {
61 let mut sc = ScopeContext::new(Scopes::Province, key);
62 sc.define_name("holder", Scopes::Character, key);
63 sc.define_name("county", Scopes::LandedTitle, key);
64 sc
65 }
66
67 let mut vd = Validator::new(block, data);
68
69 let graphical_only = block.get_field_bool("is_graphical_background").unwrap_or(false);
70 if !graphical_only {
71 let loca = format!("building_{key}");
72 data.verify_exists_implied(Item::Localization, &loca, key);
73 let loca = format!("building_{key}_desc");
74 data.verify_exists_implied(Item::Localization, &loca, key);
75 if !self.is_upgrade {
76 let loca = format!("building_type_{key}");
77 data.verify_exists_implied(Item::Localization, &loca, key);
78 let loca = format!("building_type_{key}_desc");
79 data.verify_exists_implied(Item::Localization, &loca, key);
80 }
81 }
82
83 vd.field_icon("type_icon", "NGameIcons|BUILDING_TYPE_ICON_PATH", "");
84
85 vd.field_validated("levy", validate_non_dynamic_script_value);
87 vd.field_validated("max_garrison", validate_non_dynamic_script_value);
89 vd.field_validated("garrison_reinforcement_factor", validate_non_dynamic_script_value);
91 vd.field_validated("construction_time", validate_non_dynamic_script_value);
93 vd.field_choice("type", &["regular", "special", "duchy_capital", "great_building"]);
94
95 vd.multi_field_validated_block("assets", |block, data| {
96 let mut vd = Validator::new(block, data);
97 vd.multi_field_validated_block("asset", validate_asset);
98 });
99 vd.multi_field_validated_block("asset", validate_asset);
100
101 vd.field_trigger_builder(
102 "is_enabled",
103 if graphical_only { Tooltipped::No } else { Tooltipped::FailuresOnly },
104 sc_builder,
105 );
106
107 vd.field_trigger_builder("can_rebuild", Tooltipped::Yes, sc_builder);
109
110 vd.field_trigger_builder(
112 "can_construct_potential",
113 if block.get_field_bool("show_disabled").unwrap_or(false) || self.is_upgrade {
114 Tooltipped::FailuresOnly
115 } else {
116 Tooltipped::No
117 },
118 sc_builder,
119 );
120
121 vd.field_trigger_builder(
122 "can_construct_showing_failures_only",
123 Tooltipped::FailuresOnly,
124 sc_builder,
125 );
126
127 vd.field_trigger_builder("can_construct", Tooltipped::Yes, sc_builder);
128 vd.field_bool("show_disabled");
129
130 vd.field_script_value_rooted("cost_gold", Scopes::Character);
131 vd.field_script_value_rooted("cost_piety", Scopes::Character);
132 vd.field_script_value_rooted("cost_prestige", Scopes::Character);
133 vd.field_validated_block_rooted("cost", Scopes::Character, validate_cost);
134
135 vd.field_item("next_building", Item::Building);
136 vd.field_validated_rooted("effect_desc", Scopes::None, validate_desc);
137
138 let is_duchy_capital = block.get_field_value("type").is_some_and(|t| t.is("duchy_capital"));
139 validate_modifiers(&mut vd, is_duchy_capital);
140 vd.field_validated_block("fallback", |block, data| {
142 let mut vd = Validator::new(block, data);
143 validate_modifiers(&mut vd, is_duchy_capital);
144 });
145
146 vd.multi_field_value("flag");
147
148 vd.field_validated_key("ai_value", |key, bv, data| match bv {
149 BV::Value(token) => {
150 token.expect_integer();
151 }
152 BV::Block(block) => {
153 let mut sc = ScopeContext::new(Scopes::Province, key);
154 sc.define_name("holder", Scopes::Character, key);
156 sc.define_name("holding", Scopes::HoldingType, key);
157 validate_modifiers_with_base(block, data, &mut sc);
158 }
159 });
160
161 vd.field_bool("is_graphical_background");
162
163 for field in ["on_start", "on_cancelled", "on_complete"] {
164 vd.field_effect_builder(field, Tooltipped::No, |key| {
165 let mut sc = ScopeContext::new(Scopes::Province, key);
166 sc.define_name("character", Scopes::Character, key);
167 sc.define_name("holding", Scopes::HoldingType, key);
168 sc
169 });
170 }
171
172 vd.field_item("great_project_type", Item::GreatProjectType);
174
175 vd.field_validated_block_rooted("rebuild_cost", Scopes::Character, validate_cost);
179 vd.field_bool("is_mandala_capital");
180
181 vd.field_validated_block("override_modifiers", |block, data| {
182 let mut vd = Validator::new(block, data);
183 vd.field_item("requires_dlc_flag", Item::DlcFeature);
184 validate_modifiers(&mut vd, is_duchy_capital);
185 });
186 }
187
188 fn set_property(&mut self, _key: &Token, _block: &Block, property: &str) {
189 if property == "is_upgrade" {
190 self.is_upgrade = true;
191 }
192 }
193}
194
195fn validate_asset(block: &Block, data: &Everything) {
196 let mut vd = Validator::new(block, data);
197 vd.req_field("type");
198 vd.field_choice("type", &["pdxmesh", "entity"]);
199
200 let meshes = block.field_value_is("type", "pdxmesh");
201 let itype = if meshes { Item::Pdxmesh } else { Item::Entity };
202
203 vd.multi_field_item("name", itype);
204 vd.field_list_items("names", itype);
205
206 vd.field_item("domicile_building", Item::DomicileBuilding);
207
208 vd.field_item("illustration", Item::File);
209
210 vd.multi_field_validated("soundeffect", |bv, data| {
211 match bv {
212 BV::Value(token) => data.verify_exists(Item::Sound, token),
213 BV::Block(block) => {
214 let mut vd = Validator::new(block, data);
215 vd.req_field("soundeffect");
216 vd.field_item("soundeffect", Item::Sound);
217 vd.field_block("soundparameter"); }
219 }
220 });
221
222 vd.field_list_items("graphical_cultures", Item::BuildingGfx);
223 vd.field_list_items("graphical_faiths", Item::GraphicalFaith);
224 vd.field_list_items("graphical_regions", Item::Region); vd.field_list_items("provinces", Item::Province);
226 vd.field_list_items("governments", Item::GovernmentType);
227
228 vd.field_item("requires_dlc_flag", Item::DlcFeature);
229}
230
231fn validate_modifiers(vd: &mut Validator, is_duchy_capital: bool) {
232 vd.multi_field_validated_block("character_modifier", |block, data| {
233 let vd = Validator::new(block, data);
234 validate_modifs(block, data, ModifKinds::Character, vd);
235 });
236 vd.multi_field_validated_block("character_culture_modifier", |block, data| {
237 let mut vd = Validator::new(block, data);
238 vd.req_field("parameter");
239 vd.field_item("parameter", Item::CultureParameter);
240 validate_modifs(block, data, ModifKinds::Character, vd);
241 });
242 vd.multi_field_validated_block("character_dynasty_modifier", |block, data| {
243 let mut vd = Validator::new(block, data);
244 vd.req_field("county_holder_dynasty_perk");
245 vd.field_item("county_holder_dynasty_perk", Item::DynastyPerk);
246 validate_modifs(block, data, ModifKinds::Character, vd);
247 });
248 vd.multi_field_validated_block("character_faith_modifier", |block, data| {
249 let mut vd = Validator::new(block, data);
250 vd.req_field("parameter");
251 vd.field_item("parameter", Item::DoctrineParameter);
252 validate_modifs(block, data, ModifKinds::Character, vd);
253 });
254
255 vd.multi_field_validated_block("province_modifier", |block, data| {
256 let vd = Validator::new(block, data);
257 validate_modifs(block, data, ModifKinds::Province, vd);
258 });
259 vd.multi_field_validated_block("province_culture_modifier", |block, data| {
260 let mut vd = Validator::new(block, data);
261 vd.req_field("parameter");
262 vd.field_item("parameter", Item::CultureParameter);
263 validate_modifs(block, data, ModifKinds::Province, vd);
264 });
265 vd.multi_field_validated_block("province_faith_modifier", |block, data| {
266 let mut vd = Validator::new(block, data);
267 vd.req_field("parameter");
268 vd.field_item("parameter", Item::DoctrineParameter);
269 validate_modifs(block, data, ModifKinds::Province, vd);
270 });
271 vd.multi_field_validated_block("province_terrain_modifier", |block, data| {
272 let mut vd = Validator::new(block, data);
273 vd.field_item("parameter", Item::CultureParameter);
274 vd.field_item("terrain", Item::Terrain);
275 vd.field_bool("is_coastal");
276 validate_modifs(block, data, ModifKinds::Province, vd);
277 });
278 vd.multi_field_validated_block("province_dynasty_modifier", |block, data| {
279 let mut vd = Validator::new(block, data);
280 vd.req_field("county_holder_dynasty_perk");
281 vd.field_item("county_holder_dynasty_perk", Item::DynastyPerk);
282 validate_modifs(block, data, ModifKinds::Province, vd);
283 });
284
285 vd.multi_field_validated_block("county_modifier", |block, data| {
286 let vd = Validator::new(block, data);
287 validate_modifs(block, data, ModifKinds::County, vd);
288 });
289 vd.multi_field_validated_block("county_culture_modifier", |block, data| {
290 let mut vd = Validator::new(block, data);
291 vd.req_field("parameter");
292 vd.field_item("parameter", Item::CultureParameter);
293 validate_modifs(block, data, ModifKinds::County, vd);
294 });
295 vd.multi_field_validated_block("county_faith_modifier", |block, data| {
296 let mut vd = Validator::new(block, data);
297 vd.req_field("parameter");
298 vd.field_item("parameter", Item::DoctrineParameter);
299 validate_modifs(block, data, ModifKinds::County, vd);
300 });
301
302 if is_duchy_capital {
303 vd.multi_field_validated_block("duchy_capital_county_modifier", |block, data| {
304 let vd = Validator::new(block, data);
305 validate_modifs(block, data, ModifKinds::County, vd);
306 });
307 vd.multi_field_validated_block("duchy_capital_county_culture_modifier", |block, data| {
308 let mut vd = Validator::new(block, data);
309 vd.req_field("parameter");
310 vd.field_item("parameter", Item::CultureParameter);
311 validate_modifs(block, data, ModifKinds::County, vd);
312 });
313 vd.multi_field_validated_block("duchy_capital_county_faith_modifier", |block, data| {
314 let mut vd = Validator::new(block, data);
315 vd.req_field("parameter");
316 vd.field_item("parameter", Item::DoctrineParameter);
317 validate_modifs(block, data, ModifKinds::County, vd);
318 });
319
320 vd.multi_field_validated_block("duchy_capital_county_situation_modifier", |block, data| {
321 let mut vd = Validator::new(block, data);
322 vd.req_field("parameter");
323 vd.field_item("parameter", Item::SituationPhaseParameter);
324 validate_modifs(block, data, ModifKinds::County, vd);
325 });
326 } else {
327 vd.ban_field("duchy_capital_county_modifier", || "duchy_capital buildings");
328 vd.ban_field("duchy_capital_county_culture_modifier", || "duchy_capital buildings");
329 vd.ban_field("duchy_capital_county_situation_modifier", || "duchy_capital buildings");
330 }
331
332 vd.multi_field_validated_block("county_situation_modifier", |block, data| {
333 let mut vd = Validator::new(block, data);
334 vd.req_field("parameter");
335 vd.field_item("parameter", Item::SituationPhaseParameter);
336 validate_modifs(block, data, ModifKinds::County, vd);
337 });
338
339 vd.multi_field_validated_block("province_situation_modifier", |block, data| {
340 let mut vd = Validator::new(block, data);
341 vd.req_field("parameter");
342 vd.field_item("parameter", Item::SituationPhaseParameter);
343 validate_modifs(block, data, ModifKinds::Province, vd);
344 });
345
346 vd.multi_field_validated_block("character_situation_modifier", |block, data| {
347 let mut vd = Validator::new(block, data);
348 vd.req_field("parameter");
349 vd.field_item("parameter", Item::SituationPhaseParameter);
350 validate_modifs(block, data, ModifKinds::Character, vd);
351 });
352
353 vd.multi_field_validated_block("county_holding_modifier", |block, data| {
354 let mut vd = Validator::new(block, data);
355 vd.req_field("holding");
356 vd.field_item("holding", Item::HoldingType);
357 validate_modifs(block, data, ModifKinds::County, vd);
358 });
359 vd.multi_field_validated_block("county_dynasty_modifier", |block, data| {
360 let mut vd = Validator::new(block, data);
361 vd.req_field("county_holder_dynasty_perk");
362 vd.field_item("county_holder_dynasty_perk", Item::DynastyPerk);
363 validate_modifs(block, data, ModifKinds::County, vd);
364 });
365 vd.multi_field_validated_block("county_holder_character_modifier", |block, data| {
366 let vd = Validator::new(block, data);
367 validate_modifs(block, data, ModifKinds::Character, vd);
368 });
369
370 vd.multi_field_validated_block("province_government_modifier", |block, data| {
371 let mut vd = Validator::new(block, data);
372 vd.req_field("parameter");
373 vd.field_item("parameter", Item::GovernmentFlag);
374 validate_modifs(block, data, ModifKinds::Province, vd);
375 });
376
377 vd.multi_field_validated_block("character_government_modifier", |block, data| {
378 let mut vd = Validator::new(block, data);
379 vd.req_field("parameter");
380 vd.field_item("parameter", Item::GovernmentFlag);
381 validate_modifs(block, data, ModifKinds::Character, vd);
382 });
383}