use crate::block::{BV, Block};
use crate::ck3::data::scripted_animations::validate_scripted_animation;
use crate::ck3::tables::misc::PROVINCE_FILTERS;
use crate::ck3::validate::{validate_ai_targets, validate_cost, validate_quick_trigger};
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, err, warn};
use crate::scopes::Scopes;
use crate::token::Token;
use crate::tooltipped::Tooltipped;
use crate::trigger::validate_trigger;
use crate::validate::{validate_duration, validate_modifiers_with_base};
use crate::validator::Validator;
#[derive(Clone, Debug)]
pub struct ActivityType {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::ActivityType, ActivityType::add)
}
impl ActivityType {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::ActivityType, key, block, Box::new(Self {}));
}
}
impl DbKind for ActivityType {
fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
if let Some(block) = block.get_field_block("options") {
for (key, block) in block.iter_definitions() {
db.add_flag(Item::ActivityOptionCategory, key.clone());
for (key, _) in block.iter_definitions() {
db.add_flag(Item::ActivityOption, key.clone());
}
}
}
if let Some(block) = block.get_field_block("phases") {
for (key, _) in block.iter_definitions() {
db.add_flag(Item::ActivityPhase, key.clone());
}
}
if let Some(block) = block.get_field_block("special_guests") {
for (key, _) in block.iter_definitions() {
db.add_flag(Item::SpecialGuest, key.clone());
}
}
if let Some(block) = block.get_field_block("window_characters") {
for (key, _) in block.iter_definitions() {
db.add_flag(Item::SpecialGuest, key.clone());
}
}
if let Some(vec) = block.get_field_list("guest_subsets") {
for token in vec {
db.add_flag(Item::GuestSubset, token.clone());
}
}
}
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let define = "NGameIcons|ACTIVITY_TYPE_ICON_PATH";
data.verify_icon(define, key, ".dds");
data.verify_icon(define, key, "_header.dds");
let define = "NGameIcons|ACTIVITY_HEADER_BACKGROUND_PATH";
data.verify_icon(define, key, ".dds");
let mut vd = Validator::new(block, data);
let has_special_option = block.has_key("special_option_category");
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_name");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_owner");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_destination_selection");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_selection_tooltip");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_guest_help_text");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_predicted_cost");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("TRAVEL_NAME_FOR_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
if block.has_key("province_description") {
let mut sc = ScopeContext::new(Scopes::Province, key);
sc.define_name("host", Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_validated_sc("province_description", &mut sc, validate_desc);
} else {
let loca = format!("{key}_province_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
}
if block.has_key("host_description") {
let mut sc = ScopeContext::new(Scopes::Character, key);
vd.field_validated_sc("host_description", &mut sc, validate_desc);
} else {
let loca = format!("{key}_host_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
}
vd.field_integer("sort_order");
vd.field_bool("notify_player_can_join_activity");
vd.field_item("activity_group_type", Item::ActivityGroupType);
if block.has_key("guest_description") {
let mut sc = ScopeContext::new(Scopes::Character, key);
vd.field_validated_sc("guest_description", &mut sc, validate_desc);
} else {
let loca = format!("{key}_guest_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
}
let ch_host_activity_sc = |key: &Token| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("activity", Scopes::Activity, key);
sc
};
let ac_host_activity_sc = |key: &Token| {
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("activity", Scopes::Activity, key);
sc
};
if block.has_key("conclusion_description") {
let mut sc = ch_host_activity_sc(key);
vd.field_validated_sc("conclusion_description", &mut sc, validate_desc);
} else {
let loca = format!("{key}_conclusion_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
}
let mut join_chance_sc = ScopeContext::new(Scopes::Character, key);
join_chance_sc.define_name("host", Scopes::Character, key);
join_chance_sc.define_name("minimal_travel_time", Scopes::Value, key);
join_chance_sc.define_name("estimated_arrival_diff_days", Scopes::Value, key);
join_chance_sc.define_list("special_guests", Scopes::Character, key);
let mut special_guests_sc = ScopeContext::new(Scopes::Character, key);
if has_special_option {
special_guests_sc.define_name("special_option", Scopes::Flag, key);
}
special_guests_sc.define_list("special_guests", Scopes::Character, key);
let mut max_guests_sc = ScopeContext::new(Scopes::all(), key);
if let Some(block) = block.get_field_block("special_guests") {
for (key, _) in block.iter_definitions() {
join_chance_sc.define_name(key.as_str(), Scopes::Character, key);
special_guests_sc.define_name(key.as_str(), Scopes::Character, key);
}
}
if let Some(block) = block.get_field_block("options") {
for (_, block) in block.iter_definitions() {
for (key, _) in block.iter_definitions() {
join_chance_sc.define_name(key.as_str(), Scopes::Flag, key);
max_guests_sc.define_name(key.as_str(), Scopes::Flag, key);
}
}
}
let mut sc = ScopeContext::new(Scopes::Character, key);
vd.field_trigger("is_shown", Tooltipped::No, &mut sc);
vd.field_trigger("can_plan", Tooltipped::Yes, &mut sc);
vd.field_trigger("can_start", Tooltipped::Yes, &mut sc);
vd.field_trigger("can_start_showing_failures_only", Tooltipped::FailuresOnly, &mut sc);
vd.field_bool("can_always_plan");
vd.field_script_value("num_pickable_phases", &mut sc);
vd.field_script_value("max_pickable_phases_per_province", &mut sc);
vd.field_validated_block_sc("wait_time_before_start", &mut sc, validate_duration);
vd.field_validated_block_sc("max_guest_arrival_delay_time", &mut sc, validate_duration);
vd.field_numeric("max_route_deviation_mult");
vd.field_validated_block_sc("cooldown", &mut sc, validate_duration);
vd.replaced_field("is_grand_activity", "activity_group_type");
vd.field_bool("is_single_location");
vd.field_choice("planner_type", &["province", "holder"]);
vd.field_script_value_no_breakdown("ai_will_do", &mut sc);
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();
}
});
vd.field_script_value_no_breakdown("ai_select_num_provinces", &mut sc);
vd.field_trigger_builder("is_valid", Tooltipped::No, ac_host_activity_sc);
vd.field_effect_builder("on_invalidated", Tooltipped::No, ac_host_activity_sc);
vd.field_effect_builder("on_host_death", Tooltipped::No, ac_host_activity_sc);
vd.field_choice("province_filter", PROVINCE_FILTERS);
vd.field_choice("ai_province_filter", PROVINCE_FILTERS);
let province_filter = block.get_field_value("province_filter");
let ai_province_filter = block.get_field_value("ai_province_filter");
if let Some(province_filter) = province_filter
&& province_filter.is("landed_title")
&& let Some(ai_province_filter) = ai_province_filter
&& ai_province_filter.is("landed_title")
{
if province_filter != ai_province_filter {
let msg = "for `landed_title`, `province_filter` and `ai_province_filter` must be the same";
err(ErrorKey::Validation).msg(msg).loc(ai_province_filter).push();
}
vd.field_item("province_filter_target", Item::Title);
} else if let Some(province_filter) = province_filter
&& province_filter.is("geographical_region")
&& let Some(ai_province_filter) = ai_province_filter
&& ai_province_filter.is("geographical_region")
{
if province_filter != ai_province_filter {
let msg = "for `geographical_region`, `province_filter` and `ai_province_filter` must be the same";
err(ErrorKey::Validation).msg(msg).loc(ai_province_filter).push();
}
vd.field_item("province_filter_target", Item::Region);
} else {
vd.ban_field(
"province_filter_target",
|| "`landed_title` or `geographical_region` filters",
);
}
let mut sc = ScopeContext::new(Scopes::Province, key);
sc.define_name("host", Scopes::Character, key);
vd.field_script_value_no_breakdown("province_score", &mut sc);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_trigger("is_location_valid", Tooltipped::No, &mut sc);
sc.define_name("score", Scopes::Value, key);
vd.field_script_value_no_breakdown("ai_will_select_province", &mut sc);
vd.field_integer("max_province_icons");
vd.field_item("special_option_category", Item::ActivityOptionCategory);
let special_option_category = block.get_field_value("special_option_category");
vd.field_validated_block("options", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|key, block| {
data.verify_exists(Item::Localization, key);
let mut vd = Validator::new(block, data);
let is_special_option =
special_option_category.is_some_and(|special| key.is(special.as_str()));
if !is_special_option {
let define = "NGameIcons|ACTIVITY_OPTION_CATEGORY_TEXTURE_PATH";
data.verify_icon(define, key, ".dds");
}
vd.unknown_block_fields(|key, block| {
validate_option(key, block, data, has_special_option, is_special_option);
});
});
});
vd.field_validated_block("phases", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|key, block| {
validate_phase(key, block, data, has_special_option);
});
});
vd.field_validated_key_block("cost", |key, block, data| {
let mut sc = ScopeContext::new(Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
sc.define_name("province", Scopes::Province, key);
sc.define_list("provinces", Scopes::Province, key);
validate_cost(block, data, &mut sc);
});
let mut sc = ScopeContext::new(Scopes::Character, key);
vd.field_validated_block_sc("ui_predicted_cost", &mut sc, validate_cost);
vd.field_script_value("max_guests", &mut max_guests_sc);
vd.field_script_value("reserved_guest_slots", &mut max_guests_sc);
vd.field_bool("allow_zero_guest_invites");
vd.field_validated_block("guest_invite_rules", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_validated_block("rules", |block, data| {
validate_rules(block, data);
});
vd.field_validated_block("defaults", |block, data| {
validate_rules(block, data);
});
});
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("host", Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_trigger("can_be_activity_guest", Tooltipped::No, &mut sc);
vd.field_list("guest_subsets");
vd.field_validated_block("special_guests", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|key, block| {
validate_special_guest(
key,
block,
data,
has_special_option,
&mut special_guests_sc,
);
});
});
vd.field_validated_block("locales", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|_, block| {
let mut vd = Validator::new(block, data);
vd.field_trigger_builder("is_available", Tooltipped::No, ac_host_activity_sc);
vd.field_list_items("locales", Item::ActivityLocale);
});
});
let mut sc = ch_host_activity_sc(key);
vd.field_validated_block_sc("locale_cooldown", &mut sc, validate_duration);
vd.field_validated_block_sc("auto_select_locale_cooldown", &mut sc, validate_duration);
for field in &[
"on_enter_travel_state",
"on_enter_passive_state",
"on_enter_active_state",
"on_leave_travel_state",
"on_leave_passive_state",
"on_leave_active_state",
"on_travel_state_pulse",
"on_passive_state_pulse",
"on_active_state_pulse",
"on_complete",
] {
vd.field_effect_builder(field, Tooltipped::No, ch_host_activity_sc);
}
vd.field_effect_builder("on_start", Tooltipped::No, |key| {
let mut sc = ch_host_activity_sc(key);
sc.change_root(Scopes::Activity, key);
sc
});
sc.change_root(Scopes::None, key);
vd.field_validated_block_sc("early_locale_opening_duration", &mut sc, validate_duration);
vd.field_bool("open_invite");
vd.field_validated_block("host_intents", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_list_items("intents", Item::ActivityIntent);
vd.field_item("default", Item::ActivityIntent);
vd.field_list_items("player_defaults", Item::ActivityIntent);
});
vd.field_validated_block("guest_intents", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_list_items("intents", Item::ActivityIntent);
vd.field_item("default", Item::ActivityIntent);
vd.field_list_items("player_defaults", Item::ActivityIntent);
});
vd.field_validated_block_sc(
"guest_join_chance",
&mut join_chance_sc,
validate_modifiers_with_base,
);
vd.field_validated_block("activity_window_widgets", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_value_fields(|key, _| {
let pathname = format!("gui/activity_window_widgets/{key}.gui");
data.verify_exists_implied(Item::File, &pathname, key);
});
});
vd.field_validated_block("activity_planner_widgets", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_value_fields(|key, _| {
let pathname = format!("gui/activity_window_widgets/{key}.gui");
data.verify_exists_implied(Item::File, &pathname, key);
});
});
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("activity", Scopes::Activity, key);
vd.multi_field_validated("map_entity", |bv, data| match bv {
BV::Value(token) => data.verify_exists(Item::Entity, token),
BV::Block(block) => {
let mut vd = Validator::new(block, data);
vd.field_trigger("trigger", Tooltipped::No, &mut sc);
vd.field_item("reference", Item::Entity);
}
});
vd.multi_field_validated_block("background", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_trigger("trigger", Tooltipped::No, &mut sc);
vd.field_item("texture", Item::File);
vd.field_item("environment", Item::PortraitEnvironment);
vd.field_item("ambience", Item::Sound);
vd.field_item("music", Item::Music);
});
vd.multi_field_validated_block("locale_background", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_trigger("trigger", Tooltipped::No, &mut sc);
vd.field_item("texture", Item::File);
vd.field_item("environment", Item::PortraitEnvironment);
vd.field_item("ambience", Item::Sound);
vd.field_item("music", Item::Music);
});
vd.field_validated_block("window_characters", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_block_fields(|key, block| {
validate_window_characters(key, block, data);
});
});
vd.field_validated_key_block("travel_entourage_selection", |key, block, data| {
validate_tes(key, block, data, has_special_option);
});
vd.field_validated_block("pulse_actions", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_list_items("entries", Item::ActivityPulseAction);
vd.field_numeric_range("chance_of_no_event", 0.0..=100.0);
});
}
}
fn validate_rules(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.integer_keys(|_, bv| {
if let Some(token) = bv.expect_value() {
data.verify_exists(Item::GuestInviteRule, token);
}
});
}
pub fn validate_tes(key: &Token, block: &Block, data: &Everything, has_special_option: bool) {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("owner", Scopes::Character, key);
sc.define_list("special_guests", Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
let mut vd = Validator::new(block, data);
vd.field_script_value("weight", &mut sc);
let mut sc = ScopeContext::new(Scopes::None, key);
vd.field_script_value("max", &mut sc);
vd.field_script_value("ai_max", &mut sc);
vd.field_integer("invite_rule_order");
}
fn validate_window_characters(key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let loca = format!("activity_window_character_{key}");
data.verify_exists_implied(Item::Localization, &loca, key);
vd.field_item("camera", Item::PortraitCamera);
vd.field_effect_builder("effect", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("activity", Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("player", Scopes::Character, key);
sc.define_list("characters", Scopes::Character, key);
sc
});
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("activity", Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("player", Scopes::Character, key);
sc.define_name("character", Scopes::Character, key);
vd.field_validated_sc("scripted_animation", &mut sc, validate_scripted_animation);
vd.field_item("animation", Item::PortraitAnimation);
}
fn validate_phase(key: &Token, block: &Block, data: &Everything, has_special_option: bool) {
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let define = "NGameIcons|ACTIVITY_PHASE_ICON_PATH";
data.verify_icon(define, key, ".dds");
let mut vd = Validator::new(block, data);
vd.field_bool("is_predefined");
let mut sc = ScopeContext::new(Scopes::None, key);
vd.field_script_value("number_of_picks", &mut sc);
vd.field_integer("order");
vd.field_choice(
"location_source",
&["pickable", "first_picked_phase", "last_picked_phase", "scripted"],
);
if block.field_value_is("location_source", "scripted") {
vd.field_effect_builder("select_scripted_location", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
sc
});
} else {
vd.ban_field("select_scripted_location", || "location_source = scripted");
}
vd.field_script_value_no_breakdown_builder("ai_will_do", |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("province", Scopes::Province, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
sc
});
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("province", Scopes::Province, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_trigger("is_shown", Tooltipped::No, &mut sc);
vd.field_trigger("can_pick", Tooltipped::Yes, &mut sc);
vd.field_trigger_builder("is_valid", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("province", Scopes::Province, key);
sc
});
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("activity", Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
vd.field_effect("on_enter_phase", Tooltipped::No, &mut sc);
vd.field_effect("on_phase_active", Tooltipped::No, &mut sc);
vd.field_effect("on_end", Tooltipped::No, &mut sc);
vd.field_effect("on_monthly_pulse", Tooltipped::No, &mut sc);
vd.field_effect("on_weekly_pulse", Tooltipped::No, &mut sc);
sc.define_name("province", Scopes::Province, key);
vd.field_effect("on_invalidated", Tooltipped::No, &mut sc);
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("province", Scopes::Province, key);
sc.define_name("previous_province", Scopes::Province, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_validated_block_sc("cost", &mut sc, validate_cost);
}
fn validate_special_guest(
key: &Token,
block: &Block,
data: &Everything,
has_special_option: bool,
special_guests_sc: &mut ScopeContext,
) {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_for_host");
data.verify_exists_implied(Item::Localization, &loca, key);
let loca = format!("{key}_desc_for_host");
data.verify_exists_implied(Item::Localization, &loca, key);
let mut sc = ScopeContext::new(Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_trigger("is_shown", Tooltipped::No, &mut sc);
vd.field_bool("is_required");
vd.field_effect("select_character", Tooltipped::No, special_guests_sc);
special_guests_sc.define_name("host", Scopes::Character, key);
vd.field_trigger("can_pick", Tooltipped::No, special_guests_sc);
vd.field_effect("on_invite", Tooltipped::No, special_guests_sc);
vd.field_script_value_no_breakdown_rooted("ai_will_do", Scopes::Character);
}
#[derive(Clone, Debug)]
pub struct ActivityLocale {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::ActivityLocale, ActivityLocale::add)
}
impl ActivityLocale {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::ActivityLocale, key, block, Box::new(Self {}));
}
}
impl DbKind for ActivityLocale {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let define = "NGameIcons|ACTIVITY_LOCALE_ICON_PATH";
data.verify_icon(define, key, ".dds");
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("activity", Scopes::Activity, key);
vd.field_trigger("is_available", Tooltipped::No, &mut sc);
vd.field_script_value("chance", &mut sc);
vd.field_effect("on_enter_locale", Tooltipped::No, &mut sc);
vd.field_script_value_no_breakdown("ai_will_do", &mut sc);
vd.field_validated_block_sc("cooldown", &mut sc, validate_duration);
vd.multi_field_validated_key("visuals", validate_visuals);
}
}
fn validate_visuals(key: &Token, bv: &BV, data: &Everything) {
fn validate_visual(visual: &Token, data: &Everything) {
let pathname = format!("/gui/activity_locale_widgets/{visual}.gui");
data.verify_exists_implied(Item::File, &pathname, visual);
}
match bv {
BV::Value(token) => validate_visual(token, data),
BV::Block(block) => {
let mut vd = Validator::new(block, data);
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("activity", Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
vd.field_trigger("trigger", Tooltipped::No, &mut sc);
vd.req_field("reference");
if let Some(token) = vd.field_value("reference") {
validate_visual(token, data);
}
}
}
}
#[derive(Clone, Debug)]
pub struct GuestInviteRule {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::GuestInviteRule, GuestInviteRule::add)
}
impl GuestInviteRule {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::GuestInviteRule, key, block, Box::new(Self {}));
}
}
impl DbKind for GuestInviteRule {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
vd.field_effect_builder("effect", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_list("characters", Scopes::Character, key);
sc
});
}
}
#[derive(Clone, Debug)]
pub struct ActivityPulseAction {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::ActivityPulseAction, ActivityPulseAction::add)
}
impl ActivityPulseAction {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::ActivityPulseAction, key, block, Box::new(Self {}));
}
}
impl DbKind for ActivityPulseAction {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
let loca = format!("{key}_title");
data.verify_exists_implied(Item::Localization, &loca, key);
let define = "NGameIcons|ACTIVITY_PULSE_ACTION_ICON_PATH";
let icon = vd.field_value("icon").unwrap_or(key);
data.verify_icon(define, icon, ".dds");
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("activity", Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc.define_name("province", Scopes::Province, key);
vd.field_trigger("is_valid", Tooltipped::No, &mut sc);
vd.field_script_value("weight", &mut sc);
vd.field_effect("effect", Tooltipped::No, &mut sc);
}
}
#[derive(Clone, Debug)]
pub struct ActivityIntent {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::ActivityIntent, ActivityIntent::add)
}
impl ActivityIntent {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::ActivityIntent, key, block, Box::new(Self {}));
}
}
impl DbKind for ActivityIntent {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
let define = "NGameIcons|ACTIVITY_INTENTS_ICON_PATH";
let icon = vd.field_value("icon").unwrap_or(key);
data.verify_icon(define, icon, ".dds");
vd.field_bool("auto_complete");
for field in &["is_shown", "is_valid"] {
vd.field_trigger_builder(field, Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("magnificence", Scopes::Value, key);
sc.define_name("special_option", Scopes::Flag, key);
sc
});
}
vd.field_trigger_builder("is_target_valid", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("target", Scopes::Character, key);
sc.define_name("special_option", Scopes::Flag, key);
sc
});
vd.field_effect_rooted("on_invalidated", Tooltipped::No, Scopes::Character);
vd.field_effect_builder("on_target_invalidated", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("target", Scopes::Character, key);
sc
});
vd.field_script_value_no_breakdown_builder("ai_will_do", |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("activity", Scopes::Activity, key);
sc
});
vd.multi_field_validated_block("ai_targets", validate_ai_targets);
vd.field_validated_block("ai_target_quick_trigger", validate_quick_trigger);
vd.field_script_value_no_breakdown_builder("ai_target_score", |key| {
let mut sc = ScopeContext::new(Scopes::Character, key);
sc.define_name("target", Scopes::Character, key);
sc.define_name("special_option", Scopes::Flag, key);
sc
});
let mut sc = ScopeContext::new(Scopes::Character, key);
vd.field_validated_sc("scripted_animation", &mut sc, validate_scripted_animation);
}
}
fn validate_option(
key: &Token,
block: &Block,
data: &Everything,
has_special_option: bool,
is_special_option: bool,
) {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
let loca = format!("{key}_desc");
data.verify_exists_implied(Item::Localization, &loca, key);
if is_special_option {
data.verify_icon("NGameIcons|ACTIVITY_OPTION_TEXTURE_PATH", key, ".dds");
data.verify_icon("NGameIcons|ACTIVITY_OPTION_ICON_PATH", key, "_icon.dds");
}
let mut sc = ScopeContext::new(Scopes::Character, key);
if has_special_option {
sc.define_name("special_option", Scopes::Flag, key);
}
vd.field_trigger("is_shown", Tooltipped::No, &mut sc);
vd.field_trigger("is_valid", Tooltipped::Yes, &mut sc);
vd.field_script_value_no_breakdown("ai_will_do", &mut sc);
vd.field_effect_builder("on_start", Tooltipped::No, |key| {
let mut sc = ScopeContext::new(Scopes::Activity, key);
sc.define_name("activity", Scopes::Activity, key);
sc.define_name("host", Scopes::Character, key);
sc
});
vd.field_validated("default", |bv, data| {
match bv {
BV::Value(token) => {
if !token.is("yes") {
let msg = "expected `default = yes`";
warn(ErrorKey::Validation).msg(msg).loc(token).push();
}
}
BV::Block(block) => {
let mut sc = ScopeContext::new(Scopes::Character, key);
validate_trigger(block, data, &mut sc, Tooltipped::No);
}
}
});
vd.field_list_items("blocked_intents", Item::ActivityIntent);
vd.field_list_items("blocked_phases", Item::ActivityPhase);
let mut sc = ScopeContext::new(Scopes::Character, key);
vd.field_validated_block_sc("cost", &mut sc, validate_cost);
vd.field_validated_key_block("travel_entourage_selection", |key, block, data| {
validate_tes(key, block, data, has_special_option);
});
}
#[derive(Clone, Debug)]
pub struct ActivityGroupType {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3, Item::ActivityGroupType, ActivityGroupType::add)
}
impl ActivityGroupType {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::ActivityGroupType, key, block, Box::new(Self {}));
}
}
impl DbKind for ActivityGroupType {
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");
}
}