Skip to main content

tiger_lib/vic3/
modif.rs

1use std::fmt::{Display, Formatter};
2
3use bitflags::bitflags;
4
5use crate::vic3::tables::modifs::modif_flow_suggest;
6use crate::{
7    Severity, Token, context, modif,
8    report::{ErrorKey, report},
9    scopes::Scopes,
10    vic3::tables::modifs::{MODIF_FLOW_MAP, lookup_modif},
11};
12
13bitflags! {
14    // LAST UPDATED VIC3 1.7.0
15    // Taken from the game's `modifers.log`
16    // Remember to update the display_fmt functions when ModifKinds changes.
17    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
18    #[rustfmt::skip]
19    pub struct ModifKinds: u32 {
20        const Character         = 1<<0;
21        const Country           = 1<<1;
22        const State             = 1<<2;
23        const Unit              = 1<<3 | 1<<4;
24        const Battle            = 1<<5;
25        const Building          = 1<<6;
26        const InterestGroup     = 1<<7;
27        const Market            = 1<<8;
28        const PoliticalMovement = 1<<9;
29        const Tariff            = 1<<10;
30        const Tax               = 1<<11;
31        const Goods             = 1<<12;
32        const MilitaryFormation = 1<<13;
33        const PowerBloc         = 1<<14;
34        const Ship              = 1<<15;
35        // Special scopes that allows tiger to distinguish between
36        // modifiers that apply to units generally, or while in
37        // combat. This matters because unit modifiers only flow
38        // from characters while in combat, and non-combat unit
39        // modifiers don't have an effect in that context
40        const UnitCombat        = 1<<3;
41        const UnitNonCombat     = 1<<4;
42    }
43}
44
45impl ModifKinds {
46    pub fn require_from(
47        self,
48        other: Self,
49        token: &Token,
50        scope: Option<(&Token, Scopes, &context::Reason)>,
51        sev: Severity,
52    ) {
53        let valid_kinds = self
54            .iter()
55            .map(|kind| *MODIF_FLOW_MAP.get(&kind).unwrap_or(&kind))
56            .reduce(ModifKinds::union)
57            .unwrap_or(self);
58        if !valid_kinds.intersects(other) {
59            let from = if let Some((_, scopes, _)) = scope {
60                format!("{scopes} scope")
61            } else {
62                self.to_string()
63            };
64            let msg = format!("`{token}` is a modifier for {other} and will not flow from {from}");
65            let info = if self.is_empty() {
66                format!("there are no valid modifiers for {from}")
67            } else if let Some(suggest) = modif_flow_suggest(token.as_str(), valid_kinds) {
68                format!("a similar modifier exists that would flow `{suggest}`")
69            } else {
70                format!("valid modifiers are for {valid_kinds}")
71            };
72            let mut report = report(ErrorKey::Modifiers, sev)
73                .msg(msg)
74                .info(info)
75                .wiki("https://vic3.paradoxwikis.com/Modifier_types#Modifier_type_flow")
76                .loc(token);
77            if let Some((token, _, reason)) = scope {
78                report = report
79                    .loc_msg(token, "from this temporary modifier")
80                    .loc_msg(reason.token(), format!("scope was {}", reason.msg()));
81            }
82            report.push();
83        }
84    }
85}
86
87impl modif::ModifKinds for ModifKinds {
88    fn lookup_modif(
89        name: &crate::Token,
90        data: &crate::Everything,
91        warn: Option<crate::Severity>,
92    ) -> Option<Self> {
93        lookup_modif(name, data, warn)
94    }
95
96    fn require(self, other: Self, token: &Token) {
97        self.require_from(other, token, None, Severity::Error);
98    }
99}
100
101impl Display for ModifKinds {
102    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
103        let mut vec = Vec::new();
104        if self.contains(ModifKinds::Battle) {
105            vec.push("battle");
106        }
107        if self.contains(ModifKinds::Building) {
108            vec.push("building");
109        }
110        if self.contains(ModifKinds::Character) {
111            vec.push("character");
112        }
113        if self.contains(ModifKinds::Country) {
114            vec.push("country");
115        }
116        if self.contains(ModifKinds::InterestGroup) {
117            vec.push("interest group");
118        }
119        if self.contains(ModifKinds::Market) {
120            vec.push("market");
121        }
122        if self.contains(ModifKinds::PoliticalMovement) {
123            vec.push("political movement");
124        }
125        if self.contains(ModifKinds::State) {
126            vec.push("state");
127        }
128        if self.contains(ModifKinds::Tariff) {
129            vec.push("tariff");
130        }
131        if self.contains(ModifKinds::Tax) {
132            vec.push("tax");
133        }
134        if self.contains(ModifKinds::Unit) {
135            vec.push("unit");
136        } else if self.contains(ModifKinds::UnitCombat) {
137            vec.push("unit (combat)");
138        } else if self.contains(ModifKinds::UnitNonCombat) {
139            vec.push("unit (non-combat)");
140        }
141        if self.contains(ModifKinds::Goods) {
142            vec.push("goods");
143        }
144        if self.contains(ModifKinds::MilitaryFormation) {
145            vec.push("military formation");
146        }
147        if self.contains(ModifKinds::PowerBloc) {
148            vec.push("power bloc");
149        }
150        write!(f, "{}", vec.join(", "))
151    }
152}