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("asset", validate_asset);
96
97 vd.field_trigger_builder(
98 "is_enabled",
99 if graphical_only { Tooltipped::No } else { Tooltipped::FailuresOnly },
100 sc_builder,
101 );
102
103 vd.field_trigger_builder("can_rebuild", Tooltipped::Yes, sc_builder);
105
106 vd.field_trigger_builder(
108 "can_construct_potential",
109 if block.get_field_bool("show_disabled").unwrap_or(false) || self.is_upgrade {
110 Tooltipped::FailuresOnly
111 } else {
112 Tooltipped::No
113 },
114 sc_builder,
115 );
116
117 vd.field_trigger_builder(
118 "can_construct_showing_failures_only",
119 Tooltipped::FailuresOnly,
120 sc_builder,
121 );
122
123 vd.field_trigger_builder("can_construct", Tooltipped::Yes, sc_builder);
124 vd.field_bool("show_disabled");
125
126 vd.field_script_value_rooted("cost_gold", Scopes::Character);
127 vd.field_script_value_rooted("cost_piety", Scopes::Character);
128 vd.field_script_value_rooted("cost_prestige", Scopes::Character);
129 vd.field_validated_block_rooted("cost", Scopes::Character, validate_cost);
130
131 vd.field_item("next_building", Item::Building);
132 vd.field_validated_rooted("effect_desc", Scopes::None, validate_desc);
133
134 let is_duchy_capital = block.get_field_value("type").is_some_and(|t| t.is("duchy_capital"));
135 validate_modifiers(&mut vd, is_duchy_capital);
136 vd.field_validated_block("fallback", |block, data| {
137 let mut vd = Validator::new(block, data);
138 validate_modifiers(&mut vd, is_duchy_capital);
139 });
140
141 vd.multi_field_value("flag");
142
143 vd.field_validated_key("ai_value", |key, bv, data| match bv {
144 BV::Value(token) => {
145 token.expect_integer();
146 }
147 BV::Block(block) => {
148 let mut sc = ScopeContext::new(Scopes::Province, key);
149 sc.define_name("holder", Scopes::Character, key);
151 sc.define_name("holding", Scopes::HoldingType, key);
152 validate_modifiers_with_base(block, data, &mut sc);
153 }
154 });
155
156 vd.field_bool("is_graphical_background");
157
158 for field in ["on_start", "on_cancelled", "on_complete"] {
159 vd.field_effect_builder(field, Tooltipped::No, |key| {
160 let mut sc = ScopeContext::new(Scopes::Province, key);
161 sc.define_name("character", Scopes::Character, key);
162 sc.define_name("holding", Scopes::HoldingType, key);
163 sc
164 });
165 }
166
167 vd.field_item("great_project_type", Item::GreatProjectType);
169
170 vd.field_validated_block_rooted("rebuild_cost", Scopes::Character, validate_cost);
174 vd.field_bool("is_mandala_capital");
175 }
176
177 fn set_property(&mut self, _key: &Token, _block: &Block, property: &str) {
178 if property == "is_upgrade" {
179 self.is_upgrade = true;
180 }
181 }
182}
183
184fn validate_asset(block: &Block, data: &Everything) {
185 let mut vd = Validator::new(block, data);
186 vd.req_field("type");
187 vd.field_choice("type", &["pdxmesh", "entity"]);
188
189 let meshes = block.field_value_is("type", "pdxmesh");
190 let itype = if meshes { Item::Pdxmesh } else { Item::Entity };
191
192 vd.multi_field_item("name", itype);
193 vd.field_list_items("names", itype);
194
195 vd.field_item("domicile_building", Item::DomicileBuilding);
196
197 vd.field_item("illustration", Item::File);
198
199 vd.multi_field_validated("soundeffect", |bv, data| {
200 match bv {
201 BV::Value(token) => data.verify_exists(Item::Sound, token),
202 BV::Block(block) => {
203 let mut vd = Validator::new(block, data);
204 vd.req_field("soundeffect");
205 vd.field_item("soundeffect", Item::Sound);
206 vd.field_block("soundparameter"); }
208 }
209 });
210
211 vd.field_list_items("graphical_cultures", Item::BuildingGfx);
212 vd.field_list_items("graphical_faiths", Item::GraphicalFaith);
213 vd.field_list_items("graphical_regions", Item::Region); vd.field_list_items("provinces", Item::Province);
215 vd.field_list_items("governments", Item::GovernmentType);
216
217 vd.field_item("requires_dlc_flag", Item::DlcFeature);
218}
219
220fn validate_modifiers(vd: &mut Validator, is_duchy_capital: bool) {
221 vd.multi_field_validated_block("character_modifier", |block, data| {
222 let vd = Validator::new(block, data);
223 validate_modifs(block, data, ModifKinds::Character, vd);
224 });
225 vd.multi_field_validated_block("character_culture_modifier", |block, data| {
226 let mut vd = Validator::new(block, data);
227 vd.req_field("parameter");
228 vd.field_item("parameter", Item::CultureParameter);
229 validate_modifs(block, data, ModifKinds::Character, vd);
230 });
231 vd.multi_field_validated_block("character_dynasty_modifier", |block, data| {
232 let mut vd = Validator::new(block, data);
233 vd.req_field("county_holder_dynasty_perk");
234 vd.field_item("county_holder_dynasty_perk", Item::DynastyPerk);
235 validate_modifs(block, data, ModifKinds::Character, vd);
236 });
237 vd.multi_field_validated_block("character_faith_modifier", |block, data| {
238 let mut vd = Validator::new(block, data);
239 vd.req_field("parameter");
240 vd.field_item("parameter", Item::DoctrineParameter);
241 validate_modifs(block, data, ModifKinds::Character, vd);
242 });
243
244 vd.multi_field_validated_block("province_modifier", |block, data| {
245 let vd = Validator::new(block, data);
246 validate_modifs(block, data, ModifKinds::Province, vd);
247 });
248 vd.multi_field_validated_block("province_culture_modifier", |block, data| {
249 let mut vd = Validator::new(block, data);
250 vd.req_field("parameter");
251 vd.field_item("parameter", Item::CultureParameter);
252 validate_modifs(block, data, ModifKinds::Province, vd);
253 });
254 vd.multi_field_validated_block("province_faith_modifier", |block, data| {
255 let mut vd = Validator::new(block, data);
256 vd.req_field("parameter");
257 vd.field_item("parameter", Item::DoctrineParameter);
258 validate_modifs(block, data, ModifKinds::Province, vd);
259 });
260 vd.multi_field_validated_block("province_terrain_modifier", |block, data| {
261 let mut vd = Validator::new(block, data);
262 vd.field_item("parameter", Item::CultureParameter);
263 vd.field_item("terrain", Item::Terrain);
264 vd.field_bool("is_coastal");
265 validate_modifs(block, data, ModifKinds::Province, vd);
266 });
267 vd.multi_field_validated_block("province_dynasty_modifier", |block, data| {
268 let mut vd = Validator::new(block, data);
269 vd.req_field("county_holder_dynasty_perk");
270 vd.field_item("county_holder_dynasty_perk", Item::DynastyPerk);
271 validate_modifs(block, data, ModifKinds::Province, vd);
272 });
273
274 vd.multi_field_validated_block("county_modifier", |block, data| {
275 let vd = Validator::new(block, data);
276 validate_modifs(block, data, ModifKinds::County, vd);
277 });
278 vd.multi_field_validated_block("county_culture_modifier", |block, data| {
279 let mut vd = Validator::new(block, data);
280 vd.req_field("parameter");
281 vd.field_item("parameter", Item::CultureParameter);
282 validate_modifs(block, data, ModifKinds::County, vd);
283 });
284 vd.multi_field_validated_block("county_faith_modifier", |block, data| {
285 let mut vd = Validator::new(block, data);
286 vd.req_field("parameter");
287 vd.field_item("parameter", Item::DoctrineParameter);
288 validate_modifs(block, data, ModifKinds::County, vd);
289 });
290
291 if is_duchy_capital {
292 vd.multi_field_validated_block("duchy_capital_county_modifier", |block, data| {
293 let vd = Validator::new(block, data);
294 validate_modifs(block, data, ModifKinds::County, vd);
295 });
296 vd.multi_field_validated_block("duchy_capital_county_culture_modifier", |block, data| {
297 let mut vd = Validator::new(block, data);
298 vd.req_field("parameter");
299 vd.field_item("parameter", Item::CultureParameter);
300 validate_modifs(block, data, ModifKinds::County, vd);
301 });
302 vd.multi_field_validated_block("duchy_capital_county_faith_modifier", |block, data| {
303 let mut vd = Validator::new(block, data);
304 vd.req_field("parameter");
305 vd.field_item("parameter", Item::DoctrineParameter);
306 validate_modifs(block, data, ModifKinds::County, vd);
307 });
308
309 vd.multi_field_validated_block("duchy_capital_county_situation_modifier", |block, data| {
310 let mut vd = Validator::new(block, data);
311 vd.req_field("parameter");
312 vd.field_item("parameter", Item::SituationPhaseParameter);
313 validate_modifs(block, data, ModifKinds::County, vd);
314 });
315 } else {
316 vd.ban_field("duchy_capital_county_modifier", || "duchy_capital buildings");
317 vd.ban_field("duchy_capital_county_culture_modifier", || "duchy_capital buildings");
318 vd.ban_field("duchy_capital_county_situation_modifier", || "duchy_capital buildings");
319 }
320
321 vd.multi_field_validated_block("county_situation_modifier", |block, data| {
322 let mut vd = Validator::new(block, data);
323 vd.req_field("parameter");
324 vd.field_item("parameter", Item::SituationPhaseParameter);
325 validate_modifs(block, data, ModifKinds::County, vd);
326 });
327
328 vd.multi_field_validated_block("province_situation_modifier", |block, data| {
329 let mut vd = Validator::new(block, data);
330 vd.req_field("parameter");
331 vd.field_item("parameter", Item::SituationPhaseParameter);
332 validate_modifs(block, data, ModifKinds::Province, vd);
333 });
334
335 vd.multi_field_validated_block("character_situation_modifier", |block, data| {
336 let mut vd = Validator::new(block, data);
337 vd.req_field("parameter");
338 vd.field_item("parameter", Item::SituationPhaseParameter);
339 validate_modifs(block, data, ModifKinds::Character, vd);
340 });
341
342 vd.multi_field_validated_block("county_holding_modifier", |block, data| {
343 let mut vd = Validator::new(block, data);
344 vd.req_field("holding");
345 vd.field_item("holding", Item::HoldingType);
346 validate_modifs(block, data, ModifKinds::County, vd);
347 });
348 vd.multi_field_validated_block("county_dynasty_modifier", |block, data| {
349 let mut vd = Validator::new(block, data);
350 vd.req_field("county_holder_dynasty_perk");
351 vd.field_item("county_holder_dynasty_perk", Item::DynastyPerk);
352 validate_modifs(block, data, ModifKinds::County, vd);
353 });
354 vd.multi_field_validated_block("county_holder_character_modifier", |block, data| {
355 let vd = Validator::new(block, data);
356 validate_modifs(block, data, ModifKinds::Character, vd);
357 });
358
359 vd.multi_field_validated_block("province_government_modifier", |block, data| {
360 let mut vd = Validator::new(block, data);
361 vd.req_field("parameter");
362 vd.field_item("parameter", Item::GovernmentFlag);
363 validate_modifs(block, data, ModifKinds::Province, vd);
364 });
365
366 vd.multi_field_validated_block("character_government_modifier", |block, data| {
367 let mut vd = Validator::new(block, data);
368 vd.req_field("parameter");
369 vd.field_item("parameter", Item::GovernmentFlag);
370 validate_modifs(block, data, ModifKinds::Character, vd);
371 });
372}