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"); vd.field_block("size"); 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 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 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 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}