youneuouy asked me to help him with testing this feature in scripts and also if I could explain to others how it works, so lets look into it.
Characters Labels Overhaul (scripting capabilities improvement)
M2TW Engine Overhaul Project v1.16
For starters, lets briefly talk about labels. Labels can be given only to starting characters in descr_strat or characters spawned on events in campaign_script, they can have only one label, and through those labels you can do many interesting actions with them (and their armies). You can use the label in several console commands, as well as in scripting. So basically, as long you knew the label (or a name) of those characters, you could do many things with them.
However, for perfect scripting that is not enough, as when you want to create more elaborate features and events, you usually need something that has a wider use and can target any character in the map at any time, not just starting (or spawned) characters. Such things, if not impossible, usually required insane amount of scripting to get it to work un some imperfect way.
Being able to assign labels to any characters whenever you want is something that would improve and all this, and mainly bring completely new features into scripting. And that is exactly what this next update of the M2TW Engine Overhaul Project (v1.16) does!
1. Setting up your cfg file
The first step is to download the tool and extract it into your mod. To get all its featurs to work, you have to edit your mod launcher (the .bat file) and write to its beginning "start applyHotSeatToolPatches.bat". Most of features you can set up in "limits.youneuoycfg". However, to set up labels, you have to go to youneuoy_Data folder, in which you can find "labels.youneuoycfg" and "labels_immutable.youneuoycfg". Those two are the files we are going to work with.
(and logs for each cfg you can find in the logs)
In "labels.youneuoycfg" you can set up your new precious labels and most importantly triggers for them. So lets looks into how to do it:
The basic structure is
Code:
label name
priority
number of conditions that must be true
conditions
The Label name has three lines.
- The first must be always the same, and must be saying "label:". It tells the program a new label section is starting.
- The Second line is for how will the label be referred to in the logs. Moreless something to help you remember what its supposed to be doing.
- Third and the most important line is the code name of the lebel. The word by which will you call the characters with the labels, thus, the most important. It must be one word.
So it can look something like this:
Code:
label:
My New Precious label
my_new_precious_label
The Priority tells the program which label is more important. A new label will overwrite the old label, so inevitably, you will end up many labels and there will be some important ones you will not want to be overriden by less important ones. Or for example you will have a sequence of labels, where you first need a character to get a first label, then second and so on.
- You tell the program what priority each label has simpy by a number. The higher number the higher priority. To be precise, if a character receives.
So for example:
Code:
label:
My New Precious label
my_new_precious_label
1
..
Code:
label:
My New Precious label 2
my_new_precious_label_2
0
...
If character has my_new_precious_label, it will not be overwritten by my_new_precious_label_2.
The next line is the number of conditions that must be true. Simply as for priority, you just write a number. If your trigger has only one condition, you write 1, if it has 3 conditions, you write 3. So for example:
Code:
label:
My New Precious label
my_new_precious_label
1
2
..
Lets elaborate the conditions, the essential part. Each condition must have three lines.
- The first line is the type of condition. That can be:
"passed turns" - the numbers of turns since the beginning of the game after which will this condition come true.
"name" - name of a character.
"trait" - a trait that a character must have. You can't specify the level of the trait. It means that the condition will come true for any level of the trait.
"ancillary" - an ancillary a character must have
"label" - a label that a character must have (it will be overwritten by the new label)
"4_coords" - This one is an exception and has 6 lines in total. By this you you specify coordinates in which a character must be for the condition to come true.
- The second line is a number and it can have 3 values, telling is whether this condition must be true
"0" - this means this condition is optional
"1" - this condition is mandatory
"2" - this means the condition is forbidden to come true
- The third line specifies what code name will it the trigger look for, based on the type of condition. So you will write there a name of a character, or name of a trait, or a name of ancillary, or a name of label. For 4_coords you write there x and y coordinates into 4 lines in this order: xmin, ymax, xmax, ymin. (you can write the min and max to be the same, to check only 1 tile).
So a whole working label trigger can look like this
Code:
label:
My New Precious label
my_new_precious_label
1
2
label
1
my_new_precious_label_2
4_coords
1
77
111
77
111
So if a character has my new_precious_label_2 and is standng on x 77, y 111, he will receive my_new_precious_label
We only used mandatory conditions, lets see how to use optional ones:
Code:
label:
My New Precious label
my_new_precious_label
1
2
label
1
my_new_precious_label_2
ancillary
0
sword
ancillary
0
shield
Now we have 3 conditions. The number of conditions that must come true is 2, so only 2 of them must be true. However, as "label" has value 1, and both ancillaries have value 0, it means for a character to receive my_new_precious_label he must have my_new_precious_label_2 and either sword or shield ancillary.
We can also forbid some conditions to come true, like this:
Code:
label:
My New Precious label
my_new_precious_label
1
2
label
1
my_new_precious_label_2
ancillary
0
sword
ancillary
0
shield
trait
2
too_old
So now, the same as above is valid. However if a character has any level of "too_old" trait, he will not receive my_new_precious_label.
Thats it! Now you should know all the posibilites of the file.
Hint - Make sure there are no emtpy spaces after any of the lines with the trigger. Then it all would stop working.
Hint 2 - There can be more triggers for the same label.
In the other file, "labels_immutable.youneuoycfg", you simply write labels that cannot be overwritten by other your new labels. Why? Well, if you have some existing labels that are already part of various scripts, events and so on, you dont want to override them. Simply write one per each line.
There is a video by youneuoy covering most of this:
2. Setting up campaign_script
Thats all nice, but to actually use it, you naturally have to go into campaign_script and create some scripts utilizing it. I assume most of you know how to do some scripting, so I suppose I dont need to talk
that much about it. (and Im not one the best scripters anyway)
There is
one big but, you need to be aware of. Eventhough numerous characters can now share the same label, the game will always consider only the first one to have it. So if you will have 10 characters who you want to kill (because they received the label based on your trigger), and you write "console_command kill_character my_new_precious_label", it will kill only the first one.
So either we will have to make more insane script codes to fix the new issue we just created and make it work for all of the labelled characters somehow, or youneuoy will figure it out somehow. And he did
.
If into your script (or if you just open console panel in game) write this:
"give_trait my_new_precious_label try_change_this_label"
It will erase the label! Thus, after you use this character for an action, you should write this, so his my_new_precious_label us eras, and it can be used for another labelled character in line.
A visual example in another video by youneuoy
Alternatively, you can also use this:
"give_trait my_new_precious_label try_change_this_label_to:newlabel"
That will change a label of existing character with label my_new_precious_label (still the first one in line) into a new label, called "newlabel". That can in part replace the cfg file, for some cases.
Both of those lines will say report an error, that a character with this label doesnt exist, but its all fine. The scripts will still work fine.
Alright, now to the scripting itself. Im pretty confident, you will want to make something, that can happen several times, not just once. So your script should look something like this:
Code:
monitor_event ....
....
do something
....
end_monitor
Its essential that there is no "terminate_monitor". Otherwise the event would fire just once.
So lets try to make a meaningful script. I think it might be better to avoid "usual" types of monitor_events that trigger at the beginning/end of turn, as I assume those might trigger just once eventhough you might have more labelled characters. Havent had time to test it properly though, I assume it might work when written smartly.
So we should rather use monitor_events that get triggered when we do or click on something. Some examples I can think of
Code:
monitor_event CharacterSelected CharacterIsLocal
and I_EventCounter Lets_murder_someone > 0
and I_CharacterExists my_new_precious_label
kill_character my_new_precious_label
set_counter Lets_murder_someone 0
end_monitor
Assuming my_new_precious_label was someone that deserved it, something smart done in cfg.
See, here we didnt even have to use that command for removing label, as the character died
or somethng like this:
Code:
monitor_event CharacterSelected CharacterIsLocal
and AgentType = priest
and I_CharacterExists priest_ready_to_promotion
give_ancillary priest_ready_to_promotion cardinal
give_trait priest_ready_to_promotion try_change_this_label
end_monitor
Code:
monitor_event CharacterSelected CharacterIsLocal
and IsFactionLeader
and I_FactionLeaderTrait kingship = 3
remove_ancillary faction_leader duke
give_ancillary faction_leader king
create_unit faction_leader "Mailed Knights" 3 3 3 3
create_unit faction_leader "Mailed Knights" 3 3 3 3
create_unit faction_leader "Mailed Knights" 3 3 3 3
end_monitor
or like this:
(of course assuming you would have some nice trigger for obtaitning traitor label)
Code:
monitor_event FactionTurnStart FactionIsLocal
and I_CharacterExists traitor
and I_EvenCounter Civil_War = 1
and not FactionBuildingExists > cathedral
spawn_army
faction slave, sub_faction england
character random_name, named character, age 34, x 302, y 290
traits LoyaltyStarter 1 , GoodCommander 2 , ReligionStarter 1 , GoodAttacker 1
unit Rebels exp 9 armour 0 weapon_lvl 0
unit Rebels exp 9 armour 0 weapon_lvl 0
unit Rebels exp 9 armour 0 weapon_lvl 0
unit Rebels exp 9 armour 0 weapon_lvl 0
unit Rebels exp 9 armour 0 weapon_lvl 0
end
end_monitor
Well, there of course many other better ways to use it, just something I could think of right now. I likely also made some mistake in it, so let me know to fix it
3. Implementing in your mod - Show examples
This feature possibly bring new amazing scripts that were not possible at all before. I suggest everyone who has some ideas how to use it, or already used it, show your examples so we can discover new scripting possibilites!
Some examples of my own, I managed to implement in short time in my mod TES mod.
- Crafting an alchemy potion from ingredients
cfg part
Code:
label:
potion making 1
potion1
1
3
ancillary
1
torchbug_thorax
ancillary
1
spider_egg
ancillary
1
scrib_jelly
ancillary
2
potion_hitpoints_1
script part:
Code:
monitor_event CharacterSelected CharacterIsLocal
and AgentType = merchant
(and HasAncType torchbug_thorax)
(and HasAncType spider_egg)
(and HasAncType scrib_jelly)
and I_CharacterExists potion1
remove_ancillary potion1 torchbug_thorax
remove_ancillary potion1 spider_egg
remove_ancillary potion1 scrib_jelly
give_ancillary potion1 potion_hitpoints_1
give_trait potion1 try_change_this_label
end_monitor
That is true crafting in real time! Hardly possible before for any merchant/alchemyst.
- And lets conclude with some new ways how to torment our machines
cfg part
Code:
label:
TES label 1
testestlabel
0
2
passed_Turns
1
1
ancillary
1
Almalexia
label:
TES label 2
testestlabel2
1
2
4_coords
1
332
192
332
192
label
1
testestlabel
label:
TES label 3
testestlabel
1
2
4_coords
1
370
156
370
156
label
1
testestlabel2
Code:
monitor_event FactionTurnStart FactionIsLocal
and I_CharacterExists testestlabel
repostion_character testestlabel, 332, 192
end_monitor
monitor_event FactionTurnStart FactionIsLocal
and I_CharacterExists testestlabel2
repostion_character testestlabel2, 370, 156
end_monitor
(yes, could have been just script using "try_change_this_label_to:testestlabel2" but I made it before the function was there, so at least some more examples of how can it look like tp help you out...
Share your own examples of or ideas how to use it!