• We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.
v1.34.15: https://www.dropbox.com/s/h0p4ntgjr3t5vvf/Audax.Validator v1.34.15.zip?dl=0

This fixes the quote issue (by doing a hack of replacing all the slash+quote with empty in the .mod files, so it doesn't actually check that you have quotes around mods with spaces or anything, and one asks why cannot paradox just not care if there are quotes or not anyways)

In the top right there is a menu which you can change to "Show main mod errors", which ideally should not show dependent mod errors. However, it will still try to parse dependent mods to deal with event ids and stuff.

For the ignore, I think putting it right above "id = XXX" (i.e. not above the "country_event = { ... }" part) works

Code:
# don't put the ignore here
country_event = {
  # put the ignore here
  id = whatever
}

the ignore targets the node right below it, so in the case of yours, it's targeting country_event and not id.
 
  • 3
Reactions:
This fixes the quote issue (by doing a hack of replacing all the slash+quote with empty in the .mod files, so it doesn't actually check that you have quotes around mods with spaces or anything, and one asks why cannot paradox just not care if there are quotes or not anyways)

I can confirm that this is now working. Double-quotes and brackets are now accepted in dependency names.

===========================================================================

For the ignore, I think putting it right above "id = XXX" (i.e. not above the "country_event = { ... }" part) works

Code:
# don't put the ignore here
country_event = {
  # put the ignore here
  id = whatever
}

the ignore targets the node right below it, so in the case of yours, it's targeting country_event and not id.

I can confirm that this is now working.

This suppresses the Validator error:

Code:
expd_pris_more_bloodlines_add_to_tyranny_counter = {
    character_event = {
        # Audax Validator "." Ignore_NEXT
        id = IB.70
    }
}

... but this does not:

Code:
expd_pris_more_bloodlines_add_to_tyranny_counter = {
    # Audax Validator "." Ignore_NEXT
    character_event = { id = IB.70 }
}

I think it would be valuable to add a little more explanation to the "Did You Know.pdf", to clarify that "." only means "next statement" NOT "next line".

Added text in bold and underlined:
Let’s dissect what this means. “Audax Validator” are just keywords that indicate that the comment means something. The “.” specifies the target of the ignore command. A period just means: the next non-comment in the file. (In the example above, this corresponds to just the "AND" token, not the whole line. One way to think of this is: if you rewrote the code with the minimum possible amount on each line, a "." will suppress errors on the next line. In the example above, "AND = { ai = yes }" could be written as three lines, so the "." will only suppress the first of those three lines, ie. the "." will only suppress errors caused by the "AND".) Where does 1001 come from? It is from a list of error codes in Information/errorCodes.txt. You can find the code of the error you are trying to disable in here.

===========================================================================

In the top right there is a menu which you can change to "Show main mod errors", which ideally should not show dependent mod errors. However, it will still try to parse dependent mods to deal with event ids and stuff.

Unfortunately, this really doesn't work for my use case. I need to include dependencies on workshop mods, which are zipped, which means they crash the Validation process if they are included as dependencies. (This probably affects other people too, but I have no idea how many.)

My current workaround is to comment-out the "dependencies" line in the .mod file before Validation. And uncomment it afterwards. But it's very easy to forget to uncomment it (and waste time doing useless/misleading testing), so this is not at all ideal.

Please could you think again about this issue?

  • Add a switch inside the Validator that forces it to NOT parse mod dependencies
    • IE: My previous request
  • If the Validator encounters a magic string in the .mod file then it won't parse that mod's dependencies
    • Eg. If "# Audax Validator IGNORE_DEPENDENCIES" exists in the .mod file then the Validator doesn't try to parse the dependencies
  • Allow the user to tell the Validator that the mod folder is in a different location
    • IE: Allow the user to specify something other than %USERPROFILE%\Documents\Paradox Interactive\Crusader Kings II\mod
    • The user could then set up a staging folder where everything is nicely arranged for the Validator, and use batch files etc to transfer back and forth from the "real" mod folder
  • Add the ability for the Validator to parse zip files
    • This would be a big task, but maybe worthwhile in the long run (eg. for other games)
    • 7zip portable is very useful for these kinds of projects... but I'm not sure exactly what the licensing terms are...
 
Last edited:
I can confirm that this is now working. Double-quotes and brackets are now accepted in dependency names.

===========================================================================



I can confirm that this is now working.

This suppresses the Validator error:

Code:
expd_pris_more_bloodlines_add_to_tyranny_counter = {
    character_event = {
        # Audax Validator "." Ignore_NEXT
        id = IB.70
    }
}

... but this does not:

Code:
expd_pris_more_bloodlines_add_to_tyranny_counter = {
    # Audax Validator "." Ignore_NEXT
    character_event = { id = IB.70 }
}

I think it would be valuable to add a little more explanation to the "Did You Know.pdf", to clarify that "." only means "next statement" NOT "next line".

Added text in bold and underlined:

===========================================================================



Unfortunately, this really doesn't work for my use case. I need to include dependencies on workshop mods, which are zipped, which means they crash the Validation process if they are included as dependencies. (This probably affects other people too, but I have no idea how many.)

My current workaround is to comment-out the "dependencies" line in the .mod file before Validation. And uncomment it afterwards. But it's very easy to forget to uncomment it (and waste time doing useless/misleading testing), so this is not at all ideal.

Please could you think again about this issue?

  • Add a switch inside the Validator that forces it to NOT parse mod dependencies
    • IE: My previous request
  • If the Validator encounters a magic string in the .mod file then it won't parse that mod's dependencies
    • Eg. If "# Audax Validator IGNORE_DEPENDENCIES" exists in the .mod file then the Validator doesn't try to parse the dependencies
  • Allow the user to tell the Validator that the mod folder is in a different location
    • IE: Allow the user to specify something other than %USERPROFILE%\Documents\Paradox Interactive\Crusader Kings II\mod
    • The user could then set up a staging folder where everything is nicely arranged for the Validator, and use batch files etc to transfer back and forth from the "real" mod folder
  • Add the ability for the Validator to parse zip files
    • This would be a big task, but maybe worthwhile in the long run (eg. for other games)
    • 7zip portable is very useful for these kinds of projects... but I'm not sure exactly what the licensing terms are...

Is it difficult to maintain two .mod files, one of which is for development?

By crash do you mean actually crash or not do anything, or just emit errors about the mod? If it's the former then it might be possible to just report errors that a mod was ignored for being a zip. In any case, a simple repro would be useful.
 
Is it difficult to maintain two .mod files, one of which is for development?

On one level, sure, it's not difficult.

But on another level, it does seem to be difficult for me to remember to re-enable dependencies before testing! And forgetting to do that can lead to an annoying waste of time.

By crash do you mean actually crash or not do anything, or just emit errors about the mod? If it's the former then it might be possible to just report errors that a mod was ignored for being a zip. In any case, a simple repro would be useful.

Sorry, I was unclear. The Validation process ends unexpectedly immediately after starting, because the Validator can't read the zipped dependent mod. See attached screenshot & zip file.
 

Attachments

  • 1659269996336.png
    1659269996336.png
    131,8 KB · Views: 0
  • mod.zip
    174,5 KB · Views: 0
If you have two .mod files, wouldn't you have one of them remain enabled? And that one is called "Modname" and gets shipped, while the one called "Modname (Validator)" will have no dependencies and you'd never use it for anything except Validator?
Nice idea, but it doesn't seem to work:

1659887261926.png

It's quite possible I've done something wrong here... but I can't think what!

See attached zip file for the relevant setup: I have two .mod files, one with the dependency line and one without. But the validator-only .mod file isn't recognised by the validator. (Speculation: Maybe the Validator requires both the "modname.mod" file and "modname" folder to exist, when in fact only the .mod file is needed?)

---

On a different note, I found a false-positive error in your localisation validation: §g is actually a valid colour (light grey).

1659887417964.png

The attached zip file also works to reproduce this error (as long as you first comment-out the dependencies line in the .mod).
 

Attachments

  • expanded_decisions_prisoners.zip
    22,9 KB · Views: 0
Last edited:
Does it work when you put in "ED: Prisoners (DO NOT USE)" as the mod?
Yes, that works.

Which is weird, because it implies there are two completely different ways to tell the Validator to look at a particular mod: the mod name from inside the .mod file; and the name of the mod file or folder. Is this deliberate?
 
I discovered a potential oversight in the Validator: If you make a self-targeted decision (filter = self and/or ai_target_filter = self) then the criteria restricting who can take the decision need to be in the potential block, NOT the from_potential block.

The error.log warns about this:
[decision.cpp:752]: Usage of from potential on self targeted decision found, use potential instead! key: expd_tv_make_shieldmaiden_self
...but the Validator (v1.34.17) does not.

Discovered because I started having male shieldmaidens pop up in my game when I introduced a self-targeted decision aimed at making female warrior lodge members declare themselves to be shieldmaidens. (Because, seriously, Heroines should be shieldmaidens.)

Relevant (erroneous) code snippet below, full (erroneous) mod attached.

In this erroneous code, it appear that the entire from_potential clause is disregarded, because the decision is self-targeted. The fix, apparently, is to move everything from from_potential into potential, and delete from_potential.
Code:
targetted_decisions = {   
    expd_tv_make_shieldmaiden_self = {
       
        # Female members of warrior lodges (including unlanded!) can declare themselves to be shieldmaidens
        # Intended, mostly, to ensure that AI warriors declare themselves to be shieldmaidens
        #            (because they have the ability to make other people shieldmaidens)
        #            but this may be useful for the player too, occasionally.
       
        is_in_society = yes
       
        filter = self
        ai_target_filter = self
        ai_check_interval = 24

        from_potential = {
            is_female = yes
            is_adult = yes
            NOT = { trait = shieldmaiden }
            is_member_of_any_warrior_lodge_trigger = yes
            prisoner = no
        }

        potential = {
            character = FROM
        }

Important outstanding question: Does this same rule apply to all of the *_including_me scopes?
 

Attachments

  • expanded_decisions_targeted_vanilla.zip
    12,9 KB · Views: 0
I discovered a potential oversight in the Validator: If you make a self-targeted decision (filter = self and/or ai_target_filter = self) then the criteria restricting who can take the decision need to be in the potential block, NOT the from_potential block.

The error.log warns about this:

...but the Validator (v1.34.17) does not.

Important outstanding question: Does this same rule apply to all of the *_including_me scopes?

I've come across this too. It does not apply to those other ones, from what I remember, which makes sense since they also target other characters than the decision taker.
I do have to wonder why Paradox decided to break syntax here, for so little gain.
 
  • 1Like
Reactions:
Ok, my interpretation is that if ai_target_filter and filter are BOTH in none/self, then from_potential should not be allowed.

Haven't exhaustively tested it, but it seems to be working perfectly.



Here are some more reports and various tidbits, if you're still willing to work on the Validator:

min_age and max_age do not work in create_character (and create_random_<occupation>), after more testing

A humble request:
Error code to ignore duplicate localisation keys (as per instructions in DidYouKnow.pdf and errorCodes.txt).
Currently CK2+ generates many of these needless warnings, likely due to being based on CleanSlate, which also contains all localisation from vanilla. I'd like to weed out these errors without disabling all validation of our localisation files, hence the above request.

Code:
At <mod>\events\mnm_job_offmap.txt [character_event] (Line 1467, column 1):
This hidden event has a title

Interesting quirk here. This event is indeed hidden, but the title is used for the tooltip in the list of possible job_action outcomes in the council screen.
Would it be possible to allow events listed there to have titles despite being hidden events?

Code:
action_hunt_heretics = {
    attribute = learning
    liege_modifier = apostate_liege_mod
    offmap = yes

    potential = {
        has_dlc = "Mystics"
    }

    events = {
        MNM.70100 # Hunt Apostates
    }
}

Code:
At <mod>\events\mnm_hermetics_events.txt [character_event\immediate\if\FROM] (Line 16216, column 4):
Invalid use of 'FROM': No non-repeat_event calls found for .

Works as intended, all scopes provided by an on_action are passed on 'as is' by calling an event by means of repeat_event. I hope this one isn't too tricky.
Events below:

Code:
# ROOT is new artifact owner
# FROM is inherited artifact
# FROMFROM is previous owner

# CleanSlate meta event to sort out artifact inheritance logic
character_event = {
    id = HFP.16004

    is_triggered_only = yes # on_artifact_inheritance
    hide_window = yes

    immediate = {
        if = {
            limit = { FROM = { has_artifact_flag = ingredient } }
            repeat_event = { id = MN.5108 } # Deal with inherited ingredients
        }
        else = {
            random = {
                chance = 5 # Old destruction chance
                repeat_event = { id = HFP.16005 } # Deal with all other inherited artifacts
            }
        }
    }
}

character_event = {
    id = HFP.16005

    is_triggered_only = yes
    hide_window = yes

    trigger = {
        NOT = { has_character_flag = dealing_with_inherited_artifacts }
    }

    fail_trigger_effect = {
        # If you are already dealing with another artifact, queue this one up...
        repeat_event = {
            id = HFP.16005
            days = 12
        }
    }

    immediate = {
        if = { # AIs get the short stick...
            limit = {
                ai = yes
                FROM = { is_indestructible = no }
            }

            FROM = { destroy_artifact = yes }
        }
        else = { # Players get a choice... (CleanSlate: as does AI for indestructible artifacts)
            set_character_flag = dealing_with_inherited_artifacts
            FROM = { save_event_target_as = inherited_artifact }
            FROMFROM = { save_event_target_as = previous_owner }

            character_event = {
                id = HFP.16006 # Actual event
                days = 5
            }
        }
    }
}

# Dealing with inherited ingredients - delays the visible event
character_event = {
    id = MNM.5108

    is_triggered_only = yes
    hide_window = yes

    immediate = {
        # FROMFROM = { save_event_target_as = previous_ingredient_owner } # does this work on dead folk?

        if = {
            limit = { ai = yes }
            FROM = { destroy_artifact = yes }
        }
        else = {
            set_character_flag = dealing_with_inherited_ingredients

            if = {
                limit = {
                    NOT = { has_character_flag = owns_inherited_ingredients }
                }

                set_character_flag = owns_inherited_ingredients
                repeat_event = { id = MNM.5108 days = 10 } # if this is the first instance of this event, send this event again to check if you ever inherited more than one ingredient...
            }
            else_if = { # run second time around
                limit = {
                    has_character_flag = owns_inherited_ingredients

                    any_artifact = {
                        count >= 2
                        has_artifact_flag = ingredient
                    }
                }

                repeat_event = { id = MNM.5109 } # send visible event
            }
        }
    }
}

Code:
--- Error 1 of 2 ---
At <mod>\common\objectives\000_CK2Plus_factions.txt [faction_tradition\membership\additive_opinion_modifier\who] (Line 1326, column 4):
"FROM" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.
--- Error 2 of 2 ---
At <mod>\common\objectives\000_CK2Plus_factions.txt [faction_tradition\membership\additive_opinion_modifier\this] (Line 1325, column 4):
"ROOT" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.

Tested to work, see below. THIS and PREV also work here (and all stacked FROMs and ROOT_FROMs, of course, and saved event targets, whether normal, global or persistent).

Code:
namespace = test

character_event = {
    id = test.1

    is_triggered_only = yes

    option = {
        spouse = {
            character_event = { id = test.2 }
        }
    }
}

character_event = {
    id = test.2

    is_triggered_only = yes

    option = {
        random = {
            chance = 0

            additive_opinion_modifier = {
                factor = 1
                this = FROM
                who = ROOT
            }

            wealth = 1000
        }
    }
}

Code:
--- Error 1 of 2 ---
At <mod>\common\scripted_score_values\000_CK2Plus_faction_scores.txt [faction_glory_join_score\additive_exported_value_modifier\who] (Line 50, column 3):
"ROOT" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.
* called from <mod>\common\objectives\000_CK2Plus_factions.txt [faction_glory\membership\faction_glory_join_score] (Line 930, column 3)
--- Error 2 of 2 ---
At <mod>\common\scripted_score_values\000_CK2Plus_faction_scores.txt [faction_glory_join_score\additive_exported_value_modifier\who] (Line 56, column 3):
"FROM" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.
* called from <mod>\common\objectives\000_CK2Plus_factions.txt [faction_glory\membership\faction_glory_join_score] (Line 930, column 3)

Like above, tested to work with adjusted event test.2:

Code:
character_event = {
    id = test.2

    is_triggered_only = yes

    option = {
        random = {
            chance = 0
            faction_court_start_score = yes
            faction_court_join_score = yes

            wealth = 1000
        }
    }
}

faction_court_start_score = {
    additive_exported_value_modifier = {
        value = diplomacy
        who = FROM
        factor = 5
    }
}

faction_court_join_score = {
    additive_exported_value_modifier = {
        value = diplomacy
        who = ROOT
        factor = 5
    }
}

Code:
--- Error 1 of 2 ---
At <mod>\decisions\realm_decisions.txt [decisions\form_new_empire\effect\hidden_effect\else_if\create_title\base_title] (Line 2983, column 7):
"capital_scope" is not a valid AnyTitle or MaybeEventTargetCharProvTitle.
--- Error 2 of 2 ---
At <mod>\decisions\realm_decisions.txt [decisions\form_new_empire\effect\hidden_effect\else\create_title\base_title] (Line 3003, column 7):
"primary_title" is not a valid AnyTitle or MaybeEventTargetCharProvTitle.

Both tested to work. 'location' and 'family_palace' also work.

Code:
At <mod>\decisions\zz_CK2Plus_minor_decisions.txt [decisions\come_out_of_hiding_siege\allow\custom_tooltip\NOT\capital_scope\any_province_holding\OR\NOT\controlled_by] (Line 4423, column 17):
"holder_scope" is not a valid AnyTitle, MaybeEventTargetChar, or PrefixedCharId.

Tested to work. 'owner', being an alias of 'holder_scope' also works.

Code:
At <mod>\events\base_on_action_events.txt [narrative_event\immediate\if\set_title_landless\title] (Line 4085, column 5):
"event_target:religious_head_title" is not a valid "THIS" or AnyTitle.

Tested to work. Takes any relative title scope, in the form of ROOT, FROM, ROOT_FROM, etc. Script below:

Code:
trigger_switch = {
    on_trigger = religion

    norse_pagan_reformed        = { d_norse_pagan_reformed = { save_event_target_as = religious_head_title } }
    tengri_pagan_reformed       = { d_tengri_pagan_reformed = { save_event_target_as = religious_head_title } }
    baltic_pagan_reformed       = { d_baltic_pagan_reformed = { save_event_target_as = religious_head_title } }
    finnish_pagan_reformed      = { d_finnish_pagan_reformed = { save_event_target_as = religious_head_title } }
    aztec_pagan_reformed        = { d_aztec_pagan_reformed = { save_event_target_as = religious_head_title } }
    slavic_pagan_reformed       = { d_slavic_pagan_reformed = { save_event_target_as = religious_head_title } }
    west_african_pagan_reformed = { d_west_african_pagan_reformed = { save_event_target_as = religious_head_title } }
    zun_pagan_reformed          = { d_zun_pagan_reformed = { save_event_target_as = religious_head_title } }
    bon_pagan_reformed          = { d_bon_pagan_reformed = { save_event_target_as = religious_head_title } }
    hellenic_pagan_reformed     = { k_hellenic_pagan_reformed = { save_event_target_as = religious_head_title } }
}

set_title_landless = {
    title = event_target:religious_head_title
    status = yes
}

Code:
At <mod>\decisions\settlement_decisions.txt [settlement_decisions\nomad_adopt_republicanism\effect\if\limit\FROM\any_realm_province\ROOT\NOT\is_capital] (Line 1609, column 17):
"PREV" is not a valid Bool.

Accepts scopes like ROOT and FROM, so PREV also works.

Code:
At <mod>\events\rip_physician_events.txt [character_event\option\event_target:recruited_physician_target\if\transfer_scaled_wealth\value] (Line 2735, column 6):
"local_physician_cost" is not a valid All, NnDbl, or VariableName.

This one is definitely working in-game. Script below. 'code' is in 'immediate', the other part is in 'option'. Perhaps that's where Validator has trouble. Variables prefixed with 'local_' are event variables, that are not saved in any scope and are carried along an unbroken event chain, the same way event targets are.

Code:
new_character = {
	set_character_flag = no_court_invites
	set_character_flag = is_court_physician
	save_event_target_as = recruited_physician_target

	trigger_switch = {
		on_trigger = has_character_flag

		physician_village_drunkard		= { set_variable = { which = local_physician_cost value = 0.3 } }
		physician_ships_physician		= { set_variable = { which = local_physician_cost value = 0.1 } }
		physician_wise_dwarf			= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_jewish_exile			= { set_variable = { which = local_physician_cost value = 0.2 } }
	#	physician_condemned_sorcerer	# No cost
	#	physician_cynical_clergyman		# No cost
		physician_chinese_doctor		= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_arab_scholar			= { set_variable = { which = local_physician_cost value = 0.15 } }
		physician_beloved_wise_man		= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_wandering_genius		= { set_variable = { which = local_physician_cost value = 0.35 } }
		physician_erudite_herbalist		= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_pagan_mystic			= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_insane_zealot			= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_well_traveled_pilgrim	= { set_variable = { which = local_physician_cost value = 0.15 } }
		physician_student_of_medicine	= { set_variable = { which = local_physician_cost value = 0.03 } }
		physician_blind_miracle_worker	= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_field_surgeon			= { set_variable = { which = local_physician_cost value = 0.3 } }
		physician_schooled_eunuch		= { set_variable = { which = local_physician_cost value = 0.15 } }
		physician_pus_sucking_nun		= { set_variable = { which = local_physician_cost value = 0.3 } }
	#	physician_witch					# No cost
	#	physician_horse_md				# No cost
		physician_travelling_nestorian	= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_mazdan_doctor			= { set_variable = { which = local_physician_cost value = 0.25 } }
	#	physician_manichaean_merchant	# No cost
	}
}

if = {
	limit = {
		check_variable = {
			which = local_physician_cost
			value > 0
		}
	}

	transfer_scaled_wealth = {
		from = ROOT
		value = local_physician_cost
		min = 10
	}
}
 
Last edited:
Haven't exhaustively tested it, but it seems to be working perfectly.



Here are some more reports and various tidbits, if you're still willing to work on the Validator:

min_age and max_age do not work in create_character (and create_random_<occupation>), after more testing

A humble request:
Error code to ignore duplicate localisation keys (as per instructions in DidYouKnow.pdf and errorCodes.txt).
Currently CK2+ generates many of these needless warnings, likely due to being based on CleanSlate, which also contains all localisation from vanilla. I'd like to weed out these errors without disabling all validation of our localisation files, hence the above request.

Code:
At <mod>\events\mnm_job_offmap.txt [character_event] (Line 1467, column 1):
This hidden event has a title

Interesting quirk here. This event is indeed hidden, but the title is used for the tooltip in the list of possible job_action outcomes in the council screen.
Would it be possible to allow events listed there to have titles despite being hidden events?

Code:
action_hunt_heretics = {
    attribute = learning
    liege_modifier = apostate_liege_mod
    offmap = yes

    potential = {
        has_dlc = "Mystics"
    }

    events = {
        MNM.70100 # Hunt Apostates
    }
}

Code:
At <mod>\events\mnm_hermetics_events.txt [character_event\immediate\if\FROM] (Line 16216, column 4):
Invalid use of 'FROM': No non-repeat_event calls found for .

Works as intended, all scopes provided by an on_action are passed on 'as is' by calling an event by means of repeat_event. I hope this one isn't too tricky.
Events below:

Code:
# ROOT is new artifact owner
# FROM is inherited artifact
# FROMFROM is previous owner

# CleanSlate meta event to sort out artifact inheritance logic
character_event = {
    id = HFP.16004

    is_triggered_only = yes # on_artifact_inheritance
    hide_window = yes

    immediate = {
        if = {
            limit = { FROM = { has_artifact_flag = ingredient } }
            repeat_event = { id = MN.5108 } # Deal with inherited ingredients
        }
        else = {
            random = {
                chance = 5 # Old destruction chance
                repeat_event = { id = HFP.16005 } # Deal with all other inherited artifacts
            }
        }
    }
}

character_event = {
    id = HFP.16005

    is_triggered_only = yes
    hide_window = yes

    trigger = {
        NOT = { has_character_flag = dealing_with_inherited_artifacts }
    }

    fail_trigger_effect = {
        # If you are already dealing with another artifact, queue this one up...
        repeat_event = {
            id = HFP.16005
            days = 12
        }
    }

    immediate = {
        if = { # AIs get the short stick...
            limit = {
                ai = yes
                FROM = { is_indestructible = no }
            }

            FROM = { destroy_artifact = yes }
        }
        else = { # Players get a choice... (CleanSlate: as does AI for indestructible artifacts)
            set_character_flag = dealing_with_inherited_artifacts
            FROM = { save_event_target_as = inherited_artifact }
            FROMFROM = { save_event_target_as = previous_owner }

            character_event = {
                id = HFP.16006 # Actual event
                days = 5
            }
        }
    }
}

# Dealing with inherited ingredients - delays the visible event
character_event = {
    id = MNM.5108

    is_triggered_only = yes
    hide_window = yes

    immediate = {
        # FROMFROM = { save_event_target_as = previous_ingredient_owner } # does this work on dead folk?

        if = {
            limit = { ai = yes }
            FROM = { destroy_artifact = yes }
        }
        else = {
            set_character_flag = dealing_with_inherited_ingredients

            if = {
                limit = {
                    NOT = { has_character_flag = owns_inherited_ingredients }
                }

                set_character_flag = owns_inherited_ingredients
                repeat_event = { id = MNM.5108 days = 10 } # if this is the first instance of this event, send this event again to check if you ever inherited more than one ingredient...
            }
            else_if = { # run second time around
                limit = {
                    has_character_flag = owns_inherited_ingredients

                    any_artifact = {
                        count >= 2
                        has_artifact_flag = ingredient
                    }
                }

                repeat_event = { id = MNM.5109 } # send visible event
            }
        }
    }
}

Code:
--- Error 1 of 2 ---
At <mod>\common\objectives\000_CK2Plus_factions.txt [faction_tradition\membership\additive_opinion_modifier\who] (Line 1326, column 4):
"FROM" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.
--- Error 2 of 2 ---
At <mod>\common\objectives\000_CK2Plus_factions.txt [faction_tradition\membership\additive_opinion_modifier\this] (Line 1325, column 4):
"ROOT" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.

Tested to work, see below. THIS and PREV also work here (and all stacked FROMs and ROOT_FROMs, of course, and saved event targets, whether normal, global or persistent).

Code:
namespace = test

character_event = {
    id = test.1

    is_triggered_only = yes

    option = {
        spouse = {
            character_event = { id = test.2 }
        }
    }
}

character_event = {
    id = test.2

    is_triggered_only = yes

    option = {
        random = {
            chance = 0

            additive_opinion_modifier = {
                factor = 1
                this = FROM
                who = ROOT
            }

            wealth = 1000
        }
    }
}

Code:
--- Error 1 of 2 ---
At <mod>\common\scripted_score_values\000_CK2Plus_faction_scores.txt [faction_glory_join_score\additive_exported_value_modifier\who] (Line 50, column 3):
"ROOT" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.
* called from <mod>\common\objectives\000_CK2Plus_factions.txt [faction_glory\membership\faction_glory_join_score] (Line 930, column 3)
--- Error 2 of 2 ---
At <mod>\common\scripted_score_values\000_CK2Plus_faction_scores.txt [faction_glory_join_score\additive_exported_value_modifier\who] (Line 56, column 3):
"FROM" is not a valid MaybeEventTargetChar or CharTargetExcludingMaybeEventTarget.
* called from <mod>\common\objectives\000_CK2Plus_factions.txt [faction_glory\membership\faction_glory_join_score] (Line 930, column 3)

Like above, tested to work with adjusted event test.2:

Code:
character_event = {
    id = test.2

    is_triggered_only = yes

    option = {
        random = {
            chance = 0
            faction_court_start_score = yes
            faction_court_join_score = yes

            wealth = 1000
        }
    }
}

faction_court_start_score = {
    additive_exported_value_modifier = {
        value = diplomacy
        who = FROM
        factor = 5
    }
}

faction_court_join_score = {
    additive_exported_value_modifier = {
        value = diplomacy
        who = ROOT
        factor = 5
    }
}

Code:
--- Error 1 of 2 ---
At <mod>\decisions\realm_decisions.txt [decisions\form_new_empire\effect\hidden_effect\else_if\create_title\base_title] (Line 2983, column 7):
"capital_scope" is not a valid AnyTitle or MaybeEventTargetCharProvTitle.
--- Error 2 of 2 ---
At <mod>\decisions\realm_decisions.txt [decisions\form_new_empire\effect\hidden_effect\else\create_title\base_title] (Line 3003, column 7):
"primary_title" is not a valid AnyTitle or MaybeEventTargetCharProvTitle.

Both tested to work. 'location' and 'family_palace' also work.

Code:
At <mod>\decisions\zz_CK2Plus_minor_decisions.txt [decisions\come_out_of_hiding_siege\allow\custom_tooltip\NOT\capital_scope\any_province_holding\OR\NOT\controlled_by] (Line 4423, column 17):
"holder_scope" is not a valid AnyTitle, MaybeEventTargetChar, or PrefixedCharId.

Tested to work. 'owner', being an alias of 'holder_scope' also works.

Code:
At <mod>\events\base_on_action_events.txt [narrative_event\immediate\if\set_title_landless\title] (Line 4085, column 5):
"event_target:religious_head_title" is not a valid "THIS" or AnyTitle.

Tested to work. Takes any relative title scope, in the form of ROOT, FROM, ROOT_FROM, etc. Script below:

Code:
trigger_switch = {
    on_trigger = religion

    norse_pagan_reformed        = { d_norse_pagan_reformed = { save_event_target_as = religious_head_title } }
    tengri_pagan_reformed       = { d_tengri_pagan_reformed = { save_event_target_as = religious_head_title } }
    baltic_pagan_reformed       = { d_baltic_pagan_reformed = { save_event_target_as = religious_head_title } }
    finnish_pagan_reformed      = { d_finnish_pagan_reformed = { save_event_target_as = religious_head_title } }
    aztec_pagan_reformed        = { d_aztec_pagan_reformed = { save_event_target_as = religious_head_title } }
    slavic_pagan_reformed       = { d_slavic_pagan_reformed = { save_event_target_as = religious_head_title } }
    west_african_pagan_reformed = { d_west_african_pagan_reformed = { save_event_target_as = religious_head_title } }
    zun_pagan_reformed          = { d_zun_pagan_reformed = { save_event_target_as = religious_head_title } }
    bon_pagan_reformed          = { d_bon_pagan_reformed = { save_event_target_as = religious_head_title } }
    hellenic_pagan_reformed     = { k_hellenic_pagan_reformed = { save_event_target_as = religious_head_title } }
}

set_title_landless = {
    title = event_target:religious_head_title
    status = yes
}

Code:
At <mod>\decisions\settlement_decisions.txt [settlement_decisions\nomad_adopt_republicanism\effect\if\limit\FROM\any_realm_province\ROOT\NOT\is_capital] (Line 1609, column 17):
"PREV" is not a valid Bool.

Accepts scopes like ROOT and FROM, so PREV also works.

Code:
At <mod>\events\rip_physician_events.txt [character_event\option\event_target:recruited_physician_target\if\transfer_scaled_wealth\value] (Line 2735, column 6):
"local_physician_cost" is not a valid All, NnDbl, or VariableName.

This one is definitely working in-game. Script below. 'code' is in 'immediate', the other part is in 'option'. Perhaps that's where Validator has trouble. Variables prefixed with 'local_' are event variables, that are not saved in any scope and are carried along an unbroken event chain, the same way event targets are.

Code:
new_character = {
	set_character_flag = no_court_invites
	set_character_flag = is_court_physician
	save_event_target_as = recruited_physician_target

	trigger_switch = {
		on_trigger = has_character_flag

		physician_village_drunkard		= { set_variable = { which = local_physician_cost value = 0.3 } }
		physician_ships_physician		= { set_variable = { which = local_physician_cost value = 0.1 } }
		physician_wise_dwarf			= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_jewish_exile			= { set_variable = { which = local_physician_cost value = 0.2 } }
	#	physician_condemned_sorcerer	# No cost
	#	physician_cynical_clergyman		# No cost
		physician_chinese_doctor		= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_arab_scholar			= { set_variable = { which = local_physician_cost value = 0.15 } }
		physician_beloved_wise_man		= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_wandering_genius		= { set_variable = { which = local_physician_cost value = 0.35 } }
		physician_erudite_herbalist		= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_pagan_mystic			= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_insane_zealot			= { set_variable = { which = local_physician_cost value = 0.05 } }
		physician_well_traveled_pilgrim	= { set_variable = { which = local_physician_cost value = 0.15 } }
		physician_student_of_medicine	= { set_variable = { which = local_physician_cost value = 0.03 } }
		physician_blind_miracle_worker	= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_field_surgeon			= { set_variable = { which = local_physician_cost value = 0.3 } }
		physician_schooled_eunuch		= { set_variable = { which = local_physician_cost value = 0.15 } }
		physician_pus_sucking_nun		= { set_variable = { which = local_physician_cost value = 0.3 } }
	#	physician_witch					# No cost
	#	physician_horse_md				# No cost
		physician_travelling_nestorian	= { set_variable = { which = local_physician_cost value = 0.2 } }
		physician_mazdan_doctor			= { set_variable = { which = local_physician_cost value = 0.25 } }
	#	physician_manichaean_merchant	# No cost
	}
}

if = {
	limit = {
		check_variable = {
			which = local_physician_cost
			value > 0
		}
	}

	transfer_scaled_wealth = {
		from = ROOT
		value = local_physician_cost
		min = 10
	}
}
For easy testing, are these from a specific mod? (If not I can work with the samples you give, but a specific mod to test against is always convenient)
 
They should all be found in CK2+, which requires CleanSlate as a dependency.
"AddFlag = Localization.IgnoreNonReplaceDuplicatesIfInVanilla" (works for main mod vs base mod localization, despite the name; the similar sounding one in the current version's settings file is a typo)

Works as intended, all scopes provided by an on_action are passed on 'as is' by calling an event by means of repeat_event. I hope this one isn't too tricky.
Events below:
Can't repro. Did you run all validation options when running into this error? If not things like this can happen.

Accepts scopes like ROOT and FROM, so PREV also works.
So if I understand correctly, this is in title scope, and it is checking against a title to see if the current scope title is the capital of the other title?

This one is definitely working in-game. Script below. 'code' is in 'immediate', the other part is in 'option'. Perhaps that's where Validator has trouble. Variables prefixed with 'local_' are event variables, that are not saved in any scope and are carried along an unbroken event chain, the same way event targets are.
Is emf_nwo_create_female_ruler_effect defined correctly? From what I can tell `new_character` should be a part of it but is instead below. This causes Validator to think that the new_character in the call is a scripted_effect call, and hence not look under it for set_variable. I guess Validator can always look under even if it thinks it's a scripted_effect, but is naming scripted effects builtin things like `new_character` really valid?
 
Last edited:
"AddFlag = Localization.IgnoreNonReplaceDuplicatesIfInVanilla" (works for main mod vs base mod localization, despite the name; the similar sounding one in the current version's settings file is a typo)

Thanks, exactly what I was looking for!

Can't repro. Did you run all validation options when running into this error? If not things like this can happen.

All but 'characters'. No longer shows up with 1.35.3, however, so I think this case is closed.

So if I understand correctly, this is in title scope, and it is checking against a title to see if the current scope title is the capital of the other title?

In this case, it's checked in character scope, so it's checking if the target province is the capital of the scoped character. This trigger only works like that in character scope, so it's effectively an entirely different trigger than the one that's used in title and province scopes.

Is emf_nwo_create_female_ruler_effect defined correctly? From what I can tell `new_character` should be a part of it but is instead below. This causes Validator to think that the new_character in the call is a scripted_effect call, and hence not look under it for set_variable. I guess Validator can always look under even if it thinks it's a scripted_effect, but is naming scripted effects builtin things like `new_character` really valid?

There was indeed an issue there, exactly as you described, so there's no need to change anything in the Validator. I don't see how it was related to the issue at hand, but fixing the effect has fixed the report coming up.