tiger_lib/ck3/data/
innovations.rs

1use crate::block::Block;
2use crate::ck3::modif::ModifKinds;
3use crate::ck3::validate::validate_maa_stats;
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, err, warn};
11use crate::scopes::Scopes;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::validator::{Validator, ValueValidator};
15
16#[derive(Clone, Debug)]
17pub struct Innovation {}
18
19inventory::submit! {
20    ItemLoader::Normal(GameFlags::Ck3, Item::Innovation, Innovation::add)
21}
22
23impl Innovation {
24    pub fn add(db: &mut Db, key: Token, block: Block) {
25        db.add(Item::Innovation, key, block, Box::new(Self {}));
26    }
27}
28
29impl DbKind for Innovation {
30    fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
31        for token in block.get_field_values("flag") {
32            db.add_flag(Item::InnovationFlag, token.clone());
33        }
34        if let Some(block) = block.get_field_block("parameters") {
35            for (key, _) in block.iter_assignments() {
36                db.add_flag(Item::InnovationParameter, key.clone());
37                db.add_flag(Item::CultureParameter, key.clone());
38            }
39        }
40    }
41
42    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
43        let mut vd = Validator::new(block, data);
44        let mut sc = ScopeContext::new(Scopes::Culture, key);
45
46        data.verify_exists(Item::Localization, key);
47        data.localization.suggest(&format!("{key}_desc"), key);
48
49        vd.field_item("culture_era", Item::CultureEra);
50        vd.field_choice(
51            "group",
52            &["culture_group_military", "culture_group_civic", "culture_group_regional"],
53        );
54        vd.field_item("icon", Item::File);
55        vd.field_item("skill", Item::Skill);
56
57        vd.field_script_value_no_breakdown_rooted("ai_weight_for_spread", Scopes::Character);
58        vd.field_script_value_no_breakdown_builder("ai_weight_for_fascination", |key| {
59            let mut sc = ScopeContext::new(Scopes::Culture, key);
60            sc.define_name("character", Scopes::Character, key);
61            sc
62        });
63
64        vd.multi_field_validated_key_block("asset", |key, block, data| {
65            let mut vd = Validator::new(block, data);
66            vd.field_trigger_rooted("trigger", Tooltipped::No, Scopes::Culture);
67            if !block.has_key("name") && !block.has_key("icon") {
68                let msg = "asset must have `name` or `icon";
69                warn(ErrorKey::FieldMissing).msg(msg).loc(key).push();
70            }
71            vd.field_item("name", Item::Localization);
72            vd.field_item("icon", Item::File);
73        });
74
75        vd.field_item("region", Item::Region);
76        vd.field_trigger("potential", Tooltipped::No, &mut sc);
77        vd.field_trigger("can_progress", Tooltipped::Yes, &mut sc);
78
79        // TODO: everything after this duplicates CultureEra validation,
80        // except the `type` field in `maa_upgrade`
81        vd.field_validated_block("character_modifier", |block, data| {
82            let vd = Validator::new(block, data);
83            validate_modifs(block, data, ModifKinds::Character, vd);
84        });
85        vd.field_validated_block("culture_modifier", |block, data| {
86            let vd = Validator::new(block, data);
87            validate_modifs(block, data, ModifKinds::Culture, vd);
88        });
89        vd.field_validated_block("county_modifier", |block, data| {
90            let vd = Validator::new(block, data);
91            validate_modifs(block, data, ModifKinds::County, vd);
92        });
93        vd.field_validated_block("province_modifier", |block, data| {
94            let vd = Validator::new(block, data);
95            validate_modifs(block, data, ModifKinds::Province, vd);
96        });
97
98        vd.field_validated_block("parameters", |block, data| {
99            let mut vd = Validator::new(block, data);
100            vd.unknown_value_fields(|key, value| {
101                // only bool parameters supported here
102                ValueValidator::new(value, data).bool();
103                // culture parameter loca are lowercased, verified in 1.11
104                let loca = format!("culture_parameter_{}", key.as_str().to_ascii_lowercase());
105                data.verify_exists_implied(Item::Localization, &loca, key);
106            });
107        });
108        vd.multi_field_value("flag");
109
110        vd.multi_field_item("unlock_building", Item::Building);
111        vd.multi_field_item("unlock_decision", Item::Decision);
112        vd.multi_field_item("unlock_casus_belli", Item::CasusBelli);
113        vd.multi_field_item("unlock_maa", Item::MenAtArms);
114        vd.multi_field_item("unlock_law", Item::Law);
115
116        vd.multi_field_item("custom", Item::Localization);
117
118        vd.multi_field_validated_block("maa_upgrade", |block, data| {
119            let mut vd = Validator::new(block, data);
120            if let Some(token) = vd.field_value("type") {
121                if !data.item_exists(Item::MenAtArms, token.as_str())
122                    && !data.item_exists(Item::MenAtArmsBase, token.as_str())
123                {
124                    let msg = format!("{token} is not a men-at-arms type or base type");
125                    err(ErrorKey::MissingItem).msg(msg).loc(token).push();
126                }
127            }
128            validate_maa_stats(&mut vd);
129        });
130    }
131}