veloren_rtsim/data/
architect.rs

1use std::collections::VecDeque;
2
3use common::{
4    comp,
5    resources::TimeOfDay,
6    rtsim::{FactionId, Role},
7};
8use enum_map::EnumMap;
9use serde::{Deserialize, Serialize};
10
11use super::Npc;
12
13#[derive(Clone, Serialize, Deserialize)]
14pub struct Death {
15    pub time: TimeOfDay,
16    pub body: comp::Body,
17    pub role: Role,
18    pub faction: Option<FactionId>,
19}
20
21#[derive(enum_map::Enum)]
22pub enum TrackedPopulation {
23    Adventurers,
24    Merchants,
25    Guards,
26    Captains,
27    OtherTownNpcs,
28
29    PirateCaptains,
30    Pirates,
31    Cultists,
32
33    GigasFrost,
34    GigasFire,
35    OtherMonsters,
36
37    CloudWyvern,
38    FrostWyvern,
39    SeaWyvern,
40    FlameWyvern,
41    WealdWyvern,
42    Phoenix,
43    Roc,
44    Cockatrice,
45
46    Other,
47}
48
49impl TrackedPopulation {
50    pub fn from_body_and_role(body: &comp::Body, role: &Role) -> Self {
51        match (body, role) {
52            (_, Role::Civilised(role)) => match role {
53                Some(role) => match role {
54                    common::rtsim::Profession::Farmer
55                    | common::rtsim::Profession::Herbalist
56                    | common::rtsim::Profession::Blacksmith
57                    | common::rtsim::Profession::Chef
58                    | common::rtsim::Profession::Alchemist
59                    | common::rtsim::Profession::Hunter => Self::OtherTownNpcs,
60                    common::rtsim::Profession::Merchant => Self::Merchants,
61                    common::rtsim::Profession::Guard => Self::Guards,
62                    common::rtsim::Profession::Adventurer(_) => Self::Adventurers,
63                    common::rtsim::Profession::Pirate(false) => Self::Pirates,
64                    common::rtsim::Profession::Pirate(true) => Self::PirateCaptains,
65                    common::rtsim::Profession::Cultist => Self::Cultists,
66                    common::rtsim::Profession::Captain => Self::Captains,
67                },
68                None => Self::OtherTownNpcs,
69            },
70            (comp::Body::BirdLarge(bird), Role::Wild) => match bird.species {
71                comp::bird_large::Species::Phoenix => Self::Phoenix,
72                comp::bird_large::Species::Cockatrice => Self::Cockatrice,
73                comp::bird_large::Species::Roc => Self::Roc,
74                comp::bird_large::Species::FlameWyvern => Self::FlameWyvern,
75                comp::bird_large::Species::CloudWyvern => Self::CloudWyvern,
76                comp::bird_large::Species::FrostWyvern => Self::FrostWyvern,
77                comp::bird_large::Species::SeaWyvern => Self::SeaWyvern,
78                comp::bird_large::Species::WealdWyvern => Self::WealdWyvern,
79            },
80            (comp::Body::BipedLarge(biped), Role::Monster) => match biped.species {
81                comp::biped_large::Species::Gigasfrost => Self::GigasFrost,
82                comp::biped_large::Species::Gigasfire => Self::GigasFire,
83                _ => Self::OtherMonsters,
84            },
85
86            _ => Self::Other,
87        }
88    }
89}
90
91#[derive(Default, Clone)]
92pub struct Population {
93    populations: EnumMap<TrackedPopulation, u32>,
94}
95
96impl Population {
97    pub fn total(&self) -> u32 { self.populations.values().sum::<u32>() }
98
99    pub fn iter(&self) -> impl ExactSizeIterator<Item = (TrackedPopulation, u32)> + use<'_> {
100        self.populations.iter().map(|(k, v)| (k, *v))
101    }
102
103    pub fn of_death(&self, death: &Death) -> u32 {
104        let pop = TrackedPopulation::from_body_and_role(&death.body, &death.role);
105        self.populations[pop]
106    }
107
108    fn of_death_mut(&mut self, death: &Death) -> &mut u32 {
109        let pop = TrackedPopulation::from_body_and_role(&death.body, &death.role);
110        &mut self.populations[pop]
111    }
112
113    pub fn on_death(&mut self, death: &Death) {
114        let n = self.of_death_mut(death);
115
116        *n = n.saturating_sub(1);
117    }
118
119    pub fn on_spawn(&mut self, death: &Death) {
120        let n = self.of_death_mut(death);
121
122        *n = n.saturating_add(1);
123    }
124
125    pub fn add(&mut self, pop: TrackedPopulation, amount: u32) { self.populations[pop] += amount; }
126}
127
128/// The architect has the responsibility of making sure the game keeps working.
129/// Which means keeping the simulation in check, and making sure interesting
130/// stuff keeps happening.
131///
132/// Currently it handles:
133/// - Keeping track of all deaths that happen, and respawn something similar to
134///   keep the world from dying out.
135#[derive(Default, Clone, Serialize, Deserialize)]
136pub struct Architect {
137    pub deaths: VecDeque<Death>,
138
139    /// This is calculated on startup. And includes both dead and alive.
140    #[serde(skip)]
141    pub population: Population,
142    /// This is calculated on startup, based on world size and what sites there
143    /// are.
144    #[serde(skip)]
145    pub wanted_population: Population,
146}
147
148impl Architect {
149    pub fn on_death(&mut self, npc: &Npc, time: TimeOfDay) {
150        let death = Death {
151            time,
152            body: npc.body,
153            role: npc.role.clone(),
154            faction: npc.faction,
155        };
156        self.deaths.push_back(death)
157    }
158}