· 4 years ago · Mar 12, 2021, 11:14 AM
1local script_author = "8 Hertz WAN IP";
2local script_version = "2.2";
3local mod_name = "hertz_province_patrols";
4
5-- When patrols fail to get an order, the script will retry giving orders to patrols.
6-- These functions control and limit the amount of times the script wil retry ordering all patrols to avoid infinite loops
7local order_retry_count = 0;
8local order_retry_cap = 5;
9
10out(mod_name.." script version V" ..script_version.. " - Made by " ..script_author);
11out("HERTZ EDIT - [@wh_dlc07_peasant_economy.lua]: Removed province patrols from peasant effect calculation on line [104]");
12out("HERTZ EDIT - [@wh_campaign_setup.lua]: Removed province patrols supply bundle application on line [1156]");
13
14-- Mostly used for tracking faction wars atm
15local faction_patrol_list = {};
16
17-- Checks which patrols are removed when they are lost in the wrong region
18-- Also tracks previous turn settlement rank for incidents
19local patrol_saved_information = {removed_cqis = {}};
20
21-- Player faction
22local player_faction = nil;
23
24-- If patrols have moved from their original positon this goes to true
25local hertz_patrols_ordered = false;
26
27-- Vortex or Mortal Empires campaign
28local campaign_model = nil;
29
30-- Activates leightweight mode where patrols teleport for the player instead of normally move
31local leightweight_mode_cap = 50;
32
33-- Disables the functionality that orders all patrols after a dilemma has fired.
34-- Ordering patrols after a dilemma is fired is only needed at turn start so will need to disabled after this phase has ended.
35local disable_post_dilemma_orders = false;
36
37
38-- The following factions are excluded from the province patrol functionality
39local war_faction_exclusion_list = {
40 "wh2_dlc11_cst_harpoon_the_sunken_land_corsairs",
41 "wh2_dlc11_cst_rogue_bleak_coast_buccaneers",
42 "wh2_dlc11_cst_rogue_boyz_of_the_forbidden_coast",
43 "wh2_dlc11_cst_rogue_freebooters_of_port_royale",
44 "wh2_dlc11_cst_rogue_grey_point_scuttlers",
45 "wh2_dlc11_cst_rogue_terrors_of_the_dark_straights",
46 "wh2_dlc11_cst_rogue_the_churning_gulf_raiders",
47 "wh2_dlc11_cst_rogue_tyrants_of_the_black_ocean",
48 "wh2_dlc11_cst_shanty_dragon_spine_privateers",
49 "wh2_dlc11_cst_shanty_middle_sea_brigands",
50 "wh2_dlc11_cst_shanty_shark_straight_seadogs";
51}
52
53-- These are factions that are only active on the campaign map under certain conditions. Example: Rebellions
54local war_faction_inclusion_list = {
55 "wh2_dlc09_tmb_tomb_kings_rebels",
56 "wh2_dlc11_cst_vampire_coast_rebellion_rebels",
57 "wh2_main_def_dark_elves_rebels",
58 "wh2_main_hef_high_elves_rebels",
59 "wh2_main_lzd_lizardmen_rebels",
60 "wh2_main_skv_skaven_rebels",
61 "wh_dlc03_bst_beastmen_rebels",
62 "wh_dlc03_bst_beastmen_rebels",
63 "wh_dlc05_wef_wood_elves_rebels",
64 "wh_main_brt_bretonnia_rebels",
65 "wh_main_chs_chaos_rebels",
66 "wh_main_dwf_dwarf_rebels",
67 "wh_main_emp_empire_rebels",
68 "wh_main_emp_empire_rebels",
69 "wh_main_emp_marienburg_rebels",
70 "wh_main_grn_greenskins_rebels",
71 "wh_main_grn_greenskins_rebels",
72 "wh_main_ksl_kislev_rebels",
73 "wh_main_nor_norsca_rebels",
74 "wh_main_teb_border_princes_rebels",
75 "wh_main_teb_estalia_rebels",
76 "wh_main_teb_tilea_rebels",
77 "wh2_dlc09_skv_clan_rictus_separatists",
78 "wh2_dlc11_cst_noctilus_separatists",
79 "wh2_dlc11_cst_pirates_of_sartosa_separatists",
80 "wh2_dlc11_cst_the_drowned_separatists",
81 "wh2_dlc11_cst_vampire_coast_separatists",
82 "wh2_dlc11_def_the_blessed_dread_separatists",
83 "wh2_main_def_cult_of_pleasure_separatists",
84 "wh2_main_def_hag_graef_separatists",
85 "wh2_main_def_har_ganeth_separatists",
86 "wh2_main_def_naggarond_separatists",
87 "wh2_main_skv_clan_eshin_separatists",
88 "wh2_main_skv_clan_mors_separatists",
89 "wh2_main_skv_clan_pestilens_separatists",
90 "wh2_main_skv_clan_skryre_separatists",
91 "wh_main_chs_chaos_separatists",
92 "wh_main_emp_empire_separatists",
93 "wh_main_nor_norsca_separatists",
94 "wh_main_nor_norsca_separatists_sorcerer_lord",
95 "wh_main_vmp_vampire_rebels";
96}
97
98-- Subcultures using the province patrol logic
99-- This activates all province patrol functionality for all included subcultures
100local active_faction_subcultures = {
101 "wh_main_sc_vmp_vampire_counts",
102 "wh_main_sc_nor_norsca",
103 "wh_main_sc_grn_greenskins",
104 "wh_main_sc_teb_teb",
105 "wh_main_sc_ksl_kislev",
106 "wh_main_sc_emp_empire",
107 "wh_main_sc_dwf_dwarfs",
108 "wh_main_sc_brt_bretonnia",
109 "wh_dlc05_sc_wef_wood_elves",
110 "wh2_main_sc_skv_skaven",
111 "wh2_main_sc_lzd_lizardmen",
112 "wh2_main_sc_hef_high_elves",
113 "wh2_main_sc_def_dark_elves",
114 "wh2_dlc09_sc_tmb_tomb_kings",
115 "wh2_dlc11_sc_cst_vampire_coast";
116}
117
118-- All the army setup information for all factions (excluding tomb kings) is found in this table.
119-- Tomb kings patrol army info is below this table
120--[[
121 The first index listed is a template for all army setup information needed to built a province patrol
122
123 -- Table index should be the same subculture entered in active_faction_subcultures()
124 -- Table for faction specific customized patrols(TABLE_OBJECT)
125 - Enables faction specific customized patrols(BOOLEAN true/false)
126 - Sets faction specific customized patrol type for another commander or a full unit list(STRING "com"/"full")
127 -- The factions that will have faction specific customized patrols(STRING TABLE faction_keys)
128 -- Target faction table(INDEX STRING faction_key)
129 - Replacing commander/army table(TABLE_OBJECTC)
130 - or (switches on custom patrol type "com"/"full")
131 - Replacing patrol general (STRING from the agents_subtypes_tables)
132 -- The Replacing Province Patrol name (STRING from the names_name_tables)
133 - Replacing Infantry melee units for the faction patrol army(STRING TABLE unit_key from land_units_tables)
134 - Replacing Infantry missile units for the faction patrol army(STRING TABLE unit_key from land_units_tables)
135 - Replacing Monsters and cavalry units for the faction patrol army(STRING TABLE unit_key from land_units_tables)
136 - Replacing Random/specialized units for the faction patrol army(STRING TABLE unit_key from land_units_tables)
137 -- Default patrol general (STRING from the agents_subtypes_tables)
138 -- The Default Province Patrol name (STRING from the names_name_tables)
139 - Default Infantry melee units for the default patrol army(STRING TABLE unit_key from land_units_tables)
140 - Default Infantry missile units for the default patrol army(STRING TABLE unit_key from land_units_tables)
141 - Default Monsters and cavalry units for the default patrol army(STRING TABLE unit_key from land_units_tables)
142 - Default Random/specialized units for the default patrol army(STRING TABLE unit_key from land_units_tables)
143]]
144local ram_unit_lists = {
145 ["TEMPLATE"] = {
146 faction_custom_patrols = {
147 enabled = false,
148 type = "none",
149 factions = {""},
150 [""] = {
151 com = ""
152 };
153 },
154 com = "",
155 name = "",
156 inf_mel = {
157 "",
158 },
159 inf_mis = {
160 "",
161 },
162 large = {
163 "",
164 },
165 random = {
166 "",
167 };
168 },
169 ["wh_main_sc_vmp_vampire_counts"] = {
170 faction_custom_patrols = {
171 enabled = false,
172 type = "none",
173 factions = {},
174 },
175 com = "vmp_master_necromancer",
176 name = "names_name_500376",
177 },
178 ["wh_main_sc_nor_norsca"] = {
179 faction_custom_patrols = {
180 enabled = false,
181 type = "none",
182 factions = {},
183 },
184 com = "nor_marauder_chieftain",
185 name = "names_name_500378",
186 inf_mel = {
187 "wh_main_nor_inf_chaos_marauders_0",
188 "wh_main_nor_inf_chaos_marauders_1";
189 },
190 inf_mis = {
191 "wh_dlc08_nor_inf_marauder_hunters_1" ,
192 "wh_dlc08_nor_inf_marauder_hunters_0";
193 },
194 large = {
195 "wh_dlc08_nor_mon_fimir_0" ,
196 "wh_dlc08_nor_mon_skinwolves_0" ,
197 "wh_dlc08_nor_cav_marauder_horsemasters_0" ,
198 "wh_main_nor_cav_marauder_horsemen_0" ,
199 "wh_main_nor_cav_marauder_horsemen_1";
200 },
201 random = {
202 "wh_dlc08_nor_inf_marauder_spearman_0" ,
203 "wh_dlc08_nor_inf_marauder_berserkers_0";
204 };
205 },
206 ["wh_main_sc_grn_savage_orcs"] = {
207 faction_custom_patrols = {
208 enabled = false,
209 type = "none",
210 factions = {},
211 },
212 com = "grn_orc_warboss",
213 name = "names_name_500386",
214 inf_mel = {
215 "wh_main_grn_inf_savage_orcs",
216 "wh_main_grn_inf_goblin_spearmen";
217 },
218 inf_mis = {
219 "wh_main_grn_inf_savage_orc_arrer_boyz";
220 },
221 large = {
222 "wh_main_grn_mon_trolls",
223 "wh_main_grn_cav_savage_orc_boar_boyz";
224 },
225 random = {
226 "wh_main_grn_inf_savage_orc_arrer_boyz",
227 "wh_main_grn_inf_savage_orc_big_uns",
228 "wh_main_grn_inf_savage_orcs",
229 "wh_main_grn_cav_savage_orc_boar_boy_big_uns",
230 "wh_main_grn_cav_savage_orc_boar_boyz";
231 };
232 },
233 ["wh_main_sc_grn_greenskins"] = {
234 faction_custom_patrols = {
235 enabled = false,
236 type = "none",
237 factions = {},
238 },
239 com = "grn_orc_warboss",
240 name = "names_name_500383",
241 inf_mel = {
242 "wh_main_grn_inf_goblin_spearmen",
243 "wh_main_grn_inf_night_goblins",
244 "wh_main_grn_inf_orc_boyz";
245 },
246 inf_mis = {
247 "wh_main_grn_inf_goblin_archers",
248 "wh_main_grn_inf_night_goblin_archers",
249 "wh_main_grn_inf_orc_arrer_boyz";
250 },
251 large = {
252 "wh_main_grn_cav_orc_boar_chariot",
253 "wh_main_grn_cav_orc_boar_boyz",
254 "wh_main_grn_cav_goblin_wolf_riders_0";
255 },
256 random = {
257 "wh2_dlc15_grn_mon_stone_trolls_0",
258 "wh_main_grn_mon_trolls",
259 "wh2_dlc15_grn_veh_snotling_pump_wagon_0",
260 "wh_dlc06_grn_cav_squig_hoppers_0";
261 };
262 },
263 ["wh_main_sc_teb_teb"] = {
264 faction_custom_patrols = {
265 enabled = false,
266 type = "none",
267 factions = {},
268 },
269 com = "teb_lord",
270 name = "names_name_500381",
271 inf_mel = {
272 "wh_main_emp_inf_spearmen_1",
273 "wh_main_emp_inf_swordsmen";
274 },
275 inf_mis = {
276 "wh_main_emp_inf_handgunners",
277 "wh2_dlc13_emp_inf_archers_0",
278 "wh_main_emp_inf_crossbowmen",
279 "wh_main_emp_inf_handgunners";
280 },
281 large = {
282 "wh_main_emp_cav_outriders_0",
283 "wh_main_emp_cav_pistoliers_1",
284 "wh_main_emp_cav_empire_knights";
285 },
286 random = {
287 "wh_dlc04_emp_inf_flagellants_0",
288 "wh_main_emp_inf_spearmen_1",
289 "wh_main_emp_inf_swordsmen",
290 "wh2_dlc13_emp_inf_archers_0",
291 "wh_main_emp_cav_pistoliers_1";
292 };
293 },
294 ["wh_main_sc_ksl_kislev"] = {
295 faction_custom_patrols = {
296 enabled = false,
297 type = "none",
298 factions = {},
299 },
300 com = "ksl_lord",
301 name = "names_name_500380",
302 inf_mel = {
303 "wh_main_emp_inf_spearmen_1",
304 "wh_main_emp_inf_swordsmen";
305 },
306 inf_mis = {
307 "wh_main_emp_inf_handgunners",
308 "wh2_dlc13_emp_inf_archers_0",
309 "wh_main_emp_inf_crossbowmen",
310 "wh_main_emp_inf_handgunners";
311 },
312 large = {
313 "wh_main_emp_cav_outriders_0",
314 "wh_main_emp_cav_pistoliers_1",
315 "wh_main_emp_cav_empire_knights";
316 },
317 random = {
318 "wh_dlc04_emp_inf_flagellants_0",
319 "wh_main_emp_inf_spearmen_1",
320 "wh_main_emp_inf_swordsmen",
321 "wh2_dlc13_emp_inf_archers_0",
322 "wh_main_emp_cav_pistoliers_1";
323 };
324 },
325 ["wh_main_sc_emp_empire"] = {
326 faction_custom_patrols = {
327 enabled = true,
328 type = "lord",
329 factions = {"wh2_dlc13_emp_the_huntmarshals_expedition"},
330 ["wh2_dlc13_emp_the_huntmarshals_expedition"] = {
331 com = "wh2_dlc13_emp_cha_huntsmarshal_0"
332 };
333 },
334 com = "emp_lord",
335 name = "names_name_500384",
336 inf_mel = {
337 "wh_main_emp_inf_spearmen_1",
338 "wh_main_emp_inf_swordsmen";
339 },
340 inf_mis = {
341 "wh_main_emp_inf_handgunners",
342 "wh2_dlc13_emp_inf_archers_0",
343 "wh_main_emp_inf_crossbowmen",
344 "wh_main_emp_inf_handgunners";
345 },
346 large = {
347 "wh_main_emp_cav_outriders_0",
348 "wh_main_emp_cav_pistoliers_1",
349 "wh_main_emp_cav_empire_knights";
350 },
351 random = {
352 "wh_dlc04_emp_inf_flagellants_0",
353 "wh_main_emp_inf_spearmen_1",
354 "wh_main_emp_inf_swordsmen",
355 "wh2_dlc13_emp_inf_archers_0",
356 "wh_main_emp_cav_pistoliers_1";
357 };
358 },
359 ["wh_main_sc_dwf_dwarfs"] = {
360 faction_custom_patrols = {
361 enabled = true,
362 type = "full",
363 factions = {"wh_main_dwf_karak_izor", "wh_main_dwf_karak_kadrin", "wh_main_dwf_dwarfs"},
364 ["wh_main_dwf_karak_izor"] = {
365 com = "dwf_lord",
366 name = "names_name_500368",
367 inf_mel = {
368 "wh_main_dwf_inf_dwarf_warrior_0",
369 "wh_main_dwf_inf_dwarf_warrior_1",
370 "wh_dlc06_dwf_inf_rangers_0";
371 },
372 inf_mis = {
373 "wh_dlc06_dwf_inf_rangers_0",
374 "wh_dlc06_dwf_inf_rangers_1";
375 },
376 large = {
377 "wh_main_dwf_art_cannon",
378 "wh_dlc06_dwf_art_bolt_thrower_0",
379 "wh_main_dwf_art_grudge_thrower",
380 "wh_dlc06_dwf_inf_bugmans_rangers_0";
381 },
382 random = {
383 "wh_main_dwf_art_cannon",
384 "wh_main_dwf_inf_dwarf_warrior_0",
385 "wh_main_dwf_veh_gyrocopter_1";
386 };
387 },
388 ["wh_main_dwf_karak_kadrin"] = {
389 com = "dwf_lord",
390 name = "names_name_500368",
391 inf_mel = {
392 "wh_main_dwf_inf_dwarf_warrior_0",
393 "wh_main_dwf_inf_dwarf_warrior_1",
394 "wh_main_dwf_inf_slayers";
395 },
396 inf_mis = {
397 "wh_main_dwf_inf_quarrellers_0",
398 "wh_main_dwf_inf_thunderers_0";
399 },
400 large = {
401 "wh2_dlc10_dwf_inf_giant_slayers",
402 "wh_dlc06_dwf_art_bolt_thrower_0",
403 "wh_main_dwf_art_grudge_thrower",
404 "wh_main_dwf_inf_slayers";
405 },
406 random = {
407 "wh2_dlc10_dwf_inf_giant_slayers",
408 "wh_main_dwf_inf_dwarf_warrior_0",
409 "wh_main_dwf_inf_slayers";
410 };
411 },
412 ["wh_main_dwf_dwarfs"] = {
413 com = "dwf_lord",
414 name = "names_name_500368",
415 inf_mel = {
416 "wh_main_dwf_inf_dwarf_warrior_0",
417 "wh_main_dwf_inf_dwarf_warrior_1",
418 "wh_main_dwf_inf_miners_0";
419 },
420 inf_mis = {
421 "wh_main_dwf_inf_quarrellers_0",
422 "wh_main_dwf_inf_thunderers_0";
423 },
424 large = {
425 "wh_main_dwf_art_cannon",
426 "wh_main_dwf_inf_hammerers",
427 "wh_main_dwf_art_grudge_thrower",
428 "wh_main_dwf_veh_gyrocopter_0";
429 },
430 random = {
431 "wh_main_dwf_inf_slayers",
432 "wh_main_dwf_art_cannon",
433 "wh_main_dwf_inf_hammerers",
434 "wh_main_dwf_veh_gyrocopter_1";
435 };
436 },
437 },
438 com = "dwf_lord",
439 name = "names_name_500368",
440 inf_mel = {
441 "wh_main_dwf_inf_dwarf_warrior_0",
442 "wh_main_dwf_inf_dwarf_warrior_1",
443 "wh_main_dwf_inf_miners_0";
444 },
445 inf_mis = {
446 "wh_main_dwf_inf_quarrellers_0",
447 "wh_main_dwf_inf_thunderers_0";
448 },
449 large = {
450 "wh_main_dwf_art_cannon",
451 "wh_dlc06_dwf_art_bolt_thrower_0",
452 "wh_main_dwf_art_grudge_thrower",
453 "wh_main_dwf_veh_gyrocopter_0";
454 },
455 random = {
456 "wh_main_dwf_inf_slayers",
457 "wh_main_dwf_art_cannon",
458 "wh_main_dwf_inf_miners_1",
459 "wh_main_dwf_veh_gyrocopter_1";
460 };
461 },
462 ["wh_main_sc_brt_bretonnia"] = {
463 faction_custom_patrols = {
464 enabled = false,
465 type = "none",
466 factions = {},
467 },
468 com = "brt_lord",
469 name = "names_name_500375",
470 inf_mel = {
471 "wh_dlc07_brt_inf_foot_squires_0",
472 "wh_dlc07_brt_inf_men_at_arms_1";
473 },
474 inf_mis = {
475 "wh_dlc07_brt_inf_peasant_bowmen_1",
476 "wh_dlc07_brt_inf_peasant_bowmen_2";
477 },
478 large = {
479 "wh_main_brt_cav_knights_of_the_realm",
480 "wh_dlc07_brt_cav_knights_errant_0";
481 },
482 random = {
483 "wh_dlc07_brt_cav_knights_errant_0",
484 "wh_main_brt_cav_knights_of_the_realm";
485 };
486 },
487 ["wh_dlc05_sc_wef_wood_elves"] = {
488 faction_custom_patrols = {
489 enabled = true,
490 type = "full",
491 factions = {"wh2_dlc16_wef_drycha"},
492 ["wh2_dlc16_wef_drycha"] = {
493 com = "wh2_dlc16_wef_malicious_ancient_treeman_shadows",
494 name = "names_name_500373",
495 inf_mel = {
496 "wh2_dlc16_wef_inf_malicious_dryads_0",
497 "wh2_dlc16_wef_mon_giant_spiders_0",
498 "wh2_dlc16_wef_mon_wolves_0";
499 },
500 inf_mis = {
501 "wh2_dlc16_wef_mon_cave_bats",
502 "wh2_dlc16_wef_inf_malicious_dryads_0",
503 },
504 large = {
505 "wh2_dlc16_wef_mon_malicious_treekin_0",
506 "wh2_dlc16_wef_mon_hawks_0",
507 },
508 random = {
509 "wh2_dlc16_wef_mon_hawks_0",
510 "wh_dlc05_wef_mon_great_eagle_0",
511 "wh2_dlc16_wef_mon_feral_manticore",
512 "wh2_dlc16_wef_mon_zoats",
513 "wh2_dlc16_wef_mon_malicious_treekin_0";
514 };
515 };
516 },
517 com = "dlc05_wef_glade_lord",
518 name = "names_name_500373",
519 inf_mel = {
520 "wh_dlc05_wef_inf_eternal_guard_1",
521 "wh_dlc05_wef_inf_wildwood_rangers_0";
522 },
523 inf_mis = {
524 "wh_dlc05_wef_inf_glade_guard_0",
525 "wh_dlc05_wef_inf_glade_guard_1",
526 "wh_dlc05_wef_inf_glade_guard_2",
527 "wh_dlc05_wef_inf_deepwood_scouts_0";
528 },
529 large = {
530 "wh_dlc05_wef_inf_dryads_0",
531 "wh_dlc05_wef_mon_great_eagle_0";
532 },
533 random = {
534 "wh_dlc05_wef_mon_treekin_0",
535 "wh_dlc05_wef_mon_great_eagle_0",
536 "wh_dlc05_wef_inf_glade_guard_1",
537 "wh_dlc05_wef_inf_glade_guard_2",
538 "wh_dlc05_wef_inf_deepwood_scouts_0";
539 };
540 },
541 ["wh2_main_sc_skv_skaven"] = {
542 faction_custom_patrols = {
543 enabled = true,
544 type = "lord",
545 factions = {"wh2_main_skv_clan_pestilens", "wh2_main_skv_clan_pestilens", "wh2_main_skv_clan_skyre"},
546 ["wh2_main_skv_clan_pestilens"] = {
547 com = "wh2_dlc14_skv_master_assassin"
548 },
549 ["wh2_main_skv_clan_pestilens"] = {
550 com = "wh2_main_skv_grey_seer_plague"
551 },
552 ["wh2_main_skv_clan_skyre"] = {
553 com = "wh2_dlc12_skv_warlock_master"
554 };
555 },
556 com = "wh2_main_skv_warlord",
557 name = "names_name_500370",
558 inf_mel = {
559 "wh2_main_skv_inf_clanrat_spearmen_0",
560 "wh2_main_skv_inf_clanrats_0";
561 },
562 inf_mis = {
563 "wh2_main_skv_inf_gutter_runners_0",
564 "wh2_main_skv_inf_gutter_runners_1",
565 "wh2_main_skv_inf_night_runners_0",
566 "wh2_main_skv_inf_night_runners_1";
567 },
568 large = {
569 "wh2_main_skv_mon_rat_ogres",
570 "wh2_dlc12_skv_veh_doom_flayer_0";
571 },
572 random = {
573 "wh2_main_skv_mon_rat_ogres",
574 "wh2_main_skv_inf_plague_monks",
575 "wh2_dlc12_skv_veh_doom_flayer_0",
576 "wh2_main_skv_inf_skavenslave_spearmen_0",
577 "wh2_main_skv_inf_skavenslaves_0";
578 };
579 },
580 ["wh2_main_sc_lzd_lizardmen"] = {
581 faction_custom_patrols = {
582 enabled = true,
583 type = "full",
584 factions = {"wh2_dlc12_lzd_cult_of_sotek", "wh2_main_lzd_tlaqua", "wh2_dlc13_lzd_spirits_of_the_jungle"},
585 ["wh2_dlc12_lzd_cult_of_sotek"] = {
586 com = "wh2_dlc12_lzd_red_crested_skink_chief",
587 name = "names_name_500369",
588 inf_mel = {
589 "wh2_main_lzd_inf_skink_skirmishers_0",
590 "wh2_dlc12_lzd_inf_skink_red_crested_0",
591 },
592 inf_mis = {
593 "wh2_main_lzd_inf_chameleon_skinks_0",
594 "wh2_main_lzd_inf_skink_cohort_1",
595 },
596 large = {
597 "wh2_main_lzd_mon_bastiladon_0",
598 "wh2_dlc13_lzd_mon_razordon_pack_0",
599 "wh2_main_lzd_mon_kroxigors",
600 },
601 random = {
602 "wh2_main_lzd_cav_terradon_riders_0",
603 "wh2_main_lzd_mon_bastiladon_1",
604 "wh2_main_lzd_cav_terradon_riders_1",
605 };
606 },
607 ["wh2_dlc13_lzd_spirits_of_the_jungle"] = {
608 com = "wh2_dlc13_lzd_kroxigor_ancient",
609 name = "names_name_500369",
610 inf_mel = {
611 "wh2_main_lzd_inf_skink_skirmishers_0",
612 "wh2_main_lzd_inf_skink_cohort_0";
613 },
614 inf_mis = {
615 "wh2_main_lzd_inf_chameleon_skinks_0",
616 "wh2_main_lzd_inf_skink_cohort_1";
617 },
618 large = {
619 "wh2_dlc13_lzd_mon_sacred_kroxigors_0",
620 "wh2_main_lzd_mon_kroxigors";
621 },
622 random = {
623 "wh2_main_lzd_cav_terradon_riders_0",
624 "wh2_dlc13_lzd_mon_sacred_kroxigors_0",
625 "wh2_main_lzd_mon_kroxigors";
626 "wh2_main_lzd_inf_saurus_spearmen_0",
627 "wh2_main_lzd_inf_saurus_warriors_0";
628 "wh2_main_lzd_inf_saurus_spearmen_0",
629 "wh2_main_lzd_inf_saurus_warriors_0";
630 };
631 },
632 ["wh2_main_lzd_tlaqua"] = {
633 com = "wh2_dlc12_lzd_red_crested_skink_chief",
634 name = "names_name_500369",
635 inf_mel = {
636 "wh2_main_lzd_inf_skink_skirmishers_0",
637 "wh2_main_lzd_inf_skink_cohort_0",
638 },
639 inf_mis = {
640 "wh2_dlc12_lzd_cav_terradon_riders_0_tlaqua",
641 "wh2_dlc12_lzd_cav_terradon_riders_1_tlaqua",
642 },
643 large = {
644 "wh2_dlc12_lzd_cav_terradon_riders_0_tlaqua",
645 "wh2_dlc12_lzd_cav_terradon_riders_1_tlaqua",
646 },
647 random = {
648 "wh2_main_lzd_inf_chameleon_skinks_0",
649 "wh2_dlc12_lzd_inf_skink_red_crested_0",
650 "wh2_main_lzd_inf_skink_cohort_1",
651 "wh2_main_lzd_mon_kroxigors",
652 "wh2_main_lzd_mon_bastiladon_0",
653 };
654 };
655 },
656 com = "wh2_main_lzd_saurus_old_blood",
657 name = "names_name_500369",
658 inf_mel = {
659 "wh2_main_lzd_inf_skink_cohort_0",
660 "wh2_dlc12_lzd_inf_skink_red_crested_0",
661 "wh2_main_lzd_inf_saurus_spearmen_0",
662 "wh2_main_lzd_inf_saurus_warriors_0";
663 },
664 inf_mis = {
665 "wh2_main_lzd_inf_skink_skirmishers_0",
666 "wh2_main_lzd_inf_chameleon_skinks_0";
667 },
668 large = {
669 "wh2_main_lzd_mon_bastiladon_0",
670 "wh2_main_lzd_mon_stegadon_0",
671 "wh2_main_lzd_cav_cold_one_spearmen_1";
672 },
673 random = {
674 "wh2_main_lzd_cav_cold_one_spearmen_1",
675 "wh2_dlc12_lzd_mon_salamander_pack_0",
676 "wh2_main_lzd_mon_kroxigors";
677 };
678 },
679 ["wh2_main_sc_hef_high_elves"] = {
680 faction_custom_patrols = {
681 enabled = false,
682 type = "none",
683 factions = {},
684 },
685 com = "wh2_main_hef_prince",
686 name = "names_name_500372",
687 inf_mel = {
688 "wh2_main_hef_inf_spearmen_0",
689 "wh2_dlc15_hef_inf_rangers_0";
690 },
691 inf_mis = {
692 "wh2_main_hef_inf_archers_0",
693 "wh2_main_hef_inf_archers_1";
694 },
695 large = {
696 "wh2_main_hef_mon_great_eagle",
697 "wh2_dlc15_hef_mon_war_lions_of_chrace_0",
698 "wh2_main_hef_cav_ellyrian_reavers_0",
699 "wh2_main_hef_cav_ellyrian_reavers_1";
700 },
701 random = {
702 "wh2_main_hef_cav_silver_helms_0",
703 "wh2_main_hef_cav_silver_helms_1",
704 "wh2_main_hef_mon_great_eagle",
705 "wh2_dlc15_hef_mon_war_lions_of_chrace_0",
706 "wh2_main_hef_inf_white_lions_of_chrace_0";
707 };
708 },
709 ["wh2_main_sc_def_dark_elves"] = {
710 faction_custom_patrols = {
711 enabled = false,
712 type = "none",
713 factions = {},
714 },
715 com = "wh2_main_def_dreadlord",
716 name = "names_name_500374",
717 inf_mel = {
718 "wh2_main_def_inf_bleakswords_0",
719 "wh2_main_def_inf_dreadspears_0",
720 "wh2_main_def_inf_black_ark_corsairs_0";
721 },
722 inf_mis = {
723 "wh2_main_def_inf_darkshards_0",
724 "wh2_main_def_inf_darkshards_1",
725 "wh2_main_def_inf_black_ark_corsairs_1";
726 },
727 large = {
728 "wh2_main_def_cav_dark_riders_0",
729 "wh2_main_def_cav_dark_riders_1",
730 "wh2_main_def_cav_dark_riders_2";
731 },
732 random = {
733 "wh2_dlc10_def_mon_feral_manticore_0",
734 "wh2_main_def_inf_shades_0",
735 "wh2_main_def_cav_cold_one_knights_0",
736 "wh2_main_def_inf_shades_1",
737 "wh2_main_def_cav_cold_one_chariot";
738 };
739 },
740 ["wh2_dlc11_sc_cst_vampire_coast"] = {
741 faction_custom_patrols = {
742 enabled = false,
743 type = "none",
744 factions = {},
745 },
746 com = "wh2_dlc11_cst_admiral_deep",
747 name = "names_name_500382",
748 inf_mel = {
749 "wh2_dlc11_cst_inf_zombie_deckhands_mob_1",
750 "wh2_dlc11_cst_inf_zombie_gunnery_mob_0";
751 },
752 inf_mis = {
753 "wh2_dlc11_cst_inf_zombie_gunnery_mob_1",
754 "wh2_dlc11_cst_inf_zombie_gunnery_mob_2",
755 "wh2_dlc11_cst_inf_zombie_gunnery_mob_3";
756 },
757 large = {
758 "wh2_dlc11_cst_mon_bloated_corpse_0",
759 "wh2_dlc11_cst_mon_mournguls_0",
760 "wh2_dlc11_cst_mon_scurvy_dogs",
761 "wh2_dlc11_cst_cav_deck_droppers_1";
762 },
763 random = {
764 "wh2_dlc11_cst_mon_mournguls_0",
765 "wh2_dlc11_cst_mon_fell_bats",
766 "wh2_dlc11_cst_inf_zombie_deckhands_mob_0",
767 "wh2_dlc11_cst_mon_rotting_prometheans_gunnery_mob_0",
768 "wh2_dlc11_cst_cav_deck_droppers_0";
769 };
770 },
771
772 -- Template for patrol army size
773 ["TEMPLATE"] = { -- Settlement level or rule for specific faction
774 force_size = 9,
775 inf_mel = 4,
776 inf_mis = 3,
777 large = 1,
778 random = 1;
779 },
780
781 --province settlement level for wef outposts
782 ["wef_patrols"] = {
783 force_size = 9,
784 inf_mel = 4,
785 inf_mis = 3,
786 large = 1,
787 random = 1;
788 },
789
790 --province settlement level for Nekai's patrols
791 ["nekai_patrols"] = {
792 force_size = 9,
793 inf_mel = 3,
794 inf_mis = 2,
795 large = 3,
796 random = 1;
797 },
798
799 --province settlement level to patrol army size
800 [2] = {
801 force_size = 7,
802 inf_mel = 3,
803 inf_mis = 3,
804 large = 0,
805 random = 1;
806 },
807 [3] = {
808 force_size = 9,
809 inf_mel = 4,
810 inf_mis = 3,
811 large = 1,
812 random = 1;
813 },
814 [4] = {
815 force_size = 11,
816 inf_mel = 4,
817 inf_mis = 3,
818 large = 2,
819 random = 2;
820 },
821 [5] = {
822 force_size = 15,
823 inf_mel = 4,
824 inf_mis = 3,
825 large = 3,
826 random = 5;
827 };
828};
829
830local tomb_king_patrol_pool = {
831 ["playable_factions"] = {
832 "wh2_dlc09_tmb_exiles_of_nehek",
833 "wh2_dlc09_tmb_lybaras",
834 "wh2_dlc09_tmb_khemri",
835 "wh2_dlc09_tmb_followers_of_nagash";
836 },
837 ["wh2_dlc09_tmb_exiles_of_nehek"] = {
838 unit_list = {
839 "wh2_dlc09_tmb_cha_liche_priest_light_1",
840 "wh2_dlc09_tmb_mon_ushabti_0",
841 "wh2_dlc09_tmb_art_casket_of_souls_0",
842 "wh2_dlc09_tmb_inf_nehekhara_warriors_0";
843 },
844 skill_effect = "hertz_tk_patrol_abilities_light"
845 },
846 ["wh2_dlc09_tmb_lybaras"] = {
847 unit_list = {
848 "wh2_dlc09_tmb_cha_liche_priest_nehekhara_1",
849 "wh2_dlc09_tmb_cav_necropolis_knights_0",
850 "wh2_dlc09_tmb_inf_nehekhara_warriors_0",
851 "wh2_dlc09_tmb_mon_sepulchral_stalkers_0";
852 },
853 skill_effect = "hertz_tk_patrol_abilities_nehek"
854 },
855 ["wh2_dlc09_tmb_khemri"] = {
856 unit_list = {
857 "wh2_dlc09_tmb_mon_ushabti_0",
858 "wh2_dlc09_tmb_cha_liche_priest_nehekhara_1",
859 "wh2_dlc09_tmb_cha_tomb_prince_2",
860 "wh2_dlc09_tmb_veh_skeleton_archer_chariot_0",
861 "wh2_dlc09_tmb_mon_tomb_scorpion_0",
862 "wh2_dlc09_tmb_veh_skeleton_chariot_0",
863 "wh2_dlc09_tmb_veh_skeleton_chariot_0";
864 },
865 skill_effect = "hertz_tk_patrol_abilities_nehek"
866 },
867 ["wh2_dlc09_tmb_followers_of_nagash"] = {
868 unit_list = {
869 "wh2_dlc09_tmb_cha_liche_priest_death_1",
870 "wh2_dlc09_tmb_cav_hexwraiths",
871 "wh2_dlc09_tmb_inf_crypt_ghouls",
872 "wh2_dlc09_tmb_mon_dire_wolves",
873 "wh2_dlc09_tmb_mon_fell_bats";
874 },
875 skill_effect = "hertz_tk_patrol_abilities_death"
876
877 },
878 ["default"] = {
879 com = "wh2_dlc09_tmb_tomb_prince",
880 name = "names_name_500377",
881 inf_mel = {
882 "wh2_dlc09_tmb_inf_nehekhara_warriors_0",
883 "wh2_dlc09_tmb_inf_skeleton_warriors_0",
884 "wh2_dlc09_tmb_inf_skeleton_spearmen_0";
885 },
886 inf_mis = {
887 "wh2_dlc09_tmb_inf_skeleton_archers_0",
888 },
889 large = {
890 "wh2_dlc09_tmb_cav_skeleton_horsemen_0",
891 "wh2_dlc09_tmb_cav_skeleton_horsemen_archers_0",
892 "wh2_dlc09_tmb_veh_skeleton_chariot_0";
893 },
894 random = {
895 "wh2_dlc09_tmb_mon_ushabti_0",
896 "wh2_dlc09_tmb_cav_skeleton_horsemen_archers_0",
897 "wh2_dlc09_tmb_mon_tomb_scorpion_0",
898 "wh2_dlc09_tmb_inf_skeleton_archers_0",
899 },
900 non_elite = {
901 "wh2_dlc09_tmb_inf_nehekhara_warriors_0",
902 "wh2_dlc09_tmb_inf_skeleton_warriors_0",
903 "wh2_dlc09_tmb_inf_skeleton_spearmen_0",
904 "wh2_dlc09_tmb_inf_skeleton_archers_0";
905 };
906 },
907 tomb_kings_patrol_unit_cap = {
908 target_unit_list = {
909 "wh2_dlc09_tmb_mon_tomb_scorpion_0",
910 "wh2_dlc09_tmb_mon_ushabti_0",
911 "wh2_dlc09_tmb_art_casket_of_souls_0",
912 "wh2_dlc09_tmb_cha_liche_priest_light_1",
913 "wh2_dlc09_tmb_cha_liche_priest_nehekhara_1",
914 "wh2_dlc09_tmb_cav_necropolis_knights_0",
915 "wh2_dlc09_tmb_mon_sepulchral_stalkers_0",
916 "wh2_dlc09_tmb_cha_tomb_prince_2",
917 "wh2_dlc09_tmb_cha_liche_priest_death_1";
918 },
919 ["wh2_dlc09_tmb_mon_tomb_scorpion_0"] = {
920 cap = 1,
921 army_count = 0;
922 },
923 ["wh2_dlc09_tmb_mon_ushabti_0"] = {
924 cap = 2,
925 army_count = 0;
926 },
927 ["wh2_dlc09_tmb_art_casket_of_souls_0"] = {
928 cap = 1,
929 army_count = 0;
930 },
931 ["wh2_dlc09_tmb_cha_liche_priest_light_1"] = {
932 cap = 1,
933 army_count = 0;
934 },
935 ["wh2_dlc09_tmb_cha_liche_priest_nehekhara_1"] = {
936 cap = 1,
937 army_count = 0;
938 },
939 ["wh2_dlc09_tmb_cav_necropolis_knights_0"] = {
940 cap = 1,
941 army_count = 0;
942 },
943 ["wh2_dlc09_tmb_mon_sepulchral_stalkers_0"] = {
944 cap = 2,
945 army_count = 0;
946 },
947 ["wh2_dlc09_tmb_cha_tomb_prince_2"] = {
948 cap = 2,
949 army_count = 0;
950 },
951 ["wh2_dlc09_tmb_cha_liche_priest_nehekhara_1"] = {
952 cap = 1,
953 army_count = 0;
954 },
955 ["wh2_dlc09_tmb_cha_liche_priest_death_1"] = {
956 cap = 1,
957 army_count = 0;
958 };
959 };
960};
961
962-- Custom army unit setups to allow for more cav
963local brettonia_patrol_settings = {
964 --province settlement levels
965 [2] = {
966 force_size = 7,
967 inf_mel = 2,
968 inf_mis = 2,
969 large = 2,
970 random = 1;
971 },
972 [3] = {
973 force_size = 9,
974 inf_mel = 2,
975 inf_mis = 2,
976 large = 4,
977 random = 1;
978 },
979 [4] = {
980 force_size = 11,
981 inf_mel = 2,
982 inf_mis = 2,
983 large = 5,
984 random = 2;
985 },
986 [5] = {
987 force_size = 15,
988 inf_mel = 2,
989 inf_mis = 2,
990 large = 6,
991 random = 5;
992 };
993}
994
995local vamp_unit_lists = {
996 [2] = {
997 -- Default rank 1 force
998 required_corruption = 0.5,
999 inf_mel = {
1000 "wh_main_vmp_inf_crypt_ghouls",
1001 "wh_main_vmp_inf_skeleton_warriors_0",
1002 "wh_main_vmp_inf_skeleton_warriors_1";
1003 },
1004 inf_mis = {
1005 "wh_main_vmp_mon_dire_wolves",
1006 "wh_main_vmp_mon_fell_bats",
1007 "wh_main_vmp_inf_skeleton_warriors_1",
1008 "wh_main_vmp_inf_skeleton_warriors_0",
1009 },
1010 large = {
1011 "wh_main_vmp_cav_black_knights_0",
1012 "wh_main_vmp_inf_zombie",
1013 "wh_main_vmp_mon_crypt_horrors";
1014 },
1015 random = {
1016 "wh_main_vmp_inf_skeleton_warriors_0",
1017 "wh_main_vmp_mon_fell_bats",
1018 "wh_main_vmp_cav_black_knights_0",
1019 "wh_main_vmp_mon_crypt_horrors";
1020 };
1021 },
1022 [3] = {
1023 -- Rank 2 force
1024 -- Added Cairn_wraiths, and vargheists
1025 required_corruption = 0.6,
1026 inf_mel = {
1027 "wh_main_vmp_inf_crypt_ghouls",
1028 "wh_main_vmp_inf_skeleton_warriors_1",
1029 "wh_main_vmp_inf_skeleton_warriors_0",
1030 },
1031 inf_mis = {
1032 "wh_main_vmp_mon_dire_wolves",
1033 "wh_main_vmp_mon_dire_wolves",
1034 "wh_main_vmp_inf_cairn_wraiths",
1035 "wh_main_vmp_inf_skeleton_warriors_0",
1036 "wh_main_vmp_inf_skeleton_warriors_1",
1037 },
1038 large = {
1039 "wh_main_vmp_cav_black_knights_0",
1040 "wh_main_vmp_inf_zombie",
1041 "wh_main_vmp_inf_zombie",
1042 "wh_main_vmp_mon_crypt_horrors";
1043 },
1044 random = {
1045 "wh_main_vmp_inf_skeleton_warriors_0",
1046 "wh_main_vmp_inf_skeleton_warriors_1",
1047 "wh_main_vmp_inf_zombie",
1048 "wh_main_vmp_mon_vargheists",
1049 "wh_main_vmp_cav_black_knights_0",
1050 "wh_main_vmp_inf_crypt_ghouls",
1051 "wh_main_vmp_mon_crypt_horrors";
1052 };
1053 },
1054 [4] = {
1055 -- Rank 3 force
1056 -- Added Grave Guard (Great Weapons), corpse cart, and black knight lances
1057 required_corruption = 0.7,
1058 inf_mel = {
1059 "wh_main_vmp_inf_crypt_ghouls",
1060 "wh_main_vmp_inf_crypt_ghouls",
1061 "wh_main_vmp_inf_skeleton_warriors_0";
1062 },
1063 inf_mis = {
1064 "wh_main_vmp_inf_crypt_ghouls",
1065 "wh_main_vmp_inf_grave_guard_1",
1066 "wh_main_vmp_inf_cairn_wraiths",
1067 "wh_main_vmp_inf_skeleton_warriors_1";
1068 },
1069 large = {
1070 "wh_main_vmp_cav_black_knights_0",
1071 "wh_dlc04_vmp_veh_corpse_cart_0",
1072 "wh_main_vmp_inf_crypt_ghouls",
1073 "wh_main_vmp_inf_zombie",
1074 "wh_main_vmp_inf_crypt_ghouls",
1075 "wh_main_vmp_inf_zombie";
1076 },
1077 random = {
1078 "wh_main_vmp_inf_skeleton_warriors_1",
1079 "wh_main_vmp_mon_vargheists",
1080 "wh_main_vmp_inf_crypt_ghouls",
1081 "wh_main_vmp_inf_skeleton_warriors_0",
1082 "wh_main_vmp_cav_black_knights_0",
1083 "wh_main_vmp_cav_black_knights_3",
1084 "wh_main_vmp_inf_crypt_ghouls",
1085 "wh_main_vmp_inf_zombie";
1086 };
1087 },
1088 [5] = {
1089 -- Rank 4 force
1090 -- Added Grave Guard (shields), and decreased chaff unit probability
1091 required_corruption = 0.8,
1092 inf_mel = {
1093 "wh_main_vmp_inf_crypt_ghouls",
1094 "wh_main_vmp_inf_zombie",
1095 "wh_main_vmp_inf_grave_guard_0",
1096 "wh_main_vmp_inf_cairn_wraiths";
1097 },
1098 inf_mis = {
1099 "wh_main_vmp_inf_grave_guard_1",
1100 "wh_main_vmp_inf_zombie",
1101 "wh_main_vmp_mon_vargheists",
1102 "wh_main_vmp_inf_cairn_wraiths",
1103 "wh_main_vmp_inf_grave_guard_0",
1104 },
1105 large = {
1106 "wh_main_vmp_cav_black_knights_0",
1107 "wh_dlc04_vmp_veh_corpse_cart_0",
1108 "wh_main_vmp_inf_crypt_ghouls",
1109 "wh_main_vmp_inf_skeleton_warriors_1",
1110 "wh_main_vmp_inf_skeleton_warriors_0",
1111 "wh_main_vmp_cav_black_knights_3";
1112 },
1113 random = {
1114 "wh_main_vmp_inf_grave_guard_0",
1115 "wh_main_vmp_mon_vargheists",
1116 "wh_main_vmp_cav_black_knights_0",
1117 "wh_main_vmp_inf_crypt_ghouls",
1118 "wh_main_vmp_inf_skeleton_warriors_1",
1119 "wh_main_vmp_inf_skeleton_warriors_0",
1120 "wh_main_vmp_cav_black_knights_3";
1121 };
1122 },
1123
1124 -- vamp counts province patrol force size.
1125 ["force_size"] = {
1126 force_size = 15,
1127 inf_mel = 4,
1128 inf_mis = 3,
1129 large = 3,
1130 random = 5;
1131 };
1132};
1133
1134local province_exlusion = {
1135 ["wh_main_warhammer"] = {
1136 "";
1137 },
1138 ["wh2_main_great_vortex"] = {
1139 "wh2_main_vor_sea_of_dread",
1140 "wh2_main_vor_the_dragon_isles",
1141 },
1142};
1143
1144local region_exclusion = {
1145 ["wh_main_warhammer"] = {
1146 "wh_main_black_mountains_karak_angazhar",
1147 "wh_main_reikland_altdorf",
1148 "wh_main_mountains_of_naglfari_naglfari_plain",
1149 "wh_main_northern_grey_mountains_grung_zint",
1150 "wh_main_the_silver_road_the_pillars_of_grungni",
1151 "wh_main_ostland_wolfenburg",
1152 "wh2_main_aghol_wastelands_the_palace_of_ruin",
1153 "wh2_main_northern_jungle_of_pahualaxa_port_reaver";
1154 },
1155 ["wh2_main_great_vortex"] = {
1156 "wh2_main_vor_ironfrost_glacier_mung_encampment",
1157 "wh2_main_vor_albion_albion",
1158 },
1159};
1160
1161-- Sets up all listeners and is reserved for other possible variables that need to be set every time the lua environment is build
1162function hertz_province_patrols_init()
1163
1164 --sets variables needed for mod updates
1165 if cm:get_saved_value("mod_version_v" ..script_version) == nil then
1166 cm:set_saved_value("patrol_region", false);
1167 cm:set_saved_value("patrol_battle", false);
1168 cm:set_saved_value("patrol_force_count", 0);
1169
1170 cm:set_saved_value("mod_version_v" ..script_version, true);
1171 end;
1172 -- Sets variables needed from turn one
1173 if cm:get_saved_value(mod_name.."_hertz_province_patrols_turn_one_init") == nil then
1174 cm:set_saved_value("patrol_region", false);
1175 cm:set_saved_value("patrol_battle", false);
1176 cm:set_saved_value("patrol_force_count", 0);
1177 cm:set_saved_value("tk_checked_provinces", nil);
1178
1179 cm:set_saved_value(mod_name.."_hertz_province_patrols_turn_one_init", true);
1180 end;
1181
1182
1183 if cm:model():campaign_name("wh2_main_great_vortex") then
1184 campaign_model = "wh2_main_great_vortex"
1185 else
1186 campaign_model = "wh_main_warhammer"
1187 end;
1188 out("Campaign model is ["..campaign_model.."]")
1189
1190 local faction_list = cm:model():world():faction_list()
1191 for i = 0, faction_list:num_items() - 1 do
1192 local target_faction = faction_list:item_at(i);
1193 faction_patrol_list[target_faction:name()] = {province_list = {}, patrols = {}, at_war_with = {}}
1194 end;
1195
1196 -- Checks and sets all faction wars for province patrol target selection
1197 for j = 0, faction_list:num_items() - 1 do
1198 local target_faction = faction_list:item_at(j);
1199 get_faction_wars(target_faction);
1200 end;
1201
1202 hertz_province_listeners()
1203end;
1204
1205function hertz_province_listeners()
1206 out("########## ADDING HERTZ PROVINCE PATROLS LISTENERS ##########")
1207
1208 -- Resets all tracking values and starts the Province Patrol Logic
1209 core:add_listener(
1210 mod_name.."_FactionBeginTurnPhaseNormal",
1211 "FactionBeginTurnPhaseNormal",
1212 function(context)
1213 if table_contains(active_faction_subcultures, context:faction():subculture()) then
1214 return true;
1215 end;
1216 end,
1217 function(context)
1218 cm:set_saved_value("tk_checked_provinces", false);
1219 local target_faction = context:faction()
1220 hertz_patrols_ordered = false;
1221 order_retry_count = 0;
1222 patrol_saved_information.removed_cqis = {};
1223
1224 patrol_and_province_commands(target_faction)
1225 end,
1226 true
1227 );
1228
1229 -- Patroll commands get dropped if a dilemma is open at the start of the players turn. This restars the province patrol logic.
1230 core:add_listener(
1231 mod_name.."_DilemmaChoiceMadeEvent_order_patrols",
1232 "DilemmaChoiceMadeEvent",
1233 function(context)
1234 return context:dilemma() ~= "hertz_patrol_defeated_dilemma";
1235 end,
1236 function(context)
1237 if not hertz_patrols_ordered and disable_post_dilemma_orders == true then
1238 order_retry_count = 0;
1239 patrol_and_province_commands(context:faction());
1240 end;
1241 end,
1242 true
1243 );
1244
1245 -- Update faction wars for the factions involved in the treaty
1246 core:add_listener(
1247 mod_name.."_FactionLeaderSignsPeaceTreaty",
1248 "FactionLeaderSignsPeaceTreaty",
1249 true,
1250 function(context)
1251 local target_faction = context:character():faction();
1252 get_faction_wars(target_faction);
1253 end,
1254 true
1255 );
1256
1257 -- Update faction wars for the factions involved in the treaty
1258 core:add_listener(
1259 mod_name.."_FactionLeaderDeclaresWar",
1260 "FactionLeaderDeclaresWar",
1261 true,
1262 function(context)
1263 local target_faction = context:character():faction();
1264 get_faction_wars(target_faction);
1265 end,
1266 true
1267 );
1268
1269 core:add_listener(
1270 mod_name.."_CharacterSelected",
1271 "CharacterSelected",
1272 true,
1273 function(context)
1274 -- Disable movement for patrols when selected just to be safe.
1275 if context:character():faction():is_human() and context:character():military_force():has_effect_bundle("hertz_patrol_bundle") then
1276 cm:disable_movement_for_character(cm:char_lookup_str(context:character():command_queue_index()));
1277 end;
1278 end,
1279 true
1280 );
1281
1282 -- Disables the functionality that orders all patrols after a dilemma has fired.
1283 core:add_listener(
1284 mod_name.."_DisablePostDilemmaPatrolCommands",
1285 "CharacterSelected",
1286 function(context)
1287 --if context:character():faction():is_human() and context:character():has_effect_bundle("hertz_patrol_bundle") then
1288 if context:character():has_effect_bundle("hertz_patrol_bundle") or context:character():military_force():has_effect_bundle("hertz_patrol_bundle") then
1289 return true;
1290 end;
1291 end,
1292 function(context)
1293 out("Force as patrol bundle ["..tostring(context:character():military_force():has_effect_bundle("hertz_patrol_bundle")).."]")
1294 out("Character as patrol bundle ["..tostring(context:character():has_effect_bundle("hertz_patrol_bundle")).."]")
1295 disable_post_dilemma_orders = false
1296 end,
1297 true
1298 );
1299
1300 -- Closes the unit exchange panel when a force is standing next to a province patrol
1301 core:add_listener(
1302 mod_name.."_PanelOpenedCampaign",
1303 "PanelOpenedCampaign",
1304 function(context)
1305 return context.string == "unit_exchange";
1306 end,
1307 function(context)
1308 local patrol_force = hertz_is_patrol_merging()
1309 if patrol_force then
1310 local uic_close_button = find_uicomponent(core:get_ui_root(), "unit_exchange", "hud_center_docker", "ok_cancel_buttongroup", "button_cancel");
1311
1312 if uic_close_button and uic_close_button:Visible(true) then
1313 uic_close_button:SimulateLClick();
1314 end;
1315 end;
1316 end,
1317 true
1318 );
1319
1320 -- Handles all patrol battles, and cleans up afterwards
1321 core:add_listener(
1322 mod_name.."_PanelOpenedCampaign_battle",
1323 "PanelOpenedCampaign",
1324 function(context)
1325 return context.string == "popup_pre_battle";
1326 end,
1327 function(context)
1328 out("HERTZ_OUT: pre_battle_panel_opened")
1329 local pb = cm:model():pending_battle();
1330 local attacker = pb:attacker();
1331 local defender = pb:defender();
1332 local secondary_attackers = pb:secondary_attackers();
1333 local secondary_defenders = pb:secondary_defenders();
1334
1335
1336 -- When a player starts a province patrol battle this listener will look for any force containing the province patrol bundle
1337 -- When a patrol is found all necessary info will be saved to be called when the battle is fought
1338 -- Info saved:
1339 -- Region the patrol belongs to - Used for effect bundles and dilemma setup
1340 -- which force in the pending battle is the patrol (attacker/defender/secondary_attacker/secondary_defender)
1341 -- The unit count of the patrol force - Used to trigger the dilemma
1342 if cm:get_saved_value("patrol_battle") == "tomb_kings" then
1343 out("Tomb Kings patrol found in [".. cm:get_saved_value("patrol_region").."]");
1344
1345 elseif defender:military_force():has_effect_bundle("hertz_patrol_bundle") then
1346 cm:set_saved_value("patrol_region", defender:region():name());
1347 cm:set_saved_value("patrol_battle", "defender");
1348 out(defender:military_force():unit_list():num_items())
1349 cm:set_saved_value("patrol_force_count", defender:military_force():unit_list():num_items());
1350 out("Defender patrol found in ["..defender:region():name().."]");
1351
1352 elseif attacker:military_force():has_effect_bundle("hertz_patrol_bundle") then
1353 cm:set_saved_value("patrol_region", attacker:region():name());
1354 cm:set_saved_value("patrol_battle", "attacker");
1355 cm:set_saved_value("patrol_force_count", attacker:military_force():unit_list():num_items());
1356 out("attacker patrol found in ["..attacker:region():name().."]");
1357 else
1358 cm:set_saved_value("patrol_region", attacker:region():name());
1359 cm:set_saved_value("patrol_battle", false);
1360 cm:set_saved_value("patrol_force_count", 0);
1361 for k = 0, secondary_attackers:num_items() - 1 do
1362 if secondary_attackers:item_at(k):military_force():has_effect_bundle("hertz_patrol_bundle") then
1363 cm:set_saved_value("patrol_region", secondary_attackers:region():name());
1364 cm:set_saved_value("patrol_battle", "attacker_reinforcements");
1365 cm:set_saved_value("patrol_force_count", secondary_attackers:item_at(k):military_force():unit_list():num_items());
1366 cm:set_saved_value("patrol_number", k);
1367 out("attacker patrol found in ["..secondary_attackers:region():name().."]");
1368 end;
1369 end;
1370
1371 for m = 0, secondary_defenders:num_items() - 1 do
1372 if secondary_defenders:item_at(m):military_force():has_effect_bundle("hertz_patrol_bundle") then
1373 cm:set_saved_value("patrol_region", secondary_defenders:region():name());
1374 cm:set_saved_value("patrol_battle", "defender_reinforcements");
1375 cm:set_saved_value("patrol_force_count", secondary_defenders:item_at(m):military_force():unit_list():num_items());
1376 cm:set_saved_value("patrol_number", m);
1377 out("Defender patrol found in ["..secondary_defenders:region():name().."]");
1378 end;
1379 end;
1380 end;
1381 end,
1382 true
1383 );
1384 core:add_listener(
1385 mod_name.."BattleCompleted",
1386 "BattleCompleted",
1387 true,
1388 function(context)
1389 if cm:get_saved_value("patrol_battle") ~= false then
1390 out("HERTZ_OUT: BattleCompleted do the things")
1391 local patrol_force = cm:get_saved_value("patrol_battle");
1392 local pb = cm:model():pending_battle();
1393 local attacker = pb:attacker();
1394 local defender = pb:defender();
1395 local secondary_attackers = pb:secondary_attackers();
1396 local secondary_defenders = pb:secondary_defenders();
1397 local target_region = cm:get_region(cm:get_saved_value("patrol_region"))
1398 out("Battle patrol type ["..patrol_force.."]")
1399
1400 if not pb:has_been_fought() and patrol_force == "tomb_kings" then
1401 --out("Tomb Kings patrol ambush")
1402 cm:disable_event_feed_events(true, "wh_event_category_character", "wh_event_subcategory_character_deaths", "");
1403
1404 local province_regions = hertz_get_province_owned_regions(target_region:province_name(), attacker:faction():name(), false)
1405 local tomb_kings_patrol_invasion = invasion_manager:get_invasion("hertz_tomb_invasion_force")
1406
1407 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", attacker:region():name(), 2);
1408
1409 cm:remove_effect_bundle_from_region("hertz_patrol_alive_bundle", attacker:region():name());
1410 cm:remove_effect_bundle_from_region("hertz_tk_patrol_ambush_bundle", attacker:region():name());
1411
1412 tomb_kings_patrol_invasion:set_general_immortal(false);
1413
1414
1415 invasion_manager:kill_invasion_by_key("hertz_tomb_invasion_force")
1416 remove_patrol_bundle(province_regions)
1417 else
1418 local attacker_won = cm:pending_battle_cache_attacker_victory()
1419 out("Battle fought in ["..cm:get_saved_value("patrol_region").."]")
1420
1421 --The new way checks saved_value for the region the patrol was in when battle started.
1422 --The old way checked who was still alive and grabbed that region.
1423 local old_region_check = false
1424
1425 if pb:has_been_fought() then
1426 ---------------------------------------------------------------------------------------------------------
1427 -- Tomb Kings post patrol battle functionality. Will kill the tomb kings patrol weither they won or lost.
1428 -- If lost will also trigger patrol battle dilemma if the other conditions have been met.
1429 ---------------------------------------------------------------------------------------------------------
1430 if patrol_force == "tomb_kings" then
1431 out("Tomb Kings patrol ambush")
1432 cm:disable_event_feed_events(true, "wh_event_category_character", "wh_event_subcategory_character_deaths", "");
1433
1434 local province_regions = hertz_get_province_owned_regions(target_region:province_name(), attacker:faction():name(), false)
1435 local tomb_kings_patrol_invasion = invasion_manager:get_invasion("hertz_tomb_invasion_force")
1436
1437 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", attacker:region():name(), 2);
1438
1439 cm:remove_effect_bundle_from_region("hertz_patrol_alive_bundle", attacker:region():name());
1440 cm:remove_effect_bundle_from_region("hertz_tk_patrol_ambush_bundle", attacker:region():name());
1441
1442 tomb_kings_patrol_invasion:set_general_immortal(false);
1443
1444 if not attacker_won then
1445 cm:remove_effect_bundle_from_region("hertz_patrol_replenish_bundle", attacker:region():name());
1446 hertz_patrol_death(defender, attacker, target_region);
1447 end
1448 invasion_manager:kill_invasion_by_key("hertz_tomb_invasion_force")
1449 remove_patrol_bundle(province_regions)
1450
1451 elseif patrol_force == "attacker" then
1452 -------------------------------------------------------------------
1453 -- Check if the patrol force won the battle if it was the attacker.
1454 -------------------------------------------------------------------
1455 if attacker:is_null_interface() and old_region_check then
1456 target_region = defender:region();
1457 elseif old_region_check then
1458 target_region = attacker:region();
1459 end
1460 if not attacker_won then
1461 -- If the patrol is defeated run the patrol death functionality
1462 hertz_patrol_death(defender, attacker, target_region);
1463 else
1464 cm:disable_movement_for_character(cm:char_lookup_str(attacker:command_queue_index()))
1465 cm:zero_action_points(cm:char_lookup_str(attacker:command_queue_index()));
1466 end
1467
1468 elseif patrol_force == "defender" then
1469 -------------------------------------------------------------------
1470 -- Check if the patrol force won the battle if it was the defender.
1471 -------------------------------------------------------------------
1472 if defender:is_null_interface() and old_region_check then
1473 target_region = attacker:region();
1474 elseif old_region_check then
1475 target_region = defender:region();
1476 end
1477 if attacker_won then
1478 -- If the patrol is defeated run the patrol death functionality
1479 hertz_patrol_death(attacker, defender, target_region);
1480 else
1481 cm:disable_movement_for_character(cm:char_lookup_str(defender:command_queue_index()))
1482 cm:zero_action_points(cm:char_lookup_str(defender:command_queue_index()));
1483 end
1484
1485 elseif patrol_force == "attacker_reinforcements" then
1486 -----------------------------------------------------------------------------------
1487 -- Check if the patrol force won the battle if it was the attackers reinforcements.
1488 -----------------------------------------------------------------------------------
1489 local target_patrol = secondary_attackers:item_at(cm:get_saved_value("patrol_number"))
1490 if target_patrol:is_null_interface() and old_region_check then
1491 target_region = defender:region();
1492 elseif old_region_check then
1493 target_region = attacker:region();
1494 end
1495 if not attacker_won then
1496 -- If the patrol is defeated run the patrol death functionality
1497 hertz_patrol_death(defender, target_patrol, target_region);
1498 else
1499 cm:disable_movement_for_character(cm:char_lookup_str(target_patrol:command_queue_index()))
1500 cm:zero_action_points(cm:char_lookup_str(target_patrol:command_queue_index()));
1501 end
1502
1503 elseif patrol_force == "defender_reinforcements" then
1504 -----------------------------------------------------------------------------------
1505 -- Check if the patrol force won the battle if it was the defenders reinforcements.
1506 -----------------------------------------------------------------------------------
1507 local target_patrol = secondary_defenders:item_at(cm:get_saved_value("patrol_number"))
1508 if target_patrol:is_null_interface() and old_region_check then
1509 target_region = attacker:region();
1510 elseif old_region_check then
1511 target_region = defender:region();
1512 end
1513 if attacker_won then
1514 -- If the patrol is defeated run the patrol death functionality
1515 hertz_patrol_death(attacker, target_patrol, target_region);
1516 else
1517 cm:disable_movement_for_character(cm:char_lookup_str(target_patrol:command_queue_index()))
1518 cm:zero_action_points(cm:char_lookup_str(target_patrol:command_queue_index()));
1519 end
1520 end;
1521 end;
1522 end;
1523 -- Reset the value which checks if current battle has a patrol force in it
1524 cm:set_saved_value("patrol_battle", false);
1525 -- Disable character death sidebar events
1526 cm:disable_event_feed_events(false, "wh_event_category_character", "wh_event_subcategory_character_deaths", "");
1527 end;
1528 end,
1529 true
1530 );
1531end;
1532
1533------------------------------------------------------------------------------------------------------------
1534-- This function runs every start of the turn for a faction.
1535-- It checks the factions owned regions and provinces and spawns and orders patrols when conditions are met.
1536------------------------------------------------------------------------------------------------------------
1537function patrol_and_province_commands(faction_script_interface)
1538 if order_retry_count <= order_retry_cap then
1539 order_retry_count = order_retry_count + 1;
1540
1541 local target_faction = faction_script_interface;
1542 local region_list = target_faction:region_list();
1543 local checking_province = "";
1544 local checked_provinces = {};
1545 local patrol_not_moved = true;
1546 faction_patrol_list[target_faction:name()].patrols = {};
1547 out("Checking " ..target_faction:name())
1548
1549 -- When checking Nekai, check its vassals provinces for patrol availability
1550 if target_faction:name() == "wh2_dlc13_lzd_spirits_of_the_jungle" then
1551 target_faction = cm:get_faction("wh2_dlc13_lzd_defenders_of_the_great_plan");
1552 region_list = target_faction:region_list();
1553 end;
1554
1555 local faction_region_count = region_list:num_items()
1556 out("region count is ["..faction_region_count.."]")
1557
1558 for i = 0, region_list:num_items() - 1 do
1559 --Switch back to the vassal faction for Nekai to continue checking their regions
1560 if target_faction:name() == "wh2_dlc13_lzd_spirits_of_the_jungle" then
1561 target_faction = cm:get_faction("wh2_dlc13_lzd_defenders_of_the_great_plan");
1562 end
1563 local target_region = region_list:item_at(i);
1564 local province_regions = nil;
1565
1566 -- Check for each province once to make sure not too many checks are spammed.
1567 if target_region:province_name() ~= checking_province and not table_contains(province_exlusion[campaign_model], target_region:province_name()) then
1568 checking_province = target_region:province_name();
1569
1570 -- Goes through the target factions region lists and returns all fully owned provinces.
1571 province_regions = hertz_get_province_owned_regions(checking_province, target_faction:name(), false)
1572
1573 -- Swap the target faction to Nekai to make sure that any patrols that are checked or spawned belong to Nekai.
1574 if target_faction:name() == "wh2_dlc13_lzd_defenders_of_the_great_plan" then
1575 target_faction = cm:get_faction("wh2_dlc13_lzd_spirits_of_the_jungle");
1576 end;
1577
1578 -- Gets the province patrol of the current target province if it exists. Otherwise returns "false"
1579 local target_patrol = get_province_patrol(checking_province, target_faction)
1580 local faction_table = faction_patrol_list[target_faction:name()]
1581
1582 -- Checks if the faction owns the entire province, and if the entire province is bigger than 1 region.
1583 if province_regions ~= false and not table_contains(checked_provinces, checking_province) and table.getn(province_regions) > 1 then
1584 out("amount of regions in province: " ..table.getn(province_regions))
1585 table.insert(checked_provinces, checking_province);
1586
1587 -- Start the patrol functionality for the Tomb Kings.
1588 if target_faction:subculture() == "wh2_dlc09_sc_tmb_tomb_kings" then
1589 tomb_kings_province_patrol(province_regions, target_faction);
1590
1591 -- Start the patrol functionality for the other factions.
1592 elseif target_faction:subculture() ~= "wh2_dlc09_sc_tmb_tomb_kings" then
1593 -- Check if a patrol is found in the checking province.
1594 if target_patrol ~= false then
1595 -- Reset their force_type to default PATROL_ARMY if not set.
1596 if target_patrol:military_force():force_type():key() ~= "PATROL_ARMY" then
1597 cm:convert_force_to_type(target_patrol:military_force(), "PATROL_ARMY");
1598 end;
1599
1600 -- Order the patrol when it is found.
1601 out("Moving patrol")
1602 if patrol_not_moved then
1603 hertz_order_patrol(province_regions, target_faction, target_patrol, faction_region_count)
1604 end;
1605
1606 -- Target faction owns an entire province but a patrol is not currently alive.
1607 elseif not target_patrol then
1608 local patrol_alive_bundle = false
1609 local patrol_replenish_bundle = false
1610 local patrol_dead_bundle = false
1611
1612 -- Check the entire region for the patrol cooldown effect bundle
1613 for j = 1, #province_regions do
1614
1615 if province_regions[j]:has_effect_bundle("hertz_patrol_alive_bundle") then
1616 hertz_patrol_alive_bundle = true;
1617 cm:remove_effect_bundle_from_region("hertz_patrol_alive_bundle", province_regions[j]:name())
1618 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", province_regions[j]:name(), 5);
1619 end;
1620
1621 if province_regions[j]:has_effect_bundle("hertz_patrol_replenish_bundle") then
1622 patrol_replenish_bundle = true;
1623 end;
1624
1625 if province_regions[j]:has_effect_bundle("hertz_patrol_died_bundle") then
1626 patrol_dead_bundle = true;
1627 end;
1628 end;
1629 -- If the patrol is not on cooldown, spawn the patrol.
1630 if not patrol_alive_bundle and not patrol_replenish_bundle and not patrol_dead_bundle and target_faction:holds_entire_province(checking_province, true) and table.getn(province_regions) > 1 then
1631 hertz_create_new_patrol(province_regions, target_faction);
1632 end;
1633 end;
1634 end;
1635 end;
1636 end;
1637 end;
1638
1639 -----------------------------------------------------------------------------------------------------------------
1640 -- Patrol Order retry Logic
1641 -- Some bugs or features which slow the game down will make the script skip all patrol orders.
1642 -- This piece of code checks the players patrols and retries giving them orders if they have not moved this turn.
1643 -----------------------------------------------------------------------------------------------------------------
1644 if target_faction:is_human() then
1645 --Checks and gives rewards for any patrol dilemma's from the previous turn.
1646 check_dilemma_rewards(target_faction);
1647 patrol_not_moved = false;
1648
1649 -- 3 second callback timer that checks the players patrol positions.
1650 -- If the patrols have not moved, restart this entire function.
1651 -- This callback will run every 3 seconds up to 15 seconds untill patrols have moved.
1652 cm:callback(
1653 function()
1654 for k = 1, #faction_patrol_list[target_faction:name()].patrols do
1655 if not table_contains(patrol_saved_information.removed_cqis, faction_patrol_list[target_faction:name()].patrols[k]:command_queue_index()) then
1656 local checking_patrol = faction_patrol_list[target_faction:name()].patrols[k];
1657 local saved_pos = patrol_saved_information[checking_patrol:command_queue_index()];
1658 --out("Patrol in ["..faction_patrol_list[target_faction:name()].patrols[k]:region():name().."] not moved. ["..faction_patrol_list[target_faction:name()].patrols[k]:action_points_remaining_percent().."] action pints remaining");
1659 if checking_patrol:logical_position_x() == saved_pos.pos_x and checking_patrol:logical_position_y() == saved_pos.pos_y then
1660 patrol_not_moved = true;
1661 hertz_patrols_ordered = false;
1662 end;
1663 end
1664 end;
1665
1666 if patrol_not_moved then
1667 patrol_and_province_commands(target_faction);
1668 else
1669 order_retry_count = order_retry_cap;
1670 hertz_patrols_ordered = true;
1671 cm:disable_event_feed_events(false, "wh_event_category_character", "wh_event_subcategory_character_deaths", "");
1672 end;
1673 if target_faction ~= "wh2_dlc13_lzd_spirits_of_the_jungle" then
1674 clean_lost_patrols(target_faction);
1675 end;
1676 end,
1677 3
1678 )
1679 else
1680 -- AI Patrols have a tendency to end up in other factions territory.
1681 -- It is very hard to efficiently track which province the patrol belongs to without filling the internal registry with information
1682 -- So when a wrong position has been given to the patrol it will delete the AI patrol and respawn him in the next turn
1683 -- The players patrols have a different functionality when they end up in other peoples territory
1684 clean_lost_patrols(target_faction);
1685 end;
1686 end;
1687end;
1688--------------------------------------------------
1689-- The creation and spawning logic for new patrols
1690--------------------------------------------------
1691function hertz_create_new_patrol(province_regions)
1692 local province_capital = nil;
1693 local existing_patrol = get_province_patrol(province_regions[1]:province_name(), province_regions[1]:owning_faction());
1694 local force_created = false;
1695 local target_exclusion_region = false;
1696
1697 -- Check if no other patrols exist in the current province.
1698 if existing_patrol == false then
1699 out("Checking for excluded capitals.")
1700 for i = 1, #region_exclusion[campaign_model] do
1701 if province_regions[1]:province_name() == cm:get_region(region_exclusion[campaign_model][i]):province_name() and cm:get_region(region_exclusion[campaign_model][i]):is_province_capital() then
1702 target_exclusion_region = cm:get_region(region_exclusion[campaign_model][i])
1703 end;
1704 end;
1705
1706 -- Look for the province Capital
1707 for i = 1, #province_regions do
1708 local target_region = province_regions[i];
1709 if target_region:is_province_capital() or target_exclusion_region ~= false then
1710 if target_exclusion_region ~= false then
1711 out("exclusion capital region found ["..target_exclusion_region:name().."]");
1712 target_region = target_exclusion_region;
1713 end;
1714
1715 local region_key = target_region:name();
1716 local faction_key = target_region:owning_faction():name();
1717 local settlement_script_interface = target_region:settlement();
1718 local target_subculture = settlement_script_interface:faction():subculture();
1719 local settlement_level = 0;
1720
1721 -- Get the current province Capital settlement rank
1722 if target_exclusion_region ~= false then
1723 -- If the Capital is in an excluded region, it needs to grab that settlement rank but spawn the force in a different region
1724 settlement_level = tonumber(settlement_script_interface:primary_slot():building():building_level());
1725 target_exclusion_region = false;
1726 else
1727 settlement_level = hertz_get_settlement_level(province_regions, true);
1728 end;
1729
1730 out("Settlement level = " ..settlement_level);
1731
1732 -- Swap target faction to Nekai's force when checking his vassals territory
1733 -- This is done to make sure the patrol force spawns under Nekai's banner.
1734 if faction_key == "wh2_dlc13_lzd_defenders_of_the_great_plan" then
1735 faction_key = "wh2_dlc13_lzd_spirits_of_the_jungle"
1736 end;
1737
1738 -- Only spawn patrols in settlements higher than rank 1.
1739 if settlement_level > 1 or target_subculture == "wh_dlc05_sc_wef_wood_elves" or faction_key == "wh2_dlc13_lzd_spirits_of_the_jungle" then
1740 -- Generate the patrols unit list
1741 generated_patrol_force = hertz_generate_patrol_unit_list(target_subculture, faction_key, settlement_level)
1742
1743 if generated_patrol_force ~= false then
1744
1745 -- Set the variables and numbers needed for force generation
1746 local ram_force_units = ram_unit_lists[target_subculture];
1747
1748 -- Check if the subculture has special patrol setups.
1749 if ram_unit_lists[target_subculture].faction_custom_patrols.enabled == true then
1750 if table_contains(ram_unit_lists[target_subculture].faction_custom_patrols.factions, faction_key) then
1751 -- Full custom unit list per predetermined factions
1752 if ram_unit_lists[target_subculture].faction_custom_patrols.type == "full" then
1753 ram_force_units = ram_unit_lists[target_subculture].faction_custom_patrols[faction_key];
1754
1755 -- Custom commander per predetermined faction
1756 elseif ram_unit_lists[target_subculture].faction_custom_patrols.type == "com" then
1757 ram_force_units.com = ram_unit_lists[target_subculture].faction_custom_patrols[faction_key].com
1758 end;
1759 end;
1760 end;
1761
1762 -- Get a valid spawning location from the target region inside the owned province.
1763 local pos_x, pos_y = cm:find_valid_spawn_location_for_character_from_settlement(faction_key, region_key, false, true, 5);
1764
1765 --Spawn the patrol
1766 cm:create_force_with_general(
1767 faction_key,
1768 generated_patrol_force,
1769 region_key,
1770 pos_x,
1771 pos_y,
1772 "general",
1773 ram_force_units.com,
1774 ram_force_units.name,
1775 "",
1776 "",
1777 "",
1778 false,
1779 function(cqi)
1780 local target_char = cm:get_character_by_cqi(cqi);
1781 --out(cqi)
1782 -- Safe-net for if loyalty still shows up for a province patrol.
1783 -- Keeps it neutral so that no dilemmas or events pop up for the patrol.
1784 if target_char:loyalty() < 5 then
1785 cm:modify_character_personal_loyalty_factor(cm:char_lookup_str(target_char:command_queue_index()), 5 - target_char:loyalty());
1786
1787 elseif target_char:loyalty() > 5 then
1788 cm:modify_character_personal_loyalty_factor(cm:char_lookup_str(target_char:command_queue_index()), 5 - target_char:loyalty());
1789 end
1790 force_created = true;
1791 cm:convert_force_to_type(target_char:military_force(), "PATROL_ARMY");
1792
1793 -- Set custom Vampire Counts province patrol alive bundle according to settlement rank.
1794 if target_subculture == "wh_main_sc_vmp_vampire_counts" then
1795 out("applying vampire bundle [hertz_vamp_patrol_"..tostring(settlement_level).."] with the effect value of ["..tostring((settlement_level * 2) - 2).."]")
1796 local vamp_patrol_bundle = cm:create_new_custom_effect_bundle("hertz_patrol_bundle");
1797 vamp_patrol_bundle:set_duration(-1);
1798 vamp_patrol_bundle:add_effect("hertz_vamp_patrol_"..settlement_level, "force_to_province_own", (settlement_level * 2) - 2);
1799 cm:apply_custom_effect_bundle_to_force(vamp_patrol_bundle, target_char:military_force());
1800
1801 -- Set the default province patrol alive bundle.
1802 else
1803 cm:apply_effect_bundle_to_characters_force("hertz_patrol_bundle", cqi, -1, true);
1804 end;
1805 cm:apply_effect_bundle_to_region("hertz_patrol_alive_bundle", province_regions[i]:name(), -1);
1806 cm:cai_disable_movement_for_character(cm:char_lookup_str(cqi));
1807 cm:zero_action_points(cm:char_lookup_str(cqi));
1808
1809 -- Apply special upkeep bundle for Nekai's patrols.
1810 if faction_key == "wh2_dlc13_lzd_spirits_of_the_jungle" then
1811 cm:apply_effect_bundle_to_characters_force("wh2_dlc16_bundle_military_upkeep_free_force_immune_to_regionless_attrition", cqi, -1, true);
1812 end;
1813 end
1814 );
1815 end;
1816 end;
1817 end;
1818 end;
1819 end;
1820end;
1821
1822-------------------------------------------
1823-- Generate the unit lists for new patrols.
1824-------------------------------------------
1825function hertz_generate_patrol_unit_list(target_subculture, faction_key, settlement_level)
1826 local ram_force_numbers = {};
1827
1828 -- When generating Bretonnia patrols, use their custom unit type number list.
1829 if target_subculture == "wh_main_sc_brt_bretonnia" then
1830 ram_force_numbers = brettonia_patrol_settings[settlement_level];
1831
1832 -- When generating Bretonnia patrols, use their custom unit type number list.
1833 elseif target_subculture == "wh_dlc05_sc_wef_wood_elves" then
1834 ram_force_numbers = ram_unit_lists["wef_patrols"];
1835
1836 -- When generating Vampire Counts patrols, their own custom spawning which helped build this one
1837 elseif target_subculture == "wh_main_sc_vmp_vampire_counts" then
1838 return generate_vamp_unit_lists(target_subculture, faction_key, settlement_level)
1839
1840 -- When generating Bretonnia patrols, use their custom unit type number list.
1841 elseif faction_key == "wh2_dlc13_lzd_spirits_of_the_jungle" then
1842 ram_force_numbers = ram_unit_lists["nekai_patrols"];
1843
1844 -- Default patrol force unit type numbers.
1845 else
1846 ram_force_numbers = ram_unit_lists[settlement_level];
1847 end;
1848
1849 -- Get the patrol unit list per subculture.
1850 local ram_force_units = ram_unit_lists[target_subculture];
1851
1852 -- Check if the subculture has special patrol setups.
1853 if ram_unit_lists[target_subculture].faction_custom_patrols.enabled == true then
1854 if table_contains(ram_unit_lists[target_subculture].faction_custom_patrols.factions, faction_key) then
1855
1856 -- Full custom unit list per predetermined factions
1857 if ram_unit_lists[target_subculture].faction_custom_patrols.type == "full" then
1858 ram_force_units = ram_unit_lists[target_subculture].faction_custom_patrols[faction_key];
1859
1860 -- Custom commander per predetermined faction
1861 elseif ram_unit_lists[target_subculture].faction_custom_patrols.type == "com" then
1862 ram_force_units.com = ram_unit_lists[target_subculture].faction_custom_patrols[faction_key].com
1863 end;
1864 end;
1865 end;
1866
1867 -- Generate the full force list
1868 random_army_manager:new_force("new_patrol_force"..faction_key)
1869 for i = 1, ram_force_numbers.inf_mel do
1870 -- Generate melee infantry
1871 random_number = cm:random_number(table.getn(ram_force_units.inf_mel), 1)
1872 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.inf_mel[random_number], 1)
1873 end
1874 for i = 1, ram_force_numbers.inf_mis do
1875 -- Generate missile infantry
1876 random_number = cm:random_number(table.getn(ram_force_units.inf_mis), 1)
1877 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.inf_mis[random_number], 1)
1878 end
1879 for i = 1, ram_force_numbers.large do
1880 -- Generate large units
1881 random_number = cm:random_number(table.getn(ram_force_units.large), 1)
1882 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.large[random_number], 1)
1883 end
1884 for i = 1, ram_force_numbers.random do
1885 -- Generate random other/specialized units
1886 random_number = cm:random_number(table.getn(ram_force_units.random), 1)
1887 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.random[random_number], 1)
1888 end
1889
1890 -- Create and return full unit list.
1891 return random_army_manager:generate_force("new_patrol_force"..faction_key, ram_force_numbers.force_size, false);
1892end;
1893
1894function generate_vamp_unit_lists(target_subculture, faction_key, settlement_level)
1895 local ram_force_numbers = {};
1896
1897 ram_force_units = vamp_unit_lists[settlement_level];
1898
1899 local ram_force_numbers = vamp_unit_lists["force_size"];
1900
1901
1902 random_army_manager:new_force("new_patrol_force"..faction_key)
1903 for i = 1, ram_force_numbers.inf_mel do
1904 random_number = cm:random_number(table.getn(ram_force_units.inf_mel), 1)
1905 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.inf_mel[random_number], 1)
1906 end
1907 for i = 1, ram_force_numbers.inf_mis do
1908 random_number = cm:random_number(table.getn(ram_force_units.inf_mis), 1)
1909 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.inf_mis[random_number], 1)
1910 end
1911 for i = 1, ram_force_numbers.large do
1912 random_number = cm:random_number(table.getn(ram_force_units.large), 1)
1913 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.large[random_number], 1)
1914 end
1915 for i = 1, ram_force_numbers.random do
1916 random_number = cm:random_number(table.getn(ram_force_units.random), 1)
1917 random_army_manager:add_mandatory_unit("new_patrol_force"..faction_key, ram_force_units.random[random_number], 1)
1918 end
1919
1920 return random_army_manager:generate_force("new_patrol_force"..faction_key, ram_force_numbers.force_size, false);
1921end;
1922
1923---------------------------------------------------------------------------------------
1924-- Function that takes a string and splits it to a table according to input seperators.
1925-- Professionally copy-pasted from StackOverflow
1926---------------------------------------------------------------------------------------
1927function hertz_split_str(inputstr, sep)
1928 local t = {}
1929 if inputstr == nil or inputstr == "" then
1930 out("ERROR: input string is empty")
1931 else
1932 if sep == nil then
1933 sep = "%s"
1934 end;
1935
1936 for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
1937 table.insert(t, str)
1938 end;
1939
1940 return t
1941 end;
1942end;
1943------------------------------------
1944-- Tomb Kings Province Patrol logic.
1945------------------------------------
1946function tomb_kings_province_patrol(province_regions, faction_script_interface)
1947 local checked_provinces = cm:get_saved_value("tk_checked_provinces")
1948 -- Check if any province are available for province patrol checks.
1949 if checked_provinces ~= nil and checked_provinces ~= "" and checked_provinces ~= false then
1950 checked_provinces = hertz_split_str(checked_provinces, "%s")
1951 out(table.concat(checked_provinces, " "))
1952 else
1953 checked_provinces = {};
1954 end
1955 out("Tomb kings province patrol called")
1956
1957 if faction_script_interface:holds_entire_province(province_regions[1]:province_name(), true) and not table_contains(checked_provinces, province_regions[1]:province_name()) then
1958 out("Suitable province found");
1959 local ambush_effect_bundle = nil;
1960 local turn_counter = 21;
1961 local ambush_chance = 0;
1962 table.insert(checked_provinces, province_regions[1]:province_name());
1963 local settlement_level = hertz_get_settlement_level(province_regions, true)
1964 local patrol_bundle = false;
1965 local tk_enemy_ambush_bundle = cm:create_new_custom_effect_bundle("hertz_tk_patrol_ambush_bundle")
1966
1967 -- Checks if the province capital is above rank 1
1968 if settlement_level > 1 then
1969 for i = 1, #province_regions do
1970 -- Look for any province patrol ambush effects on the province regions.
1971 if province_regions[i]:has_effect_bundle("hertz_tk_patrol_ambush_bundle") then
1972 ambush_effect_bundle = hertz_get_effect_bundle_from_region(province_regions[i]:name(), "hertz_tk_patrol_ambush_bundle")
1973 turn_counter = ambush_effect_bundle:duration()
1974 -- Remove the current existing effect after copying its information
1975 cm:remove_effect_bundle_from_region("hertz_tk_patrol_ambush_bundle", province_regions[i]:name());
1976 end;
1977 -- Check if any patrols are replenishing or alive.
1978 if province_regions[i]:has_effect_bundle("hertz_patrol_alive_bundle") then
1979 patrol_bundle = "hertz_patrol_alive_bundle";
1980
1981 elseif province_regions[i]:has_effect_bundle("hertz_patrol_replenish_bundle") then
1982 patrol_bundle = "hertz_patrol_replenish_bundle";
1983
1984 elseif province_regions[i]:has_effect_bundle("hertz_patrol_died_bundle") then
1985 patrol_bundle = "hertz_patrol_died_bundle";
1986 end;
1987 end;
1988
1989 -- If a new patrol is created, apply the patrol alive bundle to the region.
1990 if not patrol_bundle then
1991 cm:apply_effect_bundle_to_region("hertz_patrol_alive_bundle", province_regions[1]:name(), -1);
1992 end;
1993
1994 -- Check for any enemies inside any of the tomb kings regions
1995 local enemy_targets = get_target_in_tomb_kings_region(faction_script_interface, province_regions)
1996
1997 -- If a target is found, calculate ambush chance
1998 if enemy_targets ~= false then
1999 -- Calulate ambush chances according to target force stance
2000 ambush_chance = calculate_tk_ambush_chance(turn_counter, enemy_targets);
2001
2002 -- Calculate RNG
2003 local rng = cm:random_number(100, 1);
2004
2005 -- Trigger tomb kings thread event when ambush chance is > 50%
2006 -- Not sure if this is actually functional though
2007 if ambush_chance > 50 and enemy_targets[1]:faction() == player_faction then
2008 cm:trigger_incident(enemy_targets[1]:faction():name(), "hertz_tomb_kings_patrol_threat");
2009 end;
2010
2011 -- Create a custom effect bundle for the Tomb Kings Patrol ambush.
2012 tk_enemy_ambush_bundle:add_effect("hertz_tomb_kings_patrol_chance_effect", "region_to_region_own", ambush_chance);
2013 tk_enemy_ambush_bundle:add_effect("wh_main_effect_force_army_campaign_movement_range", "region_to_force_enemy", (ambush_chance / 2) * -1);
2014 tk_enemy_ambush_bundle:set_duration(turn_counter);
2015 cm:apply_custom_effect_bundle_to_region(tk_enemy_ambush_bundle, province_regions[1]);
2016 out("Spawning Tomb Kings Patrol");
2017
2018 -- If ambush chance is succefull trigger an ambush battle
2019 if rng < ambush_chance and patrol_bundle == "hertz_patrol_alive_bundle" then
2020 tomb_kings_patrol_emerge(enemy_targets[1]:command_queue_index(), faction_script_interface, settlement_level);
2021 end;
2022 -- if no target has been found, reset the ambush bundle.
2023 else
2024 for j = 1, #province_regions do
2025 if province_regions[j]:has_effect_bundle("hertz_tk_patrol_ambush_bundle") then
2026 cm:remove_effect_bundle_from_region("hertz_tk_patrol_ambush_bundle", province_regions[i]:name())
2027 out("No target found")
2028 end;
2029 end;
2030 end;
2031 -- Reset checked province variable for the next tomb kings faction.
2032 cm:set_saved_value("tk_checked_provinces", table.concat(checked_provinces, " "));
2033 end;
2034 end;
2035end;
2036
2037-- The base effect bundle that is used to calculate the probabilty that calls the tomb_kings_patrol_emerge function is on a 20 turn timer
2038-- Each turn adds 5% chance tomb_kings_patrol_emerge is called
2039-- Different stances wil subtract more turns than default
2040-- This is done so that I don't have to safe the ambush chance for every freaking region for every TK faction using cm:set_saved_value()
2041function calculate_tk_ambush_chance(turn_counter, enemy_targets)
2042 local base_chance = 5;
2043 local turns_spend = 21 - turn_counter
2044
2045 local mil_force_stance_turn_counter_increase = {
2046 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_AMBUSH"] = 4,
2047 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_ASTROMANCY"] = 4,
2048 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_CHANNELING"] = 4,
2049 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_DEFAULT"] = 4,
2050 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_DOUBLE_TIME"] = 5,
2051 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_LAND_RAID"] = 8,
2052 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_MARCH"] = 6,
2053 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_MUSTER"] = 4,
2054 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_PATROL"] = 4,
2055 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_RAISE_DEAD"] = 4,
2056 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_SEA_RAID"] = 4,
2057 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_SETTLE"] = 4,
2058 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_SET_CAMP"] = 8,
2059 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_SET_CAMP_RAIDING"] = 10,
2060 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_STALKING"] = 0,
2061 ["MILITARY_FORCE_ACTIVE_STANCE_TYPE_TUNNELING"] = -1,
2062 ["MILITARY_FORCE_SITUATIONAL_STANCE_BLOCKADE"] = 0,
2063 ["MILITARY_FORCE_SITUATIONAL_STANCE_DOCK"] = 0,
2064 ["MILITARY_FORCE_SITUATIONAL_STANCE_GARRISON"] = 0,
2065 ["MILITARY_FORCE_SITUATIONAL_STANCE_LAY_SIEGE"] = 0;
2066 };
2067 if is_table(enemy_targets) then
2068 for i = 1, table.getn(enemy_targets) do
2069 local mil_force_stance = enemy_targets[i]:military_force():active_stance()
2070 turns_spend = turns_spend + mil_force_stance_turn_counter_increase[mil_force_stance];
2071 end;
2072 else
2073 script_error("calculate_tk_ambush_chance() called but supplied [enemy_targets] is not a table")
2074 end;
2075 ambush_chance = base_chance * turns_spend
2076 if ambush_chance < 0 then
2077 ambush_chance = 0;
2078 end;
2079
2080 return ambush_chance
2081end;
2082
2083--[[
2084mortal empires attack testing console inputs.
2085tomb_kings_patrol_emerge(842, "wh2_dlc09_tmb_khemri", 4)
2086tomb_kings_patrol_emerge(231, "wh2_dlc09_tmb_exiles_of_nehek", 5)
2087tomb_kings_patrol_emerge(90, "wh2_dlc09_tmb_followers_of_nagash", 5)
2088tomb_kings_patrol_emerge(30, "wh2_dlc09_tmb_lybaras", 5)
2089]]
2090
2091----------------------------------------------------------
2092-- patrol creation and ambush function for the Tomb Kings.
2093----------------------------------------------------------
2094function tomb_kings_patrol_emerge(character_cqi, faction_script_interface, settlement_level)
2095 local character = cm:get_character_by_cqi(character_cqi)
2096 local tomb_kings_force_list = nil;
2097 local tomb_kings_faction = nil
2098
2099 -- Get the correcnt faction script interface from input
2100 if not is_faction(faction_script_interface) then
2101 out("is a string")
2102 tomb_kings_faction = cm:get_faction(faction_script_interface);
2103 else
2104 out("is not a string")
2105 tomb_kings_faction = faction_script_interface;
2106 end;
2107 out("Target faction is []")
2108 --local tomb_kings_faction = cm:get_faction(faction_script_interface);
2109
2110 -- Find a valid spawning position to ambush the target
2111 local x, y = cm:find_valid_spawn_location_for_character_from_character(character:faction():name(), cm:char_lookup_str(character_cqi), true, 1);
2112
2113 if tomb_kings_force_list ~= nil then
2114 random_army_manager:remove_force("hertz_tomb_invasion_force");
2115 end;
2116 random_army_manager:new_force("hertz_tomb_invasion_force")
2117
2118 -- Get patrol force numbers
2119 local ram_force_numbers = ram_unit_lists[settlement_level];
2120
2121 -- Start with the default Tomb Kings patrols.
2122 local default_unit_pool = tomb_king_patrol_pool["default"];
2123
2124 local faction_specifc_pool = tomb_king_patrol_pool["default"].random
2125 local patrol_unit_caps = tomb_king_patrol_pool.tomb_kings_patrol_unit_cap
2126
2127 -- Replace patrol units when the tomb kings faction is playable.
2128 if table_contains(tomb_king_patrol_pool["playable_factions"], tomb_kings_faction:name()) then
2129 faction_specifc_pool = tomb_king_patrol_pool[tomb_kings_faction:name()].unit_list;
2130 end
2131
2132 -- Generate the unit list
2133 for j = 1, ram_force_numbers.inf_mel do
2134 random_number = cm:random_number(table.getn(default_unit_pool.inf_mel), 1)
2135 random_army_manager:add_mandatory_unit("hertz_tomb_invasion_force", default_unit_pool.inf_mel[random_number], 1)
2136 end
2137
2138 for k = 1, ram_force_numbers.inf_mis do
2139 random_number = cm:random_number(table.getn(default_unit_pool.inf_mis), 1)
2140 random_army_manager:add_mandatory_unit("hertz_tomb_invasion_force", default_unit_pool.inf_mis[random_number], 1)
2141 end
2142
2143 for l = 1, ram_force_numbers.large do
2144 random_number = cm:random_number(table.getn(default_unit_pool.large), 1)
2145 random_army_manager:add_mandatory_unit("hertz_tomb_invasion_force", default_unit_pool.large[random_number], 1)
2146 end
2147
2148 for m = 1, ram_force_numbers.random do
2149 random_number = cm:random_number(table.getn(faction_specifc_pool), 1)
2150
2151 -- Some special/strong units in the random unit list are capped to make sure not too many special units spawn, making the patrol incredibly strong.
2152 if table_contains(patrol_unit_caps.target_unit_list, faction_specifc_pool[random_number]) then
2153
2154 if patrol_unit_caps[faction_specifc_pool[random_number]].army_count < patrol_unit_caps[faction_specifc_pool[random_number]].cap then
2155 patrol_unit_caps[faction_specifc_pool[random_number]].army_count = patrol_unit_caps[faction_specifc_pool[random_number]].army_count + 1;
2156 random_army_manager:add_mandatory_unit("hertz_tomb_invasion_force", faction_specifc_pool[random_number], 1)
2157
2158 else
2159 random_number = cm:random_number(table.getn(tomb_king_patrol_pool["default"].non_elite), 1)
2160 random_army_manager:add_mandatory_unit("hertz_tomb_invasion_force", tomb_king_patrol_pool["default"].non_elite[random_number], 1)
2161 end;
2162
2163 else
2164 random_army_manager:add_mandatory_unit("hertz_tomb_invasion_force", faction_specifc_pool[random_number], 1)
2165 end;
2166 end;
2167
2168 tomb_kings_force_list = random_army_manager:generate_force("hertz_tomb_invasion_force", ram_force_numbers.force_size, false);
2169
2170 -- Create the Tomb Kings ptrol invasion to spawn an army and ambush the target.
2171 local tomb_kings_patrol_invasion = invasion_manager:new_invasion("hertz_tomb_invasion_force", tomb_kings_faction:name(), tomb_kings_force_list, {x, y})
2172
2173 tomb_kings_patrol_invasion:set_target("CHARACTER", character_cqi, character:faction():name());
2174 tomb_kings_patrol_invasion:set_general_immortal(false);
2175 tomb_kings_patrol_invasion:apply_effect("wh_main_bundle_military_upkeep_free_force", -1);
2176 tomb_kings_patrol_invasion:apply_effect("hertz_patrol_bundle", -1);
2177 if table_contains(tomb_king_patrol_pool["playable_factions"], tomb_kings_faction:name()) then
2178 tomb_kings_patrol_invasion:apply_effect(tomb_king_patrol_pool[tomb_kings_faction:name()].skill_effect, -1);
2179 end;
2180 tomb_kings_patrol_invasion:create_general(
2181 false,
2182 tomb_king_patrol_pool["default"].com,
2183 tomb_king_patrol_pool["default"].name,
2184 "",
2185 "",
2186 ""
2187 );
2188
2189 tomb_kings_patrol_invasion:start_invasion(
2190 function(self)
2191 -- Reset the unit caps for the next force.
2192 tomb_kings_reset_unit_caps()
2193 local force_leader = self:get_general();
2194 local mil_force = self:get_force();
2195 local character_list = mil_force:character_list();
2196
2197 -- Set saved battle onformation for post battle cleanup.
2198 cm:set_saved_value("patrol_region", force_leader:region():name());
2199 cm:set_saved_value("patrol_battle", "tomb_kings");
2200 cm:set_saved_value("patrol_force_count", mil_force:unit_list():num_items());
2201
2202 -- Scroll the camera to the fight's location
2203 if faction_script_interface == player_faction then
2204 cm:scroll_camera_from_current(false, 6, {force_leader:display_position_x(), force_leader:display_position_y(), 14.768, 0.0, 12.0});
2205 end;
2206
2207 cm:force_declare_war(tomb_kings_faction:name(), character:faction():name(), false, false, false);
2208
2209 -- If the target force is in military encampent stance, force an interception battle
2210 -- otherwise trigger an ambush.
2211 if character:military_force():active_stance() == "MILITARY_FORCE_ACTIVE_STANCE_TYPE_SET_CAMP" or character:military_force():active_stance() == "MILITARY_FORCE_ACTIVE_STANCE_TYPE_SET_CAMP_RAIDING" then
2212 cm:force_attack_of_opportunity(force_leader:military_force():command_queue_index(), character:military_force():command_queue_index(), false);
2213 else
2214 cm:force_attack_of_opportunity(force_leader:military_force():command_queue_index(), character:military_force():command_queue_index(), true);
2215 end;
2216 end,
2217 false,
2218 false,
2219 false
2220 );
2221end;
2222
2223function tomb_kings_reset_unit_caps()
2224 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_mon_tomb_scorpion_0"].army_count = 0;
2225 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_mon_ushabti_0"].army_count = 0;
2226 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_art_casket_of_souls_0"].army_count = 0;
2227 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_cha_liche_priest_light_1"].army_count = 0;
2228 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_cha_liche_priest_nehekhara_1"].army_count = 0;
2229 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_cav_necropolis_knights_0"].army_count = 0;
2230 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_mon_sepulchral_stalkers_0"].army_count = 0;
2231 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_cha_tomb_prince_2"].army_count = 0;
2232 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_cha_liche_priest_nehekhara_1"].army_count = 0;
2233 tomb_king_patrol_pool.tomb_kings_patrol_unit_cap["wh2_dlc09_tmb_cha_liche_priest_death_1"].army_count = 0;
2234end
2235
2236-----------------------------------------------------------------------
2237-- This function gives commands for every patrol alive.
2238-- This function will not run for patrols who spawned in the same turn.
2239-----------------------------------------------------------------------
2240function hertz_order_patrol(province_regions, faction_se, target_patrol, region_count)
2241 out("looking for patrol")
2242 local closest_region = target_patrol:region();
2243 local target_region = nil
2244 local alive_bundle_active = false;
2245 local target_exclusion_region = false;
2246
2247
2248 -- Check if the patrol exists.
2249 if target_patrol ~= false then
2250 out("Ordering patrol ["..target_patrol:command_queue_index().."]")
2251 -- Disable AI movement for patrol
2252 cm:cai_disable_movement_for_character(cm:char_lookup_str(target_patrol:command_queue_index()));
2253
2254 cm:replenish_action_points(cm:char_lookup_str(target_patrol:command_queue_index()))
2255
2256 -- Check for enemies within the same region.
2257 local enemy_in_region = get_target_in_region(target_patrol:faction(), target_patrol);
2258
2259 out("Checking for excluded capitals.")
2260 for i = 1, #region_exclusion[campaign_model] do
2261 if province_regions[1]:province_name() == cm:get_region(region_exclusion[campaign_model][i]):province_name() and cm:get_region(region_exclusion[campaign_model][i]):is_province_capital() then
2262 target_exclusion_region = cm:get_region(region_exclusion[campaign_model][i])
2263 out("region found")
2264 end;
2265 end;
2266
2267 -- Get the target owned province capital settlement rank.
2268 local local_settlement_level = 0;
2269 if target_exclusion_region ~= false then
2270 local_settlement_level = tonumber(target_exclusion_region:settlement():primary_slot():building():building_level());
2271 target_exclusion_region = false;
2272 else
2273 local_settlement_level = hertz_get_settlement_level(province_regions, true);
2274 end;
2275
2276 -- Get Army size to check if the patrol is missing units, or has too many.
2277 local army_size = target_patrol:military_force():unit_list():num_items();
2278 local army_size_target = nil;
2279 local vamp_patrol_update = false;
2280
2281 -- Disable character death events for patrol replenishment.
2282 cm:disable_event_feed_events(true, "wh_event_category_character", "wh_event_subcategory_character_deaths", "");
2283 table.insert(patrol_saved_information, target_patrol:command_queue_index(), {pos_x = target_patrol:logical_position_x(), pos_y = target_patrol:logical_position_y(), last_turn_province_rank = 0})
2284
2285 -- Check the province capital rank from last turn for replenishment event generation.
2286 if patrol_saved_information[target_patrol:command_queue_index()].last_turn_province_rank == 0 then
2287 patrol_saved_information[target_patrol:command_queue_index()].last_turn_province_rank = local_settlement_level;
2288 end;
2289
2290 if target_patrol:military_force():force_type():key() ~= "PATROL_ARMY" then
2291 cm:convert_force_to_type(target_char:military_force(), "PATROL_ARMY");
2292 end;
2293
2294 -- Check if any local region is under siege
2295 local region_is_under_siege = false;
2296 for j = 1, #province_regions do
2297 if province_regions[j]:garrison_residence():is_under_siege() then
2298 region_is_under_siege = true;
2299 target_region = province_regions[j];
2300 out("["..target_region:name().."] is under siege!")
2301 end;
2302 end;
2303
2304 -- Set character loyalty to default.
2305 if target_patrol:loyalty() < 5 then
2306 cm:modify_character_personal_loyalty_factor(cm:char_lookup_str(target_patrol:command_queue_index()), 5 - target_patrol:loyalty());
2307
2308 elseif target_patrol:loyalty() > 5 then
2309 cm:modify_character_personal_loyalty_factor(cm:char_lookup_str(target_patrol:command_queue_index()), 5 - target_patrol:loyalty());
2310 end
2311
2312 -- Set the target army size of the target p[atrol to check the amount of allowed units in the force.
2313 if faction_se:subculture() == "wh_dlc05_sc_wef_wood_elves" then
2314 army_size_target = ram_unit_lists["wef_patrols"].force_size + 1;
2315
2316 elseif faction_se:name() == "wh2_dlc13_lzd_spirits_of_the_jungle" then
2317 army_size_target = ram_unit_lists["nekai_patrols"].force_size + 1;
2318
2319 elseif faction_se:subculture() == "wh_main_sc_vmp_vampire_counts" then
2320 army_size_target = vamp_unit_lists["force_size"].force_size + 1;
2321
2322 else
2323 army_size_target = ram_unit_lists[local_settlement_level].force_size + 1;
2324 end
2325
2326 -----------------------------------------------------------------------------------------------------------------------
2327 --If patrol is a vampire counts patrol, check its patrol effect bundle for the corruption effect to check patrol rank.
2328 -----------------------------------------------------------------------------------------------------------------------
2329 if faction_se:subculture() == "wh_main_sc_vmp_vampire_counts" and local_settlement_level > 2 then
2330 local patrol_effect_bundles = target_patrol:military_force():effect_bundles()
2331 for i = 0, patrol_effect_bundles:num_items() - 1 do
2332 local target_bundle = patrol_effect_bundles:item_at(i);
2333 local effect_list = target_bundle:effects()
2334 for j = 0, effect_list:num_items() - 1 do
2335 if effect_list:item_at(j):key() == "hertz_vamp_patrol_2" and local_settlement_level ~= 2 then
2336 vamp_patrol_update = true
2337
2338 elseif effect_list:item_at(j):key() == "hertz_vamp_patrol_3" and local_settlement_level ~= 3 then
2339 vamp_patrol_update = true
2340
2341 elseif effect_list:item_at(j):key() == "hertz_vamp_patrol_4" and local_settlement_level ~= 4 then
2342 vamp_patrol_update = true
2343
2344 elseif effect_list:item_at(j):key() == "hertz_vamp_patrol_5" and local_settlement_level ~= 5 then
2345 vamp_patrol_update = true
2346 end;
2347 end;
2348 end;
2349 end;
2350
2351 if army_size >= army_size_target then
2352 -- Check regions for active patrol bundles.
2353 for i = 1, #province_regions do
2354 if province_regions[i]:has_effect_bundle("hertz_patrol_alive_bundle") then
2355 alive_bundle_active = true;
2356 else
2357 alive_bundle_active = false;
2358 end;
2359 end;
2360 -- reapply if current patrol region has no effect.
2361 if not alive_bundle_active then
2362 cm:apply_effect_bundle_to_region("hertz_patrol_alive_bundle", closest_region:name(), -1);
2363 end;
2364 end;
2365 out("army_size_target " ..army_size_target);
2366 out("army_size " ..army_size);
2367 out("local_settlement_level " ..local_settlement_level);
2368
2369 -- Replenish current patrol if units are missing, or capital levels up.
2370 if army_size < army_size_target or vamp_patrol_update then
2371 out("Units missing, replenishing force");
2372 out("this turn rank ["..local_settlement_level.."]")
2373 out("last turn rank ["..patrol_saved_information[target_patrol:command_queue_index()].last_turn_province_rank.."]")
2374 local number_units_missing = army_size_target - army_size
2375 local unit_replenish_incident = "";
2376 target_region = target_patrol:region();
2377
2378 -- Sets the target event to pop up after a patrol started replenishing.
2379 if local_settlement_level > patrol_saved_information[target_patrol:command_queue_index()].last_turn_province_rank then
2380 unit_replenish_incident = "hertz_patrol_rank_up";
2381 else
2382 unit_replenish_incident = "hertz_patrol_replenishing";
2383 end;
2384
2385 -- Set the time to replenish according to the number of units missing, with a minimum of 2 turns and a maximum of 5 turns.
2386 if number_units_missing > 5 then
2387 number_units_missing = 5;
2388 elseif army_size == army_size_target then
2389 number_units_missing = 2;
2390 end;
2391
2392 -- Target the closest owned settlement to move towards to start replenishing.
2393 local pos_x, pos_y = cm:find_valid_spawn_location_for_character_from_settlement(faction_se:name(), target_region:name(), false, true, 1);
2394 cm:set_character_immortality(cm:char_lookup_str(target_patrol:command_queue_index()), false);
2395 if faction_se:is_human() and region_count < leightweight_mode_cap then
2396 cm:move_character(
2397 target_patrol:command_queue_index(),
2398 pos_x,
2399 pos_y,
2400 true,
2401 false,
2402 function()
2403 -- Kill the patrol and apply the cooldown effect on the region.
2404 cm:kill_character(target_patrol:command_queue_index(), true, true);
2405 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", closest_region:name(), number_units_missing);
2406 remove_patrol_bundle(province_regions);
2407
2408 if not table_contains(patrol_saved_information.removed_cqis, target_patrol:command_queue_index()) then
2409 table.insert(patrol_saved_information.removed_cqis, target_patrol:command_queue_index());
2410 end;
2411
2412 if faction_se:is_human() then
2413 if unit_replenish_incident == "hertz_patrol_rank_up" or unit_replenish_incident == "hertz_patrol_replenishing" then
2414 cm:trigger_incident_with_targets(
2415 faction_se:command_queue_index(),
2416 unit_replenish_incident,
2417 0,
2418 0,
2419 0,
2420 0,
2421 target_region:cqi(),
2422 target_region:settlement():cqi()
2423 );
2424 end;
2425 end;
2426 end,
2427
2428 -- The AI does not need to move to a region to replenish, as most of this would happen in the fog of war.
2429 function()
2430 cm:kill_character(target_patrol:command_queue_index(), true, true);
2431 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", closest_region:name(), number_units_missing);
2432 remove_patrol_bundle(province_regions);
2433
2434 if not table_contains(patrol_saved_information.removed_cqis, target_patrol:command_queue_index()) then
2435 table.insert(patrol_saved_information.removed_cqis, target_patrol:command_queue_index());
2436 end;
2437
2438 if faction_se:is_human() then
2439 if unit_replenish_incident == "hertz_patrol_rank_up" or unit_replenish_incident == "hertz_patrol_replenishing" then
2440 cm:trigger_incident_with_targets(
2441 faction_se:command_queue_index(),
2442 unit_replenish_incident,
2443 0,
2444 0,
2445 0,
2446 0,
2447 target_region:cqi(),
2448 target_region:settlement():cqi()
2449 );
2450 end;
2451 end;
2452 end
2453 );
2454 else
2455 -- When Lightweight mode is active simply delete the patrol and apply the cooldown.
2456 cm:kill_character(target_patrol:command_queue_index(), true, false);
2457 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", closest_region:name(), number_units_missing);
2458 remove_patrol_bundle(province_regions);
2459 end;
2460
2461 -- If there are too many units in a force, the force goes to replenish but without a cooldown, and will get replaced immediatly.
2462 elseif army_size > army_size_target then
2463 out("Too many units, refreshing force");
2464 local number_units_missing = army_size_target - army_size
2465 target_region = target_patrol:region();
2466 local pos_x, pos_y = cm:find_valid_spawn_location_for_character_from_settlement(faction_se:name(), target_region:name(), false, true, 1);
2467 cm:set_character_immortality(cm:char_lookup_str(target_patrol:command_queue_index()), false);
2468 if faction_se:is_human() then
2469 cm:move_character(
2470 target_patrol:command_queue_index(),
2471 pos_x,
2472 pos_y,
2473 true,
2474 false,
2475 function()
2476 cm:kill_character(target_patrol:command_queue_index(), true, false);
2477 hertz_create_new_patrol(province_regions);
2478
2479 end,
2480 function()
2481 cm:kill_character(target_patrol:command_queue_index(), true, false);
2482 hertz_create_new_patrol(province_regions);
2483 end
2484 );
2485 else
2486 cm:kill_character(target_patrol:command_queue_index(), true, false);
2487 cm:apply_effect_bundle_to_region("hertz_patrol_replenish_bundle", target_region:name(), 1);
2488 remove_patrol_bundle(province_regions);
2489 end;
2490
2491 -- Start the regular patrol movement.
2492 elseif enemy_in_region == false then
2493 out("Patroling province")
2494 local region_amount = table.getn(province_regions);
2495 local rng = cm:random_number(region_amount, 1);
2496 local distance_from_settlement = 3;
2497
2498 -- Go towards any region that is under siege if no target was found.
2499 -- Otherwise pick a random settlement within the province that is not inside the region the patrol is currently in.
2500 if not region_is_under_siege then
2501 distance_from_settlement = cm:random_number(15, 7)
2502 target_region = province_regions[rng]
2503
2504 if target_region == closest_region then
2505 if rng == region_amount then
2506 rng = rng - 1;
2507 else
2508 rng = rng + 1;
2509 end
2510 target_region = province_regions[rng]
2511 end
2512 end
2513
2514 if closest_region:name() ~= target_region:name() or region_is_under_siege then
2515 local pos_x = 0
2516 local pos_y = 0
2517 -- Get valid position to movee the patrol towards.
2518 -- If a region is under siege the position is nil, so we grab the settlement logical position and go from there.
2519 if closest_region:name() ~= target_region:name() and region_is_under_siege then
2520 pos_x, pos_y = cm:find_valid_spawn_location_for_character_from_position(faction_se:name(), target_region:settlement():logical_position_x(), target_region:settlement():logical_position_y(), true, distance_from_settlement);
2521 else
2522 pos_x, pos_y = cm:find_valid_spawn_location_for_character_from_settlement(faction_se:name(), target_region:name(), false, true, distance_from_settlement);
2523 end
2524 -- Patrol movement for human players is done via the normal move_character event. For AI the patrols are simply teleported.
2525 if faction_se:is_human() and region_count < leightweight_mode_cap then
2526 table.insert(faction_patrol_list[faction_se:name()].patrols, target_patrol);
2527 cm:move_character(
2528 target_patrol:command_queue_index(),
2529 pos_x,
2530 pos_y,
2531 false,
2532 false,
2533 function()
2534 -- End patrol movement in ambush stance for skaven and woodelves
2535 if faction_se:subculture() == "wh2_main_sc_skv_skaven" or faction_se:subculture() == "wh_dlc05_sc_wef_wood_elves" then
2536 --cm:replenish_action_points(cm:char_lookup_str(target_patrol:command_queue_index()))
2537 cm:force_character_force_into_stance(cm:char_lookup_str(target_patrol:command_queue_index()), "MILITARY_FORCE_ACTIVE_STANCE_TYPE_AMBUSH");
2538 end;
2539 cm:disable_movement_for_character(cm:char_lookup_str(target_patrol:command_queue_index()));
2540
2541 if target_patrol:region():province_name() ~= target_region:province_name() then
2542 cm:teleport_to(cm:char_lookup_str(target_patrol:command_queue_index()), pos_x, pos_y, true)
2543 end
2544 end,
2545 function()
2546 -- End patrol movement in ambush stance for skaven and woodelves
2547 if faction_se:subculture() == "wh2_main_sc_skv_skaven" or faction_se:subculture() == "wh_dlc05_sc_wef_wood_elves" then
2548 --cm:replenish_action_points(cm:char_lookup_str(target_patrol:command_queue_index()))
2549 cm:force_character_force_into_stance(cm:char_lookup_str(target_patrol:command_queue_index()), "MILITARY_FORCE_ACTIVE_STANCE_TYPE_AMBUSH");
2550 end;
2551 cm:disable_movement_for_character(cm:char_lookup_str(target_patrol:command_queue_index()));
2552
2553 if target_patrol:region():province_name() ~= target_region:province_name() then
2554 cm:teleport_to(cm:char_lookup_str(target_patrol:command_queue_index()), pos_x, pos_y, true)
2555 end
2556 end
2557 );
2558 else
2559 cm:teleport_to(cm:char_lookup_str(target_patrol:command_queue_index()), pos_x, pos_y, true)
2560 if faction_se:subculture() == "wh2_main_sc_skv_skaven" or faction_se:subculture() == "wh_dlc05_sc_wef_wood_elves" then
2561 cm:force_character_force_into_stance(cm:char_lookup_str(target_patrol:command_queue_index()), "MILITARY_FORCE_ACTIVE_STANCE_TYPE_AMBUSH");
2562 end;
2563 end;
2564 else
2565 script_error("ERROR: No compatible regions found to move unit to. Amount of regions: " ..tostring(region_amount)..". grabbing index number: " ..tostring(rng)..". Target region: " ..target_region:name());
2566 end;
2567
2568 -- AI Patrol movement
2569 else
2570 cm:disable_event_feed_events(false, "wh_event_category_character", "wh_event_subcategory_character_deaths", "");
2571 out("Atacking")
2572 if faction_se:is_human() then
2573 cm:attack_queued(cm:char_lookup_str(target_patrol:command_queue_index()), cm:char_lookup_str(enemy_in_region:command_queue_index()), true)
2574 else
2575 cm:attack(cm:char_lookup_str(target_patrol:command_queue_index()), cm:char_lookup_str(enemy_in_region:command_queue_index()), true)
2576 end;
2577 end;
2578 patrol_saved_information[target_patrol:command_queue_index()].last_turn_province_rank = local_settlement_level;
2579
2580 -- If no patrol is found to give commands to inside the province, the patrol_alive effect bundle is removed from the province.
2581 else
2582 remove_patrol_bundle(province_regions)
2583 end;
2584end;
2585
2586-----------------------------------------------------
2587--Functionality for when patrols lose after a battle
2588-----------------------------------------------------
2589function hertz_patrol_death(winning_force, patrol_se, region)
2590 out("Patrol in province " ..region:province_name().. " defeated. triggering dilemma");
2591
2592
2593 -- If patrol died in the wrong province, pick the other side of the pending battle as patrol province
2594 local province_regions = {};
2595 local target_region = nil;
2596 local patrol_faction = region:owning_faction();
2597 local suitible_province_found = false;
2598 local existing_patrol = get_province_patrol(region:province_name(), patrol_faction);
2599 local dilemma_complete = false;
2600 local target_is_patrol_force = true;
2601 local force_unit_count = cm:get_saved_value("patrol_force_count");
2602 out("Force unit count is ["..force_unit_count.."]")
2603
2604 -- Check if the patrol force is still alive after the battle
2605 if not patrol_se:is_null_interface() then
2606 if patrol_se:has_effect_bundle("hertz_patrol_bundle") or patrol_se:military_force():has_effect_bundle("hertz_patrol_bundle") then
2607 out("patrol stil alive, commence slaughter without mercy...")
2608 cm:kill_character(patrol_se:command_queue_index(), true, false);
2609 else
2610 out("Normal force selected to kill. Cancelling rest of function");
2611 target_is_patrol_force = false;
2612 end;
2613 end
2614
2615 if target_is_patrol_force then
2616 if existing_patrol ~= false then
2617 -- Region patrol died in has another patrol in it. Triggering patrol died in the wrong region grabbing winning_force region.
2618 province_regions = hertz_get_province_owned_regions(winning_force:region():province_name(), patrol_faction:name(), false);
2619 existing_patrol = get_province_patrol(winning_force:region():province_name(), patrol_faction)
2620 if existing_patrol == false then
2621 suitible_province_found = true;
2622 target_region = winning_force:region();
2623 end;
2624 else
2625 -- Patrol died in region without another patrol in it. Grabbing province regions
2626 province_regions = hertz_get_province_owned_regions(region:province_name(), patrol_faction:name(), false);
2627 suitible_province_found = true;
2628 target_region = region;
2629 end
2630
2631 if suitible_province_found then
2632 out("suitable Province found applying effects.")
2633 cm:apply_effect_bundle_to_region("hertz_patrol_died_bundle", province_regions[1]:name(), 5);
2634 remove_patrol_bundle(province_regions);
2635 else
2636 out("no suitable Province doing nothing.")
2637 end
2638
2639 -- Trigger the dilemma for when a patrol is defeated.
2640 if winning_force:faction():is_human() then
2641 out("Winning force is player, doing the dilemma thingy")
2642 hertz_patrol_dilemma(winning_force:faction(), target_region, target_region:settlement(), winning_force, province_regions, force_unit_count)
2643 end;
2644 end;
2645end;
2646
2647-------------------------------------------------------------------------------
2648-- Sets up effect bundles and the patrol defeated dilemma event for the player.
2649-------------------------------------------------------------------------------
2650function hertz_patrol_dilemma(faction_se, region_se, settlement_se, player_force, province_regions, patrol_force_count)
2651 out("Player force unit count ["..player_force:military_force():unit_list():num_items().."]");
2652
2653 -- Only trigger when the player force is equal or smaller than the patrol force.
2654 if player_force:military_force():unit_list():num_items() <= patrol_force_count then
2655 local caravan_effect_bundle = nil
2656 local settlement_effect_bundle = nil
2657 local religion_effect_bundle = nil
2658 local faction_religion = faction_se:state_religion()
2659 --out(faction_religion);
2660 local capital_level = hertz_get_settlement_level(province_regions, true);
2661 local local_settlement_level = hertz_get_local_settlement_level(region_se);
2662
2663 --out("Creating dilemma for region: " ..region_se:name())
2664
2665 -- Set corruption religion effect bundle
2666 if faction_religion == "wh2_main_religion_skaven" then
2667 religion_effect_bundle = "hertz_spread_corruption_bundle_skaven"
2668 elseif faction_religion == "wh_main_religion_chaos" then
2669 religion_effect_bundle = "hertz_spread_corruption_bundle_chaos"
2670 elseif faction_religion == "wh_main_religion_undeath" then
2671 religion_effect_bundle = "hertz_spread_corruption_bundle_vampire"
2672 elseif faction_religion == "wh_main_religion_untainted" then
2673 religion_effect_bundle = "hertz_spread_corruption_bundle_neutral"
2674 end
2675
2676 -- Setup raid caravan bundle according to province capital level.
2677 if capital_level == 1 then
2678 caravan_effect_bundle = "hertz_raid_caravan_bundle_1"
2679 elseif capital_level == 2 then
2680 caravan_effect_bundle = "hertz_raid_caravan_bundle_2"
2681 elseif capital_level == 3 then
2682 caravan_effect_bundle = "hertz_raid_caravan_bundle_3"
2683 elseif capital_level == 4 then
2684 caravan_effect_bundle = "hertz_raid_caravan_bundle_4"
2685 elseif capital_level == 5 then
2686 caravan_effect_bundle = "hertz_raid_caravan_bundle_5"
2687 end
2688
2689 -- Setup raid settlement bundle according to the local settlement level.
2690 if local_settlement_level == 1 then
2691 settlement_effect_bundle = "hertz_raid_settlement_bundle_1"
2692 elseif local_settlement_level == 2 then
2693 settlement_effect_bundle = "hertz_raid_settlement_bundle_2"
2694 elseif local_settlement_level == 3 then
2695 settlement_effect_bundle = "hertz_raid_settlement_bundle_3"
2696 end
2697 --out("Effect bundles set")
2698
2699 -- Setup the dilemma bundles and their effects.
2700 core:add_listener(
2701 mod_name.."_DilemmaChoiceMadeEvent",
2702 "DilemmaChoiceMadeEvent",
2703 function(context)
2704 return context:dilemma() == "hertz_patrol_defeated_dilemma";
2705 end,
2706 function(context)
2707 local dilemma_choice = context:choice();
2708 --out(dilemma_choice)
2709 if dilemma_choice == 0 then
2710 --out("Dilemma choice 1: Push the advantage");
2711 cm:replenish_action_points(cm:char_lookup_str(player_force:command_queue_index()))
2712
2713 elseif dilemma_choice == 1 then
2714 --out("Dilemma choice 2: Pillage caravans");
2715 cm:apply_effect_bundle_to_characters_force(caravan_effect_bundle, player_force:command_queue_index(), -1, true);
2716 cm:zero_action_points(cm:char_lookup_str(player_force:command_queue_index()))
2717
2718 elseif dilemma_choice == 2 then
2719 --out("Dilemma choice 3: Raid settlements");
2720 cm:apply_effect_bundle_to_characters_force(settlement_effect_bundle, player_force:command_queue_index(), -1, true);
2721 cm:zero_action_points(cm:char_lookup_str(player_force:command_queue_index()))
2722
2723 elseif dilemma_choice == 3 then
2724 --out("Dilemma choice 4: Spread corruption");
2725 cm:apply_effect_bundle_to_region(religion_effect_bundle, region_se:name(), 5);
2726 cm:zero_action_points(cm:char_lookup_str(player_force:command_queue_index()))
2727 end;
2728 end,
2729 false
2730 );
2731 -- trigger patrol defeated dilemma
2732 cm:trigger_dilemma_with_targets(
2733 faction_se:command_queue_index(),
2734 "hertz_patrol_defeated_dilemma",
2735 0,
2736 0,
2737 0,
2738 0,
2739 region_se:cqi(),
2740 settlement_se:cqi()
2741 );
2742 else
2743 out("force too big")
2744 end;
2745end;
2746
2747----------------------------------------------------------------------------------------------------
2748-- Adds treasury or experience according to the dilemma reward effects on any of the players forces.
2749----------------------------------------------------------------------------------------------------
2750function check_dilemma_rewards(target_faction)
2751 local mil_force_list = target_faction:military_force_list()
2752 local payload = nil;
2753 local gold_amount = 0;
2754 local exp_amount = 0;
2755 for i = 0, mil_force_list:num_items() - 1 do
2756 if mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_1") then
2757 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_1", mil_force_list:item_at(i):command_queue_index())
2758 gold_amount = cm:random_number(500, 100);
2759 cm:trigger_custom_incident(target_faction:name(), "hertz_caravans_incident", true, "payload{money "..gold_amount..";}");
2760
2761 elseif mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_2") then
2762 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_2", mil_force_list:item_at(i):command_queue_index())
2763 gold_amount = cm:random_number(1000, 400);
2764 cm:trigger_custom_incident(target_faction:name(), "hertz_caravans_incident", true, "payload{money "..gold_amount..";}");
2765
2766 elseif mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_3") then
2767 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_3", mil_force_list:item_at(i):command_queue_index())
2768 gold_amount = cm:random_number(1500, 600);
2769 cm:trigger_custom_incident(target_faction:name(), "hertz_caravans_incident", true, "payload{money "..gold_amount..";}");
2770
2771 elseif mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_4") then
2772 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_4", mil_force_list:item_at(i):command_queue_index())
2773 gold_amount = cm:random_number(2000, 1000);
2774 cm:trigger_custom_incident(target_faction:name(), "hertz_caravans_incident", true, "payload{money "..gold_amount..";}");
2775
2776 elseif mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_5") then
2777 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_5", mil_force_list:item_at(i):command_queue_index())
2778 gold_amount = cm:random_number(2500, 1200);
2779 cm:trigger_custom_incident(target_faction:name(), "hertz_caravans_incident", true, "payload{money "..gold_amount..";}");
2780 end;
2781
2782 if mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_1") then
2783 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_1", mil_force_list:item_at(i):command_queue_index())
2784 exp_amount = cm:random_number(500, 100);
2785 cm:add_agent_experience(cm:char_lookup_str(mil_force_list:item_at(i):general_character():command_queue_index(), exp_amount))
2786 cm:add_experience_to_units_commanded_by_character(cm:char_lookup_str(mil_force_list:item_at(i):general_character():command_queue_index(), 1));
2787
2788 elseif mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_2") then
2789 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_2", mil_force_list:item_at(i):command_queue_index())
2790 exp_amount = cm:random_number(1000, 400);
2791 cm:add_agent_experience(cm:char_lookup_str(mil_force_list:item_at(i):general_character():command_queue_index(), exp_amount))
2792 cm:add_experience_to_units_commanded_by_character(cm:char_lookup_str(mil_force_list:item_at(i):general_character():command_queue_index(), 2));
2793
2794 elseif mil_force_list:item_at(i):has_effect_bundle("hertz_raid_caravan_bundle_3") then
2795 cm:remove_effect_bundle_from_force("hertz_raid_caravan_bundle_3", mil_force_list:item_at(i):command_queue_index())
2796 exp_amount = cm:random_number(1500, 600);
2797 cm:add_agent_experience(cm:char_lookup_str(mil_force_list:item_at(i):general_character():command_queue_index(), exp_amount))
2798 cm:add_experience_to_units_commanded_by_character(cm:char_lookup_str(mil_force_list:item_at(i):general_character():command_queue_index(), 3));
2799 end;
2800 end;
2801end;
2802-----------------------------------------------------------------------------------------------------------------
2803-- Removes any patrols that end their turn inside another province owned by a different faction and getting lost.
2804-- Units are removed when they are found
2805-----------------------------------------------------------------------------------------------------------------
2806function clean_lost_patrols(faction_se)
2807 out("HERTZ_OUT: Cleaning lost patrols for ["..faction_se:name().."]")
2808 local lost_patrols = {};
2809 for i = 0, faction_se:military_force_list():num_items() - 1 do
2810
2811 if faction_se:military_force_list():item_at(i):has_effect_bundle("hertz_patrol_bundle") then
2812 out("Patrol found")
2813 local target_patrol_force = faction_se:military_force_list():item_at(i)
2814 local target_region = target_patrol_force:general_character():region();
2815
2816 if not target_region:is_abandoned() then
2817 local target_region_owner = target_patrol_force:general_character():region():owning_faction();
2818 out("Target province is ["..target_region:province_name().."] - Patrol in region ["..target_region:name().."] - Owned by ["..target_region_owner:name().."]")
2819
2820 if faction_se:name() == "wh2_dlc13_lzd_defenders_of_the_great_plan" or faction_se:name() == "wh2_dlc13_lzd_spirits_of_the_jungle" then
2821 if target_region_owner:holds_entire_province(target_region:province_name(), true) and target_region_owner:name() ~= "wh2_dlc13_lzd_defenders_of_the_great_plan" and target_region_owner:name() ~= "wh2_dlc13_lzd_spirits_of_the_jungle" then
2822 out("Lost patrol found attempting to kill def great plan")
2823 table.insert(lost_patrols, target_patrol_force:general_character():command_queue_index())
2824 end;
2825 else
2826 if target_region_owner:holds_entire_province(target_region:province_name(), true) and target_region_owner ~= faction_se then
2827 out("Lost patrol found attempting to kill")
2828 table.insert(lost_patrols, target_patrol_force:general_character():command_queue_index())
2829 end;
2830 end;
2831 else
2832 local owned_region_found = false;
2833
2834 for j = 0, faction_se:region_list():num_items() - 1 do
2835 if faction_se:region_list():item_at(j):province_name() == target_region:province_name() then
2836 owned_region_found = true;
2837 end;
2838 end;
2839
2840 if not owned_region_found then
2841 --out("Lost patrol found attempting to kill")
2842 table.insert(lost_patrols, target_patrol_force:general_character():command_queue_index())
2843 end;
2844 end;
2845 end;
2846 end;
2847 --out("Lost patrols found ["..table.concat(lost_patrols, ", ").."]")
2848 for k = 1, #lost_patrols do
2849 cm:kill_character(lost_patrols[k], true, false);
2850 end;
2851end;
2852
2853--------------------------------------------------------------------------------------------
2854-- Goes through every region in the given list to remove any patrol bundles that are active.
2855--------------------------------------------------------------------------------------------
2856function remove_patrol_bundle(province_regions)
2857 for i = 1, #province_regions do
2858 if province_regions[i]:has_effect_bundle("hertz_patrol_alive_bundle") then
2859 cm:remove_effect_bundle_from_region("hertz_patrol_alive_bundle", province_regions[i]:name());
2860 cm:remove_effect_bundle_from_region("hertz_tk_patrol_ambush_bundle", province_regions[i]:name());
2861 end;
2862 end;
2863end;
2864
2865------------------------------------------------------------------------------------------------------------------
2866-- Checks the given unit lists and lists every unit type with the amount of units of that type into a table.
2867-- Unused in current function but quite a nice function on its own, which makes it easy to check any army for any unit.
2868------------------------------------------------------------------------------------------------------------------
2869function hertz_force_unit_counter(unit_list)
2870 local unit_type_list = {}
2871 local output_table = {
2872 ["unit_type_list"] = {};
2873 }
2874 out("-----[[ FORCE UNIT COUNTER ]]-----");
2875 for i = 1, unit_list:num_items() - 1 do
2876 local target_unit_key = unit_list:item_at(i):unit_key()
2877 if not table_contains(unit_type_list, target_unit_key) then
2878 table.insert(unit_type_list, target_unit_key);
2879 end
2880 if output_table[target_unit_key] == nil then
2881 output_table[target_unit_key] = 1;
2882 else
2883 output_table[target_unit_key] = output_table[target_unit_key] + 1;
2884 end
2885 end
2886 for j = 1, #unit_type_list do
2887 out("["..unit_type_list[j].."] count = " ..output_table[unit_type_list[j]]);
2888 end
2889 output_table["unit_type_list"] = unit_type_list
2890 out("[unit_type_list] = " ..table.concat(output_table["unit_type_list"], " | "));
2891 out("-----[[ RETURN ]]-----");
2892 return output_table;
2893end
2894
2895------------------------------------------------------------------------------------------------------
2896-- Checks the faction the triggering patrol's faction is at war with.
2897-- If any of those faction's armies is inside the same region as the patrol. Pick that army as target.
2898------------------------------------------------------------------------------------------------------
2899function get_target_in_region(faction_se, target_patrol, province_regions)
2900 local target_region = target_patrol:region()
2901 local target_found = false;
2902 local at_war_with_list = faction_patrol_list[faction_se:name()].at_war_with;
2903 local target_char = nil
2904
2905 -- Check factions at war with
2906 for i = 1, #at_war_with_list do
2907 if not table_contains(war_faction_exclusion_list, at_war_with_list[i]) then
2908 out(at_war_with_list[i]);
2909 local target_faction = cm:get_faction(at_war_with_list[i]);
2910 if target_faction ~= false then
2911 if cm:faction_is_alive(target_faction) then
2912 local enemy_mil_force_list = target_faction:military_force_list();
2913
2914 for j = 0, enemy_mil_force_list:num_items() - 1 do
2915 local current_mf = enemy_mil_force_list:item_at(j);
2916 target_char = current_mf:general_character();
2917 local force_type = current_mf:force_type()
2918
2919 if current_mf:is_armed_citizenry() == false and current_mf:has_general() == true and force_type ~= "SUPPORT_ARMY" and force_type ~= "PATROL_ARMY" and not target_char:is_at_sea() then
2920 --out("Character ["..target_char:command_queue_index().."] in region ["..target_char:region():name().."]")
2921 if get_distance_between_objects(target_patrol, target_char) < 800 and target_char:region():province_name() == target_patrol:region():province_name() then
2922 target_found = true;
2923 out("target_found");
2924 return target_char;
2925 end;
2926 end;
2927 end;
2928 end;
2929 end;
2930 end;
2931 end;
2932
2933 -- Check rebellions.
2934 for k = 1, #war_faction_inclusion_list do
2935 local secondary_faction = cm:get_faction(war_faction_inclusion_list[k]);
2936 if secondary_faction ~= false then
2937 if cm:faction_is_alive(secondary_faction) then
2938 --out(war_faction_inclusion_list[k]);
2939 local sec_fac_mil_force = secondary_faction:military_force_list();
2940 --out("mil_force_list get")
2941
2942 if sec_fac_mil_force:num_items() > 0 then
2943 for l = 0, sec_fac_mil_force:num_items() - 1 do
2944 local rebel_mf = sec_fac_mil_force:item_at(l);
2945 --out("rebel_mf get");
2946 local rebel_char = rebel_mf:general_character();
2947 --out("rebel_char get")
2948 local force_type = rebel_mf:force_type()
2949 --out("force-type get")
2950 if rebel_mf:is_armed_citizenry() == false and rebel_mf:has_general() == true and force_type ~= "SUPPORT_ARMY" and force_type ~= "PATROL_ARMY" and not target_char:is_at_sea() then
2951 if rebel_char:region():name() == target_region:name() then
2952 target_found = true;
2953 target_char = rebel_char;
2954 --out("target_found");
2955 return target_char;
2956 end;
2957 end;
2958 end;
2959 end;
2960 end;
2961 end;
2962 end;
2963
2964 if target_found == false then
2965 --out("No target found")
2966 return false;
2967 end;
2968end;
2969
2970
2971--------------------------------------------------------------------------------------------------------
2972-- This is the tomb kings variant of the function above.
2973-- This function returns a list of possible targets within the region instead of the first target found.
2974-- This is done so that all military stances can be read of the targets within the tomb kings province.
2975--------------------------------------------------------------------------------------------------------
2976function get_target_in_tomb_kings_region(faction_se, province_regions)
2977 local target_province = province_regions[1]:province_name()
2978 local target_found = false;
2979 local at_war_with_list = faction_patrol_list[faction_se:name()].at_war_with;
2980 local target_char = nil
2981 local target_char_table = {}
2982
2983 for i = 1, #at_war_with_list do
2984 if not table_contains(war_faction_exclusion_list, at_war_with_list[i]) then
2985 out(at_war_with_list[i]);
2986 local target_faction = cm:get_faction(at_war_with_list[i]);
2987 if target_faction ~= false then
2988 if cm:faction_is_alive(target_faction) then
2989 local enemy_mil_force_list = target_faction:military_force_list();
2990
2991 for j = 0, enemy_mil_force_list:num_items() - 1 do
2992 local current_mf = enemy_mil_force_list:item_at(j);
2993 target_char = current_mf:general_character();
2994 local force_type = current_mf:force_type()
2995
2996 if current_mf:is_armed_citizenry() == false and current_mf:has_general() == true and force_type ~= "SUPPORT_ARMY" and force_type ~= "PATROL_ARMY" and not target_char:is_at_sea() then
2997 --out("Character ["..target_char:command_queue_index().."] in region ["..target_char:region():name().."]")
2998 if target_char:region():province_name() == target_province then
2999 target_found = true;
3000 out("target_found");
3001 table.insert(target_char_table, target_char);
3002 end;
3003 end;
3004 end;
3005 end;
3006 end;
3007 end;
3008 end;
3009 if target_found then
3010 out("Target found ["..tostring(target_found).."]");
3011 return target_char_table
3012 elseif target_found == false then
3013 for k = 1, #war_faction_inclusion_list do
3014 local secondary_faction = cm:get_faction(war_faction_inclusion_list[k]);
3015
3016 if secondary_faction ~= false then
3017
3018 if cm:faction_is_alive(secondary_faction) then
3019 --out(war_faction_inclusion_list[k]);
3020 local sec_fac_mil_force = secondary_faction:military_force_list();
3021 --out("mil_force_list get")
3022
3023 if sec_fac_mil_force:num_items() > 0 then
3024
3025 for l = 0, sec_fac_mil_force:num_items() - 1 do
3026 local rebel_mf = sec_fac_mil_force:item_at(l);
3027 --out("rebel_mf get");
3028 local rebel_char = rebel_mf:general_character();
3029 --out("rebel_char get")
3030 local force_type = rebel_mf:force_type()
3031 --out("force-type get")
3032
3033 if rebel_mf:is_armed_citizenry() == false and rebel_mf:has_general() == true and force_type ~= "SUPPORT_ARMY" and force_type ~= "PATROL_ARMY" and not rebel_char:is_at_sea() then
3034 --out("Character ["..rebel_char:command_queue_index().."] in region ["..rebel_char:region():name().."]")
3035
3036 if rebel_char:region():province_name() == target_province then
3037 target_found = true;
3038 out("target_found");
3039 table.insert(target_char_table, rebel_char);
3040 end;
3041 end;
3042 end;
3043 end;
3044 end;
3045 end;
3046 end;
3047 if target_found == false then
3048 --out("No target found")
3049 return false;
3050 end;
3051 end;
3052end;
3053
3054--------------------------------------------------------------------------------------------------------------
3055-- Returns the distance between 2 objects provided to the function
3056-- These objects can be a REGION_SCRIPT_INTERFACE, CHARACTER_SCRIPT_INTERFACE, or SETTLEMENT_SCRIPT_INTERFACE
3057--------------------------------------------------------------------------------------------------------------
3058function get_distance_between_objects(source_obj, target_obj)
3059 if is_region(source_obj) then
3060 source_obj = source_obj:settlement()
3061 end;
3062 if is_region(target_obj) then
3063 target_obj = target_obj:settlement()
3064 end;
3065 local obj_1 = {}
3066 local obj_2 = {}
3067 if is_region(source_obj) or is_character(source_obj) or is_settlement(source_obj) then
3068 obj_1.pos = {source_obj:logical_position_x(), source_obj:logical_position_y()}
3069 else
3070 script_error("get_distance_between_objects() called but supplied source object is not a Region Object, Settlement Object or Character Object")
3071 end;
3072 if is_region(source_obj) or is_character(source_obj) or is_settlement(target_obj) then
3073 obj_2.pos = {target_obj:logical_position_x(), target_obj:logical_position_y()}
3074 else
3075 script_error("get_distance_between_objects() called but supplied target object is not a Region Object, Settlement Object or Character Object")
3076 end;
3077
3078 --out("Distance between forces ["..distance_squared(obj_1.pos[1], obj_1.pos[2], obj_2.pos[1], obj_2.pos[2]).."] meters")
3079 return distance_squared(obj_1.pos[1], obj_1.pos[2], obj_2.pos[1], obj_2.pos[2]);
3080end;
3081
3082-------------------------------------------------------------------------
3083-- Return the province patrol that is patrolling in the province provided
3084-------------------------------------------------------------------------
3085function get_province_patrol(province_key, faction_se)
3086 local target_found = false;
3087 for i = 0, faction_se:military_force_list():num_items() - 1 do
3088 local target_mf = faction_se:military_force_list():item_at(i);
3089 local force_type = target_mf:force_type()
3090 if target_mf:has_effect_bundle("hertz_patrol_bundle") and target_mf:general_character():region():province_name() == province_key then
3091 target_found = true;
3092 return target_mf:general_character();
3093 end;
3094 end;
3095 if not target_found then
3096 --out("HERTZ_OUT: Province patrol not found. Returning false.")
3097 return false;
3098 end;
3099end;
3100
3101----------------------------------------------------------------------
3102-- Keeps track of each faction and which faction they are at war with.
3103----------------------------------------------------------------------
3104function get_faction_wars(source_faction, test)
3105 local faction_list = cm:model():world():faction_list()
3106 local faction_war_list_1 = {};
3107 local faction_war_list_2 = {};
3108
3109 for i = 0, faction_list:num_items() - 1 do
3110 local target_faction = faction_list:item_at(i);
3111
3112 if source_faction:at_war_with(target_faction) then
3113 if not table_contains(faction_patrol_list[source_faction:name()].at_war_with, target_faction:name()) then
3114 table.insert(faction_patrol_list[source_faction:name()].at_war_with, target_faction:name())
3115 --out("Faction: " ..source_faction:name().. " is at war with: " ..target_faction:name());
3116 end;
3117 if not table_contains(faction_patrol_list[target_faction:name()].at_war_with, source_faction:name()) then
3118 table.insert(faction_patrol_list[target_faction:name()].at_war_with, source_faction:name())
3119 if test then
3120 for k = 1, #faction_patrol_list[target_faction:name()].at_war_with do
3121 --out("Faction: " ..target_faction:name().. " is at war with: " ..faction_patrol_list[target_faction:name()].at_war_with[k]);
3122 end;
3123 end
3124 end;
3125 end;
3126 end;
3127 if test then
3128 for j = 1, #faction_patrol_list[source_faction:name()].at_war_with do
3129 --out("Faction: " ..source_faction:name().. " is at war with: " ..faction_patrol_list[source_faction:name()].at_war_with[j]);
3130 end;
3131 end
3132 return faction_patrol_list[source_faction:name()].at_war_with;
3133end;
3134
3135-----------------------------------------------------------------------------------------------------
3136-- returns the highest settlement level in the province, or the current rank of the province capital.
3137-----------------------------------------------------------------------------------------------------
3138function hertz_get_settlement_level(province_regions, capital)
3139 if capital then
3140 for i = 1, #province_regions do
3141 local target_region = province_regions[i];
3142
3143 if target_region:is_province_capital() then
3144 local current_rank = tonumber(target_region:settlement():primary_slot():building():building_level());
3145 if province_regions[i]:owning_faction():subculture() == "wh_main_sc_vmp_vampire_counts" then
3146 return hertz_vampire_counts_settlement_rank(current_rank, province_regions[i]:religion_proportion("wh_main_religion_undeath"))
3147 else
3148 return current_rank;
3149 end;
3150 end;
3151 end;
3152 else
3153 local current_rank = 0;
3154 for j = 1, #province_regions do
3155 local target_region = province_regions[j];
3156
3157 if not target_region:is_province_capital() then
3158 if target_region:settlement():primary_slot():building():building_level() > current_rank then
3159 current_rank = tonumber(target_region:settlement():primary_slot():building():building_level());
3160 end;
3161 end;
3162 end;
3163 if province_regions[1]:owning_faction():subculture() == "wh_main_sc_vmp_vampire_counts" then
3164 return hertz_vampire_counts_settlement_rank(current_rank, province_regions[1]:religion_proportion("wh_main_religion_undeath"))
3165 else
3166 return current_rank;
3167 end;
3168 end;
3169end;
3170---------------------------------------------------------------------------------
3171-- The vampire settlement/patrol rank is also determined by the local corruption.
3172---------------------------------------------------------------------------------
3173function hertz_vampire_counts_settlement_rank(settlement_rank, corruption)
3174 if settlement_rank == 2 then
3175 if corruption > vamp_unit_lists[settlement_rank].required_corruption then
3176 return settlement_rank
3177 else
3178 return false
3179 end;
3180 elseif settlement_rank == 3 then
3181 if corruption > vamp_unit_lists[settlement_rank].required_corruption then
3182 return settlement_rank
3183 else
3184 hertz_vampire_counts_settlement_rank(settlement_rank - 1, corruption)
3185 end;
3186 elseif settlement_rank == 4 then
3187 if corruption > vamp_unit_lists[settlement_rank].required_corruption then
3188 return settlement_rank
3189 else
3190 hertz_vampire_counts_settlement_rank(settlement_rank - 1, corruption)
3191 end;
3192 elseif settlement_rank == 5 then
3193 if corruption > vamp_unit_lists[settlement_rank].required_corruption then
3194 return settlement_rank
3195 else
3196 hertz_vampire_counts_settlement_rank(settlement_rank - 1, corruption)
3197 end;
3198 end;
3199end;
3200
3201function hertz_get_local_settlement_level(region)
3202 if is_region(region) then
3203 return tonumber(region:settlement():primary_slot():building():building_level());
3204 else
3205 script_error("ERROR: hertz_get_local_settlement_level() called. But supplied region is not a REGION_SCRIPT_INTERFACE.");
3206 end;
3207end;
3208
3209-- Check if any force is standing against a patrol to disable army merging.
3210function hertz_is_patrol_merging()
3211 local mil_force_list = player_faction:military_force_list()
3212 local patrol_force = {};
3213 local target_force = {};
3214 local unit_close_enough = false
3215
3216 for i = 0, mil_force_list:num_items() - 1 do
3217 if mil_force_list:item_at(i):has_effect_bundle("hertz_patrol_bundle") then
3218 patrol_force.force = mil_force_list:item_at(i);
3219 patrol_force.general = patrol_force.force:general_character();
3220 patrol_force.region = patrol_force.general:region();
3221 patrol_force.x = patrol_force.general:logical_position_x()
3222 patrol_force.y = patrol_force.general:logical_position_y()
3223
3224 for j = 0, mil_force_list:num_items() - 1 do
3225 target_force.force = mil_force_list:item_at(j);
3226 if target_force.force:has_general() and not target_force.force:is_armed_citizenry() then
3227 target_force.general = target_force.force:general_character();
3228 target_force.region = target_force.general:region();
3229 target_force.x = target_force.general:logical_position_x();
3230 target_force.y = target_force.general:logical_position_y();
3231
3232 if patrol_force.region == target_force.region then
3233 local distance = distance_squared(patrol_force.x, patrol_force.y, target_force.x, target_force.y);
3234 --out("Distance is: [" ..distance.."] meters")
3235 if distance < 5 then
3236 unit_close_enough = true
3237 return true;
3238 end;
3239 end;
3240 end;
3241 end;
3242 end;
3243 end;
3244 if not unit_close_enough then
3245 return false;
3246 end;
3247end;
3248
3249-------------------------------------------------------------------------
3250-- Old Legacy function that removed the supply lines bundle from patrols
3251-- NO LONGER USED.
3252-------------------------------------------------------------------------
3253function remove_supply_line_bundles(faction)
3254 cm:callback(function()
3255 if faction:is_human() then
3256 local supply_line_fix_bundle = false;
3257
3258 local difficulty = cm:model():combined_difficulty_level();
3259
3260 local cqi = 0;
3261 local effect_bundle = "wh_main_bundle_force_additional_army_upkeep_easy"; -- easy
3262 local supply_line_fix_effect_bundle = "patrol_no_supply_bundle_easy";
3263
3264 if(cm:is_multiplayer()) then
3265 -- In multiplayer we force everyone's upkeep difficulty to 2% (normal) to avoid desyncs
3266 supply_line_fix_effect_bundle = "patrol_no_supply_bundle_normal";
3267 effect_bundle = "wh_main_bundle_force_additional_army_upkeep_normal";
3268 else
3269 if difficulty == 0 then
3270 effect_bundle = "wh_main_bundle_force_additional_army_upkeep_normal"; -- normal
3271 supply_line_fix_effect_bundle = "patrol_no_supply_bundle_normal"; -- normal
3272 elseif difficulty == -1 then
3273 effect_bundle = "wh_main_bundle_force_additional_army_upkeep_hard"; -- hard
3274 supply_line_fix_effect_bundle = "patrol_no_supply_bundle_hard"; -- hard
3275 elseif difficulty == -2 then
3276 effect_bundle = "wh_main_bundle_force_additional_army_upkeep_very_hard"; -- very hard
3277 supply_line_fix_effect_bundle = "patrol_no_supply_bundle_very_hard"; -- very hard
3278 elseif difficulty == -3 then
3279 effect_bundle = "wh_main_bundle_force_additional_army_upkeep_legendary"; -- legendary
3280 supply_line_fix_effect_bundle = "patrol_no_supply_bundle_legendary"; -- legendary
3281 end
3282 end
3283
3284 local mf_list = faction:military_force_list();
3285
3286 for i = 0, mf_list:num_items() - 1 do
3287 local current_mf = mf_list:item_at(i);
3288 local force_type = current_mf:force_type():key()
3289
3290 if current_mf:is_armed_citizenry() == false and current_mf:has_general() == true and current_mf:has_effect_bundle("hertz_patrol_bundle") then
3291 if current_mf:has_effect_bundle(supply_line_fix_effect_bundle) then
3292 supply_line_fix_bundle = true;
3293 end;
3294 local general = current_mf:general_character();
3295 local character_subtype_key = general:character_subtype_key();
3296 cqi = general:command_queue_index();
3297 cm:remove_effect_bundle_from_characters_force(effect_bundle, cqi);
3298 end
3299 end
3300
3301 if not supply_line_fix_bundle then
3302 cm:apply_effect_bundle_to_characters_force(supply_line_fix_effect_bundle, cqi, -1, true)
3303 end;
3304 end
3305 end, 0.6);
3306end
3307
3308-----------------------------------------------------------------------------------
3309-- Checks if supplied effect bundle string is currently active on the input region.
3310-----------------------------------------------------------------------------------
3311function hertz_get_effect_bundle_from_region(region_key, effect_bundle_key)
3312 if not is_string(region_key) then
3313 script_error("hertz_get_effect_bundle_from_region() called but supplied [region_key] is not a [string]")
3314 end;
3315
3316 if region_key == nil or region_key == "" then
3317 script_error("hertz_get_effect_bundle_from_region() called but supplied [region_key] is ["..tostring(region_key).."]")
3318 end;
3319
3320 if not is_string(effect_bundle_key) then
3321 script_error("hertz_get_effect_bundle_from_region() called but supplied [effect_bundle_key] is not a [string]")
3322 end;
3323
3324 if effect_bundle_key == nil or effect_bundle_key == "" then
3325 script_error("hertz_get_effect_bundle_from_region() called but supplied [effect_bundle_key] is ["..tostring(effect_bundle_key).."]")
3326 end;
3327
3328 local region_se = cm:get_region(region_key);
3329 local effect_bundle_list = region_se:effect_bundles()
3330 local bundle_found = false
3331
3332 for i = 0, effect_bundle_list:num_items() - 1 do
3333 local target_effect_bundle = effect_bundle_list:item_at(i);
3334
3335 if target_effect_bundle:key() == effect_bundle_key then
3336 bundle_found = true
3337 return target_effect_bundle;
3338 end;
3339 end;
3340
3341 if not bundle_found then
3342 out("hertz_get_effect_bundle_from_region() called but supplied effect bundle key ["..effect_bundle_key.."] was not found in the region ["..region_key.."]")
3343 return false;
3344 end;
3345end;
3346
3347----------------------------------------------------------------
3348-- Returns all owned region in the supplied province_key string
3349----------------------------------------------------------------
3350function hertz_get_province_owned_regions(province_key, faction_key, return_owns_entire_province)
3351 out("Province name = " ..province_key)
3352 --out("faction = " ..faction);
3353 if return_owns_entire_province == nil then
3354 return_owns_entire_province = false;
3355 end
3356 local regions_table = {};
3357 local regions_added = false;
3358 local faction_script_interface = cm:get_faction(faction_key)
3359 local faction_region_list = faction_script_interface:region_list();
3360
3361 for i = 0, faction_region_list:num_items() - 1 do
3362 local target_region = faction_region_list:item_at(i);
3363 if target_region:province_name() == province_key then
3364 if not table_contains(regions_table, target_region) and not table_contains(region_exclusion[campaign_model], target_region:name()) and target_region:owning_faction():name() == faction_key then
3365 regions_added = true;
3366 table.insert(regions_table, target_region);
3367 out("Region inserted ["..target_region:name().."] for ["..faction_key.."]");
3368 end;
3369 end;
3370 end;
3371
3372 if regions_added then
3373 if return_owns_entire_province then
3374 if faction_script_interface:holds_entire_province(province_key, true) then
3375 --out("Regions adding complete");
3376 return regions_table;
3377 else
3378 -- --out("Regions adding failed. faction does not hold entire province.");
3379 return false
3380 end;
3381 else
3382 -- --out("Regions adding complete");
3383 return regions_table;
3384 end;
3385 else
3386 -- --out("Regions adding failed");
3387 return false
3388 end;
3389end;
3390
3391cm:add_first_tick_callback(function() hertz_province_patrols_init() end);
3392
3393-----------------------------------------------------------------
3394---------------------======Save Game======-----------------------
3395-----------------------------------------------------------------
3396cm:add_saving_game_callback(function() hertz_province_patrols_saved_game() end);
3397function hertz_province_patrols_saved_game()
3398
3399end;
3400
3401-----------------------------------------------------------------
3402---------------------======Load Game======-----------------------
3403-----------------------------------------------------------------
3404cm:add_loading_game_callback(function() hertz_province_patrols_loaded_game() end);
3405
3406function hertz_province_patrols_loaded_game()
3407 hertz_patrols_ordered = true;
3408end;
3409
3410--[[notes
3411
3412 TO DO
3413 -- Change army size picking to dynamic factions for sub-mods n shit
3414]]