use crate::block::{BV, Block};
use crate::ck3::tables::misc::CUSTOM_RELIGION_LOCAS;
use crate::ck3::validate::validate_traits;
use crate::context::ScopeContext;
use crate::db::{Db, DbKind};
use crate::everything::Everything;
use crate::fileset::FileKind;
use crate::game::GameFlags;
use crate::helpers::TigerHashMap;
use crate::item::{Item, ItemLoader};
use crate::report::{ErrorKey, err, warn};
use crate::scopes::Scopes;
use crate::token::Token;
use crate::tooltipped::Tooltipped;
use crate::validate::validate_possibly_named_color;
use crate::validator::Validator;
#[derive(Clone, Debug)]
pub struct Religion {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::Religion, Religion::add)
}
impl Religion {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::Religion, key, block, Box::new(Self {}));
}
}
impl DbKind for Religion {
fn add_subitems(&self, key: &Token, block: &Block, db: &mut Db) {
if let Some(block) = block.get_field_block("faiths") {
for (faith, block) in block.iter_definitions() {
if let Some(token) = block.get_field_value("graphical_faith") {
db.add_flag(Item::GraphicalFaith, token.clone());
}
if let Some(token) = block.get_field_value("icon") {
db.add_flag(Item::FaithIcon, token.clone());
} else {
db.add_flag(Item::FaithIcon, faith.clone());
}
if let Some(token) = block.get_field_value("reformed_icon") {
db.add_flag(Item::FaithIcon, token.clone());
}
let kind = Box::new(Faith { religion: key.clone() });
db.add(Item::Faith, faith.clone(), block.clone(), kind);
}
}
if let Some(block) = block.get_field_block("custom_faith_icons") {
for token in block.iter_values() {
db.add_flag(Item::FaithIcon, token.clone());
}
}
if let Some(token) = block.get_field_value("graphical_faith") {
db.add_flag(Item::GraphicalFaith, token.clone());
}
}
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_adj");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_adherent");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_adherent_plural");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let mut vd = Validator::new(block, data);
vd.req_field("family");
vd.field_item("family", Item::ReligionFamily);
validate_doctrines("religion", data, &mut vd);
vd.field_icon("doctrine_background_icon", "NGameIcons|FAITH_DOCTRINE_BACKGROUND_PATH", "");
validate_piety_icon_group(vd.field_value("piety_icon_group"), data);
vd.field_value("graphical_faith");
vd.field_bool("pagan_roots");
vd.field_validated_block("traits", validate_traits);
vd.field_validated_list("custom_faith_icons", |icon, data| {
data.verify_icon("NGameIcons|FAITH_ICON_PATH", icon, ".dds");
});
vd.field_list("reserved_male_names"); vd.field_list("reserved_female_names");
vd.field_validated_block("holy_order_names", validate_holy_order_names);
vd.field_list_items("holy_order_maa", Item::MenAtArms);
vd.field_validated_block("localization", validate_localization);
vd.field_validated_block("faiths", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|_, _| ()); });
}
fn has_property(
&self,
_key: &Token,
block: &Block,
property: &str,
_data: &Everything,
) -> bool {
if let Some(block) = block.get_field_block("localization") {
block.has_key(property)
} else {
false
}
}
}
fn validate_doctrines(iname: &str, data: &Everything, vd: &mut Validator) {
let mut groups: TigerHashMap<&str, Vec<Token>> = TigerHashMap::default();
vd.multi_field_validated_value("doctrine", |_, mut vd| {
vd.item(Item::Doctrine);
let doctrine = vd.value();
for (group, block) in data.database.iter_key_block(Item::DoctrineGroup) {
if let Some(doctrines) = block.get_field_block("doctrine_types") {
if !doctrines.iter_values().any(|t| t == doctrine) {
continue;
}
} else {
continue;
}
if let Some(seen) = groups.get_mut(group.as_str()) {
let picks_token = block.get_field_value("number_of_picks");
let picks = picks_token.and_then(Token::get_integer).unwrap_or(1);
#[allow(clippy::cast_possible_wrap)]
if let Some(other_doctrine) = seen.iter().find(|&d| d == doctrine) {
let msg = format!("duplicate doctrine {doctrine}");
warn(ErrorKey::DuplicateField)
.msg(msg)
.loc(doctrine)
.loc_msg(other_doctrine, "earlier doctrine")
.push();
} else if picks == 1 {
let other_doctrine = &seen[0];
let msg = format!("{doctrine} and {other_doctrine} are both from {group}");
let info = format!("{group} only allows 1 pick");
err(ErrorKey::Conflict)
.msg(msg)
.info(info)
.loc(doctrine)
.loc_msg(other_doctrine, "earlier doctrine")
.push();
} else if picks == (seen.len() as i64) {
let msg = format!("{iname} has more than {picks} doctrines from {group}");
err(ErrorKey::Conflict)
.msg(msg)
.loc(doctrine)
.loc_msg(picks_token.unwrap(), "picks")
.push();
}
seen.push(doctrine.clone());
} else {
groups.insert(group.as_str(), vec![doctrine.clone()]);
}
}
});
vd.multi_field_validated_block("doctrine_selection_pair", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_item("requires_dlc_flag", Item::DlcFeature);
vd.field_item("doctrine", Item::Doctrine);
vd.field_item("fallback_doctrine", Item::Doctrine);
});
}
fn validate_localization(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
for field in CUSTOM_RELIGION_LOCAS {
vd.field_validated(field, |bv, data| match bv {
BV::Value(token) => data.verify_exists(Item::Localization, token),
BV::Block(block) => {
let mut vd = Validator::new(block, data);
for token in vd.values() {
data.verify_exists(Item::Localization, token);
}
}
});
}
}
fn validate_holy_order_names(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
for block in vd.blocks() {
let mut vd = Validator::new(block, data);
vd.req_field("name");
vd.field_item("name", Item::Localization);
vd.field_item("coat_of_arms", Item::Coa);
}
}
#[derive(Clone, Debug)]
pub struct Faith {
religion: Token,
}
impl Faith {
fn check_have_customs(&self, key: &Token, block: &Block, data: &Everything) {
let locas = block.get_field_block("localization");
for loca in CUSTOM_RELIGION_LOCAS {
if let Some(block) = locas
&& block.has_key(loca)
{
continue;
}
if data.item_has_property(Item::Religion, self.religion.as_str(), loca) {
continue;
}
let msg = format!("faith or religion missing localization for {loca}");
warn(ErrorKey::MissingLocalization).msg(msg).loc(key).push();
}
}
}
impl DbKind for Faith {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_adj");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_adherent");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_adherent_plural");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let pagan = block
.get_field_values("doctrine")
.iter()
.any(|value| data.item_has_property(Item::Doctrine, value.as_str(), "unreformed"));
if pagan {
let loca = format!("{key}_old");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_old_adj");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_old_adherent");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_old_adherent_plural");
data.verify_exists_implied(Item::Localization, &loca, key);
}
let mut vd = Validator::new(block, data);
vd.req_field("color");
vd.field_validated("color", validate_possibly_named_color);
let icon = vd.field_value("icon").unwrap_or(key);
data.verify_icon("NGameIcons|FAITH_ICON_PATH", icon, ".dds");
if pagan {
vd.req_field_fatal("reformed_icon");
} else {
vd.ban_field("reformed_icon", || "unreformed faiths");
}
vd.field_icon("reformed_icon", "NGameIcons|FAITH_ICON_PATH", ".dds");
vd.field_value("graphical_faith");
validate_piety_icon_group(vd.field_value("piety_icon_group"), data);
vd.field_icon("doctrine_background_icon", "NGameIcons|FAITH_DOCTRINE_BACKGROUND_PATH", "");
vd.field_item("religious_head", Item::Title);
vd.req_field("holy_site");
vd.multi_field_item("holy_site", Item::HolySite);
validate_doctrines("faith", data, &mut vd);
vd.field_list("reserved_male_names");
vd.field_list("reserved_female_names");
vd.field_validated_block("localization", validate_localization);
vd.field_validated_block("holy_order_names", validate_holy_order_names);
vd.field_list_items("holy_order_maa", Item::MenAtArms);
self.check_have_customs(key, block, data);
}
fn has_property(
&self,
key: &Token,
_block: &Block,
property: &str,
_data: &Everything,
) -> bool {
if property == "is_modded" {
return key.loc.kind == FileKind::Mod;
}
false
}
}
#[derive(Clone, Debug)]
pub struct ReligionFamily {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::ReligionFamily, ReligionFamily::add)
}
impl ReligionFamily {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::ReligionFamily, key, block, Box::new(Self {}));
}
}
impl DbKind for ReligionFamily {
fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
if let Some(token) = block.get_field_value("graphical_faith") {
db.add_flag(Item::GraphicalFaith, token.clone());
}
}
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let name = vd.field_value("name").unwrap_or(key);
data.verify_exists(Item::Localization, name);
let loca = format!("{name}_desc");
data.verify_exists_implied(Item::Localization, &loca, name);
vd.field_bool("is_pagan");
vd.field_value("graphical_faith");
validate_piety_icon_group(vd.field_value("piety_icon_group"), data);
vd.field_icon("doctrine_background_icon", "NGameIcons|FAITH_DOCTRINE_BACKGROUND_PATH", "");
vd.field_item("hostility_doctrine", Item::Doctrine);
}
}
#[derive(Clone, Debug)]
pub struct FervorModifier {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::FervorModifier, FervorModifier::add)
}
impl FervorModifier {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::FervorModifier, key, block, Box::new(Self {}));
}
}
impl DbKind for FervorModifier {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let mut sc = ScopeContext::new(Scopes::Faith, key);
vd.field_script_value("value", &mut sc);
vd.field_trigger("trigger", Tooltipped::No, &mut sc);
}
}
fn validate_piety_icon_group(group: Option<&Token>, data: &Everything) {
if let Some(group) = group {
if let Some(valid) = data.get_defined_array_warn(group, "NGameIcons|PIETY_GROUPS") {
for valid_group in valid.iter_values() {
if group == valid_group {
return;
}
}
}
let msg = "piety icon group not listed in PIETY_GROUPS define";
warn(ErrorKey::Choice).msg(msg).loc(group).push();
}
}