rpfm_extensions/startpos/world/rebels.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//! Rebel setup of each region (`REGION/REGION_REBEL_MANAGER/REBEL_SETUP`).
12//!
13//! Regions owned by the rebels get a setup for their alternative rebel faction. Every other region stores the same default
14//! setup (in Attila: Persis's flag with the Arian religion and an empire government, apparently a leftover).
15//! Verified on Attila's main campaign: 186 of 186 regions match.
16
17use getset::Getters;
18
19use rpfm_lib::error::{RLibError, Result};
20
21use super::RegionEntry;
22use super::factions::{FactionFlag, faction_row, flag};
23use crate::startpos::tables::StartposTables;
24
25#[cfg(test)] #[path = "rebels_test.rs"] mod rebels_test;
26
27//---------------------------------------------------------------------------//
28// Types
29//---------------------------------------------------------------------------//
30
31/// The rebel setup of a region owned by the rebels.
32#[derive(Debug, Clone, PartialEq, Eq, Default, Getters)]
33#[getset(get = "pub")]
34pub struct RebelSetup {
35
36 /// Rebel faction key (`start_pos_regions.alternative_rebel_faction`).
37 faction: String,
38
39 /// The region's majority religion.
40 religion: String,
41
42 /// The rebel faction's flag, with its primary, secondary and uniform colours.
43 flag: FactionFlag,
44}
45
46//---------------------------------------------------------------------------//
47// Implementations
48//---------------------------------------------------------------------------//
49
50/// Returns the rebel setup of a region.
51///
52/// # Arguments
53///
54/// * `tables` - Tables with `start_pos_regions`, `start_pos_region_religions` and `factions`.
55/// * `region` - The region.
56///
57/// # Returns
58///
59/// The setup for regions owned by the rebels, `None` for the others (which store the default setup).
60///
61/// # Errors
62///
63/// Returns an error if a table or column is missing, or the rebel faction isn't in `factions`.
64pub fn rebel_setup(tables: &StartposTables, region: &RegionEntry) -> Result<Option<RebelSetup>> {
65 if region.owner().is_some() {
66 return Ok(None);
67 }
68
69 let mut faction = None;
70 for row in tables.table("start_pos_regions")?.rows() {
71 if row.str("id")? == *region.id() {
72 faction = Some(row.str("alternative_rebel_faction")?.into_owned());
73 }
74 }
75 let faction = faction.ok_or_else(|| RLibError::StartposInvalidValue("start_pos_regions".to_owned(), "id".to_owned(), region.id().to_owned()))?;
76
77 let flag = flag(&faction_row(tables, &faction)?, "flags_path", ["primary_colour_hex", "secondary_colour_hex", "uniform_colour_hex"])?;
78 Ok(Some(RebelSetup {
79 religion: majority_religion(tables, region)?.unwrap_or_default(),
80 faction,
81 flag,
82 }))
83}
84
85/// Returns the religion with the largest share of a region, the first in table order on ties.
86///
87/// # Arguments
88///
89/// * `tables` - Tables with `start_pos_region_religions`.
90/// * `region` - The region.
91///
92/// # Returns
93///
94/// The religion key, or `None` if the region has no religion rows.
95///
96/// # Errors
97///
98/// Returns an error if a table or column is missing.
99pub fn majority_religion(tables: &StartposTables, region: &RegionEntry) -> Result<Option<String>> {
100 let mut best: Option<(f64, String)> = None;
101 for row in tables.table("start_pos_region_religions")?.rows() {
102 if row.str("region")? != *region.id() {
103 continue;
104 }
105 let share = row.number("percentage")?;
106 if best.as_ref().is_none_or(|(best_share, _)| share > *best_share) {
107 best = Some((share, row.str("religion")?.into_owned()));
108 }
109 }
110 Ok(best.map(|(_, religion)| religion))
111}