use serde::{Deserialize, Serialize};
use crate::types::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvestigationFragment {
pub id: String,
pub domain: InvestigationDomain,
pub reader: CharacterId,
pub revelation: String,
pub discovered: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum InvestigationDomain {
Medical,
Financial,
LandGrant,
DeathRegister,
FirePattern,
Terrain,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvestigationState {
pub fragments: Vec<InvestigationFragment>,
pub convergence_reached: bool,
pub domains_read: Vec<InvestigationDomain>,
}
impl InvestigationState {
pub fn read_fragment(&mut self, fragment_id: &str, reader: &CharacterId) -> Option<&str> {
if let Some(frag) = self.fragments.iter_mut().find(|f| f.id == fragment_id) {
if frag.reader == *reader && !frag.discovered {
frag.discovered = true;
if !self.domains_read.contains(&frag.domain) {
self.domains_read.push(frag.domain);
}
return Some(&frag.revelation);
}
}
None
}
pub fn domain_read(&self, domain: InvestigationDomain) -> bool {
self.domains_read.contains(&domain)
}
pub fn check_convergence(&mut self, required: &[InvestigationDomain]) -> bool {
let all_read = required.iter().all(|d| self.domains_read.contains(d));
if all_read {
self.convergence_reached = true;
}
all_read
}
pub fn discovered_count(&self) -> usize {
self.fragments.iter().filter(|f| f.discovered).count()
}
}
pub fn burned_mission_investigation() -> InvestigationState {
InvestigationState {
fragments: vec![
InvestigationFragment {
id: "medical_records".to_string(),
domain: InvestigationDomain::Medical,
reader: CharacterId::new("ada"),
revelation: "The mission's medical records show treatment patterns \
that predate the current fever by decades. The same water \
source, the same symptoms, the same communities.".to_string(),
discovered: false,
},
InvestigationFragment {
id: "financial_transfers".to_string(),
domain: InvestigationDomain::Financial,
reader: CharacterId::new("eli"),
revelation: "Financial documents show the mission's original land \
grant was transferred, amended, and 'lost' through a \
chain of territorial re-filings. The money trail predates \
the railroad by forty years.".to_string(),
discovered: false,
},
InvestigationFragment {
id: "land_grants".to_string(),
domain: InvestigationDomain::LandGrant,
reader: CharacterId::new("galen"),
revelation: "The mission's land grants prove the original claim \
covered the territory the rail is now claiming. The \
grants were never legally voided — they were burned.".to_string(),
discovered: false,
},
InvestigationFragment {
id: "death_register".to_string(),
domain: InvestigationDomain::DeathRegister,
reader: CharacterId::new("miriam"),
revelation: "More names in the death register than markers in the \
cemetery. People were disappeared, not just killed. \
The count doesn't match. It has never matched.".to_string(),
discovered: false,
},
InvestigationFragment {
id: "fire_pattern".to_string(),
domain: InvestigationDomain::FirePattern,
reader: CharacterId::new("lucien"),
revelation: "This was a job. Better than mine, but the same language. \
Directed ignition. Controlled enough to destroy the record \
rooms while leaving the walls standing long enough for \
people to believe the 'accident' story.".to_string(),
discovered: false,
},
InvestigationFragment {
id: "water_terrain".to_string(),
domain: InvestigationDomain::Terrain,
reader: CharacterId::new("rosa"),
revelation: "The well sits on the valley's anchor water table. \
The mission founders built here because of the water. \
Whoever controls this well controls the valley. That \
has been true for eighty years.".to_string(),
discovered: false,
},
],
convergence_reached: false,
domains_read: Vec::new(),
}
}