Example Scripts: Difference between revisions

From Total War Modding
No edit summary
m (Set a space between the [[ of the example as it was getting picked up as a link and not as the symbols (as it should since you want to show how to comment in portuguese moon))
(10 intermediate revisions by one other user not shown)
Line 1: Line 1:
A list of example scripts that can be easily copied, edited, and then pasted into a [[Mods|mod]] for quick functionality. Typically will have a block of variables to edit the values of for your own version.
A list of example scripts that can easily be copied, edited and then put into a [[Mods|mod]].


== Using Example Scripts ==
== Using Example Scripts ==
Unless otherwise stated, these scripts go into a .pack file at `script/campaign/mod/?.lua`, replace ? with whatever your file name is.
Unless otherwise stated these scripts go into a .pack file at `script/campaign/mod/?.lua`, replace ? with whatever your file name is.


Each script is self-contained - so you can use multiple in a single .lua file.
Each script listed is self-contained - so you can use multiple of the example scripts in a single .lua file.


When using the scripts, please keep the credits at the top, and don't remove comments.
When using the scripts, please keep the credits at the top and don't remove comments. Comments are the text following -- or in a comment block denoted by --[ [ Text here ] ].


Typically, an example script will have a list of variables that should have the value changed, that are more or less self-explanatory. For instance:
Typically an example script will have a list of variables that should be changed to tailor the script to your particular mod, these will often have comments next to them explaining where you will find the keys or values from.


`local unit_health = 100`
Example in the Replace Starting General example script, you have a line that reads 'local faction_name = "wh_main_emp_empire";'


If you'd like to change the unit_health from 100 to, say, 7000000 you would simply replace the 100:
This could instead be changed to 'local faction_name = "wh2_dlc11_vmp_the_barrow_legion";' to affect the Barrow Legion faction instead of The Empire.


`local unit_health = 7000000`
If there are out() calls within the example script, you are encouraged to insert a unique text identifier, so you can find your unique output in the [[Script logging|script logs]] using ctrl+f.


If there are out() calls within the example script, you are encouraged to replace the text within to something more relevant for you, so you can find your output in the [[Script logging|script logs]].
Example in the Replacing Starting General Script, replace REPLAC: with your unique identifier, maybe the starting letters of your discord username, or the mod name or the name of your cat, or something else unique to you.


== Making Example Scripts ==
== Contributing new scripts ==
If you have some scripted functionality you think would be useful, please bring it here! Add a new Sub-Heading 1 within the Example Scropts section below, give it a meaningful title, and a short description. Include what games it is valid for, and any notes necessary - ie., has to go in this directory, has to be done this way.
If you have some generic scripted functionality you think would be useful for the wider community, please consider adding it to the wiki here, or bring attention to it via Modding Discord in the channel for the wiki.


Please don't use any global variables in the example script you put below - use local variables all around, and direct the modder to which sections should and shouldn't be edited. The more commented the example is, the better - that way we can provide some context and explanation while also offering a valuable tool!
Add a new Sub-Heading 1 within the Example Scripts section below, give it a meaningful title and a short description. Include what games it is valid for and any specific notes necessary, ie. has to go in this directory, has to be done this way etc.


And keep complications to a minimum - if your script runs through a single init function that's triggered through a first tick callback, give the local function a meaningful name and implement the first-tick-callback, and leave it at that. No need to have the modder replace the local function name in their own replication, that only adds complications.
Avoid using global variables in the example scripts, use local variables and local functions to avoid incidents involving scripts that have overlapping names. Make sure to use comments explaining what the various parts of the script is doing or when referring to specific keys from the database, like where to find them. Context and explanation goes a long way towards helping make sense of the scripts for beginners.


Mind that plopping something in this page leaves it open for collaboration, so someone in the future may come in and add more comments, change anything necessary for an update, make an extra version for another game, or much else. '''If you're editing the original script, leave the credits the same, or add your name to the credits if you think the addition is meaningful'''. Don't remove the OG author for sure.
If your script runs through a single init function that's triggered through a first tick callback, give the local function a meaningful name and implement the first-tick-callback, and leave it at that. No need to have the modder replace the local function name in their own replication.


== Example Scropts ==
Mind that plopping something in this page leaves it open for collaboration, so someone in the future may come in and add more comments, change anything necessary for an update, make an extra version for another game, or much else. If you're editing the original script, leave the credits the same, or add your name to the credits if you think the addition is meaningful. Don't remove the OG author for sure.
 
== Example Scripts ==


=== Replacing a Starting General ===
=== Replacing a Starting General ===
Line 37: Line 39:
]]
]]


-- Rename replace_starting_general
local function replace_starting_general()
local function replace_starting_general()
if cm:is_new_game() then
if cm:is_new_game() then
-- The Empire
local faction_name = "wh_main_emp_empire"; -- Faction key from Factions table
local faction = cm:get_faction(faction_name);


-- Creating replacement for Karl Franz with a regular Empire General
if faction:is_null_interface() == false and faction:is_dead() == false then
local general_faction_str = "wh_main_emp_empire"; -- faction key from factions table
local general_faction_obj = cm:get_faction(general_faction_str);
local general_unit_list = "wh_main_emp_cav_reiksguard,wh_main_emp_cav_reiksguard,wh_main_emp_cav_reiksguard"; -- unit keys from main_units table
local general_region_key = general_faction_obj:home_region():name(); -- the replacement general will spawn adjacent to the settlement in the home region for the faction
local general_x_pos, general_y_pos = cm:find_valid_spawn_location_for_character_from_settlement(
general_faction_str,
general_region_key,
false,
true
);
local general_type = "general"; -- agent type
local general_subtype = "emp_lord"; -- agent subtype
local general_forename = "names_name_1904032251"; -- from local_en names table, Bernhoff the Butcher is now ruler of Reikland
local general_clanname = ""; -- from local_en names table
local general_surname = ""; -- from local_en names table
local general_othername = ""; -- from local_en names table
local general_is_faction_leader = true; -- bool for whether the general being replaced is the new faction leader


