1use crate::block::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::Severity;
11use crate::scopes::Scopes;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::validator::{Validator, ValueValidator};
15
16#[derive(Clone, Debug)]
17pub struct House {}
18
19inventory::submit! {
20 ItemLoader::Normal(GameFlags::Ck3, Item::House, House::add)
21}
22
23impl House {
24 pub fn add(db: &mut Db, key: Token, block: Block) {
25 db.add(Item::House, key, block, Box::new(Self {}));
26 }
27
28 pub fn get_dynasty<'a>(key: &str, data: &'a Everything) -> Option<&'a Token> {
29 data.database
30 .get_key_block(Item::House, key)
31 .and_then(|(_, block)| block.get_field_value("dynasty"))
32 }
33}
34
35impl DbKind for House {
36 fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
37 let mut vd = Validator::new(block, data);
38
39 vd.req_field("name");
40 vd.req_field("dynasty");
41
42 vd.field_item("name", Item::Localization);
43 vd.field_item("prefix", Item::Localization);
44 vd.field_item("motto", Item::Localization);
45 vd.field_item("dynasty", Item::Dynasty);
46 vd.field_value("forced_coa_religiongroup"); }
48}
49
50#[derive(Clone, Debug)]
51pub struct HouseAspiration {}
52
53inventory::submit! {
54 ItemLoader::Normal(GameFlags::Ck3, Item::HouseAspiration, HouseAspiration::add)
55}
56
57impl HouseAspiration {
58 pub fn add(db: &mut Db, key: Token, block: Block) {
59 db.add(Item::HouseAspiration, key, block, Box::new(Self {}));
60 }
61}
62
63impl DbKind for HouseAspiration {
64 fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
65 for block in block.get_field_blocks("level") {
66 if let Some(block) = block.get_field_block("parameters") {
67 for (key, value) in block.iter_assignments() {
68 if value.lowercase_is("yes") || value.lowercase_is("no") {
69 db.add_flag(Item::BooleanHousePowerParameter, key.clone());
70 }
71 }
72 }
73 if let Some(block) = block.get_field_block("house_head_parameters") {
74 for (key, value) in block.iter_assignments() {
75 if value.lowercase_is("yes") || value.lowercase_is("no") {
76 db.add_flag(Item::BooleanHouseHeadParameter, key.clone());
77 }
78 }
79 }
80 }
81 }
82
83 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
84 let mut vd = Validator::new(block, data);
85
86 let loca = format!("{key}_house_power");
87 data.verify_exists_implied(Item::Localization, &loca, key);
88
89 data.verify_icon("NGameIcons|HOUSE_POWER_BONUS_ICON_PATH", key, ".dds");
90
91 vd.field_bool("show_in_main_hud");
92 vd.field_trigger_rooted("is_shown", Tooltipped::No, Scopes::DynastyHouse);
93
94 vd.field_bool("is_default");
95
96 vd.req_field("level");
97 vd.multi_field_validated_block("level", |block, data| {
98 let mut vd = Validator::new(block, data);
99 vd.field_validated_key_block("cost", |key, block, data| {
100 let mut sc = ScopeContext::new(Scopes::Character, key);
101 validate_cost(block, data, &mut sc);
102 });
103 for field in &[
104 "powerful_family_top_liege_modifier",
105 "powerful_family_member_modifier",
106 "any_house_member_modifier",
107 "house_head_modifier",
108 ] {
109 vd.field_validated_block(field, |block, data| {
110 let vd = Validator::new(block, data);
111 validate_modifs(block, data, ModifKinds::Character, vd);
112 });
113 }
114
115 vd.field_script_value_no_breakdown_rooted("ai_score", Scopes::Character);
116 for field in &["parameters", "house_head_parameters"] {
117 vd.field_validated_block(field, |block, data| {
118 let mut vd = Validator::new(block, data);
119 vd.unknown_value_fields(|key, value| {
120 let loca = format!("house_power_parameter_{key}");
121 data.verify_exists_implied(Item::Localization, &loca, key);
122
123 let mut vvd = ValueValidator::new(value, data);
124 vvd.bool();
125 });
126 });
127 }
128
129 vd.field_bool("can_request_great_project_contributions_from_allies");
130 vd.field_trigger_rooted("can_upgrade", Tooltipped::Yes, Scopes::Character);
131 });
132
133 vd.field_item("illustration", Item::File);
134
135 vd.field_validated_block("cooldown", |block, data| {
137 let mut vd = Validator::new(block, data);
138 vd.field_integer("days");
139 vd.field_integer("weeks");
140 vd.field_integer("months");
141 vd.field_integer("years");
142 });
143
144 for field in &["on_changed", "on_upgraded"] {
145 vd.field_effect_builder(field, Tooltipped::Yes, |key| {
146 let mut sc = ScopeContext::new(Scopes::Character, key);
147 sc.define_name("house", Scopes::DynastyHouse, key);
148 sc
149 });
150 }
151
152 vd.field_item("confederation_type", Item::ConfederationType);
155 }
156}
157
158#[derive(Clone, Debug)]
159pub struct HouseRelationType {}
160
161inventory::submit! {
162 ItemLoader::Normal(GameFlags::Ck3, Item::HouseRelationType, HouseRelationType::add)
163}
164
165impl HouseRelationType {
166 pub fn add(db: &mut Db, key: Token, block: Block) {
167 db.add(Item::HouseRelationType, key, block, Box::new(Self {}));
168 }
169}
170
171impl DbKind for HouseRelationType {
172 fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
173 if let Some(block) = block.get_field_block("levels") {
174 for (key, block) in block.iter_definitions() {
175 db.add_flag(Item::HouseRelationLevel, key.clone());
176 if let Some(block) = block.get_field_block("parameters") {
177 for value in block.iter_values() {
178 db.add_flag(Item::BooleanHouseRelationParameter, value.clone());
179 }
180 }
181 }
182 }
183 }
184
185 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
186 data.verify_exists(Item::Localization, key);
187
188 let mut vd = Validator::new(block, data);
189 vd.field_item("neutral_level", Item::HouseRelationLevel);
191
192 vd.req_field("levels");
193 vd.field_validated_block("levels", |block, data| {
194 let mut vd = Validator::new(block, data);
195 vd.unknown_block_fields(|k, block| {
196 let loca = format!("{key}_level_{k}");
197 data.verify_exists_implied(Item::Localization, &loca, key);
198 let loca = format!("{key}_level_{k}_desc");
199 data.verify_exists_implied(Item::Localization, &loca, key);
200 if let Some(icon_path) = data.get_defined_string_warn(
201 k,
202 "NGameIcons|HOUSE_RELATION_LEVEL_RENDERED_ICON_PATH",
203 ) {
204 let pathname = format!("{icon_path}/{key}_level_{k}_rendered_icon.dds");
205 data.verify_exists_implied_max_sev(Item::File, &pathname, k, Severity::Warning);
206 }
207 if let Some(icon_path) = data
208 .get_defined_string_warn(k, "NGameIcons|HOUSE_RELATION_LEVEL_FLAT_ICON_PATH")
209 {
210 let pathname = format!("{icon_path}/{key}_level_{k}_flat_icon.dds");
211 data.verify_exists_implied_max_sev(Item::File, &pathname, k, Severity::Warning);
212 }
213
214 let mut vd = Validator::new(block, data);
215 vd.field_integer("opinion");
216 vd.field_integer("cohesion_contribution");
217 vd.field_validated_list("parameters", |value, data| {
218 let loca = format!("house_relation_parameter_{value}");
219 data.verify_exists_implied(Item::Localization, &loca, value);
220 });
221 });
222 });
223 }
224}