use crate::block::{BV, Block, Comparator, Eq::Single};
use crate::context::{ScopeContext, Temporary};
use crate::desc::validate_desc;
use crate::effect::{validate_effect, validate_effect_control};
use crate::everything::Everything;
#[cfg(feature = "jomini")]
use crate::game::Game;
use crate::item::Item;
use crate::lowercase::Lowercase;
use crate::report::{ErrorKey, Severity, err, warn};
use crate::scopes::Scopes;
#[cfg(feature = "jomini")]
use crate::script_value::validate_script_value;
use crate::special_tokens::SpecialTokens;
use crate::token::Token;
use crate::tooltipped::Tooltipped;
use crate::trigger::{validate_target_ok_this, validate_trigger_key_bv};
use crate::validate::{validate_identifier, validate_optional_duration};
use crate::validator::{Validator, ValueValidator};
#[allow(dead_code)]
#[cfg(feature = "imperator")]
pub fn validate_add_to_list_imperator(
key: &Token,
mut vd: ValueValidator,
sc: &mut ScopeContext,
_tooltipped: Tooltipped,
) {
let temp = if key.as_str().contains("_temporary_") { Temporary::Yes } else { Temporary::No };
vd.identifier("list name");
sc.define_or_expect_list_this(vd.value(), vd.data(), temp);
vd.accept();
}
#[allow(dead_code)]
#[cfg(any(feature = "ck3", feature = "vic3"))]
pub fn validate_add_to_list(
key: &Token,
bv: &BV,
data: &Everything,
sc: &mut ScopeContext,
_tooltipped: Tooltipped,
) {
let temp = if key.as_str().contains("_temporary_") { Temporary::Yes } else { Temporary::No };
match bv {
BV::Value(name) => {
validate_identifier(name, "list name", Severity::Error);
sc.define_or_expect_list_this(name, data, temp);
}
BV::Block(block) => {
let mut vd = Validator::new(block, data);
vd.req_field("name");
vd.req_field("value");
if let Some(target) = vd.field_value("value").cloned()
&& let Some(name) = vd.field_value("name")
{
validate_identifier(name, "list name", Severity::Error);
let target_scopes = sc.local_list_scopes(name.as_str(), data);
let outscopes = validate_target_ok_this(&target, data, sc, target_scopes);
sc.define_or_expect_list(name, outscopes, data, temp);
}
}
}
}
#[cfg(feature = "jomini")]
pub fn validate_add_to_variable_list(
key: &Token,
_block: &Block,
data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
_tooltipped: Tooltipped,
) {
vd.req_field("name");
vd.req_field("target");
if let Some(target) = vd.field_value("target").cloned()
&& let Some(name) = vd.field_value("name")
{
validate_identifier(name, "list name", Severity::Error);
if key.as_str().contains("_local_") {
let target_scopes = sc.local_list_scopes(name.as_str(), data);
let outscopes = validate_target_ok_this(&target, data, sc, target_scopes);
sc.define_or_expect_local_list(name, outscopes, data);
} else if key.as_str().contains("_global_") {
let target_scopes = data.global_list_scopes.scopes(name.as_str());
let outscopes = validate_target_ok_this(&target, data, sc, target_scopes);
data.global_list_scopes.expect(name.as_str(), name, outscopes);
} else {
let target_scopes = data.variable_list_scopes.scopes(name.as_str());
let outscopes = validate_target_ok_this(&target, data, sc, target_scopes);
data.variable_list_scopes.expect(name.as_str(), name, outscopes);
}
}
if key.starts_with("add_") && (Game::is_ck3() || Game::is_vic3()) {
validate_optional_duration(&mut vd, sc);
}
}
#[cfg(feature = "jomini")]
pub fn validate_change_variable(
key: &Token,
_block: &Block,
data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
_tooltipped: Tooltipped,
) {
vd.req_field("name");
if let Some(name) = vd.field_value("name") {
validate_identifier(name, "variable name", Severity::Error);
if key.as_str().contains("_local_") {
sc.expect_local(name, Scopes::Value, data);
} else if key.as_str().contains("_global_") {
data.global_scopes.expect(name.as_str(), name, Scopes::Value);
} else {
data.variable_scopes.expect(name.as_str(), name, Scopes::Value);
}
}
vd.field_script_value("add", sc);
vd.field_script_value("subtract", sc);
vd.field_script_value("multiply", sc);
vd.field_script_value("divide", sc);
vd.field_script_value("modulo", sc);
vd.field_script_value("min", sc);
vd.field_script_value("max", sc);
}
#[cfg(feature = "jomini")]
pub fn validate_clamp_variable(
key: &Token,
_block: &Block,
data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
_tooltipped: Tooltipped,
) {
vd.req_field("name");
if let Some(name) = vd.field_value("name") {
validate_identifier(name, "variable name", Severity::Error);
if key.as_str().contains("_local_") {
sc.expect_local(name, Scopes::Value, data);
} else if key.as_str().contains("_global_") {
data.global_scopes.expect(name.as_str(), name, Scopes::Value);
} else {
data.variable_scopes.expect(name.as_str(), name, Scopes::Value);
}
}
vd.field_script_value("min", sc);
vd.field_script_value("max", sc);
}
#[cfg(feature = "jomini")]
pub fn validate_random_list(
key: &Token,
_block: &Block,
data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
tooltipped: Tooltipped,
special_tokens: &mut SpecialTokens,
) -> bool {
let caller = Lowercase::new(key.as_str());
vd.field_integer("pick");
vd.field_bool("unique"); vd.field_validated_sc("desc", sc, validate_desc);
let mut has_tooltip = false;
vd.unknown_block_fields(|key, block| {
if let Some(n) = key.expect_number() {
if n < 0.0 {
let msg = "negative weights make the whole `random_list` fail";
err(ErrorKey::Range).strong().msg(msg).loc(key).push();
} else if n > 0.0 && n < 1.0 {
let msg = "fractional weights are treated as just 0 in `random_list`";
err(ErrorKey::Range).strong().msg(msg).loc(key).push();
} else if n.fract() != 0.0 {
let msg = "fractions are discarded in `random_list` weights";
warn(ErrorKey::Range).strong().msg(msg).loc(key).push();
}
has_tooltip |=
validate_effect_control(&caller, block, data, sc, tooltipped, special_tokens);
}
});
if has_tooltip && key.is("random_list") {
special_tokens.insert(key);
}
has_tooltip
}
#[cfg(feature = "jomini")]
pub fn validate_remove_from_list(
_key: &Token,
mut vd: ValueValidator,
sc: &mut ScopeContext,
_tooltipped: Tooltipped,
) {
vd.identifier("list name");
sc.expect_list(vd.value(), vd.data());
vd.accept();
}
#[cfg(feature = "jomini")]
pub fn validate_round_variable(
key: &Token,
_block: &Block,
data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
_tooltipped: Tooltipped,
) {
vd.req_field("name");
vd.req_field("nearest");
if let Some(name) = vd.field_value("name") {
validate_identifier(name, "variable name", Severity::Error);
if key.as_str().contains("_local_") {
sc.expect_local(name, Scopes::Value, data);
} else if key.as_str().contains("_global_") {
data.global_scopes.expect(name.as_str(), name, Scopes::Value);
} else {
data.variable_scopes.expect(name.as_str(), name, Scopes::Value);
}
}
vd.field_script_value("nearest", sc);
}
#[cfg(feature = "jomini")]
pub fn validate_save_scope(
key: &Token,
mut vd: ValueValidator,
sc: &mut ScopeContext,
_tooltipped: Tooltipped,
) {
let temp = if key.as_str().contains("_temporary_") { Temporary::Yes } else { Temporary::No };
vd.identifier("scope name");
sc.save_current_scope(vd.value().as_str(), temp);
vd.accept();
}
#[cfg(feature = "jomini")]
pub fn validate_save_scope_value(
key: &Token,
_block: &Block,
_data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
_tooltipped: Tooltipped,
) {
let temp = if key.as_str().contains("_temporary_") { Temporary::Yes } else { Temporary::No };
vd.req_field("name");
vd.req_field("value");
if let Some(name) = vd.field_identifier_or_flag("name", sc) {
sc.define_name_token(name.as_str(), Scopes::primitive(), name, temp);
}
vd.field_script_value_or_flag("value", sc);
}
#[cfg(feature = "jomini")]
pub fn validate_set_variable(
key: &Token,
bv: &BV,
data: &Everything,
sc: &mut ScopeContext,
_tooltipped: Tooltipped,
) {
match bv {
BV::Value(token) => {
validate_identifier(token, "variable name", Severity::Error);
if key.as_str().contains("_local_") {
sc.set_local_variable(token, Scopes::Bool);
} else if key.as_str().contains("_global_") {
data.global_scopes.expect(token.as_str(), token, Scopes::Bool);
} else {
data.variable_scopes.expect(token.as_str(), token, Scopes::Bool);
}
}
BV::Block(block) => {
let mut vd = Validator::new(block, data);
vd.set_case_sensitive(false);
vd.req_field("name");
let name = vd.field_identifier("name", "variable name").cloned();
vd.field_validated("value", |bv, data| match bv {
BV::Value(token) => {
if let Some(name) = &name {
if key.as_str().contains("_local_") {
let target_scopes = sc.local_variable_scopes(name.as_str(), data);
let outscopes = validate_target_ok_this(token, data, sc, target_scopes);
sc.set_local_variable(name, outscopes);
} else if key.as_str().contains("_global_") {
let target_scopes = data.global_scopes.scopes(name.as_str());
let outscopes = validate_target_ok_this(token, data, sc, target_scopes);
data.global_scopes.expect(name.as_str(), name, outscopes);
} else {
let target_scopes = data.variable_scopes.scopes(name.as_str());
let outscopes = validate_target_ok_this(token, data, sc, target_scopes);
data.variable_scopes.expect(name.as_str(), name, outscopes);
}
}
}
BV::Block(_) => {
#[cfg(feature = "jomini")]
if Game::is_jomini() {
validate_script_value(bv, data, sc);
if let Some(name) = &name {
if key.as_str().contains("_local_") {
sc.set_local_variable(name, Scopes::Value);
} else if key.as_str().contains("_global_") {
data.global_scopes.expect(name.as_str(), name, Scopes::Value);
} else {
data.variable_scopes.expect(name.as_str(), name, Scopes::Value);
}
}
}
}
});
validate_optional_duration(&mut vd, sc);
}
}
}
#[cfg(feature = "jomini")]
pub fn validate_switch(
key: &Token,
_block: &Block,
data: &Everything,
sc: &mut ScopeContext,
mut vd: Validator,
tooltipped: Tooltipped,
) {
vd.set_case_sensitive(true);
vd.req_field("trigger");
if let Some(target) = vd.field_value("trigger").cloned() {
let mut count = 0;
vd.set_allow_questionmark_equals(true);
vd.unknown_block_fields(|key, block| {
count += 1;
if !key.is("fallback") {
let synthetic_bv = BV::Value(key.clone());
validate_trigger_key_bv(
&target,
Comparator::Equals(Single),
&synthetic_bv,
data,
sc,
tooltipped,
false,
Severity::Error,
);
}
validate_effect(block, data, sc, tooltipped);
});
if count == 0 {
let msg = "switch with no branches";
err(ErrorKey::Logic).msg(msg).loc(key).push();
}
}
}
#[cfg(feature = "jomini")]
pub fn validate_trigger_event(
_key: &Token,
bv: &BV,
data: &Everything,
sc: &mut ScopeContext,
_tooltipped: Tooltipped,
) {
match bv {
BV::Value(token) => {
data.verify_exists(Item::Event, token);
data.event_check_scope(token, sc);
if let Some(mut event_sc) = sc.root_for_event(token, data) {
data.event_validate_call(token, &mut event_sc);
}
}
BV::Block(block) => {
let mut vd = Validator::new(block, data);
vd.set_case_sensitive(false);
vd.field_event("id", sc);
vd.field_action("on_action", sc);
#[cfg(feature = "ck3")]
if Game::is_ck3() {
vd.field_target("saved_event_id", sc, Scopes::Flag);
vd.field_date("trigger_on_next_date");
vd.field_bool("delayed");
}
#[cfg(feature = "vic3")]
if Game::is_vic3() {
vd.field_bool("popup");
}
validate_optional_duration(&mut vd, sc);
}
}
}