tiger_lib/vic3/
events.rs

1use crate::block::{BV, Block};
2use crate::context::ScopeContext;
3use crate::data::events::Event;
4use crate::desc::validate_desc;
5use crate::effect::{validate_effect, validate_effect_internal};
6use crate::everything::Everything;
7use crate::item::Item;
8use crate::lowercase::Lowercase;
9use crate::report::{ErrorKey, err};
10use crate::scopes::Scopes;
11use crate::special_tokens::SpecialTokens;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::validate::{
15    ListType, validate_ai_chance, validate_duration, validate_modifiers_with_base,
16};
17use crate::validator::Validator;
18use crate::vic3::tables::misc::EVENT_CATEGORIES;
19
20const EVENT_TYPES: &[&str] = &["character_event", "country_event", "state_event"];
21
22pub fn get_event_scope(key: &Token, block: &Block) -> (Scopes, Token) {
23    if let Some(event_type) = block.get_field_value("type") {
24        match event_type.as_str() {
25            "character_event" => (Scopes::Character, event_type.clone()),
26            "country_event" => (Scopes::Country, event_type.clone()),
27            "state_event" => (Scopes::State, event_type.clone()),
28            _ => (Scopes::Country, key.clone()),
29        }
30    } else {
31        (Scopes::Country, key.clone())
32    }
33}
34
35pub fn validate_event(event: &Event, data: &Everything, sc: &mut ScopeContext) {
36    let mut vd = Validator::new(&event.block, data);
37
38    let mut tooltipped_immediate = Tooltipped::Past;
39    let mut tooltipped = Tooltipped::Yes;
40
41    // TODO: should character_event always be hidden?
42    vd.field_choice("type", EVENT_TYPES);
43
44    // TODO: what is this for and what else can it be?
45    vd.field_choice("category", EVENT_CATEGORIES);
46
47    vd.field_bool("is_popup");
48    vd.field_bool("orphan");
49    vd.field_bool("hidden");
50    let hidden = event.block.field_value_is("hidden", "yes");
51    if hidden {
52        tooltipped_immediate = Tooltipped::No;
53        tooltipped = Tooltipped::No;
54    }
55
56    vd.field_item("dlc", Item::Dlc);
57
58    vd.field_trigger("trigger", Tooltipped::No, sc);
59    vd.field_validated_block_sc("weight_multiplier", sc, validate_modifiers_with_base);
60    sc.wipe_temporaries();
61    vd.field_effect("immediate", tooltipped_immediate, sc);
62
63    vd.multi_field_validated_block("event_image", |block, data| {
64        let mut vd = Validator::new(block, data);
65        vd.field_trigger("trigger", Tooltipped::No, sc);
66        if let Some(token) = vd.field_value("video") {
67            if token.as_str().contains('/') {
68                data.verify_exists(Item::File, token);
69            } else {
70                data.verify_exists(Item::MediaAlias, token);
71            }
72        }
73        vd.field_item("texture", Item::File);
74        vd.field_item("on_created_soundeffect", Item::Sound);
75    });
76
77    vd.field_value("gui_window"); // TODO
78
79    vd.field_item("on_created_soundeffect", Item::Sound);
80    vd.field_item("on_opened_soundeffect", Item::Sound);
81    vd.field_item("icon", Item::File);
82
83    vd.field_integer("duration");
84
85    vd.field_trigger("cancellation_trigger", Tooltipped::No, sc);
86
87    vd.field_validated_sc("title", sc, validate_desc);
88    vd.field_validated_sc("desc", sc, validate_desc);
89    vd.field_validated_sc("flavor", sc, validate_desc);
90    vd.field_validated_block_sc("cooldown", sc, validate_duration);
91
92    vd.field_target("placement", sc, Scopes::Country | Scopes::State | Scopes::StateRegion);
93
94    // Which scope types are accepted in these icons depends on the gui files,
95    // which may be modded so we can't be certain.
96    // In principle, any scope that supports GetIcon in the datatype functions can work,
97    // and that's approximately all of them.
98    vd.field_item_or_target("minor_left_icon", sc, Item::File, Scopes::all_but_none());
99    vd.field_item_or_target("minor_right_icon", sc, Item::File, Scopes::all_but_none());
100    vd.field_item_or_target("left_icon", sc, Item::File, Scopes::all_but_none());
101    vd.field_item_or_target("right_icon", sc, Item::File, Scopes::all_but_none());
102    vd.field_item_or_target("center_icon", sc, Item::File, Scopes::all_but_none());
103
104    if !hidden {
105        vd.req_field("option");
106    }
107    let mut has_options = false;
108    vd.multi_field_validated_block("option", |block, data| {
109        has_options = true;
110        sc.wipe_temporaries();
111        validate_event_option(block, data, sc, tooltipped);
112    });
113    vd.field_validated_key_block("after", |key, block, data| {
114        if !has_options {
115            let msg = "`after` effect will not run if there are no `option` blocks";
116            let info = "you can put it in `immediate` instead";
117            err(ErrorKey::Logic).msg(msg).info(info).loc(key).push();
118        }
119        sc.wipe_temporaries();
120        validate_effect(block, data, sc, tooltipped);
121    });
122}
123
124fn validate_event_option(
125    block: &Block,
126    data: &Everything,
127    sc: &mut ScopeContext,
128    tooltipped: Tooltipped,
129) {
130    // TODO: warn if they use desc, first_valid, random_valid, or triggered_desc directly
131    // in the name or tooltip.
132
133    let mut vd = Validator::new(block, data);
134    vd.multi_field_validated("name", |bv, data| match bv {
135        BV::Value(t) => {
136            data.verify_exists(Item::Localization, t);
137        }
138        BV::Block(b) => {
139            let mut vd = Validator::new(b, data);
140            vd.req_field("text");
141            vd.field_trigger("trigger", Tooltipped::No, sc);
142            vd.field_validated_sc("text", sc, validate_desc);
143        }
144    });
145
146    vd.field_trigger("trigger", Tooltipped::No, sc);
147    // undocumented
148    vd.field_trigger("show_as_unavailable", Tooltipped::No, sc);
149
150    vd.field_bool("default_option");
151    vd.field_bool("highlighted_option");
152    vd.field_bool("fallback");
153    vd.field_validated_sc("ai_chance", sc, validate_ai_chance);
154    validate_effect_internal(
155        &Lowercase::new_unchecked("option"),
156        ListType::None,
157        block,
158        data,
159        sc,
160        &mut vd,
161        tooltipped,
162        &mut SpecialTokens::none(),
163    );
164}