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 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"); 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 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 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}