tiger_lib/ck3/data/
gameconcepts.rs1use std::path::PathBuf;
2
3use crate::block::Block;
4use crate::everything::Everything;
5use crate::fileset::{FileEntry, FileHandler};
6use crate::helpers::{TigerHashMap, dup_error};
7use crate::item::Item;
8use crate::parse::ParserMemory;
9use crate::pdxfile::PdxFile;
10use crate::report::{ErrorKey, warn};
11use crate::token::Token;
12use crate::validator::Validator;
13
14#[derive(Clone, Debug, Default)]
15pub struct GameConcepts {
16 concepts: TigerHashMap<&'static str, Concept>,
17 aliases: TigerHashMap<&'static str, &'static str>,
18}
19
20impl GameConcepts {
21 pub fn load_item(&mut self, key: Token, block: Block) {
22 if let Some(other) = self.concepts.get(key.as_str()) {
23 if other.key.loc.kind >= key.loc.kind {
24 dup_error(&key, &other.key, "game concept");
25 }
26 }
27 self.concepts.insert(key.as_str(), Concept::new(key, block));
28 }
29
30 pub fn exists(&self, key: &str) -> bool {
31 self.concepts.contains_key(key) || self.aliases.contains_key(key)
32 }
33
34 pub fn iter_keys(&self) -> impl Iterator<Item = &Token> {
35 self.concepts.values().map(|item| &item.key)
36 }
37
38 pub fn validate(&self, data: &Everything) {
39 for item in self.concepts.values() {
40 item.validate(data);
41 }
42 }
43}
44
45impl FileHandler<Block> for GameConcepts {
46 fn subpath(&self) -> PathBuf {
47 PathBuf::from("common/game_concepts")
48 }
49
50 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
51 if !entry.filename().to_string_lossy().ends_with(".txt") {
52 return None;
53 }
54
55 PdxFile::read(entry, parser)
56 }
57
58 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
59 for (key, block) in block.drain_definitions_warn() {
60 self.load_item(key, block);
61 }
62 }
63
64 fn finalize(&mut self) {
65 for (key, concept) in &self.concepts {
66 for token in concept.block.get_multi_field_list("alias") {
67 self.aliases.insert(token.as_str(), key);
68 }
69 }
70 }
71}
72
73#[derive(Clone, Debug)]
74pub struct Concept {
75 key: Token,
76 block: Block,
77}
78
79impl Concept {
80 pub fn new(key: Token, block: Block) -> Self {
81 Self { key, block }
82 }
83
84 pub fn validate(&self, data: &Everything) {
85 fn validate_framesize(block: &Block, data: &Everything) {
86 let mut vd = Validator::new(block, data);
87 vd.req_tokens_integers_exactly(2);
88 }
89
90 let loca = format!("game_concept_{}", self.key);
91 data.verify_exists_implied(Item::Localization, &loca, &self.key);
92 let loca = format!("game_concept_{}_desc", self.key);
93 data.verify_exists_implied(Item::Localization, &loca, &self.key);
94
95 let mut vd = Validator::new(&self.block, data);
96 vd.multi_field_validated_list("alias", |alias, data| {
97 let loca = format!("game_concept_{alias}");
98 data.verify_exists_implied(Item::Localization, &loca, alias);
99 });
100
101 vd.field_item("parent", Item::GameConcept);
102 if let Some(token) = vd.field_value("texture") {
103 if !token.is("piety") {
105 data.fileset.verify_exists(token);
106 }
107 }
108 if let Some(texture) = self.block.get_field_value("texture") {
109 vd.field_validated_block("framesize", validate_framesize);
110 vd.field_integer("frame");
111 if self.block.has_key("framesize") != self.block.has_key("frame") {
112 let msg = "`framesize` and `frame` should be specified together";
113 warn(ErrorKey::Validation).msg(msg).loc(&self.key).push();
114 }
115 if let Some(frame) = self.block.get_field_integer("frame") {
116 if let Some(b) = self.block.get_field_block("framesize") {
117 let tokens: Vec<&Token> = b.iter_values().collect();
118 if tokens.len() == 2 {
119 if let Ok(width) = tokens[0].as_str().parse::<u32>() {
120 if let Ok(height) = tokens[1].as_str().parse::<u32>() {
121 #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)]
123 data.dds.validate_frame(texture, width, height, frame as u32);
124 }
125 }
126 }
127 }
128 }
129 } else {
130 vd.advice_field("framesize", "not needed without texture");
131 vd.advice_field("frame", "not needed without texture");
132 }
133 vd.field_item("requires_dlc_flag", Item::DlcFeature);
134 vd.field_bool("shown_in_encyclopedia");
135 }
136}