tiger_lib/ck3/data/
artifacts.rs

1use crate::block::{BV, Block};
2use crate::ck3::modif::ModifKinds;
3use crate::ck3::tables::misc::{ARTIFACT_RARITIES, SUPPORT_TYPES};
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, verify_modif_exists};
10use crate::report::{ErrorKey, Severity, warn};
11use crate::scopes::Scopes;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::validator::Validator;
15
16#[derive(Clone, Debug)]
17pub struct ArtifactSlot {}
18
19inventory::submit! {
20    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactSlot, ArtifactSlot::add)
21}
22
23impl ArtifactSlot {
24    pub fn add(db: &mut Db, key: Token, block: Block) {
25        db.add(Item::ArtifactSlot, key, block, Box::new(Self {}));
26    }
27}
28
29impl DbKind for ArtifactSlot {
30    fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
31        if let Some(slot_type) = block.get_field_value("type") {
32            db.add_flag(Item::ArtifactSlotType, slot_type.clone());
33        }
34    }
35
36    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
37        let mut vd = Validator::new(block, data);
38        data.verify_exists(Item::Localization, key);
39        vd.field_item("type", Item::ArtifactSlotType);
40        vd.field_choice("category", &["inventory", "court"]);
41        if let Some(category) = block.get_field_value("category") {
42            // TODO: this can probably be simplified
43            if category.is("inventory") {
44                let icon = vd.field_value("icon").unwrap_or(key);
45                data.verify_icon("NGameIcons|INVENTORY_SLOT_ICON_PATH", icon, ".dds");
46            } else if let Some(icon) = vd.field_value("icon") {
47                data.verify_icon("NGameIcons|INVENTORY_SLOT_ICON_PATH", icon, ".dds");
48            }
49        }
50    }
51}
52
53#[derive(Clone, Debug)]
54pub struct ArtifactType {}
55
56inventory::submit! {
57    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactType, ArtifactType::add)
58}
59
60impl ArtifactType {
61    pub fn add(db: &mut Db, key: Token, block: Block) {
62        db.add(Item::ArtifactType, key, block, Box::new(Self {}));
63    }
64}
65
66impl DbKind for ArtifactType {
67    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
68        let mut vd = Validator::new(block, data);
69        let loca = format!("artifact_{key}");
70        data.verify_exists_implied(Item::Localization, &loca, key);
71
72        vd.field_item("slot", Item::ArtifactSlotType);
73        vd.field_list_items("required_features", Item::ArtifactFeatureGroup);
74        vd.field_list_items("optional_features", Item::ArtifactFeatureGroup);
75        vd.field_bool("can_reforge");
76        vd.field_item("default_visuals", Item::ArtifactVisual);
77    }
78}
79
80#[derive(Clone, Debug)]
81pub struct ArtifactTemplate {}
82
83inventory::submit! {
84    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactTemplate, ArtifactTemplate::add)
85}
86
87impl ArtifactTemplate {
88    pub fn add(db: &mut Db, key: Token, block: Block) {
89        db.add(Item::ArtifactTemplate, key, block, Box::new(Self {}));
90    }
91}
92
93impl DbKind for ArtifactTemplate {
94    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
95        let mut vd = Validator::new(block, data);
96        let mut sc = ScopeContext::new(Scopes::Character, key);
97        sc.define_name("artifact", Scopes::Artifact, key);
98
99        vd.field_trigger("can_equip", Tooltipped::Yes, &mut sc);
100        vd.field_trigger("can_benefit", Tooltipped::Yes, &mut sc);
101        vd.field_trigger("can_reforge", Tooltipped::Yes, &mut sc);
102        vd.field_trigger("can_repair", Tooltipped::Yes, &mut sc);
103
104        vd.field_validated_block("fallback", |block, data| {
105            let vd = Validator::new(block, data);
106            validate_modifs(block, data, ModifKinds::Character, vd);
107        });
108
109        vd.field_script_value("ai_score", &mut sc);
110        vd.field_bool("unique");
111    }
112}
113
114#[derive(Clone, Debug)]
115pub struct ArtifactVisual {}
116
117inventory::submit! {
118    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactVisual, ArtifactVisual::add)
119}
120
121impl ArtifactVisual {
122    pub fn add(db: &mut Db, key: Token, block: Block) {
123        db.add(Item::ArtifactVisual, key, block, Box::new(Self {}));
124    }
125}
126
127impl DbKind for ArtifactVisual {
128    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
129        let mut vd = Validator::new(block, data);
130        let mut sc = ScopeContext::new(Scopes::Character, key);
131        sc.define_name("artifact", Scopes::Artifact, key);
132
133        vd.field_value("default_type"); // unused
134
135        // These two are undocumented
136        vd.field_choice("pedestal", SUPPORT_TYPES);
137        vd.field_choice("support_type", SUPPORT_TYPES);
138
139        let mut unconditional = false;
140        vd.multi_field_validated("icon", |bv, data| match bv {
141            BV::Value(icon) => {
142                unconditional = true;
143                data.verify_icon("NGameIcons|ARTIFACT_ICON_PATH", icon, "");
144            }
145            BV::Block(block) => {
146                let mut vd = Validator::new(block, data);
147                if !block.has_key("trigger") {
148                    unconditional = true;
149                }
150                vd.field_trigger("trigger", Tooltipped::No, &mut sc);
151                vd.field_icon("reference", "NGameIcons|ARTIFACT_ICON_PATH", "");
152            }
153        });
154        if !unconditional {
155            let msg = "needs one icon without a trigger";
156            warn(ErrorKey::Validation).msg(msg).loc(key).push();
157        }
158
159        unconditional = false;
160        vd.multi_field_validated("asset", |bv, data| match bv {
161            BV::Value(asset) => {
162                unconditional = true;
163                data.verify_exists(Item::Asset, asset);
164            }
165            BV::Block(block) => {
166                let mut vd = Validator::new(block, data);
167                if !block.has_key("trigger") {
168                    unconditional = true;
169                }
170                vd.field_trigger("trigger", Tooltipped::No, &mut sc);
171                vd.field_validated_value("reference", |_, mut vd| {
172                    vd.item(Item::Asset);
173                });
174            }
175        });
176        if !unconditional {
177            let msg = "needs at least one asset without a trigger";
178            warn(ErrorKey::Validation).msg(msg).loc(key).push();
179        }
180    }
181}
182
183#[derive(Clone, Debug)]
184pub struct ArtifactFeature {}
185
186inventory::submit! {
187    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactFeature, ArtifactFeature::add)
188}
189
190impl ArtifactFeature {
191    pub fn add(db: &mut Db, key: Token, block: Block) {
192        db.add(Item::ArtifactFeature, key, block, Box::new(Self {}));
193    }
194}
195
196impl DbKind for ArtifactFeature {
197    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
198        let mut vd = Validator::new(block, data);
199        // TODO: it's not clear what the scope is for these triggers
200        let mut sc = ScopeContext::new_unrooted(Scopes::Artifact | Scopes::Character, key);
201        sc.define_name("newly_created_artifact", Scopes::Artifact, key);
202        sc.define_name("owner", Scopes::Character, key);
203        sc.define_name("wealth", Scopes::Value, key);
204
205        let loca = format!("feature_{key}");
206        data.verify_exists_implied(Item::Localization, &loca, key);
207
208        vd.field_item("group", Item::ArtifactFeatureGroup);
209        vd.field_script_value("weight", &mut sc);
210
211        vd.field_trigger("trigger", Tooltipped::No, &mut sc);
212    }
213}
214
215#[derive(Clone, Debug)]
216pub struct ArtifactFeatureGroup {}
217
218inventory::submit! {
219    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactFeatureGroup, ArtifactFeatureGroup::add)
220}
221
222impl ArtifactFeatureGroup {
223    pub fn add(db: &mut Db, key: Token, block: Block) {
224        db.add(Item::ArtifactFeatureGroup, key, block, Box::new(Self {}));
225    }
226}
227
228impl DbKind for ArtifactFeatureGroup {
229    fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
230        let mut _vd = Validator::new(block, data);
231    }
232}
233
234#[derive(Clone, Debug)]
235pub struct ArtifactBlueprint {}
236
237inventory::submit! {
238    ItemLoader::Normal(GameFlags::Ck3, Item::ArtifactBlueprint, ArtifactBlueprint::add)
239}
240
241impl ArtifactBlueprint {
242    pub fn add(db: &mut Db, key: Token, block: Block) {
243        db.add(Item::ArtifactBlueprint, key, block, Box::new(Self {}));
244    }
245}
246
247impl DbKind for ArtifactBlueprint {
248    fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
249        let mut vd = Validator::new(block, data);
250
251        vd.req_field("in_type");
252        vd.req_field("in_visuals");
253        vd.field_item("in_type", Item::ArtifactType);
254        vd.field_item("in_visuals", Item::ArtifactVisual);
255
256        vd.req_field("out_type");
257        vd.req_field("out_visuals");
258        vd.field_item("out_type", Item::ArtifactType);
259        vd.field_item("out_visuals", Item::ArtifactVisual);
260
261        vd.field_validated_list("disallowed_modifiers", |token, data| {
262            verify_modif_exists(token, data, ModifKinds::Character, Severity::Warning);
263        });
264        vd.field_validated_block("replacement_modifiers", |block, data| {
265            let mut vd = Validator::new(block, data);
266            for field in ARTIFACT_RARITIES {
267                vd.field_validated_list(field, |token, data| {
268                    data.verify_exists(Item::Modifier, token);
269                    // Verify that all the modifs in this modifier are artifact-compatible.
270                    data.database.validate_property_use(
271                        Item::Modifier,
272                        token,
273                        data,
274                        token,
275                        "artifact_modifier",
276                    );
277                });
278            }
279        });
280
281        vd.field_item("template", Item::ArtifactTemplate);
282    }
283}