Advertisement
Guest User

Untitled

a guest
Oct 16th, 2018
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 79.52 KB | None | 0 0
  1. printd = warfare.printd
  2.  
  3. hide_smarts = false
  4.  
  5. -- Timer used for capturing smart terrains.
  6. point_cap_timers = {}
  7. smart_owners = {}
  8. manual_point = {}
  9.  
  10. --[[
  11. random_patrols = {
  12. [squad.id] = {
  13. target = target_id,
  14. stay_time = time in minutes,
  15. arrive_time = game.get_game_time()
  16. }
  17. }
  18. ]]
  19. random_patrols = {}
  20.  
  21. --[[
  22. patrol_squads = {
  23. [smart.id] = {
  24. [squad.id] = {
  25. target = target_id,
  26. stay_time = time in minutes,
  27. arrive_time = game.get_game_time()
  28. }
  29. }
  30. }
  31. ]]
  32. patrol_squads = {}
  33.  
  34. invasions = {}
  35.  
  36. -- When you choose to send a squad somewhere, it will flag the smart and use up the smarts respawn time.
  37. control_hold = {}
  38.  
  39. pda_icons = {
  40. ["stalker"] = "circle_stalker",
  41. ["bandit"] = "circle_bandit",
  42. ["csky"] = "circle_csky",
  43. ["army"] = "circle_army",
  44. ["freedom"] = "circle_freedom",
  45. ["dolg"] = "circle_dolg",
  46. ["ecolog"] = "circle_ecolog",
  47. ["killer"] = "circle_killer",
  48. ["monolith"] = "circle_monolith",
  49. ["zombied"] = "circle_zombied",
  50. ["none"] = "circle_empty",
  51. ["target"] = "circle_target",
  52. }
  53.  
  54. function set_max_population(smart)
  55. local stalker_pop_factor = (axr_main.config:r_value("mm_options","alife_stalker_pop",2) or 1)
  56. local monster_pop_factor = (axr_main.config:r_value("mm_options","alife_mutant_pop",2) or 1)
  57.  
  58. if (smart.props and (smart.props.base > 0 or smart.props.resource > 0 or smart.props.territory > 0)) then
  59. smart.max_population = utils.round(math.max(1, smart.default_max_population * stalker_pop_factor))
  60. else
  61. smart.max_population = utils.round(math.max(1, smart.default_max_population * monster_pop_factor))
  62. end
  63. end
  64.  
  65. function smart_terrain_on_update(smart)
  66. printd(0, smart:name())
  67. -- avoid vanilla respawns
  68. local curr_time = game.get_game_time()
  69. smart.last_respawn_update = curr_time
  70.  
  71. -- check if level_id is set, if not set it.
  72. if not (smart.level_id) then
  73. smart.level_id = game_graph():vertex(smart.m_game_vertex_id):level_id()
  74. end
  75.  
  76. if not (smart.first_update) then
  77. smart.first_update = true
  78.  
  79. -- hackish and will result in the squatting squads, but meh.
  80. if (smart.max_population == 0) then
  81. smart.max_population = 1
  82. end
  83.  
  84. smart.default_max_population = smart.max_population
  85.  
  86. if (smart_owners[smart.id]) then
  87. smart.owning_faction = smart_owners[smart.id]
  88. smart.capping_faction = smart_owners[smart.id]
  89. end
  90.  
  91. set_max_population(smart)
  92.  
  93. smart.global_position = global_position.from(smart)
  94. end
  95.  
  96. --printf("check_owner start")
  97. check_owner(smart)
  98.  
  99. check_unique_npcs(smart)
  100.  
  101. --printf("check_owner end")
  102.  
  103. if (not warfare_options.options.disable_offline_combat) then
  104. update_offline_combat(smart)
  105. end
  106.  
  107. if not (smart.player_target_smarts) then
  108. smart.player_target_smarts = {}
  109. end
  110.  
  111. if not (smart.target_smarts) then
  112. smart.target_smarts = {}
  113. end
  114.  
  115. if not (smart.target_smart_count) then
  116. smart.target_smart_count = 0
  117. end
  118.  
  119. --update_icon_smart_terrain(smart)
  120.  
  121. if not (warfare.initialized) then
  122. printd(1, smart:name())
  123. return
  124. end
  125.  
  126. process_manual_capture(smart)
  127. process_squads(smart)
  128.  
  129. if (smart.owning_faction ~= "none" and warfare_options.options.factions[smart.owning_faction] and warfare_options.options.factions[smart.owning_faction].participate_in_warfare) then
  130. if (smart.props and smart.props.base > 0) then
  131. if (smart.owning_faction ~= "none") then
  132. update_resources(smart)
  133. spawn_defense(smart)
  134.  
  135. process_targets(smart)
  136.  
  137. if not (patrol_squads[smart.id]) then
  138. patrol_squads[smart.id] = {}
  139. end
  140.  
  141. spawn_patrols(smart)
  142. process_patrols(smart)
  143. end
  144. end
  145. else
  146. -- Non warring factions are simulated with random patrols. Random patrols will receive targets from the vanilla CoC targeting system.
  147. --spawn_random_patrols(smart)
  148. end
  149. -- Only bases will spawn squads; patrols will no longer spawn at territory points. Resource points will also not spawn any squads, making holding them a little more challenging.
  150.  
  151.  
  152. if (smart.props --[[and (smart.props.lair > 0 or smart.props.territory > 0)]] and smart.owning_faction == "none") then -- mutants will spawn on any unoccupied smarts
  153. spawn_mutants(smart)
  154.  
  155. process_mutants(smart)
  156. end
  157.  
  158. printd(2, smart:name())
  159. end
  160.  
  161. function spawn_non_warring(smart)
  162.  
  163. end
  164.  
  165. function process_non_warring(smart)
  166.  
  167. end
  168.  
  169. function spawn_random_patrols(smart)
  170. printd(0, smart:name())
  171.  
  172. if not (defense_timers[smart.id]) then
  173. update_resources(smart)
  174. local min_respawn = warfare_options.options.factions[smart.owning_faction] and warfare_options.options.factions[smart.owning_faction].min_faction_respawn or 10
  175. local max_respawn = warfare_options.options.factions[smart.owning_faction] and warfare_options.options.factions[smart.owning_faction].max_factino_respawn or 180
  176. local current_respawn = warfare.lerp(min_respawn, max_respawn, math.random())
  177. defense_next_update[smart.id] = current_respawn
  178. defense_timers[smart.id] = game.get_game_time()
  179. return
  180. elseif (game.get_game_time():diffSec(defense_timers[smart.id]) > (defense_next_update[smart.id]*60)) then
  181. update_resources(smart)
  182. local min_respawn = warfare_options.options.factions[smart.owning_faction] and warfare_options.options.factions[smart.owning_faction].min_faction_respawn or 10
  183. local max_respawn = warfare_options.options.factions[smart.owning_faction] and warfare_options.options.factions[smart.owning_faction].max_factino_respawn or 180
  184. local current_respawn = warfare.lerp(min_respawn, max_respawn, math.random())
  185. defense_next_update[smart.id] = current_respawn
  186. defense_timers[smart.id] = game.get_game_time()
  187. else
  188. return
  189. end
  190.  
  191. if (smart.props) then
  192. local factions = {}
  193.  
  194. if (smart.props.all > 0) then
  195. for i=1,smart.props.all do
  196. for k,v in pairs(warfare_factions.factions) do
  197. factions[#factions+1] = v
  198. end
  199. end
  200. end
  201.  
  202. for i,f in pairs(warfare_factions.factions) do
  203. if (smart.props[f]) then
  204. for i=1,smart.props[f]*3 do
  205. factions[#factions+1] = f
  206. end
  207. end
  208. end
  209.  
  210. warfare.shuffleTable(factions)
  211.  
  212. for _,f in pairs(factions) do
  213. printf(f)
  214. end
  215.  
  216. local chosen = factions[math.random(#factions)]
  217.  
  218. if (chosen) then
  219. --printf("faction = " .. tostring(chosen))
  220. local resource_count = math.random(warfare.resource_count)
  221. local section = faction_expansions.get_spawn_section(chosen, resource_count)
  222. local squad = alun_utils.create_squad(section, smart:name())
  223. local min_idle = warfare_options.options.factions[squad:get_squad_community()].min_patrol_idle_time
  224. local max_idle = warfare_options.options.factions[squad:get_squad_community()].max_patrol_idle_time
  225.  
  226. --printf("--- spawning random patrol at " .. smart:name() .. " ---")
  227.  
  228. random_patrols[squad.id] = {
  229. target = smart.id,
  230. stay_time = math.random(min_idle, max_idle),
  231. arrive_time = game.get_game_time()
  232. }
  233. end
  234. end
  235.  
  236. printd(1, smart:name())
  237. end
  238.  
  239. function process_manual_capture(smart)
  240. local level = game_graph():vertex(smart.m_game_vertex_id):level_id()
  241. local actor_level = game_graph():vertex(alife():actor().m_game_vertex_id):level_id()
  242.  
  243. if (warfare_options.options.enable_auto_capture) then
  244. if (level == actor_level and warfare_levels.level_information[level]) then
  245. for i,sm in pairs(warfare_levels.level_information[level].smarts) do
  246. if (manual_point == nil) then
  247. local smart = sm and alife():object(sm)
  248. if (smart and smart.props and smart.props.base > 0 and smart.owning_faction == "none") then
  249. local dist = warfare.distance_to_xz_sqr(smart.position, alife():actor().position)
  250. if (dist <= math.pow(warfare_options.options.auto_capture_max_distance, 2)) then
  251. manual_point = { smart.id, game.get_game_time(), }
  252. --log("------>DEBUG-»>"..game.translate_string(smart:name()))
  253. local warfareName= game.translate_string(smart:name())
  254. SetHudMsg("Auto capturing ".. tostring(warfareName) .. "!", 3)
  255. break
  256. end
  257. end
  258. end
  259. end
  260.  
  261. for i,sm in pairs(warfare_levels.level_information[level].smarts) do
  262. if (manual_point == nil) then
  263. local smart = sm and alife():object(sm)
  264. if (smart and smart.props and smart.props.resource > 0 and smart.owning_faction == "none") then
  265. local dist = warfare.distance_to_xz_sqr(smart.position, alife():actor().position)
  266. if (dist <= math.pow(warfare_options.options.auto_capture_max_distance, 2)) then
  267. manual_point = { smart.id, game.get_game_time(), }
  268. --log("------>DEBUG-»>"..game.translate_string(smart:name()))
  269. local warfareName= game.translate_string(smart:name())
  270. SetHudMsg("Auto capturing ".. tostring(warfareName) .. "!", 3)
  271. break
  272. end
  273. end
  274. end
  275. end
  276. end
  277. end
  278.  
  279. if (manual_point and manual_point[1] and manual_point[2]) then
  280. if (manual_point[1] == smart.id) then
  281. if (smart.owning_faction ~= "none") then
  282. manual_point = nil
  283. process_manual_capture(smart)
  284. return
  285. end
  286.  
  287. local startTime = manual_point[2]
  288.  
  289. local diff = utils.round(game.get_game_time():diffSec(startTime)/60)
  290.  
  291. if (diff > (warfare_options.options.auto_capture_wait_time)) then
  292. manual_point = nil
  293.  
  294. local targeting = squad_count(smart, warfare.actor_faction)
  295.  
  296. if (targeting < smart.max_population) then
  297. local section = faction_expansions.get_spawn_section(warfare.actor_faction, warfare_factions.faction_information[warfare.actor_faction].resource_count)
  298. local squad = alun_utils.create_squad(section, smart:name())
  299. sim_squad_warfare.set_target(squad, smart.id)
  300. --printf("/// SPAWNING DEFENSE: base=%s, squad=%s, targeting=%s ///", smart:name(), squad:name(), tostring(targeting))
  301. end
  302. else
  303. local obj = manual_point[1] and alife_object(manual_point[1])
  304.  
  305. if (obj) then
  306. local dist = warfare.distance_to_xz_sqr(obj.position, alife():actor().position)
  307.  
  308. if (dist > math.pow(warfare_options.options.auto_capture_max_distance, 2)) then
  309. manual_point = nil
  310. end
  311. end
  312. end
  313. end
  314. end
  315. end
  316.  
  317. function process_squads(smart)
  318. if not (smart.process_squads_time) or (game.get_game_time():diffSec(smart.process_squads_time) >= smart.next_process_squads_time*60) then
  319. smart.process_squads_time = game.get_game_time()
  320. smart.next_process_squads_time = math.random(1, 5)
  321. else
  322. return
  323. end
  324.  
  325. set_max_population(smart)
  326.  
  327. printd(0, smart:name())
  328.  
  329. local squads = SIMBOARD.smarts[smart.id].squads
  330.  
  331. for sid,_ in pairs(squads) do
  332. local squad = sid and alife_object(sid)
  333.  
  334. if (squad) then
  335. local faction = squad:get_squad_community()
  336.  
  337. if (squad.registered_with_warfare and squad.current_action == 1) then
  338. if not (warfare.ignore[squad:section_name()]) then
  339. local removeSquad = false
  340. local squadCount = squad_count_defending(smart, faction)
  341.  
  342. if (faction ~= smart.owning_faction and not game_relations.is_factions_enemies(faction, smart.owning_faction)) then
  343. if not (smart.owning_faction == "none" and faction == "monster") then
  344. --printf("!!! REMOVE NON-ENEMY IDLE SQUAD. " .. squad:section_name() .. " at " .. smart:name() .. " !!!")
  345. removeSquad = true
  346. end
  347. end
  348.  
  349. local size = invasions[smart.id] and invasions[smart.id][2] or 0
  350. local totalSize = size + smart.max_population
  351.  
  352. if (squadCount > totalSize) then
  353. --printf("!!! REMOVE OVERFLOW SQUAD. " .. squad:section_name() .. " at " .. smart:name() .. " - " .. tostring(squadCount) .. " | " .. tostring(smart.max_population) .. " !!!")
  354. removeSquad = true
  355. end
  356.  
  357. if (removeSquad and not warfare_options.options.disable_smart_pop_cap) then
  358. --printf("!!! REMOVING SQUAD (overflow)" .. squad:name())
  359. sim_squad_warfare.remove_squad(squad)
  360. end
  361. end
  362. end
  363. end
  364. end
  365.  
  366. printd(1, smart:name())
  367. end
  368.  
  369. -- TODO: switch over to the warfare_options.script values
  370. MAX_SMART_TARGETS = 2
  371. MAX_SQUADS_TARGETING_SMART = 3
  372.  
  373. function process_targets(smart)
  374. --printf(smart:name() .. "- 00")
  375. local level_id = game_graph():vertex(smart.m_game_vertex_id):level_id()
  376.  
  377. if not (smart.process_targets_time) or (game.get_game_time():diffSec(smart.process_targets_time) >= smart.next_process_targets_time*60) then
  378. smart.process_targets_time = game.get_game_time()
  379. smart.next_process_targets_time = math.random(1, 3)
  380. else
  381. return
  382. end
  383.  
  384. printd(0, smart:name())
  385.  
  386. local fo = warfare_options.options.factions[smart.owning_faction]
  387.  
  388. if not (fo and fo.participate_in_warfare) then
  389. printd(1, smart:name())
  390. return
  391. end
  392.  
  393. -- Make sure all targets are still valid and count them if they are (can't use # operator on hash tables)
  394. local target_smart_count = 0
  395. for target,v in pairs(smart.player_target_smarts) do
  396. local other = alife():object(target)
  397.  
  398. if (other and other.owning_faction) then
  399. if (other.owning_faction ~= "none" and not game_relations.is_factions_enemies(smart.owning_faction, other.owning_faction)) then
  400. smart.player_target_smarts[other.id] = nil
  401. else
  402. target_smart_count = target_smart_count + 1
  403. end
  404. end
  405. end
  406.  
  407. -- Make sure all targets are still valid and count them if they are (can't use # operator on hash tables)
  408. for target,v in pairs(smart.target_smarts) do
  409. local other = alife():object(target)
  410.  
  411. if (other and other.owning_faction) then
  412. if (other.owning_faction ~= "none" and not game_relations.is_factions_enemies(smart.owning_faction, other.owning_faction)) then
  413. smart.target_smarts[other.id] = nil
  414. else
  415. target_smart_count = target_smart_count + 1
  416. end
  417. end
  418. end
  419.  
  420. -- Make sure invasion is valid, and if so and all squads are spawned, send them out.
  421. if (invasions[smart.id]) then
  422. local current = smart.defense_count
  423. local size = invasions[smart.id][2]
  424. local totalSize = invasions[smart.id][2] + math.max(1, smart.max_population*0.5)
  425.  
  426. local targetID = invasions[smart.id][1]
  427. local target = targetID and alife():object(targetID)
  428.  
  429. if (target.owning_faction ~= "none" and not game_relations.is_factions_enemies(smart.owning_faction, target.owning_faction)) then
  430. target = get_invasion_target(smart)
  431.  
  432. if (target) then
  433. local targetPopulation = math.max(1, target.max_population * (warfare_factions.faction_information[smart.owning_faction].invasion_size))
  434. invasions[smart.id] = { target.id, math.max(1, utils.round(targetPopulation)), }
  435. else
  436. invasions[smart.id] = nil
  437. end
  438. end
  439.  
  440. if (target) then
  441. if (current >= totalSize) then
  442. local departure_chance = warfare_options.options.factions[smart.owning_faction].invasion_departure_chance
  443. local night_chance = warfare_options.options.factions[smart.owning_faction].night_activity_chance
  444. local isNight = (level.get_time_hours() < 6 or level.get_time_hours() > 20)
  445.  
  446. if (isNight and math.random(100) <= night_chance or not isNight) then
  447. if (math.random(100) <= departure_chance) then
  448. local numSent = 0
  449. local squads = SIMBOARD.smarts[smart.id].squads
  450.  
  451. for sid,_ in pairs(squads) do
  452. local squad = sid and alife_object(sid)
  453.  
  454. if (squad) then
  455. if (squad.registered_with_warfare and squad:get_squad_community() == smart.owning_faction and squad.current_action == 1) then
  456. numSent = numSent + 1
  457. sim_squad_warfare.set_target(squad, target.id)
  458.  
  459. if (numSent == size) then
  460. break
  461. end
  462. end
  463. end
  464. end
  465.  
  466. invasions[smart.id] = nil
  467. end
  468. end
  469. end
  470. end
  471. end
  472.  
  473. local aggression = warfare_options.options.factions[smart.owning_faction].expansion_aggression
  474.  
  475. -- If the random number is less than or equal to the aggression for the faction, choose a new target to consider for invasion.
  476. if (math.random(100) <= aggression) then
  477. if ((pda_actor.manual_control and smart.owning_faction ~= warfare.actor_faction) or (not pda_actor.manual_control)) then
  478. local targets = find_targets(smart)
  479.  
  480. if (#targets > 0) then
  481. for i=1,#targets do
  482. if (target_smart_count < warfare_options.options.factions[smart.owning_faction].max_smart_targets_per_base) then
  483. target_smart_count = target_smart_count + 1
  484. smart.target_smarts[targets[i][2]] = true
  485.  
  486. local o = targets[i][2] and alife_object(targets[i][2])
  487. else
  488. break
  489. end
  490. end
  491. end
  492. end
  493. end
  494.  
  495. -- Choose a new invasion target if needed from the list of chosen targets.
  496. if not (invasions[smart.id]) then
  497. local target = get_invasion_target(smart)
  498.  
  499. if (target) then
  500. local targetPopulation = math.max(1, target.max_population * (warfare_factions.faction_information[smart.owning_faction].invasion_size))
  501. invasions[smart.id] = { target.id, math.max(1, utils.round(targetPopulation)), }
  502. end
  503. end
  504.  
  505. printd(2, smart:name())
  506. end
  507.  
  508. function get_invasion_target(smart)
  509. local target
  510. local fi = warfare_factions.faction_information[smart.owning_faction]
  511.  
  512. for t,v in pairs(smart.player_target_smarts) do
  513. local other = alife():object(t)
  514.  
  515. if other then
  516. local numTargeting = squad_count(other, smart.owning_faction)
  517.  
  518. if (numTargeting < warfare_options.options.factions[smart.owning_faction].max_active_squads_per_target and numTargeting < smart.max_population) then
  519. target = other
  520. --printf("target=%s, num=%s", target:name(), tostring(numTargeting))
  521. break
  522. else
  523. --printf("INVALID - %s - target=%s, num=%s", tostring(smart.owning_faction), other:name(), tostring(numTargeting))
  524. end
  525. end
  526. end
  527.  
  528. if not (target) then
  529. for t,v in pairs(smart.target_smarts) do
  530. local other = alife():object(t)
  531.  
  532. if other then
  533. local numTargeting = squad_count(other, smart.owning_faction)
  534.  
  535. local info = warfare_levels.level_information[smart.level_id]
  536. local neighbors = info.smarts
  537.  
  538. local total_power_targeting = 0
  539.  
  540. for i,sm in pairs(neighbors) do
  541. if (sm and invasions[sm]) then
  542. if (invasions[sm][1] == t) then
  543. local neighbor = alife():object(sm)
  544. total_power_targeting = total_power_targeting + neighbor.max_population
  545. end
  546. end
  547. end
  548.  
  549. if (total_power_targeting < other.max_population) then
  550. if squad_count(other, smart.owning_faction) < utils.round((warfare_options.options.factions[smart.owning_faction].max_active_squads_per_target*0.25)+0.5) then
  551. target = other
  552. --printf("target=%s, num=%s", target:name(), tostring(numTargeting))
  553. break
  554. else
  555. --printf("INVALID - %s - target=%s, num=%s", tostring(smart.owning_faction), other:name(), tostring(numTargeting))
  556. end
  557. else
  558. --printf("INVALID - %s - target = %s, total population targeting = %s", tostring(smart.owning_faction), other:name(), tostring(total_power_targeting))
  559. end
  560. end
  561. end
  562. end
  563.  
  564. return target
  565. end
  566.  
  567. defense_timers = {}
  568. defense_next_update = {}
  569. patrol_timers = {}
  570. patrol_next_update = {}
  571.  
  572. function update_resources(smart)
  573. if not (smart.resource_timer) or game.get_game_time():diffSec(smart.resource_timer) > (smart.resource_next_update*60) then
  574. smart.resource_timer = game.get_game_time()
  575. smart.resource_next_update = 5
  576. else
  577. return
  578. end
  579.  
  580. if (warfare_factions.faction_information and warfare_factions.faction_information[smart.owning_faction]) then
  581. local global_resources = warfare_factions.faction_information[smart.owning_faction].resource_count
  582. local levelID = game_graph():vertex(smart.m_game_vertex_id):level_id()
  583. local modifier = warfare_options.options.factions[smart.owning_faction].resource_count_modifier
  584. local regional_resources = warfare_levels.level_information[levelID] and warfare_levels.level_information[levelID].faction_stats and warfare_levels.level_information[levelID].faction_stats[smart.owning_faction] and warfare_levels.level_information[levelID].faction_stats[smart.owning_faction].linked_resource_count or 0
  585. local local_resources = warfare_levels.level_information[levelID] and warfare_levels.level_information[levelID].faction_stats and warfare_levels.level_information[levelID].faction_stats[smart.owning_faction] and warfare_levels.level_information[levelID].faction_stats[smart.owning_faction].resource_count or 0
  586. local total_resources = global_resources + (regional_resources * 2) + (local_resources * 3)
  587. smart.resource_count = total_resources + modifier
  588. else
  589. smart.resource_count = 0
  590. warfare_factions.update_faction(smart.owning_faction)
  591. end
  592. end
  593.  
  594. function spawn_defense(smart)
  595. if not (defense_timers[smart.id]) then
  596. local min_respawn = warfare_options.options.factions[smart.owning_faction].min_faction_respawn or 10
  597. local max_respawn = warfare_options.options.factions[smart.owning_faction].max_factino_respawn or 60;
  598. local current_respawn = warfare.lerp(min_respawn, max_respawn, math.clamp(smart.resource_count / warfare.resource_count, 0, 1))
  599. defense_next_update[smart.id] = current_respawn
  600. defense_timers[smart.id] = game.get_game_time()
  601. return
  602. elseif (game.get_game_time():diffSec(defense_timers[smart.id]) > (defense_next_update[smart.id]*60)) then
  603. local min_respawn = warfare_options.options.factions[smart.owning_faction].min_faction_respawn or 10
  604. local max_respawn = warfare_options.options.factions[smart.owning_faction].max_factino_respawn or 60;
  605. local current_respawn = warfare.lerp(min_respawn, max_respawn, math.clamp(smart.resource_count / warfare.resource_count, 0, 1))
  606. defense_next_update[smart.id] = current_respawn
  607. defense_timers[smart.id] = game.get_game_time()
  608. else
  609. return
  610. end
  611.  
  612. printd(0, smart:name())
  613.  
  614. if (game_relations.is_factions_enemies(smart.owning_faction, warfare.actor_faction) and smart.is_on_actor_level and smart.dist_to_actor) then
  615. if (smart.dist_to_actor < smart.respawn_radius) then
  616. printd(1, smart:name())
  617. return
  618. end
  619. end
  620.  
  621.  
  622. local current = squad_count(smart, smart.owning_faction)
  623. local size = invasions[smart.id] and invasions[smart.id][2] or 0
  624. local totalSize = size + smart.max_population
  625.  
  626. if (current < totalSize) then
  627. local section = faction_expansions.get_spawn_section(smart.owning_faction, smart.resource_count)
  628. local squad = alun_utils.create_squad(section, smart:name())
  629. sim_squad_warfare.set_target(squad, smart.id)
  630. end
  631.  
  632. printd(2, smart:name())
  633. end
  634.  
  635. function process_defense(smart)
  636. -- TODO: interpolate between min and max respawn times (make two different sets of variables for defense and offense?)
  637. if not (smart.process_defense_timer) or (game.get_game_time():diffSec(smart.process_defense_timer) > (smart.next_process_defense_update*60)) then
  638. smart.process_defense_timer = game.get_game_time()
  639. smart.next_process_defense_update = math.random(2, 10)
  640. else
  641. return
  642. end
  643. end
  644.  
  645. function spawn_patrols(smart)
  646. printd(0, smart:name())
  647.  
  648. if not (patrol_timers[smart.id]) then
  649. patrol_timers[smart.id] = game.get_game_time()
  650. patrol_next_update[smart.id] = 10
  651.  
  652. if (smart.owning_faction ~= "none") then
  653. local ff = warfare_options.options.factions[smart.owning_faction]
  654. local respawn
  655.  
  656. if (ff) then
  657. respawn = warfare.lerp(ff.max_patrol_time, ff.min_patrol_time, math.clamp(smart.resource_count / warfare.resource_count, 0, 1))
  658. else
  659. respawn = 10
  660. end
  661.  
  662. patrol_next_update[smart.id] = respawn
  663. end
  664.  
  665. return
  666. elseif (game.get_game_time():diffSec(patrol_timers[smart.id]) > (patrol_next_update[smart.id]*60)) then
  667. patrol_timers[smart.id] = game.get_game_time()
  668. patrol_next_update[smart.id] = 10
  669.  
  670. if (smart.owning_faction ~= "none") then
  671. local ff = warfare_options.options.factions[smart.owning_faction]
  672. local respawn
  673.  
  674. if (ff) then
  675. respawn = warfare.lerp(ff.max_patrol_time, ff.min_patrol_time, math.clamp(smart.resource_count / warfare.resource_count, 0, 1))
  676. else
  677. respawn = 10
  678. end
  679.  
  680. patrol_next_update[smart.id] = respawn
  681. end
  682. else
  683. return
  684. end
  685.  
  686. local squads = patrol_squads[smart.id]
  687.  
  688. local count = 0
  689. for squad_id, sobj in pairs(squads) do
  690. count = count + 1
  691. end
  692.  
  693. local patrol_pop_factor = warfare_factions.faction_information[smart.owning_faction].patrol_pop_factor
  694.  
  695. if (count < (smart.max_population * patrol_pop_factor)) then
  696. local level_id = smart.level_id
  697. local linked_levels = level_targets.level_links[level_id]
  698. local level_smarts = warfare_levels.level_information[level_id].smarts
  699. local smarts_by_distance = {}
  700.  
  701. for _,smart_id in pairs(level_smarts) do
  702. local other = smart_id and alife_object(smart_id)
  703.  
  704. if (other) then
  705. if (other.props and (other.props.territory > 0 and other.props.resource == 0)) then
  706. local d = warfare.distance_to_xz_sqr(global_position.from(smart), global_position.from(other))
  707. smarts_by_distance[#smarts_by_distance + 1] = { other.id, d }
  708. end
  709. end
  710. end
  711.  
  712. table.sort(smarts_by_distance, function(a, b)
  713. return a[2] < b[2]
  714. end)
  715.  
  716. for _,tbl in pairs(smarts_by_distance) do
  717. local other = tbl[1] and alife_object(tbl[1])
  718.  
  719. local targeted = false
  720.  
  721. if not (squads) then
  722. patrol_squads[smart.id] = {}
  723. else
  724. for squad_id, sobj in pairs(squads) do
  725. local other_squad = squad_id and alife_object(squad_id)
  726.  
  727. if (other_squad) then
  728. if (other_squad:clsid() == clsid.online_offline_group_s) then
  729. if (other_squad:get_squad_community() == smart.owning_faction) then
  730. if (sobj.target == other.id) then
  731. targeted = true
  732. break
  733. end
  734. end
  735. else
  736. printf("THIS IS NOT A PATROL SQUAD DAMN IT. %s", other_squad:name())
  737. patrol_squads[smart.id][squad_id] = nil
  738. end
  739. end
  740. end
  741. end
  742.  
  743. if not (targeted) then
  744. local section = faction_expansions.get_spawn_section(smart.owning_faction, smart.resource_count)
  745. local squad = alun_utils.create_squad(section, smart:name())
  746.  
  747. local night_chance = warfare_options.options.factions[smart.owning_faction].night_activity_chance
  748. local isNight = (level.get_time_hours() < 6 or level.get_time_hours() > 20)
  749.  
  750. local t = nil
  751. if (isNight and math.random(100) <= night_chance or not isNight) then
  752. t = other
  753. else
  754. t = smart
  755. end
  756.  
  757. sim_squad_warfare.set_target(squad, t.id)
  758.  
  759. patrol_squads[smart.id][squad.id] = {
  760. target = t.id,
  761. stay_time = nil,
  762. arrive_time = nil
  763. }
  764.  
  765. squad.patrol_table = patrol_squads[smart.id][squad.id]
  766. squad.patrol_owner = smart.id
  767.  
  768. local count = 0
  769. for squad_id, sobj in pairs(squads) do
  770. count = count + 1
  771. end
  772.  
  773. printd(3, smart:name())
  774. return
  775. end
  776. end
  777. end
  778.  
  779. local count = 0
  780. for squad_id, sobj in pairs(squads) do
  781. count = count + 1
  782. end
  783. end
  784.  
  785. function process_patrols(smart)
  786. -- TODO: interpolate between min and max respawn times (make two different sets of variables for defense and offense?)
  787. if not (smart.process_patrols_timer) then
  788. smart.process_patrols_timer = game.get_game_time()
  789. smart.next_process_patrols_update = math.random(2, 10)
  790. return
  791. end
  792.  
  793. if (game.get_game_time():diffSec(smart.process_patrols_timer) > (smart.next_process_patrols_update*60)) then
  794. smart.process_patrols_timer = game.get_game_time()
  795. smart.next_process_patrols_update = math.random(2, 10)
  796. else
  797. return
  798. end
  799.  
  800. local squads = patrol_squads[smart.id]
  801.  
  802. local count = 0
  803. for squad_id, sobj in pairs(squads) do
  804. count = count + 1
  805. end
  806.  
  807. for squad_id, sobj in pairs(squads) do
  808. local squad = squad_id and alife_object(squad_id)
  809.  
  810. if (squad and squad:clsid() == clsid.online_offline_group_s) then
  811. squad.patrol_table = patrol_squads[smart.id][squad_id]
  812. squad.owning_smart = smart.id
  813.  
  814. if not (squad.clsid and squad:clsid() == clsid.online_offline_group_s) then
  815. patrol_squads[smart.id][squad_id] = nil
  816. elseif (squad:npc_count() == 0) then
  817. patrol_squads[smart.id][squad_id] = nil
  818. elseif not (squad.get_squad_community and squad:get_squad_community() == smart.owning_faction) then
  819. patrol_squads[smart.id][squad_id] = nil
  820. else
  821. local find_target = false
  822. local other = sobj.target and alife_object(sobj.target)
  823.  
  824. if not (other and squad.current_target_id) then
  825. find_target = true
  826. end
  827.  
  828. if (sobj.attacking) then
  829. if (other and other:clsid() == clsid.online_offline_group_s) then
  830. if not (game_relations.is_factions_enemies(other:get_squad_community(), squad:get_squad_community())) then
  831. find_target = true
  832. else
  833. local lvl = game_graph():vertex(squad.m_game_vertex_id):level_id()
  834. local other_lvl = game_graph():vertex(other.m_game_vertex_id):level_id()
  835.  
  836. if (lvl ~= other_lvl) then
  837. find_target = true
  838. end
  839. end
  840. else
  841. find_target = true
  842. end
  843. end
  844.  
  845. if (find_target or squad.current_action == 1) then
  846. if (not find_target and not (sobj.attacking or sobj.arrive_time)) then
  847. patrol_squads[smart.id][squad_id].arrive_time = game.get_game_time()
  848. local min_idle = warfare_options.options.factions[squad:get_squad_community()].min_patrol_idle_time
  849. local max_idle = warfare_options.options.factions[squad:get_squad_community()].max_patrol_idle_time
  850. patrol_squads[smart.id][squad_id].stay_time = math.random(min_idle, max_idle)
  851. elseif (find_target or game.get_game_time():diffSec(sobj.arrive_time) >= (sobj.stay_time*60)) then
  852. local night_chance = warfare_options.options.factions[smart.owning_faction].night_activity_chance
  853. local isNight = (level.get_time_hours() < 6 or level.get_time_hours() > 20)
  854.  
  855. if (isNight and math.random(100) <= night_chance or not isNight) then
  856. local level_id = smart.level_id
  857. local linked_levels = level_targets.level_links[level_id]
  858. local level_smarts = warfare_levels.level_information[level_id].smarts
  859. local found_target = false
  860. local smarts_by_distance = {}
  861.  
  862. local target_squads = {}
  863.  
  864. for _,other_id in pairs(level_smarts) do
  865. local squads2 = patrol_squads[other_id]
  866.  
  867. if (squads2) then
  868. for patrol_id,obj in pairs(squads2) do
  869. local other_patrol = patrol_id and alife_object(patrol_id)
  870.  
  871. if (other_patrol) then
  872. if (other_patrol:clsid() == clsid.online_offline_group_s) then
  873. if (game_relations.is_factions_enemies(other_patrol:get_squad_community(), squad:get_squad_community())) then
  874. target_squads[#target_squads+1] = other_patrol
  875. end
  876. end
  877. end
  878. end
  879. end
  880. end
  881.  
  882. for other_level,_ in pairs(linked_levels) do
  883. local other_smarts = warfare_levels.level_information[other_level].smarts
  884.  
  885. if (other_smarts) then
  886. for _,other_id in pairs(other_smarts) do
  887. local squads2 = patrol_squads[other_id]
  888.  
  889. if (squads2) then
  890. for patrol_id,obj in pairs(squads2) do
  891. local other_patrol = patrol_id and alife_object(patrol_id)
  892.  
  893. if (other_patrol) then
  894. if (other_patrol:clsid() == clsid.online_offline_group_s) then
  895. if (game_relations.is_factions_enemies(other_patrol:get_squad_community(), squad:get_squad_community())) then
  896. target_squads[#target_squads+1] = other_patrol
  897. end
  898. end
  899. end
  900. end
  901. end
  902. end
  903. end
  904. end
  905.  
  906. local hunt_chance = warfare_options.options.factions[smart.owning_faction].patrol_hunt_chance
  907.  
  908. if (#target_squads > 0 and hunt_chance >= math.random(100)) then
  909. local other = target_squads[math.random(#target_squads)]
  910.  
  911. if (other) then
  912. found_target = true
  913. sim_squad_warfare.set_target(squad, other.id)
  914.  
  915. printf("!!! sending patrol " .. squad:name() .. " to attack " .. other:name())
  916.  
  917. patrol_squads[smart.id][squad.id] = {
  918. target = other.id,
  919. attacking = true,
  920. stay_time = nil,
  921. arrive_time = nil
  922. }
  923. end
  924. end
  925.  
  926. if not (found_target) then
  927. for _,other_id in pairs(level_smarts) do
  928. local other = other_id and alife_object(other_id)
  929.  
  930. if (other) then
  931. if (other.props and (other.props.territory > 0 and other.props.resource == 0)) then
  932. local dist = warfare.distance_to_xz_sqr(global_position.from(smart), global_position.from(other))
  933. smarts_by_distance[#smarts_by_distance + 1] = { other.id, dist }
  934. end
  935. end
  936. end
  937.  
  938. for other_level,_ in pairs(linked_levels) do
  939. local other_smarts = warfare_levels.level_information[other_level].smarts
  940.  
  941. if (other_smarts) then
  942. for _,other_id in pairs(other_smarts) do
  943. local other = other_id and alife_object(other_id)
  944.  
  945. if (other and other.props and (other.props.territory > 0 and other.props.resource == 0)) then
  946. local dist = warfare.distance_to_xz_sqr(global_position.from(smart), global_position.from(other))
  947. smarts_by_distance[#smarts_by_distance + 1] = { other.id, dist }
  948. end
  949. end
  950. end
  951. end
  952.  
  953. table.sort(smarts_by_distance, function(a, b)
  954. return a[2] < b[2]
  955. end)
  956.  
  957. for _,tbl in pairs(smarts_by_distance) do
  958. if (tbl ~= nil) then
  959. local other = tbl[1] and alife_object(tbl[1])
  960. local targeted = false
  961.  
  962. if (other.id == sobj.target) then
  963. targeted = true
  964. end
  965.  
  966. for squad2_id, sobj2 in pairs(squads) do
  967. if (sobj2 and sobj2.target == other.id) then
  968. targeted = true
  969. break
  970. end
  971. end
  972.  
  973. if not (targeted) then
  974. sim_squad_warfare.set_target(squad, other.id)
  975.  
  976. patrol_squads[smart.id][squad.id] = {
  977. target = other.id,
  978. stay_time = nil,
  979. arrive_time = nil
  980. }
  981.  
  982. printf("!!! sending patrol " .. squad:name() .. " to attack " .. other:name())
  983.  
  984. squad.patrol_table = patrol_squads[smart.id][squad.id]
  985. squad.patrol_owner = smart.id
  986.  
  987. break
  988. end
  989. end
  990. end
  991. end
  992. end
  993. end
  994. end
  995. end
  996. end
  997. end
  998.  
  999. local count = 0
  1000. for squad_id, sobj in pairs(squads) do
  1001. count = count + 1
  1002. end
  1003. end
  1004.  
  1005. function spawn_mutants(smart)
  1006. if not (smart.spawn_mutants_update) then
  1007. smart.spawn_mutants_update = game.get_game_time()
  1008. local min_respawn = warfare_options.options.monster_min_faction_respawn
  1009. local max_respawn = warfare_options.options.monster_max_faction_respawn
  1010. smart.next_spawn_mutants_update = math.random(min_respawn, max_respawn)
  1011. return
  1012. end
  1013.  
  1014. if (game.get_game_time():diffSec(smart.spawn_mutants_update) >= (smart.next_spawn_mutants_update*60)) then
  1015. smart.spawn_mutants_update = game.get_game_time()
  1016. local min_respawn = warfare_options.options.monster_min_faction_respawn
  1017. local max_respawn = warfare_options.options.monster_max_faction_respawn
  1018. smart.next_spawn_mutants_update = math.random(min_respawn, max_respawn)
  1019. else
  1020. return
  1021. end
  1022.  
  1023. printd(0, smart:name())
  1024.  
  1025. if (smart.is_on_actor_level and smart.dist_to_actor ~= nil) then
  1026. if (smart.dist_to_actor < smart.respawn_radius) then
  1027. printd(1, smart:name())
  1028. return
  1029. end
  1030. end
  1031.  
  1032. local max_active = warfare_options.options.monster_max_squads_per_level
  1033. --printf("levelid: " .. tostring(smart.level_id))
  1034. local mutants_on_level = offline_combat_simulator.get_num_squads_on_level(smart.level_id, "monster")
  1035. local mutants = squad_count(smart, "monster")
  1036.  
  1037. if (mutants_on_level < max_active) then
  1038. --printf("mutants_on_level=%s, max_active=%s", mutants_on_level, max_active)
  1039.  
  1040. if (mutants < smart.max_population) then
  1041. --printf("mutants=%s, max_population=%s", mutants, smart.max_population)
  1042.  
  1043. local section = faction_expansions.get_spawn_section("monster", math.random(warfare.resource_count))
  1044. local squad = alun_utils.create_squad(section, smart:name())
  1045. sim_squad_warfare.set_target(squad, smart.id)
  1046. end
  1047. end
  1048.  
  1049. printd(2, smart:name())
  1050. end
  1051.  
  1052. -- Will be used for handling mutant behavior, choosing new targets etc.
  1053. function process_mutants(smart)
  1054. if not (smart.process_mutants_update) or (game.get_game_time():diffSec(smart.process_mutants_update) >= (smart.next_process_mutants_update*60)) then
  1055. smart.process_mutants_update = game.get_game_time()
  1056. local min_respawn = warfare_options.options.monster_min_faction_respawn
  1057. local max_respawn = warfare_options.options.monster_max_faction_respawn
  1058. smart.next_process_mutants_update = math.random(min_respawn, max_respawn)
  1059. else
  1060. return
  1061. end
  1062.  
  1063. printd(0, smart:name())
  1064.  
  1065. local squads = SIMBOARD.smarts[smart.id].squads
  1066.  
  1067. for sid,_ in pairs(squads) do
  1068. local squad = sid and alife_object(sid)
  1069.  
  1070. if (squad) then
  1071. if (squad.current_action == 1) then
  1072. if not (squad.wait_time) then
  1073. squad.wait_time = math.random(10, 60)
  1074. end
  1075. end
  1076.  
  1077. if (squad.arrive_time and squad.wait_time) then
  1078. if (game.get_game_time():diffSec(squad.arrive_time) >= squad.wait_time) then
  1079. local max_active = warfare_options.options.monster_max_squads_per_level
  1080.  
  1081. -- Just for now, 1 in 10 chance of moving off level.
  1082. -- TODO: Make this an option that can be tweaked
  1083. if (math.random(1, 10) > 9) then
  1084. local linked = level_targets.level_links[smart.level_id]
  1085. local keys = warfare.hash_table_to_array(linked)
  1086. local chosen = keys[math.random(#keys)].key
  1087. local level_info = warfare_levels.level_information[chosen]
  1088. local mutants_on_level = offline_combat_simulator.get_num_squads_on_level(chosen, "monster")
  1089.  
  1090. if (mutants_on_level < max_active) then
  1091. for i,sm in pairs(level_info.smarts) do
  1092. local other = sm and alife():object(sm)
  1093.  
  1094. if (other and other.props and other.props.lair > 0) then
  1095. local num_targeting = squad_count_targeting(other, "monster")
  1096.  
  1097. if (num_targeting < other.max_population) then
  1098. sim_squad_warfare.set_target(squad, sm)
  1099. break
  1100. end
  1101. end
  1102. end
  1103. end
  1104. else
  1105. local level_info = warfare_levels.level_information[smart.level_id]
  1106. local mutants_on_level = offline_combat_simulator.get_num_squads_on_level(smart.level_id, "monster")
  1107.  
  1108. if (mutants_on_level < max_active) then
  1109. for i,sm in pairs(level_info.smarts) do
  1110. local other = sm and alife():object(sm)
  1111.  
  1112. if (other and other.props and other.props.lair > 0) then
  1113. local num_targeting = squad_count_targeting(other, "monster")
  1114.  
  1115. if (num_targeting < other.max_population) then
  1116. sim_squad_warfare.set_target(squad, sm)
  1117. break
  1118. end
  1119. end
  1120. end
  1121. end
  1122. end
  1123. end
  1124. end
  1125. end
  1126. end
  1127.  
  1128. printd(1, smart:name())
  1129. end
  1130.  
  1131. -- Weights used for prioritization
  1132.  
  1133. BASE_WEIGHT = 2 -- if target is a base, add (base prop * N) to priority
  1134. RESOURCE_WEIGHT = 1 -- if target is a resource, add (resource prop * N) to priority
  1135. FACTION_WEIGHT = 1 -- if target is flagged for faction, add (faction prop * N) to priority
  1136. IS_BEING_TARGETED_WEIGHT = 3 -- If target is currently targeting you, add N to priority
  1137. INFLUENCE_WEIGHT = 1 -- If user has flagged smart, add (points*options.actor_influence_weight) to priority
  1138. TARGET_WEAKER_THAN_TARGETING_WEIGHT = 1 -- If target is weaker than current smart, modify influence
  1139. TARGET_STRONGER_THAN_TARGETING_WEIGHT = -1 -- If target is stronger than current smart, modify influence
  1140.  
  1141. function get_faction_power(smart, faction)
  1142. local squads = SIMBOARD.smarts[smart.id].squads
  1143. local power = 0
  1144.  
  1145. for sid,_ in pairs(squads) do
  1146. local squad = sid and alife_object(sid)
  1147.  
  1148. if (squad) then
  1149. if (squad.current_action == 1 and squad.registered_with_warfare) then
  1150. if (squad:get_squad_community() == faction) then
  1151. power = power + offline_combat_simulator.ocs_power_table[squad.id]
  1152. end
  1153. end
  1154. end
  1155. end
  1156.  
  1157. return power
  1158. end
  1159.  
  1160. --[[
  1161. function find_target(smart, faction_override)
  1162. printd(0, smart:name())
  1163.  
  1164. local level_info = warfare_levels.level_information[smart.level_id]
  1165. local faction = smart.owning_faction or "none"
  1166.  
  1167. if (faction_override) then
  1168. faction = faction_override
  1169. end
  1170.  
  1171. if (level_info and level_info.faction_stats and level_info.faction_stats[faction]) then
  1172. local target_level = level_info.faction_stats[faction].target_level
  1173. local target_info = warfare_levels.level_information[target_level]
  1174.  
  1175. local smarts
  1176. if (level_info.faction_stats[faction].faction_war or (level_info.faction_stats["none"].base_count > 0 or level_info.faction_stats["none"].resource_count > 0)) then
  1177. smarts = level_info.smarts
  1178. elseif (target_level and target_info.faction_stats) then
  1179. smarts = target_info.smarts
  1180. end
  1181.  
  1182. if (smarts) then
  1183. local targets = {}
  1184.  
  1185. for i,sm in pairs(smarts) do
  1186. local other = sm and alife():object(sm)
  1187.  
  1188. -- Don't target territory points unless they are a resource.
  1189. if (other and other.props and (other.props.base > 0 or other.props.resource > 0)) then
  1190. local dist = warfare.distance_to_xz_sqr(smart.global_position, other.global_position)
  1191. local priority = 1
  1192. local f2 = other.owning_faction or "none"
  1193. local d2 = other.defense_power or 0
  1194.  
  1195. local foundNeutralOrFriend = false
  1196.  
  1197. local squads = SIMBOARD.smarts[other.id].squads
  1198.  
  1199. for sid,_ in pairs(squads) do
  1200. local squad = sid and alife_object(sid)
  1201.  
  1202. if (squad) then
  1203. if (squad.registered_with_warfare and squad:get_squad_community() ~= faction and not game_relations.is_factions_enemies(squad:get_squad_community(), faction)) then
  1204. foundNeutralOrFriend = true
  1205. end
  1206. end
  1207. end
  1208.  
  1209. if (warfare.influence_levels[smart.id]) then
  1210. priority = priority + (warfare.influence_levels[smart.id] * warfare_options.options.actor_influence_weight)
  1211. end
  1212.  
  1213. if (other.props) then
  1214. if (other.props[faction] > 0) then
  1215. priority = priority + (other.props[faction] * warfare_options.options.factions[faction].faction_flag_priority)
  1216. end
  1217.  
  1218. if (other.props.base > 0) then
  1219. priority = priority + (other.props.base * warfare_options.options.factions[faction].base_priority)
  1220. end
  1221.  
  1222. -- Consider editing sim_objects_props.ltx to have resource > 1; will make some points more valuable.
  1223. if (other.props.resource > 0) then
  1224. priority = priority + (other.props.resource * warfare_options.options.factions[faction].resource_priority)
  1225. end
  1226. end
  1227.  
  1228. if (game_relations.is_factions_enemies(faction, f2)) then
  1229. local diff = smart.defense_power - d2
  1230.  
  1231. if (diff > 0) then
  1232. priority = priority + warfare_options.options.factions[faction].target_weaker_priority
  1233. else
  1234. priority = priority + warfare_options.options.factions[faction].target_stronger_priority
  1235. end
  1236. end
  1237.  
  1238. if (other.target_smarts) then
  1239. if (other.target_smarts[smart.id]) then
  1240. priority = priority + warfare_options.options.factions[faction].is_being_targeted_priority
  1241. end
  1242. end
  1243.  
  1244. if (other.player_target_smarts) then
  1245. if (other.player_target_smarts[smart.id]) then
  1246. priority = priority + warfare_options.options.factions[faction].is_being_targeted_priority
  1247. end
  1248. end
  1249.  
  1250. if (not foundNeutralOrFriend and not smart.target_smarts[other.id] and not smart.player_target_smarts[other.id] and (f2 == "none" or game_relations.is_factions_enemies(faction, f2))) then
  1251. targets[#targets+1] = { other.id, dist/priority }
  1252. --printf("adding " .. other:name() .. " to targets for faction " .. smart.owning_faction .. " against " .. other.owning_faction)
  1253. else
  1254. if faction == warfare.actor_faction then
  1255. --printf("not adding " .. other:name() .. " to target list for " .. smart:name())
  1256. end
  1257. end
  1258. end
  1259. end
  1260.  
  1261. function target_sort(a, b)
  1262. return a[2] < b[2]
  1263. end
  1264.  
  1265. table.sort(targets, target_sort)
  1266.  
  1267. printd(1, smart:name())
  1268.  
  1269. return targets[1] and targets[1][1]
  1270. end
  1271. end
  1272.  
  1273. printd(2, smart:name())
  1274.  
  1275. return nil
  1276. end
  1277. ]]
  1278.  
  1279. -- displays warfare's icon for the smart terrain
  1280. --function smart_terrain.se_smart_terrain.show(self)
  1281. --function update_icon_smart_terrain(smart)
  1282. function smart_terrain.se_smart_terrain:show()
  1283. if not (warfare.initialized) then
  1284. return
  1285. end
  1286.  
  1287. printd(0, self:name())
  1288.  
  1289. local c = 0
  1290.  
  1291. local faction = self.owning_faction or "none"
  1292. local actor_faction = faction == warfare.actor_faction
  1293.  
  1294. local friends = game_relations.is_factions_friends(warfare.actor_faction, faction)
  1295. local enemies = game_relations.is_factions_enemies(warfare.actor_faction, faction)
  1296.  
  1297. local actor_level
  1298.  
  1299. if (warfare_options.options.fog_of_war) then
  1300. local lvl = game_graph():vertex(self.m_game_vertex_id):level_id()
  1301. local visible = false
  1302.  
  1303. if (offline_combat_simulator.squads_by_level[lvl] ~= nil) then
  1304. local fog_of_war_distance = warfare_options.options.fog_of_war_distance
  1305. fog_of_war_distance = tonumber(fog_of_war_distance)
  1306.  
  1307. if (self.is_on_actor_level) then
  1308. local dist = warfare.distance_to_xz_sqr(self.position, alife():actor().position)
  1309.  
  1310. if (dist < fog_of_war_distance*fog_of_war_distance) then
  1311. visible = true
  1312. end
  1313. end
  1314.  
  1315. if not (visible) then
  1316. for squadID,_ in pairs(offline_combat_simulator.squads_by_level[lvl]) do
  1317. local squad = squadID and alife():object(squadID)
  1318.  
  1319. if (squad) then
  1320. if (squad.get_squad_community and squad:get_squad_community() == warfare.actor_faction) then
  1321.  
  1322. local dist = warfare.distance_to_xz_sqr(squad.position, self.position)
  1323.  
  1324. if (warfare.distance_to_xz_sqr(squad.position, self.position) < fog_of_war_distance*fog_of_war_distance) then
  1325. visible = true
  1326. break
  1327. end
  1328. end
  1329. end
  1330. end
  1331. end
  1332.  
  1333. if (warfare_options.options.hide_smarts) then
  1334. visible = false
  1335. end
  1336.  
  1337. if not (self.target_spots) then
  1338. self.target_spots = {}
  1339. end
  1340.  
  1341. for t,spot in pairs(self.target_spots) do
  1342. local other = alife():object(t)
  1343.  
  1344. if (other) then
  1345. if (other.owning_faction == warfare.actor_faction or not (other.owning_faction == "none" or game_relations.is_factions_enemies(other.owning_faction, warfare.actor_faction))) then
  1346. level.map_remove_object_spot(t, pda_icons[spot])
  1347. self.target_spots[t] = nil
  1348. other.actor_target = nil
  1349. else
  1350. level.map_change_spot_hint(t, pda_icons[spot], get_target_info(other))
  1351. end
  1352. end
  1353. end
  1354.  
  1355. if (actor_faction) then
  1356. for t,_ in pairs(self.target_smarts) do
  1357. if not (self.target_spots[t]) then
  1358. local other = alife():object(t)
  1359.  
  1360. if (other) then
  1361. if ((not other.actor_target) and other.owning_faction ~= warfare.actor_faction and (other.owning_faction == "none" or game_relations.is_factions_enemies(other.owning_faction, warfare.actor_faction))) then
  1362. other.actor_target = true
  1363.  
  1364. other:hide()
  1365.  
  1366. level.map_add_object_spot(t, "circle_target", get_target_info(other))
  1367. self.target_spots[t] = "target"
  1368. end
  1369. end
  1370. end
  1371. end
  1372. end
  1373.  
  1374. if (self.actor_target) then
  1375. visible = false
  1376. end
  1377.  
  1378. local hide_self = nil
  1379. if (warfare_names.point_names_hide[self:name()] and warfare_options.options.hide_undegraund_smarts) then
  1380. hide_self = true
  1381. end
  1382.  
  1383. if (visible == true) then
  1384. if (not self.warfare_spot) then
  1385. self.warfare_spot = self.owning_faction
  1386.  
  1387. if (self.warfare_spot) then
  1388. level.map_add_object_spot(self.id, pda_icons[self.warfare_spot], get_warfare_info(self))
  1389. end
  1390. else
  1391. level.map_change_spot_hint(self.id, pda_icons[self.warfare_spot], get_warfare_info(self))
  1392. end
  1393. else
  1394. if (warfare_options.options.hide_smarts or hide_self) then
  1395. if (self.warfare_spot) then
  1396. level.map_remove_object_spot(self.id, pda_icons[self.warfare_spot])
  1397. self.warfare_spot = nil
  1398. end
  1399. else
  1400. if (self.warfare_spot ~= "none") then
  1401. level.map_remove_object_spot(self.id, pda_icons[self.warfare_spot])
  1402. self.warfare_spot = "none"
  1403. level.map_add_object_spot(self.id, pda_icons[self.warfare_spot], get_target_info(self))
  1404. else
  1405. level.map_change_spot_hint(self.id, pda_icons[self.warfare_spot], get_target_info(self))
  1406. end
  1407. end
  1408. end
  1409. end
  1410. else
  1411. if (hide_smarts or warfare_options.options.hide_smarts) then
  1412. if (not self.warfare_spot and self.owning_faction == warfare.actor_faction) then
  1413. self.warfare_spot = self.owning_faction
  1414.  
  1415. if (self.warfare_spot) then
  1416. level.map_add_object_spot(self.id, pda_icons[self.warfare_spot], get_warfare_info(self))
  1417. end
  1418. end
  1419.  
  1420. if (self.warfare_spot and self.owning_faction == warfare.actor_faction) then
  1421. level.map_change_spot_hint(self.id, pda_icons[self.warfare_spot], get_warfare_info(self))
  1422. end
  1423.  
  1424. if (self.warfare_spot and self.owning_faction ~= warfare.actor_faction) then
  1425. level.map_remove_object_spot(self.id, pda_icons[self.warfare_spot])
  1426. self.warfare_spot = nil
  1427. end
  1428.  
  1429. if not (self.target_spots) then
  1430. self.target_spots = {}
  1431. end
  1432.  
  1433. for t,spot in pairs(self.target_spots) do
  1434. local other = alife():object(t)
  1435.  
  1436. if (other) then
  1437. if (other.owning_faction == warfare.actor_faction or not (other.owning_faction == "none" or game_relations.is_factions_enemies(other.owning_faction, warfare.actor_faction))) then
  1438. level.map_remove_object_spot(t, pda_icons[spot])
  1439. self.target_spots[t] = nil
  1440. other.actor_target = nil
  1441. else
  1442. level.map_change_spot_hint(t, pda_icons[spot], get_warfare_info(other))
  1443. end
  1444. end
  1445. end
  1446.  
  1447.  
  1448. if (actor_faction) then
  1449. for t,_ in pairs(self.target_smarts) do
  1450. if not (self.target_spots[t]) then
  1451. local other = alife():object(t)
  1452.  
  1453. if (other) then
  1454. if ((not other.actor_target) and other.owning_faction ~= warfare.actor_faction and (other.owning_faction == "none" or game_relations.is_factions_enemies(other.owning_faction, warfare.actor_faction))) then
  1455. other.actor_target = true
  1456.  
  1457. other:hide()
  1458.  
  1459. level.map_add_object_spot(t, "circle_target", get_target_info(other))
  1460. self.target_spots[t] = "target"
  1461. end
  1462. end
  1463. end
  1464. end
  1465. end
  1466. else
  1467. if not (self.target_spots) then
  1468. self.target_spots = {}
  1469. end
  1470. for t,spot in pairs(self.target_spots) do
  1471. local other = alife():object(t)
  1472.  
  1473. if (other) then
  1474. if (other.owning_faction == warfare.actor_faction or not (other.owning_faction == "none" or game_relations.is_factions_enemies(other.owning_faction, warfare.actor_faction))) then
  1475. level.map_remove_object_spot(t, pda_icons[spot])
  1476. self.target_spots[t] = nil
  1477. other.actor_target = nil
  1478. else
  1479. level.map_change_spot_hint(t, pda_icons[spot], get_warfare_info(other))
  1480. end
  1481. end
  1482. end
  1483.  
  1484.  
  1485. if (actor_faction) then
  1486. for t,_ in pairs(self.target_smarts) do
  1487. if not (self.target_spots[t]) then
  1488. local other = alife():object(t)
  1489.  
  1490. if (other) then
  1491. if ((not other.actor_target) and other.owning_faction ~= warfare.actor_faction and (other.owning_faction == "none" or game_relations.is_factions_enemies(other.owning_faction, warfare.actor_faction))) then
  1492. other.actor_target = true
  1493.  
  1494. other:hide()
  1495.  
  1496. level.map_add_object_spot(t, "circle_target", get_target_info(other))
  1497. self.target_spots[t] = "target"
  1498. end
  1499. end
  1500. end
  1501. end
  1502. end
  1503.  
  1504. if not (self.actor_target) then
  1505. if (self.warfare_spot) then
  1506. if (self.warfare_spot ~= faction) then
  1507. level.map_remove_object_spot(self.id, pda_icons[self.warfare_spot])
  1508. level.map_add_object_spot(self.id, pda_icons[faction], get_warfare_info(self))
  1509. self.warfare_spot = faction
  1510. else
  1511. level.map_change_spot_hint(self.id, pda_icons[faction], get_warfare_info(self))
  1512. end
  1513. else
  1514. level.map_add_object_spot(self.id, pda_icons[faction], get_warfare_info(self))
  1515. --printf("after add")
  1516. self.warfare_spot = faction
  1517. end
  1518. end
  1519.  
  1520. -------------------av661194
  1521. local name_self = self:name()
  1522. local rus_name = warfare_names.point_names_hide[name_self] or name_self
  1523. if (rus_name ~= name_self and warfare_options.options.hide_undegraund_smarts) then
  1524. if (self.warfare_spot) then
  1525. if (self.warfare_spot==faction) then
  1526. level.map_remove_object_spot(self.id, pda_icons[faction])
  1527. else
  1528. level.map_remove_object_spot(self.id, pda_icons[self.warfare_spot])
  1529. end
  1530. self.warfare_spot = nil
  1531. end
  1532. end
  1533. --------------------av661194
  1534. end
  1535. end
  1536.  
  1537. printd(1, self:name())
  1538. end
  1539.  
  1540. function smart_terrain.se_smart_terrain:hide()
  1541. printd(0, self:name())
  1542.  
  1543. if (self.warfare_spot) then
  1544. level.map_remove_object_spot(self.id, pda_icons[self.warfare_spot])
  1545. self.warfare_spot = nil
  1546. end
  1547.  
  1548. if (self.target_spots) then
  1549. for t,spot in pairs(self.target_spots) do
  1550. local other = alife():object(t)
  1551.  
  1552. if (other) then
  1553. level.map_remove_object_spot(t, pda_icons[spot])
  1554. self.target_spots[t] = nil
  1555. other.actor_target = nil
  1556. other:show()
  1557. end
  1558. end
  1559. end
  1560.  
  1561. printd(1, self:name())
  1562. end
  1563.  
  1564. --[[
  1565. -- hides warfare's icon for the smart terrain
  1566. function hide_smart_terrain(smart)
  1567. printd(0)
  1568. level.map_remove_object_spot(smart.id, "circle_small_yellow")
  1569. smart.warfare_spot = nil
  1570. printd(1)
  1571. end
  1572. ]]
  1573.  
  1574. function get_target_info(smart)
  1575. printd(0, smart:name())
  1576.  
  1577. local owning_faction = warfare_names.faction_names[smart.owning_faction]
  1578. --log("------>DEBUG-»>"..game.translate_string(smart:name()))
  1579. local warfareName= game.translate_string(smart:name())
  1580. local props = "\\n" .. warfareName .. "\\n"
  1581.  
  1582. if (smart.props) then
  1583. if (smart.props.base > 0) then
  1584. props = props .. "\\nBase\\n"
  1585. end
  1586.  
  1587. if (smart.props.resource > 0) then
  1588. props = props .. "\\nResource\\n"
  1589. end
  1590.  
  1591. if (smart.props.territory > 0) then
  1592. props = props .. "\\nTerritory\\n"
  1593. end
  1594. end
  1595.  
  1596. printd(1, smart:name())
  1597. return props
  1598. end
  1599.  
  1600. -- gets information for the smart terrain hint
  1601. function get_warfare_info(smart)
  1602. printd(0, smart:name())
  1603.  
  1604.  
  1605. local owning_faction_ru = warfare_names.faction_names[smart.owning_faction]
  1606. --log("------>DEBUG-»>"..game.translate_string(smart:name()))
  1607. local warfareName= game.translate_string(smart:name())
  1608.  
  1609. local props = ""
  1610.  
  1611. if (warfareName) then
  1612. props = "\\n" .. warfareName .. " | id="..smart.id .. "\\n"
  1613. else
  1614. props = "\\n" .. smart:name() .. "\\n"
  1615. end
  1616.  
  1617. if (manual_point and manual_point[1] and smart.id == manual_point[1]) then
  1618. local diff = utils.round(game.get_game_time():diffSec(manual_point[2])/60)
  1619. local remaining = (warfare_options.options.auto_capture_wait_time) - diff
  1620.  
  1621. if (remaining >= 0) then
  1622. props = props.."\\nTime until player capture: "..remaining.." min\\n"
  1623. end
  1624. end
  1625.  
  1626. if (smart.props) then
  1627. if (smart.props.base > 0 and smart.props.resource > 0) then
  1628. props = game.translate_string(base_and_resource)
  1629. elseif (smart.props.base > 0) then
  1630. props = game.translate_string(base)
  1631. elseif (smart.props.resource > 0) then
  1632. props = game.translate_string(resource)
  1633. elseif (smart.props.territory > 0) then
  1634. props = game.translate_string(territory)
  1635. elseif (smart.props.lair > 0) then
  1636. props = game.translate_string(lair)
  1637. end
  1638. end
  1639. if (smart.owning_faction) then
  1640. props = props .. "\\nFaction: " .. game.translate_string(smart.owning_faction) .. "\\n"
  1641. end
  1642.  
  1643. if (smart.props and smart.props.base > 0 and smart.resource_count) then
  1644. props = props .. "\\nResource count: " .. tostring(smart.resource_count) .. "\\n"
  1645. end
  1646.  
  1647. props = props .. "\\nCurrent Population: " .. tostring(smart.defense_count) .. "\\n"
  1648. props = props .. "\\nMax Population: " .. tostring(smart.max_population) .. "\\n"
  1649.  
  1650. if (defense_timers[smart.id]) then
  1651. props = props .. "\\n----------\\n"
  1652.  
  1653. local defense_timer = defense_timers[smart.id]
  1654. local next_defense = defense_next_update[smart.id]
  1655.  
  1656. local diff = game.get_game_time():diffSec(defense_timer)
  1657. local remaining = (next_defense*60) - diff
  1658. -- xQd, show the timer in hours and minutes
  1659. --remaining = utils.round(remaining / 60)
  1660. if remaining <= 3600 then -- show in minutes if less than an hour left
  1661. remaining = "" .. utils.round(remaining / 60) .. " minutes"
  1662. else -- otherwise show in hours and minutes
  1663. local remaining_hrs_round = math.floor((remaining / 60)/60)
  1664. local remaining_hrs_float = (remaining / 60)/60
  1665. local remaining_mins_diff = utils.round((remaining_hrs_float - remaining_hrs_round) * 60)
  1666. remaining = "" .. remaining_hrs_round .. " hours and " .. remaining_mins_diff .. " minutes"
  1667. end
  1668. props = props .. "\\nTime until defense/attack respawn: "..remaining
  1669. -- xQd end
  1670. end
  1671.  
  1672. if (patrol_timers[smart.id]) then
  1673. props = props .. "\\n----------\\n"
  1674.  
  1675. local patrol_timer = patrol_timers[smart.id]
  1676. local next_patrol = patrol_next_update[smart.id]
  1677.  
  1678. local diff = game.get_game_time():diffSec(patrol_timer)
  1679. local remaining = (next_patrol*60) - diff
  1680. -- xQd, show the timer in hours and minutes
  1681. --remaining = utils.round(remaining / 60)
  1682. if remaining <= 3600 then -- show in minutes if less than an hour left
  1683. remaining = "" .. utils.round(remaining / 60) .. " minutes"
  1684. else -- otherwise show in hours and minutes
  1685. local remaining_hrs_round = math.floor((remaining / 60)/60)
  1686. local remaining_hrs_float = (remaining / 60)/60
  1687. local remaining_mins_diff = utils.round((remaining_hrs_float - remaining_hrs_round) * 60)
  1688. remaining = "" .. remaining_hrs_round .. " hours and " .. remaining_mins_diff .. " minutes"
  1689. end
  1690. props = props .. "\\nTime until patrol respawn: "..remaining
  1691. end
  1692.  
  1693. local f = false
  1694. if (smart.target_smarts) then
  1695. for target,_ in pairs(smart.target_smarts) do
  1696. if not (f) then
  1697. f = true
  1698. props = props .. "\\n-------------\\n"
  1699. props = props .. "\\nTargets:\\n"
  1700. end
  1701.  
  1702. local other = alife():object(target)
  1703.  
  1704. if (other) then
  1705. local otherWarfare = warfare_names.point_names[other:name()] or other:name()
  1706. local other_level = game.translate_string(alife():level_name(game_graph():vertex(other.m_game_vertex_id):level_id()))
  1707.  
  1708. props = props .. "\\n" .. otherWarfare .. " ("..other_level..")\\n"
  1709. end
  1710. end
  1711. end
  1712.  
  1713. if (invasions[smart.id]) then
  1714. local other = invasions[smart.id][1] and alife_object(invasions[smart.id][1])
  1715. local otherName = warfare_names.point_names[other:name()]
  1716. local other_level = game.translate_string(alife():level_name(game_graph():vertex(other.m_game_vertex_id):level_id()))
  1717. otherName = otherName or smart:name()
  1718. props = props .. "\\n-------------\\n"
  1719. props = props .. "\\nInvasion Target: " .. otherName .."\\n"
  1720. props = props .. "\\nInvasion Size: " .. invasions[smart.id][2] .. "\\n"
  1721. end
  1722.  
  1723. printd(1, smart:name())
  1724.  
  1725. return props
  1726. end
  1727.  
  1728. -- Includes both squads currently at the smart, as well as those targeting it.
  1729. function squad_count(smart, faction)
  1730. printd(0, smart:name())
  1731. local squads = SIMBOARD.smarts[smart.id].squads
  1732. local squadCount = 0
  1733.  
  1734. if not (smart.owning_faction) then
  1735. smart.owning_faction = "none"
  1736. end
  1737.  
  1738. for sid,_ in pairs(squads) do
  1739. local squad = sid and alife_object(sid)
  1740.  
  1741. if (squad) then
  1742. if (squad.registered_with_warfare and squad:get_squad_community() == faction) then
  1743. local power = offline_combat_simulator.ocs_power_table[squad.id]
  1744.  
  1745. if (power and power > 0 and squad:npc_count() > 0) then
  1746. squadCount = squadCount + 1
  1747. end
  1748. end
  1749. end
  1750. end
  1751.  
  1752. printd(1, smart:name())
  1753.  
  1754. --printf("squad count=%s", tostring(squadCount))
  1755. --printf("smart=%s", smart:name())
  1756. --printf("faction=%s", faction)
  1757.  
  1758. return squadCount
  1759. end
  1760.  
  1761. function squad_count_defending(smart, faction)
  1762. printd(0, smart:name())
  1763. local squads = SIMBOARD.smarts[smart.id].squads
  1764. local squadCount = 0
  1765.  
  1766. if not (smart.owning_faction) then
  1767. smart.owning_faction = "none"
  1768. end
  1769.  
  1770. for sid,_ in pairs(squads) do
  1771. local squad = sid and alife_object(sid)
  1772.  
  1773. if (squad) then
  1774. if (squad.registered_with_warfare and squad:get_squad_community() == faction and squad.current_action == 1) then
  1775. squadCount = squadCount + 1
  1776. end
  1777. end
  1778. end
  1779.  
  1780. printd(1, smart:name())
  1781.  
  1782. return squadCount
  1783. end
  1784.  
  1785. function squad_count_targeting(smart, faction)
  1786. printd(0, smart:name())
  1787.  
  1788. local squads = SIMBOARD.smarts[smart.id].squads
  1789. local squadCount = 0
  1790.  
  1791. if not (smart.owning_faction) then
  1792. smart.owning_faction = "none"
  1793. end
  1794.  
  1795. for sid,_ in pairs(squads) do
  1796. local squad = sid and alife_object(sid)
  1797.  
  1798. if (squad) then
  1799. if (squad.registered_with_warfare and squad:get_squad_community() == faction and squad.current_action == 0) then
  1800. squadCount = squadCount + 1
  1801. end
  1802. end
  1803. end
  1804.  
  1805. printd(1, smart:name())
  1806.  
  1807. return squadCount
  1808. end
  1809.  
  1810. function check_owner(smart)
  1811. if not (smart) then
  1812. return
  1813. end
  1814.  
  1815. printd(0, smart:name())
  1816.  
  1817. if (not SIMBOARD.smarts[smart.id]) then
  1818. smart.owning_faction = "none"
  1819. return
  1820. end
  1821.  
  1822. if not (SIMBOARD.smarts[smart.id].squads) then
  1823. smart.owning_faction = "none"
  1824. return
  1825. end
  1826.  
  1827. local squads = SIMBOARD.smarts[smart.id].squads
  1828. local squadCount = {}
  1829. local squadPowers = {}
  1830.  
  1831. if not (smart.owning_faction) then
  1832. smart.owning_faction = "none"
  1833. end
  1834.  
  1835. if not (smart.capping_faction) then
  1836. smart.capping_faction = "none"
  1837. end
  1838.  
  1839.  
  1840. for sid,_ in pairs(squads) do
  1841. local squad = sid and alife_object(sid)
  1842.  
  1843. if (squad) then
  1844. if ((squad.current_action == 1 or squad.combat_action == 1) and squad.current_target_id == smart.id and squad.registered_with_warfare) then
  1845. local faction = squad:get_squad_community()
  1846.  
  1847. if not (squadCount[faction]) then
  1848. squadCount[faction] = 0
  1849. squadPowers[faction] = 0
  1850. end
  1851.  
  1852. local power = offline_combat_simulator.ocs_power_table[squad.id]
  1853.  
  1854. if (power and power > 0) then
  1855. squadPowers[faction] = squadPowers[faction] + power
  1856. end
  1857.  
  1858. squadCount[faction] = squadCount[faction] + 1
  1859.  
  1860. if (smart:name() == "cit_killers") then
  1861. --printf(squad:name())
  1862. end
  1863. end
  1864. end
  1865. end
  1866.  
  1867. local owner = "none"
  1868. for faction,count in pairs(squadCount) do
  1869. if (owner == "none" or faction == smart.owning_faction) then
  1870. owner = faction
  1871. elseif (squadCount[owner] < count and smart.owning_faction == "none") then
  1872. owner = faction
  1873. end
  1874. end
  1875.  
  1876. if (owner == "monster") then
  1877. owner = "none"
  1878. end
  1879.  
  1880. if (manual_point and manual_point[1] == smart.id) then
  1881. if (owner ~= warfare.actor_faction) then
  1882. owner = "none"
  1883. end
  1884. end
  1885.  
  1886. -- Want to clear if a non-enemy faction takes point
  1887. if (warfare.influence_levels[smart.id]) then
  1888. local enemies = game_relations.is_factions_enemies(owner, warfare.actor_faction)
  1889.  
  1890. if not (enemies) then
  1891. local val = warfare.influence_levels[smart.id]
  1892. warfare.actor_influence_points = warfare.actor_influence_points + val
  1893. warfare.influence_levels[smart.id] = nil
  1894. end
  1895. end
  1896.  
  1897. -- Store information in smart_terrain; no need to calculate again elsewhere
  1898. smart.defense_count = squadCount[owner] or 0
  1899. smart.defense_power = squadPowers[owner] or 0
  1900.  
  1901. if (smart.defense_count > 25) then
  1902. SetHudMsg("Smart " .. smart:name() .. " has " .. tostring(smart.defense_count) .." squads!", 60)
  1903. --printf("smart " .. smart:name() .. " has over 25 squads! max pop = " .. tostring(smart.max_population))
  1904. end
  1905.  
  1906. if not (smart.last_owner) then
  1907. smart.last_owner = owner
  1908. end
  1909.  
  1910. if (smart.last_owner ~= smart.owning_faction) then
  1911. local squads = patrol_squads[smart.id]
  1912.  
  1913. if (squads) then
  1914. for squad_id, target_id in pairs(squads) do
  1915. local squad = squad_id and alife_object(squad_id)
  1916.  
  1917. if (squad and squad:clsid() == clsid.online_offline_group_s) then
  1918. if (squad:get_squad_community() ~= smart.owning_faction) then
  1919. sim_squad_warfare.set_target(squad, smart.id)
  1920.  
  1921. patrol_squads[smart.id][squad.id] = {
  1922. target = smart.id,
  1923. stay_time = nil,
  1924. arrive_time = nil
  1925. }
  1926.  
  1927. end
  1928. else
  1929. patrol_squads[smart.id][squad_id] = nil
  1930. end
  1931. end
  1932. end
  1933.  
  1934. if (smart.target_smarts) then
  1935. smart:hide()
  1936. smart.target_smarts = {}
  1937. invasions[smart.id] = nil
  1938. smart:show()
  1939. end
  1940. end
  1941.  
  1942. if (smart.last_owner) then
  1943. smart.last_owner = smart.owning_faction
  1944. end
  1945.  
  1946. smart.owning_faction = owner
  1947. --smart_owners[smart.id] = owner
  1948.  
  1949.  
  1950. printd(1, smart:name())
  1951. end
  1952.  
  1953. function faction_enemy_present(smart, faction)
  1954.  
  1955. end
  1956.  
  1957. function update_offline_combat(smart)
  1958. --printf("--- updating offline combat for " .. smart:name() .. " ---")
  1959. printd(0, smart:name())
  1960.  
  1961. -- fill a new table with squads so we can shuffle it
  1962. local squads = {}
  1963.  
  1964. for sid,_ in pairs(SIMBOARD.smarts[smart.id].squads) do
  1965. local squad = sid and alife_object(sid)
  1966.  
  1967. if (squad) then
  1968. squads[#squads+1] = squad
  1969. end
  1970. end
  1971.  
  1972. if (#squads > 0) then
  1973. warfare.shuffle(squads)
  1974.  
  1975. for i,squad in pairs(squads) do
  1976. if (faction_enemy_present(smart, squad:get_squad_community())) then
  1977. for j,squad2 in pairs(squads) do
  1978. if (game_relations.is_factions_enemies(squad:get_squad_community(), squad2:get_squad_community())) then
  1979. local sim = alife()
  1980.  
  1981. local power1 = offline_combat_simulator.ocs_power_table[squad.id]
  1982. local power2 = offline_combat_simulator.ocs_power_table[squad2.id]
  1983.  
  1984. if (squad2:npc_count() > 0 and power2 > 0) then
  1985. local total_casualties = 0
  1986.  
  1987. --[[local squad1_npcs = {}
  1988. for k in squad:squad_members() do
  1989. squad1_npcs[#squad1_npcs+1] = k
  1990. end]]
  1991.  
  1992. local squad2_npcs = {}
  1993. for k in squad2:squad_members() do
  1994. squad2_npcs[#squad2_npcs+1] = k
  1995. end
  1996.  
  1997. --local k1 = squad1_npcs[math.random(#squad1_npcs)]
  1998. local k2 = squad2_npcs[math.random(#squad2_npcs)]
  1999. --local attacker = k1.object or k1.id and sim:object(k1.id)
  2000. local victim = k2.object or k2.id and sim:object(k2.id)
  2001. local damage = power1 * (0.5 + math.random() * 1.5)
  2002. local new_power = math.max(0, power2 - damage)
  2003. local diff = power2 - new_power
  2004. local kp = victim:rank()
  2005. local mul = 1 / math.random(3, 5)
  2006.  
  2007. --printf(squad:name() .. " attacked " .. squad2:name() .. " at " .. smart:name())
  2008.  
  2009. offline_combat_simulator.ocs_power_table[squad2.id] = new_power
  2010.  
  2011. if new_power <= 0 then
  2012. --printf("!!! %s killed %s !!!", squad:name(), squad2:name())
  2013.  
  2014. for i,npc in pairs(squad2_npcs) do
  2015. squad2:remove_npc(npc)
  2016. squad_npc_killed_offline_combat(squad:get_squad_community(), squad2:get_squad_community())
  2017. end
  2018.  
  2019. sim_squad_warfare.remove_squad(squad2)
  2020. self:unregister(squad2)
  2021. else
  2022. if kp * mul <= diff then
  2023. squad2:remove_npc(k2.id)
  2024.  
  2025. if squad2:npc_count() <= 1 then
  2026. --printf("!!! REMOVING SQUAD (offline combat): " .. squad2)
  2027. sim_squad_warfare.remove_squad(squad2)
  2028. end
  2029.  
  2030. squad_npc_killed_offline_combat(squad:get_squad_community(), squad2:get_squad_community())
  2031. --printf("!!! %s NPC killed %s NPC !!!", squad:name(), squad2:name())
  2032. end
  2033. end
  2034.  
  2035. break
  2036. end
  2037. end
  2038. end
  2039. end
  2040. end
  2041. end
  2042.  
  2043. printd(1, smart:name())
  2044. end
  2045.  
  2046. function calculate_target_density(smart)
  2047. printd(0, smart:name())
  2048.  
  2049. local level = game_graph():vertex(smart.m_game_vertex_id):level_id()
  2050. local linked = level_targets.level_links[level]
  2051. local smart_collection = {}
  2052.  
  2053. for i=1,#warfare_levels.level_information[level].smarts do
  2054. smart_collection[#smart_collection+1] = warfare_levels.level_information[level].smarts[i]
  2055. end
  2056.  
  2057. for lvl,_ in pairs(linked) do
  2058. for i=1,#warfare_levels.level_information[lvl].smarts do
  2059. smart_collection[#smart_collection+1] = warfare_levels.level_information[level].smarts[i]
  2060. end
  2061. end
  2062.  
  2063. local targets = {}
  2064.  
  2065. local enemy_points = 0
  2066. local aggregate_distance = 0
  2067.  
  2068. -- bases will be considered as being closer, then owned resources are taken at their current value, while territory and lairs are viewed as being further away as they matter less.
  2069. local base_weight = 0.75
  2070. local resource_weight = 1
  2071. local territory_weight = 1.5
  2072. local lair_weight = 1.5
  2073.  
  2074. for i=1,#smart_collection do
  2075. local other = smart_collection[i] and alife_object(smart_collection[i])
  2076.  
  2077. if (other and other.props) then
  2078. local owner = other.owning_faction
  2079. local other_level = game_graph():vertex(other.m_game_vertex_id):level_id()
  2080.  
  2081. if not (smart.global_position) then
  2082. global_position.from(smart)
  2083. end
  2084.  
  2085. if not (other.global_position) then
  2086. global_position.from(other)
  2087. end
  2088.  
  2089. local dist = warfare.distance_to_xz_sqr(smart.global_position, other.global_position)
  2090.  
  2091. if (owner and owner ~= "none" and game_relations.is_factions_enemies(smart.owning_faction, owner) == true) then
  2092. local points = 0
  2093.  
  2094. if (other.props.base > 0) then
  2095. points = 1.5
  2096. elseif (other.props.resource > 0) then
  2097. points = 1.2
  2098. elseif (other.props.territory > 0) then
  2099. points = 1.0
  2100. elseif (other.props.lair > 0) then
  2101. points = 0.75
  2102. end
  2103.  
  2104. -- Distant levels shouldn't be included in the weight as much.
  2105. if (other_level ~= level) then
  2106. points = points * 0.5
  2107. end
  2108.  
  2109. aggregate_distance = aggregate_distance + dist
  2110. enemy_points = enemy_points + points
  2111. end
  2112. end
  2113. end
  2114.  
  2115. local final_distance = aggregate_distance / enemy_points
  2116.  
  2117. printd(1, smart:name())
  2118.  
  2119. return final_distance
  2120. end
  2121.  
  2122. function find_targets(smart, faction_override)
  2123. printd(0, smart:name())
  2124.  
  2125. local level = game_graph():vertex(smart.m_game_vertex_id):level_id()
  2126. local linked = level_targets.level_links[level]
  2127. local currentLevelSmarts = warfare_levels.level_information[level].smarts
  2128. local smartCollection = {}
  2129. local f = smart.owning_faction or "none"
  2130. local d = smart.defense_power or 0
  2131.  
  2132. if (faction_override) then
  2133. f = faction_override
  2134. d = get_faction_power(smart, f)
  2135. end
  2136.  
  2137. for i=1,#currentLevelSmarts do
  2138. smartCollection[#smartCollection+1] = currentLevelSmarts[i]
  2139. end
  2140.  
  2141. -- May not want to include non-linked levels
  2142. for lvl,_ in pairs(linked) do
  2143. --for _,lvl in pairs(level_targets.active_levels) do
  2144. local smarts = warfare_levels.level_information[lvl].smarts
  2145.  
  2146. for i=1,#smarts do
  2147. smartCollection[#smartCollection+1] = smarts[i]
  2148. end
  2149. end
  2150.  
  2151. local targets = {}
  2152.  
  2153. if (f == "monster") then
  2154. return
  2155. end
  2156.  
  2157. for i=1,#smartCollection do
  2158. -- Do not even factor in which level this base is on. We want to have fluid borders at this point.
  2159. local other = smartCollection[i] and alife_object(smartCollection[i])
  2160.  
  2161. if (other and other.props) then
  2162. local skip = false
  2163.  
  2164. for j=1,#smartCollection do
  2165. if (i ~= j) then
  2166. local otherBase = smartCollection[j] and alife_object(smartCollection[j])
  2167.  
  2168. if (otherBase and otherBase.owning_faction == smart.owning_faction) then
  2169. if (otherBase.target_smarts) then
  2170. if (otherBase.target_smarts[other.id]) then
  2171. skip = true
  2172. printf(smart:name() .. " SKIPPING " .. other:name() .. " BECAUSE OF " .. otherBase:name())
  2173. break
  2174. end
  2175. end
  2176. end
  2177. end
  2178. end
  2179.  
  2180. local dist = (1 / math.max(1, warfare.distance_to_xz_sqr(global_position.from(smart), global_position.from(other))))
  2181. local priority = 0
  2182. local f2 = other.owning_faction or "none"
  2183. local d2 = other.defense_power or 0
  2184. local other_level = game_graph():vertex(other.m_game_vertex_id):level_id()
  2185. local other_resources = warfare_levels.level_information[other_level].resource_count
  2186. local cName = warfare_names.point_names[smart:name()]
  2187. local pName = warfare_names.point_names[other:name()]
  2188.  
  2189. if not (cName) then
  2190. cName = smart:name()
  2191. end
  2192.  
  2193. if not (pName) then
  2194. pName = other:name()
  2195. else
  2196. pName = pName.." ("..other:name()..")"
  2197. end
  2198.  
  2199. if (warfare.influence_levels[other.id]) then
  2200. priority = priority + (warfare.influence_levels[other.id] * warfare_options.options.actor_influence_weight)
  2201. end
  2202.  
  2203.  
  2204. if (other.props[f] > 0) then
  2205. priority = priority + (other.props[f] * warfare_options.options.factions[f].faction_flag_priority)
  2206. end
  2207.  
  2208. if (other.props.base > 0) then
  2209. priority = priority + (other.props.base * warfare_options.options.factions[f].base_priority)
  2210. end
  2211.  
  2212. if (other.props.resource > 0) then
  2213. priority = priority + (other.props.resource * warfare_options.options.factions[f].resource_priority)
  2214. end
  2215.  
  2216. if (other.props.territory > 0) then
  2217. priority = priority + (other.props.territory * warfare_options.options.factions[f].territory_priority)
  2218. end
  2219.  
  2220. if (other.target_smarts) then
  2221. if (other.target_smarts[smart.id]) then
  2222. priority = priority + warfare_options.options.factions[f].is_being_targeted_priority
  2223. end
  2224. end
  2225.  
  2226. if (other.player_target_smarts) then
  2227. if (other.player_target_smarts[smart.id]) then
  2228. priority = priority + warfare_options.options.factions[f].is_being_targeted_priority
  2229. end
  2230. end
  2231.  
  2232. if (game_relations.is_factions_enemies(f, f2)) then
  2233. local diff = d - d2
  2234.  
  2235. if (diff > 0) then
  2236. priority = priority + warfare_options.options.factions[f].target_weaker_priority
  2237. else
  2238. priority = priority + warfare_options.options.factions[f].target_stronger_priority
  2239. end
  2240. end
  2241.  
  2242. if (f2 == "none" or game_relations.is_factions_enemies(f, f2) and not skip) then
  2243. if (priority > 0) then
  2244. priority = priority + (warfare_options.options.factions[f].target_resource_priority * other_resources)
  2245.  
  2246. if (f2 ~= "none") then
  2247. local f_bases = warfare_factions.faction_information[f].base_count
  2248. local f_resources = warfare_factions.faction_information[f].resource_count
  2249. local f_est = f_bases + (f_resources * f_resources)
  2250.  
  2251. local f2_bases = warfare_factions.faction_information[f2].base_count
  2252. local f2_resources = warfare_factions.faction_information[f2].resource_count
  2253. local f2_est = f2_bases + (f2_resources * f2_resources)
  2254.  
  2255. if (f_est > f2_est) then
  2256. priority = priority + warfare_options.options.factions[f].target_faction_weaker_priority
  2257. elseif (f2_est > f_est) then
  2258. priority = priority + warfare_options.options.factions[f].target_faction_stronger_priority
  2259. end
  2260. end
  2261.  
  2262. if (other_level == level) then
  2263. priority = priority + warfare_options.options.factions[f].target_on_same_level_priority
  2264. end
  2265.  
  2266. targets[#targets+1] = { priority * dist, other.id }
  2267. end
  2268. end
  2269. end
  2270. end
  2271.  
  2272. table.sort(targets, function(a, b)
  2273. return math.abs(a[1]) > math.abs(b[1])
  2274. end)
  2275.  
  2276. printd(1, smart:name())
  2277.  
  2278. return targets
  2279. end
  2280.  
  2281. function check_unique_npcs(smart)
  2282. printd(0, smart:name())
  2283.  
  2284. local name = smart:name()
  2285. local faction = smart.owning_faction
  2286.  
  2287. --[[
  2288. local tg = time_global()
  2289.  
  2290. if smart.last_unique_check ~= nil and tg < smart.last_unique_check then
  2291. printd(1, smart:name())
  2292. return
  2293. end]]
  2294.  
  2295. --smart.last_unique_check = tg + 1000 + math.random(3*60*1000,6*60*1000) -- xQd, delay trader spawns to 3-6 real life minutes. No more instant respawning traders when they die
  2296.  
  2297. if not (smart.online) then
  2298. printd(2, smart:name())
  2299. return
  2300. end
  2301.  
  2302. if faction == nil or faction == "none" or faction == "zombied" then
  2303. printd(3, smart:name())
  2304. return
  2305. end
  2306.  
  2307. if faction == "killer" then
  2308. faction = "merc"
  2309. end
  2310.  
  2311. local trader = faction .. "_sim_squad_trader"
  2312. local mechanic = faction .. "_sim_squad_mechanic"
  2313.  
  2314. local location = nil
  2315. local spawnTrader = false
  2316. local spawnMechanic = false
  2317.  
  2318. -- [1] = trader count, [2] = mechanic count
  2319. local uniques = {}
  2320. uniques.mar_smart_terrain_base = { 3, 1 }
  2321. uniques.esc_smart_terrain_5_7 = { 1, 1 }
  2322. uniques.esc_smart_terrain_3_16 = { 1, 1 }
  2323. uniques.val_smart_terrain_7_3 = { 0, 1 }
  2324. uniques.val_smart_terrain_7_4 = { 2, 0 }
  2325. uniques.agr_smart_terrain_1_6 = { 1, 1 }
  2326. uniques.bar_visitors = { 1, 1 }
  2327. uniques.bar_dolg_general = { 1, 0 }
  2328. uniques.mil_smart_terrain_7_7 = { 0, 1 }
  2329. uniques.mil_smart_terrain_7_10 = { 1, 0 }
  2330. uniques.cit_killers = { 1, 1 }
  2331. uniques.pri_monolith = { 1, 1 }
  2332. uniques.zat_stalker_base_smart = { 3, 1 }
  2333. uniques.yan_smart_terrain_6_4 = { 1, 0 }
  2334. uniques.jup_a6 = { 3, 1 }
  2335. uniques.jup_b41 = { 1, 1 }
  2336. uniques.jup_smart_terrain_50 = { 1, 1 }
  2337. uniques.ds2_domik_st = { 1, 1 }
  2338. uniques.val_smart_terrain_4_0 = { 1, 1 }
  2339. uniques.val_smart_terrain_1_2 = { 1, 1 }
  2340. uniques.gar_smart_terrain_3_5 = { 1, 1 }
  2341. uniques.gen_smart_terrain_lab_entrance_2 = { 1, 1 }
  2342. uniques.jup_a12 = { 1, 1 }
  2343. uniques.lim_smart_terrain_9 = { 1, 1 }
  2344. uniques.mar_smart_terrain_10_5 = { 1, 1 }
  2345. uniques.mil_smart_terrain_2_4 = { 1, 1 }
  2346. uniques.rad_antenna_patrol = { 1, 1 }
  2347. uniques.red_smart_terrain_bridge = { 1, 1 }
  2348. uniques.ros_smart_stalker_killers1 = { 1, 1 }
  2349. uniques.aes2_monolith_camp3 = { 1, 1 }
  2350. uniques.rad_entrance = { 1, 0 }
  2351. uniques.trc_sim_20 = { 1, 1 }
  2352. uniques.zat_b40_smart_terrain = { 1, 1 }
  2353. uniques.zat_b38 = { 1, 1 }
  2354. uniques.pri_b36_smart_terrain = { 1, 1 }
  2355. uniques.aes_smart_terrain_monolit_blockpost = { 1, 1 }
  2356.  
  2357. if (uniques[smart:name()]) then
  2358. local tbl = uniques[smart:name()]
  2359. local maxTraders = tbl[1]
  2360. local maxMechanics = tbl[2]
  2361.  
  2362. local squads = SIMBOARD.smarts[smart.id].squads
  2363. local traderCount = 0
  2364. local mechanicCount = 0
  2365.  
  2366. for sid,_ in pairs(squads) do
  2367. local s = sid and alife_object(sid)
  2368.  
  2369. if (s) then
  2370. -- make sure there are no vanilla traders or mechanics
  2371. if string.find(s:name(), mechanic) or string.find(s:name(), "tech") or string.find(s:name(), "mechanic") then
  2372. mechanicCount = mechanicCount + 1
  2373. elseif string.find(s:name(), trader) or string.find(s:name(), "trader") or string.find(s:name(), "barman") or string.find(s:name(), "barmen") or string.find(s:name(), "sakharov") or string.find(s:name(), "ashot") or string.find(s:name(), "doctor") or string.find(s:name(), "scientist") then
  2374. traderCount = traderCount + 1
  2375. end
  2376. end
  2377. end
  2378.  
  2379. while traderCount < maxTraders do
  2380. local squad = alun_utils.create_squad(trader, smart:name())
  2381.  
  2382. sim_squad_warfare.set_target_trader(squad, smart.id, smart:name())
  2383. --printf("SPAWNED TRADER AT "..smart:name())
  2384. traderCount = traderCount + 1
  2385. end
  2386.  
  2387. while mechanicCount < maxMechanics do
  2388. local squad = alun_utils.create_squad(mechanic, smart:name())
  2389.  
  2390. sim_squad_warfare.set_target_trader(squad, smart.id, smart:name())
  2391. --printf("SPAWNED MECHANIC AT "..smart:name())
  2392. mechanicCount = mechanicCount + 1
  2393. end
  2394.  
  2395. --printf("end")
  2396. end
  2397.  
  2398. printd(4, smart:name())
  2399. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement