tiger_lib/vic3/data/
journalentries.rs

1use crate::block::Block;
2use crate::context::ScopeContext;
3use crate::data::on_actions::validate_on_action;
4use crate::db::{Db, DbKind};
5use crate::desc::validate_desc;
6use crate::everything::Everything;
7use crate::game::GameFlags;
8use crate::item::{Item, ItemLoader};
9use crate::report::{ErrorKey, err};
10use crate::scopes::Scopes;
11use crate::token::Token;
12use crate::tooltipped::Tooltipped;
13use crate::validator::Validator;
14
15#[derive(Clone, Debug)]
16pub struct JournalEntry {}
17
18inventory::submit! {
19    ItemLoader::Normal(GameFlags::Vic3, Item::JournalEntry, JournalEntry::add)
20}
21
22impl JournalEntry {
23    pub fn add(db: &mut Db, key: Token, block: Block) {
24        db.add(Item::JournalEntry, key, block, Box::new(Self {}));
25    }
26}
27
28impl DbKind for JournalEntry {
29    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
30        let mut vd = Validator::new(block, data);
31
32        data.verify_exists(Item::Localization, key);
33        let loca = format!("{key}_reason");
34        data.verify_exists_implied(Item::Localization, &loca, key);
35        // TODO: make this depend on whether the journalentry uses the "goal" mechanic
36        let loca = format!("{key}_goal");
37        data.mark_used(Item::Localization, &loca);
38
39        let sc_context = if let Some(group) = block.get_field_value("group") {
40            if data.item_has_property(Item::JournalEntryGroup, group.as_str(), "none_context") {
41                Scopes::None
42            } else {
43                Scopes::Country
44            }
45        } else {
46            Scopes::Country
47        };
48        let mut sc = ScopeContext::new(sc_context, key);
49        sc.define_name("journal_entry", Scopes::JournalEntry, key);
50        sc.define_name("target", Scopes::all(), key);
51
52        let mut country_sc = ScopeContext::new(Scopes::Country, key);
53        country_sc.define_name("journal_entry", Scopes::JournalEntry, key);
54        country_sc.define_name("target", Scopes::all(), key);
55
56        vd.field_item("group", Item::JournalEntryGroup);
57
58        vd.field_item("icon", Item::File);
59
60        vd.field_trigger("is_shown_when_inactive", Tooltipped::No, &mut sc);
61        vd.field_trigger("should_be_involved", Tooltipped::No, &mut country_sc);
62        vd.field_trigger("should_show_when_not_involved", Tooltipped::No, &mut country_sc);
63
64        vd.multi_field_item("scripted_button", Item::ScriptedButton);
65
66        vd.field_trigger("possible", Tooltipped::Yes, &mut sc);
67        vd.field_effect("immediate", Tooltipped::No, &mut sc);
68        vd.field_effect("immediate_all_involved", Tooltipped::No, &mut country_sc);
69        vd.field_trigger("complete", Tooltipped::Yes, &mut sc);
70        vd.field_effect("on_complete", Tooltipped::Yes, &mut sc);
71        vd.field_effect("on_complete_all_involved", Tooltipped::No, &mut country_sc);
72        vd.field_trigger("fail", Tooltipped::Yes, &mut sc);
73        vd.field_effect("on_fail", Tooltipped::Yes, &mut sc);
74        vd.field_effect("on_fail_all_involved", Tooltipped::No, &mut country_sc);
75        vd.field_trigger("invalid", Tooltipped::No, &mut sc);
76        vd.field_effect("on_invalid", Tooltipped::Yes, &mut sc);
77        vd.field_effect("on_invalid_all_involved", Tooltipped::No, &mut country_sc);
78        vd.field_effect("on_become_involved_after_activation", Tooltipped::No, &mut country_sc);
79        vd.field_effect("on_no_longer_involved", Tooltipped::No, &mut country_sc);
80
81        if !vd.field_validated_sc("status_desc", &mut sc, validate_desc) {
82            data.mark_used(Item::Localization, &format!("{key}_status"));
83        }
84
85        vd.multi_field_validated_sc("event_outcome_activated_desc", &mut sc, validate_desc);
86        vd.multi_field_validated_block_sc(
87            "event_outcome_activated_effect_desc",
88            &mut sc,
89            validate_effect_desc,
90        );
91        vd.multi_field_validated_sc("event_outcome_invalidated_desc", &mut sc, validate_desc);
92        vd.multi_field_validated_block_sc(
93            "event_outcome_invalidated_effect_desc",
94            &mut sc,
95            validate_effect_desc,
96        );
97        vd.multi_field_validated_sc("event_outcome_completed_desc", &mut sc, validate_desc);
98        vd.multi_field_validated_block_sc(
99            "event_outcome_completed_effect_desc",
100            &mut sc,
101            validate_effect_desc,
102        );
103        vd.multi_field_validated_sc("event_outcome_failed_desc", &mut sc, validate_desc);
104        vd.multi_field_validated_block_sc(
105            "event_outcome_failed_effect_desc",
106            &mut sc,
107            validate_effect_desc,
108        );
109        vd.multi_field_validated_sc("event_outcome_timeout_desc", &mut sc, validate_desc);
110        vd.multi_field_validated_block_sc(
111            "event_outcome_timeout_effect_desc",
112            &mut sc,
113            validate_effect_desc,
114        );
115        vd.field_localization("custom_completion_header", &mut sc);
116        vd.field_localization("custom_failure_header", &mut sc);
117        vd.field_localization("custom_on_completion_header", &mut sc);
118        vd.field_localization("custom_on_failure_header", &mut sc);
119
120        vd.field_script_value("timeout", &mut sc);
121        if let Some(token) = block.get_field_value("timeout") {
122            if token.is("0") {
123                let msg = "as of 1.9.5, a timeout of 0 will close the journal entry immediately";
124                err(ErrorKey::Logic).msg(msg).loc(token).push();
125            }
126        }
127        vd.field_effect("on_timeout", Tooltipped::Yes, &mut sc);
128        vd.field_effect("on_timeout_all_involved", Tooltipped::No, &mut country_sc);
129
130        vd.field_list_items("modifiers_while_active", Item::Modifier);
131
132        for field in &["on_weekly_pulse", "on_monthly_pulse", "on_yearly_pulse"] {
133            vd.multi_field_validated_block_sc(field, &mut sc, validate_on_action);
134        }
135
136        vd.field_script_value("current_value", &mut sc);
137        vd.field_script_value("goal_add_value", &mut sc);
138        vd.field_script_value("weight", &mut sc);
139
140        vd.field_bool("transferable");
141        vd.field_bool("can_revolution_inherit");
142
143        vd.field_trigger("is_progressing", Tooltipped::No, &mut sc);
144        vd.field_bool("progressbar");
145        vd.multi_field_item("scripted_progress_bar", Item::ScriptedProgressBar);
146
147        vd.field_bool("can_deactivate");
148
149        if block.field_value_is("progressbar", "yes") {
150            if !vd.field_validated_sc("progress_desc", &mut sc, validate_desc) {
151                data.mark_used(Item::Localization, &format!("{key}_progress"));
152            }
153        } else {
154            vd.ban_field("progress_desc", || "progressbar = yes");
155        }
156
157        vd.field_item("how_tutorial", Item::TutorialLesson);
158        vd.field_item("why_tutorial", Item::TutorialLesson);
159
160        vd.field_bool("should_be_pinned_by_default");
161
162        // undocumented
163
164        vd.field_integer("active_update_frequency");
165        vd.field_bool("should_update_on_player_command");
166        vd.field_bool("display_progressbar_as_months");
167        vd.field_trigger("is_shown_in_lobby", Tooltipped::No, &mut sc);
168    }
169}
170
171#[derive(Clone, Debug)]
172pub struct JournalEntryGroup {}
173
174inventory::submit! {
175    ItemLoader::Normal(GameFlags::Vic3, Item::JournalEntryGroup, JournalEntryGroup::add)
176}
177
178impl JournalEntryGroup {
179    pub fn add(db: &mut Db, key: Token, block: Block) {
180        db.add(Item::JournalEntryGroup, key, block, Box::new(Self {}));
181    }
182}
183
184impl DbKind for JournalEntryGroup {
185    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
186        data.verify_exists(Item::Localization, key);
187
188        let mut vd = Validator::new(block, data);
189        vd.field_choice("context", &["none", "country"]);
190    }
191
192    fn has_property(
193        &self,
194        _key: &Token,
195        block: &Block,
196        property: &str,
197        _data: &Everything,
198    ) -> bool {
199        if property == "none_context" {
200            return block.get_field_value("context").is_some_and(|v| v.is("none"));
201        }
202        false
203    }
204}
205
206fn validate_effect_desc(block: &Block, data: &Everything, sc: &mut ScopeContext) {
207    let mut vd = Validator::new(block, data);
208
209    vd.field_localization("header", sc);
210    vd.field_effect("effect", Tooltipped::Yes, sc);
211}