Skip to main content

rpfm_extensions/startpos/world/
estates.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Estates of the region slots (`REGION_SLOT/REGION_BUILDING_MANAGER/ESTATES`, Thrones of Britannia only).
12//!
13//! Each building gives its slot the estates of its `amount` effects (`building_effects_junction` effects that
14//! `effect_bonus_value_estate_junctions` maps to an estate type with the `amount` bonus value), as many of each
15//! type as the effect's value. The `start_pos_character_estates` rows give the nth estate of a type in a region to
16//! a character; the others belong to the faction leader of the building's owner. Verified on Thrones' campaign:
17//! the estates of 598 of 598 slots, and the owners of 97 of 97 estates (77 leaders, 20 from the table).
18
19use std::collections::HashMap;
20
21use rpfm_lib::error::Result;
22
23use crate::startpos::tables::StartposTables;
24
25#[cfg(test)] #[path = "estates_test.rs"] mod estates_test;
26
27/// Bonus value of the effects that give estates.
28const AMOUNT_BONUS_VALUE: &str = "amount";
29
30/// What the startpos tables say about estates.
31#[derive(Debug, Clone, Default)]
32pub struct EstateRules {
33
34    /// Estate types and amounts each building level gives, in table order.
35    provided: HashMap<String, Vec<(String, u32)>>,
36
37    /// `start_pos_characters` id of the owner of the nth estate of a type in a region, by (region, type, n).
38    owners: HashMap<(String, String, u32), String>,
39}
40
41impl EstateRules {
42
43    /// Reads the rules from the tables. Games without estates get none.
44    ///
45    /// # Errors
46    ///
47    /// Returns an error if a table or column is missing.
48    pub fn new(tables: &StartposTables) -> Result<Self> {
49        let mut types = HashMap::new();
50        for row in tables.table("effect_bonus_value_estate_junctions")?.rows() {
51            if row.str("bonus_value_id")? == AMOUNT_BONUS_VALUE {
52                types.insert(row.str("effect")?.into_owned(), row.str("estate")?.into_owned());
53            }
54        }
55
56        let mut provided = HashMap::<String, Vec<(String, u32)>>::new();
57        if !types.is_empty() {
58            for row in tables.table("building_effects_junction")?.rows() {
59                if let Some(estate) = types.get(row.str("effect")?.as_ref()) {
60                    let amount = row.number("value")?.max(0.0) as u32;
61                    provided.entry(row.str("building")?.into_owned()).or_default().push((estate.to_owned(), amount));
62                }
63            }
64        }
65
66        let mut owners = HashMap::new();
67        for row in tables.table("start_pos_character_estates")?.rows() {
68            let number = u32::try_from(row.int("number")?).unwrap_or_default();
69            owners.insert((row.str("region")?.into_owned(), row.str("estate_type")?.into_owned(), number), row.str("character")?.into_owned());
70        }
71        Ok(Self { provided, owners })
72    }
73
74    /// Returns the estate types a building level gives, one entry per estate, in table order.
75    pub fn estates_of(&self, building: &str) -> Vec<&str> {
76        self.provided.get(building).into_iter().flatten()
77            .flat_map(|(estate, amount)| std::iter::repeat_n(estate.as_str(), *amount as usize))
78            .collect()
79    }
80
81    /// Returns the `start_pos_characters` id of the owner the table gives the nth estate of a type in a region.
82    pub fn table_owner(&self, region: &str, estate: &str, number: u32) -> Option<&str> {
83        self.owners.get(&(region.to_owned(), estate.to_owned(), number)).map(String::as_str)
84    }
85}