Advertisement
renard162

arszi_psy.script

Sep 23rd, 2022
982
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 33.21 KB | Gaming | 0 0
  1. --STRESSFULL ZONE MOD v0.8.2----------------------------------------------
  2.  
  3. --PPE SETTINGS------------------------------------------------------------
  4. local c_ppe_max_intensity = 0.5 --Max intensity of the sound shock PPE effect.
  5.  
  6. local c_id_ppe_blur = 5600
  7. local c_id_ppe_black_infinite = 5606
  8. local c_id_ppe_snd_shock = 5607
  9. local c_id_ppe_radiation = 5608
  10. local c_id_ppe_alcohol = 5609
  11.  
  12. local c_stage_default = -1
  13. local c_stage_0 = 0
  14. local c_stage_1 = 1
  15. local c_stage_2 = 2
  16. local c_stage_3 = 3
  17.  
  18. --PSY-HEALTH SETTINGS------------------------------------------------------------
  19. local c_psy_health_regen_per_second = ( stress_mcm.get_config("regen") or 26 ) / 2160000                        --base psy health regeneration per in-game second, when something comfortrting is affecting the stalker
  20. local c_psy_stress_drain_per_second = ( stress_mcm.get_config("drain") or 13 ) / 2160000                        --psy health drain per in-game second, when stalker is out and about in the Zone
  21. local c_psy_health_regen_booster_divider = 25                                                                   --psy health regeneration divider for medicines
  22. local c_psy_health_regen_artefacts_divider = 100                                                                --psy health regeneration divider for artefacts and attachments
  23. local safe_distance_to_base = 35
  24. local danger_distance_static_anomaly = 14
  25. local fate_of_player_zombfication = false
  26.  
  27. --PSY-HEALTH REGEN AT CAMPFIRES--------------------------------------------------
  28. local c_psy_health_regen_campfire_enabled = stress_mcm.get_config("campfire_enabled" ) or false
  29. local c_psy_health_regen_campfire_bonus = ( stress_mcm.get_config("campfire_regen" ) or 40 ) / 60000            --psy health regeneration bonus for campfire bonus
  30. local c_psy_health_regen_dist_to_campfire = stress_mcm.get_config("campfire_distance" ) or 15                   --distance at which to apply campfire regen bonus
  31.  
  32. --PSY-DAMAGE---------------------------------------------------------------------
  33. local c_psy_controler_damage_multiplier = ( stress_mcm.get_config("controler_damage_multiplier" ) or 40) / 100      --psy damage divider, to balace out controller attacks
  34. local c_controler_damage_max_distance = ( stress_mcm.get_config("controler_damage_max_distance" ) or 50)            --with every X meters, controller's tube attack will do 1% less damage. 100*X for max distance.
  35. local c_psy_hit_stress_multiplier = ( stress_mcm.get_config("hit_stress") or 2.8 ) / 100                            --multiplier for stress on being hit
  36. local c_psy_wound_stress_multiplier = ( stress_mcm.get_config("wounded_stress") or 14 ) / 600000                    --multiplier for stress drain from not being at full physical health
  37.  
  38. --PSY-STATE CONTROLS--------------------------------------------------------------
  39. local c_psy_stage_1 = ( stress_mcm.get_config("stage_1") or 75 ) / 100                                          --psy health at which first effects of stress are visible
  40. local c_psy_stage_2 = ( stress_mcm.get_config("stage_2") or 50 ) / 100                                          --psy health at wichi sever effects of stress are present
  41. local c_psy_voices = ( stress_mcm.get_config("voices") or 80 ) / 100                                            --psy health at which stalker starts hearing voices, first alomost silently, than louder
  42. --config check-- c_psy_stage_2 shouldn't be highter than c_psy_stage_1
  43. c_psy_stage_2 = math.min(c_psy_stage_2, (c_psy_stage_1 - 0.01))
  44.  
  45. --TABLES-------------------------------------------------------------------------
  46. local psy_table = {}
  47. local safe_bases = {}
  48. local level_static_anomaly = {}
  49. -- local musical_instrument_bonus = {
  50. --     ["guitar_a"] = 1.33,
  51. --     ["harmonica_a"] = 1.00,
  52. -- }
  53.  
  54. --SOUND--------------------------------------------------------------------------
  55. local sound_zombified = sound_object("arszi_psy\\pripyat_whispers_final")
  56. local sound_psy_death_scene = sound_object("arszi_psy\\zombie_die_3")
  57. local c_sound_zombified_volume = 20
  58. local c_sound_psy_death_scene_volume = 30
  59.  
  60. --MISC---------------------------------------------------------------------------
  61. local arszi_previous_time = nil
  62. local arszi_zombification_time_counter = 0
  63. local campfire_bonus = 1.33
  64. local safe_area_bonus = 1.13
  65.  
  66. --FLAGS--------------------------------------------------------------------------
  67. local anomaly_close = false
  68. local nearby_lit_campfire = false
  69. local inside_safe_area = false
  70. local in_combat = false
  71.  
  72. --DEBUG CONTROL------------------------------------------------------------------
  73. local debug_time = nil
  74. local debug_trigger_time = 60
  75. local debug_triggered = false
  76.  
  77.  
  78. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  79. ------------INITIALIZATIONS-----------------------------------------------------------------------------------------------------------------------------------------------------------------
  80.  
  81. function on_game_start()
  82.  
  83.     RegisterScriptCallback("actor_on_update", actor_on_update)
  84.     RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
  85.     RegisterScriptCallback("actor_on_before_hit", actor_on_before_hit)
  86.     RegisterScriptCallback("actor_on_feeling_anomaly", actor_on_feeling_anomaly)
  87.     RegisterScriptCallback("actor_on_item_use", actor_on_item_use)
  88.     RegisterScriptCallback("actor_on_sleep", actor_on_sleep)
  89.     RegisterScriptCallback("actor_on_hit_callback", actor_on_hit_callback)
  90.  
  91.     RegisterScriptCallback("on_game_load", on_game_load)
  92.     RegisterScriptCallback("save_state", save_state)
  93.     RegisterScriptCallback("load_state", load_state)
  94.  
  95.     RegisterScriptCallback("on_enemy_eval", on_enemy_eval)
  96.  
  97.     RegisterScriptCallback("on_option_change", reinitialization)
  98. end
  99.  
  100.  
  101. function actor_on_feeling_anomaly(obj, flags)
  102.     --dynamic anomaly detection
  103.     anomaly_close = obj and true or false
  104. end
  105.  
  106.  
  107. function on_game_load()
  108.     if (not psy_table.actor_psy_health) then
  109.         set_psy_health(1.0)
  110.     end
  111.     if (not psy_table.actor_zombied) then
  112.         psy_table.actor_zombied = false
  113.     end
  114.     if (not psy_table.current_stage) then
  115.         psy_table.current_stage = c_stage_default
  116.     end
  117. end
  118.  
  119.  
  120. function actor_on_first_update()
  121.     arszi_previous_time = nil
  122.     psy_table.current_stage = c_stage_default
  123.     arszi_zombification_time_counter = 0
  124.  
  125.     init_safe_bases()
  126.     init_static_anomaly_zones()
  127. end
  128.  
  129.  
  130. function save_state(m_data)
  131.     m_data.psy_table = psy_table
  132. end
  133.  
  134.  
  135. function load_state(m_data)
  136.     psy_table = m_data.psy_table or {}
  137. end
  138.  
  139.  
  140. function reinitialization()
  141.     --MCM configuration change
  142.     if ui_mcm then
  143.         c_psy_health_regen_per_second = ( stress_mcm.get_config("regen") or 26) / 2160000
  144.         c_psy_stress_drain_per_second = ( stress_mcm.get_config("drain") or 13) / 2160000
  145.         c_psy_health_regen_campfire_enabled = stress_mcm.get_config("campfire_enabled") or false
  146.         c_psy_health_regen_campfire_bonus = ( stress_mcm.get_config("campfire_regen") or 40 ) / 60000
  147.         c_psy_health_regen_dist_to_campfire = stress_mcm.get_config("campfire_distance") or 15
  148.         c_psy_controler_damage_multiplier = ( stress_mcm.get_config("controler_damage_multiplier") or 40) / 100
  149.         c_controler_damage_max_distance = ( stress_mcm.get_config("controler_damage_max_distance") or 50)
  150.         c_psy_hit_stress_multiplier = ( stress_mcm.get_config("hit_stress") or 2.8) / 100
  151.         c_psy_wound_stress_multiplier = ( stress_mcm.get_config("wounded_stress") or 14) / 600000
  152.         c_psy_stage_1 = ( stress_mcm.get_config("stage_1") or 75) / 100
  153.         c_psy_stage_2 = ( stress_mcm.get_config("stage_2") or 50) / 100
  154.         c_psy_voices = ( stress_mcm.get_config("voices") or 80) / 100
  155.  
  156.         c_psy_stage_2 = math.min(c_psy_stage_2, (c_psy_stage_1 - 0.01))
  157.     end
  158. end
  159.  
  160.  
  161. function init_safe_bases()
  162.     --builds table of non-hostile stalker bases on current level
  163.     local player_faction = gameplay_disguise.get_default_comm()
  164.     local safe_factions = {}
  165.     safe_bases = {}
  166.  
  167.     printdbg("# non-hostile factions: ")
  168.  
  169.     for id, faction in pairs(game_relations.factions_table) do
  170.         local hostile = game_relations.is_factions_enemies(player_faction, faction)
  171.  
  172.         if not hostile then
  173.             printdbg("%s ", faction)
  174.  
  175.             table.insert(safe_factions, faction)
  176.         end
  177.     end
  178.  
  179.     printdbg("# caching safe camps")
  180.  
  181.     for id, smart in pairs(db.smart_terrain_by_id) do
  182.         if smart and smart.is_on_actor_level and smart.props.base > 0 then
  183.             local debug_line = ""
  184.             if DEV_DEBUG then
  185.                 local debug_line = ""
  186.                 debug_line = string.format("base name %s", smart:name())
  187.                 debug_line = string.format("%s - all: %d", debug_line, smart.props.all)
  188.  
  189.                 for id, faction in pairs(game_relations.factions_table) do
  190.                     debug_line = string.format("%s / %s: %d", debug_line, faction, smart.props[faction])
  191.                 end
  192.             end
  193.  
  194.             local is_safe = smart.props.all ~= 0 or false
  195.  
  196.             for id, faction in pairs(safe_factions) do
  197.                 is_safe = is_safe or smart.props[faction] ~= 0
  198.             end
  199.  
  200.             if is_safe then
  201.                 if DEV_DEBUG then
  202.                     debug_line = string.format("base %s - is safe", debug_line)
  203.                     printf(debug_line)
  204.                 end
  205.  
  206.                 table.insert(safe_bases, smart)
  207.             end
  208.         end
  209.     end
  210.  
  211. end
  212.  
  213.  
  214. function init_static_anomaly_zones()
  215.     --builds table of static anomali zones on current level
  216.     printdbg("# caching anomalys zones")
  217.  
  218.     for id, anomaly in pairs(db.anomaly_by_name) do
  219.         if anomaly and anomaly.object then
  220.             printdbg("static anomaly zone %s ", tostring(id))
  221.  
  222.             table.insert(level_static_anomaly, anomaly)
  223.         end
  224.     end
  225. end
  226.  
  227.  
  228. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  229. ------------MAIN SCRIPTS--------------------------------------------------------------------------------------------------------------------------------------------------------------------
  230.  
  231. function actor_on_update()
  232.  
  233.     local curr_time = game.get_game_time()
  234.     if (arszi_previous_time == nil) then arszi_previous_time = curr_time end
  235.  
  236.     time_delta = curr_time:diffSec(arszi_previous_time)
  237.  
  238.     --used to maintain reasonable amount of logs entries in debug mode
  239.     if DEV_DEBUG then
  240.         --nested if, because it would be faster for non-debug mode.
  241.         if debug_time == nil or (curr_time:diffSec(debug_time) > debug_trigger_time) then
  242.             debug_triggered = true
  243.             debug_time = curr_time
  244.             printf(string.format("PSY HEALTH: %.4f; PROT: %.4f CHANGE: %.4e", psy_table.actor_psy_health, get_telepatic_protection_total(), get_psy_health_change()))
  245.         else
  246.             debug_triggered = false
  247.         end
  248.     end
  249.  
  250.     if (time_delta >= 1) then
  251.         arszi_previous_time = curr_time
  252.  
  253.         set_in_combat_flag()
  254.         set_nearby_lit_campfire_flag()
  255.         set_inside_safe_area_flag()
  256.  
  257.         manage_psy_health(time_delta)
  258.  
  259.         manage_ppe_effects()
  260.         manage_sound_effects()
  261.         manage_zombification()
  262.  
  263.         manage_psy_bar()
  264.  
  265.         reset_anomaly_close_flag()
  266.         --Uncomment to see psy prot and psy health
  267.         --show_message(string.format("PSY HEALTH: %f, PSY PROTECTION %f, CHANGE %f", psy_table.actor_psy_health, get_telepatic_protection_total()), get_psy_health_change())
  268.         --Uncomment to see psy config
  269.         --show_message(string.format("current_config: reg %f / drn %f \n cdm %f / cdd %f / hsd %f / wsd %f / stg1 %.2f / stg2 %.2f / vcs %f / cmp %s / cmpdst %u / cmpreg %f |", c_psy_health_regen_per_second, c_psy_stress_drain_per_second, c_psy_controler_damage_multiplier, c_controler_damage_max_distance, c_psy_hit_stress_multiplier, c_psy_wound_stress_multiplier, c_psy_stage_1, c_psy_stage_2, c_psy_voices, tostring(c_psy_health_regen_campfire_enabled), c_psy_health_regen_dist_to_campfire, c_psy_health_regen_campfire_bonus))
  270.     end
  271. end
  272.  
  273.  
  274. function actor_on_item_use(obj)
  275.     if (obj and (obj:section() == "drug_anabiotic")) then
  276.         set_psy_health(1.0)
  277.     end
  278. end
  279.  
  280.  
  281. function on_enemy_eval(obj, enemy, flags)
  282.     --Psy death scene
  283.     if (psy_table.psy_death_scene and enemy:id() == db.actor:id()) then
  284.         flags.override = true
  285.         flags.result = false
  286.         return
  287.     end
  288. end
  289.  
  290.  
  291. function actor_on_before_hit(s_hit)
  292.     --override for Controller PSY attack
  293.     --printf(string.format("HIT POWER: %f HIT TYPE: %d", s_hit.power, s_hit.type)
  294.     if (s_hit.type ~= 4) then return end
  295.     if (is_actor_zombied()) then
  296.         s_hit.power = 0
  297.         return
  298.     end
  299.     if (s_hit.draftsman and is_controller(s_hit.draftsman)) then
  300.         s_hit.power = get_controller_tube_damage(s_hit.draftsman:position(), s_hit.power)
  301.         -- show_message_news("HIT POWER: "..s_hit.power.." DISTANCE: "..get_distance(db.actor:position(), s_hit.draftsman:position()))
  302.     end
  303.     local total_damage = ( s_hit.power - get_telepatic_protection_total() ) * c_psy_controler_damage_multiplier
  304.     total_damage = clamp(total_damage, 0, s_hit.power)
  305.     -- show_message_news("PROTECTION: "..get_telepatic_protection_total().. " RECEIVED DAMAGE: "..total_damage)
  306.     set_psy_health(psy_table.actor_psy_health - total_damage)
  307.     s_hit.power = 0
  308. end
  309.  
  310.  
  311. function actor_on_hit_callback(obj, amount, local_direction, who, bone_id)
  312.     --Stress resulting from physical damage--------------------------
  313.     local c_psy_hit_stress = amount * c_psy_hit_stress_multiplier
  314.     --show_message_news("DAMAGE: "..(amount * 100).."% WOUND STRESS DAMAGE: "..(c_psy_hit_stress * 100).."% PSY HEALTH BEFORE: "..(psy_table.actor_psy_health * 100).." AFTER: "..((psy_table.actor_psy_health - c_psy_hit_stress) * 100))
  315.     set_psy_health(psy_table.actor_psy_health - c_psy_hit_stress)
  316. end
  317.  
  318.  
  319. function actor_on_sleep(hours)
  320.     --Regenerate Psy Health while sleeping
  321.     --Temprorary boosts and stresses are not considered
  322.     local psy_health_regen_while_slept = get_psy_health_regeneration() * 60 * 60 * hours
  323.     if inside_safe_area then
  324.         --Sleep in safe areas is considerable more efficient!
  325.         psy_health_regen_while_slept = psy_health_regen_while_slept * safe_area_bonus^2
  326.     end
  327.     set_psy_health(psy_table.actor_psy_health + psy_health_regen_while_slept)
  328.     arszi_previous_time = nil
  329. end
  330.  
  331.  
  332. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  333. ------------PPE EFFECTS AND SOUND-----------------------------------------------------------------------------------------------------------------------------------------------------------
  334.  
  335. function manage_ppe_effects()
  336.     --manage psy damage state changes and resulting UI/audio effects for non-zombified actor
  337.     if (psy_table.actor_psy_health >= c_psy_stage_1) then
  338.         if (psy_table.current_stage ~= c_stage_0) then
  339.             -- show_message_news("ENTER STAGE 0")
  340.             remove_all_psy_ppe_effects()
  341.             psy_table.current_stage = c_stage_0
  342.         end
  343.     elseif (psy_table.actor_psy_health < c_psy_stage_1 and psy_table.actor_psy_health >= c_psy_stage_2) then
  344.         if (psy_table.current_stage ~= c_stage_1) then
  345.             -- show_message_news("ENTER STAGE 1")
  346.             remove_all_psy_ppe_effects()
  347.             level.add_pp_effector("blur.ppe", c_id_ppe_blur, true)
  348.             psy_table.current_stage = c_stage_1
  349.         end
  350.     else
  351.         if (psy_table.current_stage ~= c_stage_2) then
  352.             -- show_message_news("ENTER STAGE 2")
  353.             remove_all_psy_ppe_effects()
  354.             level.add_pp_effector("blur.ppe", c_id_ppe_blur, true)
  355.             level.add_pp_effector("snd_shock.ppe", c_id_ppe_snd_shock, true)
  356.             psy_table.current_stage = c_stage_2
  357.         end
  358.         level.set_pp_effector_factor(c_id_ppe_snd_shock, get_ppe_intensity())
  359.     end
  360. end
  361.  
  362.  
  363. function manage_zombification()
  364.     --Original community will be restored on load game always. Temp solution.
  365.     if (psy_table.actor_zombied and db.actor:character_community() ~= "actor_zombied") then
  366.         db.actor:set_character_community("actor_zombied", 0, 0)
  367.     end
  368.  
  369.     --Add visual effects
  370.     if (psy_table.actor_zombied) then
  371.         --Todo why is alcohol not working!?
  372.         level.add_pp_effector("radiation.ppe", c_id_ppe_radiation, true)
  373.         level.add_pp_effector("alcohol.ppe", c_id_ppe_alcohol, true)
  374.     end
  375.  
  376.     if (psy_table.actor_psy_health <= 0) then
  377.         --Death or Zombification
  378.         if (psy_table.current_stage ~= c_stage_3) then
  379.             -- show_message_news("ENTER TERMINAL STAGE 6")
  380.             remove_all_psy_ppe_effects()
  381.             level.add_pp_effector("black_infinite.ppe", c_id_ppe_black_infinite, true)
  382.             psy_table.psy_death_scene = true
  383.             level.disable_input()
  384.             psy_table.current_stage = c_stage_3
  385.         end
  386.  
  387.         arszi_zombification_time_counter = arszi_zombification_time_counter + 1
  388.         if (arszi_zombification_time_counter == 1) then
  389.             sound_psy_death_scene:play(db.actor, 0, sound_object.s2d)
  390.             sound_psy_death_scene.volume = c_sound_psy_death_scene_volume
  391.         end
  392.  
  393.         if (arszi_zombification_time_counter == 25) then
  394.             if (fate_of_player_zombfication) then
  395.                 set_psy_health(1.0)
  396.                 --trace_this("GOODWILL BEFORE: "..relation_registry.community_goodwill("stalker", AC_ID))
  397.                 db.actor:set_character_community("actor_zombied", 0, 0)
  398.                 psy_table.actor_zombied = true
  399.                 --game_relations.set_factions_community_num("actor_zombied", "stalker", -5000)
  400.                 --trace_this("GOODWILL AFTER: "..relation_registry.community_goodwill("stalker", AC_ID))
  401.  
  402.                 show_message(game.translate_string("st_psy_zombification_scene"))
  403.             else
  404.                 if (not level_environment.is_actor_immune()) then
  405.                     db.actor:kill(db.actor)
  406.                 end
  407.                 remove_all_psy_ppe_effects()
  408.                 show_message(game.translate_string("st_psy_death_scene"))
  409.             end
  410.  
  411.             level.enable_input()
  412.             psy_table.psy_death_scene = nil
  413.         end
  414.     end
  415. end
  416.  
  417.  
  418. function manage_sound_effects()
  419.     if (psy_table.actor_zombied) then
  420.         if (not sound_zombified:playing()) then
  421.             sound_zombified:play(db.actor, 0, sound_object.s2d)
  422.             -- show_message_news("STARTED ZOMBIFIED SOUND")
  423.         end
  424.         sound_zombified.volume = 7.5 * c_sound_zombified_volume
  425.     end
  426.  
  427.     if (psy_table.actor_psy_health <= c_psy_voices) then
  428.         if (not sound_zombified:playing()) then
  429.             sound_zombified:play(db.actor, 0, sound_object.s2d)
  430.             --static voices volume
  431.             -- sound_zombified.volume = c_sound_zombified_volume
  432.             -- show_message_news("VOICES STARTED")
  433.         end
  434.         --gradually increasing volume
  435.         local whisper_volume = math.max(10*(c_psy_voices - psy_table.actor_psy_health)/c_psy_voices, 1.0) * c_sound_zombified_volume
  436.         sound_zombified.volume = whisper_volume
  437.         -- show_message("WHISPER VOLUME: "..tostring(whisper_volume))
  438.     end
  439.  
  440.     if (not psy_table.actor_zombied and psy_table.actor_psy_health > c_psy_voices) then
  441.         if (sound_zombified:playing()) then
  442.             sound_zombified:stop()
  443.             -- show_message_news("VOICES STOPPED")
  444.         end
  445.     end
  446. end
  447.  
  448.  
  449. function manage_psy_bar()
  450.     local conditions = db.actor:cast_Actor():conditions()
  451.     conditions:SetPsyBar(psy_table.actor_psy_health or 0)
  452. end
  453.  
  454.  
  455. function remove_all_psy_ppe_effects()
  456.     level.remove_pp_effector(c_id_ppe_blur)
  457.     level.remove_pp_effector(c_id_ppe_black_infinite)
  458.     level.remove_pp_effector(c_id_ppe_snd_shock)
  459.     level.remove_pp_effector(c_id_ppe_alcohol)
  460. end
  461.  
  462.  
  463. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  464. ------------PSI HEALTH------------------------------------------------------------------------------------------------------------------------------------------------------------
  465.  
  466. function manage_psy_health(time_delta)
  467.     local new_psy_health = psy_table.actor_psy_health + (get_psy_health_change() * time_delta) -- + get_playing_music_bonus()
  468.     set_psy_health(new_psy_health)
  469. end
  470.  
  471.  
  472. function get_psy_health_change()
  473.     local psy_heal_campfire = (not in_combat) and get_nearby_lit_campfire_bonus() or 0
  474.     local psy_heal_booster = get_telepatic_protection_booster() / c_psy_health_regen_booster_divider
  475.     local psy_heal_artefacts = get_telepatic_protection_artefacts() / c_psy_health_regen_artefacts_divider
  476.     local psy_damage = get_psy_wound_stress() + (c_psy_stress_drain_per_second * get_stress_multiplier())
  477.  
  478.     -- show_message("psy_damage:"..psy_damage)
  479.     --psy regenaration works only together with active boosters or nearby a calm campfire.
  480.     --would be better to remove it and unify calcluation, but that would require rebalancing of all
  481.     --psy protecting items.
  482.     if (psy_heal_booster == 0) and (psy_heal_campfire == 0) then
  483.         -- dbglog("PSY DAMAGE", psy_heal_artefacts - psy_damage)
  484.         return psy_heal_artefacts - psy_damage
  485.     else
  486.         -- dbglog("PSY DAMAHE/HEAL", c_psy_health_regen_per_second + psy_heal_booster + psy_heal_campfire + psy_heal_artefacts - psy_damage)
  487.         return c_psy_health_regen_per_second + psy_heal_booster + psy_heal_campfire + psy_heal_artefacts - psy_damage
  488.     end
  489. end
  490.  
  491.  
  492. -- function get_playing_music_bonus()
  493. --     if itms_manager.playing_instrument then
  494. --         local music_psy_heal = c_psy_health_regen_campfire_bonus * itms_manager.playing_music_length * 75
  495. --         local instrument_factor = musical_instrument_bonus[itms_manager.playing_instrument] or 1
  496. --         local stress_factor = clamp((db.actor.health + psy_table.actor_psy_health) / (2 * math.max(get_stress_multiplier(), 1)), 0.25, 1.00)
  497. --         music_psy_heal = music_psy_heal * instrument_factor
  498. --         music_psy_heal = apply_area_and_campfire_bonus(music_psy_heal) * stress_factor
  499.  
  500. --         itms_manager.playing_instrument = nil
  501. --         itms_manager.playing_music_length = 0
  502.  
  503. --         return music_psy_heal
  504. --     end
  505. --     return 0
  506. -- end
  507.  
  508.  
  509. function get_psy_health_regeneration()
  510.     local bonus_psy_heal_booster = get_telepatic_protection_booster() / c_psy_health_regen_booster_divider
  511.     local bonus_psy_heal_artefacts = get_telepatic_protection_artefacts() / c_psy_health_regen_artefacts_divider
  512.     local bonus_psy_heal_campfire = get_nearby_lit_campfire_bonus()
  513.     return c_psy_health_regen_per_second + bonus_psy_heal_booster + bonus_psy_heal_artefacts + bonus_psy_heal_campfire
  514. end
  515.  
  516.  
  517. function get_nearby_lit_campfire_bonus()
  518.     if not c_psy_health_regen_campfire_enabled then return 0 end
  519.     if nearby_lit_campfire then return c_psy_health_regen_campfire_bonus end
  520.     return 0
  521. end
  522.  
  523.  
  524. function get_psy_wound_stress()
  525.     local health_factor = math.min(2 * (1 - db.actor.health)^2, 1)
  526.     local bleed_factor = 1 + math.max(db.actor.bleeding, 0) * 4
  527.     local c_psy_wound_stress = c_psy_wound_stress_multiplier * health_factor * bleed_factor
  528.     return math.max(c_psy_wound_stress, 0)
  529. end
  530.  
  531.  
  532. function get_stress_multiplier()
  533.     local stress_multiplier = 1
  534.     local state_surface = not GetEvent("underground")
  535.     local state_safe_camp = state_surface and inside_safe_area
  536.     local state_emission_cover = state_surface and GetEvent("current_safe_cover")
  537.     local state_surge = state_surface and ( GetEvent("surge", "state") or GetEvent("psi_storm", "state") )
  538.     local state_anomaly_zone_close = is_anomaly_zone_close()
  539.  
  540.     ------------------------------------------------------------------------------
  541.     if debug_triggered then
  542.         printf(string.format( "current states:" ))
  543.         printf(string.format( "%s / %s / %s / %s / %s / %s / %s |", state_emission_cover and "surge protected" or "no surge protection", state_surge and "surge ongoing" or "clear sky", state_surface and "on surface" or "underground", state_safe_camp and "in non-hostile camp" or "no camp", state_anomaly_zone_close and "inside static anomaly zone" or "no static anomaly zone", anomaly_close and "anomaly close" or "no anomalies", in_combat and "in combat" or "not fighting"))
  544.     end
  545.     ------------------------------------------------------------------------------
  546.  
  547.     --calming conditions
  548.     if state_emission_cover then stress_multiplier                  = stress_multiplier - 1.25 end
  549.     if (not in_combat) and state_safe_camp then stress_multiplier   = stress_multiplier - 0.50 end
  550.     --stressfull conditions
  551.     if state_surge then stress_multiplier                           = stress_multiplier + 1.25 end
  552.     if not state_surface then stress_multiplier                     = stress_multiplier + 0.33 end
  553.     if state_anomaly_zone_close then tress_multiplier               = stress_multiplier + 0.50 end
  554.     if anomaly_close then stress_multiplier                         = stress_multiplier + 0.50 end
  555.     if in_combat then stress_multiplier                             = stress_multiplier + 0.66 end
  556.     stress_multiplier = stress_multiplier + (get_bleeding_level() * 0.66)
  557.     stress_multiplier = stress_multiplier + (actor_status_sleep.get_sleep_deprivation(true) or 0 * 0.50)
  558.     stress_multiplier = stress_multiplier + (actor_status_thirst.get_water_deprivation(true) or 0 * 0.20)
  559.     stress_multiplier = stress_multiplier + (actor_status.get_satienty(true) * 0.20)
  560.  
  561.     return math.max(stress_multiplier, 0)
  562. end
  563.  
  564.  
  565. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  566. ------------TELEPATIC PROTECTION------------------------------------------------------------------------------------------------------------------------------------------------------------
  567.  
  568. function get_telepatic_protection_outfit()
  569.     local telepatic_protection = 0
  570.     local outfit = db.actor:item_in_slot(7)
  571.     if (outfit) then
  572.         local c_outfit = outfit:cast_CustomOutfit()
  573.         if (c_outfit) then
  574.             telepatic_protection = c_outfit:GetDefHitTypeProtection(HitTypeID["Telepatic"]) or 0
  575.         end
  576.     end
  577.     return telepatic_protection
  578. end
  579.  
  580.  
  581. function get_telepatic_protection_helmet()
  582.     local telepatic_protection = 0
  583.     local helm = db.actor:item_in_slot(12)
  584.     if (helm) then
  585.         local c_helm = helm:cast_Helmet()
  586.         if (c_helm) then
  587.             telepatic_protection = c_helm:GetDefHitTypeProtection(HitTypeID["Telepatic"]) or 0
  588.         end
  589.     end
  590.     return telepatic_protection
  591. end
  592.  
  593.  
  594. function get_telepatic_protection_artefacts()
  595.     local telepatic_protection = 0
  596.     db.actor:iterate_belt( function(owner, obj)
  597.         local cond = obj:condition()
  598.         local immunities_sec = SYS_GetParam(0,obj:section(),"hit_absorbation_sect")
  599.         telepatic_protection = telepatic_protection + ( cond * SYS_GetParam(2, immunities_sec, "telepatic_immunity", 0) )
  600.     end)
  601.     return telepatic_protection
  602. end
  603.  
  604.  
  605. function get_telepatic_protection_booster()
  606.     local telepatic_protection = 0
  607.     local booster_type_psi = 6
  608.     db.actor:cast_Actor():conditions():BoosterForEach( function(booster_type, booster_time, booster_value)
  609.         if (booster_type == booster_type_psi) then
  610.             telepatic_protection = booster_value
  611.         end
  612.     end)
  613.     telepatic_protection = apply_area_and_campfire_bonus(telepatic_protection)
  614.     return telepatic_protection
  615. end
  616.  
  617.  
  618. function get_telepatic_protection_total()
  619.     local psy_prot_outfit = get_telepatic_protection_outfit()
  620.     local psy_prot_helmet = get_telepatic_protection_helmet()
  621.     local psy_prot_artefacts = get_telepatic_protection_artefacts()
  622.     local psy_prot_booster = get_telepatic_protection_booster()
  623.     return psy_prot_outfit + psy_prot_helmet + psy_prot_artefacts + psy_prot_booster
  624. end
  625.  
  626.  
  627. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  628. -----------STATE VERIFICATION-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  629.  
  630. function set_nearby_lit_campfire_flag()
  631.     local campfire = bind_campfire.get_nearby_campfire(c_psy_health_regen_dist_to_campfire, true)
  632.     if campfire and campfire:is_on() then
  633.         if nearby_lit_campfire then return end
  634.         nearby_lit_campfire = true
  635.         return
  636.     end
  637.     if nearby_lit_campfire then
  638.         nearby_lit_campfire = false
  639.     end
  640. end
  641.  
  642.  
  643. function set_inside_safe_area_flag()
  644.     if debug_triggered then printf(string.format("safe bases")) end
  645.     for id, base in pairs(safe_bases) do
  646.         if debug_triggered then printf(string.format("safe base: %s, distance %.2f", base:name(), base.dist_to_actor or 9999)) end
  647.         if base and base.dist_to_actor and base.dist_to_actor < safe_distance_to_base then
  648.             if debug_triggered then printf(string.format("actor inside: %s", base:name(), base.dist_to_actor)) end
  649.             if inside_safe_area then return end
  650.             inside_safe_area = true
  651.             return
  652.         end
  653.     end
  654.     if inside_safe_area then
  655.         inside_safe_area = false
  656.     end
  657. end
  658.  
  659.  
  660. function set_in_combat_flag()
  661.     in_combat = (not (is_empty(xr_combat_ignore.fighting_with_actor_npcs))) or false
  662. end
  663.  
  664.  
  665. function reset_anomaly_close_flag()
  666.     anomaly_close = false
  667. end
  668.  
  669.  
  670. function is_anomaly_zone_close()
  671.     if debug_triggered then printf(string.format("local anomalous zones")) end
  672.  
  673.     local actor_position = db.actor:position()
  674.  
  675.     for id, static_anomaly in pairs(level_static_anomaly) do
  676.  
  677.         local zone_distance = static_anomaly.object:position() and get_distance(actor_position, static_anomaly.object:position()) or nil
  678.         if debug_triggered and actor_position and zone_distance then printf(string.format("static anomaly: %s, distance %.2f", static_anomaly.object:name(), zone_distance )) end
  679.  
  680.         if static_anomaly and zone_distance and zone_distance < danger_distance_static_anomaly then
  681.             if debug_triggered then printf(string.format("actor inside static anomaly: %s, distance %.2f, anomaly_close flag: %s", static_anomaly.object:name(), zone_distance, tostring(anomaly_close) )) end
  682.             return true
  683.         end
  684.     end
  685.     return false
  686. end
  687.  
  688.  
  689. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  690. -----------UTILITY/MISC METHODS-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  691.  
  692. function apply_area_and_campfire_bonus(psy_heal)
  693.     -- Calming areas provides bonuses to psy health restoration
  694.     if not psy_heal then return 0 end
  695.     if nearby_lit_campfire then
  696.         psy_heal = psy_heal * campfire_bonus
  697.     end
  698.     if inside_safe_area then
  699.         psy_heal = psy_heal * safe_area_bonus
  700.     end
  701.     return psy_heal
  702. end
  703.  
  704.  
  705. function get_bleeding_level()
  706.     if db.actor.bleeding and db.actor.bleeding > 0.01 then
  707.         return clamp(math.ceil(db.actor.bleeding * 4) + 1, 1, 4)
  708.     end
  709.     return 0
  710. end
  711.  
  712.  
  713. function get_ppe_intensity()
  714.     local health_factor = 1 - psy_table.actor_psy_health
  715.     return health_factor * c_ppe_max_intensity
  716. end
  717.  
  718.  
  719. function get_controller_tube_damage(obj_position, power)
  720.     local distance = get_distance(db.actor:position(), obj_position)
  721.     --this does something wierd, replaced with linear scale from max distance to 0.1 of max distance
  722.     --probaly the intention was power * (1 - 100 * distance / c_controler_damage_max_distance)
  723.     --local damage = power - (distance * (c_controler_damage_max_distance / 100))
  724.     --linear scale from max distance to 0.1 of max distance
  725.     --so if distance is 120 meters, closer than 12 meters there would be full damage, 50% at 66m, 0.9% at 129 and 0 at 120
  726.     local damage = power * (c_controler_damage_max_distance - distance) / (0.9 * c_controler_damage_max_distance )
  727.     if (damage <= 0) then return 0 end
  728.     return math.min(damage, power)
  729. end
  730.  
  731.  
  732. function is_actor_zombied()
  733.     return db.actor:character_community() == "actor_zombied"
  734. end
  735.  
  736.  
  737. function is_controller(obj)
  738.     return obj:clsid() == clsid.controller_s
  739. end
  740.  
  741.  
  742. function get_distance(position_1, position_2)
  743.     local distance = 0.01 * distance_2d_sqr(position_1, position_2)
  744.     return distance
  745. end
  746.  
  747.  
  748. function set_psy_health(amount)
  749.     psy_table.actor_psy_health = clamp(amount, 0.0, 1.0)
  750. end
  751.  
  752.  
  753. function show_message(msg)
  754.     actor_menu.set_msg(1, tostring(msg), 4)
  755. end
  756.  
  757.  
  758. function show_message_news(message)
  759.     news_manager.send_tip(db.actor, tostring(message), nil, nil, 10000)
  760. end
  761.  
  762.  
  763. function dbglog(type,msg,...)
  764.     printdbg("- DEBUG "..tostring(type).." LOG: "..tostring(msg,...))
  765. end
  766.  
  767. -- function trace_this(to_trace)
  768. --     local log_file = io.open("log_arszi_psy.txt", "a")
  769. --     log_file:write(to_trace.."\n")
  770. --     log_file:close(log_file)
  771. -- end
  772.  
Advertisement
Comments
  • renard162
    1 year (edited)
    # text 1.49 KB | 0 0
    1. Fixes:
    2. - Many typos, undeclared variables, wrong values (like c_psy_stage_2 = math.min(c_psy_stage_2, (c_psy_stage_1 - 1) in line 43, that result in negative c_psy_stage_2 blocking PSY STAGE 2 from being activated)
    3. - Fixed state_anomaly_zone_close check in stress_multiplier calculation.
    4.  
    5. Refactorization:
    6. - Replaced many local functions to Anomaly's native functions, like printdbg, bind_campfire.get_nearby_campfire and clamp.
    7. - Replaced some checks by math.min and math.max to increase readability
    8.  
    9. Optimizations:
    10. - Changed some check functions to flag setter and replacing the function calling to flag read to prevent repeated checks per tick.
    11.  
    12. Implementations:
    13. - Bleed, Thirst, Hunger and Sleepiness increases stress_multiplier.
    14. - Bleed increases psy_wound_stress.
    15. - Increased the curve rate of psy_wound_stress (max psy damage still the same, but now the max damage occur with ~30% of HP).
    16. - campfire regeneration bonus is applied only if player is not in combat (check in line 473).
    17. - Stay in base or nearly campfire increases booster effect.
    18. - Sleep inside bases have more psy heal effect than use sleepbag.
    19. - Adjusted whisper sound volume and progressive volume increase (to return to initial volume, just change c_sound_zombified_volume in line 57 to 4).
    20. - Musical instruments recovers psy health (Commented due necessity of editing itms_manager.script, to re-enable uncomment the function get_playing_music_bonus() and the bonus sum in line 467 and apply the modifications to itms_manager.script).
Add Comment
Please, Sign In to add comment
Advertisement