Skip to main content

proof_engine/worldgen/
artifacts.rs

1//! Artifact generation — items with histories tied to cultures and events.
2
3use super::Rng;
4use super::history::Civilization;
5use super::mythology::Myth;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum ArtifactType { Weapon, Armor, Jewelry, Tome, Relic, Instrument, Tool, Crown, Staff, Amulet }
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum ArtifactRarity { Common, Uncommon, Rare, Legendary, Mythic }
12
13#[derive(Debug, Clone)]
14pub struct Artifact {
15    pub id: u32,
16    pub name: String,
17    pub artifact_type: ArtifactType,
18    pub rarity: ArtifactRarity,
19    pub description: String,
20    pub origin_civ: u32,
21    pub creation_year: i32,
22    pub creator_name: String,
23    pub related_myth: Option<u32>,
24    pub power_level: f32,
25    pub properties: Vec<String>,
26}
27
28pub fn generate(civs: &[Civilization], myths: &[Myth], rng: &mut Rng) -> Vec<Artifact> {
29    let mut artifacts = Vec::new();
30    let mut next_id = 0u32;
31
32    for civ in civs {
33        let num = (civ.culture_score * 5.0) as usize + 1;
34        for _ in 0..num {
35            let atype = random_type(rng);
36            let rarity = if rng.coin(0.05) { ArtifactRarity::Mythic }
37                else if rng.coin(0.1) { ArtifactRarity::Legendary }
38                else if rng.coin(0.2) { ArtifactRarity::Rare }
39                else if rng.coin(0.3) { ArtifactRarity::Uncommon }
40                else { ArtifactRarity::Common };
41
42            let related_myth = myths.iter()
43                .filter(|m| m.source_civ == civ.id)
44                .next()
45                .map(|m| m.id);
46
47            let (name, desc, props) = generate_artifact_details(atype, rarity, &civ.name, rng);
48
49            artifacts.push(Artifact {
50                id: next_id,
51                name, artifact_type: atype, rarity, description: desc,
52                origin_civ: civ.id,
53                creation_year: civ.founding_year + rng.range_u32(0, 1000) as i32,
54                creator_name: format!("{}smith", &civ.name[..civ.name.len().min(4)]),
55                related_myth,
56                power_level: rarity_power(rarity) * rng.range_f32(0.8, 1.2),
57                properties: props,
58            });
59            next_id += 1;
60        }
61    }
62    artifacts
63}
64
65fn random_type(rng: &mut Rng) -> ArtifactType {
66    match rng.range_u32(0, 10) {
67        0 => ArtifactType::Weapon, 1 => ArtifactType::Armor, 2 => ArtifactType::Jewelry,
68        3 => ArtifactType::Tome, 4 => ArtifactType::Relic, 5 => ArtifactType::Instrument,
69        6 => ArtifactType::Tool, 7 => ArtifactType::Crown, 8 => ArtifactType::Staff,
70        _ => ArtifactType::Amulet,
71    }
72}
73
74fn rarity_power(r: ArtifactRarity) -> f32 {
75    match r {
76        ArtifactRarity::Common => 1.0, ArtifactRarity::Uncommon => 2.0,
77        ArtifactRarity::Rare => 4.0, ArtifactRarity::Legendary => 8.0, ArtifactRarity::Mythic => 16.0,
78    }
79}
80
81fn generate_artifact_details(atype: ArtifactType, rarity: ArtifactRarity, civ_name: &str, rng: &mut Rng) -> (String, String, Vec<String>) {
82    let prefixes = ["Shadow", "Storm", "Void", "Star", "Moon", "Sun", "Blood", "Soul", "Dream", "Fate"];
83    let prefix = prefixes[rng.next_u64() as usize % prefixes.len()];
84
85    let type_name = match atype {
86        ArtifactType::Weapon => "Blade", ArtifactType::Armor => "Shield",
87        ArtifactType::Jewelry => "Ring", ArtifactType::Tome => "Codex",
88        ArtifactType::Relic => "Fragment", ArtifactType::Instrument => "Horn",
89        ArtifactType::Tool => "Compass", ArtifactType::Crown => "Crown",
90        ArtifactType::Staff => "Staff", ArtifactType::Amulet => "Amulet",
91    };
92
93    let name = format!("{}{} of {}", prefix, type_name, civ_name);
94    let desc = format!("A {} {} forged in the {} tradition.", rarity_adj(rarity), type_name.to_lowercase(), civ_name);
95    let props = match rarity {
96        ArtifactRarity::Mythic => vec!["Reality-warping".to_string(), "Sentient".to_string(), "Indestructible".to_string()],
97        ArtifactRarity::Legendary => vec!["Elemental mastery".to_string(), "Soul-bound".to_string()],
98        ArtifactRarity::Rare => vec!["Enhanced".to_string(), "Self-repairing".to_string()],
99        ArtifactRarity::Uncommon => vec!["Durable".to_string()],
100        ArtifactRarity::Common => vec![],
101    };
102    (name, desc, props)
103}
104
105fn rarity_adj(r: ArtifactRarity) -> &'static str {
106    match r {
107        ArtifactRarity::Common => "simple", ArtifactRarity::Uncommon => "finely crafted",
108        ArtifactRarity::Rare => "exquisite", ArtifactRarity::Legendary => "legendary",
109        ArtifactRarity::Mythic => "mythic",
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    #[test]
117    fn test_generate_artifacts() {
118        let mut rng = Rng::new(42);
119        let civs = vec![Civilization {
120            id: 0, name: "TestCiv".to_string(), founding_year: -1000, collapse_year: None,
121            capital_settlement: 0, settlements: vec![0], population: 10000,
122            technology_level: 0.5, military_strength: 0.3, culture_score: 0.6,
123            trade_score: 0.3, government: super::super::history::GovernmentType::Monarchy,
124            religion: super::super::history::ReligionType::Polytheism,
125            relations: Vec::new(), historical_events: Vec::new(), traits: Vec::new(),
126        }];
127        let artifacts = generate(&civs, &[], &mut rng);
128        assert!(!artifacts.is_empty());
129    }
130}