use crate::block::{BV, Block};
use crate::ck3::validate::validate_cost;
use crate::context::ScopeContext;
use crate::db::{Db, DbKind};
use crate::desc::validate_desc;
use crate::everything::Everything;
use crate::game::GameFlags;
use crate::item::{Item, ItemLoader};
use crate::report::{ErrorKey, warn};
use crate::scopes::Scopes;
use crate::token::Token;
use crate::tooltipped::Tooltipped;
use crate::validate::{validate_duration, validate_modifiers_with_base};
use crate::validator::Validator;
#[derive(Clone, Debug)]
pub struct Decision {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::Decision, Decision::add)
}
impl Decision {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::Decision, key, block, Box::new(Self {}));
}
}
impl DbKind for Decision {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let mut sc = ScopeContext::new(Scopes::Character, key);
if let Some(block) = block.get_field_block("widget") {
if let Some(controller) = block.get_field_value("controller") {
if controller.is("decision_option_list_controller") {
for block in block.get_field_blocks("item") {
if let Some(token) = block.get_field_value("value") {
sc.define_name(token.as_str(), Scopes::Bool, token);
}
}
} else if controller.is("create_holy_order") {
sc.define_name("ruler", Scopes::Character, controller);
} else if controller.is("revoke_holy_order_lease") {
sc.define_name("barony", Scopes::LandedTitle, controller);
}
}
}
vd.req_field_warn("picture");
vd.multi_field_validated_block("picture", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_trigger_rooted("trigger", Tooltipped::No, Scopes::Character);
vd.field_item("reference", Item::File);
vd.field_item("soundeffect", Item::Sound);
});
vd.field_item("extra_picture", Item::File);
vd.advice_field("major", "Replaced with decision_group_type");
vd.field_item("decision_group_type", Item::DecisionGroup);
vd.field_script_value_rooted("progress", Scopes::Character);
vd.field_validated_block("advice", validate_advice);
vd.field_integer("sort_order");
vd.field_bool("is_invisible");
vd.field_bool("ai_goal");
vd.field_integer("ai_check_interval");
vd.field_validated_key_block("ai_check_interval_by_tier", |key, b, data| {
let mut vd = Validator::new(b, data);
for tier in &["barony", "county", "duchy", "kingdom", "empire", "hegemony"] {
vd.req_field(tier);
vd.field_integer(tier);
}
if block.has_key("ai_check_interval") {
let msg = "must not have both `ai_check_interval` and `ai_check_interval_by_tier`";
warn(ErrorKey::Validation).msg(msg).loc(key).push();
}
});
if block.get_field_bool("ai_goal").unwrap_or(false) {
vd.advice_field("ai_check_interval", "not needed if ai_goal = yes");
}
vd.field_validated_block_sc("cooldown", &mut sc, validate_duration);
vd.field_item("confirm_click_sound", Item::Sound);
if !vd.field_validated_sc("selection_tooltip", &mut sc, validate_desc) {
let loca = format!("{key}_tooltip");
data.verify_exists_implied(Item::Localization, &loca, key);
data.validate_localization_sc(&loca, &mut sc);
}
if !vd.field_validated_sc("title", &mut sc, validate_desc) {
data.verify_exists(Item::Localization, key);
data.validate_localization_sc(key.as_str(), &mut sc);
}
if !vd.field_validated_sc("desc", &mut sc, validate_desc) {
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
data.validate_localization_sc(&loca, &mut sc);
}
if !vd.field_validated_sc("confirm_text", &mut sc, validate_desc) {
let loca = format!("{key}_confirm");
data.verify_exists_implied(Item::Localization, &loca, key);
data.validate_localization_sc(&loca, &mut sc);
}
vd.field_trigger("is_shown", Tooltipped::No, &mut sc);
vd.field_trigger("is_valid_showing_failures_only", Tooltipped::FailuresOnly, &mut sc);
vd.field_trigger("is_valid", Tooltipped::Yes, &mut sc);
vd.multi_field_validated_block("cost", |b, data| validate_cost(b, data, &mut sc));
check_cost(&block.get_field_blocks("cost"));
vd.multi_field_validated_block("minimum_cost", |b, data| validate_cost(b, data, &mut sc));
check_cost(&block.get_field_blocks("minimum_cost"));
vd.field_effect("effect", Tooltipped::Yes, &mut sc);
vd.field_trigger("ai_potential", Tooltipped::No, &mut sc);
vd.field_validated_block_sc("ai_will_do", &mut sc, validate_modifiers_with_base);
vd.field_trigger("should_create_alert", Tooltipped::No, &mut sc);
vd.field_validated("widget", |bv, data| {
match bv {
BV::Value(value) => {
let filename =
format!("gui/decision_view_widgets/decision_view_widget_{value}.gui");
data.verify_exists_implied(Item::File, &filename, value);
}
BV::Block(block) => {
let mut vd = Validator::new(block, data);
vd.field_validated_value("gui", |key, mut vd| {
let filename = format!("gui/decision_view_widgets/{vd}.gui");
data.verify_exists_implied(Item::File, &filename, key);
vd.accept();
});
vd.field_choice(
"controller",
&[
"default",
"decision_option_list_controller",
"create_holy_order",
"revoke_holy_order_lease", ],
);
vd.field_bool("show_from_start");
vd.field_item("decision_to_second_step_button", Item::Localization);
match block.get_field_value("controller").map(Token::as_str) {
Some("decision_option_list_controller") => {
vd.multi_field_validated_block("item", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_value("value");
vd.field_trigger_rooted(
"is_shown",
Tooltipped::No,
Scopes::Character,
);
vd.field_trigger_rooted(
"is_valid",
Tooltipped::FailuresOnly,
Scopes::Character,
);
vd.field_validated_rooted(
"current_description",
Scopes::Character,
validate_desc,
);
vd.field_validated_rooted(
"localization",
Scopes::Character,
validate_desc,
);
vd.field_bool("is_default");
vd.field_item("icon", Item::File);
vd.field_bool("flat");
vd.field_script_value_no_breakdown_rooted(
"ai_chance",
Scopes::Character,
);
});
}
Some("create_holy_order" | "revoke_holy_order_lease") => {
vd.field_trigger_builder("barony_valid", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::LandedTitle, key);
sc.define_name("ruler", Scopes::Character, key);
sc
});
}
_ => (),
}
}
}
});
}
}
fn check_cost(blocks: &[&Block]) {
let mut seen_gold = false;
let mut seen_prestige = false;
let mut seen_piety = false;
if blocks.len() > 1 {
for cost in blocks {
if let Some(gold) = cost.get_field("gold") {
if seen_gold {
let msg = "This value of the gold cost overrides the previously set cost.";
warn(ErrorKey::Conflict).msg(msg).loc(gold).push();
}
seen_gold = true;
}
if let Some(prestige) = cost.get_field("prestige") {
if seen_prestige {
let msg = "This value of the prestige cost overrides the previously set cost.";
warn(ErrorKey::Conflict).msg(msg).loc(prestige).push();
}
seen_prestige = true;
}
if let Some(piety) = cost.get_field("piety") {
if seen_piety {
let msg = "This value of the piety cost overrides the previously set cost.";
warn(ErrorKey::Conflict).msg(msg).loc(piety).push();
}
seen_piety = true;
}
}
}
}
#[derive(Clone, Debug)]
pub struct DecisionGroup {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::DecisionGroup, DecisionGroup::add)
}
impl DecisionGroup {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::DecisionGroup, key, block, Box::new(Self {}));
}
}
impl DbKind for DecisionGroup {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let loca = format!("decision_group_type_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
vd.field_integer("sort_order");
vd.field_list("gui_tags");
vd.field_bool("important_decision_group");
}
}
fn validate_advice(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.field_item("region", Item::Region);
vd.field_validated_key_block("per_title_hint", |key, block, data| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("title", Scopes::LandedTitle, key);
sc.define_name("doing", Scopes::Bool, key);
let mut vd = Validator::new(block, data);
vd.field_trigger("is_valid", Tooltipped::No, &mut sc);
vd.field_trigger("is_doing", Tooltipped::No, &mut sc);
vd.field_script_value_no_breakdown("relevance", &mut sc);
vd.field_validated_sc("summary", &mut sc, validate_desc);
vd.field_validated_sc("description", &mut sc, validate_desc);
});
}