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 vd.field_choice("type", EVENT_TYPES);
43
44 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"); 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 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 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 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}