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 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 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}