Skip to main content

tiger_lib/
item.rs

1//! Giant enum for all the [`Item`] types in the game.
2
3pub use tiger_tables::item::Item;
4
5use crate::block::Block;
6use crate::db::Db;
7#[cfg(feature = "eu5")]
8use crate::eu5::item::injectable_eu5;
9#[cfg(doc)]
10use crate::everything::Everything;
11use crate::game::{Game, GameFlags};
12use crate::pdxfile::PdxEncoding;
13use crate::report::{Confidence, Severity};
14use crate::token::Token;
15#[cfg(feature = "vic3")]
16use crate::vic3::item::injectable_vic3;
17
18pub trait ItemExt {
19    fn confidence(self) -> Confidence;
20    fn severity(self) -> Severity;
21    #[cfg(any(feature = "vic3", feature = "eu5"))]
22    fn injectable(self) -> bool;
23}
24
25impl ItemExt for Item {
26    /// Confidence value to use when reporting that an item is missing.
27    /// Should be `Strong` for most, `Weak` for items that aren't defined anywhere but just used (such as gfx flags).
28    fn confidence(self) -> Confidence {
29        match self {
30            #[cfg(feature = "jomini")]
31            Item::AccessoryTag => Confidence::Weak,
32
33            // GuiType and GuiTemplate are here because referring to templates in other mods is a
34            // common compatibility technique.
35            Item::GuiType | Item::GuiTemplate | Item::Sound => Confidence::Weak,
36
37            #[cfg(feature = "ck3")]
38            Item::AccoladeCategory
39            | Item::BuildingGfx
40            | Item::ClothingGfx
41            | Item::CoaGfx
42            | Item::CultureParameter
43            | Item::MemoryCategory
44            | Item::UnitGfx => Confidence::Weak,
45
46            #[cfg(feature = "ck3")]
47            Item::SpecialBuilding => Confidence::Reasonable,
48
49            _ => Confidence::Strong,
50        }
51    }
52
53    /// Severity value to use when reporting that an item is missing.
54    /// * `Error` - most things
55    /// * `Warning` - things that only impact visuals or presentation
56    /// * `Untidy` - things that don't matter much at all
57    /// * `Fatal` - things that cause crashes if they're missing
58    ///
59    /// This is only one piece of the severity puzzle. It can also depend on the caller who's expecting the item to exist.
60    /// That part isn't handled yet.
61    fn severity(self) -> Severity {
62        match self {
63            // GuiType and GuiTemplate are here because referring to templates in other mods is a
64            // common compatibility technique.
65            Item::GuiType | Item::GuiTemplate => Severity::Untidy,
66
67            Item::File | Item::Localization | Item::MapEnvironment => Severity::Warning,
68
69            #[cfg(feature = "jomini")]
70            Item::Accessory
71            | Item::AccessoryTag
72            | Item::AccessoryVariation
73            | Item::AccessoryVariationLayout
74            | Item::AccessoryVariationTextures
75            | Item::Coa
76            | Item::CoaColorList
77            | Item::CoaColoredEmblemList
78            | Item::CoaPatternList
79            | Item::CoaTemplate
80            | Item::CoaTemplateList
81            | Item::CoaTexturedEmblemList
82            | Item::CustomLocalization
83            | Item::EffectLocalization
84            | Item::Ethnicity
85            | Item::GameConcept
86            | Item::NamedColor
87            | Item::PortraitAnimation
88            | Item::PortraitCamera
89            | Item::PortraitEnvironment
90            | Item::Sound
91            | Item::TextFormat
92            | Item::TextIcon
93            | Item::TextureFile
94            | Item::TriggerLocalization => Severity::Warning,
95
96            #[cfg(feature = "ck3")]
97            Item::AccoladeIcon
98            | Item::ArtifactVisual
99            | Item::BuildingGfx
100            | Item::ClothingGfx
101            | Item::CoaDynamicDefinition
102            | Item::CoaGfx
103            | Item::CultureAesthetic
104            | Item::CultureCreationName
105            | Item::EventBackground
106            | Item::EventTheme
107            | Item::EventTransition
108            | Item::Flavorization
109            | Item::GraphicalFaith
110            | Item::ModifierFormat
111            | Item::MottoInsert
112            | Item::Motto
113            | Item::Music
114            | Item::Nickname
115            | Item::RulerObjectiveType
116            | Item::ScriptedIllustration
117            | Item::UnitGfx => Severity::Warning,
118
119            #[cfg(feature = "vic3")]
120            Item::MapLayer
121            | Item::ModifierTypeDefinition
122            | Item::TerrainManipulator
123            | Item::TerrainMask
124            | Item::TerrainMaterial => Severity::Warning,
125
126            #[cfg(feature = "hoi4")]
127            Item::Sprite => Severity::Warning,
128
129            _ => Severity::Error,
130        }
131    }
132
133    #[cfg(any(feature = "vic3", feature = "eu5"))]
134    fn injectable(self) -> bool {
135        match Game::game() {
136            #[cfg(feature = "vic3")]
137            Game::Vic3 => injectable_vic3(self),
138            #[cfg(feature = "eu5")]
139            Game::Eu5 => injectable_eu5(self),
140        }
141    }
142}
143
144/// The callback type for adding one item instance to the database.
145pub(crate) type ItemAdder = fn(&mut Db, Token, Block);
146
147/// The specification for loading an [`Item`] type into the [`Db`].
148///
149/// An instance of this can be placed in every `data` module using the `inventory::submit!` macro.
150/// This will register the loader so that the [`Everything`] object can load all defined items.
151// Note that this is an enum so that users can more conveniently construct it. It used to be a
152// struct with various constructor functions, but that didn't work because the ItemAdder type has a
153// &mut in it, and that wasn't allowed in const functions even though the function pointer itself
154// is const. See https://github.com/rust-lang/rust/issues/57349 for details.
155// TODO: once that issue stabilizes, we can revisit the ItemLoader type.
156pub(crate) enum ItemLoader {
157    /// A convenience variant for loaders that are the most common type.
158    ///
159    /// * [`GameFlags`] is which games this item loader is for.
160    /// * [`Item`] is the item type being loaded.
161    ///
162    /// The [`ItemAdder`] function does not have to load exclusively this type of item.
163    /// Related items are ok. The main use of the [`Item`] field is to get the path for this item
164    /// type, so that files are loaded from that folder.
165    ///
166    /// `Normal` loaders have extension `.txt`, `LoadAsFile::No`, and `Recursive::Maybe`. They default
167    /// to a [`PdxEncoding`] appropriate to the game being validated.
168    Normal(GameFlags, Item, ItemAdder),
169    /// A variant that allows the full range of item loader behvavior.
170    /// * [`PdxEncoding`] indicates whether to expect utf-8 and/or a BOM in the files.
171    /// * The `&'static str` is the file extension to look for (including the dot).
172    /// * [`LoadAsFile`] is whether to load the whole file as one item, or treat it as normal with a
173    ///   series of items in one file.
174    /// * [`Recursive`] indicates whether to load subfolders of the item's main folder.
175    ///   `Recursive::Maybe` means apply game-dependent logic.
176    Full(GameFlags, Item, PdxEncoding, &'static str, LoadAsFile, Recursive, ItemAdder),
177}
178
179inventory::collect!(ItemLoader);
180
181impl ItemLoader {
182    pub fn for_game(&self, game: Game) -> bool {
183        let game_flags = match self {
184            ItemLoader::Normal(game_flags, _, _)
185            | ItemLoader::Full(game_flags, _, _, _, _, _, _) => game_flags,
186        };
187        game_flags.contains(GameFlags::from(game))
188    }
189
190    pub fn itype(&self) -> Item {
191        match self {
192            ItemLoader::Normal(_, itype, _) | ItemLoader::Full(_, itype, _, _, _, _, _) => *itype,
193        }
194    }
195
196    pub fn encoding(&self) -> PdxEncoding {
197        match self {
198            ItemLoader::Normal(_, _, _) => {
199                #[cfg(feature = "hoi4")]
200                if Game::is_hoi4() {
201                    return PdxEncoding::Utf8NoBom;
202                }
203                PdxEncoding::Utf8Bom
204            }
205            ItemLoader::Full(_, _, encoding, _, _, _, _) => *encoding,
206        }
207    }
208
209    pub fn extension(&self) -> &'static str {
210        match self {
211            ItemLoader::Normal(_, _, _) => ".txt",
212            ItemLoader::Full(_, _, _, extension, _, _, _) => extension,
213        }
214    }
215
216    pub fn whole_file(&self) -> bool {
217        match self {
218            ItemLoader::Normal(_, _, _) => false,
219            ItemLoader::Full(_, _, _, _, load_as_file, _, _) => {
220                matches!(load_as_file, LoadAsFile::Yes)
221            }
222        }
223    }
224
225    pub fn recursive(&self) -> bool {
226        match self {
227            ItemLoader::Normal(_, _, _) => {
228                Game::is_ck3() && self.itype().path().starts_with("common/")
229            }
230            ItemLoader::Full(_, _, _, _, _, recursive, _) => match recursive {
231                Recursive::Yes => true,
232                Recursive::No => false,
233                Recursive::Maybe => Game::is_ck3() && self.itype().path().starts_with("common/"),
234            },
235        }
236    }
237
238    pub fn adder(&self) -> ItemAdder {
239        match self {
240            ItemLoader::Normal(_, _, adder) | ItemLoader::Full(_, _, _, _, _, _, adder) => *adder,
241        }
242    }
243}
244
245pub enum LoadAsFile {
246    Yes,
247    No,
248}
249
250pub enum Recursive {
251    Yes,
252    No,
253    #[allow(dead_code)]
254    Maybe,
255}