cm:create_force_with_general(
-- Creating replacement for Karl Franz with a regular Empire General
general_faction_str,
local general_details = {
general_unit_list,
general_faction = faction_name,
general_region_key,
unit_list = "wh_main_emp_cav_reiksguard,wh_main_emp_cav_reiksguard,wh_main_emp_cav_reiksguard"; -- unit keys from main_units table
general_x_pos,
region_key = faction:home_region():name(),
general_y_pos,
type = "general", -- Agent type
general_type,
subtype = "emp_lord", -- Agent subtype
general_subtype,
forename = "names_name_1904032251", -- From local_en names table, Bernhoff the Butcher is now ruler of Reikland
general_forename,
clanname = "", -- From local_en names table
general_clanname,
surname = "names_name_151217003", -- From local_en names table
general_surname,
othername = "", -- From local_en names table
general_othername,
is_faction_leader = true, -- Bool for whether the general being replaced is the new faction leader
general_is_faction_leader,
trait = "wh2_dlc09_trait_benevolence" -- The trait key you want to assign to the new General from character traits table
};


-- Generals created this way does not come with a trait and aren't immortal
local general_x_pos, general_y_pos = cm:find_valid_spawn_location_for_character_from_settlement(general_details.general_faction, general_details.region_key, false, true, 8);
function(cqi)
out(faction_name .. " home region name is " .. general_details.region_key);
local char_str = cm:char_lookup_str(cqi);
-- Adding a new trait to the above general
cm:force_add_trait(char_str, "wh2_dlc09_trait_benevolence", true);
out("Adding a trait to " .. general_forename);


-- Making the new general immortal (!!Use Skill based immortality instead!!)
cm:create_force_with_general(
cm:set_character_immortality(char_str, true);
general_details.general_faction,
out("Making general with forename " .. general_forename .. " immortal");
general_details.unit_list,
end
general_details.region_key,
);
general_x_pos,
out("Creating Neo_Karl Franz for Reikland");
general_y_pos,
general_details.type,
general_details.subtype,
general_details.forename,
general_details.clanname,
general_details.surname,
general_details.othername,
general_details.is_faction_leader,


-- Killing original Karl Franz
-- Generals created this way does not come with a trait normally
local char_list = general_faction_obj:character_list();
function(cqi)
local leader_subtype = "emp_karl_franz"; -- Karl Franz's agent subtype
local char_str = cm:char_lookup_str(cqi);
local leader_forename = "names_name_2147343849"; -- Karl Franz's forename


for i = 0, char_list:num_items() - 1 do
-- Adding a new trait to the above general
local current_char = char_list:item_at(i);
cm:force_add_trait(char_str, general_details.trait, true);
out("Adding Replacement General's trait");
end
);
out("Created replacement Lord " .. general_details.forename .. " for " .. faction_name);


if current_char:is_null_interface() == false and current_char:character_subtype_key() == leader_subtype and current_char:get_forename() == leader_forename and current_char:has_military_force() == true then
-- Killing Karl Franz permanently
cm:set_character_immortality("character_cqi:"..current_char:command_queue_index(), false); -- Removing Karl Franz's immortality
local char_list = faction:character_list();
cm:disable_event_feed_events(true, "wh_event_category_character", "", ""); -- Disabling event feed messages for character related events
local char_subtype = "emp_karl_franz"; -- Karl Franz's agent subtype
cm:kill_character(current_char:command_queue_index(), true, true); -- Killing Karl Franz
local char_forename = "names_name_2147343849"; -- Karl Franz's forename
cm:callback(function() cm:disable_event_feed_events(false, "wh_event_category_character", "", "") end, 1); -- Enabling event feed messages for character related events
 
out("Killing original " .. leader_subtype .. " with forename " .. leader_forename .. " for " .. general_faction_str .. " permanently");
for i = 0, char_list:num_items() - 1 do
local current_char = char_list:item_at(i);
local char_str = cm:char_lookup_str(current_char);
 
if current_char:is_null_interface() == false and current_char:character_subtype_key() == char_subtype and current_char:get_forename() == char_forename and current_char:has_military_force() == true then
cm:set_character_immortality(char_str, false);
cm:disable_event_feed_events(true, "wh_event_category_character", "", "");
cm:kill_character(current_char:command_queue_index(), true, true);
cm:callback(function() cm:disable_event_feed_events(false, "wh_event_category_character", "", "") end, 0.5);
out("Killing original " .. char_subtype .. " with forename " .. char_forename .. " for " .. faction_name .. " permanently");
end;
end;
end;
end;
end;
Line 107: Line 111:
end;
end;


-- Rename replace_starting_general to match the function name at the top
cm:add_first_tick_callback(function() replace_starting_general() end);</nowiki>
cm:add_first_tick_callback(function() replace_starting_general() end);</nowiki>


=== Adding custom RoRs to mercenary pool ===
=== Adding RoRs to mercenary pool ===


  <nowiki>--[[
  <nowiki>--[[
    Script by Aexrael Dex
Script by Aexrael Dex
    Adds custom Regiments of Reknown to defined factions
Adds custom Regiments of Reknown to specified factions
]]
]]


local function cror_initiator()
local function add_custom_ror()
    -- Checking whether the script has already run for saved games and if it has then the script doesn't need to run again
    if cm:get_saved_value("custom_ror_enabled") == nil then


        -- Table for faction, unit key and parameters for add_unit_to_faction_mercenary_pool
