1use std::fmt::{Display, Formatter};
4use std::path::PathBuf;
5use std::str::FromStr;
6use std::sync::atomic::Ordering;
7
8use atomic_enum::atomic_enum;
9
10use crate::block::{BV, Block, Comparator, Eq::*};
11use crate::ck3::data::houses::House;
12use crate::ck3::validate::validate_portrait_modifier_overrides;
13use crate::context::ScopeContext;
14use crate::date::Date;
15use crate::effect::{validate_effect, validate_effect_field};
16use crate::everything::Everything;
17use crate::fileset::{FileEntry, FileHandler};
18use crate::helpers::{TigerHashMap, TigerHashSet};
19use crate::item::Item;
20use crate::lowercase::Lowercase;
21use crate::parse::ParserMemory;
22use crate::pdxfile::PdxFile;
23use crate::report::{ErrorKey, Severity, err, fatal, untidy, warn};
24use crate::scopes::Scopes;
25use crate::special_tokens::SpecialTokens;
26use crate::token::Token;
27use crate::tooltipped::Tooltipped;
28use crate::validate::validate_color;
29use crate::validator::Validator;
30use crate::variables::Variables;
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
33pub enum Gender {
34 Male,
35 Female,
36}
37
38impl Gender {
39 fn from_female_bool(b: bool) -> Self {
40 if b { Gender::Female } else { Gender::Male }
41 }
42
43 fn flip(self) -> Self {
44 match self {
45 Gender::Male => Gender::Female,
46 Gender::Female => Gender::Male,
47 }
48 }
49}
50
51impl Display for Gender {
52 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
53 match *self {
54 Gender::Male => write!(f, "male"),
55 Gender::Female => write!(f, "female"),
56 }
57 }
58}
59
60#[derive(Debug, Default)]
61#[allow(clippy::struct_field_names)]
62pub struct Characters {
63 config_only_born: Option<Date>,
64
65 characters: TigerHashMap<&'static str, Character>,
66
67 duplicates: Vec<Character>,
70}
71
72impl Characters {
73 fn load_item(&mut self, key: Token, block: Block) {
74 if let Some(other) = self.characters.get(key.as_str()) {
75 if self.config_only_born.is_none()
76 || self
77 .config_only_born
78 .and_then(|date| block.get_field_at_date("birth", date))
79 .is_some()
80 {
81 err(ErrorKey::DuplicateCharacter)
82 .strong()
83 .msg("duplicate character id")
84 .info("this will create two characters with the same id")
85 .loc(&other.key)
86 .loc_msg(&key, "duplicate")
87 .push();
88 self.duplicates.push(Character::new(key, block));
89 }
90 } else {
91 self.characters.insert(key.as_str(), Character::new(key, block));
92 }
93 }
94
95 pub fn verify_exists_gender(&self, item: &Token, gender: Gender) {
96 if let Some(ch) = self.characters.get(item.as_str()) {
97 if gender != ch.gender() {
98 let msg = format!("character is not {gender}");
99 err(ErrorKey::WrongGender).msg(msg).loc(item).push();
100 }
101 } else {
102 let msg = format!("character {item} not defined in history/characters/");
103 err(ErrorKey::MissingItem).msg(msg).loc(item).push();
104 }
105 }
106
107 pub fn scan_variables(&self, registry: &mut Variables) {
108 for item in self.characters.values() {
109 registry.scan(&item.block);
110 }
111 for item in &self.duplicates {
112 registry.scan(&item.block);
113 }
114 }
115
116 pub fn exists(&self, key: &str) -> bool {
117 self.characters.contains_key(key)
118 }
119
120 pub fn iter_keys(&self) -> impl Iterator<Item = &Token> {
121 self.characters.values().map(|ch| &ch.key).chain(self.duplicates.iter().map(|ch| &ch.key))
122 }
123
124 pub fn is_alive(&self, item: &Token, date: Date) -> bool {
125 if let Some(item) = self.characters.get(item.as_str()) {
126 item.is_alive(date)
127 } else {
128 false
129 }
130 }
131
132 pub fn verify_alive(&self, item: &Token, date: Date) {
133 if !self.is_alive(item, date) {
134 let msg = format!("{item} is not alive on {date}");
135 warn(ErrorKey::History).msg(msg).loc(item).push();
136 }
137 }
138
139 pub fn get_dynasty<'a>(
140 &'a self,
141 id: &Token,
142 date: Date,
143 data: &'a Everything,
144 ) -> Option<&'a Token> {
145 self.characters.get(id.as_str()).and_then(|ch| {
146 ch.get_dynasty(date).or_else(|| {
147 ch.get_house(date).and_then(|house| House::get_dynasty(house.as_str(), data))
148 })
149 })
150 }
151
152 pub fn get_house(&self, id: &Token, date: Date) -> Option<&Token> {
153 self.characters.get(id.as_str()).and_then(|ch| ch.get_house(date))
154 }
155
156 pub fn get_culture(&self, id: &Token, date: Date) -> Option<&Token> {
157 self.characters.get(id.as_str()).and_then(|ch| ch.get_culture(date))
158 }
159
160 pub fn get_faith(&self, id: &Token, date: Date) -> Option<&Token> {
161 self.characters.get(id.as_str()).and_then(|ch| ch.get_faith(date))
162 }
163
164 pub fn validate(&self, data: &Everything) {
165 for item in self.characters.values() {
166 if item.born_by(self.config_only_born) {
167 item.validate(data);
168 }
169 }
170 for item in &self.duplicates {
171 if item.born_by(self.config_only_born) {
172 item.validate(data);
173 }
174 }
175 }
176
177 fn check_ancestor_cycles(&self, ch: &Character) -> Option<Vec<Token>> {
186 match ch.ancestor_state.load(Ordering::Acquire) {
187 AncestorState::Unchecked => {
188 ch.ancestor_state.store(AncestorState::Checking, Ordering::Release);
189 }
190 AncestorState::Checking => {
191 return Some(vec![ch.key.clone()]);
193 }
194 AncestorState::Checked => {
195 return None;
198 }
199 }
200
201 for field in &["father", "mother"] {
202 if let Some(token) = ch.block.get_field_value(field) {
203 if let Some(parent) = self.characters.get(token.as_str()) {
204 if let Some(mut cycle_vec) = self.check_ancestor_cycles(parent) {
205 if &ch.key == cycle_vec.first().unwrap() {
207 let msg = "character is their own ancestor";
208 cycle_vec.reverse();
209 cycle_vec.pop();
210 let mut report = fatal(ErrorKey::Crash).msg(msg).loc(&ch.key);
211 for token in cycle_vec {
212 report = report.loc_msg(token, "from here");
213 }
214 report.push();
215 } else {
216 cycle_vec.push(token.clone());
217 ch.ancestor_state.store(AncestorState::Checked, Ordering::Release);
221 return Some(cycle_vec);
222 }
223 }
224 }
225 }
226 }
227 ch.ancestor_state.store(AncestorState::Checked, Ordering::Release);
228 None
229 }
230
231 pub fn check_pod_flags(&self, data: &Everything) {
232 for item in self.characters.values() {
233 if item.born_by(self.config_only_born) {
234 item.check_pod_flags(data);
235 }
236 }
237 }
238}
239
240impl FileHandler<Block> for Characters {
241 fn config(&mut self, config: &Block) {
242 if let Some(block) = config.get_field_block("characters") {
243 if let Some(born) = block.get_field_value("only_born") {
244 if let Ok(date) = Date::try_from(born) {
245 self.config_only_born = Some(date);
246 }
247 }
248 }
249 }
250
251 fn subpath(&self) -> PathBuf {
252 PathBuf::from("history/characters")
253 }
254
255 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
256 if !entry.filename().to_string_lossy().ends_with(".txt") {
257 return None;
258 }
259
260 PdxFile::read(entry, parser)
261 }
262
263 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
264 for (key, block) in block.drain_definitions_warn() {
265 self.load_item(key, block);
266 }
267 }
268
269 fn finalize(&mut self) {
270 for item in self.characters.values() {
272 let opt_cycle_vec = self.check_ancestor_cycles(item);
273 assert!(opt_cycle_vec.is_none());
275 }
276 }
277}
278
279#[atomic_enum]
280enum AncestorState {
281 Unchecked,
282 Checking,
283 Checked,
284}
285
286#[derive(Debug)]
287pub struct Character {
288 key: Token,
289 block: Block,
290 ancestor_state: AtomicAncestorState,
292}
293
294impl Character {
295 pub fn new(key: Token, block: Block) -> Self {
296 Self { key, block, ancestor_state: AtomicAncestorState::new(AncestorState::Unchecked) }
297 }
298
299 pub fn born_by(&self, born_by: Option<Date>) -> bool {
300 if let Some(date) = born_by {
301 self.block.get_field_at_date("birth", date).is_some()
302 } else {
303 true
304 }
305 }
306
307 pub fn gender(&self) -> Gender {
308 Gender::from_female_bool(self.block.get_field_bool("female").unwrap_or(false))
309 }
310
311 pub fn is_alive(&self, date: Date) -> bool {
312 self.block.get_field_at_date("birth", date).is_some()
314 && self.block.get_field_at_date("death", date).is_none()
315 }
316
317 pub fn get_dynasty(&self, date: Date) -> Option<&Token> {
318 self.block.get_field_value_at_date("dynasty", date)
319 }
320
321 pub fn get_house(&self, date: Date) -> Option<&Token> {
322 self.block.get_field_value_at_date("dynasty_house", date)
323 }
324
325 pub fn get_culture(&self, date: Date) -> Option<&Token> {
326 self.block.get_field_value_at_date("culture", date)
327 }
328
329 pub fn get_faith(&self, date: Date) -> Option<&Token> {
330 self.block
331 .get_field_value_at_date("faith", date)
332 .or_else(|| self.block.get_field_value_at_date("religion", date))
333 }
334
335 fn validate_life_event(
336 date: Date,
337 gender: Gender,
338 key: &Token,
339 bv: &BV,
340 data: &Everything,
341 sc: &mut ScopeContext,
342 ) -> Option<(LifeEventType, Token)> {
343 use LifeEventType::*;
344
345 match bv {
346 BV::Value(value) => {
347 if matches!(key.as_str(), "trait" | "add_trait") && value.as_str() == "saint" {
348 return Some((Posthumous, value.clone()));
349 }
350
351 match key.as_str() {
352 "name" => {
353 data.localization.verify_name_exists(value, Severity::Warning);
354 return None;
355 }
356 "birth" => {
357 if !value.is("yes") && Date::from_str(value.as_str()).is_err() {
358 let msg = "expected `yes` or a date";
359 err(ErrorKey::Validation).msg(msg).loc(value).push();
360 }
361 return Some((Birth, key.clone()));
362 }
363 "death" => {
364 if !value.is("yes") && !value.is_date() {
365 data.verify_exists(Item::DeathReason, value);
366 }
367 return Some((Death, key.clone()));
368 }
369 "religion" | "faith" => {
371 data.verify_exists(Item::Faith, value);
372 return None;
373 }
374 "culture" => {
375 data.verify_exists(Item::Culture, value);
376 return None;
377 }
378 "trait" => {
379 data.verify_exists(Item::Trait, value);
380 return None;
381 }
382 "employer" => {
383 if value.is("0") {
384 return Some((Unemployed, key.clone()));
385 }
386 data.verify_exists(Item::Character, value);
387 if data.item_exists(Item::Character, value.as_str()) {
388 data.characters.verify_alive(value, date);
389 }
390 return Some((Employed, key.clone()));
391 }
392 "moved_to_pool" => {
393 if !value.is("yes") {
394 let msg = "expected `yes`";
395 err(ErrorKey::Validation).msg(msg).loc(value).push();
396 }
397 return Some((Unemployed, key.clone()));
398 }
399 "give_council_position" => {
400 data.verify_exists(Item::CouncilPosition, value);
401 return None;
402 }
403 "capital" => {
404 data.verify_exists(Item::Title, value);
405 if !value.as_str().starts_with("c_") {
406 err(ErrorKey::Validation)
407 .msg("capital must be a county")
408 .loc(value)
409 .push();
410 }
411 return None;
412 }
413 "add_spouse" | "add_matrilineal_spouse" => {
414 data.characters.verify_exists_gender(value, gender.flip());
415 if data.item_exists(Item::Character, value.as_str()) {
416 data.characters.verify_alive(value, date);
417 }
418 return Some((AddSpouse, value.clone()));
419 }
420 "add_same_sex_spouse" => {
421 data.characters.verify_exists_gender(value, gender);
422 if data.item_exists(Item::Character, value.as_str()) {
423 data.characters.verify_alive(value, date);
424 }
425 return Some((AddSpouse, value.clone()));
426 }
427 "add_concubine" => {
428 data.characters.verify_exists_gender(value, gender.flip());
429 if data.item_exists(Item::Character, value.as_str()) {
430 data.characters.verify_alive(value, date);
431 }
432 return None;
433 }
434 "remove_spouse" => return Some((RemoveSpouse, value.clone())),
435 "dynasty" => {
436 data.verify_exists(Item::Dynasty, value);
437 return None;
438 }
439 "dynasty_house" => {
440 data.verify_exists(Item::House, value);
441 return None;
442 }
443 _ => (),
444 }
445 }
446 BV::Block(block) => match key.as_str() {
447 "death" => {
448 let mut vd = Validator::new(block, data);
449 vd.req_field("death_reason");
450 vd.field_item("death_reason", Item::DeathReason);
451 vd.field_item("killer", Item::Character);
452 return Some((Death, key.clone()));
453 }
454 "effect" => {
455 validate_effect(block, data, sc, Tooltipped::No);
456 return None;
457 }
458 _ => (),
459 },
460 }
461
462 validate_effect_field(
464 Lowercase::empty(),
465 key,
466 Comparator::Equals(Single),
467 bv,
468 data,
469 sc,
470 Tooltipped::No,
471 &mut SpecialTokens::none(),
472 );
473
474 None
475 }
476
477 fn validate_life(character: &Token, life_events: Vec<LifeEvent>) {
478 let mut birth = None;
479 let mut death = None;
480 let mut spouses = TigerHashSet::<Token>::default();
481 let mut employed = false;
482
483 for LifeEvent { date, index: _, token, event } in life_events {
484 use LifeEventType::*;
485
486 if birth.is_none() && event != Birth {
487 let msg = format!("{character} was not born yet on {date}");
488 let mut loc = token.loc;
489 loc.column = 0;
490 warn(ErrorKey::History).msg(msg).loc(loc).push();
491 }
492
493 if let Some((death_date, death_loc)) = death {
494 if event != Posthumous {
495 let msg = format!(
496 "{character} was not alive on {date}, had already died on {death_date}"
497 );
498 let mut loc = token.loc;
499 loc.column = 0;
500 warn(ErrorKey::History)
501 .msg(msg)
502 .loc(loc)
503 .loc_msg(death_loc, "from here")
504 .push();
505 }
506 }
507
508 match event {
509 Birth => {
510 let mut loc = token.loc;
511 loc.column = 0;
512
513 if let Some((birth_date, birth_loc)) = birth {
514 let msg = format!(
515 "{character} couldn't be born again on {date}, was born already on {birth_date}"
516 );
517 warn(ErrorKey::History)
518 .msg(msg)
519 .loc(loc)
520 .loc_msg(birth_loc, "from here")
521 .push();
522 }
523 birth = Some((date, loc));
524 }
525 AddSpouse => {
526 if !spouses.insert(token.clone()) {
527 let msg = format!("{character} already had {token} as a spouse on {date}");
528 let curr_token = spouses.get(&token).unwrap();
529 warn(ErrorKey::History)
530 .msg(msg)
531 .loc(token)
532 .loc_msg(curr_token, "from here")
533 .push();
534 }
535 }
536 RemoveSpouse => {
537 if !spouses.remove(&token) {
538 let msg = format!("{character} did not have {token} as a spouse on {date}");
539 warn(ErrorKey::History).msg(msg).loc(token).push();
540 }
541 }
542 Employed => employed = true,
543 Unemployed => {
544 if !employed {
545 let msg = format!("{character} was unemployed anyway on {date}");
546 untidy(ErrorKey::History).msg(msg).loc(token).push();
547 }
548 employed = false;
549 }
550 Death => {
551 let mut loc = token.loc;
552 loc.column = 0;
553 death = Some((date, loc));
554 }
555 Posthumous => {
556 if death.is_none() {
557 let msg = format!("{character} had not died yet on {date}");
558 warn(ErrorKey::History).msg(msg).loc(token).push();
559 }
560 }
561 }
562 }
563 }
564
565 fn validate(&self, data: &Everything) {
566 let mut vd = Validator::new(&self.block, data);
567 let mut sc = ScopeContext::new(Scopes::Character, &self.key);
568
569 if self.key.as_str().contains('.') {
570 let msg =
571 format!("`character:{}` will not work because of the dot in the id", &self.key);
572 let info = "script code will not be able to refer to this character";
573 warn(ErrorKey::CharacterId).msg(msg).info(info).loc(&self.key).push();
574 }
575
576 vd.req_field("name");
577 if let Some(name) = vd.field_value("name") {
578 data.localization.verify_name_exists(name, Severity::Warning);
579 }
580
581 vd.field_item("dna", Item::Dna);
582 vd.field_bool("female");
583 vd.field_integer("martial");
584 vd.field_integer("prowess");
585 vd.field_integer("diplomacy");
586 vd.field_integer("intrigue");
587 vd.field_integer("stewardship");
588 vd.field_integer("learning");
589 vd.multi_field_item("trait", Item::Trait);
590
591 if let Some(ch) = vd.field_value("father") {
592 data.characters.verify_exists_gender(ch, Gender::Male);
593 }
594
595 if let Some(ch) = vd.field_value("mother") {
596 data.characters.verify_exists_gender(ch, Gender::Female);
597 }
598
599 vd.field_bool("disallow_random_traits");
600
601 vd.field_item("religion", Item::Faith);
603 vd.field_item("faith", Item::Faith);
604
605 vd.field_item("culture", Item::Culture);
606
607 vd.field_item("dynasty", Item::Dynasty);
608 vd.field_item("dynasty_house", Item::House);
609
610 vd.field_item("give_nickname", Item::Nickname);
611 vd.field_item("sexuality", Item::Sexuality);
612 vd.field_numeric("health");
613 vd.field_numeric("fertility");
614
615 vd.field_validated_block("portrait_override", |block, data| {
616 let mut vd = Validator::new(block, data);
617 vd.field_validated_block(
618 "portrait_modifier_overrides",
619 validate_portrait_modifier_overrides,
620 );
621 vd.field_validated_block("hair", validate_color);
622 });
623
624 let mut life_events = Vec::new();
625 let gender = Gender::from_female_bool(self.block.get_field_bool("female").unwrap_or(false));
626 vd.validate_history_blocks(|date, _key, block, data| {
627 for (index, (key, bv)) in block.iter_assignments_and_definitions_warn().enumerate() {
628 if let Some((life_event_type, token)) =
629 Self::validate_life_event(date, gender, key, bv, data, &mut sc)
630 {
631 life_events.push(LifeEvent { date, index, event: life_event_type, token });
632 }
633 }
634 });
635
636 life_events.sort_unstable();
637 Self::validate_life(&self.key, life_events);
638 }
639
640 fn check_pod_flags(&self, _data: &Everything) {
641 if self.block.has_key("dna")
642 && self.has_trait("nosferatu")
643 && !self.has_flag("had_POD_character_nosferatu_looks")
644 && !self.key.is("791762")
645 {
646 let msg = "nosferatu with predefined dna lacks had_POD_character_nosferatu_looks";
647 err(ErrorKey::PrincesOfDarkness).msg(msg).loc(&self.key).push();
648 }
649 }
650
651 fn has_flag(&self, flag: &str) -> bool {
652 for (key, block) in self.block.iter_definitions() {
653 if key.is_date() {
654 if block_has_flag(block, flag) {
655 return true;
656 }
657 for block in block.get_field_blocks("effect") {
658 if block_has_flag(block, flag) {
659 return true;
660 }
661 }
662 }
663 }
664 false
665 }
666
667 fn has_trait(&self, tr: &str) -> bool {
668 for token in self.block.get_field_values("trait") {
669 if token.is(tr) {
670 return true;
671 }
672 }
673 for (key, block) in self.block.iter_definitions() {
674 if key.is_date() {
675 for token in block.get_field_values("add_trait") {
676 if token.is(tr) {
677 return true;
678 }
679 }
680 if let Some(block) = block.get_field_block("effect") {
681 for token in block.get_field_values("add_trait") {
682 if token.is(tr) {
683 return true;
684 }
685 }
686 }
687 }
688 }
689 false
690 }
691}
692
693fn block_has_flag(block: &Block, flag: &str) -> bool {
694 for token in block.get_field_values("add_character_flag") {
695 if token.is(flag) {
696 return true;
697 }
698 }
699 for block in block.get_field_blocks("add_character_flag") {
700 if block.field_value_is("flag", flag) {
701 return true;
702 }
703 }
704 false
705}
706
707#[derive(Debug)]
708enum LifeEventType {
709 Birth,
711 AddSpouse,
712 RemoveSpouse,
713 Employed,
714 Unemployed,
716 Death,
718 Posthumous,
719 }
721
722impl PartialEq for LifeEventType {
723 fn eq(&self, other: &Self) -> bool {
724 std::mem::discriminant(self) == std::mem::discriminant(other)
725 }
726}
727
728impl Eq for LifeEventType {}
729
730#[derive(Debug)]
731struct LifeEvent {
732 date: Date,
733 index: usize,
734 event: LifeEventType,
735 token: Token,
736}
737
738impl PartialEq for LifeEvent {
739 fn eq(&self, other: &Self) -> bool {
740 self.date == other.date && self.index == other.index
741 }
742}
743
744impl Eq for LifeEvent {}
745
746impl Ord for LifeEvent {
747 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
748 match self.date.cmp(&other.date) {
749 std::cmp::Ordering::Equal => self.index.cmp(&other.index),
750 other => other,
751 }
752 }
753}
754
755impl PartialOrd for LifeEvent {
756 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
757 Some(self.cmp(other))
758 }
759}