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}