tiger_lib/ck3/data/
domiciles.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::everything::Everything;
7use crate::game::GameFlags;
8use crate::item::{Item, ItemLoader};
9use crate::modif::validate_modifs;
10use crate::report::{ErrorKey, warn};
11use crate::scopes::Scopes;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::validate::validate_duration;
15use crate::validator::Validator;
16
17#[derive(Clone, Debug)]
18pub struct DomicileType {}
19
20inventory::submit! {
21    ItemLoader::Normal(GameFlags::Ck3, Item::DomicileType, DomicileType::add)
22}
23
24impl DomicileType {
25    pub fn add(db: &mut Db, key: Token, block: Block) {
26        db.add(Item::DomicileType, key, block, Box::new(Self {}));
27    }
28}
29
30impl DbKind for DomicileType {
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::Domicile, key);
34
35        let loca = format!("domicile_{key}");
36        data.verify_exists_implied(Item::Localization, &loca, key);
37
38        vd.field_trigger_rooted("allowed_for_character", Tooltipped::Yes, Scopes::Character);
39        vd.field_choice("rename_window", &["none", "primary_title", "house"]);
40        vd.field_item("illustration", Item::File);
41        vd.field_item("icon", Item::File);
42        vd.field_item("map_pin_texture", Item::File);
43        vd.field_choice("map_pin_anchor", &["up", "right"]);
44        vd.field_bool("map_pin_lobby");
45        vd.field_bool("provisions");
46        vd.field_bool("travel");
47        vd.field_bool("herd");
48        vd.field_bool("culture_and_faith");
49        vd.field_bool("move_with_realm_capital");
50        vd.field_bool("can_move_manually");
51        vd.field_validated_block_sc("move_cooldown", &mut sc, validate_duration);
52        vd.field_validated_block_sc("move_cost", &mut sc, validate_cost);
53        vd.multi_field_validated_key_block(
54            "domicile_temperament_low_modifier",
55            |key, block, data| {
56                let mut sc = ScopeContext::new(Scopes::Character, key);
57                let mut vd = Validator::new(block, data);
58                vd.field_item("name", Item::Localization);
59                vd.field_script_value("scale", &mut sc);
60                validate_modifs(block, data, ModifKinds::Character, vd);
61            },
62        );
63        vd.multi_field_validated_key_block(
64            "domicile_temperament_high_modifier",
65            |key, block, data| {
66                let mut sc = ScopeContext::new(Scopes::Character, key);
67                let mut vd = Validator::new(block, data);
68                vd.field_item("name", Item::Localization);
69                vd.field_script_value("scale", &mut sc);
70                validate_modifs(block, data, ModifKinds::Character, vd);
71            },
72        );
73        vd.field_integer("base_external_slots");
74        vd.field_validated_block("domicile_building_slots", |block, data| {
75            let mut vd = Validator::new(block, data);
76            vd.unknown_block_fields(|key, block| {
77                validate_building_slot(key, block, data);
78            });
79        });
80        vd.multi_field_validated_block("domicile_asset", validate_domicile_asset);
81
82        vd.multi_field_validated("map_entity", validate_map_entity);
83    }
84}
85
86fn validate_building_slot(_key: &Token, block: &Block, data: &Everything) {
87    let mut vd = Validator::new(block, data);
88    vd.field_choice("slot_type", &["main", "external"]);
89    vd.field_block("position"); // TODO
90    vd.field_block("size"); // TODO
91    vd.multi_field_validated_block("empty_slot_asset", validate_slot_asset);
92    vd.multi_field_validated_block("construction_slot_asset", validate_slot_asset);
93}
94
95fn validate_slot_asset(block: &Block, data: &Everything) {
96    let mut vd = Validator::new(block, data);
97    vd.field_trigger_rooted("trigger", Tooltipped::No, Scopes::Domicile);
98    vd.field_item("icon", Item::File);
99    vd.field_item("texture", Item::File);
100    vd.field_item("intersectionmask_texture", Item::File);
101}
102
103fn validate_domicile_asset(block: &Block, data: &Everything) {
104    let mut vd = Validator::new(block, data);
105    vd.field_trigger_builder("trigger", Tooltipped::No, sc_domicile_owner);
106    vd.field_item("background", Item::File);
107    vd.field_item("foreground", Item::File);
108    vd.field_item("ambience", Item::Sound);
109}
110
111fn validate_map_entity(bv: &BV, data: &Everything) {
112    match bv {
113        BV::Value(value) => data.verify_exists(Item::Entity, value),
114        BV::Block(block) => {
115            let mut vd = Validator::new(block, data);
116            vd.field_trigger_builder("trigger", Tooltipped::No, sc_domicile_owner);
117            vd.field_item("reference", Item::Entity);
118        }
119    }
120}
121
122#[derive(Clone, Debug)]
123pub struct DomicileBuilding {}
124
125inventory::submit! {
126    ItemLoader::Normal(GameFlags::Ck3, Item::DomicileBuilding, DomicileBuilding::add)
127}
128
129impl DomicileBuilding {
130    pub fn add(db: &mut Db, key: Token, block: Block) {
131        db.add(Item::DomicileBuilding, key, block, Box::new(Self {}));
132    }
133}
134
135impl DbKind for DomicileBuilding {
136    fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
137        for parameters in block.get_field_blocks("parameters") {
138            for (key, _) in parameters.iter_assignments() {
139                db.add_flag(Item::DomicileParameter, key.clone());
140            }
141        }
142    }
143
144    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
145        // TODO: verify scope type
146        fn sc_ai_value(key: &Token) -> ScopeContext {
147            let mut sc = ScopeContext::new(Scopes::Domicile, key);
148            sc.define_name("owner", Scopes::Character, key);
149            sc
150        }
151
152        let mut vd = Validator::new(block, data);
153
154        let loca = format!("{key}_domicile_building_desc");
155        data.localization.suggest(&loca, key);
156
157        vd.field_trigger_rooted("can_construct", Tooltipped::Yes, Scopes::Character);
158        vd.field_trigger_rooted("can_construct_potential", Tooltipped::No, Scopes::Character);
159
160        for field in &["on_start", "on_canceled", "on_complete"] {
161            vd.field_effect_builder(field, Tooltipped::No, sc_domicile_owner);
162        }
163
164        vd.field_integer("construction_time");
165
166        vd.multi_field_validated_block("parameters", |block, data| {
167            let mut vd = Validator::new(block, data);
168            vd.unknown_value_fields(|key, value| {
169                let loca = format!("domicile_building_parameter_{key}");
170                data.verify_exists_implied(Item::Localization, &loca, key);
171                if !value.is("yes") {
172                    let msg = "only `yes` currently makes sense here";
173                    warn(ErrorKey::Validation).msg(msg).loc(value).push();
174                }
175            });
176        });
177
178        vd.field_choice("slot_type", &["main", "external", "internal"]);
179        vd.field_integer("internal_slots");
180        vd.field_list_items("allowed_domicile_types", Item::DomicileType);
181        vd.field_item("previous_building", Item::DomicileBuilding);
182
183        // TODO: verify scope type
184        let mut sc = ScopeContext::new(Scopes::Character, key);
185        vd.field_validated_block_sc("cost", &mut sc, validate_cost);
186        vd.field_validated_block_sc("refund", &mut sc, validate_cost);
187
188        vd.field_validated_block("character_modifier", |block, data| {
189            let vd = Validator::new(block, data);
190            validate_modifs(block, data, ModifKinds::Character, vd);
191        });
192
193        vd.field_validated_block("province_modifier", |block, data| {
194            let vd = Validator::new(block, data);
195            validate_modifs(block, data, ModifKinds::Province, vd);
196        });
197
198        vd.field_script_value_no_breakdown_builder("ai_value", sc_ai_value);
199
200        vd.multi_field_validated_block("asset", validate_building_asset);
201
202        // undocumented
203
204        vd.field_validated_block_sc("refund", &mut sc, validate_cost);
205    }
206}
207
208fn validate_building_asset(block: &Block, data: &Everything) {
209    let mut vd = Validator::new(block, data);
210    vd.field_trigger_rooted("trigger", Tooltipped::No, Scopes::Domicile);
211    vd.field_item("icon", Item::File);
212    vd.field_item("texture", Item::File);
213    vd.field_item("intersectionmask_texture", Item::File);
214    vd.field_item("soundeffect", Item::Sound);
215}
216
217fn sc_domicile_owner(key: &Token) -> ScopeContext {
218    let mut sc = ScopeContext::new(Scopes::Domicile, key);
219    sc.define_name("owner", Scopes::Character, key);
220    sc
221}