tiger_lib/ck3/data/
gamerules.rs

1use crate::block::Block;
2use crate::db::{Db, DbKind};
3use crate::everything::Everything;
4use crate::game::GameFlags;
5use crate::item::{Item, ItemLoader};
6use crate::report::{ErrorKey, err};
7use crate::token::Token;
8use crate::validator::Validator;
9
10#[derive(Clone, Debug)]
11pub struct GameRule {}
12
13inventory::submit! {
14    ItemLoader::Normal(GameFlags::Ck3, Item::GameRule, GameRule::add)
15}
16
17impl GameRule {
18    pub fn add(db: &mut Db, key: Token, block: Block) {
19        db.add(Item::GameRule, key, block, Box::new(Self {}));
20    }
21}
22
23impl DbKind for GameRule {
24    fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
25        for (key, _) in block.iter_definitions() {
26            if !key.is("categories") {
27                db.add_flag(Item::GameRuleSetting, key.clone());
28            }
29        }
30    }
31
32    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
33        let mut vd = Validator::new(block, data);
34
35        let loca = format!("rule_{key}");
36        data.verify_exists_implied(Item::Localization, &loca, key);
37
38        vd.field_validated_block("categories", |block, data| {
39            let mut vd = Validator::new(block, data);
40            for token in vd.values() {
41                let loca = format!("game_rule_category_{token}");
42                data.verify_exists_implied(Item::Localization, &loca, token);
43            }
44        });
45
46        if let Some(token) = vd.field_value("default") {
47            if token.is("categories") || block.get_field_block(token.as_str()).is_none() {
48                let msg = "this rule does not have that setting";
49                err(ErrorKey::MissingItem).msg(msg).loc(token).push();
50            }
51        }
52
53        vd.unknown_block_fields(|setting, block| {
54            let mut vd = Validator::new(block, data);
55            let loca = format!("setting_{setting}");
56            data.verify_exists_implied(Item::Localization, &loca, setting);
57            let loca = format!("setting_{setting}_desc");
58            data.verify_exists_implied(Item::Localization, &loca, setting);
59            if let Some(token) = vd.field_value("apply_modifier") {
60                if let Some((category, modifier)) = token.split_once(':') {
61                    if !category.is("player") && !category.is("ai") && !category.is("all") {
62                        let msg = "expected player: ai: or all:";
63                        err(ErrorKey::Validation).msg(msg).loc(category).push();
64                    }
65                    data.verify_exists(Item::Modifier, &modifier);
66                } else {
67                    let msg = "expected format category:modifier";
68                    err(ErrorKey::Validation).msg(msg).loc(token).push();
69                }
70            }
71
72            vd.field_validated_block("defines", |block, data| {
73                let mut vd = Validator::new(block, data);
74                vd.unknown_block_fields(|group, block| {
75                    let mut vd = Validator::new(block, data);
76                    vd.unknown_fields(|key, _| {
77                        let define_key = format!("{group}|{key}");
78                        data.verify_exists_implied(Item::Define, &define_key, key);
79                    });
80                });
81            });
82
83            // Flags can be checked in gui with datatype function GameRuleSetting.HasFlag,
84            // so allow arbitrary flags.
85            vd.multi_field_value("flag");
86        });
87    }
88}