-- Checking whether the script has already run for saved games and if it has then the script doesn't need to run again
        local cror_list = {
if cm:get_saved_value("custom_ror_enabled") == nil then
            {faction = "wh_main_vmp_schwartzhafen", unit = "wh2_dlc11_cst_inf_zombie_deckhands_mob_ror_0", count = 1, rcp = 100, munits = 1, murpt = 0.1, xplevel = 0, frr = "", srr = "", trr = "", replen = false},
            {faction = "wh_main_vmp_vampire_counts", unit = "wh2_dlc11_cst_inf_zombie_deckhands_mob_ror_0", count = 1, rcp = 100, munits = 1, murpt = 0.1, xplevel = 0, frr = "", srr = "", trr = "", replen = false}
        };


        -- Loop for the table above
-- Table for faction, unit key and parameters for add_unit_to_faction_mercenary_pool
        for i = 1, #cror_list do
local cror_list = {
            local faction_str = cror_list[i].faction; -- Faction whose pool the unit(s) should be added to
{faction_key = "wh_main_vmp_schwartzhafen", unit = "wh2_dlc11_cst_inf_zombie_deckhands_mob_ror_0", count = 1, rcp = 100, munits = 1, murpt = 0.1, xplevel = 0, frr = "", srr = "", trr = "", replen = true},
            local faction_obj = cm:get_faction(faction_str); -- FACTION_SCRIPT_INTERFACE faction
{faction_key = "wh_main_vmp_vampire_counts", unit = "wh2_dlc11_cst_inf_zombie_deckhands_mob_ror_0", count = 1, rcp = 100, munits = 1, murpt = 0.1, xplevel = 0, frr = "", srr = "", trr = "", replen = true}
            local unit_key = cror_list[i].unit; -- Key of unit to add to the mercenary pool, from the main_units table
};
            local unit_count = cror_list[i].count; -- Number of units to add to the mercenary pool
            local rcp = cror_list[i].rcp; -- Replenishment chance, as a percentage
            local munits = cror_list[i].munits; -- The maximum number of units of the supplied type that the pool is allowed to contain.
            local murpt = cror_list[i].murpt; -- The maximum number of units of the supplied type that may be added by replenishment per-turn
            local xplevel = cror_list[i].xplevel; -- The experience level of the units when recruited
            local frr = cror_list[i].frr; -- (may be empty) The key of the faction who can actually recruit the units, from the factions database table
            local srr = cror_list[i].srr; -- (may be empty) The key of the subculture who can actually recruit the units, from the cultures_subcultures database table
            local trr = cror_list[i].trr; -- (may be empty) The key of a technology that must be researched in order to recruit the units, from the technologies database table
            local replen = cror_list[i].replen; -- Allow replenishment of partial units


            -- Adding the listed unit to the listed faction in the above table
-- Loop for the table above
            cm:add_unit_to_faction_mercenary_pool(faction_obj, unit_key, unit_count, rcp, munits, murpt, xplevel, frr, srr, trr, replen);
for i = 1, #cror_list do
local faction_name = cror_list[i].faction_key; -- Faction whose pool the unit(s) should be added to
local faction = cm:get_faction(faction_name); -- FACTION_SCRIPT_INTERFACE
local unit_key = cror_list[i].unit; -- Key of unit to add to the mercenary pool, from the main_units table
local unit_count = cror_list[i].count; -- Number of units to add to the mercenary pool
local rcp = cror_list[i].rcp; -- Replenishment chance, as a percentage
local munits = cror_list[i].munits; -- The maximum number of units of the supplied type that the pool is allowed to contain.
local murpt = cror_list[i].murpt; -- The maximum number of units of the supplied type that may be added by replenishment per-turn
local xplevel = cror_list[i].xplevel; -- The experience level of the units when recruited
local frr = cror_list[i].frr; -- (may be empty) The key of the faction who can actually recruit the units, from the factions database table
local srr = cror_list[i].srr; -- (may be empty) The key of the subculture who can actually recruit the units, from the cultures_subcultures database table
local trr = cror_list[i].trr; -- (may be empty) The key of a technology that must be researched in order to recruit the units, from the technologies database table
local replen = cror_list[i].replen; -- Allow replenishment of partial units


            -- Setting saved value, so that the script doesn't run again when reloaded from a saved game
-- Adding the listed unit to the listed faction in the above table
            cm:set_saved_value("custom_ror_enabled", true);
cm:add_unit_to_faction_mercenary_pool(faction, unit_key, unit_count, rcp, munits, murpt, xplevel, frr, srr, trr, replen);


            -- Debug message for log
-- Debug message for log
            out("adding the custom ror unit " .. unit_key .. " to " .. faction_str);
out("CROR: adding the custom ror unit " .. unit_key .. " to " .. faction_name);
    end;
end;
    end;
 
-- Setting saved value, so that the script doesn't run again when reloaded from a saved game
cm:set_saved_value("custom_ror_enabled", true);
end;
end;
end;


cm:add_first_tick_callback(function() cror_initiator() end);</nowiki>
cm:add_first_tick_callback(function() add_custom_ror() end);</nowiki>


