tiger_lib/data/
customloca.rs

1use crate::block::Block;
2use crate::context::ScopeContext;
3use crate::data::localization::Language;
4use crate::db::{Db, DbKind};
5use crate::everything::Everything;
6use crate::game::GameFlags;
7use crate::item::{Item, ItemLoader};
8use crate::report::{ErrorKey, warn};
9use crate::scopes::Scopes;
10use crate::token::Token;
11use crate::tooltipped::Tooltipped;
12use crate::validate::validate_modifiers_with_base;
13use crate::validator::Validator;
14
15#[cfg(feature = "eu5")]
16const INVALID_LOC_HANDLING: &[&str] = &["return_empty", "fallback_to_next_entry", "return_loc_key"];
17
18#[derive(Clone, Debug)]
19pub struct CustomLocalization {}
20
21inventory::submit! {
22    ItemLoader::Normal(GameFlags::jomini(), Item::CustomLocalization, CustomLocalization::add)
23}
24
25impl CustomLocalization {
26    pub fn add(db: &mut Db, key: Token, block: Block) {
27        db.add(Item::CustomLocalization, key, block, Box::new(Self {}));
28    }
29}
30
31impl DbKind for CustomLocalization {
32    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
33        let mut vd = Validator::new(block, data);
34
35        let mut sc;
36        if let Some(token) = vd.field_value("type") {
37            if token.is("all") {
38                sc = ScopeContext::new(Scopes::all(), token);
39            } else if let Some(scopes) = Scopes::from_snake_case(token.as_str()) {
40                sc = ScopeContext::new(scopes, token);
41            } else {
42                warn(ErrorKey::Scopes).msg("unknown scope type").loc(token).push();
43                sc = ScopeContext::new(Scopes::all(), token);
44            }
45        } else {
46            sc = ScopeContext::new(Scopes::all(), key);
47        }
48        // TODO: Scopes depend on the scopes available in the loca where Custom or Custom2 is called.
49        sc.set_strict_scopes(false);
50        vd.field_bool("log_loc_errors");
51        vd.field_bool("random_valid");
52
53        #[cfg(feature = "eu5")]
54        vd.field_choice("if_invalid_loc", INVALID_LOC_HANDLING);
55
56        if block.has_key("parent") {
57            vd.field_item("parent", Item::CustomLocalization);
58            vd.req_field("suffix");
59            vd.field_value("suffix");
60            // Actual loca existence is checked in validate_custom_call
61            return;
62        }
63        vd.req_field("type");
64
65        vd.multi_field_validated_block("text", |block, data| {
66            let mut vd = Validator::new(block, data);
67            vd.field_effect("setup_scope", Tooltipped::No, &mut sc);
68            vd.field_trigger("trigger", Tooltipped::No, &mut sc);
69            vd.field_validated_block_sc("weight_multiplier", &mut sc, validate_modifiers_with_base);
70
71            vd.req_field("localization_key");
72            // Actual loca existence is checked in validate_custom_call
73            vd.field_value("localization_key");
74            vd.field_bool("fallback");
75        });
76    }
77}
78
79impl CustomLocalization {
80    #[allow(clippy::too_many_arguments)] // nothing can be cut here
81    pub fn validate_custom_call(
82        key: &Token,
83        block: &Block,
84        data: &Everything,
85        caller: &Token,
86        scopes: Scopes,
87        lang: Option<Language>,
88        suffix_str: &str,
89        suffix_token: Option<&Token>,
90    ) {
91        if let Some(token) = block.get_field_value("type") {
92            if let Some(this_scopes) = Scopes::from_snake_case(token.as_str()) {
93                if !scopes.contains(this_scopes) {
94                    let msg = format!(
95                        "custom localization {key} is for {this_scopes} but context is {scopes}"
96                    );
97                    warn(ErrorKey::Scopes).msg(msg).loc(caller).push();
98                }
99            }
100        }
101
102        if let Some(parent) = block.get_field_value("parent") {
103            if let Some(suffix) = block.get_field_value("suffix") {
104                if let Some((key, block)) =
105                    data.get_key_block(Item::CustomLocalization, parent.as_str())
106                {
107                    let suffix_str = format!("{suffix_str}{suffix}");
108                    let suffix_token =
109                        if suffix_token.is_some() { suffix_token } else { Some(suffix) };
110                    Self::validate_custom_call(
111                        key,
112                        block,
113                        data,
114                        caller,
115                        scopes,
116                        lang,
117                        &suffix_str,
118                        suffix_token,
119                    );
120                }
121            }
122        } else {
123            for block in block.get_field_blocks("text") {
124                if let Some(key) = block.get_field_value("localization_key") {
125                    if let Some(token) = suffix_token {
126                        let loca = format!("{key}{suffix_str}");
127                        data.localization.verify_exists_implied_lang(&loca, token, lang);
128                    } else {
129                        data.localization.verify_exists_lang(key, lang);
130                    }
131                }
132            }
133        }
134    }
135}