tiger_lib/data/
scripted_rules.rs1use std::sync::LazyLock;
2
3use crate::block::Block;
4use crate::context::ScopeContext;
5use crate::db::{Db, DbKind};
6use crate::everything::Everything;
7use crate::game::{Game, GameFlags};
8use crate::helpers::TigerHashMap;
9use crate::item::{Item, ItemLoader};
10use crate::parse::pdxfile::parse_pdx_internal;
11use crate::report::{ErrorKey, err};
12use crate::scopes::Scopes;
13use crate::token::Token;
14use crate::tooltipped::Tooltipped;
15use crate::trigger::validate_trigger;
16
17#[derive(Clone, Debug)]
18pub struct ScriptedRule {}
19
20inventory::submit! {
21 ItemLoader::Normal(GameFlags::Ck3.union(GameFlags::Vic3), Item::ScriptedRule, ScriptedRule::add)
22}
23
24impl ScriptedRule {
25 pub fn add(db: &mut Db, key: Token, block: Block) {
26 db.add(Item::ScriptedRule, key, block, Box::new(Self {}));
27 }
28}
29
30impl DbKind for ScriptedRule {
31 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
32 if let Some(sr_sc) = SCRIPTED_RULE_SCOPES_MAP.get(key.as_str()) {
33 let mut sc = ScopeContext::new(sr_sc.root, key);
34 for (name, s) in &sr_sc.names {
35 sc.define_name(name, *s, key);
36 }
37 for (list, s) in &sr_sc.names {
38 sc.define_list(list, *s, key);
39 }
40 validate_trigger(block, data, &mut sc, sr_sc.tooltipped);
41 } else {
42 let msg = "unknown scripted rule";
43 err(ErrorKey::Validation).msg(msg).loc(key).push();
44 let mut sc = ScopeContext::new(Scopes::non_primitive(), key);
45 sc.set_strict_scopes(false);
46 validate_trigger(block, data, &mut sc, Tooltipped::No);
47 }
48 }
49}
50
51#[derive(Debug, Clone)]
52struct ScriptedRuleScopeContext {
53 tooltipped: Tooltipped,
54 root: Scopes,
55 names: Vec<(&'static str, Scopes)>,
56 lists: Vec<(&'static str, Scopes)>,
57}
58
59static SCRIPTED_RULE_SCOPES_MAP: LazyLock<TigerHashMap<&'static str, ScriptedRuleScopeContext>> =
61 LazyLock::new(|| {
62 let rules = match Game::game() {
63 #[cfg(feature = "ck3")]
64 Game::Ck3 => crate::ck3::tables::rules::SCRIPTED_RULES,
65 #[cfg(feature = "vic3")]
66 Game::Vic3 => crate::vic3::tables::rules::SCRIPTED_RULES,
67 #[cfg(feature = "imperator")]
68 Game::Imperator => unimplemented!(),
69 #[cfg(feature = "hoi4")]
70 Game::Hoi4 => unimplemented!(),
71 };
72 build_scripted_rule_hashmap(rules)
73 });
74
75fn build_scripted_rule_hashmap(
78 description: &'static str,
79) -> TigerHashMap<&'static str, ScriptedRuleScopeContext> {
80 let mut hash: TigerHashMap<&'static str, ScriptedRuleScopeContext> = TigerHashMap::default();
81
82 let mut block = parse_pdx_internal(description, "scripted rule builtin scopes");
83 for (key, block) in block.drain_definitions_warn() {
84 let root = block.get_field_value("root").expect("internal error");
85 let root = if root.lowercase_is("all") {
86 Scopes::all()
87 } else {
88 Scopes::from_snake_case(root.as_str()).expect("internal error")
89 };
90 let tooltipped = if block.field_value_is("tooltipped", "yes") {
91 Tooltipped::Yes
92 } else {
93 Tooltipped::No
94 };
95 let mut value =
96 ScriptedRuleScopeContext { tooltipped, root, names: Vec::new(), lists: Vec::new() };
97 for (key, token) in block.iter_assignments() {
98 if key.is("root") || key.is("tooltipped") {
99 continue;
100 }
101 let s = Scopes::from_snake_case(token.as_str()).expect("internal error");
102 value.names.push((key.as_str(), s));
103 }
104 for (key, block) in block.iter_definitions() {
105 if key.is("list") {
106 for (key, token) in block.iter_assignments() {
107 let s = Scopes::from_snake_case(token.as_str()).expect("internal error");
108 value.lists.push((key.as_str(), s));
109 }
110 }
111 }
112 hash.insert(key.as_str(), value);
113 }
114
115 hash
116}