=== Replacing Starting Armies ===
=== Replacing Starting Armies ===
Line 160: Line 164:
  <nowiki>--[[
  <nowiki>--[[
Script by Aexrael Dex
Script by Aexrael Dex
Replaces the starting units for the designated characters
Replaces the starting units for specified starting Lords
]]
 
local function army_tweaks()
 
-- Checking whether it's a new game, we don't want to replace armies in the middle of a campaign
if cm:is_new_game() then
 
-- Creating a table with entries for the various starting Lords, their Faction key from Factions table, subtype key from agent_subtypes, Forename from names table and Unit List with keys from Main Units
local starting_army = {
-- Vlad, Carsteins
{faction_key = "wh_main_vmp_schwartzhafen", subtype = "dlc04_vmp_vlad_con_carstein", forename = "names_name_2147345130", units = {"wh_main_vmp_inf_skeleton_warriors_0", "wh_main_vmp_inf_skeleton_warriors_0", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_cav_black_knights_0", "wh_main_vmp_cav_black_knights_0"}},
-- Isabella, Carsteins
{faction_key = "wh_main_vmp_schwartzhafen", subtype = "pro02_vmp_isabella_von_carstein", forename = "names_name_2147345124", units = {"wh_main_vmp_inf_zombie", "wh_main_vmp_inf_zombie", "wh_main_vmp_mon_dire_wolves", "wh_main_vmp_mon_dire_wolves", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_mon_vargheists"}}
};
 
for i = 1, #starting_army do
local faction_name = starting_army[i].faction_key;
local faction = cm:get_faction(faction_name);
local char_list = faction:character_list();
local general_subtype = starting_army[i].subtype;
local general_forename = starting_army[i].forename;
local unit_list = starting_army[i].units;
 
for j = 0, char_list:num_items() - 1 do
local current_char = char_list:item_at(j);
local char_str = cm:char_lookup_str(current_char);
 
if current_char:is_null_interface() == false and current_char:character_subtype_key() == general_subtype and current_char:get_forename() == general_forename and current_char:has_military_force() == true then
 
-- Removing all existing units from the General
cm:remove_all_units_from_general(current_char);
out("ARMY: Removing starting units from " .. general_subtype .. " with forename " .. general_forename);
 
for k = 1, #unit_list do
local unit = unit_list[k];
 
-- Granting new units to the General
cm:grant_unit_to_character(char_str, unit);
out("ARMY: Adding new starting units to " .. general_subtype .. " with forename " .. general_forename);
end;
end;
end;
end;
end;
end;
 
cm:add_first_tick_callback(function() army_tweaks() end);</nowiki>
 
=== Changing CAI based on campaign difficulty ===
 
<nowiki>--[[
Script by Aexrael Dex
Replaces the Campaign AI Personality for specific factions depending on the chosen campaign difficulty level
]]
]]


-- Rename edit_starting_army
local function replace_cai_difficulty()
local function edit_starting_army()
if cm:is_new_game() == true then
    if cm:is_new_game() then
local difficulty_str = cm:get_difficulty(true);
        local custom_starting_army = {
local def_personality = "";
            -- Vlad, Carsteins
local hef_personality = "";
            {faction = "wh_main_vmp_schwartzhafen", subtype = "dlc04_vmp_vlad_con_carstein", forename = "names_name_2147345130", units = {"wh_main_vmp_inf_skeleton_warriors_0", "wh_main_vmp_inf_skeleton_warriors_0", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_cav_black_knights_0", "wh_main_vmp_cav_black_knights_0"}},
local wef_personality = "";
            -- Isabella, Carsteins
 
            {faction = "wh_main_vmp_schwartzhafen", subtype = "pro02_vmp_isabella_von_carstein", forename = "names_name_2147345124", units = {"wh_main_vmp_inf_zombie", "wh_main_vmp_inf_zombie", "wh_main_vmp_mon_dire_wolves", "wh_main_vmp_mon_dire_wolves", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_mon_vargheists"}}
-- CAI personality to use for easy difficulty
        };
if difficulty_str == "easy" then


        for i = 1, #custom_starting_army do
def_personality = "wh2_darkelf_early_easy";
            local faction_str = custom_starting_army[i].faction;
hef_personality = "wh2_highelf_early_easy";
            local faction_obj = cm:get_faction(faction_str);
wef_personality = "wh_dlc05_wood_elves_default_main";
            local char_list = faction_obj:character_list();
            local general_subtype = custom_starting_army[i].subtype;
            local general_forename = custom_starting_army[i].forename;
            local unit_list = custom_starting_army[i].units;


            for j = 0, char_list:num_items() - 1 do
-- CAI personality to use for normal difficulty
                local current_char = char_list:item_at(j);
elseif difficulty_str == "normal" then
       
 
                if current_char:is_null_interface() == false and current_char:character_subtype_key() == general_subtype and current_char:get_forename() == general_forename and current_char:has_military_force() == true then
def_personality = "wh2_darkelf_early";
                    cm:remove_all_units_from_general(current_char);
hef_personality = "wh2_highelf_early";
                    out("Removing starting units from " .. general_subtype .. " with forename " .. general_forename);
wef_personality = "wh_dlc05_wood_elves_default_main";
 
-- CAI personality to use for hard difficulty
elseif difficulty_str == "hard" then
 
def_personality = "wh2_darkelf_early_hard";
hef_personality = "wh2_highelf_early_hard";
wef_personality = "wh_dlc05_wood_elves_default_main";
 
-- CAI personality to use for very hard difficulty
elseif difficulty_str == "very hard" then
 
def_personality = "wh2_darkelf_early_hard";
hef_personality = "wh2_highelf_early_hard";
wef_personality = "wh_dlc05_wood_elves_default_main";
 
-- CAI personality to use for legendary difficulty
elseif difficulty_str == "legendary" then
 
def_personality = "wh2_darkelf_early_hard";
hef_personality = "wh2_highelf_early_hard";
wef_personality = "wh_dlc05_wood_elves_default_main";
end;
 
local faction_list = cm:model():world():faction_list();
 
for i = 0, faction_list:num_items() - 1 do
local faction = faction_list:item_at(i);
local faction_name = faction:name();
local faction_culture = faction:culture();
 
-- Checking whetether the faction exists and is alive
if faction:is_null_interface() == false and faction:is_dead() == false and faction:is_human() == false then
 
-- Dark Elves culture
if faction_culture == "wh2_main_def_dark_elves" then
 
-- Changing the CAI for all Dark Elf factions
cm:force_change_cai_faction_personality(faction_name, def_personality);
out("ELF: Changing CAI personality for " .. faction_name .. " to " .. def_personality .. " on " .. difficulty_str .. " difficulty");
 
-- High Elves culture
elseif faction_culture == "wh2_main_hef_high_elves" then
 
-- Changing the CAI for all High Elf factions
cm:force_change_cai_faction_personality(faction_name, hef_personality);
out("ELF: Changing CAI personality for " .. faction_name .. " to " .. hef_personality .. " on " .. difficulty_str .. " difficulty");
 
-- Wood Elves culture
elseif faction_culture == "wh_dlc05_wef_wood_elves" then
 
-- Changing the CAI for all Wood Elf factions
cm:force_change_cai_faction_personality(faction_name, wef_personality);
out("ELF: Changing CAI personality for " .. faction_name .. " to " .. wef_personality .. " on " .. difficulty_str .. " difficulty");
end;
end;
end;
end;
end;
 
cm:add_first_tick_callback(function() replace_cai_difficulty() end);</nowiki>
 
=== Spawning a Unique Agent ===
 
<nowiki>
--[[
Script by Aexrael Dex
Spawns a Unique Agent next to the starting lord of a specified faction
]]
 
local function add_unique_agent()
 
-- Checking whether the script has already run for saved games and if it has then the script doesn't need to run again
if cm:get_saved_value("unique_agent_enabled") == nil then
 
-- Starting agent setup
unique_agent_setup();
end;
end;
 
function unique_agent_setup()
-- Agent Details
local agent_details = {
faction_str = "wh2_dlc13_emp_the_huntmarshals_expedition", -- faction_key from factions
forename_key = "names_name_2147359013", -- forename_key from names
family_name_key = "names_name_1535812850", -- family_name_key from names
subtype_key = "wh2_dlc13_emp_hunter_jorek_grimm", -- agent subtype_key from agent_subtypes
art_set_key = "wh2_dlc13_art_set_emp_hunter_jorek_grimm_0", -- agent art_set_id from campaign_character_arts
unique_string = "wh2_dlc13_emp_hunter_jorek_grimm", -- unique agent string from unique_agents
saved_value = "unique_agent_enabled", -- saved_value string
trait = "wh2_dlc09_trait_benevolence" -- agent trait from character_traits
};
 
-- Monitor activated, listening for FactionTurnStart for The Huntmarshals Expedition
core:add_listener(
"unique_agent_setup",
"FactionTurnStart",
function(context)
return context:faction():name() == agent_details.faction_str;
end,
function(context)
-- Spawning Jorek as a unique agent next to Markus
local faction = cm:get_faction(agent_details.faction_str);
local faction_cqi = faction:command_queue_index();
local faction_leader_cqi = faction:faction_leader():command_queue_index();
 
cm:disable_event_feed_events(true, "wh_event_category_agent", "", "");
cm:spawn_unique_agent_at_character(
faction_cqi,
agent_details.unique_string,
faction_leader_cqi,
true
);
cm:callback(function()
cm:disable_event_feed_events(false, "wh_event_category_agent", "", "");
CampaignUI.ClearSelection();
end, 0.5);
out("UNIQ: Spawned Jorek next to Markus");
 
-- Looping through the character list for The Huntmarshals Expedition
local char_list = faction:character_list();
 
for i = 0, char_list:num_items() - 1 do
local current_char = char_list:item_at(i);
local char_str = cm:char_lookup_str(current_char);
 
-- Adding Joreks trait, replenishing AP and overriding the art set
if current_char:is_null_interface() == false and current_char:character_subtype_key() == agent_details.subtype_key then
 
-- Adding trait
cm:force_add_trait(char_str, agent_details.trait, false);
out("UNIQ: Adding " .. agent_details.trait .. " to Jorek");
 
-- Replenishing action points
cm:replenish_action_points(char_str);
out("UNIQ: Replenishing the action points of Jorek");
 
-- Failsafe for the random chance that the art_set doesn't apply properly
cm:add_unit_model_overrides(char_str, agent_details.art_set_key);
out("UNIQ: Adding unit model override for Jorek");
end;
end;


                    for k = 1, #unit_list do
-- Setting saved value, so that the script doesn't run again when reloaded from a saved game
                        local unit = unit_list[k];
cm:set_saved_value(agent_details.saved_value, true);
                        cm:grant_unit_to_character(cm:char_lookup_str(current_char:cqi()), unit);
out("UNIQ: Setting saved value " .. agent_details.saved_value);
                        out("Granting new starting units to " .. general_subtype .. " with forename " .. general_forename);
end,
                    end;
false
                end;
);
            end;
        end;
    end;
end;
end;


-- Rename edit_starting_army to match the function name at the top
cm:add_first_tick_callback(function() add_unique_agent() end);</nowiki>
cm:add_first_tick_callback(function() edit_starting_army() end);</nowiki>

Revision as of 13:44, 29 March 2021

A list of example scripts that can easily be copied, edited and then put into a mod.

Using Example Scripts

Unless otherwise stated these scripts go into a .pack file at `script/campaign/mod/?.lua`, replace ? with whatever your file name is.

Each script listed is self-contained - so you can use multiple of the example scripts in a single .lua file.

When using the scripts, please keep the credits at the top and don't remove comments. Comments are the text following -- or in a comment block denoted by --[ [ Text here ] ].

Typically an example script will have a list of variables that should be changed to tailor the script to your particular mod, these will often have comments next to them explaining where you will find the keys or values from.

Example in the Replace Starting General example script, you have a line that reads 'local faction_name = "wh_main_emp_empire";'

This could instead be changed to 'local faction_name = "wh2_dlc11_vmp_the_barrow_legion";' to affect the Barrow Legion faction instead of The Empire.

If there are out() calls within the example script, you are encouraged to insert a unique text identifier, so you can find your unique output in the script logs using ctrl+f.

Example in the Replacing Starting General Script, replace REPLAC: with your unique identifier, maybe the starting letters of your discord username, or the mod name or the name of your cat, or something else unique to you.

Contributing new scripts

If you have some generic scripted functionality you think would be useful for the wider community, please consider adding it to the wiki here, or bring attention to it via Modding Discord in the channel for the wiki.

Add a new Sub-Heading 1 within the Example Scripts section below, give it a meaningful title and a short description. Include what games it is valid for and any specific notes necessary, ie. has to go in this directory, has to be done this way etc.

Avoid using global variables in the example scripts, use local variables and local functions to avoid incidents involving scripts that have overlapping names. Make sure to use comments explaining what the various parts of the script is doing or when referring to specific keys from the database, like where to find them. Context and explanation goes a long way towards helping make sense of the scripts for beginners.

If your script runs through a single init function that's triggered through a first tick callback, give the local function a meaningful name and implement the first-tick-callback, and leave it at that. No need to have the modder replace the local function name in their own replication.

Mind that plopping something in this page leaves it open for collaboration, so someone in the future may come in and add more comments, change anything necessary for an update, make an extra version for another game, or much else. If you're editing the original script, leave the credits the same, or add your name to the credits if you think the addition is meaningful. Don't remove the OG author for sure.

Example Scripts

Replacing a Starting General

--[[
	Script by Aexrael Dex
	Replaces the starting general for a specific faction
]]

local function replace_starting_general()
	if cm:is_new_game() then
		-- The Empire
		local faction_name = "wh_main_emp_empire";		-- Faction key from Factions table
		local faction = cm:get_faction(faction_name);

		if faction:is_null_interface() == false and faction:is_dead() == false then

			-- Creating replacement for Karl Franz with a regular Empire General
			local general_details = {
				general_faction = faction_name,
				unit_list = "wh_main_emp_cav_reiksguard,wh_main_emp_cav_reiksguard,wh_main_emp_cav_reiksguard"; -- unit keys from main_units table
				region_key = faction:home_region():name(),
				type = "general",																				-- Agent type
				subtype = "emp_lord",																			-- Agent subtype
				forename = "names_name_1904032251",																-- From local_en names table, Bernhoff the Butcher is now ruler of Reikland
				clanname = "",																					-- From local_en names table
				surname = "names_name_151217003",																-- From local_en names table
				othername = "",																					-- From local_en names table
				is_faction_leader = true,																		-- Bool for whether the general being replaced is the new faction leader
				trait = "wh2_dlc09_trait_benevolence"															-- The trait key you want to assign to the new General from character traits table
			};

			local general_x_pos, general_y_pos = cm:find_valid_spawn_location_for_character_from_settlement(general_details.general_faction, general_details.region_key, false, true, 8);
			out(faction_name .. " home region name is " .. general_details.region_key);

			cm:create_force_with_general(
				general_details.general_faction,
				general_details.unit_list,
				general_details.region_key,
				general_x_pos,
				general_y_pos,
				general_details.type,
				general_details.subtype,
				general_details.forename,
				general_details.clanname,
				general_details.surname,
				general_details.othername,
				general_details.is_faction_leader,

				-- Generals created this way does not come with a trait normally
				function(cqi)
					local char_str = cm:char_lookup_str(cqi);

					-- Adding a new trait to the above general
					cm:force_add_trait(char_str, general_details.trait, true);
					out("Adding Replacement General's trait");
				end
			);
			out("Created replacement Lord " .. general_details.forename .. " for " .. faction_name);

			-- Killing Karl Franz permanently
			local char_list = faction:character_list();
			local char_subtype = "emp_karl_franz"; -- Karl Franz's agent subtype
			local char_forename = "names_name_2147343849"; -- Karl Franz's forename

			for i = 0, char_list:num_items() - 1 do
				local current_char = char_list:item_at(i);
				local char_str = cm:char_lookup_str(current_char);

				if current_char:is_null_interface() == false and current_char:character_subtype_key() == char_subtype and current_char:get_forename() == char_forename and current_char:has_military_force() == true then
					cm:set_character_immortality(char_str, false);
					cm:disable_event_feed_events(true, "wh_event_category_character", "", "");
					cm:kill_character(current_char:command_queue_index(), true, true);
					cm:callback(function() cm:disable_event_feed_events(false, "wh_event_category_character", "", "") end, 0.5);
					out("Killing original " .. char_subtype .. " with forename " .. char_forename .. " for " .. faction_name .. " permanently");
				end;
			end;
		end;
	end;
end;

cm:add_first_tick_callback(function() replace_starting_general() end);

Adding RoRs to mercenary pool

--[[
	Script by Aexrael Dex
	Adds custom Regiments of Reknown to specified factions
]]

local function add_custom_ror()

	-- Checking whether the script has already run for saved games and if it has then the script doesn't need to run again
	if cm:get_saved_value("custom_ror_enabled") == nil then

		-- Table for faction, unit key and parameters for add_unit_to_faction_mercenary_pool
		local cror_list = {
			{faction_key = "wh_main_vmp_schwartzhafen", unit = "wh2_dlc11_cst_inf_zombie_deckhands_mob_ror_0", count = 1, rcp = 100, munits = 1, murpt = 0.1, xplevel = 0, frr = "", srr = "", trr = "", replen = true},
			{faction_key = "wh_main_vmp_vampire_counts", unit = "wh2_dlc11_cst_inf_zombie_deckhands_mob_ror_0", count = 1, rcp = 100, munits = 1, murpt = 0.1, xplevel = 0, frr = "", srr = "", trr = "", replen = true}
		};

		-- Loop for the table above
		for i = 1, #cror_list do
			local faction_name = cror_list[i].faction_key;	-- Faction whose pool the unit(s) should be added to
			local faction = cm:get_faction(faction_name);	-- FACTION_SCRIPT_INTERFACE
			local unit_key = cror_list[i].unit;				-- Key of unit to add to the mercenary pool, from the main_units table
			local unit_count = cror_list[i].count;			-- Number of units to add to the mercenary pool
			local rcp = cror_list[i].rcp;					-- Replenishment chance, as a percentage
			local munits = cror_list[i].munits;				-- The maximum number of units of the supplied type that the pool is allowed to contain.
			local murpt = cror_list[i].murpt;				-- The maximum number of units of the supplied type that may be added by replenishment per-turn
			local xplevel = cror_list[i].xplevel;			-- The experience level of the units when recruited
			local frr = cror_list[i].frr;					-- (may be empty) The key of the faction who can actually recruit the units, from the factions database table
			local srr = cror_list[i].srr;					-- (may be empty) The key of the subculture who can actually recruit the units, from the cultures_subcultures database table
			local trr = cror_list[i].trr;					-- (may be empty) The key of a technology that must be researched in order to recruit the units, from the technologies database table
			local replen = cror_list[i].replen;				-- Allow replenishment of partial units

			-- Adding the listed unit to the listed faction in the above table
			cm:add_unit_to_faction_mercenary_pool(faction, unit_key, unit_count, rcp, munits, murpt, xplevel, frr, srr, trr, replen);

			-- Debug message for log
			out("CROR: adding the custom ror unit " .. unit_key .. " to " .. faction_name);
		end;

		-- Setting saved value, so that the script doesn't run again when reloaded from a saved game
		cm:set_saved_value("custom_ror_enabled", true);
	end;
end;

cm:add_first_tick_callback(function() add_custom_ror() end);

Replacing Starting Armies

--[[
	Script by Aexrael Dex
	Replaces the starting units for specified starting Lords
]]

local function army_tweaks()

	-- Checking whether it's a new game, we don't want to replace armies in the middle of a campaign
	if cm:is_new_game() then

		-- Creating a table with entries for the various starting Lords, their Faction key from Factions table, subtype key from agent_subtypes, Forename from names table and Unit List with keys from Main Units
		local starting_army = {
			-- Vlad, Carsteins
			{faction_key = "wh_main_vmp_schwartzhafen", subtype = "dlc04_vmp_vlad_con_carstein", forename = "names_name_2147345130", units = {"wh_main_vmp_inf_skeleton_warriors_0", "wh_main_vmp_inf_skeleton_warriors_0", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_cav_black_knights_0", "wh_main_vmp_cav_black_knights_0"}},
			-- Isabella, Carsteins
			{faction_key = "wh_main_vmp_schwartzhafen", subtype = "pro02_vmp_isabella_von_carstein", forename = "names_name_2147345124", units = {"wh_main_vmp_inf_zombie", "wh_main_vmp_inf_zombie", "wh_main_vmp_mon_dire_wolves", "wh_main_vmp_mon_dire_wolves", "wh_main_vmp_mon_fell_bats", "wh_main_vmp_mon_vargheists"}}
		};

		for i = 1, #starting_army do
			local faction_name = starting_army[i].faction_key;
			local faction = cm:get_faction(faction_name);
			local char_list = faction:character_list();
			local general_subtype = starting_army[i].subtype;
			local general_forename = starting_army[i].forename;
			local unit_list = starting_army[i].units;

			for j = 0, char_list:num_items() - 1 do
				local current_char = char_list:item_at(j);
				local char_str = cm:char_lookup_str(current_char);

				if current_char:is_null_interface() == false and current_char:character_subtype_key() == general_subtype and current_char:get_forename() == general_forename and current_char:has_military_force() == true then

					-- Removing all existing units from the General
					cm:remove_all_units_from_general(current_char);
					out("ARMY: Removing starting units from " .. general_subtype .. " with forename " .. general_forename);

					for k = 1, #unit_list do
						local unit = unit_list[k];

						-- Granting new units to the General
						cm:grant_unit_to_character(char_str, unit);
						out("ARMY: Adding new starting units to " .. general_subtype .. " with forename " .. general_forename);
					end;
				end;
			end;
		end;
	end;
end;

cm:add_first_tick_callback(function() army_tweaks() end);

Changing CAI based on campaign difficulty

--[[
	Script by Aexrael Dex
	Replaces the Campaign AI Personality for specific factions depending on the chosen campaign difficulty level
]]

local function replace_cai_difficulty()
	if cm:is_new_game() == true then
		local difficulty_str = cm:get_difficulty(true);
		local def_personality = "";
		local hef_personality = "";
		local wef_personality = "";

		-- CAI personality to use for easy difficulty
		if difficulty_str == "easy" then

			def_personality = "wh2_darkelf_early_easy";
			hef_personality = "wh2_highelf_early_easy";
			wef_personality = "wh_dlc05_wood_elves_default_main";

		-- CAI personality to use for normal difficulty
		elseif difficulty_str == "normal" then

			def_personality = "wh2_darkelf_early";
			hef_personality = "wh2_highelf_early";
			wef_personality = "wh_dlc05_wood_elves_default_main";

		-- CAI personality to use for hard difficulty
		elseif difficulty_str == "hard" then

			def_personality = "wh2_darkelf_early_hard";
			hef_personality = "wh2_highelf_early_hard";
			wef_personality = "wh_dlc05_wood_elves_default_main";

		-- CAI personality to use for very hard difficulty
		elseif difficulty_str == "very hard" then

			def_personality = "wh2_darkelf_early_hard";
			hef_personality = "wh2_highelf_early_hard";
			wef_personality = "wh_dlc05_wood_elves_default_main";

		-- CAI personality to use for legendary difficulty
		elseif difficulty_str == "legendary" then

			def_personality = "wh2_darkelf_early_hard";
			hef_personality = "wh2_highelf_early_hard";
			wef_personality = "wh_dlc05_wood_elves_default_main";
		end;

		local faction_list = cm:model():world():faction_list();

		for i = 0, faction_list:num_items() - 1 do
			local faction = faction_list:item_at(i);
			local faction_name = faction:name();
			local faction_culture = faction:culture();

			-- Checking whetether the faction exists and is alive
			if faction:is_null_interface() == false and faction:is_dead() == false and faction:is_human() == false then

					-- Dark Elves culture
				if faction_culture == "wh2_main_def_dark_elves" then

					-- Changing the CAI for all Dark Elf factions
					cm:force_change_cai_faction_personality(faction_name, def_personality);
					out("ELF: Changing CAI personality for " .. faction_name .. " to " .. def_personality .. " on " .. difficulty_str .. " difficulty");

					-- High Elves culture
				elseif faction_culture == "wh2_main_hef_high_elves" then

					-- Changing the CAI for all High Elf factions
					cm:force_change_cai_faction_personality(faction_name, hef_personality);
					out("ELF: Changing CAI personality for " .. faction_name .. " to " .. hef_personality .. " on " .. difficulty_str .. " difficulty");

					-- Wood Elves culture
				elseif faction_culture == "wh_dlc05_wef_wood_elves" then

					-- Changing the CAI for all Wood Elf factions
					cm:force_change_cai_faction_personality(faction_name, wef_personality);
					out("ELF: Changing CAI personality for " .. faction_name .. " to " .. wef_personality .. " on " .. difficulty_str .. " difficulty");
				end;
			end;
		end;
	end;
end;

cm:add_first_tick_callback(function() replace_cai_difficulty() end);

Spawning a Unique Agent

--[[
	Script by Aexrael Dex
	Spawns a Unique Agent next to the starting lord of a specified faction
]]

local function add_unique_agent()

	-- Checking whether the script has already run for saved games and if it has then the script doesn't need to run again
	if cm:get_saved_value("unique_agent_enabled") == nil then

		-- Starting agent setup
		unique_agent_setup();
	end;
end;

function unique_agent_setup()
	-- Agent Details
	local agent_details = {
		faction_str = "wh2_dlc13_emp_the_huntmarshals_expedition",	-- faction_key from factions
		forename_key = "names_name_2147359013",						-- forename_key from names
		family_name_key = "names_name_1535812850",					-- family_name_key from names
		subtype_key = "wh2_dlc13_emp_hunter_jorek_grimm",			-- agent subtype_key from agent_subtypes
		art_set_key = "wh2_dlc13_art_set_emp_hunter_jorek_grimm_0",	-- agent art_set_id from campaign_character_arts
		unique_string = "wh2_dlc13_emp_hunter_jorek_grimm",			-- unique agent string from unique_agents
		saved_value = "unique_agent_enabled",						-- saved_value string
		trait = "wh2_dlc09_trait_benevolence"						-- agent trait from character_traits
	};

	-- Monitor activated, listening for FactionTurnStart for The Huntmarshals Expedition
	core:add_listener(
		"unique_agent_setup",
		"FactionTurnStart",
		function(context)
			return context:faction():name() == agent_details.faction_str;
		end,
		function(context)
			-- Spawning Jorek as a unique agent next to Markus
			local faction = cm:get_faction(agent_details.faction_str);
			local faction_cqi = faction:command_queue_index();
			local faction_leader_cqi = faction:faction_leader():command_queue_index();

			cm:disable_event_feed_events(true, "wh_event_category_agent", "", "");
			cm:spawn_unique_agent_at_character(
				faction_cqi,
				agent_details.unique_string,
				faction_leader_cqi,
				true
			);
			cm:callback(function()
				cm:disable_event_feed_events(false, "wh_event_category_agent", "", "");
				CampaignUI.ClearSelection();
			end, 0.5);
			out("UNIQ: Spawned Jorek next to Markus");

			-- Looping through the character list for The Huntmarshals Expedition
			local char_list = faction:character_list();

			for i = 0, char_list:num_items() - 1 do
				local current_char = char_list:item_at(i);
				local char_str = cm:char_lookup_str(current_char);

				-- Adding Joreks trait, replenishing AP and overriding the art set
				if current_char:is_null_interface() == false and current_char:character_subtype_key() == agent_details.subtype_key then

					-- Adding trait
					cm:force_add_trait(char_str, agent_details.trait, false);
					out("UNIQ: Adding " .. agent_details.trait .. " to Jorek");

					-- Replenishing action points
					cm:replenish_action_points(char_str);
					out("UNIQ: Replenishing the action points of Jorek");

					-- Failsafe for the random chance that the art_set doesn't apply properly
					cm:add_unit_model_overrides(char_str, agent_details.art_set_key);
					out("UNIQ: Adding unit model override for Jorek");
				end;
			end;

			-- Setting saved value, so that the script doesn't run again when reloaded from a saved game
			cm:set_saved_value(agent_details.saved_value, true);
			out("UNIQ: Setting saved value " .. agent_details.saved_value);
		end,
		false
	);
end;

cm:add_first_tick_callback(function() add_unique_agent() end);