Skip to main content

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            && let Some(this_scopes) = Scopes::from_snake_case(token.as_str())
93            && !scopes.contains(this_scopes)
94        {
95            let msg =
96                format!("custom localization {key} is for {this_scopes} but context is {scopes}");
97            warn(ErrorKey::Scopes).msg(msg).loc(caller).push();
98        }
99
100        if let Some(parent) = block.get_field_value("parent") {
101            if let Some(suffix) = block.get_field_value("suffix")
102                && let Some((key, block)) =
103                    data.get_key_block(Item::CustomLocalization, parent.as_str())
104            {
105                let suffix_str = format!("{suffix_str}{suffix}");
106                let suffix_token = if suffix_token.is_some() { suffix_token } else { Some(suffix) };
107                Self::validate_custom_call(
108                    key,
109                    block,
110                    data,
111                    caller,
112                    scopes,
113                    lang,
114                    &suffix_str,
115                    suffix_token,
116                );
117            }
118        } else {
119            for block in block.get_field_blocks("text") {
120                if let Some(key) = block.get_field_value("localization_key") {
121                    if let Some(token) = suffix_token {
122                        let loca = format!("{key}{suffix_str}");
123                        data.localization.verify_exists_implied_lang(&loca, token, lang);
124                    } else {
125                        data.localization.verify_exists_lang(key, lang);
126                    }
127                }
128            }
129        }
130    }
131}