1use std::path::PathBuf;
2use std::str::FromStr;
3use std::sync::Mutex;
4
5use rayon::prelude::*;
6
7use crate::block::{Block, BlockItem, Field};
8use crate::context::{Reason, ScopeContext, Signature};
9use crate::data::scripted_effects::Effect;
10use crate::data::scripted_triggers::Trigger;
11use crate::everything::Everything;
12use crate::fileset::{FileEntry, FileHandler};
13use crate::game::Game;
14use crate::helpers::{TigerHashMap, TigerHashSet, dup_error};
15use crate::item::Item;
16use crate::parse::ParserMemory;
17use crate::pathtable::PathTableIndex;
18use crate::pdxfile::PdxFile;
19use crate::report::{ErrorKey, err, warn};
20use crate::scopes::Scopes;
21use crate::token::Token;
22use crate::variables::Variables;
23
24#[derive(Debug, Default)]
25#[allow(clippy::struct_field_names)]
26pub struct Events {
27 events: TigerHashMap<(&'static str, u16), Event>,
28 namespaces: TigerHashSet<Token>,
29 triggers: TigerHashMap<(PathTableIndex, &'static str), Trigger>,
30 effects: TigerHashMap<(PathTableIndex, &'static str), Effect>,
31}
32
33impl Events {
34 fn load_event(&mut self, key: Token, block: Block) {
35 if let Some((key_a, key_b)) = key.as_str().split_once('.')
36 && let Ok(id) = u16::from_str(key_b)
37 {
38 if let Some(other) = self.get_event(key.as_str()) {
39 #[allow(clippy::redundant_else)]
40 if Game::is_vic3() {
41 if other.key.loc.kind <= key.loc.kind {
44 dup_error(&other.key, &key, "event");
45 }
46 return;
47 } else {
48 dup_error(&key, &other.key, "event");
50 }
51 }
52 self.events.insert((key_a, id), Event::new(key, block));
53 return;
54 }
55 let msg = "Event names should be in the form NAMESPACE.NUMBER";
56 let info = "where NAMESPACE is the namespace declared at the top of the file, and NUMBER is a series of up to 4 digits.";
57 warn(ErrorKey::EventNamespace).msg(msg).info(info).loc(key).push();
58 }
59
60 fn load_scripted_trigger(&mut self, key: Token, block: Block) {
61 let index = (key.loc.idx, key.as_str());
62 if let Some(other) = self.triggers.get(&index) {
63 dup_error(&key, &other.key, "scripted trigger");
64 }
65 self.triggers.insert(index, Trigger::new(key, block, None));
66 }
67
68 fn load_scripted_effect(&mut self, key: Token, block: Block) {
69 let index = (key.loc.idx, key.as_str());
70 if let Some(other) = self.effects.get(&index) {
71 dup_error(&key, &other.key, "scripted effect");
72 }
73 self.effects.insert(index, Effect::new(key, block, None));
74 }
75
76 pub fn scan_variables(&self, registry: &mut Variables) {
77 for item in self.events.values() {
78 registry.scan(&item.block);
79 }
80 for item in self.triggers.values() {
81 registry.scan(&item.block);
82 }
83 for item in self.effects.values() {
84 registry.scan(&item.block);
85 }
86 }
87
88 #[cfg(any(feature = "ck3", feature = "eu5"))]
89 pub fn get_trigger(&self, key: &Token) -> Option<&Trigger> {
90 let index = (key.loc.idx, key.as_str());
91 self.triggers.get(&index)
92 }
93
94 #[cfg(any(feature = "ck3", feature = "eu5"))]
95 pub fn get_effect(&self, key: &Token) -> Option<&Effect> {
96 let index = (key.loc.idx, key.as_str());
97 self.effects.get(&index)
98 }
99
100 fn get_event<'a>(&'a self, key: &'a str) -> Option<&'a Event> {
101 if let Some((namespace, id)) = key.split_once('.')
102 && let Ok(id) = u16::from_str(id)
103 {
104 return self.events.get(&(namespace, id));
105 }
106 None
107 }
108
109 pub fn check_scope(&self, token: &Token, sc: &mut ScopeContext, data: &Everything) {
110 if let Some(event) = self.get_event(token.as_str()) {
111 sc.expect(event.expects_scope, &Reason::Token(token.clone()), data);
112 }
113 }
114
115 pub fn namespace_exists(&self, key: &str) -> bool {
116 self.namespaces.contains(key)
117 }
118
119 pub fn iter_namespace_keys(&self) -> impl Iterator<Item = &Token> {
120 self.namespaces.iter()
121 }
122
123 pub fn exists(&self, key: &str) -> bool {
124 if let Some((namespace, id)) = key.split_once('.')
125 && let Ok(id) = u16::from_str(id)
126 && self.events.contains_key(&(namespace, id))
127 {
128 return true;
129 }
130 false
131 }
132
133 pub fn iter_keys(&self) -> impl Iterator<Item = &Token> {
134 self.events.values().map(|item| &item.key)
135 }
136
137 pub fn validate(&self, data: &Everything) {
138 for item in self.effects.values() {
139 item.validate(data);
140 }
141
142 for item in self.triggers.values() {
143 item.validate(data);
144 }
145
146 self.events.par_iter().for_each(|(_, item)| {
147 item.validate(data);
148 });
149 }
150
151 pub fn validate_call(&self, key: &Token, data: &Everything, sc: &mut ScopeContext) {
152 if let Some(event) = self.get_event(key.as_str()) {
153 event.validate_call(data, sc);
154 }
155 }
156}
157
158impl FileHandler<Block> for Events {
159 fn subpath(&self) -> PathBuf {
160 PathBuf::from("events")
161 }
162
163 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
164 if !entry.filename().to_string_lossy().ends_with(".txt") {
165 return None;
166 }
167
168 PdxFile::read(entry, parser)
169 }
170
171 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
172 #[derive(Copy, Clone)]
173 enum Expecting {
174 Event,
175 ScriptedTrigger,
176 ScriptedEffect,
177 }
178
179 let mut expecting = Expecting::Event;
180
181 for item in block.drain() {
182 if let BlockItem::Field(Field(key, _, bv)) = item {
183 if key.is("namespace") {
184 if let Some(value) = bv.expect_into_value() {
185 self.namespaces.insert(value);
186 }
187 } else if key.is("scripted_trigger") || key.is("scripted_effect") {
188 let msg = format!("`{key}` should be used without `=`");
189 err(ErrorKey::ParseError).msg(msg).loc(key).push();
190 } else if let Some(block) = bv.into_block() {
191 match expecting {
192 Expecting::ScriptedTrigger => {
193 self.load_scripted_trigger(key, block);
194 expecting = Expecting::Event;
195 }
196 Expecting::ScriptedEffect => {
197 self.load_scripted_effect(key, block);
198 expecting = Expecting::Event;
199 }
200 Expecting::Event => {
201 self.load_event(key, block);
202 }
203 }
204 } else {
205 let msg = "unknown setting in event file";
206 err(ErrorKey::UnknownField).msg(msg).loc(key).push();
207 }
208 } else if let Some(key) = item.expect_value() {
209 if matches!(expecting, Expecting::Event) && key.is("scripted_trigger") {
210 if !Game::is_ck3() && !Game::is_eu5() {
211 let msg = "scripted triggers in event files are only for CK3 and EU5";
212 err(ErrorKey::WrongGame).msg(msg).loc(key).push();
213 }
214 expecting = Expecting::ScriptedTrigger;
215 } else if matches!(expecting, Expecting::Event) && key.is("scripted_effect") {
216 if !Game::is_ck3() && !Game::is_eu5() {
217 let msg = "scripted effects in event files are only for CK3 and EU5";
218 err(ErrorKey::WrongGame).msg(msg).loc(key).push();
219 }
220 expecting = Expecting::ScriptedEffect;
221 } else {
222 err(ErrorKey::Validation)
223 .msg("unexpected token")
224 .info("Did you forget an = ?")
225 .loc(key)
226 .push();
227 }
228 }
229 }
230 }
231}
232
233#[derive(Debug)]
234pub struct Event {
235 pub key: Token,
236 pub block: Block,
237 expects_scope: Scopes,
238 expects_from_token: Token,
239 visited: Mutex<TigerHashSet<Signature>>,
240}
241
242impl Event {
243 pub fn new(key: Token, block: Block) -> Self {
244 let (expects_scope, expects_from_token) = match Game::game() {
245 #[cfg(feature = "ck3")]
246 Game::Ck3 => crate::ck3::events::get_event_scope(&key, &block),
247 #[cfg(feature = "vic3")]
248 Game::Vic3 => crate::vic3::events::get_event_scope(&key, &block),
249 #[cfg(feature = "imperator")]
250 Game::Imperator => crate::imperator::events::get_event_scope(&key, &block),
251 #[cfg(feature = "eu5")]
252 Game::Eu5 => crate::eu5::events::get_event_scope(&key, &block),
253 #[cfg(feature = "hoi4")]
254 Game::Hoi4 => unimplemented!(),
255 };
256 let visited = Mutex::new(TigerHashSet::default());
257 Self { key, block, expects_scope, expects_from_token, visited }
258 }
259
260 pub fn validate(&self, data: &Everything) {
261 if let Some((namespace, _)) = self.key.as_str().split_once('.')
262 && !data.item_exists(Item::EventNamespace, namespace)
263 {
264 let msg = format!("event file should start with `namespace = {namespace}`");
265 let info = "otherwise the event won't be found in-game";
266 err(ErrorKey::EventNamespace).msg(msg).info(info).loc(&self.key).push();
267 }
268
269 let mut sc = ScopeContext::new(self.expects_scope, &self.expects_from_token);
270 sc.set_strict_scopes(false);
271 sc.set_source(&self.key);
272
273 match Game::game() {
274 #[cfg(feature = "ck3")]
275 Game::Ck3 => crate::ck3::events::validate_event(self, data, &mut sc),
276 #[cfg(feature = "vic3")]
277 Game::Vic3 => crate::vic3::events::validate_event(self, data, &mut sc),
278 #[cfg(feature = "imperator")]
279 Game::Imperator => crate::imperator::events::validate_event(self, data, &mut sc),
280 #[cfg(feature = "eu5")]
281 Game::Eu5 => crate::eu5::events::validate_event(self, data, &mut sc),
282 #[cfg(feature = "hoi4")]
283 Game::Hoi4 => unimplemented!(),
284 }
285 }
286
287 pub fn validate_call(&self, data: &Everything, sc: &mut ScopeContext) {
288 if !self.visited.lock().unwrap().insert(sc.signature(data)) {
289 return;
291 }
292 match Game::game() {
293 #[cfg(feature = "ck3")]
294 Game::Ck3 => crate::ck3::events::validate_event(self, data, sc),
295 #[cfg(feature = "vic3")]
296 Game::Vic3 => crate::vic3::events::validate_event(self, data, sc),
297 #[cfg(feature = "imperator")]
298 Game::Imperator => crate::imperator::events::validate_event(self, data, sc),
299 #[cfg(feature = "eu5")]
300 Game::Eu5 => crate::eu5::events::validate_event(self, data, sc),
301 #[cfg(feature = "hoi4")]
302 Game::Hoi4 => unimplemented!(),
303 }
304 }
305}