use crate::block::Block;
use crate::ck3::modif::ModifKinds;
use crate::context::ScopeContext;
use crate::db::{Db, DbKind};
use crate::everything::Everything;
use crate::game::GameFlags;
use crate::item::{Item, ItemLoader, LoadAsFile, Recursive};
use crate::modif::validate_modifs;
use crate::pdxfile::PdxEncoding;
use crate::report::{ErrorKey, err, warn};
use crate::scopes::Scopes;
use crate::script_value::validate_non_dynamic_script_value;
use crate::token::Token;
use crate::tooltipped::Tooltipped;
use crate::validate::{validate_duration, validate_possibly_named_color};
use crate::validator::Validator;
#[derive(Clone, Debug)]
pub struct Situation {}
#[derive(Clone, Debug)]
pub struct SituationCatalyst {}
#[derive(Clone, Debug)]
pub struct SituationHistory {}
#[derive(Clone, Debug)]
pub struct SituationGroupType {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::Situation, Situation::add)
}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::SituationCatalyst, SituationCatalyst::add)
}
inventory::submit! {
ItemLoader::Full(GameFlags::Ck3, Item::SituationHistory, PdxEncoding::Utf8Bom, ".txt", LoadAsFile::Yes, Recursive::No, SituationHistory::add)
}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::SituationGroupType, SituationGroupType::add)
}
impl Situation {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::Situation, key, block, Box::new(Self {}));
}
}
impl SituationCatalyst {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::SituationCatalyst, key, block, Box::new(Self {}));
}
}
impl SituationHistory {
pub fn add(db: &mut Db, file: Token, block: Block) {
db.add(Item::SituationHistory, file, block, Box::new(Self {}));
}
}
impl SituationGroupType {
pub fn add(db: &mut Db, file: Token, block: Block) {
db.add(Item::SituationGroupType, file, block, Box::new(Self {}));
}
}
impl DbKind for Situation {
fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
if let Some(block) = block.get_field_block("sub_regions") {
for (key, _) in block.iter_definitions() {
db.add_flag(Item::SituationSubRegion, key.clone());
}
}
if let Some(block) = block.get_field_block("participant_groups") {
for (key, _) in block.iter_definitions() {
db.add_flag(Item::SituationParticipantGroup, key.clone());
}
}
if let Some(block) = block.get_field_block("phases") {
for (key, block) in block.iter_definitions() {
if let Some(block) = block.get_field_block("parameters") {
for (key, _) in block.iter_assignments() {
db.add_flag(Item::SituationPhaseParameter, key.clone());
}
}
if let Some(block) = block.get_field_block("modifier_sets") {
for (_, block) in block.iter_definitions() {
for (_, block) in block.iter_definitions() {
if let Some(block) = block.get_field_block("parameters") {
for (key, _) in block.iter_assignments() {
db.add_flag(
Item::SituationParticipantGroupParameter,
key.clone(),
);
}
}
}
}
}
db.add_flag(Item::SituationPhase, key.clone());
}
}
}
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let loca = format!("situation_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("situation_{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("situation_type_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("situation_type_{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
vd.field_choice(
"window",
&["situation", "the_great_steppe", "silk_road", "dynastic_cycle"],
);
if let Some(token) = vd.field_value("gui_window_name") {
let pathname = format!("gui/{token}.gui");
data.verify_exists_implied(Item::File, &pathname, token);
}
if let Some(token) = vd.field_value("gui_participation_window_name") {
let pathname = format!("gui/{token}.gui");
data.verify_exists_implied(Item::File, &pathname, token);
}
vd.field_bool("gui_tooltip_group_focused");
vd.field_item("illustration", Item::File);
vd.multi_field_validated_block("icon", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_trigger_rooted("trigger", Tooltipped::No, Scopes::Situation);
vd.field_item("reference", Item::File);
});
vd.field_item("situation_group_type", Item::SituationGroupType);
vd.field_integer("sort_order");
vd.field_validated_value("map_mode", |_, mut vvd| {
vvd.maybe_is("participant_groups");
vvd.maybe_is("sub_regions");
vvd.item(Item::MapMode);
});
vd.req_field("sub_regions");
vd.field_validated_block("sub_regions", |block, data| {
let mut vd = Validator::new(block, data);
let mut count = 0;
vd.unknown_block_fields(|sub_region_key, block| {
count += 1;
validate_sub_region(sub_region_key, block, data, key);
});
if count == 0 {
let msg = "situation needs at least one sub-region";
err(ErrorKey::FieldMissing).msg(msg).loc(key).push();
}
});
vd.req_field("participant_groups");
vd.field_validated_block("participant_groups", |block, data| {
let mut vd = Validator::new(block, data);
let mut count = 0;
vd.unknown_block_fields(|pg_key, block| {
count += 1;
validate_participant_group(pg_key, block, data, key);
});
if count == 0 {
let msg = "situation needs at least one participant group";
err(ErrorKey::FieldMissing).msg(msg).loc(key).push();
}
});
vd.req_field("phases");
vd.field_validated_block("phases", |block, data| {
let mut vd = Validator::new(block, data);
let mut count = 0;
vd.unknown_block_fields(|phase_key, block| {
count += 1;
validate_phase(phase_key, block, data, key);
});
if count == 0 {
let msg = "situation needs at least one phase";
err(ErrorKey::FieldMissing).msg(msg).loc(key).push();
}
});
vd.field_effect_rooted("on_start", Tooltipped::No, Scopes::Situation);
vd.field_effect_rooted("on_end", Tooltipped::No, Scopes::Situation);
vd.field_effect_rooted("on_monthly", Tooltipped::No, Scopes::Situation);
vd.field_effect_rooted("on_yearly", Tooltipped::No, Scopes::Situation);
vd.field_effect_rooted("on_join", Tooltipped::Yes, Scopes::Situation);
vd.field_effect_rooted("on_leave", Tooltipped::Yes, Scopes::Situation);
vd.field_bool("is_unique");
vd.field_bool("keep_full_history");
vd.field_bool("migration");
vd.field_item("start_phase", Item::SituationPhase);
vd.field_bool("use_situation_phase_flat_icons");
}
}
impl DbKind for SituationCatalyst {
fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
let _vd = Validator::new(block, data);
}
}
impl DbKind for SituationHistory {
fn validate(&self, _file: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.validate_history_blocks(|_, _, block, data| {
let mut vd = Validator::new(block, data);
vd.field_effect_rooted("effect", Tooltipped::No, Scopes::None);
});
}
}
impl DbKind for SituationGroupType {
fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.field_integer("sort_order");
vd.field_list("gui_tags");
}
}
fn validate_participant_group(key: &Token, block: &Block, data: &Everything, situation: &Token) {
fn sc_builder(key: &Token) -> ScopeContext {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("situation", Scopes::Situation, key);
sc.define_name("situation_sub_region", Scopes::SituationSubRegion, key);
sc
}
fn sc_with_group(key: &Token) -> ScopeContext {
let mut sc = sc_builder(key);
sc.define_name("situation_participant_group", Scopes::SituationParticipantGroup, key);
sc
}
let mut vd = Validator::new(block, data);
let loca = format!("{situation}_participant_group_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{situation}_participant_group_{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
vd.field_item("icon", Item::File);
vd.field_bool("auto_add_rulers");
vd.field_bool("auto_add_landless_rulers");
vd.field_validated("map_color", validate_possibly_named_color);
vd.field_bool("require_capital_in_sub_region");
vd.field_bool("require_domain_in_sub_region");
vd.field_bool("require_realm_in_sub_region");
vd.field_bool("require_domicile_in_sub_region");
vd.field_trigger_builder("is_character_valid", Tooltipped::Yes, sc_builder);
vd.field_effect_builder("on_join", Tooltipped::Yes, sc_with_group);
vd.field_effect_builder("on_leave", Tooltipped::Yes, sc_with_group);
}
fn validate_phase(key: &Token, block: &Block, data: &Everything, situation: &Token) {
fn sc_builder(key: &Token) -> ScopeContext {
let mut sc = ScopeContext::new(Scopes::Situation, key);
sc.define_name("situation", Scopes::Situation, key);
sc.define_name("situation_sub_region", Scopes::SituationSubRegion, key);
sc
}
fn sc_builder2(key: &Token) -> ScopeContext {
let mut sc = ScopeContext::new(Scopes::Situation, key);
sc.define_name("situation_sub_region", Scopes::SituationSubRegion, key);
sc
}
let mut vd = Validator::new(block, data);
let loca = format!("{situation}_{key}_situation_phase");
data.verify_exists_implied(Item::Localization, &loca, key);
vd.field_validated_block("parameters", validate_parameters);
vd.field_effect_builder("on_start", Tooltipped::No, sc_builder);
vd.field_effect_builder("on_end", Tooltipped::No, sc_builder);
vd.field_item("illustration", Item::File);
vd.field_item("icon", Item::File);
vd.field_item("map_province_effect", Item::ProvinceEffect);
vd.field_numeric_range("map_province_effect_intensity", 0.0..=1.0);
vd.field_validated_block_sc("max_duration", &mut sc_builder2(key), validate_duration);
vd.field_choice(
"max_duration_next_phase",
&[
"highest_points",
"weighted_random_points",
"random_non_takeover",
"weighted_non_takeover",
],
);
vd.field_validated_block("future_phases", |block, data| {
let mut vd = Validator::new(block, data);
vd.validate_item_key_blocks(Item::SituationPhase, |_, block, data| {
let mut vd = Validator::new(block, data);
vd.field_choice("takeover_type", &["none", "points", "duration"]);
vd.field_script_value_no_breakdown_builder("takeover_points", sc_builder2);
vd.field_script_value_no_breakdown_builder("weight", sc_builder2);
vd.field_validated_block_sc(
"takeover_duration",
&mut sc_builder2(key),
validate_duration,
);
vd.field_validated_block("catalysts", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_fields(|key, bv| {
data.verify_exists(Item::SituationCatalyst, key);
validate_non_dynamic_script_value(bv, data);
});
});
});
});
vd.advice_field("modifier_named_sets", "docs say modifier_named_sets but it's modifier_sets");
vd.field_validated_block("modifier_sets", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|key, block| {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
vd.field_item("icon", Item::File);
vd.field_validated_block("all", validate_modifier_set);
vd.validate_item_key_blocks(Item::SituationParticipantGroup, |_, block, data| {
validate_modifier_set(block, data);
});
});
});
}
fn validate_sub_region(key: &Token, block: &Block, data: &Everything, situation: &Token) {
let mut vd = Validator::new(block, data);
let loca = format!("{situation}_sub_region_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
vd.field_item("illustration", Item::File);
vd.field_item("icon", Item::File);
vd.field_validated("map_color", validate_possibly_named_color);
vd.field_list_items("geographical_regions", Item::Region);
vd.field_item("capital_province", Item::Province);
}
fn validate_modifier_set(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.field_validated_block("county_modifier", |block, data| {
let vd = Validator::new(block, data);
validate_modifs(block, data, ModifKinds::County, vd);
});
vd.field_validated_block("character_modifier", |block, data| {
let vd = Validator::new(block, data);
validate_modifs(block, data, ModifKinds::Character, vd);
});
vd.multi_field_validated_block("doctrine_character_modifier", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_item("name", Item::Localization);
vd.field_item("doctrine", Item::Doctrine);
validate_modifs(block, data, ModifKinds::Character, vd);
});
vd.field_validated_block("parameters", validate_parameters);
}
fn validate_parameters(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.unknown_value_fields(|key, value| {
let loca = format!("situation_parameter_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
if !value.is("yes") {
let msg = "only `yes` makes sense here";
warn(ErrorKey::Validation).msg(msg).loc(value).push();
}
});
}