use crate::block::Block;
#[cfg(feature = "vic3")]
use crate::context::ScopeContext;
use crate::datacontext::DataContext;
use crate::datatype::Datatype;
use crate::db::{Db, DbKind};
use crate::everything::Everything;
use crate::game::{Game, GameFlags};
use crate::gui::validate_datatype_field;
use crate::item::{Item, ItemLoader};
use crate::scopes::Scopes;
use crate::token::Token;
use crate::tooltipped::Tooltipped;
use crate::validator::{Validator, ValueValidator};
#[derive(Clone, Debug)]
pub struct TutorialLesson {}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3.union(GameFlags::Vic3), Item::TutorialLesson, TutorialLesson::add)
}
impl TutorialLesson {
pub fn add(db: &mut Db, key: Token, block: Block) {
db.add(Item::TutorialLesson, key, block, Box::new(Self {}));
}
}
impl DbKind for TutorialLesson {
fn add_subitems(&self, _key: &Token, block: &Block, db: &mut Db) {
let chain = block.get_field_value("chain");
for (key, block) in block.iter_definitions() {
if !key.is("trigger") && !key.is("trigger_transition") {
db.add(
Item::TutorialLessonStep,
key.clone(),
block.clone(),
Box::new(TutorialLessonStep { chain: chain.cloned() }),
);
}
}
}
#[allow(unused_variables)] fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.field_item("chain", Item::TutorialLessonChain);
vd.field_bool("start_automatically");
vd.field_trigger_rooted("trigger", Tooltipped::No, game_tutorial_scope());
vd.multi_field_value("gui_tag");
vd.field_value("highlight_widget");
vd.field_validated_key("highlight_widget_dynamic_loc", |key, bv, data| {
validate_datatype_field(
Datatype::Unknown,
key,
bv,
data,
&mut DataContext::new(),
false,
);
});
#[cfg(feature = "vic3")]
{
let mut sc = ScopeContext::new(Scopes::JournalEntry, key);
vd.multi_field_target("highlight_target", &mut sc, Scopes::all());
}
vd.multi_field_validated_block("trigger_transition", validate_trigger_transition);
vd.field_integer("delay");
vd.field_integer("default_lesson_step_delay");
vd.field_bool("finish_gamestate_tutorial");
vd.field_bool("shown_in_encyclopedia");
vd.field_item("encyclopedia_text", Item::Localization);
vd.unknown_block_fields(|_, _| ());
}
}
#[derive(Clone, Debug)]
pub struct TutorialLessonChain {
gamestate_tutorial: bool,
}
inventory::submit! {
ItemLoader::Normal(GameFlags::Ck3.union(GameFlags::Vic3), Item::TutorialLessonChain, TutorialLessonChain::add)
}
impl TutorialLessonChain {
pub fn add(db: &mut Db, key: Token, block: Block) {
let gamestate_tutorial =
block.get_field_bool("save_progress_in_gamestate").unwrap_or(false);
db.add(Item::TutorialLessonChain, key, block, Box::new(Self { gamestate_tutorial }));
}
}
impl DbKind for TutorialLessonChain {
fn validate(&self, _key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.field_trigger_rooted("trigger", Tooltipped::No, game_tutorial_scope());
vd.field_integer("delay");
vd.field_bool("save_progress_in_gamestate");
}
fn has_property(
&self,
_key: &Token,
_block: &Block,
property: &str,
_data: &Everything,
) -> bool {
property == "gamestate_tutorial" && self.gamestate_tutorial
}
fn merge_in(&mut self, other: Box<dyn DbKind>) {
if let Some(other) = other.as_any().downcast_ref::<Self>() {
self.gamestate_tutorial |= other.gamestate_tutorial;
}
}
}
#[derive(Clone, Debug)]
pub struct TutorialLessonStep {
chain: Option<Token>,
}
impl DbKind for TutorialLessonStep {
fn validate(&self, key: &Token, block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
data.verify_exists(Item::Localization, key);
vd.field_item("text", Item::Localization);
vd.field_item("header_info", Item::Localization);
vd.multi_field_value("gui_tag");
vd.field_value("window_name");
vd.multi_field_value("highlight_widget");
vd.field_validated_key("highlight_widget_dynamic_loc", |key, bv, data| {
validate_datatype_field(
Datatype::Unknown,
key,
bv,
data,
&mut DataContext::new(),
false,
);
});
#[cfg(feature = "vic3")]
{
let mut sc = ScopeContext::new(Scopes::JournalEntry, key);
vd.multi_field_target("highlight_target", &mut sc, Scopes::all());
}
vd.field_item("soundeffect", Item::Sound);
vd.field_item("voice", Item::Sound);
vd.field_bool("repeat_sound_effect");
vd.field_integer("delay");
vd.field_value("animation");
vd.field_bool("shown_in_encyclopedia");
vd.field_item("encyclopedia_text", Item::Localization);
vd.multi_field_validated_block("gui_transition", |block, data| {
let mut vd = Validator::new(block, data);
vd.field_value("button_id");
vd.field_item("button_text", Item::Localization);
vd.field_validated_value("target", validate_lesson_target);
vd.field_trigger_rooted("enabled", Tooltipped::No, game_tutorial_scope());
});
vd.multi_field_validated_block("trigger_transition", validate_trigger_transition);
vd.field_effect_rooted("interface_effect", Tooltipped::No, Scopes::None);
if self.chain.as_ref().is_some_and(|t| {
data.item_has_property(Item::TutorialLessonChain, t.as_str(), "gamestate_tutorial")
}) {
vd.field_bool("pause_game");
vd.field_bool("force_pause_game");
vd.field_effect_rooted("effect", Tooltipped::No, game_tutorial_scope());
} else {
vd.ban_field("pause_game", || "gamestate tutorial chains");
vd.ban_field("force_pause_game", || "gamestate tutorial chains");
vd.ban_field("effect", || "gamestate tutorial chains");
}
#[cfg(feature = "ck3")]
vd.field_validated_block("highlight_widget_with_index", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_value_fields(|_, value| {
let mut vvd = ValueValidator::new(value, data);
vvd.integer();
});
});
#[cfg(feature = "ck3")]
vd.field_validated_block("highlight_child_widget_of", |block, data| {
let mut vd = Validator::new(block, data);
vd.unknown_value_fields(|_, _| {
});
});
}
}
fn validate_trigger_transition(block: &Block, data: &Everything) {
let mut vd = Validator::new(block, data);
vd.field_trigger_rooted("trigger", Tooltipped::No, game_tutorial_scope());
vd.field_validated_value("target", validate_lesson_target);
vd.field_value("button_id");
vd.field_item("button_text", Item::Localization);
}
fn validate_lesson_target(_key: &Token, mut vd: ValueValidator) {
vd.maybe_is("lesson_finish");
vd.maybe_is("lesson_abort");
vd.item(Item::TutorialLessonStep);
}
fn game_tutorial_scope() -> Scopes {
match Game::game() {
#[cfg(feature = "ck3")]
Game::Ck3 => Scopes::Character,
#[cfg(feature = "vic3")]
Game::Vic3 => Scopes::Country,
#[cfg(feature = "imperator")]
Game::Imperator => unimplemented!(),
#[cfg(feature = "hoi4")]
Game::Hoi4 => unimplemented!(),
}
}