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 && let Some(parent) = self.characters.get(token.as_str())
204 && let Some(mut cycle_vec) = self.check_ancestor_cycles(parent)
205 {
206 if &ch.key == cycle_vec.first().unwrap() {
208 let msg = "character is their own ancestor";
209 cycle_vec.reverse();
210 cycle_vec.pop();
211 let mut report = fatal(ErrorKey::Crash).msg(msg).loc(&ch.key);
212 for token in cycle_vec {
213 report = report.loc_msg(token, "from here");
214 }
215 report.push();
216 } else {
217 cycle_vec.push(token.clone());
218 ch.ancestor_state.store(AncestorState::Checked, Ordering::Release);
222 return Some(cycle_vec);
223 }
224 }
225 }
226 ch.ancestor_state.store(AncestorState::Checked, Ordering::Release);
227 None
228 }
229
230 pub fn check_pod_flags(&self, data: &Everything) {
231 for item in self.characters.values() {
232 if item.born_by(self.config_only_born) {
233 item.check_pod_flags(data);
234 }
235 }
236 }
237}
238
239impl FileHandler<Block> for Characters {
240 fn config(&mut self, config: &Block) {
241 if let Some(block) = config.get_field_block("characters")
242 && let Some(born) = block.get_field_value("only_born")
243 && let Ok(date) = Date::try_from(born)
244 {
245 self.config_only_born = Some(date);
246 }
247 }
248
249 fn subpath(&self) -> PathBuf {
250 PathBuf::from("history/characters")
251 }
252
253 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
254 if !entry.filename().to_string_lossy().ends_with(".txt") {
255 return None;
256 }
257
258 PdxFile::read(entry, parser)
259 }
260
261 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
262 for (key, block) in block.drain_definitions_warn() {
263 self.load_item(key, block);
264 }
265 }
266
267 fn finalize(&mut self) {
268 for item in self.characters.values() {
270 let opt_cycle_vec = self.check_ancestor_cycles(item);
271 assert!(opt_cycle_vec.is_none());
273 }
274 }
275}
276
277#[atomic_enum]
278enum AncestorState {
279 Unchecked,
280 Checking,
281 Checked,
282}
283
284#[derive(Debug)]
285pub struct Character {
286 key: Token,
287 block: Block,
288 ancestor_state: AtomicAncestorState,
290}
291
292impl Character {
293 pub fn new(key: Token, block: Block) -> Self {
294 Self { key, block, ancestor_state: AtomicAncestorState::new(AncestorState::Unchecked) }
295 }
296
297 pub fn born_by(&self, born_by: Option<Date>) -> bool {
298 if let Some(date) = born_by {
299 self.block.get_field_at_date("birth", date).is_some()
300 } else {
301 true
302 }
303 }
304
305 pub fn gender(&self) -> Gender {
306 Gender::from_female_bool(self.block.get_field_bool("female").unwrap_or(false))
307 }
308
309 pub fn is_alive(&self, date: Date) -> bool {
310 self.block.get_field_at_date("birth", date).is_some()
312 && self.block.get_field_at_date("death", date).is_none()
313 }
314
315 pub fn get_dynasty(&self, date: Date) -> Option<&Token> {
316 self.block.get_field_value_at_date("dynasty", date)
317 }
318
319 pub fn get_house(&self, date: Date) -> Option<&Token> {
320 self.block.get_field_value_at_date("dynasty_house", date)
321 }
322
323 pub fn get_culture(&self, date: Date) -> Option<&Token> {
324 self.block.get_field_value_at_date("culture", date)
325 }
326
327 pub fn get_faith(&self, date: Date) -> Option<&Token> {
328 self.block
329 .get_field_value_at_date("faith", date)
330 .or_else(|| self.block.get_field_value_at_date("religion", date))
331 }
332
333 fn validate_life_event(
334 date: Date,
335 gender: Gender,
336 key: &Token,
337 bv: &BV,
338 data: &Everything,
339 sc: &mut ScopeContext,
340 ) -> Option<(LifeEventType, Token)> {
341 use LifeEventType::*;
342
343 match bv {
344 BV::Value(value) => {
345 if matches!(key.as_str(), "trait" | "add_trait") && value.as_str() == "saint" {
346 return Some((Posthumous, value.clone()));
347 }
348
349 match key.as_str() {
350 "name" => {
351 data.localization.verify_name_exists(value, Severity::Warning);
352 return None;
353 }
354 "birth" => {
355 if !value.is("yes") && Date::from_str(value.as_str()).is_err() {
356 let msg = "expected `yes` or a date";
357 err(ErrorKey::Validation).msg(msg).loc(value).push();
358 }
359 return Some((Birth, key.clone()));
360 }
361 "death" => {
362 if !value.is("yes") && !value.is_date() {
363 data.verify_exists(Item::DeathReason, value);
364 }
365 return Some((Death, key.clone()));
366 }
367 "religion" | "faith" => {
369 data.verify_exists(Item::Faith, value);
370 return None;
371 }
372 "culture" => {
373 data.verify_exists(Item::Culture, value);
374 return None;
375 }
376 "trait" => {
377 data.verify_exists(Item::Trait, value);
378 return None;
379 }
380 "employer" => {
381 if value.is("0") {
382 return Some((Unemployed, key.clone()));
383 }
384 data.verify_exists(Item::Character, value);
385 if data.item_exists(Item::Character, value.as_str()) {
386 data.characters.verify_alive(value, date);
387 }
388 return Some((Employed, key.clone()));
389 }
390 "moved_to_pool" => {
391 if !value.is("yes") {
392 let msg = "expected `yes`";
393 err(ErrorKey::Validation).msg(msg).loc(value).push();
394 }
395 return Some((Unemployed, key.clone()));
396 }
397 "give_council_position" => {
398 data.verify_exists(Item::CouncilPosition, value);
399 return None;
400 }
401 "capital" => {
402 data.verify_exists(Item::Title, value);
403 if !value.as_str().starts_with("c_") {
404 err(ErrorKey::Validation)
405 .msg("capital must be a county")
406 .loc(value)
407 .push();
408 }
409 return None;
410 }
411 "add_spouse" | "add_matrilineal_spouse" => {
412 data.characters.verify_exists_gender(value, gender.flip());
413 if data.item_exists(Item::Character, value.as_str()) {
414 data.characters.verify_alive(value, date);
415 }
416 return Some((AddSpouse, value.clone()));
417 }
418 "add_same_sex_spouse" => {
419 data.characters.verify_exists_gender(value, gender);
420 if data.item_exists(Item::Character, value.as_str()) {
421 data.characters.verify_alive(value, date);
422 }
423 return Some((AddSpouse, value.clone()));
424 }
425 "add_concubine" => {
426 data.characters.verify_exists_gender(value, gender.flip());
427 if data.item_exists(Item::Character, value.as_str()) {
428 data.characters.verify_alive(value, date);
429 }
430 return None;
431 }
432 "remove_spouse" => return Some((RemoveSpouse, value.clone())),
433 "dynasty" => {
434 data.verify_exists(Item::Dynasty, value);
435 return None;
436 }
437 "dynasty_house" => {
438 data.verify_exists(Item::House, value);
439 return None;
440 }
441 _ => (),
442 }
443 }
444 BV::Block(block) => match key.as_str() {
445 "death" => {
446 let mut vd = Validator::new(block, data);
447 vd.req_field("death_reason");
448 vd.field_item("death_reason", Item::DeathReason);
449 vd.field_item("killer", Item::Character);
450 return Some((Death, key.clone()));
451 }
452 "effect" => {
453 validate_effect(block, data, sc, Tooltipped::No);
454 return None;
455 }
456 _ => (),
457 },
458 }
459
460 validate_effect_field(
462 Lowercase::empty(),
463 key,
464 Comparator::Equals(Single),
465 bv,
466 data,
467 sc,
468 Tooltipped::No,
469 &mut SpecialTokens::none(),
470 );
471
472 None
473 }
474
475 fn validate_life(character: &Token, life_events: Vec<LifeEvent>) {
476 let mut birth = None;
477 let mut death = None;
478 let mut spouses = TigerHashSet::<Token>::default();
479 let mut employed = false;
480
481 for LifeEvent { date, index: _, token, event } in life_events {
482 use LifeEventType::*;
483
484 if birth.is_none() && event != Birth {
485 let msg = format!("{character} was not born yet on {date}");
486 let mut loc = token.loc;
487 loc.column = 0;
488 warn(ErrorKey::History).msg(msg).loc(loc).push();
489 }
490
491 if let Some((death_date, death_loc)) = death
492 && event != Posthumous
493 {
494 let msg = format!(
495 "{character} was not alive on {date}, had already died on {death_date}"
496 );
497 let mut loc = token.loc;
498 loc.column = 0;
499 warn(ErrorKey::History).msg(msg).loc(loc).loc_msg(death_loc, "from here").push();
500 }
501
502 match event {
503 Birth => {
504 let mut loc = token.loc;
505 loc.column = 0;
506
507 if let Some((birth_date, birth_loc)) = birth {
508 let msg = format!(
509 "{character} couldn't be born again on {date}, was born already on {birth_date}"
510 );
511 warn(ErrorKey::History)
512 .msg(msg)
513 .loc(loc)
514 .loc_msg(birth_loc, "from here")
515 .push();
516 }
517 birth = Some((date, loc));
518 }
519 AddSpouse => {
520 if !spouses.insert(token.clone()) {
521 let msg = format!("{character} already had {token} as a spouse on {date}");
522 let curr_token = spouses.get(&token).unwrap();
523 warn(ErrorKey::History)
524 .msg(msg)
525 .loc(token)
526 .loc_msg(curr_token, "from here")
527 .push();
528 }
529 }
530 RemoveSpouse => {
531 if !spouses.remove(&token) {
532 let msg = format!("{character} did not have {token} as a spouse on {date}");
533 warn(ErrorKey::History).msg(msg).loc(token).push();
534 }
535 }
536 Employed => employed = true,
537 Unemployed => {
538 if !employed {
539 let msg = format!("{character} was unemployed anyway on {date}");
540 untidy(ErrorKey::History).msg(msg).loc(token).push();
541 }
542 employed = false;
543 }
544 Death => {
545 let mut loc = token.loc;
546 loc.column = 0;
547 death = Some((date, loc));
548 }
549 Posthumous => {
550 if death.is_none() {
551 let msg = format!("{character} had not died yet on {date}");
552 warn(ErrorKey::History).msg(msg).loc(token).push();
553 }
554 }
555 }
556 }
557 }
558
559 fn validate(&self, data: &Everything) {
560 let mut vd = Validator::new(&self.block, data);
561 let mut sc = ScopeContext::new(Scopes::Character, &self.key);
562
563 if self.key.as_str().contains('.') {
564 let msg =
565 format!("`character:{}` will not work because of the dot in the id", &self.key);
566 let info = "script code will not be able to refer to this character";
567 warn(ErrorKey::CharacterId).msg(msg).info(info).loc(&self.key).push();
568 }
569
570 vd.req_field("name");
571 if let Some(name) = vd.field_value("name") {
572 data.localization.verify_name_exists(name, Severity::Warning);
573 }
574
575 vd.field_item("dna", Item::Dna);
576 vd.field_bool("female");
577 vd.field_integer("martial");
578 vd.field_integer("prowess");
579 vd.field_integer("diplomacy");
580 vd.field_integer("intrigue");
581 vd.field_integer("stewardship");
582 vd.field_integer("learning");
583 vd.multi_field_item("trait", Item::Trait);
584
585 if let Some(ch) = vd.field_value("father") {
586 data.characters.verify_exists_gender(ch, Gender::Male);
587 }
588
589 if let Some(ch) = vd.field_value("mother") {
590 data.characters.verify_exists_gender(ch, Gender::Female);
591 }
592
593 vd.field_bool("disallow_random_traits");
594
595 vd.field_item("religion", Item::Faith);
597 vd.field_item("faith", Item::Faith);
598
599 vd.field_item("culture", Item::Culture);
600
601 vd.field_item("dynasty", Item::Dynasty);
602 vd.field_item("dynasty_house", Item::House);
603
604 vd.field_item("give_nickname", Item::Nickname);
605 vd.field_item("sexuality", Item::Sexuality);
606 vd.field_numeric("health");
607 vd.field_numeric("fertility");
608
609 vd.field_validated_block("portrait_override", |block, data| {
610 let mut vd = Validator::new(block, data);
611 vd.field_validated_block(
612 "portrait_modifier_overrides",
613 validate_portrait_modifier_overrides,
614 );
615 vd.field_validated_block("hair", validate_color);
616 });
617
618 let mut life_events = Vec::new();
619 let gender = Gender::from_female_bool(self.block.get_field_bool("female").unwrap_or(false));
620 vd.validate_history_blocks(|date, _key, block, data| {
621 for (index, (key, bv)) in block.iter_assignments_and_definitions_warn().enumerate() {
622 if let Some((life_event_type, token)) =
623 Self::validate_life_event(date, gender, key, bv, data, &mut sc)
624 {
625 life_events.push(LifeEvent { date, index, event: life_event_type, token });
626 }
627 }
628 });
629
630 life_events.sort_unstable();
631 Self::validate_life(&self.key, life_events);
632 }
633
634 fn check_pod_flags(&self, _data: &Everything) {
635 if self.block.has_key("dna")
636 && self.has_trait("nosferatu")
637 && !self.has_flag("had_POD_character_nosferatu_looks")
638 && !self.key.is("791762")
639 {
640 let msg = "nosferatu with predefined dna lacks had_POD_character_nosferatu_looks";
641 err(ErrorKey::PrincesOfDarkness).msg(msg).loc(&self.key).push();
642 }
643 }
644
645 fn has_flag(&self, flag: &str) -> bool {
646 for (key, block) in self.block.iter_definitions() {
647 if key.is_date() {
648 if block_has_flag(block, flag) {
649 return true;
650 }
651 for block in block.get_field_blocks("effect") {
652 if block_has_flag(block, flag) {
653 return true;
654 }
655 }
656 }
657 }
658 false
659 }
660
661 fn has_trait(&self, tr: &str) -> bool {
662 for token in self.block.get_field_values("trait") {
663 if token.is(tr) {
664 return true;
665 }
666 }
667 for (key, block) in self.block.iter_definitions() {
668 if key.is_date() {
669 for token in block.get_field_values("add_trait") {
670 if token.is(tr) {
671 return true;
672 }
673 }
674 if let Some(block) = block.get_field_block("effect") {
675 for token in block.get_field_values("add_trait") {
676 if token.is(tr) {
677 return true;
678 }
679 }
680 }
681 }
682 }
683 false
684 }
685}
686
687fn block_has_flag(block: &Block, flag: &str) -> bool {
688 for token in block.get_field_values("add_character_flag") {
689 if token.is(flag) {
690 return true;
691 }
692 }
693 for block in block.get_field_blocks("add_character_flag") {
694 if block.field_value_is("flag", flag) {
695 return true;
696 }
697 }
698 false
699}
700
701#[derive(Debug)]
702enum LifeEventType {
703 Birth,
705 AddSpouse,
706 RemoveSpouse,
707 Employed,
708 Unemployed,
710 Death,
712 Posthumous,
713 }
715
716impl PartialEq for LifeEventType {
717 fn eq(&self, other: &Self) -> bool {
718 std::mem::discriminant(self) == std::mem::discriminant(other)
719 }
720}
721
722impl Eq for LifeEventType {}
723
724#[derive(Debug)]
725struct LifeEvent {
726 date: Date,
727 index: usize,
728 event: LifeEventType,
729 token: Token,
730}
731
732impl PartialEq for LifeEvent {
733 fn eq(&self, other: &Self) -> bool {
734 self.date == other.date && self.index == other.index
735 }
736}
737
738impl Eq for LifeEvent {}
739
740impl Ord for LifeEvent {
741 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
742 match self.date.cmp(&other.date) {
743 std::cmp::Ordering::Equal => self.index.cmp(&other.index),
744 other => other,
745 }
746 }
747}
748
749impl PartialOrd for LifeEvent {
750 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
751 Some(self.cmp(other))
752 }
753}