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 vd.field_validated_block("widget", |block, data| {
82 let mut vd = Validator::new(block, data);
83 vd.field_item("gui", Item::File);
84 vd.field_value("name");
85 vd.field_value("container");
86 });
87
88 if !vd.field_validated_sc("status_desc", &mut sc, validate_desc) {
89 data.mark_used(Item::Localization, &format!("{key}_status"));
90 }
91
92 vd.multi_field_validated_sc("event_outcome_activated_desc", &mut sc, validate_desc);
93 vd.multi_field_validated_block_sc(
94 "event_outcome_activated_effect_desc",
95 &mut sc,
96 validate_effect_desc,
97 );
98 vd.multi_field_validated_sc("event_outcome_invalidated_desc", &mut sc, validate_desc);
99 vd.multi_field_validated_block_sc(
100 "event_outcome_invalidated_effect_desc",
101 &mut sc,
102 validate_effect_desc,
103 );
104 vd.multi_field_validated_sc("event_outcome_completed_desc", &mut sc, validate_desc);
105 vd.multi_field_validated_block_sc(
106 "event_outcome_completed_effect_desc",
107 &mut sc,
108 validate_effect_desc,
109 );
110 vd.multi_field_validated_sc("event_outcome_failed_desc", &mut sc, validate_desc);
111 vd.multi_field_validated_block_sc(
112 "event_outcome_failed_effect_desc",
113 &mut sc,
114 validate_effect_desc,
115 );
116 vd.multi_field_validated_sc("event_outcome_timeout_desc", &mut sc, validate_desc);
117 vd.multi_field_validated_block_sc(
118 "event_outcome_timeout_effect_desc",
119 &mut sc,
120 validate_effect_desc,
121 );
122 vd.field_localization("custom_completion_header", &mut sc);
123 vd.field_localization("custom_failure_header", &mut sc);
124 vd.field_localization("custom_on_completion_header", &mut sc);
125 vd.field_localization("custom_on_failure_header", &mut sc);
126
127 vd.field_script_value("timeout", &mut sc);
128 if let Some(token) = block.get_field_value("timeout")
129 && token.is("0")
130 {
131 let msg = "as of 1.9.5, a timeout of 0 will close the journal entry immediately";
132 err(ErrorKey::Logic).msg(msg).loc(token).push();
133 }
134 vd.field_effect("on_timeout", Tooltipped::Yes, &mut sc);
135 vd.field_effect("on_timeout_all_involved", Tooltipped::No, &mut country_sc);
136
137 vd.field_list_items("modifiers_while_active", Item::Modifier);
138
139 for field in &["on_weekly_pulse", "on_monthly_pulse", "on_yearly_pulse"] {
140 vd.multi_field_validated_block_sc(field, &mut sc, validate_on_action);
141 }
142
143 vd.field_script_value("current_value", &mut sc);
144 vd.field_script_value("goal_add_value", &mut sc);
145 vd.field_script_value("weight", &mut sc);
146
147 vd.field_bool("transferable");
148 vd.field_bool("can_revolution_inherit");
149
150 vd.field_trigger("is_progressing", Tooltipped::No, &mut sc);
151 vd.field_bool("progressbar");
152 vd.multi_field_item("scripted_progress_bar", Item::ScriptedProgressBar);
153
154 vd.field_bool("can_deactivate");
155
156 if block.field_value_is("progressbar", "yes") {
157 if !vd.field_validated_sc("progress_desc", &mut sc, validate_desc) {
158 data.mark_used(Item::Localization, &format!("{key}_progress"));
159 }
160 } else {
161 vd.ban_field("progress_desc", || "progressbar = yes");
162 }
163
164 vd.field_item("how_tutorial", Item::TutorialLesson);
165 vd.field_item("why_tutorial", Item::TutorialLesson);
166
167 vd.field_bool("should_be_pinned_by_default");
168 vd.field_bool("should_be_pinned_by_default_uninvolved_or_context");
169
170 vd.field_integer("active_update_frequency");
173 vd.field_bool("should_update_on_player_command");
174 vd.field_bool("display_progressbar_as_months");
175 vd.field_trigger("is_shown_in_lobby", Tooltipped::No, &mut sc);
176 }
177}
178
179#[derive(Clone, Debug)]
180pub struct JournalEntryGroup {}
181
182inventory::submit! {
183 ItemLoader::Normal(GameFlags::Vic3, Item::JournalEntryGroup, JournalEntryGroup::add)
184}
185
186impl JournalEntryGroup {
187 pub fn add(db: &mut Db, key: Token, block: Block) {
188 db.add(Item::JournalEntryGroup, key, block, Box::new(Self {}));
189 }
190}
191
192impl DbKind for JournalEntryGroup {
193 fn validate(&self, key: &Token, block: &Block, data: &Everything) {
194 data.verify_exists(Item::Localization, key);
195
196 let mut vd = Validator::new(block, data);
197 vd.field_choice("context", &["none", "country"]);
198 }
199
200 fn has_property(
201 &self,
202 _key: &Token,
203 block: &Block,
204 property: &str,
205 _data: &Everything,
206 ) -> bool {
207 if property == "none_context" {
208 return block.get_field_value("context").is_some_and(|v| v.is("none"));
209 }
210 false
211 }
212}
213
214fn validate_effect_desc(block: &Block, data: &Everything, sc: &mut ScopeContext) {
215 let mut vd = Validator::new(block, data);
216
217 vd.field_localization("header", sc);
218 vd.field_effect("effect", Tooltipped::Yes, sc);
219}