Skip to main content

tiger_lib/vic3/data/
diplomatic_actions.rs

1use crate::block::Block;
2use crate::context::ScopeContext;
3use crate::db::{Db, DbKind};
4use crate::everything::Everything;
5use crate::game::GameFlags;
6use crate::item::{Item, ItemLoader};
7use crate::modif::validate_modifs;
8use crate::scopes::Scopes;
9use crate::token::Token;
10use crate::tooltipped::Tooltipped;
11use crate::validator::{Validator, ValueValidator};
12use crate::vic3::modif::ModifKinds;
13
14#[derive(Clone, Debug)]
15pub struct DiplomaticAction {}
16
17inventory::submit! {
18    ItemLoader::Normal(GameFlags::Vic3, Item::DiplomaticAction, DiplomaticAction::add)
19}
20
21impl DiplomaticAction {
22    pub fn add(db: &mut Db, key: Token, block: Block) {
23        db.add(Item::DiplomaticAction, key, block, Box::new(Self {}));
24    }
25}
26
27impl DbKind for DiplomaticAction {
28    fn validate(&self, key: &Token, block: &Block, data: &Everything) {
29        let mut vd = Validator::new(block, data);
30        let mut sc = ScopeContext::new(Scopes::Country, key);
31        sc.define_name("target_country", Scopes::Country, key);
32        // TODO: in which fields exactly is scope:actor defined?
33        sc.define_name("actor", Scopes::Country, key);
34
35        data.verify_exists(Item::Localization, key);
36        let loca = format!("{key}_desc");
37        data.verify_exists_implied(Item::Localization, &loca, key);
38
39        if block.get_field_bool("requires_approval").unwrap_or(true) {
40            let loca = format!("{key}_action_name");
41            data.verify_exists_implied(Item::Localization, &loca, key);
42            // The actions that are not shown in the lens are meant to be
43            // enforced through diplomatic plays, rather than as proposals.
44            if block.get_field_bool("show_in_lens").unwrap_or(true) {
45                let loca = format!("{key}_proposal_accepted_name");
46                data.verify_exists_implied(Item::Localization, &loca, key);
47                let loca = format!("{key}_proposal_accepted_desc");
48                data.verify_exists_implied(Item::Localization, &loca, key);
49                let loca = format!("{key}_proposal_declined_name");
50                data.verify_exists_implied(Item::Localization, &loca, key);
51                let loca = format!("{key}_proposal_declined_desc");
52                data.verify_exists_implied(Item::Localization, &loca, key);
53                let loca = format!("{key}_proposal_notification_name");
54                data.verify_exists_implied(Item::Localization, &loca, key);
55                let loca = format!("{key}_proposal_notification_desc");
56                data.verify_exists_implied(Item::Localization, &loca, key);
57                let loca = format!("{key}_proposal_third_party_accepted_name");
58                data.verify_exists_implied(Item::Localization, &loca, key);
59                let loca = format!("{key}_proposal_third_party_accepted_desc");
60                data.verify_exists_implied(Item::Localization, &loca, key);
61                let loca = format!("{key}_proposal_third_party_declined_name");
62                data.verify_exists_implied(Item::Localization, &loca, key);
63                let loca = format!("{key}_proposal_third_party_declined_desc");
64                data.verify_exists_implied(Item::Localization, &loca, key);
65            }
66        } else {
67            let loca = format!("{key}_action_notification_name");
68            data.verify_exists_implied(Item::Localization, &loca, key);
69            let loca = format!("{key}_action_notification_desc");
70            data.verify_exists_implied(Item::Localization, &loca, key);
71            if block.get_field_bool("should_notify_third_parties").unwrap_or(false) {
72                let loca = format!("{key}_action_notification_third_party_name");
73                data.verify_exists_implied(Item::Localization, &loca, key);
74                let loca = format!("{key}_action_notification_third_party_desc");
75                data.verify_exists_implied(Item::Localization, &loca, key);
76            }
77        }
78        if block.has_key("pact") {
79            let loca = format!("{key}_pact_desc");
80            data.verify_exists_implied(Item::Localization, &loca, key);
81            let loca = format!("{key}_action_propose_name");
82            data.verify_exists_implied(Item::Localization, &loca, key);
83            let loca = format!("{key}_action_break_name");
84            data.verify_exists_implied(Item::Localization, &loca, key);
85            let loca = format!("{key}_action_notification_break_name");
86            data.verify_exists_implied(Item::Localization, &loca, key);
87            let loca = format!("{key}_action_notification_break_desc");
88            data.verify_exists_implied(Item::Localization, &loca, key);
89            if block.get_field_bool("should_notify_third_parties").unwrap_or(false) {
90                let loca = format!("{key}_action_notification_third_party_break_name");
91                data.verify_exists_implied(Item::Localization, &loca, key);
92                let loca = format!("{key}_action_notification_third_party_break_desc");
93                data.verify_exists_implied(Item::Localization, &loca, key);
94            }
95        }
96
97        vd.field_validated_list("groups", |token, data| {
98            let mut vd = ValueValidator::new(token, data);
99            vd.choice(&[
100                "general",
101                "subject",
102                "overlord",
103                "power_bloc",
104                "power_bloc_leader",
105                "power_bloc_member",
106            ]);
107        });
108
109        vd.field_bool("requires_approval");
110        vd.field_bool("uses_random_approval");
111        vd.field_bool("show_confirmation_box");
112        vd.field_bool("is_hostile");
113        vd.field_bool("should_notify_third_parties"); // undocumented
114        vd.field_bool("show_effect_in_tooltip"); // undocumented
115        vd.field_bool("violates_sovereignty"); // undocumented
116        vd.field_bool("can_use_obligations"); // undocumented
117        vd.field_bool("can_select"); // undocumented
118        vd.field_bool("can_select_to_break"); // undocumented
119        vd.field_bool("show_in_lens"); // undocumented
120
121        vd.field_choice(
122            "state_selection",
123            &[
124                "first_required",
125                "first_optional",
126                "second_required",
127                "second_optional",
128                "both_required",
129                "both_optional",
130                "any_required",
131            ],
132        );
133        for field in &["first_state_list", "second_state_list"] {
134            vd.field_choice(
135                field,
136                &[
137                    "first_country",
138                    "second_country",
139                    "all",
140                    "first_country_and_subjects",
141                    "second_country_and_subjects",
142                ],
143            );
144        }
145
146        vd.multi_field_list_items("unlocking_technologies", Item::Technology); // undocumented
147
148        vd.field_trigger("selectable", Tooltipped::No, &mut sc);
149        vd.field_trigger("potential", Tooltipped::No, &mut sc);
150        vd.field_trigger("possible", Tooltipped::Yes, &mut sc);
151        for field in &["first_state_trigger", "second_state_trigger"] {
152            vd.field_trigger_builder(field, Tooltipped::Yes, |key| {
153                let mut sc = ScopeContext::new(Scopes::State, key);
154                sc.define_name("country", Scopes::Country, key);
155                sc.define_name("target_country", Scopes::Country, key);
156                sc
157            });
158        }
159        vd.field_effect("accept_effect", Tooltipped::Yes, &mut sc);
160        vd.field_effect("decline_effect", Tooltipped::Yes, &mut sc);
161        vd.advice_field("effect", "the docs say effect but the field is called accept_effect");
162        vd.field_validated_block_sc("pact", &mut sc, validate_pact);
163        vd.field_validated_block_sc("ai", &mut sc, validate_ai);
164
165        vd.field_item("reverse_pact", Item::DiplomaticAction); // undocumented
166        vd.field_item("transfer_pact", Item::DiplomaticAction); // undocumented
167
168        vd.field_item("confirmation_sound", Item::Sound);
169        vd.field_item("request_sound", Item::Sound);
170        vd.field_item("hostile_sound", Item::Sound);
171        vd.field_item("benign_sound", Item::Sound);
172
173        // undocumented
174
175        vd.field_item("texture", Item::File);
176    }
177}
178
179fn validate_pact(block: &Block, data: &Everything, sc: &mut ScopeContext) {
180    let mut vd = Validator::new(block, data);
181
182    vd.field_numeric("cost");
183    vd.field_bool("counts_for_tech_spread");
184    vd.field_bool("show_in_outliner"); // undocumented
185    vd.field_bool("can_be_used_in_sway_offers"); // undocumented
186    vd.field_integer("sway_maneuvers_cost"); // undocumented
187    vd.field_bool("infamy_affects_maintenance"); // undocumented
188    vd.field_bool("has_junior_participant"); // undocumented
189    vd.field_bool("is_two_sided_pact"); // undocumented
190    vd.field_bool("is_alliance"); // undocumented
191    vd.field_bool("is_defensive_pact"); // undocumented
192    vd.field_bool("is_rivalry"); // undocumented
193    vd.field_bool("is_embargo"); // undocumented
194    vd.field_bool("is_trade_agreement"); // undocumented
195    vd.field_bool("is_customs_union"); // undocumented
196    vd.field_bool("is_humiliation"); // undocumented
197    vd.field_bool("is_colonization_rights"); // undocumented
198    vd.field_bool("is_hostile"); // undocumented
199    vd.field_bool("is_guarantee_independence"); // undocumented
200    vd.field_bool("exempt_from_service"); // undocumented
201    vd.field_bool("is_foreign_investment_rights"); // undocumented
202    vd.field_bool("junior_support_only_against_overlord"); // undocumented
203
204    vd.field_item("subject_type", Item::SubjectType); // undocumented
205    //
206    vd.field_choice("maintenance_paid_by", &["first_country", "second_country"]); // undocumented
207    vd.advice_field("recipient_pays_maintenance", "removed in 1.9.3");
208
209    vd.replaced_field(
210        "recipient_gets_income_transfer",
211        "renamed to second_country_gets_income_transfer",
212    );
213    vd.field_bool("second_country_gets_income_transfer");
214    vd.replaced_field(
215        "income_transfer_based_on_recipient",
216        "renamed to income_transfer_based_on_second_country",
217    );
218    vd.field_bool("income_transfer_based_on_second_country");
219    vd.field_validated_block("income_transfer_to_pops", |block, data| {
220        let mut vd = Validator::new(block, data);
221        vd.replaced_field("allow_discriminated", "allow_non_fully_accepted");
222        vd.field_bool("allow_non_fully_accepted");
223        for field in &["upper_strata_pops", "middle_strata_pops", "lower_strata_pops"] {
224            vd.field_script_value(field, sc);
225        }
226    });
227    vd.field_numeric("income_transfer"); // may be negative
228    vd.field_numeric("max_paying_country_income_to_transfer");
229
230    vd.field_numeric("relations_progress_per_day"); // undocumented
231    vd.field_numeric("relations_improvement_max"); // undocumented
232    vd.field_numeric("relations_improvement_min"); // undocumented
233
234    vd.field_integer("forced_duration");
235    vd.field_integer("max_target_involvement");
236    vd.field_choice(
237        "target_involvement_applies_to",
238        &["source_country", "target_country", "mutual"],
239    );
240
241    vd.field_item("propose_string", Item::Localization);
242    vd.field_item("break_string", Item::Localization);
243    vd.field_item("ask_to_end_string", Item::Localization);
244
245    vd.field_bool("military_access");
246    vd.field_bool("actor_requires_approval_to_break");
247    vd.field_bool("target_requires_approval_to_break");
248    vd.field_bool("is_breaking_hostile");
249    vd.field_bool("is_target_breaking_hostile");
250
251    vd.replaced_field(
252        "is_about_to_auto_break",
253        "show_about_to_break_warning in requirement_to_maintain",
254    );
255    vd.replaced_field("should_auto_break", "trigger in requirement_to_maintain");
256    vd.replaced_field("should_invalidate", "trigger in requirement_to_maintain");
257    vd.field_trigger("actor_can_break", Tooltipped::No, sc);
258    vd.field_trigger("target_can_break", Tooltipped::No, sc);
259    vd.multi_field_validated_block("requirement_to_maintain", |block, data| {
260        let mut vd = Validator::new(block, data);
261        vd.field_trigger("trigger", Tooltipped::No, sc);
262        vd.field_trigger("show_about_to_break_warning", Tooltipped::No, sc);
263    });
264
265    vd.replaced_field("break_effect", "manual_break_effect and auto_break_effect");
266    for field in &[
267        "daily_effect",
268        "weekly_effect",
269        "monthly_effect",
270        "manual_break_effect",
271        "auto_break_effect",
272    ] {
273        vd.field_effect(field, Tooltipped::No, sc);
274    }
275
276    vd.field_validated_block("subject_relation", |block, data| {
277        let mut vd = Validator::new(block, data);
278        vd.field_bool("annex_on_country_formation");
279    });
280
281    vd.multi_field_item("auto_support_type", Item::DiplomaticPlay);
282
283    // undocumented
284
285    for field in &["first_modifier", "second_modifier"] {
286        vd.field_validated_block(field, |block, data| {
287            let vd = Validator::new(block, data);
288            validate_modifs(block, data, ModifKinds::Country, vd);
289        });
290    }
291
292    // TODO: the existence of first_foreign_pro_country_lobby_member_modifier
293    // and first_foreign_anti_country_lobby_member_modifier is a guess. Verify.
294    for field in &[
295        "first_foreign_pro_country_lobby_member_modifier",
296        "first_foreign_anti_country_lobby_member_modifier",
297        "second_foreign_pro_country_lobby_member_modifier",
298        "second_foreign_anti_country_lobby_member_modifier",
299    ] {
300        vd.field_validated_block(field, |block, data| {
301            let vd = Validator::new(block, data);
302            validate_modifs(block, data, ModifKinds::InterestGroup, vd);
303        });
304    }
305
306    vd.field_choice("market_owner", &["first_country", "second_country"]);
307}
308
309fn validate_ai(block: &Block, data: &Everything, sc: &mut ScopeContext) {
310    let mut vd = Validator::new(block, data);
311
312    vd.field_script_value_rooted("evaluation_chance", Scopes::Country);
313
314    vd.field_trigger_rooted("will_select_as_first_state", Tooltipped::No, Scopes::State);
315    vd.field_trigger_rooted("will_select_as_second_state", Tooltipped::No, Scopes::State);
316
317    vd.field_bool("check_acceptance_for_will_break");
318    vd.field_bool("check_acceptance_for_will_propose");
319
320    vd.field_numeric_range("max_influence_spending_fraction", 0.0..=1.0);
321
322    for field in &[
323        "will_propose_with_states",
324        "will_propose",
325        "will_break",
326        "will_propose_even_if_not_accepted",
327    ] {
328        vd.field_trigger(field, Tooltipped::No, sc);
329    }
330
331    for field in &[
332        "accept_score",
333        "accept_break_score",
334        "propose_score",
335        "propose_break_score",
336        "use_obligation_chance",
337        "owe_obligation_chance",
338        "junior_accept_score",
339    ] {
340        vd.field_script_value(field, sc);
341    }
342    vd.advice_field("use_favor_chance", "this field is called use_obligation_chance");
343    vd.advice_field("owe_favor_chance", "this field is called owe_obligation_chance");
344}