Skip to main content

tiger_lib/ck3/data/
buildings.rs

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        // TODO: int
86        vd.field_validated("levy", validate_non_dynamic_script_value);
87        // TODO: int
88        vd.field_validated("max_garrison", validate_non_dynamic_script_value);
89        // TODO: 0..1
90        vd.field_validated("garrison_reinforcement_factor", validate_non_dynamic_script_value);
91        // TODO: int
92        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        // TODO: only for Great Buildings
108        vd.field_trigger_builder("can_rebuild", Tooltipped::Yes, sc_builder);
109
110        // For buildings that are upgrades, can_construct_potential is added to can_construct_showing_failures_only so it will be tooltipped
111        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        // TODO: fallback requires an `is_enabled` trigger to be configured as well
141        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                // TODO: docs say scope:character but script uses holder
155                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        // TODO: only for Great Buildings
173        vd.field_item("great_project_type", Item::GreatProjectType);
174
175        // undocumented
176
177        // TODO: only for Great Buildings
178        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"); // TODO
218            }
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); // TODO check that it's a graphical region
225    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}