tiger_lib/imperator/
events.rs

1use crate::block::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, warn};
10use crate::scopes::Scopes;
11use crate::special_tokens::SpecialTokens;
12use crate::token::Token;
13use crate::tooltipped::Tooltipped;
14use crate::validate::{ListType, validate_ai_chance, validate_modifiers_with_base};
15use crate::validator::Validator;
16
17const EVENT_TYPES: &[&str] = &[
18    "character_event",
19    "minor_character_event",
20    "country_event",
21    "minor_country_event",
22    "major_country_event",
23    "state_event",
24    "province_event",
25];
26
27pub fn get_event_scope(key: &Token, block: &Block) -> (Scopes, Token) {
28    if let Some(event_type) = block.get_field_value("type") {
29        match event_type.as_str() {
30            "minor_character_event" | "character_event" => (Scopes::Character, event_type.clone()),
31            "country_event" | "minor_country_event" | "major_country_event" => {
32                (Scopes::Country, event_type.clone())
33            }
34            "state_event" => (Scopes::State, event_type.clone()),
35            "province_event" => (Scopes::Province, event_type.clone()),
36            _ => (Scopes::Country, key.clone()),
37        }
38    } else {
39        (Scopes::Country, key.clone())
40    }
41}
42
43pub fn validate_event(event: &Event, data: &Everything, sc: &mut ScopeContext) {
44    let mut vd = Validator::new(&event.block, data);
45
46    let mut tooltipped_immediate = Tooltipped::Past;
47    let mut tooltipped = Tooltipped::Yes;
48
49    vd.field_choice("type", EVENT_TYPES);
50    vd.field_bool("hidden");
51    vd.field_bool("interface_lock");
52    vd.field_bool("fire_only_once");
53
54    vd.field_trigger("trigger", Tooltipped::No, sc);
55    vd.field_validated_block_sc("weight_multiplier", sc, validate_modifiers_with_base);
56
57    vd.field_item_or_target(
58        "goto_location",
59        sc,
60        Item::Province,
61        Scopes::Province.union(Scopes::Country),
62    );
63
64    vd.field_validated_sc("title", sc, validate_desc);
65    vd.field_validated_sc("desc", sc, validate_desc);
66
67    sc.wipe_temporaries();
68    vd.field_effect("immediate", tooltipped_immediate, sc);
69
70    let hidden = event.block.field_value_is("hidden", "yes");
71    if hidden {
72        tooltipped_immediate = Tooltipped::No;
73        tooltipped = Tooltipped::No;
74    }
75
76    let mut minor_event = false;
77    if event.block.field_value_is("type", "minor_character_event")
78        || event.block.field_value_is("type", "minor_country_event")
79    {
80        minor_event = true;
81    }
82
83    if !hidden && !minor_event {
84        vd.req_field("picture");
85    }
86    vd.field_item("picture", Item::EventPicture);
87
88    for field in &["left_portrait", "right_portrait"] {
89        let mut count = 0;
90        vd.multi_field_validated_value(field, |_, mut vd| {
91            count += 1;
92            vd.target_ok_this(sc, Scopes::Character);
93            if count == 4 {
94                let msg = format!("Event has more than 3 {field} attributes.");
95                let info = "Events can only have up to 3 portraits displayed at a time.";
96                warn(ErrorKey::Validation).msg(msg).info(info).loc(&event.key).push();
97            }
98        });
99    }
100
101    if !hidden {
102        vd.req_field("option");
103    }
104    let mut has_options = false;
105    vd.multi_field_validated_block("option", |block, data| {
106        has_options = true;
107        sc.wipe_temporaries();
108        validate_event_option(block, data, sc, tooltipped);
109    });
110
111    vd.field_validated_key_block("after", |key, block, data| {
112        if !has_options {
113            let msg = "`after` effect will not run if there are no `option` blocks";
114            let info = "you can put it in `immediate` instead";
115            err(ErrorKey::Logic).msg(msg).info(info).loc(key).push();
116        }
117        sc.wipe_temporaries();
118        validate_effect(block, data, sc, tooltipped_immediate);
119    });
120}
121
122fn validate_event_option(
123    block: &Block,
124    data: &Everything,
125    sc: &mut ScopeContext,
126    tooltipped: Tooltipped,
127) {
128    let mut vd = Validator::new(block, data);
129    vd.field_validated_sc("name", sc, validate_desc);
130
131    vd.field_trigger("trigger", Tooltipped::No, sc);
132
133    vd.field_bool("exclusive");
134    vd.field_bool("highlight");
135    vd.field_validated_sc("ai_chance", sc, validate_ai_chance);
136    validate_effect_internal(
137        &Lowercase::new_unchecked("option"),
138        ListType::None,
139        block,
140        data,
141        sc,
142        &mut vd,
143        tooltipped,
144        &mut SpecialTokens::none(),
145    );
146}