4429 lines
155 KiB
Plaintext
4429 lines
155 KiB
Plaintext
--[[
|
|
- actor_hits_npc: most damage talents executed here
|
|
- actor_before_hit: most defense talents executed here
|
|
- npc_hit: when npc hit npc and on npc death
|
|
- all_functions: most talent functions
|
|
- other_callbacks: other talents/functions/cooldowns/durations executed here on other callbacks
|
|
--]]
|
|
|
|
talents_key = talents_mcm.get_config("talents_key")
|
|
talents_dbg = talents_mcm.get_config("talents_dbg")
|
|
|
|
local ctime_to_t = utils_data.CTime_to_table
|
|
local t_to_ctime = utils_data.CTime_from_table
|
|
local gt = game.translate_string
|
|
|
|
picked_class = "null"
|
|
|
|
|
|
cooldowns_t = {}
|
|
durations_t = {}
|
|
|
|
-- sniper vars
|
|
local deep_wound_var = {}
|
|
cooldowns_t.evasion_cd = 0
|
|
|
|
-- trooper vars
|
|
local thrill_var = false
|
|
cooldowns_t.precision_var = 0
|
|
local precision_allow_dmg = false
|
|
durations_t.barrage_var = 0
|
|
local collector_var = 0
|
|
|
|
durations_t.suppresion_fire_var = 0
|
|
cooldowns_t.cautious_var = 0
|
|
durations_t.leap_var = 0
|
|
|
|
-- jugger vars
|
|
durations_t.fury_var = 0
|
|
|
|
durations_t.dismorale_var = 0
|
|
durations_t.sturdy_var = 0
|
|
cooldowns_t.robust_cd = 0
|
|
|
|
-- monster hunter vars
|
|
local monster_hunter_trophies = {}
|
|
durations_t.fierce_var = 0
|
|
fierce_stacks = 0
|
|
local deadly_blow_var = true -- small 0.15 delay against shotguns and explosives
|
|
|
|
durations_t.special_diet_var = 0
|
|
local kindred_cd = nil
|
|
local kindred_var = nil
|
|
local kindred_prev_lvl = nil
|
|
|
|
-- merc vars
|
|
local merc_head_hunter_t = {}
|
|
local modified_bullets_var = {}
|
|
durations_t.overdrive_var = 0
|
|
durations_t.bullseye_var = 0
|
|
local liquidator_var = true -- small 0.15 delay against shotguns and explosives
|
|
|
|
durations_t.chem_reliant_var = 0
|
|
durations_t.grenadier_var = 0
|
|
durations_t.flurry_var = 0
|
|
durations_t.reposition_var = 0
|
|
|
|
-- assassin vars
|
|
durations_t.spot_weakness_var = 0
|
|
spot_weakness_stacks = 0
|
|
|
|
local acrobatic_var = false
|
|
cooldowns_t.cunning_cd = 0
|
|
|
|
-- endurance
|
|
durations_t.adrenaline_rush_var = 0
|
|
cooldowns_t.adrenaline_rush_var = 0
|
|
|
|
durations_t.courage_var = 0
|
|
local second_wind_cd = nil
|
|
|
|
-- speech
|
|
local artistic_cd = nil
|
|
local cynical_cd = nil
|
|
----------------
|
|
|
|
-- machine guns table
|
|
local machine_guns_t = {
|
|
-- vanilla
|
|
wpn_pkm = true, wpn_pkm_zulus = true, wpn_pkp = true, wpn_rpd = true,
|
|
wpn_m249 = true, wpn_rpk = true, wpn_rpk74 = true,
|
|
-- boomstick
|
|
wpn_pkm_siber = true, wpn_pkp_siber = true, wpn_pk_siber = true,
|
|
wpn_pkm_zenit_siber = true, wpn_pkp_tac_siber = true, wpn_pkm_zenit_shorty = true,
|
|
}
|
|
|
|
-- hit type conversion
|
|
local hit_convert = {
|
|
[hit.burn] = "burn",
|
|
[hit.chemical_burn] = "chem",
|
|
[hit.shock] = "shock",
|
|
[hit.radiation] = "radi",
|
|
[hit.wound] = "rup",
|
|
[hit.strike] = "strike",
|
|
[hit.fire_wound] = "bal",
|
|
[hit.explosion] = "expl",
|
|
[hit.telepatic] = "psi",
|
|
}
|
|
|
|
local class_special_stats = {
|
|
["sniper"] = { speed = 10, weight = -10 },
|
|
["trooper"] = { speed = 0, weight = 0 },
|
|
["jugger"] = { speed = -10, weight = 10 },
|
|
["monster_hunter"] = { speed = 5, weight = -5 },
|
|
["merc"] = { speed = -5, weight = 5 },
|
|
["assassin"] = { speed = 10, weight = -10 },
|
|
}
|
|
|
|
local descr_sizes_t = {
|
|
[1] = 0.75, [2] = 1, [3] = 1.25, [4] = 1.5, [5] = 2,
|
|
}
|
|
|
|
talents_table = {
|
|
|
|
["offensive"] = {
|
|
["sniper"] = {
|
|
sniper_rifles = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, wpns = {"w_sniper"}, damage = {3, 6, 10}, crit = {2, 3, 5}, descr = {"damage", "crit"} },
|
|
assault_rifles_novice = { level = 0, max = 3, req = {all = 0}, pos = { x = 2, y = 0 }, wpns = {"w_rifle"}, damage = {3, 6, 10}, crit = {1, 2, 3}, descr = {"damage", "crit"} },
|
|
pistols = { level = 0, max = 3, req = {all = 0}, pos = { x = 4, y = 0 }, wpns = {"w_pistol"}, damage = {5, 10, 15}, crit = {3, 6, 10}, descr = {"damage", "crit"} },
|
|
|
|
experienced_sniper = { level = 0, max = 1, req = {all = 3}, pos = { x = 1, y = 1 }, wpns = {"w_sniper"}, crit = {10}, descr = {"crit"}, func = "talent_experienced_sniper" },
|
|
|
|
steady = { level = 0, max = 3, req = {all = 6, experienced_sniper = 1}, pos = { x = 0, y = 2 }, wpns = {"w_sniper"}, crit = {3, 6, 10}, descr = {"crit"}, func = "talent_steady", hinder = "convenience" },
|
|
convenience = { level = 0, max = 3, req = {all = 6, experienced_sniper = 1}, pos = { x = 2, y = 2 }, wpns = {"w_sniper"}, crit = {5, 10, 15}, descr = {"crit"}, func = "talent_convenience", hinder = "steady" },
|
|
gunslinger = { level = 0, max = 1, req = {all = 6, pistols = 3}, pos = { x = 4, y = 2 }, wpns = {"w_pistol"}, damage = {15}, crit = {15}, reload = {40}, descr = {"damage", "crit", "reload"}, descr_size = 2, func = "talent_gunslinger" },
|
|
|
|
sharpshooter = { level = 0, max = 1, req = {all = 9, steady = 3}, pos = { x = 0, y = 3 }, wpns = {"w_sniper"}, crit = {0.05}, descr = {"crit"}, func = "talent_sharpshooter" },
|
|
ambush = { level = 0, max = 1, req = {all = 9, convenience = 3}, pos = { x = 2, y = 3 }, wpns = {"w_sniper"}, crit = {20}, descr = {"crit"}, func = "talent_ambush" },
|
|
deep_wound = { level = 0, max = 2, req = {all = 9}, pos = { x = 4, y = 3 }, wpns = {"w_sniper"}, bleed = {15, 30}, descr = {"bleed"} },
|
|
|
|
loner = { level = 0, max = 3, req = {all = 12}, pos = { x = 0, y = 4 }, wpns = {"w_sniper", "w_rifle"}, damage = {3, 6, 10}, descr = {"damage"}, func = "talent_loner" },
|
|
soft_spot = { level = 0, max = 3, req = {all = 12}, pos = { x = 2, y = 4 }, wpns = {"w_sniper"}, crit = {3, 6, 10}, descr = {"crit"}, func = "talent_soft_spot" },
|
|
nocturnal = { level = 0, max = 3, req = {all = 12}, pos = { x = 4, y = 4 }, wpns = {"w_sniper", "w_pistol"}, crit = {5, 10, 15}, descr = {"crit"}, func = "talent_nocturnal" },
|
|
|
|
carnage = { level = 0, max = 1, req = {all = 15}, pos = { x = 2, y = 5 }, wpns = {"w_sniper"}, damage = {10}, crit_damage = {50}, descr = {"damage", "crit_damage"} },
|
|
},
|
|
|
|
["trooper"] = {
|
|
assault_rifles = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, wpns = {"w_rifle"}, damage = {3, 6, 10}, descr = {"damage"} },
|
|
shotguns_novice = { level = 0, max = 3, req = {all = 0}, pos = { x = 2, y = 0 }, wpns = {"w_shotgun"}, damage = {5, 10, 15}, descr = {"damage"} },
|
|
submachine_guns = { level = 0, max = 3, req = {all = 0}, pos = { x = 4, y = 0 }, wpns = {"w_smg"}, damage = {3, 6, 10}, descr = {"damage"} },
|
|
|
|
experienced_rifleman = { level = 0, max = 1, req = {all = 3}, pos = { x = 1, y = 1 }, wpns = {"w_rifle"}, crit = {2}, descr = {"crit"} },
|
|
demolition_expert = { level = 0, max = 2, req = {all = 3}, pos = { x = 3, y = 1 }, wpns = {"w_explosive", "w_base"}, damage = {10, 20}, weight = {25, 50}, descr = {"damage", "weight"} },
|
|
|
|
duelist = { level = 0, max = 3, req = {all = 6, experienced_rifleman = 1}, pos = { x = 0, y = 2 }, wpns = {"w_rifle"}, damage = {3, 6, 10}, descr = {"damage"}, func = "talent_duelist", hinder = "focus" },
|
|
focus = { level = 0, max = 3, req = {all = 6, experienced_rifleman = 1}, pos = { x = 2, y = 2 }, wpns = {"w_rifle"}, crit = {0.01, 0.02, 0.03}, descr = {"crit"}, func = "talent_focus", hinder = "duelist" },
|
|
|
|
thrill = { level = 0, max = 1, req = {all = 9, duelist = 3}, pos = { x = 0, y = 3 }, wpns = {"w_rifle"}, damage = {15}, descr = {"damage"}, func = "talent_thrill" },
|
|
precision = { level = 0, max = 1, req = {all = 9, focus = 3}, pos = { x = 2, y = 3 }, wpns = {"w_rifle"}, damage = {5}, crit = {5}, descr = {"cooldown", "damage", "crit"}, cooldown = 1.2, descr_size = 2, func = "talent_precision" },
|
|
barrage = { level = 0, max = 1, req = {all = 9, submachine_guns = 3}, pos = { x = 4, y = 3 }, wpns = {"w_smg"}, damage = {10}, crit = {3}, descr = {"duration", "damage", "crit"}, duration = 3, descr_size = 2, func = "talent_barrage" },
|
|
|
|
reckless = { level = 0, max = 3, req = {all = 12}, pos = { x = 1, y = 4 }, wpns = {"w_rifle", "w_shotgun"}, damage = {0.07, 0.14, 0.2}, descr = {"damage"}, func = "talent_reckless" },
|
|
veteran_rifleman = { level = 0, max = 3, req = {all = 12}, pos = { x = 3, y = 4 }, wpns = {"w_rifle", "w_smg"}, crit = {1, 2, 3}, descr = {"crit"} },
|
|
|
|
collector = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, wpns = {"w_rifle"}, damage = {0.01}, descr = {"damage"}, descr_size = 5, func = "talent_collector" },
|
|
},
|
|
|
|
["jugger"] = {
|
|
shotguns = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, wpns = {"w_shotgun"}, damage = {2, 3, 5}, descr = {"damage"} },
|
|
machine_guns = { level = 0, max = 3, req = {all = 0}, pos = { x = 2, y = 0 }, wpns = {"w_machine_gun"}, damage = {2, 3, 5}, descr = {"damage"} },
|
|
submachine = { level = 0, max = 2, req = {all = 0}, pos = { x = 4, y = 0 }, wpns = {"w_smg"}, damage = {5, 10}, descr = {"damage"} },
|
|
|
|
hunter = { level = 0, max = 3, req = {all = 3}, pos = { x = 0, y = 1 }, wpns = {"w_shotgun", "w_machine_gun", "w_smg"}, damage = {2, 3, 5}, descr = {"damage"}, func = "talent_hunter" },
|
|
bounty_hunter = { level = 0, max = 3, req = {all = 3}, pos = { x = 2, y = 1 }, wpns = {"w_shotgun", "w_machine_gun", "w_smg"}, damage = {2, 3, 5}, descr = {"damage"}, func = "talent_bounty_hunter" },
|
|
mangle = { level = 0, max = 2, req = {all = 3}, pos = { x = 4, y = 1 }, wpns = {"w_melee"}, damage = {15, 30}, crit = {15, 30}, descr = {"damage", "crit"} },
|
|
|
|
heavy_guns_expert = { level = 0, max = 3, req = {all = 6}, pos = { x = 1, y = 2 }, wpns = {"w_shotgun", "w_machine_gun"}, damage = {2, 3, 5}, reload = {7, 13, 20}, descr = {"damage", "reload"} },
|
|
|
|
merciless = { level = 0, max = 3, req = {all = 9, heavy_guns_expert = 3}, pos = { x = 0, y = 3 }, wpns = {"w_shotgun", "w_machine_gun"}, damage = {3, 6, 10}, descr = {"damage"}, func = "talent_merciless", hinder = "fury" },
|
|
fury = { level = 0, max = 3, req = {all = 9, heavy_guns_expert = 3}, pos = { x = 2, y = 3 }, wpns = {"all"}, damage = {2, 3, 5}, descr = {"duration", "damage"}, duration = 20, func = "talent_fury", hinder = "merciless" },
|
|
bloodthirst = { level = 0, max = 1, req = {all = 9, mangle = 2}, pos = { x = 4, y = 3 }, wpns = {"w_melee"}, crit_damage = {50}, descr = {"crit_damage"} },
|
|
|
|
execution = { level = 0, max = 1, req = {all = 12, merciless = 3}, pos = { x = 0, y = 4 }, wpns = {"w_shotgun", "w_machine_gun"}, damage = {10}, hp_thresh = 40, descr = {"hp_thresh", "damage"}, func = "talent_execution" },
|
|
frightening = { level = 0, max = 1, req = {all = 12, fury = 3}, pos = { x = 2, y = 4 }, wpns = {"all"}, damage = {5}, radius = 75, descr = {"radius", "damage"}, func = "talent_frightening" },
|
|
selective = { level = 0, max = 2, req = {all = 12}, pos = { x = 4, y = 4 }, wpns = {"w_shotgun", "w_machine_gun"}, damage = {2, 5}, descr = {"damage"}, func = "talent_selective" },
|
|
|
|
mastery = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, wpns = {"w_shotgun", "w_machine_gun"}, reload = {10}, descr = {"reload"}, descr_size = 3, merciless_bonus = 5, execution_bonus = 15, fury_bonus = 5, frightening_bonus = 25 },
|
|
},
|
|
|
|
["monster_hunter"] = {
|
|
dog_hunter = { level = 0, max = 2, req = {all = 0}, pos = { x = 0, y = 0 }, wpns = {"all"}, damage = {7, 15}, descr = {"damage"}, func = "talent_dog_hunter" },
|
|
boar_hunter = { level = 0, max = 3, req = {all = 0}, pos = { x = 2, y = 0 }, wpns = {"all"}, damage = {5, 10, 15}, descr = {"damage"}, func = "talent_boar_hunter" },
|
|
zombie_hunter = { level = 0, max = 3, req = {all = 0}, pos = { x = 4, y = 0 }, wpns = {"all"}, damage = {5, 10, 15}, descr = {"damage"}, func = "talent_zombie_hunter" },
|
|
|
|
trophies = { level = 0, max = 1, req = {all = 3}, pos = { x = 1, y = 1 }, wpns = {"all"}, damage = {0.5}, descr = {"damage"}, descr_size = 4, func = "talent_trophies" },
|
|
deep_cut = { level = 0, max = 2, req = {all = 3}, pos = { x = 4, y = 1 }, wpns = {"w_melee"}, damage = {10, 20}, crit = {25, 50}, descr = {"damage", "crit"} },
|
|
|
|
beast_anatomy = { level = 0, max = 3, req = {all = 6, trophies = 1}, pos = { x = 0, y = 2 }, wpns = {"all"}, damage = {3, 6, 10}, descr = {"damage"}, descr_size = 2, func = "talent_beast_anatomy", hinder = "humanoid_anatomy" },
|
|
humanoid_anatomy = { level = 0, max = 3, req = {all = 6, trophies = 1}, pos = { x = 2, y = 2 }, wpns = {"all"}, damage = {3, 6, 10}, descr = {"damage"}, descr_size = 2, func = "talent_humanoid_anatomy", hinder = "beast_anatomy" },
|
|
expert_hunter = { level = 0, max = 2, req = {all = 6}, pos = { x = 4, y = 2 }, wpns = {"all"}, damage = {5, 10}, descr = {"damage"}, func = "talent_expert_hunter" },
|
|
|
|
beast_rivalry = { level = 0, max = 1, req = {all = 9, beast_anatomy = 3}, pos = { x = 0, y = 3 }, wpns = {"all"}, crit = {5}, descr = {"damage", "crit"}, func = "talent_beast_rivalry" },
|
|
humanoid_rivalry = { level = 0, max = 1, req = {all = 9, humanoid_anatomy = 3}, pos = { x = 2, y = 3 }, wpns = {"all"}, crit = {5}, descr = {"damage", "crit"}, func = "talent_humanoid_rivalry" },
|
|
master_hunter = { level = 0, max = 2, req = {all = 9}, pos = { x = 4, y = 3 }, wpns = {"all"}, damage = {5, 10}, descr = {"damage"}, func = "talent_master_hunter" },
|
|
|
|
fierce = { level = 0, max = 3, req = {all = 12}, pos = { x = 1, y = 4 }, wpns = {"all"}, damage = {1, 2, 3}, duration = 30, descr = {"duration", "damage"}, func = "talent_fierce" },
|
|
ranger = { level = 0, max = 3, req = {all = 12}, pos = { x = 3, y = 4 }, wpns = {"all"}, crit = {3, 6, 10}, descr = {"damage", "crit"}, func = "talent_ranger" },
|
|
|
|
deadly_blow = { level = 0, max = 1, req = {all = 15}, pos = { x = 2, y = 5 }, wpns = {"w_sniper", "w_rifle", "w_pistol", "w_shotgun", "w_smg", "w_machine_gun", "w_melee"}, func = "talent_deadly_blow" },
|
|
},
|
|
|
|
["merc"] = {
|
|
firearm_enthusiast = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, wpns = {"all"}, damage = {2, 3, 5}, descr = {"damage"} },
|
|
combat_engineer = { level = 0, max = 3, req = {all = 0}, pos = { x = 2, y = 0 }, wpns = {"w_explosive"}, damage = {10, 20, 30}, percents = {10, 20, 30}, descr = {"damage", "percents"} },
|
|
overdrive = { level = 0, max = 2, req = {all = 0}, pos = { x = 4, y = 0 }, wpns = {"all"}, damage = {5, 10}, crit = {2, 4}, duration = 600, descr = {"duration", "damage", "crit"}, descr_size = 2, func = "talent_overdrive" },
|
|
|
|
head_hunter = { level = 0, max = 1, req = {all = 3}, pos = { x = 1, y = 1 }, wpns = {"all"}, damage = {1}, descr = {"damage"}, descr_size = 3, func = "talent_head_hunter" },
|
|
arrogance = { level = 0, max = 2, req = {all = 3}, pos = { x = 4, y = 1 }, wpns = {"all"}, damage = {1, 2}, descr = {"damage"}, func = "talent_arrogance" },
|
|
|
|
aim_torso = { level = 0, max = 3, req = {all = 6, head_hunter = 1}, pos = { x = 0, y = 2 }, wpns = {"all"}, damage = {3, 6, 10}, descr = {"damage"}, func = "talent_aim_torso", hinder = "aim_limbs" },
|
|
aim_limbs = { level = 0, max = 3, req = {all = 6, head_hunter = 1}, pos = { x = 2, y = 2 }, wpns = {"all"}, crit = {2, 4, 6}, descr = {"crit"}, func = "talent_aim_limbs", hinder = "aim_torso" },
|
|
|
|
modified_bullets = { level = 0, max = 1, req = {all = 9, aim_torso = 3}, pos = { x = 0, y = 3 }, wpns = {"all"}, bleed = {0.5}, duration = 10, descr = {"duration", "bleed"} },
|
|
bullseye = { level = 0, max = 1, req = {all = 9, aim_limbs = 3}, pos = { x = 2, y = 3 }, wpns = {"all"}, crit = {10}, duration = 15, descr = {"duration", "crit"}, func = "talent_bullseye" },
|
|
searcher = { level = 0, max = 1, req = {all = 9, arrogance = 2}, pos = { x = 4, y = 3 }, wpns = {"all"} },
|
|
|
|
assault_rifles_expert = { level = 0, max = 3, req = {all = 12}, pos = { x = 0, y = 4 }, wpns = {"w_rifle"}, damage = {3, 6, 10}, crit = {1, 2, 3}, reload = {5, 10, 15}, descr = {"damage", "crit", "reload"} },
|
|
light_guns_expert = { level = 0, max = 2, req = {all = 12}, pos = { x = 2, y = 4 }, wpns = {"w_pistol", "w_smg"}, damage = {5, 10}, crit = {2, 5}, reload = {10, 20}, descr = {"damage", "crit", "reload"} },
|
|
sniper_rifles_expert = { level = 0, max = 3, req = {all = 12}, pos = { x = 4, y = 4 }, wpns = {"w_sniper"}, damage = {2, 3, 5}, crit = {5, 10, 15}, descr = {"damage", "crit"} },
|
|
|
|
liquidator = { level = 0, max = 1, req = {all = 15}, pos = { x = 2, y = 5 }, wpns = {"all"}, crit_damage = {20}, descr = {"crit_damage"} },
|
|
},
|
|
|
|
["assassin"] = {
|
|
smg_novice = { level = 0, max = 2, req = {all = 0}, pos = { x = 0, y = 0 }, wpns = {"w_smg"}, damage = {5, 10}, crit = {3, 5}, descr = {"damage", "crit"} },
|
|
handgun_novice = { level = 0, max = 2, req = {all = 0}, pos = { x = 2, y = 0 }, wpns = {"w_pistol"}, damage = {4, 8}, crit = {5, 10}, descr = {"damage", "crit"} },
|
|
knifeman = { level = 0, max = 3, req = {all = 0}, pos = { x = 4, y = 0 }, wpns = {"w_melee"}, damage = {10, 20 , 30}, crit = {10, 20, 30}, attack_speed = {13, 26, 40}, descr = {"damage", "crit", "attack_speed"} },
|
|
|
|
spot_weakness = { level = 0, max = 1, req = {all = 3}, pos = { x = 1, y = 1 }, wpns = {"w_smg", "w_pistol", "w_melee"}, crit = {3}, duration = 3, descr = {"crit"}, descr_size = 3, func = "talent_spot_weakness" },
|
|
|
|
undercover = { level = 0, max = 3, req = {all = 6, spot_weakness = 1}, pos = { x = 0, y = 2 }, wpns = {"w_smg", "w_pistol"}, damage = {5, 10, 15}, descr = {"damage"}, func = "talent_undercover", hinder = "tactical" },
|
|
tactical = { level = 0, max = 3, req = {all = 6, spot_weakness = 1}, pos = { x = 2, y = 2 }, wpns = {"w_smg", "w_pistol"}, crit = {5, 10, 15}, descr = {"crit"}, func = "talent_tactical", hinder = "undercover" },
|
|
backstab = { level = 0, max = 1, req = {all = 6, knifeman = 3}, pos = { x = 4, y = 2 }, wpns = {"w_melee"}, crit = {40}, crit_damage = {50}, descr = {"crit", "crit_damage"}, func = "talent_backstab" },
|
|
|
|
saboteur = { level = 0, max = 1, req = {all = 9, undercover = 3}, pos = { x = 0, y = 3 }, wpns = {"w_smg", "w_pistol"}, damage = {15}, descr = {"damage"}, descr_size = 2, func = "talent_saboteur" },
|
|
surprise_attack = { level = 0, max = 1, req = {all = 9, tactical = 3}, pos = { x = 2, y = 3 }, wpns = {"w_smg", "w_pistol"}, crit = {25}, crit_damage = {100}, descr = {"crit", "crit_damage"}, descr_size = 2, func = "talent_surprise_attack" },
|
|
assassination = { level = 0, max = 1, req = {all = 9, backstab = 1}, pos = { x = 4, y = 3 }, wpns = {"w_melee"}, crit_damage = {200}, descr = {"crit_damage"}, func = "talent_assassination" },
|
|
|
|
vile = { level = 0, max = 3, req = {all = 12}, pos = { x = 1, y = 4 }, wpns = {"w_smg"}, damage = {7, 14, 20}, descr = {"damage"}, func = "talent_vile" },
|
|
pragmatic = { level = 0, max = 3, req = {all = 12}, pos = { x = 3, y = 4 }, wpns = {"w_pistol"}, crit = {7, 14, 20}, crit_damage = {17, 34, 50}, descr = {"crit", "crit_damage"}, descr_size = 2, func = "talent_pragmatic" },
|
|
|
|
assassin_observant = { level = 0, max = 1, req = {all = 15}, pos = { x = 2, y = 5 }, wpns = {"all"} },
|
|
},
|
|
},
|
|
|
|
|
|
|
|
["defensive"] = {
|
|
["sniper"] = {
|
|
adaptive = { level = 0, max = 2, req = {all = 0}, pos = { x = 0, y = 0 }, outfits = {"o_medium", "o_light"}, burn = {7, 15}, chem = {7, 15}, shock = {7, 15}, descr = {"burn", "chem", "shock"} },
|
|
mild = { level = 0, max = 1, req = {all = 0}, pos = { x = 2, y = 0 }, outfits = {"o_medium", "o_light"} },
|
|
iron_stomach = { level = 0, max = 2, req = {all = 0}, pos = { x = 4, y = 0 }, outfits = {"o_medium", "o_light"}, radi = {5, 10}, rad_food = {25, 50}, descr = {"radi", "rad_food"} },
|
|
|
|
plastic_pads = { level = 0, max = 3, req = {all = 3}, pos = { x = 0, y = 1 }, outfits = {"o_medium", "o_light"}, rup = {3, 6, 10}, strike = {3, 6, 10}, descr = {"rup", "strike"} },
|
|
aluminum_plates = { level = 0, max = 3, req = {all = 3}, pos = { x = 2, y = 1 }, outfits = {"o_medium", "o_light"}, bal = {3, 6, 10}, expl = {3, 6, 10}, descr = {"bal", "expl"} },
|
|
|
|
nature_resistance = { level = 0, max = 3, req = {all = 6}, pos = { x = 1, y = 2 }, outfits = {"o_medium", "o_light"}, burn = {7, 13, 20}, chem = {7, 13, 20}, shock = {7, 13, 20}, radi = {5, 10, 15}, descr = {"burn", "chem", "shock", "radi"} },
|
|
rad_resistant = { level = 0, max = 1, req = {all = 6, iron_stomach = 2}, pos = { x = 4, y = 2 }, outfits = {"o_medium", "o_light"}, radi = {10}, descr = {"radi"}, descr_size = 2 },
|
|
|
|
technician = { level = 0, max = 1, req = {all = 9, nature_resistance = 3}, pos = { x = 0, y = 3 }, outfits = {"o_medium", "o_light"}, burn = {10}, chem = {10}, shock = {10}, radi = {10}, rup = {10}, strike = {10}, bal = {10}, expl = {10}, descr = {"burn", "chem", "shock", "radi", "rup", "strike", "bal", "expl"}, descr_size = 3, hinder = "inventor" },
|
|
inventor = { level = 0, max = 1, req = {all = 9, nature_resistance = 3}, pos = { x = 2, y = 3 }, outfits = {"o_light"}, hinder = "technician" },
|
|
|
|
expert_technician = { level = 0, max = 3, req = {all = 12, technician = 1}, pos = { x = 0, y = 4 }, outfits = {"o_medium", "o_light"}, burn = {0.05, 0.1, 0.15}, chem = {0.05, 0.1, 0.15}, shock = {0.05, 0.1, 0.15}, radi = {0.05, 0.1, 0.15}, descr = {"burn", "chem", "shock", "radi"}, descr_size = 2, func = "talent_expert_technician" },
|
|
agile = { level = 0, max = 3, req = {all = 12, inventor = 1}, pos = { x = 2, y = 4 }, outfits = {"o_light"}, burn = {5, 10, 15}, chem = {5, 10, 15}, shock = {5, 10, 15}, radi = {5, 10, 15}, rup = {5, 10, 15}, strike = {5, 10, 15}, bal = {5, 10, 15}, expl = {5, 10, 15}, evade = {5, 10, 15}, descr = {"burn", "chem", "shock", "radi", "rup", "strike", "bal", "expl", "evade"}, descr_size = 4, func = "talent_agile" },
|
|
|
|
reflexes = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, outfits = {"o_medium", "o_light"}, cooldown = 5 },
|
|
},
|
|
|
|
["trooper"] = {
|
|
ceramic_plates = { level = 0, max = 2, req = {all = 0}, pos = { x = 0, y = 0 }, outfits = {"o_medium", "o_heavy", "o_sci"}, rup = {2, 5}, strike = {2, 5}, bal = {2, 5}, expl = {2, 5}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 2, func = "talent_trooper_general_armor" },
|
|
first_aid = { level = 0, max = 2, req = {all = 0}, pos = { x = 2, y = 0 }, outfits = {"o_medium", "o_heavy", "o_sci"}, heal = {10, 20}, descr = {"heal"} },
|
|
isolated = { level = 0, max = 2, req = {all = 0}, pos = { x = 4, y = 0 }, outfits = {"o_medium", "o_heavy", "o_sci"}, shock = {5, 10}, descr = {"shock"}, func = "talent_trooper_general_armor" },
|
|
|
|
armorer = { level = 0, max = 2, req = {all = 3}, pos = { x = 1, y = 1 }, outfits = {"o_medium", "o_heavy", "o_sci"}, rup = {2, 5}, strike = {2, 5}, bal = {2, 5}, expl = {2, 5}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 2, func = "talent_trooper_general_armor" },
|
|
chem_resistant = { level = 0, max = 2, req = {all = 3}, pos = { x = 4, y = 1 }, outfits = {"o_medium", "o_heavy", "o_sci"}, chem = {5, 10}, descr = {"chem"}, func = "talent_trooper_general_armor" },
|
|
|
|
suppresion_fire = { level = 0, max = 3, req = {all = 6, armorer = 2}, pos = { x = 0, y = 2 }, outfits = {"o_medium", "o_heavy", "o_sci"}, rup = {2, 4, 6}, strike = {2, 4, 6}, bal = {2, 4, 6}, expl = {2, 4, 6}, descr = {"duration", "rup", "strike", "bal", "expl"}, descr_size = 2, duration = 5, func = "talent_suppresion_fire", hinder = "cautious" },
|
|
cautious = { level = 0, max = 3, req = {all = 6, armorer = 2}, pos = { x = 2, y = 2 }, outfits = {"o_medium", "o_sci"}, rup = {3, 6, 10}, strike = {3, 6, 10}, bal = {3, 6, 10}, expl = {3, 6, 10}, descr = {"rup", "strike", "bal", "expl", "cooldown"}, descr_size = 3, cooldown = 20, func = "talent_cautious", hinder = "suppresion_fire" },
|
|
fireproof = { level = 0, max = 2, req = {all = 6}, pos = { x = 4, y = 2 }, outfits = {"o_medium", "o_heavy", "o_sci"}, burn = {5, 10}, descr = {"burn"}, func = "talent_trooper_general_armor" },
|
|
|
|
composure = { level = 0, max = 1, req = {all = 9, suppresion_fire = 3}, pos = { x = 0, y = 3 }, outfits = {"o_medium", "o_heavy", "o_sci"}, rup = {5}, strike = {5}, bal = {5}, expl = {5}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 4, func = "talent_composure" },
|
|
enduring = { level = 0, max = 1, req = {all = 9, cautious = 3}, pos = { x = 2, y = 3 }, outfits = {"o_medium", "o_sci"}, burn = {10}, chem = {10}, shock = {10}, descr = {"burn", "chem", "shock"}, descr_size = 2 },
|
|
|
|
adorned = { level = 0, max = 3, req = {all = 12}, pos = { x = 0, y = 4 }, outfits = {"o_medium", "o_heavy", "o_sci"}, burn = {0.7, 1.3, 2}, chem = {0.7, 1.3, 2}, shock = {0.7, 1.3, 2}, descr = {"burn", "chem", "shock"}, descr_size = 3, func = "talent_adorned" },
|
|
expert_armorer = { level = 0, max = 3, req = {all = 12}, pos = { x = 2, y = 4 }, outfits = {"o_medium", "o_heavy", "o_sci"}, burn = {0.3, 0.6, 1}, chem = {0.3, 0.6, 1}, shock = {0.3, 0.6, 1}, descr = {"burn", "chem", "shock"}, descr_size = 3, func = "talent_expert_armorer" },
|
|
last_stand = { level = 0, max = 3, req = {all = 12}, pos = { x = 4, y = 4 }, outfits = {"o_medium", "o_heavy", "o_sci"}, burn = {7, 13, 20}, chem = {7, 13, 20}, shock = {7, 13, 20}, rup = {7, 13, 20}, strike = {7, 13, 20}, bal = {7, 13, 20}, expl = {7, 13, 20}, descr = {"burn", "chem", "shock", "rup", "strike", "bal", "expl"}, descr_size = 3, func = "talent_last_stand" },
|
|
|
|
leap = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, outfits = {"o_medium", "o_heavy", "o_sci"}, rup = {15}, strike = {15}, bal = {15}, expl = {15}, descr = {"duration", "rup", "strike", "bal", "expl"}, descr_size = 3, duration = 5, func = "talent_leap" },
|
|
},
|
|
|
|
["jugger"] = {
|
|
steel_plates = { level = 0, max = 2, req = {all = 0}, pos = { x = 0, y = 0 }, outfits = {"o_medium", "o_heavy"}, bal = {5, 10}, expl = {5, 10}, descr = {"bal", "expl"} },
|
|
steel_pads = { level = 0, max = 2, req = {all = 0}, pos = { x = 2, y = 0 }, outfits = {"o_medium", "o_heavy"}, rup = {5, 10}, strike = {5, 10}, descr = {"rup", "strike"} },
|
|
regenerative = { level = 0, max = 1, req = {all = 0}, pos = { x = 4, y = 0 }, outfits = {"o_medium", "o_heavy"} },
|
|
|
|
heavy_armor_expert = { level = 0, max = 3, req = {all = 3}, pos = { x = 1, y = 1 }, outfits = {"o_medium", "o_heavy"}, rup = {2, 3, 5}, strike = {2, 3, 5}, bal = {2, 3, 5}, expl = {2, 3, 5}, psi = {3, 6, 10}, weight = {10, 20, 30}, descr = {"rup", "strike", "bal", "expl", "psi", "weight"}, descr_size = 2 },
|
|
|
|
dismorale = { level = 0, max = 3, req = {all = 6, heavy_armor_expert = 3}, pos = { x = 0, y = 2 }, outfits = {"o_medium", "o_heavy"}, rup = {2, 4, 7}, strike = {2, 4, 7}, bal = {2, 4, 7}, expl = {2, 4, 7}, descr = {"duration", "rup", "strike", "bal", "expl"}, descr_size = 2, duration = 20, func = "talent_dismorale", hinder = "sturdy" },
|
|
sturdy = { level = 0, max = 3, req = {all = 6, heavy_armor_expert = 3}, pos = { x = 2, y = 2 }, outfits = {"o_medium", "o_heavy"}, rup = {2, 4, 6}, strike = {2, 4, 6}, bal = {2, 4, 6}, expl = {2, 4, 6}, descr = {"duration", "rup", "strike", "bal", "expl"}, descr_size = 2, duration = 20, func = "talent_sturdy", hinder = "dismorale" },
|
|
sanity = { level = 0, max = 2, req = {all = 6}, pos = { x = 4, y = 2 }, outfits = {"o_medium", "o_heavy"}, psi = {15, 30}, descr = {"psi"} },
|
|
|
|
anxiety = { level = 0, max = 1, req = {all = 9, dismorale = 3}, pos = { x = 0, y = 3 }, outfits = {"o_medium", "o_heavy"}, rup = {5}, strike = {5}, bal = {5}, expl = {5}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 2 },
|
|
turmoil = { level = 0, max = 1, req = {all = 9, sturdy = 3}, pos = { x = 2, y = 3 }, outfits = {"o_medium", "o_heavy"}, rup = {10}, strike = {10}, bal = {10}, expl = {10}, descr = {"rup", "strike", "bal", "expl", "radius"}, descr_size = 3, radius = 60, func = "talent_turmoil" },
|
|
|
|
bulky = { level = 0, max = 3, req = {all = 12}, pos = { x = 0, y = 4 }, outfits = {"o_medium", "o_heavy"}, rup = {5, 10, 15}, strike = {5, 10, 15}, bal = {5, 10, 15}, expl = {5, 10, 15}, speed = {-15}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 2 },
|
|
secure = { level = 0, max = 3, req = {all = 12}, pos = { x = 2, y = 4 }, outfits = {"o_medium", "o_heavy"}, rup = {1.7, 3.5, 5}, strike = {1.7, 3.5, 5}, bal = {1.7, 3.5, 5}, expl = {1.7, 3.5, 5}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 2, func = "talent_secure" },
|
|
lucid = { level = 0, max = 3, req = {all = 12, sanity = 2}, pos = { x = 4, y = 4 }, outfits = {"o_medium", "o_heavy"}, rup = {0.03, 0.06, 0.1}, strike = {0.03, 0.06, 0.1}, bal = {0.03, 0.06, 0.1}, expl = {0.03, 0.06, 0.1}, descr = {"rup", "strike", "bal", "expl"}, descr_size = 2, func = "talent_lucid" },
|
|
|
|
robust = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, outfits = {"o_medium", "o_heavy"}, rup = {50}, strike = {50}, bal = {50}, expl = {50}, descr = {"rup", "strike", "bal", "expl", "cooldown"}, descr_size = 2, cooldown = 240, func = "talent_robust" },
|
|
},
|
|
|
|
["monster_hunter"] = {
|
|
shoulder_pads = { level = 0, max = 2, req = {all = 0}, pos = { x = 1, y = 0 }, outfits = {"all"}, rup = {2, 5}, strike = {2, 5}, descr = {"rup", "strike"}, func = "talent_monster_hunter_general_armor" },
|
|
careful = { level = 0, max = 2, req = {all = 0}, pos = { x = 3, y = 0 }, outfits = {"all"}, burn = {5, 10}, chem = {5, 10}, shock = {5, 10}, descr = {"burn", "chem", "shock"}, func = "talent_monster_hunter_general_armor" },
|
|
|
|
melee_defense = { level = 0, max = 2, req = {all = 3}, pos = { x = 0, y = 1 }, outfits = {"all"}, rup = {7, 15}, strike = {7, 15}, descr = {"rup", "strike"}, func = "talent_melee_defense" },
|
|
special_diet = { level = 0, max = 2, req = {all = 3}, pos = { x = 2, y = 1 }, outfits = {"all"}, burn = {5, 10}, chem = {5, 10}, shock = {5, 10}, psi = {5, 10}, duration = 900, descr = {"duration", "burn", "chem", "shock", "psi"}, descr_size = 2, func = "talent_special_diet" },
|
|
clarity = { level = 0, max = 2, req = {all = 3}, pos = { x = 4, y = 1 }, outfits = {"all"}, psi = {5, 10}, descr = {"psi"}, func = "talent_monster_hunter_general_armor" },
|
|
|
|
tracker = { level = 0, max = 1, req = {all = 6}, pos = { x = 0, y = 2 }, outfits = {"all"} },
|
|
soft_armor = { level = 0, max = 3, req = {all = 6}, pos = { x = 2, y = 2 }, outfits = {"all"}, rup = {3, 6, 10}, strike = {3, 6, 10}, descr = {"rup", "strike"}, func = "talent_monster_hunter_general_armor" },
|
|
stable_mind = { level = 0, max = 1, req = {all = 6, clarity = 2}, pos = { x = 4, y = 2 }, outfits = {"all"}, psi = {20}, descr = {"psi"} },
|
|
|
|
beast_demeanor = { level = 0, max = 1, req = {all = 9, soft_armor = 3}, pos = { x = 1, y = 3 }, outfits = {"all"}, rup = {10}, strike = {10}, descr = {"rup", "strike"}, func = "talent_beast_demeanor", hinder = "humanoid_demeanor" },
|
|
humanoid_demeanor = { level = 0, max = 1, req = {all = 9, soft_armor = 3}, pos = { x = 3, y = 3 }, outfits = {"all"}, rup = {10}, strike = {10}, descr = {"rup", "strike"}, func = "talent_humanoid_demeanor", hinder = "beast_demeanor" },
|
|
|
|
second_skin = { level = 0, max = 3, req = {all = 12}, pos = { x = 1, y = 4 }, outfits = {"all"}, rup = {0.7, 1.4, 2.0}, strike = {0.7, 1.4, 2.0}, descr = {"rup", "strike"}, func = "talent_second_skin" },
|
|
custom_fit = { level = 0, max = 3, req = {all = 12}, pos = { x = 3, y = 4 }, outfits = {"o_medium"}, bal = {5, 10, 15}, expl = {5, 10, 15}, rup = {5, 10, 15}, strike = {5, 10, 15}, burn = {5, 10, 15}, chem = {5, 10, 15}, shock = {5, 10, 15}, psi = {5, 10, 15}, descr = {"bal", "expl", "rup", "strike", "burn", "chem", "shock", "psi"}, descr_size = 3 },
|
|
|
|
kindred = { level = 0, max = 1, req = {all = 15}, pos = { x = 2, y = 5 }, outfits = {"all"}, descr_size = 4, cooldown = 28800 },
|
|
},
|
|
|
|
["merc"] = {
|
|
mercenary_vest = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, outfits = {"o_medium", "o_heavy"}, bal = {2, 4, 7}, expl = {2, 4, 7}, descr = {"bal", "expl"} },
|
|
insensitive = { level = 0, max = 2, req = {all = 0}, pos = { x = 2, y = 0 }, outfits = {"o_medium", "o_heavy"}, burn = {10, 20}, chem = {10, 20}, shock = {10, 20}, descr = {"burn", "chem", "shock"} },
|
|
chem_reliant = { level = 0, max = 3, req = {all = 0}, pos = { x = 4, y = 0 }, outfits = {"o_medium", "o_heavy"}, bal = {4, 8, 13}, expl = {4, 8, 13}, duration = 600, descr = {"duration", "bal", "expl"}, descr_size = 2, func = "talent_chem_reliant" },
|
|
|
|
grenadier = { level = 0, max = 2, req = {all = 3}, pos = { x = 1, y = 1 }, outfits = {"o_medium", "o_heavy"}, bal = {5, 10}, expl = {5, 10}, duration = 20, descr = {"duration", "bal", "expl"}, descr_size = 2, func = "talent_grenadier" },
|
|
|
|
confusion = { level = 0, max = 3, req = {all = 6}, pos = { x = 1, y = 2 }, outfits = {"o_medium", "o_heavy"}, bal = {3, 6, 10}, expl = {3, 6, 10}, descr = {"bal", "expl"}, descr_size = 2, func = "talent_confusion" },
|
|
addictive = { level = 0, max = 1, req = {all = 6, chem_reliant = 3}, pos = { x = 4, y = 2 }, outfits = {"o_medium", "o_heavy"}, burn = {20}, chem = {20}, shock = {20}, descr = {"burn", "chem", "shock"}, descr_size = 2, func = "talent_addictive" },
|
|
|
|
direct = { level = 0, max = 3, req = {all = 9, confusion = 3}, pos = { x = 0, y = 3 }, outfits = {"o_medium", "o_heavy"}, percents = {22, 44, 66}, descr = {"percents"}, hinder = "disguise_master" },
|
|
disguise_master = { level = 0, max = 3, req = {all = 9, confusion = 3}, pos = { x = 2, y = 3 }, outfits = {"o_medium"}, disguise = {17, 34, 50}, descr = {"disguise"}, hinder = "direct" },
|
|
|
|
flurry = { level = 0, max = 1, req = {all = 12, direct = 3}, pos = { x = 0, y = 4 }, outfits = {"o_medium", "o_heavy"}, bal = {10}, expl = {10}, duration = 2, descr = {"duration", "bal", "expl"}, func = "talent_flurry" },
|
|
loose_fit = { level = 0, max = 1, req = {all = 12, disguise_master = 3}, pos = { x = 2, y = 4 }, outfits = {"o_medium"}, bal = {10}, expl = {10}, rup = {20}, strike = {20}, descr = {"bal", "expl", "rup", "strike"} },
|
|
|
|
reposition = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, outfits = {"o_medium", "o_heavy"}, bal = {10}, expl = {10}, duration = 6, descr = {"duration", "bal", "expl"}, descr_size = 2, func = "talent_reposition" },
|
|
},
|
|
|
|
["assassin"] = {
|
|
ordinary = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, outfits = {"all"}, disguise = {2, 3, 5}, descr = {"disguise"}, func = "talent_ordinary" },
|
|
acrobatic = { level = 0, max = 1, req = {all = 0}, pos = { x = 2, y = 0 }, outfits = {"o_medium", "o_sci", "o_light"} },
|
|
quiet = { level = 0, max = 3, req = {all = 0}, pos = { x = 4, y = 0 }, outfits = {"all"}, stealth = {3, 6, 10}, descr = {"stealth"}, func = "talent_quiet" },
|
|
|
|
evanescent = { level = 0, max = 1, req = {all = 3, ordinary = 3}, pos = { x = 0, y = 1 }, outfits = {"all"}, disguise = {5}, descr = {"disguise"}, descr_size = 2 },
|
|
lurking = { level = 0, max = 3, req = {all = 3}, pos = { x = 2, y = 1 }, outfits = {"all"}, disguise = {3, 6, 10}, stealth = {3, 6, 10}, descr = {"disguise", "stealth"}, func = "talent_lurking" },
|
|
subtle = { level = 0, max = 1, req = {all = 3, quiet = 3}, pos = { x = 4, y = 1 }, outfits = {"all"}, stealth = {5}, descr = {"stealth"}, descr_size = 2 },
|
|
|
|
covert = { level = 0, max = 1, req = {all = 6, lurking = 3}, pos = { x = 1, y = 2 }, outfits = {"o_medium", "o_sci"}, bal = {20}, expl = {20}, rup = {20}, strike = {20}, descr = {"bal", "expl", "rup", "strike"}, descr_size = 3, hinder = "cunning" },
|
|
cunning = { level = 0, max = 1, req = {all = 6, lurking = 3}, pos = { x = 3, y = 2 }, outfits = {"o_light"}, evade = {25}, cooldown = 3, descr = {"evade"}, descr_size = 2, hinder = "covert" },
|
|
|
|
deception = { level = 0, max = 3, req = {all = 9}, pos = { x = 0, y = 3 }, outfits = {"all"}, disguise = {2, 3, 5}, descr = {"disguise"}, func = "talent_deception" },
|
|
vague = { level = 0, max = 3, req = {all = 9}, pos = { x = 2, y = 3 }, outfits = {"all"}, disguise = {2, 3, 5}, stealth = {2, 3, 5}, descr = {"disguise", "stealth"}, descr_size = 2, func = "talent_vague" },
|
|
sneaky = { level = 0, max = 3, req = {all = 9}, pos = { x = 4, y = 3 }, outfits = {"all"}, stealth = {2, 3, 5}, descr = {"stealth"}, func = "talent_sneaky" },
|
|
|
|
incognito = { level = 0, max = 3, req = {all = 12, covert = 1}, pos = { x = 1, y = 4 }, outfits = {"o_medium", "o_sci"}, bal = {7, 14, 20}, expl = {7, 14, 20}, rup = {7, 14, 20}, strike = {7, 14, 20}, disguise = {3, 6, 10}, descr = {"bal", "expl", "rup", "strike", "disguise"}, descr_size = 2 },
|
|
ghost = { level = 0, max = 3, req = {all = 12, cunning = 1}, pos = { x = 3, y = 4 }, outfits = {"o_light"}, stealth = {3, 6, 10}, evade = {8, 16, 25}, burn = {15, 30, 45}, chem = {15, 30, 45}, shock = {15, 30, 45}, descr = {"stealth", "evade", "burn", "chem", "shock"}, descr_size = 2 },
|
|
|
|
elusive = { level = 0, max = 1, req = {all = 15}, pos = { x = 2, y = 5 }, outfits = {"o_medium", "o_sci", "o_light"}, disguise = {10}, stealth = {10}, speed = {15}, descr = {"disguise", "stealth"} },
|
|
},
|
|
},
|
|
|
|
|
|
|
|
["endurance"] = {
|
|
["sniper"] = {
|
|
ease = { level = 0, max = 3, req = {all = 0}, pos = { x = 0, y = 0 }, speed = {2, 3, 5}, descr = {"speed"}, func = "talent_ease" },
|
|
refreshing = { level = 0, max = 2, req = {all = 0}, pos = { x = 2, y = 0 }, stamina = {12, 25}, descr = {"stamina"} },
|
|
hiker = { level = 0, max = 2, req = {all = 0}, pos = { x = 4, y = 0 }, weight = {20, 40}, descr = {"weight"} },
|
|
|
|
resting = { level = 0, max = 3, req = {all = 3}, pos = { x = 1, y = 1 }, stamina = {0.33, 0.66, 1}, descr = {"stamina"}, func = "talent_resting" },
|
|
prudent = { level = 0, max = 3, req = {all = 3}, pos = { x = 3, y = 1 }, weight = {10, 20, 30}, descr = {"weight"} },
|
|
|
|
swifty = { level = 0, max = 3, req = {all = 6}, pos = { x = 0, y = 2 }, speed = {2, 3, 5}, descr = {"speed"}, func = "talent_swifty" },
|
|
tireless = { level = 0, max = 3, req = {all = 6}, pos = { x = 2, y = 2 }, stamina = {0.33, 0.66, 1}, descr = {"stamina"}, func = "talent_tireless" },
|
|
hoarder = { level = 0, max = 3, req = {all = 6}, pos = { x = 4, y = 2 }, weight = {2, 4, 6}, descr = {"weight"}, func = "talent_hoarder" },
|
|
|
|
adrenaline_rush = { level = 0, max = 1, req = {all = 9, swifty = 3}, pos = { x = 0, y = 3 }, speed = {15}, cooldown = 300, duration = 5, descr = {"duration", "speed", "cooldown"}, descr_size = 2 },
|
|
second_wind = { level = 0, max = 1, req = {all = 9, tireless = 3}, pos = { x = 2, y = 3 }, stamina = {50}, cooldown = 2400, descr = {"stamina", "cooldown"}, descr_size = 2 },
|
|
organizer = { level = 0, max = 1, req = {all = 9, hoarder = 3}, pos = { x = 4, y = 3 } },
|
|
|
|
insomnia = { level = 0, max = 2, req = {all = 12}, pos = { x = 0, y = 4 }, speed = {2, 5}, descr = {"speed"}, func = "talent_insomnia" },
|
|
courage = { level = 0, max = 2, req = {all = 12}, pos = { x = 2, y = 4 }, stamina = {0.5, 1}, duration = 5, descr = {"duration", "stamina"}, func = "talent_courage" },
|
|
belt_braces = { level = 0, max = 2, req = {all = 12}, pos = { x = 4, y = 4 }, weight = {20, 40}, descr = {"weight"} },
|
|
|
|
athletic = { level = 0, max = 1, req = {all = 15}, pos = { x = 1, y = 5 }, speed = {0.1}, descr = {"speed"}, descr_size = 2, func = "talent_athletic" },
|
|
iron_spine = { level = 0, max = 1, req = {all = 15}, pos = { x = 3, y = 5 }, weight = {0.2}, descr = {"weight"}, descr_size = 2, func = "talent_iron_spine" },
|
|
},
|
|
},
|
|
|
|
|
|
|
|
["speech"] = {
|
|
["sniper"] = {
|
|
preparation = { level = 0, max = 2, req = {all = 0}, pos = { x = 0, y = 0 }, buy = {-2, -5}, descr = {"buy"} },
|
|
confidence = { level = 0, max = 3, req = {all = 0}, pos = { x = 2, y = 0 }, defense = {3, 6, 9}, descr = {"defense"} },
|
|
supplier = { level = 0, max = 2, req = {all = 0}, pos = { x = 4, y = 0 }, sell = {2, 5}, descr = {"sell"} },
|
|
|
|
observant = { level = 0, max = 3, req = {all = 3}, pos = { x = 0, y = 1 }, buy = {-2, -4, -6}, descr = {"buy"} },
|
|
smuggler = { level = 0, max = 3, req = {all = 3}, pos = { x = 4, y = 1 }, sell = {4, 8, 12}, descr = {"sell"} },
|
|
|
|
gregarious = { level = 0, max = 3, req = {all = 6}, pos = { x = 0, y = 2 }, buy = {-2, -3, -5}, descr = {"buy"} },
|
|
encouragement = { level = 0, max = 3, req = {all = 6}, pos = { x = 2, y = 2 }, damage = {2, 4, 6}, descr = {"damage"} },
|
|
persuasion = { level = 0, max = 3, req = {all = 6}, pos = { x = 4, y = 2 }, sell = {2, 3, 5}, descr = {"sell"} },
|
|
|
|
charismatic = { level = 0, max = 3, req = {all = 9}, pos = { x = 0, y = 3 }, free = {3, 6, 10}, descr = {"free"} },
|
|
shady = { level = 0, max = 3, req = {all = 9}, pos = { x = 4, y = 3 }, stalkers_money = {1000, 2000, 3000}, sell = {3, 6, 10}, descr = {"stalkers_money", "sell"} },
|
|
|
|
artistic = { level = 0, max = 1, req = {all = 12, charismatic = 3}, pos = { x = 0, y = 4 }, cooldown = 7200, descr = {"cooldown"} },
|
|
rally = { level = 0, max = 1, req = {all = 12, encouragement = 3}, pos = { x = 2, y = 4 }, crit = {3}, descr = {"crit"} },
|
|
cynical = { level = 0, max = 1, req = {all = 12, shady = 3}, pos = { x = 4, y = 4 }, cooldown = 14400, descr_size = 3 },
|
|
|
|
well_liked = { level = 0, max = 1, req = {all = 15, artistic = 1}, pos = { x = 0, y = 5 } },
|
|
deceitful = { level = 0, max = 1, req = {all = 15, cynical = 1}, pos = { x = 4, y = 5 } },
|
|
},
|
|
},
|
|
|
|
}
|
|
|
|
talents_table["endurance"]["trooper"] = talents_table["endurance"]["sniper"]
|
|
talents_table["endurance"]["jugger"] = talents_table["endurance"]["sniper"]
|
|
talents_table["endurance"]["monster_hunter"] = talents_table["endurance"]["sniper"]
|
|
talents_table["endurance"]["merc"] = talents_table["endurance"]["sniper"]
|
|
talents_table["endurance"]["assassin"] = talents_table["endurance"]["sniper"]
|
|
|
|
talents_table["speech"]["trooper"] = talents_table["speech"]["sniper"]
|
|
talents_table["speech"]["jugger"] = talents_table["speech"]["sniper"]
|
|
talents_table["speech"]["monster_hunter"] = talents_table["speech"]["sniper"]
|
|
talents_table["speech"]["merc"] = talents_table["speech"]["sniper"]
|
|
talents_table["speech"]["assassin"] = talents_table["speech"]["sniper"]
|
|
|
|
-- to change item weights
|
|
local weight_talent_funcs = {
|
|
["demolition_expert"] = { func = "talent_weight_demolition_expert", on = {"ruck", "slot"}, off = {"drop"} },
|
|
["heavy_armor_expert"] = { func = "talent_weight_heavy_armor_expert", on = {"ruck", "slot"}, off = {"drop"} },
|
|
["hiker"] = { func = "talent_weight_hiker", on = {"ruck", "slot"}, off = {"drop"} },
|
|
["prudent"] = { func = "talent_weight_prudent", on = {"ruck", "slot"}, off = {"drop"} },
|
|
["organizer"] = { func = "talent_weight_organizer", on = {"ruck", "slot"}, off = {"drop"} },
|
|
["belt_braces"] = { func = "talent_weight_belt_braces", on = {"belt"}, off = {"ruck", "drop"} },
|
|
}
|
|
|
|
-- mutants table
|
|
local mutants_t = {
|
|
["beast"] = {
|
|
tushkano = "encyclopedia_mutants_tushkano",
|
|
flesh = "encyclopedia_mutants_flesh",
|
|
dog = "encyclopedia_mutants_blind_dog",
|
|
boar = "encyclopedia_mutants_boar",
|
|
cat = "encyclopedia_mutants_cat",
|
|
pseudodog = "encyclopedia_mutants_pseudodog",
|
|
SM_LURKER = "encyclopedia_mutants_lurker",
|
|
chimera = "encyclopedia_mutants_chimera",
|
|
giant = "encyclopedia_mutants_pseudogiant",
|
|
},
|
|
|
|
["hum"] = {
|
|
zombie = "encyclopedia_mutants_zombie",
|
|
fracture = "encyclopedia_mutants_fracture",
|
|
snork = "encyclopedia_mutants_snork",
|
|
SM_POLTER_G = "encyclopedia_mutants_poltergeist",
|
|
SM_PYRO_G = "encyclopedia_mutants_pyrogeist",
|
|
SM_PSEUDO_G = "encyclopedia_mutants_pseudogeist",
|
|
SM_KARLIK = "encyclopedia_mutants_karlik",
|
|
burer = "encyclopedia_mutants_burer",
|
|
bloodsucker = "encyclopedia_mutants_bloodsucker",
|
|
SM_PSYSUCKER = "encyclopedia_mutants_psysucker",
|
|
controller = "encyclopedia_mutants_controller",
|
|
},
|
|
}
|
|
|
|
local stalker_comms_t = {
|
|
stalker = true, monolith = true, csky = true, army = true, bandit = true, killer = true,
|
|
ecolog = true, dolg = true, freedom = true, renegade = true, greh = true,
|
|
}
|
|
|
|
-- stalker torso bones table
|
|
local stalker_bones_t = {
|
|
bip01_l_clavicle = true, bip01_r_clavicle = true, bip01_spine2 = true,
|
|
bip01_spine1 = true, bip01_spine = true, bip01_pelvis = true,
|
|
}
|
|
|
|
-- stalker head bones table
|
|
local stalker_head_bones_t = {
|
|
bip01_neck = true, bip01_head = true, eyelid_1 = true,
|
|
eye_left = true, eye_right = true, jaw_1 = true,
|
|
}
|
|
|
|
|
|
-- actor_hits_npc
|
|
function actor_hit_npc(npc, s_hit, bone_id, flags)
|
|
local draft = s_hit.draftsman
|
|
local draft_fits = draft and (IsStalker(draft) or IsMonster(draft))
|
|
if not (draft_fits and draft.alive and draft:alive() and draft:id() == 0) then return end
|
|
if not (npc:alive()) then return end
|
|
|
|
-- local wpn = draft:active_item()
|
|
local wpn_id = s_hit.weapon_id
|
|
local wpn = wpn_id and level.object_by_id(wpn_id)
|
|
local kind = wpn and ini_sys:r_string_ex(wpn:section(), "kind")
|
|
if not kind then return end
|
|
|
|
-- machine guns
|
|
kind = is_mg_type(wpn:section()) and "w_machine_gun" or kind
|
|
|
|
-- sniper
|
|
if picked_class == "sniper" then
|
|
-- deep_wound
|
|
local deep_wound_t = talents_table["offensive"]["sniper"]["deep_wound"]
|
|
if deep_wound_t.level > 0 and kind == "w_sniper" then
|
|
deep_wound_var[npc:id()] = {}
|
|
deep_wound_var[npc:id()].time = 60000
|
|
deep_wound_var[npc:id()].value = deep_wound_t.bleed[deep_wound_t.level] / 100
|
|
pr("adding bleed val: %s || to id: %s", deep_wound_t.bleed[deep_wound_t.level] / 100, npc:id())
|
|
end
|
|
end
|
|
|
|
-- merc
|
|
if picked_class == "merc" then
|
|
-- modified_bullets
|
|
local modified_bullets_t = talents_table["offensive"]["merc"]["modified_bullets"]
|
|
if modified_bullets_t.level > 0 and IsStalker(npc) then
|
|
local bone_name = bone_id and npc:bone_name(bone_id)
|
|
if bone_name and stalker_bones_t[bone_name] then
|
|
modified_bullets_var[npc:id()] = {}
|
|
modified_bullets_var[npc:id()].time = modified_bullets_t.duration * 1000
|
|
modified_bullets_var[npc:id()].value = modified_bullets_t.bleed[modified_bullets_t.level] / 100
|
|
pr("merc, modified_bullets, add val: %s || to id: %s", modified_bullets_t.bleed[modified_bullets_t.level] / 100, npc:id())
|
|
end
|
|
end
|
|
end
|
|
|
|
-- trooper suppresion_fire
|
|
if picked_class == "trooper" and talents_table["defensive"]["trooper"]["suppresion_fire"].level > 0 then
|
|
durations_t.suppresion_fire_var = talents_table["defensive"]["trooper"]["suppresion_fire"].duration
|
|
end
|
|
|
|
-- endurance courage
|
|
if talents_table["endurance"]["sniper"]["courage"].level > 0 then
|
|
pr("courage, reset duration")
|
|
durations_t.courage_var = talents_table["endurance"]["sniper"]["courage"].duration
|
|
end
|
|
|
|
-- calc actor hit from talents
|
|
local pwr = 80
|
|
local crit = 0
|
|
local crit_dmg = 150
|
|
local class_t = talents_table["offensive"][picked_class]
|
|
|
|
if class_t then
|
|
for name, t in pairs(class_t) do
|
|
-- talent picked and (for all weapons or active weapon matches)
|
|
if t.level > 0 and (in_ar(t.wpns, "all") or in_ar(t.wpns, kind)) then
|
|
pr("----------- OFFENSIVE -----------")
|
|
pr("adding talent: %s || lvl: %s || wpn: %s", name, t.level, kind)
|
|
|
|
-- add damage
|
|
if t.damage then
|
|
local val = t.damage[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](npc, val, bone_id) or 0
|
|
end
|
|
pr("+damage: %s", val)
|
|
pwr = pwr + val
|
|
end
|
|
|
|
-- add crit
|
|
if t.crit then
|
|
local val = t.crit[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](npc, val, bone_id) or 0
|
|
end
|
|
pr("+crit: %s", val)
|
|
crit = crit + val
|
|
end
|
|
|
|
-- add crit damage
|
|
if t.crit_damage then
|
|
local val = t.crit_damage[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](npc, val) or 0
|
|
end
|
|
pr("+crit_damage: %s", val)
|
|
crit_dmg = crit_dmg + val
|
|
end
|
|
|
|
-- trooper barrage set cd
|
|
if name == "barrage" then
|
|
durations_t.barrage_var = talents_table["offensive"]["trooper"]["barrage"].duration
|
|
end
|
|
|
|
-- set thrill var
|
|
if name == "thrill" and thrill_var then
|
|
thrill_var = false
|
|
end
|
|
|
|
-- set fierce duration
|
|
if IsMonster(npc) and name == "fierce" then
|
|
durations_t.fierce_var = talents_table["offensive"]["monster_hunter"]["fierce"].duration
|
|
fierce_stacks = fierce_stacks + 1
|
|
if fierce_stacks > 5 then
|
|
fierce_stacks = 5
|
|
end
|
|
end
|
|
|
|
-- monster hunter deadly blow
|
|
if name == "deadly_blow" and deadly_blow_var then
|
|
deadly_blow_var = false
|
|
CreateTimeEvent("deadly_blow_delay_e", "deadly_blow_delay_a", 0.15, function()
|
|
deadly_blow_var = true
|
|
return true
|
|
end)
|
|
|
|
if talent_deadly_blow(npc, kind) then
|
|
pwr = pwr + 100000
|
|
end
|
|
end
|
|
|
|
-- merc bullseye duration
|
|
if name == "bullseye" then
|
|
local bone_name = bone_id and npc:bone_name(bone_id)
|
|
if bone_name and stalker_head_bones_t[bone_name] then
|
|
CreateTimeEvent("bullseye_check_e_" .. npc:id(), "bullseye_check_a_" .. npc:id(), 0.5, function(id)
|
|
local stalker = id and level.object_by_id(id)
|
|
if stalker and IsStalker(stalker) and (not stalker:alive()) then -- stalker died from this headshot
|
|
durations_t.bullseye_var = talents_table["offensive"]["merc"]["bullseye"].duration
|
|
end
|
|
return true
|
|
end, npc:id())
|
|
end
|
|
end
|
|
|
|
-- merc liquidator
|
|
if name == "liquidator" and liquidator_var then
|
|
liquidator_var = false
|
|
CreateTimeEvent("liquidator_delay_e", "liquidator_delay_a", 0.15, function()
|
|
liquidator_var = true
|
|
return true
|
|
end)
|
|
|
|
if talent_liquidator(npc) then
|
|
pwr = pwr + 100000
|
|
end
|
|
end
|
|
|
|
-- assassin spot_weakness reset
|
|
if name == "spot_weakness" then
|
|
spot_weakness_stacks = 0
|
|
durations_t.spot_weakness_var = 0
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
-- new hit
|
|
local new_hit = s_hit.power * (pwr / 100)
|
|
pr("s_hit.power: %s || pwr: %s% || new_hit: %s", round_idp(s_hit.power, 4), pwr, round_idp(new_hit, 4))
|
|
|
|
-- crit
|
|
if crit > 0 and (math.random(1, 100) <= crit) then
|
|
new_hit = new_hit * (crit_dmg / 100)
|
|
pr("crit proced, new_hit: %s", round_idp(new_hit, 4))
|
|
end
|
|
|
|
-- set hit power
|
|
s_hit.power = new_hit
|
|
pr("~~~~~~~~~~~~~~~~~~~~~")
|
|
|
|
end
|
|
-------------------------------------
|
|
|
|
-- actor_before_hit
|
|
function actor_on_before_hit(s_hit, bone_id, flags)
|
|
if s_hit.power <= 0 then return end
|
|
if s_hit.type == hit.telepatic then return end -- patched below
|
|
|
|
local pwr = 120
|
|
local class_t = talents_table["defensive"][picked_class]
|
|
local hit_type = s_hit.type
|
|
hit_type = hit_type == hit.light_burn and hit.burn or hit_type
|
|
|
|
-- sniper
|
|
local draft = s_hit.draftsman
|
|
if picked_class == "sniper" then
|
|
local outfit = db.actor:item_in_slot(7)
|
|
local kind = outfit and ini_sys:r_string_ex(outfit:section(), "kind")
|
|
|
|
-- mild (fall dmg)
|
|
if s_hit.type == hit.strike and kind and draft and draft:id() == 0 and talents_table["defensive"]["sniper"]["mild"].level > 0 then
|
|
local val = (kind == "o_medium" and 45) or (kind == "o_light" and 70) or 0
|
|
pr("mild val: %s", val)
|
|
pwr = pwr - val
|
|
end
|
|
|
|
-- reflexes (evasion)
|
|
if (cooldowns_t.evasion_cd <= 0) and s_hit.power > 0.05 and kind and (not draft or draft:id() ~= 0) and talents_table["defensive"]["sniper"]["reflexes"].level > 0 then
|
|
local agile_t = talents_table["defensive"]["sniper"]["agile"]
|
|
local agile_evasion = agile_t.level > 0 and agile_t.evade[agile_t.level] or 0
|
|
local evasion_chance = (kind == "o_medium" and 35) or (kind == "o_light" and 35 + agile_evasion) or 0
|
|
if evasion_chance > 0 and math.random(1, 100) <= evasion_chance then
|
|
play_evasion_anm()
|
|
cooldowns_t.evasion_cd = talents_table["defensive"]["sniper"]["reflexes"].cooldown
|
|
s_hit.power = 0
|
|
flags.ret_value = false
|
|
return
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- jugger
|
|
if picked_class == "jugger" then
|
|
-- fury
|
|
if draft and (IsStalker(draft) or IsMonster(draft)) then
|
|
durations_t.fury_var = talents_table["offensive"]["jugger"]["fury"].duration
|
|
end
|
|
end
|
|
|
|
-- endurance adrenaline_rush
|
|
if s_hit.power > 0.2 and talents_table["endurance"]["sniper"]["adrenaline_rush"].level > 0 and cooldowns_t.adrenaline_rush_var <= 0 then
|
|
local adrenaline_rush_t = talents_table["endurance"]["sniper"]["adrenaline_rush"]
|
|
local val = adrenaline_rush_t.speed[adrenaline_rush_t.level]
|
|
pr("adrenaline_rush triggered")
|
|
cooldowns_t.adrenaline_rush_var = adrenaline_rush_t.cooldown
|
|
durations_t.adrenaline_rush_var = adrenaline_rush_t.duration
|
|
speed.add_speed("adrenaline_rush", 1 + val / 100, false, true)
|
|
end
|
|
|
|
-- assassin
|
|
if picked_class == "assassin" then
|
|
local outfit = db.actor:item_in_slot(7)
|
|
local kind = outfit and ini_sys:r_string_ex(outfit:section(), "kind")
|
|
|
|
-- cunning (fall dmg)
|
|
if s_hit.type == hit.strike and kind and draft and draft:id() == 0 and talents_table["defensive"]["assassin"]["cunning"].level > 0 and kind == "o_light" then
|
|
local val = 40
|
|
pr("cunning val: %s", val)
|
|
pwr = pwr - val
|
|
end
|
|
|
|
-- cunning and ghost (evasion)
|
|
if (cooldowns_t.cunning_cd <= 0) and s_hit.power > 0.05 and kind and (not draft or draft:id() ~= 0) and kind == "o_light" then
|
|
local cunning_t = talents_table["defensive"]["assassin"]["cunning"]
|
|
local ghost_t = talents_table["defensive"]["assassin"]["ghost"]
|
|
local cunning_chance = cunning_t.level > 0 and cunning_t.evade[cunning_t.level] or 0
|
|
local ghost_chance = ghost_t.level > 0 and ghost_t.evade[ghost_t.level] or 0
|
|
local evade_chance = cunning_chance + ghost_chance
|
|
pr("assassin, evade chance: %s", evade_chance)
|
|
if math.random(1, 100) <= evade_chance then
|
|
pr("- assassin, evade proc")
|
|
play_evasion_anm()
|
|
cooldowns_t.cunning_cd = cunning_t.cooldown
|
|
s_hit.power = 0
|
|
flags.ret_value = false
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- calc actor resists from talents
|
|
if class_t then
|
|
for name, t in pairs(class_t) do
|
|
if t.level > 0 and (outfit_type(t.outfits) or in_ar(t.outfits, "all")) then
|
|
pr("----------- DEFENSIVE -----------")
|
|
pr("adding talent: %s || lvl: %s || type: %s", name, t.level, hit_convert[hit_type])
|
|
|
|
local t_type = hit_convert[hit_type]
|
|
if t[t_type] then
|
|
local val = t[t_type][t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](val, draft) or 0
|
|
end
|
|
-- if has sniper inventor
|
|
if outfit_type({"o_light"}) and talents_table["defensive"]["sniper"]["inventor"].level > 0 then
|
|
val = val * 2
|
|
end
|
|
|
|
-- if has merc direct
|
|
if picked_class == "merc" and outfit_type({"o_heavy"}) then
|
|
local direct_t = talents_table["defensive"]["merc"]["direct"]
|
|
if direct_t.level > 0 then
|
|
local direct_val = direct_t.percents[direct_t.level] / 100
|
|
val = val * direct_val
|
|
else
|
|
val = 0
|
|
end
|
|
end
|
|
|
|
pr("+res: %s", val)
|
|
pwr = pwr - val
|
|
end
|
|
|
|
-- trooper composure
|
|
if name == "composure" and db.actor.power > 0.5 then
|
|
local stamina_hit = s_hit.power * 0.05 * 2
|
|
if db.actor.power > stamina_hit then
|
|
db.actor.power = db.actor.power - stamina_hit
|
|
end
|
|
end
|
|
|
|
-- jugger sturdy set duration
|
|
if name == "sturdy" then
|
|
durations_t.sturdy_var = t.duration
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- new hit
|
|
local new_hit = s_hit.power * (pwr / 100)
|
|
pr("s_hit.power: %s || pwr: %s% || new_hit: %s", round_idp(s_hit.power, 4), pwr, round_idp(new_hit, 4))
|
|
|
|
-- set hit power
|
|
s_hit.power = new_hit
|
|
pr("~~~~~~~~~~~~~~~~~~~~~")
|
|
|
|
end
|
|
|
|
-- patch arszy psi
|
|
talents_base_arszi_psy_prot = arszi_psy.get_telepatic_protection_total
|
|
function arszi_psy.get_telepatic_protection_total()
|
|
local jugger_defensive_t = talents_table["defensive"]["jugger"]
|
|
local monster_hunter_defensive_t = talents_table["defensive"]["monster_hunter"]
|
|
|
|
local psi_mult = 0.8
|
|
local val = 0
|
|
|
|
-- jugger heavy_armor_expert
|
|
local heavy_armor_expert_t = jugger_defensive_t["heavy_armor_expert"]
|
|
if heavy_armor_expert_t.level > 0 and outfit_type(heavy_armor_expert_t.outfits) then
|
|
val = val + (heavy_armor_expert_t.psi[heavy_armor_expert_t.level] / 100)
|
|
pr("psi, heavy_armor_expert: +%s%", val * 100)
|
|
end
|
|
|
|
-- jugger sanity
|
|
local sanity_t = jugger_defensive_t["sanity"]
|
|
if sanity_t.level > 0 and outfit_type(sanity_t.outfits) then
|
|
val = val + (sanity_t.psi[sanity_t.level] / 100)
|
|
pr("psi, sanity: +%s%", val * 100)
|
|
end
|
|
|
|
-- monster hunter clarity
|
|
local clarity_t = monster_hunter_defensive_t["clarity"]
|
|
if clarity_t.level > 0 then
|
|
local val_mult = outfit_type({"o_medium"}) and 2 or 1
|
|
val = val + val_mult * (clarity_t.psi[clarity_t.level] / 100)
|
|
pr("psi, clarity: +%s%", val * 100)
|
|
end
|
|
|
|
-- monster hunter stable_mind
|
|
local stable_mind_t = monster_hunter_defensive_t["stable_mind"]
|
|
if stable_mind_t.level > 0 then
|
|
val = val + (stable_mind_t.psi[stable_mind_t.level] / 100)
|
|
pr("psi, stable_mind: +%s%", val * 100)
|
|
end
|
|
|
|
return talents_base_arszi_psy_prot() * (psi_mult + val)
|
|
end
|
|
|
|
talents_base_arszi_psy_controller = arszi_psy.get_controller_tube_damage
|
|
function arszi_psy.get_controller_tube_damage(pos, hit_pwr)
|
|
if talents_table["defensive"]["monster_hunter"]["stable_mind"].level > 0 and (math.random(1, 100) <= 35) then
|
|
pr("psi, stable_mind, controller hit mitigated")
|
|
return 0
|
|
end
|
|
|
|
return talents_base_arszi_psy_controller(pos, hit_pwr)
|
|
end
|
|
|
|
-- functions disguise
|
|
function disguise_general_mult(npc)
|
|
local class_t = talents_table["defensive"][picked_class]
|
|
if not class_t then return end
|
|
|
|
local pwr = 100
|
|
|
|
for name, t in pairs(class_t) do
|
|
if t.level > 0 and (outfit_type(t.outfits) or in_ar(t.outfits, "all")) and t.disguise then
|
|
pr("----------- DISGUISE -----------")
|
|
pr("adding talent: %s || lvl: %s", name, t.level)
|
|
|
|
local val = t.disguise[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](val, npc) or 0
|
|
end
|
|
|
|
-- covert bonus
|
|
if class_t["covert"].level > 0 and outfit_type({"o_medium", "o_sci"}) then
|
|
val = val * 1.5
|
|
end
|
|
|
|
pr("+disguise: %s", val)
|
|
pwr = pwr - val
|
|
end
|
|
end
|
|
|
|
if pwr < 10 then
|
|
pwr = 10
|
|
end
|
|
pr("~~~~~~~~~~~~~~~~~~~~~")
|
|
|
|
return pwr / 100
|
|
end
|
|
|
|
function gain_disguise_exp(npc)
|
|
if not (npc and npc:relation(db.actor) >= game_object.enemy and picked_class == "assassin" and talents_leveling and talents_mcm) then return end
|
|
|
|
local dist = db.actor:position():distance_to(npc:position())
|
|
local val = 0.5 * (0.98)^dist
|
|
-- pr("~disguise adding exp, def: %s", val * talents_mcm.get_config("defensive"))
|
|
talents_leveling.add_exp("defensive", val * talents_mcm.get_config("defensive"))
|
|
end
|
|
|
|
base_disguise_calc = gameplay_disguise.calculate_npc_suspicion
|
|
function gameplay_disguise.calculate_npc_suspicion(npc, id, t, awareness)
|
|
local new_val = disguise_general_mult(npc)
|
|
if not new_val then
|
|
return math.floor(clamp(base_disguise_calc(npc, id, t, awareness), 1, 100))
|
|
else
|
|
gain_disguise_exp(npc) -- defensive exp
|
|
return math.floor(clamp(new_val * base_disguise_calc(npc, id, t, awareness), 1, 100))
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions stealth
|
|
function stealth_general_mult(who)
|
|
local class_t = talents_table["defensive"][picked_class]
|
|
if not (who and who:id() == 0 and class_t) then return end
|
|
|
|
local pwr = 100
|
|
|
|
for name, t in pairs(class_t) do
|
|
if t.level > 0 and (outfit_type(t.outfits) or in_ar(t.outfits, "all")) and t.stealth then
|
|
pr("----------- STEALTH -----------")
|
|
pr("adding talent: %s || lvl: %s", name, t.level)
|
|
|
|
local val = t.stealth[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](val) or 0
|
|
end
|
|
|
|
-- cunning bonus
|
|
if class_t["cunning"].level > 0 and outfit_type({"o_light"}) then
|
|
val = val * 1.5
|
|
end
|
|
|
|
pr("+stealth: %s", val)
|
|
pwr = pwr - val
|
|
end
|
|
end
|
|
|
|
if pwr < 10 then
|
|
pwr = 10
|
|
end
|
|
pr("~~~~~~~~~~~~~~~~~~~~~")
|
|
|
|
return pwr / 100
|
|
end
|
|
|
|
function gain_stealth_exp(npc, who)
|
|
if not (npc and npc:relation(db.actor) >= game_object.enemy and picked_class == "assassin" and talents_leveling and talents_mcm) then return end
|
|
if npc:see(db.actor) then return end
|
|
|
|
local dist = db.actor:position():distance_to(npc:position())
|
|
local val = 0.5 * (0.98)^dist
|
|
-- pr("-stealth adding exp, def: %s", val * talents_mcm.get_config("defensive"))
|
|
talents_leveling.add_exp("defensive", val * talents_mcm.get_config("defensive"))
|
|
end
|
|
|
|
if stealth_mcm then -- if has stealth addon
|
|
base_stealth_luminocity_mult = visual_memory_manager.get_luminocity_mult
|
|
function visual_memory_manager.get_luminocity_mult(npc, who, luminocity, object_distance, distance)
|
|
local new_val = stealth_general_mult(who)
|
|
if not new_val then
|
|
return base_stealth_luminocity_mult(npc, who, luminocity, object_distance, distance)
|
|
else
|
|
gain_stealth_exp(npc, who) -- defensive exp
|
|
return new_val * base_stealth_luminocity_mult(npc, who, luminocity, object_distance, distance)
|
|
end
|
|
end
|
|
else -- vanilla
|
|
base_stealth_visible_value = visual_memory_manager.get_visible_value
|
|
function visual_memory_manager.get_visible_value(npc,who,time_delta,time_quant,luminocity,velocity_factor,velocity,distance,object_distance,always_visible_distance)
|
|
local new_val = stealth_general_mult(who)
|
|
if not new_val then
|
|
return base_stealth_visible_value(npc,who,time_delta,time_quant,luminocity,velocity_factor,velocity,distance,object_distance,always_visible_distance)
|
|
else
|
|
gain_stealth_exp(npc, who) -- defensive exp
|
|
return new_val * base_stealth_visible_value(npc,who,time_delta,time_quant,luminocity,velocity_factor,velocity,distance,object_distance,always_visible_distance)
|
|
end
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- npc_hit
|
|
function npc_on_before_hit(npc, s_hit, bone_id, flags) -- not very automatic xd
|
|
local draft = s_hit.draftsman
|
|
local draft_fits = draft and (IsStalker(draft) or IsMonster(draft))
|
|
if not (draft_fits and draft.alive and draft:alive() and draft:id() ~= 0) then return end
|
|
if not (npc:alive()) then return end
|
|
|
|
local pwr = 100
|
|
local crit = 0
|
|
local crit_dmg = 150
|
|
|
|
-- jugger
|
|
if picked_class == "jugger" then
|
|
-- frightening
|
|
local frightening_t = talents_table["offensive"]["jugger"]["frightening"]
|
|
if frightening_t.level > 0 then
|
|
local val = talent_frightening(npc, frightening_t.damage[frightening_t.level]) or 0
|
|
pr("NPC frightening, sec: %s || val: %s", npc:section(), val)
|
|
pwr = pwr + val
|
|
end
|
|
|
|
-- turmoil
|
|
local turmoil_t = talents_table["defensive"]["jugger"]["turmoil"]
|
|
local ac_comm = get_actor_true_community()
|
|
local npc_comm = IsStalker(npc) and npc.character_community and npc:character_community()
|
|
if turmoil_t.level > 0 and npc_comm then
|
|
local npc_friend = not game_relations.is_factions_enemies(ac_comm, npc_comm)
|
|
local draft_actor_dist = draft:position():distance_to(db.actor:position())
|
|
if npc_friend and draft_actor_dist and draft_actor_dist <= turmoil_t.radius then
|
|
local val = turmoil_t.bal[turmoil_t.level]
|
|
pr("NPC turmoil, friend sec: %s || enemy sec: %s || val: %s", npc:section(), draft:section(), val)
|
|
pwr = pwr - val
|
|
end
|
|
end
|
|
end
|
|
|
|
-- speech confidence
|
|
local confidence_t = talents_table["speech"]["sniper"]["confidence"]
|
|
if confidence_t.level > 0 and npc:has_info("npcx_is_companion") then
|
|
local val = confidence_t.defense[confidence_t.level]
|
|
pr("NPC speech confidence, companion (target) sec: %s || shooter sec: %s || val: %s", npc:section(), draft:section(), val)
|
|
pwr = pwr - val
|
|
end
|
|
|
|
-- speech encouragement
|
|
local encouragement_t = talents_table["speech"]["sniper"]["encouragement"]
|
|
if encouragement_t.level > 0 and draft:has_info("npcx_is_companion") then
|
|
local val = encouragement_t.damage[encouragement_t.level]
|
|
pr("NPC speech encouragement, companion sec: %s || target sec: %s || val: %s", draft:section(), npc:section(), val)
|
|
pwr = pwr + val
|
|
end
|
|
|
|
-- speech rally
|
|
local rally_t = talents_table["speech"]["sniper"]["rally"]
|
|
if rally_t.level > 0 and draft:has_info("npcx_is_companion") then
|
|
local val = rally_t.crit[rally_t.level]
|
|
pr("NPC speech rally, companion sec: %s || target sec: %s || val: %s", draft:section(), npc:section(), val)
|
|
crit = crit + val
|
|
end
|
|
|
|
|
|
-- new hit
|
|
local new_hit = s_hit.power * (pwr / 100)
|
|
|
|
-- crit
|
|
if crit > 0 and (math.random(1, 100) <= crit) then
|
|
new_hit = new_hit * (crit_dmg / 100)
|
|
end
|
|
|
|
-- set hit power
|
|
s_hit.power = new_hit
|
|
|
|
end
|
|
|
|
function npc_on_death_callback(npc, who)
|
|
|
|
-- trooper
|
|
if picked_class == "trooper" then
|
|
-- thrill
|
|
if who:id() == 0 and talents_table["offensive"]["trooper"]["thrill"].level > 0 then
|
|
thrill_var = true
|
|
end
|
|
|
|
-- collector
|
|
if who:id() == 0 and talents_table["offensive"]["trooper"]["collector"].level > 0 then
|
|
-- table with price
|
|
local val = get_collector_val(npc)
|
|
if val then
|
|
collector_var = collector_var + val
|
|
if collector_var > 1500 then
|
|
collector_var = 1500
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- jugger
|
|
if picked_class == "jugger" then
|
|
-- dismorale
|
|
if who:id() == 0 and talents_table["defensive"]["jugger"]["dismorale"].level > 0 then
|
|
durations_t.dismorale_var = talents_table["defensive"]["jugger"]["dismorale"].duration
|
|
if talents_table["defensive"]["jugger"]["anxiety"].level > 0 then
|
|
durations_t.dismorale_var = durations_t.dismorale_var + 10
|
|
end
|
|
end
|
|
end
|
|
|
|
-- monster hunter
|
|
if picked_class == "monster_hunter" then
|
|
-- trophies
|
|
local mut_type = check_mutant_type(npc)
|
|
if who:id() == 0 and mut_type and (not monster_hunter_trophies[mut_type]) and talents_table["offensive"]["monster_hunter"]["trophies"].level > 0 then
|
|
monster_hunter_trophies[mut_type] = true
|
|
end
|
|
end
|
|
|
|
-- merc
|
|
if picked_class == "merc" then
|
|
-- head hunter
|
|
if who:id() == 0 and IsStalker(npc) and talents_table["offensive"]["merc"]["head_hunter"].level > 0 then
|
|
local comm = npc.character_community and npc:character_community()
|
|
if comm and stalker_comms_t[comm] and (not merc_head_hunter_t[comm]) then
|
|
merc_head_hunter_t[comm] = true
|
|
end
|
|
end
|
|
|
|
-- searcher
|
|
if who:id() == 0 and IsStalker(npc) and talents_table["offensive"]["merc"]["searcher"].level > 0 then
|
|
local money_ar = { "money_50_100", "money_100_200", "money_200_300" }
|
|
for i = 1, 2 do
|
|
local money_sec = money_ar[math.random(1, #money_ar)]
|
|
if money_sec then
|
|
pr("searcher, spawning %s", money_sec)
|
|
alife_create_item(money_sec, npc)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-------------------------------------
|
|
-- all_functions
|
|
|
|
-- functions attack (sniper)
|
|
function talent_experienced_sniper(npc, val)
|
|
if outfit_type({"o_medium", "o_light"}) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_steady(npc, val)
|
|
if outfit_type({"o_medium", "o_light"}) and IsMoveState('mcCrouch') then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_convenience(npc, val)
|
|
if outfit_type({"o_light"}) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_gunslinger(npc, val)
|
|
local pistol = db.actor:active_item()
|
|
local wpn1 = db.actor:item_in_slot(2)
|
|
local wpn2 = db.actor:item_in_slot(3)
|
|
if (wpn1 and pistol and pistol:id() == wpn1:id()) or (wpn2 and pistol and pistol:id() == wpn2:id()) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_gunslinger_reload(obj) -- reload
|
|
local gunslinger_t = talents_table["offensive"]["sniper"]["gunslinger"]
|
|
if gunslinger_t.level <= 0 then return end
|
|
if not (obj and IsPistol(obj)) then return end
|
|
|
|
local wpn1 = db.actor:item_in_slot(2)
|
|
local wpn2 = db.actor:item_in_slot(3)
|
|
if (wpn1 and obj:id() == wpn1:id()) or (wpn2 and obj:id() == wpn2:id()) then
|
|
local val = 1 + gunslinger_t.reload[gunslinger_t.level] / 100
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_sharpshooter(npc, val)
|
|
-- if outfit_type({"o_medium", "o_light"}) then
|
|
local dist_to = npc:position():distance_to(db.actor:position())
|
|
return val * math.ceil(dist_to)
|
|
-- end
|
|
end
|
|
|
|
function talent_ambush(npc, val)
|
|
if outfit_type({"o_light"}) and (not npc:see(db.actor)) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_loner(npc, val)
|
|
local companions = axr_companions.get_companion_count()
|
|
if companions < 1 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_soft_spot(npc, val)
|
|
local npc_dir = vector():set(npc:direction())
|
|
npc_dir.y = 0
|
|
local dir_to_ac = vector():set(db.actor:position()):sub(npc:position())
|
|
dir_to_ac.y = 0
|
|
local dp = npc_dir:dotproduct(dir_to_ac)
|
|
if dp < 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_nocturnal(npc, val)
|
|
if level.get_time_hours() < 6 or level.get_time_hours() >= 18 then
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions attack (trooper)
|
|
function talent_duelist(npc, val)
|
|
if npc and npc:see(db.actor) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_focus(npc, val)
|
|
if outfit_type({"o_medium", "o_sci"}) then
|
|
return val * db.actor.power * 100
|
|
end
|
|
end
|
|
|
|
function talent_thrill(npc, val)
|
|
if thrill_var then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_precision(npc, val)
|
|
if precision_allow_dmg then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_barrage(npc, val)
|
|
if durations_t.barrage_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_reckless(npc, val)
|
|
local missing_hp = 1 - db.actor.health
|
|
return missing_hp * val * 100
|
|
end
|
|
|
|
function talent_collector(npc, val)
|
|
if collector_var > 0 then
|
|
return collector_var * val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions attack (jugger)
|
|
function talent_hunter(npc, val)
|
|
if IsMonster(npc) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_bounty_hunter(npc, val)
|
|
if IsStalker(npc) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_merciless(npc, val)
|
|
local mastery_t = talents_table["offensive"]["jugger"]["mastery"]
|
|
local extra_val = mastery_t.level > 0 and mastery_t.merciless_bonus or 0
|
|
if npc.health < db.actor.health then
|
|
return val + extra_val
|
|
end
|
|
end
|
|
|
|
function talent_heavy_guns_expert_reload(obj) -- reload
|
|
local heavy_guns_expert_t = talents_table["offensive"]["jugger"]["heavy_guns_expert"]
|
|
if heavy_guns_expert_t.level <= 0 then return end
|
|
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
local kind_fits = (kind and kind == "w_shotgun") or is_mg_type(obj:section())
|
|
if kind_fits then
|
|
local val = 1 + heavy_guns_expert_t.reload[heavy_guns_expert_t.level] / 100
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_fury(npc, val) -- var set in actor_on_before_hit
|
|
local mastery_t = talents_table["offensive"]["jugger"]["mastery"]
|
|
local extra_val = mastery_t.level > 0 and mastery_t.fury_bonus or 0
|
|
if durations_t.fury_var > 0 then
|
|
return val + extra_val
|
|
end
|
|
end
|
|
|
|
function talent_execution(npc, val)
|
|
local exec_t = talents_table["offensive"]["jugger"]["execution"]
|
|
local mastery_t = talents_table["offensive"]["jugger"]["mastery"]
|
|
local threshold = mastery_t.level > 0 and (mastery_t.execution_bonus + exec_t.hp_thresh) or exec_t.hp_thresh
|
|
if npc.health < (threshold / 100) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_frightening(npc, val)
|
|
local ac_comm = get_actor_true_community()
|
|
local npc_comm = IsStalker(npc) and npc.character_community and npc:character_community()
|
|
local npc_enemy = npc_comm and game_relations.is_factions_enemies(ac_comm, npc_comm)
|
|
|
|
if IsMonster(npc) or npc_enemy then
|
|
local fright_t = talents_table["offensive"]["jugger"]["frightening"]
|
|
local mastery_t = talents_table["offensive"]["jugger"]["mastery"]
|
|
local radius = mastery_t.level > 0 and (mastery_t.frightening_bonus + fright_t.radius) or fright_t.radius
|
|
local dist_to = npc:position():distance_to(db.actor:position())
|
|
if dist_to and dist_to < radius then
|
|
return val
|
|
end
|
|
end
|
|
end
|
|
|
|
function talent_selective(npc, val)
|
|
local wpn1 = db.actor:item_in_slot(2)
|
|
local wpn2 = db.actor:item_in_slot(3)
|
|
if (wpn1 and not wpn2) or (wpn2 and not wpn1) then
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions attack (monster hunter)
|
|
function talent_dog_hunter(npc, val)
|
|
local mut_type_t = { "dog", "pseudodog", "cat" }
|
|
local mut_type = check_mutant_type(npc)
|
|
if mut_type and in_ar(mut_type_t, mut_type) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_boar_hunter(npc, val)
|
|
local mut_type_t = { "boar", "flesh", "SM_LURKER" }
|
|
local mut_type = check_mutant_type(npc)
|
|
if mut_type and in_ar(mut_type_t, mut_type) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_zombie_hunter(npc, val)
|
|
local mut_type_t = { "zombie", "fracture", "snork" }
|
|
local mut_type = check_mutant_type(npc)
|
|
if mut_type and in_ar(mut_type_t, mut_type) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_trophies(npc, val)
|
|
local trophies_mult = size_table(monster_hunter_trophies)
|
|
if trophies_mult > 0 then
|
|
return val * trophies_mult
|
|
end
|
|
end
|
|
|
|
function talent_beast_anatomy(npc, val)
|
|
local mut_type = check_mutant_type(npc, true)
|
|
if mut_type and mut_type == "beast" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_humanoid_anatomy(npc, val)
|
|
local mut_type = check_mutant_type(npc, true)
|
|
if mut_type and mut_type == "hum" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_expert_hunter(npc, val)
|
|
local mut_type_t = { "bloodsucker", "burer", "SM_PSYSUCKER", "SM_POLTER_G", "SM_PYRO_G", "SM_PSEUDO_G" }
|
|
local mut_type = check_mutant_type(npc)
|
|
if mut_type and in_ar(mut_type_t, mut_type) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_beast_rivalry(npc, val)
|
|
local mut_type = check_mutant_type(npc, true)
|
|
if mut_type and mut_type == "beast" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_humanoid_rivalry(npc, val)
|
|
local mut_type = check_mutant_type(npc, true)
|
|
if mut_type and mut_type == "hum" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_master_hunter(npc, val)
|
|
local mut_type_t = { "chimera", "controller", "giant" }
|
|
local mut_type = check_mutant_type(npc)
|
|
if mut_type and in_ar(mut_type_t, mut_type) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_fierce(npc, val)
|
|
if fierce_stacks > 0 then
|
|
return val * fierce_stacks
|
|
end
|
|
end
|
|
|
|
function talent_ranger(npc, val)
|
|
if outfit_type({"o_medium"}) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_deadly_blow(npc, kind)
|
|
if not IsMonster(npc) then return end
|
|
|
|
local chance = kind == "w_melee" and 5 or 1
|
|
if math.random(1, 100) <= chance then
|
|
return true
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions attack (merc)
|
|
function talent_overdrive(npc, val)
|
|
if durations_t.overdrive_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_overdrive_on_use(sec)
|
|
local overdrive_t = talents_table["offensive"]["merc"]["overdrive"]
|
|
if overdrive_t.level <= 0 then return end
|
|
|
|
if string.find(sec, "stimpack") or (sec == "cocaine") then
|
|
durations_t.overdrive_var = overdrive_t.duration
|
|
end
|
|
end
|
|
|
|
function talent_head_hunter(npc, val)
|
|
local head_hunter_mult = size_table(merc_head_hunter_t)
|
|
if IsStalker(npc) and head_hunter_mult > 0 then
|
|
return val * head_hunter_mult
|
|
end
|
|
end
|
|
|
|
function talent_arrogance(npc, val)
|
|
local arrogance_rank_t = {
|
|
novice = 1, trainee = 2, experienced = 3, professional = 4,
|
|
veteran = 5, expert = 6, master = 7, legend = 8
|
|
}
|
|
|
|
local rank = ranks.get_obj_rank_name(npc)
|
|
local rank_num = rank and arrogance_rank_t[rank]
|
|
local ac_rank = ranks.get_obj_rank_name(db.actor)
|
|
local ac_rank_num = ac_rank and arrogance_rank_t[ac_rank]
|
|
if not (rank_num and ac_rank_num) then return end
|
|
|
|
local diff = ac_rank_num - rank_num
|
|
if diff > 0 then
|
|
return val * diff
|
|
end
|
|
end
|
|
|
|
function talent_aim_torso(npc, val, bone_id)
|
|
local bone_name = npc and bone_id and npc:bone_name(bone_id)
|
|
if bone_name and stalker_bones_t[bone_name] then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_aim_limbs(npc, val, bone_id)
|
|
local bone_name = npc and bone_id and npc:bone_name(bone_id)
|
|
if bone_name and (not stalker_bones_t[bone_name]) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_bullseye(npc, val)
|
|
if durations_t.bullseye_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_assault_rifles_expert_reload(obj)
|
|
local assault_t = talents_table["offensive"]["merc"]["assault_rifles_expert"]
|
|
if assault_t.level <= 0 then return end
|
|
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
if kind and kind == "w_rifle" then
|
|
local val = 1 + assault_t.reload[assault_t.level] / 100
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_light_guns_expert_reload(obj)
|
|
local pistol_t = talents_table["offensive"]["merc"]["light_guns_expert"]
|
|
if pistol_t.level <= 0 then return end
|
|
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
if kind and (kind == "w_pistol" or kind == "w_smg") then
|
|
local val = 1 + pistol_t.reload[pistol_t.level] / 100
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_liquidator(npc)
|
|
if not (IsStalker(npc)) then return end
|
|
|
|
if math.random(1, 100) <= 1 then
|
|
return true
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions attack (assassin)
|
|
function talent_spot_weakness(npc, val)
|
|
if spot_weakness_stacks > 0 then
|
|
return val * spot_weakness_stacks
|
|
end
|
|
end
|
|
|
|
function talent_undercover(npc, val)
|
|
if not (IsStalker(npc)) then return end
|
|
|
|
local comm = npc.character_community and npc:character_community()
|
|
local outfit = db.actor:item_in_slot(7)
|
|
local outfit_comm = outfit and ini_sys:r_string_ex(outfit:section(), "community")
|
|
if not (comm and outfit_comm) then return end
|
|
|
|
if (comm == outfit_comm) or (not game_relations.is_factions_enemies(comm, outfit_comm)) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_tactical(npc, val)
|
|
local wpn = db.actor:active_item()
|
|
if wpn and wpn:weapon_is_silencer() then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_backstab(npc, val)
|
|
local npc_dir = vector():set(npc:direction())
|
|
npc_dir.y = 0
|
|
local dir_to_ac = vector():set(db.actor:position()):sub(npc:position())
|
|
dir_to_ac.y = 0
|
|
local dp = npc_dir:dotproduct(dir_to_ac)
|
|
if dp < 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_saboteur(npc, val)
|
|
local dist_to = npc:position():distance_to(db.actor:position())
|
|
local new_val = val - math.ceil(dist_to) * 0.1
|
|
return new_val > 0 and new_val or 0
|
|
end
|
|
|
|
function talent_surprise_attack(npc, val)
|
|
local in_danger = (db.storage[npc:id()] and db.storage[npc:id()].danger_flag) or (npc:best_enemy() and true) or false
|
|
if not in_danger then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_assassination(npc, val)
|
|
local npc_dir = vector():set(npc:direction())
|
|
npc_dir.y = 0
|
|
local dir_to_ac = vector():set(db.actor:position()):sub(npc:position())
|
|
dir_to_ac.y = 0
|
|
local dp = npc_dir:dotproduct(dir_to_ac)
|
|
local in_danger = (db.storage[npc:id()] and db.storage[npc:id()].danger_flag) or (npc:best_enemy() and true) or false
|
|
if dp < 0 and (not in_danger) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_vile(npc, val)
|
|
local npc_dir = vector():set(npc:direction())
|
|
npc_dir.y = 0
|
|
local dir_to_ac = vector():set(db.actor:position()):sub(npc:position())
|
|
dir_to_ac.y = 0
|
|
local dp = npc_dir:dotproduct(dir_to_ac)
|
|
if dp < 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_pragmatic(npc, val)
|
|
local pistol = db.actor:active_item()
|
|
local wpn1 = db.actor:item_in_slot(2)
|
|
local wpn2 = db.actor:item_in_slot(3)
|
|
|
|
local check1 = wpn1 and pistol and pistol:id() == wpn1:id() and (not wpn2)
|
|
local check2 = wpn2 and pistol and pistol:id() == wpn2:id() and (not wpn1)
|
|
if check1 or check2 then
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions defense (sniper)
|
|
function talent_iron_stomach(sec)
|
|
local iron_stomach_t = talents_table["defensive"]["sniper"]["iron_stomach"]
|
|
if iron_stomach_t.level <= 0 then return end
|
|
|
|
local rad_add = ini_sys:r_float_ex(sec, "eat_radiation")
|
|
if rad_add and rad_add > 0 then
|
|
local rad_reduction = rad_add * (iron_stomach_t.rad_food[iron_stomach_t.level] / 100)
|
|
pr("iron_stomach level: %s || cur_rad: %s || reduce rad by: %s", iron_stomach_t.level, db.actor.radiation, rad_reduction)
|
|
if db.actor.radiation <= rad_reduction then
|
|
db.actor.radiation = 0
|
|
else
|
|
db.actor.radiation = db.actor.radiation - rad_reduction
|
|
end
|
|
pr("iron_stomach new rad: %s", db.actor.radiation)
|
|
end
|
|
end
|
|
|
|
function talent_rad_resistant(sec)
|
|
local rad_resistant_t = talents_table["defensive"]["sniper"]["rad_resistant"]
|
|
if rad_resistant_t.level <= 0 then return end
|
|
|
|
local rad_restore = ini_sys:r_float_ex(sec, "boost_radiation_restore")
|
|
local boost_time = ini_sys:r_float_ex(sec, "boost_time")
|
|
if rad_restore and rad_restore > 0 and boost_time then
|
|
local rad_restore_tot = rad_restore * 6 * boost_time
|
|
local rad_to_remove = rad_restore_tot * 0.35
|
|
pr("rad_resistant cur_rad: %s || rad_to_remove: %s", db.actor.radiation, rad_to_remove)
|
|
if db.actor.radiation <= rad_to_remove then
|
|
db.actor.radiation = 0
|
|
else
|
|
db.actor.radiation = db.actor.radiation - rad_to_remove
|
|
end
|
|
pr("rad_resistant new rad: %s", db.actor.radiation)
|
|
end
|
|
end
|
|
|
|
function talent_technician(item)
|
|
local technician_t = talents_table["defensive"]["sniper"]["technician"]
|
|
if technician_t.level > 0 and IsOutfit(item) then
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
local cur_upgrades = utils_item.get_upgrades_installed(item, nil, true)
|
|
if kind == "o_medium" and is_empty(cur_upgrades) then
|
|
install_random_upgrade(item)
|
|
end
|
|
end
|
|
end
|
|
|
|
function talent_expert_technician(val)
|
|
local outfit = db.actor:item_in_slot(7)
|
|
if outfit then
|
|
return outfit:condition() * 100 * val
|
|
end
|
|
end
|
|
|
|
function talent_agile(val)
|
|
if IsMoveState("mcAnyMove") then
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions defense (trooper)
|
|
function talent_trooper_general_armor(val)
|
|
if outfit_type({"o_medium", "o_sci"}) then
|
|
return val * 2
|
|
end
|
|
return val
|
|
end
|
|
|
|
function talent_first_aid(sec)
|
|
local first_aid_t = talents_table["defensive"]["trooper"]["first_aid"]
|
|
if first_aid_t.level <= 0 then return end
|
|
|
|
local hp_restore = ini_sys:r_float_ex(sec, "boost_health_restore")
|
|
local boost_time = ini_sys:r_float_ex(sec, "boost_time")
|
|
if hp_restore and hp_restore > 0 and boost_time then
|
|
local hp_restore_tot = hp_restore * 6 * boost_time
|
|
local hp_to_add = hp_restore_tot * (first_aid_t.heal[first_aid_t.level] / 100)
|
|
pr("first_aid cur_hp: %s || hp_to_add: %s", db.actor.health, hp_to_add)
|
|
if db.actor.health + hp_to_add >= 1 then
|
|
db.actor.health = 1
|
|
else
|
|
db.actor.health = db.actor.health + hp_to_add
|
|
end
|
|
pr("first_aid new hp: %s", db.actor.health)
|
|
end
|
|
end
|
|
|
|
function talent_suppresion_fire(val) -- var set in actor_hit_npc
|
|
if durations_t.suppresion_fire_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_cautious(val)
|
|
if cooldowns_t.cautious_var <= 0 then
|
|
cooldowns_t.cautious_var = talents_table["defensive"]["trooper"]["cautious"].cooldown
|
|
return val * 2
|
|
end
|
|
return val
|
|
end
|
|
|
|
function talent_composure(val)
|
|
if db.actor.power > 0.5 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_adorned(val)
|
|
local belt_items = 0
|
|
db.actor:iterate_belt( function(owner, obj)
|
|
belt_items = belt_items + 1
|
|
end)
|
|
if belt_items > 0 then
|
|
return val * belt_items
|
|
end
|
|
end
|
|
|
|
function talent_expert_armorer(val)
|
|
local upgrades = 0
|
|
local outfit = db.actor:item_in_slot(7)
|
|
local cur_upgrades = outfit and utils_item.get_upgrades_installed(outfit, nil, true)
|
|
if cur_upgrades then
|
|
for name, _ in pairs(cur_upgrades) do
|
|
upgrades = upgrades + 1
|
|
end
|
|
end
|
|
if upgrades > 10 then
|
|
upgrades = 10
|
|
end
|
|
if upgrades > 0 then
|
|
return val * upgrades
|
|
end
|
|
end
|
|
|
|
function talent_last_stand(val)
|
|
if db.actor.health < 0.35 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_leap(val)
|
|
if durations_t.leap_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions defense (jugger)
|
|
function talent_dismorale(val)
|
|
if durations_t.dismorale_var > 0 then
|
|
if talents_table["defensive"]["jugger"]["anxiety"].level > 0 then
|
|
return val * 2
|
|
end
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_sturdy(val)
|
|
if durations_t.sturdy_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_turmoil(val, npc)
|
|
local turmoil_t = talents_table["defensive"]["jugger"]["turmoil"]
|
|
if npc and (IsStalker(npc) or IsMonster(npc)) and npc.alive and npc:alive() and db.actor:position():distance_to(npc:position()) <= turmoil_t.radius then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_secure(val)
|
|
local companions = axr_companions.get_companion_count()
|
|
if companions >= 1 then
|
|
local ret_val = val * companions
|
|
if ret_val > 15 then
|
|
ret_val = 15
|
|
end
|
|
return ret_val
|
|
end
|
|
end
|
|
|
|
function talent_lucid(val)
|
|
local psi_health = arszi_psy.get_psy_health()
|
|
if psi_health then
|
|
return psi_health * 100 * val
|
|
end
|
|
end
|
|
|
|
function talent_robust(val)
|
|
if cooldowns_t.robust_cd <= 0 then
|
|
cooldowns_t.robust_cd = talents_table["defensive"]["jugger"]["robust"].cooldown
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions defense (monster_hunter)
|
|
function talent_monster_hunter_general_armor(val)
|
|
if outfit_type({"o_medium"}) then
|
|
return val * 2
|
|
end
|
|
return val
|
|
end
|
|
|
|
function talent_melee_defense(val)
|
|
local wpn = db.actor:active_item()
|
|
local kind = wpn and ini_sys:r_string_ex(wpn:section(), "kind")
|
|
if kind and kind == "w_melee" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_special_diet(val)
|
|
if durations_t.special_diet_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_special_diet_on_use(sec)
|
|
local special_diet_t = talents_table["defensive"]["monster_hunter"]["special_diet"]
|
|
if special_diet_t.level <= 0 then return end
|
|
|
|
local kind = sec and ini_sys:r_string_ex(sec, "kind")
|
|
if kind and (kind == "i_mutant_cooked" or kind == "i_mutant_raw") then
|
|
durations_t.special_diet_var = special_diet_t.duration
|
|
end
|
|
end
|
|
|
|
function talent_beast_demeanor(val, npc)
|
|
local mut_type = npc and IsMonster(npc) and check_mutant_type(npc, true)
|
|
if mut_type and mut_type == "beast" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_humanoid_demeanor(val, npc)
|
|
local mut_type = npc and IsMonster(npc) and check_mutant_type(npc, true)
|
|
if mut_type and mut_type == "hum" then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_second_skin(val)
|
|
local hides_on_belt = 0
|
|
db.actor:iterate_belt( function(owner, obj)
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
if kind and kind == "i_mutant_belt" then
|
|
hides_on_belt = hides_on_belt + 1
|
|
end
|
|
end)
|
|
if hides_on_belt > 0 then
|
|
return val * hides_on_belt
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions defense (merc)
|
|
function talent_chem_reliant(val)
|
|
if durations_t.chem_reliant_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_chem_reliant_on_use(sec)
|
|
local chem_reliant_t = talents_table["defensive"]["merc"]["chem_reliant"]
|
|
if chem_reliant_t.level <= 0 then return end
|
|
|
|
if string.find(sec, "stimpack") or (sec == "morphine") then
|
|
durations_t.chem_reliant_var = chem_reliant_t.duration
|
|
end
|
|
end
|
|
|
|
function talent_addictive(val)
|
|
if durations_t.chem_reliant_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_grenadier(val)
|
|
if durations_t.grenadier_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_confusion(val, npc)
|
|
if not (IsStalker(npc)) then return end
|
|
|
|
local comm = npc.character_community and npc:character_community()
|
|
local outfit = db.actor:item_in_slot(7)
|
|
local outfit_comm = outfit and ini_sys:r_string_ex(outfit:section(), "community")
|
|
if not (comm and outfit_comm) then return end
|
|
|
|
if (comm == outfit_comm) or (not game_relations.is_factions_enemies(comm, outfit_comm)) then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_flurry(val)
|
|
if durations_t.flurry_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_reposition(val)
|
|
if durations_t.reposition_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions defense (assassin)
|
|
function talent_ordinary(val)
|
|
-- if has evanescent
|
|
local evanescent_t = talents_table["defensive"]["assassin"]["evanescent"]
|
|
if evanescent_t.level > 0 then
|
|
return val
|
|
end
|
|
|
|
local state = level.actor_moving_state()
|
|
if state >= 33 and state <= 42 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_quiet(val)
|
|
-- if has subtle
|
|
local subtle_t = talents_table["defensive"]["assassin"]["subtle"]
|
|
if subtle_t.level > 0 then
|
|
return val
|
|
end
|
|
|
|
local active_item = db.actor:active_item()
|
|
if not active_item then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_lurking(val)
|
|
if level.get_time_hours() < 6 or level.get_time_hours() >= 18 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_deception(val, npc)
|
|
local ac_pos = db.actor:position()
|
|
local npc_pos = npc:position()
|
|
if ac_pos and npc_pos and ac_pos:distance_to(npc_pos) <= 50 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_vague(val)
|
|
local rain_density = weather.get_value_numric("rain_density")
|
|
return val * rain_density
|
|
end
|
|
|
|
function talent_sneaky(val)
|
|
local crouch_state = IsMoveState('mcCrouch')
|
|
return crouch_state and val * 2 or val
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions endurance
|
|
-- speed
|
|
function talent_ease(val)
|
|
local wpn = db.actor:active_item()
|
|
if (not wpn) then
|
|
return val
|
|
end
|
|
|
|
local single = ini_sys:r_float_ex(wpn:section(), "single_handed")
|
|
if single and single == 1 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_swifty(val)
|
|
return val
|
|
end
|
|
|
|
function talent_insomnia(val)
|
|
if level.get_time_hours() < 6 or level.get_time_hours() >= 18 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_athletic(val)
|
|
if outfit_type({"o_medium", "o_sci"}) then
|
|
return val * db.actor.power * 100 / 2
|
|
elseif outfit_type({"o_light"}) then
|
|
return val * db.actor.power * 100
|
|
end
|
|
end
|
|
|
|
-- stamina
|
|
function talent_refreshing(sec)
|
|
local refreshing_t = talents_table["endurance"]["sniper"]["refreshing"]
|
|
if refreshing_t.level <= 0 then return end
|
|
|
|
local kind = ini_sys:r_string_ex(sec, "kind")
|
|
local thirst = ini_sys:r_float_ex(sec, "eat_thirstiness")
|
|
if kind and kind == "i_drink" and thirst and thirst < 0 and db.actor.power < 1 then
|
|
db.actor.power = db.actor.power + (refreshing_t.stamina[refreshing_t.level] / 100)
|
|
if db.actor.power > 1 then
|
|
db.actor.power = 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function talent_resting(val)
|
|
if not IsMoveState("mcAnyMove") then
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_tireless(val)
|
|
return val
|
|
end
|
|
|
|
function talent_courage(val)
|
|
if durations_t.courage_var > 0 then
|
|
return val
|
|
end
|
|
end
|
|
|
|
-- weight
|
|
function talent_weight_demolition_expert(item, tal_t, update_weight)
|
|
local weight = ini_sys:r_float_ex(item:section(), "inv_weight")
|
|
local explosive_cl_t = { ["S_OG7B"] = true, ["S_VOG25"] = true, ["S_M209"] = true }
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
local kind_fits = kind and kind == "w_explosive"
|
|
local class = ini_sys:r_string_ex(item:section(), "class")
|
|
local class_fits = class and explosive_cl_t[class]
|
|
|
|
if weight and (kind_fits or class_fits) then
|
|
local val = weight * (tal_t.weight[tal_t.level] / 100)
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_weight_heavy_armor_expert(item, tal_t, update_weight)
|
|
local weight = ini_sys:r_float_ex(item:section(), "inv_weight")
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
local kind_fits = kind and (kind == "o_heavy" or kind == "o_medium")
|
|
|
|
if kind_fits and weight then
|
|
local val = weight * (tal_t.weight[tal_t.level] / 100)
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_weight_hiker(item, tal_t, update_weight)
|
|
local weight = ini_sys:r_float_ex(item:section(), "inv_weight")
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
local kind_fits = kind and (kind == "i_food" or kind == "i_mutant_raw" or kind == "i_mutant_cooked")
|
|
|
|
if kind_fits and weight then
|
|
local val = weight * (tal_t.weight[tal_t.level] / 100)
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_weight_prudent(item, tal_t, update_weight)
|
|
local weight = ini_sys:r_float_ex(item:section(), "inv_weight")
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
local kind_fits = kind and kind == "i_medical"
|
|
|
|
if kind_fits and weight then
|
|
local val = weight * (tal_t.weight[tal_t.level] / 100)
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_weight_organizer(item, tal_t, update_weight)
|
|
if item:weight() >= 0.2 and (not IsWeapon(item)) then
|
|
return 0.1
|
|
end
|
|
end
|
|
|
|
function talent_weight_belt_braces(item, tal_t, update_weight)
|
|
if update_weight then
|
|
local item_on_belt = false
|
|
db.actor:iterate_belt( function(owner, obj)
|
|
if item:id() == obj:id() then
|
|
item_on_belt = true
|
|
end
|
|
end)
|
|
|
|
if not item_on_belt then
|
|
return
|
|
end
|
|
end
|
|
|
|
local weight = ini_sys:r_float_ex(item:section(), "inv_weight")
|
|
if weight then
|
|
local val = weight * (tal_t.weight[tal_t.level] / 100)
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_hoarder(val)
|
|
return val
|
|
end
|
|
|
|
function talent_iron_spine(val)
|
|
if outfit_type({"o_medium", "o_sci"}) then
|
|
return val * db.actor.health * 100 / 2
|
|
elseif outfit_type({"o_heavy"}) then
|
|
return val * db.actor.health * 100
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
-- functions speech
|
|
|
|
-- buy (on_get_item_cost)
|
|
function talent_preparation(item)
|
|
local preparation_t = talents_table["speech"]["sniper"]["preparation"]
|
|
if preparation_t.level <= 0 then return end
|
|
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
if not kind then return end
|
|
|
|
local thirst = ini_sys:r_float_ex(item:section(), "eat_thirstiness")
|
|
local food = kind == "i_food"
|
|
local water = kind == "i_drink" and thirst and thirst < 0
|
|
if food or water then
|
|
local val = preparation_t.buy[preparation_t.level]
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_observant(item)
|
|
local observant_t = talents_table["speech"]["sniper"]["observant"]
|
|
if observant_t.level <= 0 then return end
|
|
|
|
if IsWeapon(item) or IsAmmo(item) then
|
|
local val = observant_t.buy[observant_t.level]
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_gregarious(item)
|
|
local gregarious_t = talents_table["speech"]["sniper"]["gregarious"]
|
|
if gregarious_t.level <= 0 then return end
|
|
|
|
local val = gregarious_t.buy[gregarious_t.level]
|
|
return val
|
|
end
|
|
|
|
function talent_well_liked(item, trader_id)
|
|
local well_liked_t = talents_table["speech"]["sniper"]["well_liked"]
|
|
if well_liked_t.level <= 0 then return end
|
|
|
|
local trader = trader_id and level.object_by_id(trader_id)
|
|
if not (trader and trader.alive and trader:alive()) then return end
|
|
|
|
local trader_comm
|
|
if trader.section and trader:section() and (trader:section() == "m_trader" or trader:section() == "m_lesnik") then
|
|
trader_comm = "stalker"
|
|
else
|
|
trader_comm = trader:character_community()
|
|
end
|
|
if not trader_comm then return end
|
|
|
|
local gw = relation_registry.community_goodwill(trader_comm, 0)
|
|
if gw then
|
|
local val = clamp(gw / 200, 0, 8)
|
|
return -val
|
|
end
|
|
end
|
|
|
|
-- sell (on_get_item_cost)
|
|
function talent_supplier(item)
|
|
local supplier_t = talents_table["speech"]["sniper"]["supplier"]
|
|
if supplier_t.level <= 0 then return end
|
|
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
if not kind then return end
|
|
|
|
if kind then
|
|
local mutant_food = kind == "i_mutant_raw" or kind == "i_mutant_cooked"
|
|
local mutant_part = kind == "i_mutant_part"
|
|
local mutant_skin = kind == "i_mutant_belt"
|
|
if mutant_food or mutant_part or mutant_skin then
|
|
local val = supplier_t.sell[supplier_t.level]
|
|
return val
|
|
end
|
|
end
|
|
end
|
|
|
|
function talent_smuggler(item)
|
|
local smuggler_t = talents_table["speech"]["sniper"]["smuggler"]
|
|
if smuggler_t.level <= 0 then return end
|
|
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
if not kind then return end
|
|
|
|
local eat_alcohol = ini_sys:r_float_ex(item:section(), "eat_alcohol")
|
|
local medicine = kind == "i_medical"
|
|
local alcohol = kind == "i_drink" and eat_alcohol and eat_alcohol > 0
|
|
local artefact = kind == "i_arty" or kind == "i_arty_junk"
|
|
if medicine or alcohol or artefact then
|
|
local val = smuggler_t.sell[smuggler_t.level]
|
|
if artefact then
|
|
val = val / 2
|
|
end
|
|
return val
|
|
end
|
|
end
|
|
|
|
function talent_persuasion(item)
|
|
local persuasion_t = talents_table["speech"]["sniper"]["persuasion"]
|
|
if persuasion_t.level <= 0 then return end
|
|
|
|
local val = persuasion_t.sell[persuasion_t.level]
|
|
return val
|
|
end
|
|
|
|
function talent_shady(item, profile)
|
|
local shady_t = talents_table["speech"]["sniper"]["shady"]
|
|
if shady_t.level <= 0 then return end
|
|
|
|
local stalker_cfg = profile and profile.cfg and profile.cfg == "items\\trade\\trade_generic.ltx"
|
|
if stalker_cfg then
|
|
local val = shady_t.sell[shady_t.level]
|
|
return val
|
|
end
|
|
end
|
|
|
|
-- buy (on_item_buy)
|
|
local charismatic_items_ar = {}
|
|
function talent_charismatic(item, trader)
|
|
local charismatic_t = talents_table["speech"]["sniper"]["charismatic"]
|
|
if charismatic_t.level <= 0 then return end
|
|
|
|
local cost = ini_sys:r_float_ex(item:section(), "cost")
|
|
local discount = trade_manager.get_sell_discount(trader:id())
|
|
local tier = ini_sys:r_float_ex(item:section(), "tier")
|
|
local item_name = ini_sys:r_string_ex(item:section(), "inv_name")
|
|
if not (cost and discount and tier and tier <= 1) then return end
|
|
|
|
local cost_to_return = math.ceil(cost * discount)
|
|
pr("charismatic, store sec: %s || cost_to_return: %s", item:section(), cost_to_return)
|
|
charismatic_items_ar[#charismatic_items_ar + 1] = {}
|
|
charismatic_items_ar[#charismatic_items_ar].cost = cost_to_return
|
|
charismatic_items_ar[#charismatic_items_ar].name = item_name or item:section()
|
|
|
|
-- use only last created time event
|
|
RemoveTimeEvent("delay_charismatic_e", "delay_charismatic_a")
|
|
CreateTimeEvent("delay_charismatic_e", "delay_charismatic_a", 0.25, function()
|
|
if #charismatic_items_ar <= 0 then
|
|
pr("charismatic, no tier 1 items bought, return")
|
|
return true
|
|
end
|
|
|
|
for idx, items_t in ipairs(charismatic_items_ar) do
|
|
local chance = charismatic_t.free[charismatic_t.level]
|
|
if math.random(1, 100) <= chance then
|
|
-- chance procs then give money back
|
|
pr("charismatic, proc, returning cost: %s for %s", items_t.cost, gt(items_t.name))
|
|
db.actor:give_money(items_t.cost)
|
|
|
|
-- send news
|
|
news_manager.send_tip(db.actor, string.format(gt("talents_charismatic_msg"), gt(items_t.name)), 0, nil, 7500)
|
|
end
|
|
end
|
|
|
|
iempty_table(charismatic_items_ar)
|
|
|
|
return true
|
|
end)
|
|
|
|
end
|
|
|
|
-- sell (on_item_sell)
|
|
local cynical_item_id -- remove on trade close
|
|
function talent_cynical_set(item, profile, trader_id, new_cost, vanilla_cost) -- on_get_item_cost
|
|
local cynical_t = talents_table["speech"]["sniper"]["cynical"]
|
|
if cynical_t.level <= 0 then return end
|
|
|
|
-- actor has coin
|
|
if not (actor_has_coin()) then return end
|
|
|
|
-- get trader comm
|
|
local trader = trader_id and level.object_by_id(trader_id)
|
|
if not (trader and trader.alive and trader:alive()) then return end
|
|
|
|
local trader_comm
|
|
if trader.section and trader:section() and (trader:section() == "m_trader" or trader:section() == "m_lesnik") then
|
|
trader_comm = "stalker"
|
|
else
|
|
trader_comm = trader:character_community()
|
|
end
|
|
if not trader_comm then return end
|
|
|
|
-- is stalker and rank fits
|
|
local obj_rank = ranks.get_obj_rank_name(trader)
|
|
local rank_fits = obj_rank and (obj_rank == "novice" or obj_rank == "trainee")
|
|
local stalker_cfg = profile and profile.cfg and profile.cfg == "items\\trade\\trade_generic.ltx"
|
|
if not (stalker_cfg and obj_rank) then return end
|
|
|
|
-- item is misc
|
|
local cost = new_cost or vanilla_cost
|
|
local kind = ini_sys:r_string_ex(item:section(), "kind")
|
|
if not (cost and kind and kind == "i_misc") then return end
|
|
|
|
-- item to sell isnt set (or same item)
|
|
if (not cynical_item_id) or (cynical_item_id == item:id()) then
|
|
pr("cynical set item_id: %s || sec: %s || to price: %s", item:id(), item:section(), cost * 20)
|
|
cynical_item_id = item:id()
|
|
return cost * 20
|
|
end
|
|
|
|
end
|
|
|
|
function talent_cynical_sell(item, comm)
|
|
-- if we sell this misc item
|
|
if cynical_item_id and item:id() == cynical_item_id and comm then
|
|
cynical_item_id = nil
|
|
|
|
-- release coin, set cd
|
|
local cur_time = game.get_game_time()
|
|
local coin_item = db.actor:object("cynical_coin")
|
|
if coin_item and coin_item:id() then
|
|
pr("cynical release coin, set cd")
|
|
alife_release_id(coin_item:id())
|
|
cynical_cd = ctime_to_t(cur_time)
|
|
end
|
|
|
|
-- goodwill hit
|
|
local goodwill_hit = math.random(35, 70)
|
|
pr("cynical, goodwill hit: %s", -goodwill_hit)
|
|
relation_registry.change_community_goodwill(comm, 0, -goodwill_hit)
|
|
|
|
-- send msg
|
|
news_manager.send_tip(db.actor, string.format(gt("talents_cynical_msg"), gt("st_faction_" .. comm)), 0, nil, 7500)
|
|
end
|
|
|
|
end
|
|
|
|
local deceitful_cost_ar = {}
|
|
function talent_deceitful(item, trader)
|
|
local deceitful_t = talents_table["speech"]["sniper"]["deceitful"]
|
|
if deceitful_t.level <= 0 then return end
|
|
|
|
local cost = ini_sys:r_float_ex(item:section(), "cost")
|
|
local discount = trade_manager.get_buy_discount(trader:id())
|
|
local item_name = ini_sys:r_string_ex(item:section(), "inv_name")
|
|
if not (cost and discount) then return end
|
|
|
|
local cost_to_return = math.ceil(cost * discount * 2.5)
|
|
pr("deceitful, store sec: %s || cost_to_return: %s", item:section(), cost_to_return)
|
|
deceitful_cost_ar[#deceitful_cost_ar + 1] = {}
|
|
deceitful_cost_ar[#deceitful_cost_ar].cost = cost_to_return
|
|
deceitful_cost_ar[#deceitful_cost_ar].name = item_name or item:section()
|
|
|
|
RemoveTimeEvent("delay_deceitful_e", "delay_deceitful_a")
|
|
CreateTimeEvent("delay_deceitful_e", "delay_deceitful_a", 0.25, function()
|
|
if #deceitful_cost_ar <= 0 then
|
|
pr("deceitful, no tier 1 items bought, return")
|
|
return true
|
|
end
|
|
|
|
for idx, items_t in ipairs(deceitful_cost_ar) do
|
|
local chance = 2
|
|
if math.random(1, 100) <= chance then
|
|
-- chance procs then give extra money
|
|
pr("deceitful, proc, give cost: %s for %s", items_t.cost, items_t.name)
|
|
db.actor:give_money(items_t.cost)
|
|
|
|
-- send msg
|
|
news_manager.send_tip(db.actor, string.format(gt("talents_deceitful_msg"), items_t.cost, gt(items_t.name)), 0, nil, 7500)
|
|
end
|
|
end
|
|
|
|
iempty_table(deceitful_cost_ar)
|
|
|
|
return true
|
|
end)
|
|
|
|
end
|
|
|
|
-- guitar patch
|
|
base_guitar_func = itms_manager.use_guitar
|
|
function itms_manager.use_guitar(obj)
|
|
talent_artistic(obj:section())
|
|
return base_guitar_func(obj)
|
|
end
|
|
|
|
base_harmonica_func = itms_manager.use_harmonica
|
|
function itms_manager.use_harmonica(obj)
|
|
talent_artistic(obj:section())
|
|
return base_harmonica_func(obj)
|
|
end
|
|
|
|
function talent_artistic(sec)
|
|
local artistic_t = talents_table["speech"]["sniper"]["artistic"]
|
|
if artistic_t.level <= 0 then return end
|
|
|
|
if (not artistic_cd) and (sec == "guitar_a" or sec == "harmonica_a") then
|
|
|
|
-- find all stalker communities around
|
|
local gw_added = 0
|
|
local function itr(obj)
|
|
if gw_added < 2 and obj and IsStalker(obj) and obj.alive and obj:alive() then
|
|
local comm = obj.character_community() and obj:character_community()
|
|
if comm then
|
|
local val = math.random(15, 35)
|
|
pr("artistic, add goodwill %s to comm %s", val, comm)
|
|
relation_registry.change_community_goodwill(comm, 0, val)
|
|
gw_added = gw_added + 1
|
|
|
|
-- add news
|
|
news_manager.send_tip(db.actor, string.format(gt("talents_artistic_msg"), gt("st_faction_" .. comm)), 0, nil, 7500)
|
|
end
|
|
end
|
|
end
|
|
|
|
level.iterate_nearest(db.actor:position(), 30, itr)
|
|
|
|
local cur_time = game.get_game_time()
|
|
artistic_cd = ctime_to_t(cur_time)
|
|
end
|
|
|
|
end
|
|
|
|
-------------------------------------
|
|
-- other_callbacks
|
|
|
|
-- duration manager
|
|
local dur_tmr = 0
|
|
local dur_upd = 0.25
|
|
function duration_update()
|
|
local tg = time_global()
|
|
if dur_tmr > tg then return end
|
|
dur_tmr = tg + 1000 * dur_upd
|
|
|
|
for var_name, time in pairs(durations_t) do
|
|
if time > 0 then
|
|
durations_t[var_name] = durations_t[var_name] - dur_upd
|
|
if durations_t[var_name] <= 0 then
|
|
durations_t[var_name] = 0
|
|
|
|
-- adrenaline_rush (remove speed)
|
|
if var_name == "adrenaline_rush_var" then
|
|
speed.remove_speed("adrenaline_rush")
|
|
end
|
|
|
|
-- fierce remove
|
|
if var_name == "fierce_var" then
|
|
fierce_stacks = 0
|
|
end
|
|
|
|
-- spot_weakness remove
|
|
if var_name == "spot_weakness_var" then
|
|
spot_weakness_stacks = 0
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- cooldown manager
|
|
local cd_tmr = 0
|
|
local cd_upd = 0.25
|
|
function cooldown_update()
|
|
local tg = time_global()
|
|
if cd_tmr > tg then return end
|
|
cd_tmr = tg + 1000 * cd_upd
|
|
|
|
for var_name, time in pairs(cooldowns_t) do
|
|
if time > 0 then
|
|
cooldowns_t[var_name] = cooldowns_t[var_name] - cd_upd
|
|
if cooldowns_t[var_name] <= 0 then
|
|
cooldowns_t[var_name] = 0
|
|
end
|
|
end
|
|
end
|
|
|
|
-- long cooldowns in game time
|
|
local cur_time = game.get_game_time()
|
|
|
|
-- kindred
|
|
if (not kindred_cd) or (cur_time:diffSec(t_to_ctime(kindred_cd)) > talents_table["defensive"]["monster_hunter"]["kindred"].cooldown * 6) then
|
|
-- start cd again
|
|
kindred_cd = ctime_to_t(cur_time)
|
|
|
|
-- allow spawn if dogs do not exist
|
|
local dogs_exist = get_story_se_object("kindred_dogs")
|
|
if not (dogs_exist) then
|
|
kindred_var = true
|
|
end
|
|
end
|
|
|
|
-- second_wind
|
|
if second_wind_cd and (cur_time:diffSec(t_to_ctime(second_wind_cd)) > talents_table["endurance"]["sniper"]["second_wind"].cooldown * 6) then
|
|
second_wind_cd = nil
|
|
end
|
|
|
|
-- artistic
|
|
if artistic_cd and (cur_time:diffSec(t_to_ctime(artistic_cd)) > talents_table["speech"]["sniper"]["artistic"].cooldown * 6) then
|
|
artistic_cd = nil
|
|
end
|
|
|
|
-- cynical
|
|
if cynical_cd and (cur_time:diffSec(t_to_ctime(cynical_cd)) > talents_table["speech"]["sniper"]["cynical"].cooldown * 6) then
|
|
cynical_cd = nil
|
|
end
|
|
|
|
end
|
|
|
|
-- item use
|
|
function actor_on_item_use(item)
|
|
local sec = item:section()
|
|
|
|
-- iron_stomach
|
|
talent_iron_stomach(sec)
|
|
|
|
-- rad_resistant
|
|
talent_rad_resistant(sec)
|
|
|
|
-- trooper first aid
|
|
talent_first_aid(sec)
|
|
|
|
-- endurance refreshing
|
|
talent_refreshing(sec)
|
|
|
|
-- monster hunter special diet
|
|
talent_special_diet_on_use(sec)
|
|
|
|
-- merc overdrive
|
|
talent_overdrive_on_use(sec)
|
|
|
|
-- merc chem_reliant
|
|
talent_chem_reliant_on_use(sec)
|
|
|
|
end
|
|
|
|
-- item add upgrade
|
|
function upgrades_to_ruck(item)
|
|
|
|
-- sniper technician
|
|
if picked_class == "sniper" then
|
|
talent_technician(item)
|
|
end
|
|
|
|
end
|
|
|
|
-- item weight reduction
|
|
local item_weight_t = {}
|
|
|
|
local inv_weight_upd = true
|
|
|
|
function weight_to_slot(item)
|
|
pr("to slot sec: %s", item:section())
|
|
try_change_item_weight(item, "slot")
|
|
end
|
|
|
|
function weight_to_ruck(item)
|
|
pr("to ruck sec: %s", item:section())
|
|
try_change_item_weight(item, "ruck")
|
|
end
|
|
|
|
function weight_to_belt(item)
|
|
pr("to belt sec %s", item:section())
|
|
try_change_item_weight(item, "belt")
|
|
end
|
|
|
|
function weight_on_drop(item)
|
|
if item:section() == "inv_update_item_xd" then -- not to trigger weight update again
|
|
return
|
|
end
|
|
pr("to drop sec: %s", item:section())
|
|
try_change_item_weight(item, "drop")
|
|
end
|
|
|
|
function weight_first_update()
|
|
recalc_all_items_weight()
|
|
end
|
|
|
|
function server_entity_on_unregister(se_obj)
|
|
if item_weight_t[se_obj.id] then
|
|
item_weight_t[se_obj.id] = nil
|
|
end
|
|
end
|
|
|
|
|
|
function try_change_item_weight(item, cb) -- "cb" to put in callbacks
|
|
if item:section() == "inv_update_item_xd" then return end
|
|
|
|
for typ, t in pairs(talents_table) do
|
|
for class_name, t2 in pairs(t) do
|
|
for talent, tal_t in pairs(t2) do
|
|
if weight_talent_funcs[talent] and tal_t.level > 0 then
|
|
|
|
-- in "on" callbacks (add weight)
|
|
if cb and in_ar(weight_talent_funcs[talent].on, cb) then
|
|
local val = this[weight_talent_funcs[talent].func](item, tal_t)
|
|
if val then
|
|
pr("add weight, sec: %s || talent: %s || cb: %s || val: %s", item:section(), talent, cb, -val)
|
|
add_item_weight(item, talent, -val)
|
|
end
|
|
end
|
|
|
|
-- in "off" callbacks (remove weight)
|
|
if cb and in_ar(weight_talent_funcs[talent].off, cb) then
|
|
pr("remove weight from sec: %s || talent: %s || cb: %s", item:section(), talent, cb)
|
|
remove_item_weight(item, talent)
|
|
end
|
|
|
|
|
|
-- called without callbacks (first update/talent pickup) - add/update item weight
|
|
if not cb then
|
|
-- check conditions and add weight to item
|
|
local val = this[weight_talent_funcs[talent].func](item, tal_t, true)
|
|
if val then
|
|
pr("recalc, sec: %s || talent: %s || val: %s", item:section(), talent, -val)
|
|
add_item_weight(item, talent, -val)
|
|
end
|
|
end
|
|
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- update actor total weight
|
|
trigger_weight_update()
|
|
end
|
|
|
|
function add_item_weight(item, talent, val) -- add on callbacks
|
|
|
|
local id = item:id()
|
|
pr("- add_item_weight sec: %s || talent: %s || id: %s || val: %s", item:section(), talent, id, val)
|
|
|
|
-- if item not in table - add
|
|
if not item_weight_t[id] then
|
|
item_weight_t[id] = {}
|
|
end
|
|
|
|
-- if talent in table then remove its weight from item
|
|
if item_weight_t[id][talent] then
|
|
pr("- add_item_weight remove weight, cur weight: %s || weight after remove: %s", item:weight(), item:weight() - item_weight_t[id][talent])
|
|
item:set_weight(item:weight() - item_weight_t[id][talent])
|
|
pr(" test1 item weight: %s", item:weight())
|
|
end
|
|
|
|
-- dont let weight go below 0.001
|
|
if item:weight() + val <= 0.001 then
|
|
val = item:weight()
|
|
end
|
|
|
|
-- set new
|
|
item_weight_t[id][talent] = val
|
|
|
|
-- add new
|
|
pr("- add_item_weight add weight, cur weight: %s || weight after add: %s", item:weight(), item:weight() + val)
|
|
item:set_weight(item:weight() + val)
|
|
pr(" test2 item weight: %s", item:weight())
|
|
|
|
end
|
|
|
|
function remove_item_weight(item, talent) -- remove on callbacks
|
|
local id = item:id()
|
|
if not (item_weight_t[id]) then return end
|
|
|
|
-- remove talent weight from item
|
|
if item_weight_t[id][talent] then
|
|
pr("- remove_item_weight, cur weight: %s || weight after remove: %s", item:weight(), item:weight() - item_weight_t[id][talent])
|
|
item:set_weight(item:weight() - item_weight_t[id][talent])
|
|
item_weight_t[id][talent] = nil
|
|
pr(" test3 item weight: %s", item:weight())
|
|
end
|
|
|
|
-- remove item from table if no talents in it
|
|
if is_empty(item_weight_t[id]) then
|
|
item_weight_t[id] = nil
|
|
end
|
|
|
|
end
|
|
|
|
function recalc_all_items_weight() -- for first update (or talent pick up)
|
|
local function itr(owner, item)
|
|
try_change_item_weight(item)
|
|
end
|
|
db.actor:iterate_inventory(itr, db.actor)
|
|
end
|
|
|
|
function remove_all_items_weight() -- for respec
|
|
for id, t in pairs(item_weight_t) do
|
|
|
|
-- if cant find item - remove from table
|
|
local item = level.object_by_id(id)
|
|
if not item then
|
|
item_weight_t[id] = nil
|
|
end
|
|
|
|
-- item still here
|
|
if item_weight_t[id] then
|
|
for talent, val in pairs(t) do
|
|
-- remove weight and delete from item table
|
|
item:set_weight(item:weight() - val)
|
|
item_weight_t[id][talent] = nil
|
|
end
|
|
|
|
-- remove item from table if it has no more talents
|
|
if is_empty(item_weight_t[id]) then
|
|
item_weight_t[id] = nil
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
-- let me know if somebody know a better way to update actor weight after item:set_weight
|
|
function trigger_weight_update()
|
|
if not (inv_weight_upd) then return end
|
|
pr("~ update actor weight")
|
|
local se_obj = alife_create_item("inv_update_item_xd", db.actor)
|
|
inv_weight_upd = false
|
|
end
|
|
|
|
function bind(obj)
|
|
obj:bind_object(inventory_update_item(obj))
|
|
end
|
|
|
|
class "inventory_update_item" (object_binder)
|
|
|
|
function inventory_update_item:__init(obj) super(obj)
|
|
end
|
|
|
|
function inventory_update_item:update(delta)
|
|
object_binder.update(self, delta)
|
|
inv_weight_upd = true
|
|
alife_release(self.object)
|
|
end
|
|
|
|
|
|
local leap
|
|
function on_key_leap(k)
|
|
if leap then return end
|
|
if picked_class ~= "trooper" then return end
|
|
|
|
local bind = dik_to_bind(k)
|
|
if bind ~= 7 then return end
|
|
if talents_table["defensive"]["trooper"]["leap"].level <= 0 then return end
|
|
|
|
-- check if enough stamina
|
|
local stamina_hit = 0.2
|
|
if talents_table["defensive"]["trooper"]["enduring"].level > 0 and outfit_type({"o_medium", "o_sci"}) then
|
|
stamina_hit = stamina_hit * 0.5
|
|
end
|
|
|
|
if db.actor.power <= stamina_hit then return end
|
|
|
|
-- choose direction
|
|
leap = (level.actor_moving_state() == 4 and "L") or (level.actor_moving_state() == 8 and "R")
|
|
if not leap then return end
|
|
|
|
-- reduce stamina
|
|
db.actor.power = db.actor.power - stamina_hit
|
|
|
|
-- set leap_var as timer
|
|
durations_t.leap_var = talents_table["defensive"]["trooper"]["leap"].duration
|
|
|
|
-- add speed and disable input
|
|
speed.add_speed("talent_leap_speed", 15, false, true)
|
|
game.only_allow_movekeys(true)
|
|
|
|
CreateTimeEvent("roll_stop_e", "roll_stop_a", 0.6, function()
|
|
|
|
-- remove speed and enable input
|
|
level.release_action(bind_to_dik(key_bindings["k" .. leap .. "_STRAFE"]))
|
|
speed.remove_speed("talent_leap_speed")
|
|
game.only_allow_movekeys(false)
|
|
leap = nil
|
|
|
|
return true
|
|
end)
|
|
|
|
end
|
|
|
|
function update_leap()
|
|
if not leap then return end
|
|
level.hold_action(bind_to_dik(key_bindings["k" .. leap .. "_STRAFE"]))
|
|
end
|
|
|
|
local regen_tmr = 0
|
|
function jugger_regeneration_update()
|
|
local tg = time_global()
|
|
if regen_tmr > tg then return end
|
|
regen_tmr = tg + 1000
|
|
|
|
if picked_class ~= "jugger" then return end
|
|
if talents_table["defensive"]["jugger"]["regenerative"].level <= 0 then return end
|
|
|
|
local satiety = db.actor.satiety
|
|
local satiety_critical = ini_sys:r_float_ex("actor_condition", "satiety_critical")
|
|
local satiety_health_v = ini_sys:r_float_ex("actor_condition", "satiety_health_v")
|
|
|
|
local critical_something = satiety >= satiety_critical and 1 - satiety_critical or satiety_critical
|
|
local satiety_health_koef = (satiety - satiety_critical) / critical_something
|
|
local delta_health = satiety_health_v * satiety_health_koef * 6 -- engine per 1 real second
|
|
|
|
if delta_health > 0 then
|
|
db.actor:change_health(delta_health)
|
|
if db.actor.health > 1 then
|
|
db.actor:set_health_ex(1)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function actor_on_hud_animation_play(t, obj)
|
|
-- sniper gunslinger
|
|
local gunslinger_val = talent_gunslinger_reload(obj)
|
|
if gunslinger_val and string.find(t.anm_name, "reload") then
|
|
t.anm_speed = t.anm_speed * gunslinger_val
|
|
end
|
|
|
|
-- jugger heavy_guns_expert
|
|
local heavy_guns_expert_val = talent_heavy_guns_expert_reload(obj)
|
|
if heavy_guns_expert_val and string.find(t.anm_name, "reload") then
|
|
t.anm_speed = t.anm_speed * heavy_guns_expert_val
|
|
end
|
|
|
|
-- merc combat_engineer
|
|
local combat_engineer_t = talents_table["offensive"]["merc"]["combat_engineer"]
|
|
if combat_engineer_t.level > 0 then
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
if kind and kind == "w_explosive" and t.anm_name == "anm_throw_begin" then
|
|
local val = 1 + combat_engineer_t.percents[combat_engineer_t.level] / 100
|
|
t.anm_speed = t.anm_speed * val
|
|
end
|
|
end
|
|
|
|
-- merc assault_rifles_expert
|
|
local assault_rifles_expert_val = talent_assault_rifles_expert_reload(obj)
|
|
if assault_rifles_expert_val and string.find(t.anm_name, "reload") then
|
|
t.anm_speed = t.anm_speed * assault_rifles_expert_val
|
|
end
|
|
|
|
-- merc light_guns_expert
|
|
local light_guns_expert_val = talent_light_guns_expert_reload(obj)
|
|
if light_guns_expert_val and string.find(t.anm_name, "reload") then
|
|
t.anm_speed = t.anm_speed * light_guns_expert_val
|
|
end
|
|
|
|
-- merc grenadier
|
|
local grenadier_t = talents_table["defensive"]["merc"]["grenadier"]
|
|
if grenadier_t.level > 0 and t.anm_name == "anm_throw" then
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
if kind and kind == "w_explosive" then
|
|
durations_t.grenadier_var = grenadier_t.duration
|
|
end
|
|
end
|
|
|
|
-- assassin knifeman
|
|
local knifeman_t = talents_table["offensive"]["assassin"]["knifeman"]
|
|
if knifeman_t.level > 0 then
|
|
local kind = obj and ini_sys:r_string_ex(obj:section(), "kind")
|
|
if kind and kind == "w_melee" and (t.anm_name == "anm_attack" or t.anm_name == "anm_attack2") then
|
|
local val = 1 + knifeman_t.attack_speed[knifeman_t.level] / 100
|
|
t.anm_speed = t.anm_speed * val
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
local deep_wound_tmr = 0
|
|
local deep_wound_upd = 500
|
|
function deep_wound_update()
|
|
if is_empty(deep_wound_var) then return end
|
|
|
|
local tg = time_global()
|
|
if deep_wound_tmr > tg then return end
|
|
deep_wound_tmr = tg + deep_wound_upd
|
|
|
|
for id, t in pairs(deep_wound_var) do
|
|
deep_wound_var[id].time = deep_wound_var[id].time - deep_wound_upd
|
|
|
|
local npc = level.object_by_id(id)
|
|
local npc_fits = npc and (IsStalker(npc) or IsMonster(npc)) and npc.alive and npc:alive()
|
|
local bleeding = npc_fits and npc.bleeding
|
|
if (not bleeding) or (bleeding <= 0) or (deep_wound_var[id].time <= 0) then
|
|
deep_wound_var[id] = nil
|
|
end
|
|
|
|
-- continue
|
|
if deep_wound_var[id] then
|
|
local condition_sec = ini_sys:r_string_ex(npc:section(), "condition_sect")
|
|
local bleed_v = condition_sec and ini_sys:r_string_ex(condition_sec, "bleeding_v")
|
|
local extra_dmg = bleed_v and (bleeding * bleed_v * 6 * 0.5 * deep_wound_var[id].value)
|
|
if extra_dmg then
|
|
npc:change_health(-extra_dmg)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local modified_bullets_tmr = 0
|
|
local modified_bullets_upd = 1000
|
|
function modified_bullets_update()
|
|
if is_empty(modified_bullets_var) then return end
|
|
|
|
local tg = time_global()
|
|
if modified_bullets_tmr > tg then return end
|
|
modified_bullets_tmr = tg + modified_bullets_upd
|
|
|
|
for id, t in pairs(modified_bullets_var) do
|
|
modified_bullets_var[id].time = modified_bullets_var[id].time - modified_bullets_upd
|
|
|
|
local npc = level.object_by_id(id)
|
|
local npc_fits = npc and IsStalker(npc) and npc.alive and npc:alive()
|
|
if (not npc_fits) or (modified_bullets_var[id].time <= 0) then
|
|
modified_bullets_var[id] = nil
|
|
end
|
|
|
|
-- continue
|
|
if modified_bullets_var[id] then
|
|
local dmg = t.value
|
|
pr("modified_bullets, change npc id: %s || health from: %s to %s", id, round_idp(npc.health, 4), round_idp(npc.health - dmg, 4))
|
|
npc:change_health(-dmg)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
local reposition_tmr = 0
|
|
local reposition_upd = 500
|
|
local reposition_moving_for = 0
|
|
function reposition_update()
|
|
local tg = time_global()
|
|
if reposition_tmr > tg then return end
|
|
reposition_tmr = tg + reposition_upd
|
|
|
|
-- return if not merc or has no talent
|
|
if not (picked_class == "merc" and talents_table["defensive"]["merc"]["reposition"].level > 0) then
|
|
return
|
|
end
|
|
|
|
-- reset if actor not in combat
|
|
if is_empty(xr_combat_ignore.fighting_with_actor_npcs) then
|
|
reposition_moving_for = 0
|
|
return
|
|
end
|
|
|
|
-- reset if actor not moving
|
|
if not IsMoveState("mcAnyMove") then
|
|
reposition_moving_for = 0
|
|
return
|
|
end
|
|
|
|
-- add moving time
|
|
reposition_moving_for = reposition_moving_for + reposition_upd
|
|
if reposition_moving_for >= 2000 then -- reset and give reposition buff
|
|
reposition_moving_for = 0
|
|
durations_t.reposition_var = talents_table["defensive"]["merc"]["reposition"].duration
|
|
end
|
|
end
|
|
|
|
local spot_weakness_tmr = 0
|
|
function spot_weakness_update()
|
|
local tg = time_global()
|
|
if spot_weakness_tmr > tg then return end
|
|
spot_weakness_tmr = tg + 1000
|
|
|
|
-- return if not assassin or has no talent
|
|
if not (picked_class == "assassin" and talents_table["offensive"]["assassin"]["spot_weakness"].level > 0) then
|
|
return
|
|
end
|
|
|
|
local stop_itr = false
|
|
|
|
-- check if enemy in 20 meters radius and enemy and not fighting actor
|
|
local function itr(obj)
|
|
if (not stop_itr) and (IsStalker(obj) or IsMonster(obj)) and obj:alive() and obj:id() ~= 0 then
|
|
local ac_comm = get_actor_true_community()
|
|
local npc_comm = IsStalker(obj) and obj.character_community and obj:character_community()
|
|
local fighting_actor = xr_combat_ignore.fighting_with_actor_npcs[obj:id()]
|
|
if (not fighting_actor) and (IsMonster(obj) or (npc_comm and game_relations.is_factions_enemies(ac_comm, npc_comm))) then
|
|
stop_itr = true
|
|
|
|
spot_weakness_stacks = spot_weakness_stacks + 1
|
|
durations_t.spot_weakness_var = talents_table["offensive"]["assassin"]["spot_weakness"].duration
|
|
|
|
-- if has "observant" talent
|
|
if talents_table["offensive"]["assassin"]["assassin_observant"].level > 0 then
|
|
spot_weakness_stacks = 10
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
level.iterate_nearest(db.actor:position(), 20, itr)
|
|
|
|
-- clamp stacks
|
|
if spot_weakness_stacks > 10 then
|
|
spot_weakness_stacks = 10
|
|
end
|
|
|
|
end
|
|
|
|
local acrobatic_tmr = 0
|
|
function acrobatic_elusive_update()
|
|
local tg = time_global()
|
|
if acrobatic_tmr > tg then return end
|
|
acrobatic_tmr = tg + 2500
|
|
|
|
-- acrobatic
|
|
local acrobatic_t = talents_table["defensive"]["assassin"]["acrobatic"]
|
|
|
|
-- if talent picked but no bonus yet - add bonus
|
|
if (not acrobatic_var) and acrobatic_t.level > 0 and outfit_type(acrobatic_t.outfits) then
|
|
acrobatic_var = true
|
|
local cur_jump = db.actor:get_actor_jump_speed()
|
|
db.actor:set_actor_jump_speed(cur_jump * 1.3)
|
|
|
|
-- if talent not picked but has bonus - remove bonus
|
|
elseif acrobatic_var and (acrobatic_t.level <= 0 or (not outfit_type(acrobatic_t.outfits)) ) then
|
|
acrobatic_var = false
|
|
local cur_jump = db.actor:get_actor_jump_speed()
|
|
db.actor:set_actor_jump_speed(cur_jump / 1.3)
|
|
end
|
|
|
|
|
|
-- elusive
|
|
local elusive_t = talents_table["defensive"]["assassin"]["elusive"]
|
|
|
|
-- if talent picked and we in combat - add speed
|
|
local in_combat = not (is_empty(xr_combat_ignore.fighting_with_actor_npcs))
|
|
if in_combat and elusive_t.level > 0 then
|
|
local val = elusive_t.speed[elusive_t.level]
|
|
speed.add_speed("elusive", 1 + val / 100, false, true)
|
|
|
|
-- if talent not picked or we not in combat - remove speed
|
|
elseif (not in_combat) or elusive_t.level <= 0 then
|
|
speed.remove_speed("elusive")
|
|
end
|
|
|
|
end
|
|
|
|
|
|
function trooper_precision_on_fire(obj, wpn)
|
|
if not (obj:id() == 0 and picked_class == "trooper" and wpn) then return end
|
|
|
|
local precision_t = talents_table["offensive"]["trooper"]["precision"]
|
|
if precision_t.level > 0 then
|
|
local kind = ini_sys:r_string_ex(wpn:section(), "kind")
|
|
if kind and kind == "w_rifle" then
|
|
|
|
-- allow first bullet
|
|
if cooldowns_t.precision_var <= 0 then
|
|
precision_allow_dmg = true
|
|
-- dont allow if cd still on
|
|
else
|
|
precision_allow_dmg = false
|
|
end
|
|
|
|
-- set cd
|
|
cooldowns_t.precision_var = precision_t.cooldown
|
|
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function merc_flurry_on_fire(obj, wpn)
|
|
if not (obj:id() == 0 and picked_class == "merc" and wpn) then return end
|
|
|
|
local flurry_t = talents_table["defensive"]["merc"]["flurry"]
|
|
if flurry_t.level > 0 then
|
|
durations_t.flurry_var = flurry_t.duration
|
|
end
|
|
end
|
|
|
|
function on_sleep_kindred(hours)
|
|
local kindred_t = talents_table["defensive"]["monster_hunter"]["kindred"]
|
|
if kindred_t.level > 0 and kindred_var then
|
|
kindred_var = false
|
|
|
|
-- spawn dogs
|
|
local id = smart_terrain.nearest_to_actor_smart.id
|
|
local smart = id and alife_object(id)
|
|
local squad = smart and SIMBOARD:create_squad(smart, "kindred_dogs")
|
|
pr("kindred, dogs spawned on smart: %s", smart and smart:name())
|
|
end
|
|
end
|
|
|
|
function on_enemy_eval_kindred_our_dogs(obj, enemy, flags) -- for our dogs
|
|
local obj_id, en_id = obj:id(), enemy:id()
|
|
if obj_id == 0 then return end
|
|
|
|
local kindred_t = talents_table["defensive"]["monster_hunter"]["kindred"]
|
|
if kindred_t.level <= 0 then return end
|
|
|
|
local obj_sq = get_object_squad(obj)
|
|
local en_sq = en_id ~= 0 and get_object_squad(enemy)
|
|
local fights_actor = xr_combat_ignore.fighting_with_actor_npcs
|
|
|
|
-- if obj is dog (ignore actor and everyone except enemies around)
|
|
if obj_sq and obj_sq:section_name() == "kindred_dogs" and ( en_id == 0 or (not fights_actor[en_id]) ) then
|
|
flags.override = true
|
|
flags.result = false
|
|
return
|
|
end
|
|
|
|
-- if dog is enemy and obj not fighting actor (ignore dogs)
|
|
if en_sq and en_sq:section_name() == "kindred_dogs" and (not fights_actor[obj_id]) then
|
|
flags.override = true
|
|
flags.result = false
|
|
end
|
|
end
|
|
|
|
function on_enemy_eval_kindred_other_dogs(obj, enemy, flags) -- for other dogs
|
|
local obj_id, en_id = obj:id(), enemy:id()
|
|
if obj_id == 0 then return end
|
|
|
|
local dogs_exist = get_story_se_object("kindred_dogs")
|
|
if not dogs_exist then return end
|
|
|
|
-- if (enemy actor or companion) and obj is dog
|
|
local en_is_ac_or_comp = en_id == 0 or enemy:has_info("npcx_is_companion")
|
|
local obj_kind = ini_sys:r_string_ex(obj:section(), "species")
|
|
local obj_is_dog = obj_kind and (obj_kind == "dog" or obj_kind == "pseudodog")
|
|
if en_is_ac_or_comp and obj_is_dog then
|
|
flags.override = true
|
|
flags.result = false
|
|
return
|
|
end
|
|
|
|
-- if enemy is dog and (obj companion)
|
|
local en_kind = ini_sys:r_string_ex(enemy:section(), "species")
|
|
local en_is_dog = en_kind and (en_kind == "dog" or en_kind == "pseudodog")
|
|
local obj_is_comp = obj:has_info("npcx_is_companion")
|
|
if en_is_dog and obj_is_comp then
|
|
flags.override = true
|
|
flags.result = false
|
|
end
|
|
end
|
|
|
|
local kindred_dogs_hit_t = { [hit.burn] = true, [hit.chemical_burn] = true, [hit.shock] = true }
|
|
function monster_before_hit_kindred(obj, s_hit, bone_id, flags)
|
|
local obj_sq = get_object_squad(obj)
|
|
if not (obj_sq and obj_sq:section_name() == "kindred_dogs") then return end
|
|
|
|
local draft = s_hit.draftsman
|
|
if kindred_dogs_hit_t[s_hit.type] or (draft and IsAnomaly(draft)) then
|
|
flags.ret_value = false
|
|
end
|
|
end
|
|
|
|
function first_update_kindred()
|
|
if not kindred_prev_lvl then
|
|
kindred_prev_lvl = level.name()
|
|
end
|
|
|
|
if kindred_prev_lvl ~= level.name() then
|
|
pr("kindred, level changed from %s to %s || tp dogs", kindred_prev_lvl, level.name())
|
|
kindred_prev_lvl = level.name()
|
|
|
|
-- tp dogs if they exist
|
|
local dogs_squad = get_story_se_object("kindred_dogs")
|
|
if dogs_squad then
|
|
TeleportSquad(dogs_squad, db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id())
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
local tracker_particles_t = {}
|
|
local tracker_particles_tmr = 0
|
|
function monster_update_tracker(npc)
|
|
local is_bloodsucker = npc and npc:clsid() == clsid.bloodsucker_s
|
|
if not (is_bloodsucker and npc:alive()) then return end
|
|
|
|
local tracker_t = talents_table["defensive"]["monster_hunter"]["tracker"]
|
|
if tracker_t.level <= 0 then return end
|
|
|
|
local tg = time_global()
|
|
if tracker_particles_tmr > tg then return end
|
|
tracker_particles_tmr = tg + 250
|
|
|
|
-- draw particles
|
|
tracker_particles_t[tg] = particles_object("damage_fx\\smoke")
|
|
if tracker_particles_t[tg] then
|
|
tracker_particles_t[tg]:play_at_pos(npc:position())
|
|
CreateTimeEvent("remove_bloodsucker_particle_e_" .. tg, "remove_bloodsucker_particle_a_" .. tg, 0.5, function(key)
|
|
tracker_particles_t[key]:stop()
|
|
tracker_particles_t[key] = nil
|
|
return true
|
|
end, tg)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
local endur_tmr = 0
|
|
local endur_upd_rate = 0.5
|
|
function endurance_stamina_upd()
|
|
local tg = time_global()
|
|
if endur_tmr > tg then return end
|
|
endur_tmr = tg + 1000 * endur_upd_rate
|
|
|
|
if db.actor.power >=1 then return end
|
|
|
|
if db.actor.power < 0.05 and talents_table["endurance"]["sniper"]["second_wind"].level > 0 then
|
|
local wind_t = talents_table["endurance"]["sniper"]["second_wind"]
|
|
local cur_time = game.get_game_time()
|
|
if (not second_wind_cd) then
|
|
db.actor.power = db.actor.power + wind_t.stamina[wind_t.level] / 100
|
|
second_wind_cd = ctime_to_t(cur_time)
|
|
end
|
|
end
|
|
|
|
local stamina_regen = 0
|
|
for name, t in pairs(talents_table["endurance"]["sniper"]) do
|
|
if t.stamina and this[t.func] and t.level > 0 then
|
|
local val = t.stamina[t.level]
|
|
val = this[t.func](val) or 0
|
|
stamina_regen = stamina_regen + val * endur_upd_rate
|
|
end
|
|
end
|
|
|
|
db.actor.power = db.actor.power + (stamina_regen / 100)
|
|
|
|
if db.actor.power >= 1 then
|
|
db.actor.power = 1
|
|
end
|
|
|
|
end
|
|
|
|
local speed_upd_tmr = 0
|
|
function endurance_speed_upd()
|
|
local tg = time_global()
|
|
if speed_upd_tmr > tg then return end
|
|
speed_upd_tmr = tg + 1000
|
|
|
|
for name, t in pairs(talents_table["endurance"]["sniper"]) do
|
|
if t.speed and this[t.func] and t.level > 0 then
|
|
local val = t.speed[t.level]
|
|
val = this[t.func](val)
|
|
val = val and (val / 100) or 0
|
|
speed.add_speed(name, 1 + val, false, true)
|
|
end
|
|
end
|
|
|
|
if talents_table["defensive"]["jugger"]["bulky"].level > 0 then
|
|
local bulky_t = talents_table["defensive"]["jugger"]["bulky"]
|
|
local outfits = outfit_type(bulky_t.outfits)
|
|
local val = outfits and (bulky_t.speed[1] / 100) or 0
|
|
speed.add_speed("bulky", 1 + val, false, true)
|
|
end
|
|
|
|
if class_special_stats[picked_class] then
|
|
local val = class_special_stats[picked_class].speed / 100
|
|
speed.add_speed(picked_class .. "_special", 1 + val, false, true)
|
|
end
|
|
|
|
end
|
|
|
|
local weight_upd_tmr = 0
|
|
function endurance_weight_upd()
|
|
local tg = time_global()
|
|
if weight_upd_tmr > tg then return end
|
|
weight_upd_tmr = tg + 1000
|
|
|
|
for name, t in pairs(talents_table["endurance"]["sniper"]) do
|
|
if t.weight and this[t.func] and t.level > 0 then
|
|
local val = t.weight[t.level]
|
|
val = this[t.func](val) or 0
|
|
weight.add_weight(name, val, true)
|
|
end
|
|
end
|
|
|
|
if class_special_stats[picked_class] then
|
|
local val = class_special_stats[picked_class].weight
|
|
weight.add_weight(picked_class .. "_special", val, false, true)
|
|
end
|
|
|
|
if ui_inventory and ui_inventory.GUI then
|
|
ui_inventory.GUI:UpdateWeight()
|
|
end
|
|
|
|
end
|
|
|
|
-- speech price change
|
|
local cur_trader_id
|
|
function on_get_item_cost(kind, item, profile, vanilla_cost, ret)
|
|
|
|
-- buy from npc
|
|
if profile.mode == 2 then
|
|
|
|
local add_buy = 0
|
|
pr("------------ buy sec: %s ------------", item:section())
|
|
|
|
-- preparation
|
|
local preparation_val = talent_preparation(item)
|
|
if preparation_val then
|
|
pr("preparation, sec: %s || val: %s", item:section(), preparation_val)
|
|
add_buy = add_buy + preparation_val
|
|
end
|
|
|
|
-- observant
|
|
local observant_val = talent_observant(item)
|
|
if observant_val then
|
|
pr("observant, sec: %s || val: %s", item:section(), observant_val)
|
|
add_buy = add_buy + observant_val
|
|
end
|
|
|
|
-- gregarious
|
|
local gregarious_val = talent_gregarious(item)
|
|
if gregarious_val then
|
|
pr("gregarious, sec: %s || val: %s", item:section(), gregarious_val)
|
|
add_buy = add_buy + gregarious_val
|
|
end
|
|
|
|
-- well_liked
|
|
local well_liked_val = talent_well_liked(item, cur_trader_id)
|
|
if well_liked_val then
|
|
pr("well_liked, sec: %s || val: %s", item:section(), well_liked_val)
|
|
add_buy = add_buy + well_liked_val
|
|
end
|
|
|
|
-- buy total
|
|
pr("BUY old cost: %s", ret.new_cost or vanilla_cost)
|
|
ret.new_cost = (ret.new_cost or vanilla_cost) * (1 + add_buy / 100)
|
|
pr("BUY new cost: %s", ret.new_cost)
|
|
end
|
|
|
|
|
|
-- sell to npc
|
|
if profile.mode == 1 then
|
|
|
|
local add_sell = 0
|
|
pr("------------ sell sec: %s ------------", item:section())
|
|
|
|
-- supplier
|
|
local supplier_val = talent_supplier(item)
|
|
if supplier_val then
|
|
pr("supplier, sec: %s || val: %s", item:section(), supplier_val)
|
|
add_sell = add_sell + supplier_val
|
|
end
|
|
|
|
-- smuggler
|
|
local smuggler_val = talent_smuggler(item)
|
|
if smuggler_val then
|
|
pr("smuggler, sec: %s || val: %s", item:section(), smuggler_val)
|
|
add_sell = add_sell + smuggler_val
|
|
end
|
|
|
|
-- persuasion
|
|
local persuasion_val = talent_persuasion(item)
|
|
if persuasion_val then
|
|
pr("persuasion, sec: %s || val: %s", item:section(), persuasion_val)
|
|
add_sell = add_sell + persuasion_val
|
|
end
|
|
|
|
-- shady
|
|
local shady_val = talent_shady(item, profile)
|
|
if shady_val then
|
|
pr("shady, sec: %s || val: %s", item:section(), shady_val)
|
|
add_sell = add_sell + shady_val
|
|
end
|
|
|
|
-- cynical
|
|
local cynical_val = talent_cynical_set(item, profile, cur_trader_id, ret.new_cost, vanilla_cost)
|
|
if cynical_val then
|
|
pr("cynical, sec: %s || val: %s", item:section(), cynical_val)
|
|
add_sell = add_sell + cynical_val
|
|
end
|
|
|
|
-- sell total
|
|
pr("SELL old cost: %s", ret.new_cost or vanilla_cost)
|
|
ret.new_cost = (ret.new_cost or vanilla_cost) * (1 + add_sell / 100)
|
|
pr("SELL new cost: %s", ret.new_cost)
|
|
end
|
|
|
|
end
|
|
|
|
function ActorMenu_on_before_init_mode(mode, flags, obj)
|
|
if mode == "trade" and obj and obj:id() then
|
|
local shady_t = talents_table["speech"]["sniper"]["shady"]
|
|
local cfg = trade_manager.get_trade_profile(obj:id(), "cfg_ltx")
|
|
if shady_t.level > 0 and cfg and cfg == "items\\trade\\trade_generic.ltx" then
|
|
|
|
local money_per_level = shady_t.stalkers_money[1]
|
|
|
|
-- shady (give/update npc money on trade init)
|
|
for i = 1, shady_t.level do
|
|
if (not obj:has_info("shady_" .. i)) then
|
|
obj:give_info_portion("shady_" .. i)
|
|
pr("shady level %s, give %s money for stalker", i, money_per_level)
|
|
obj:give_money(money_per_level)
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
function open_trade(mode)
|
|
if mode ~= 2 then return end
|
|
|
|
local npc = mob_trade.GetTalkingNpc()
|
|
local is_trader = npc and trade_manager.get_trade_profile(npc:id(), "cfg_ltx")
|
|
if not is_trader then return end
|
|
|
|
cur_trader_id = npc:id()
|
|
end
|
|
|
|
function close_trade(mode)
|
|
if cur_trader_id and mode ~= 2 then
|
|
cur_trader_id = nil
|
|
end
|
|
end
|
|
|
|
local cyn_tmr = 0
|
|
function cynical_coin_update()
|
|
local tg = time_global()
|
|
if cyn_tmr > tg then return end
|
|
cyn_tmr = tg + 5000
|
|
|
|
if talents_table["speech"]["sniper"]["cynical"].level > 0 and (not cynical_cd) then
|
|
-- if no coin then spawn coin
|
|
local has_coin = db.actor:object("cynical_coin")
|
|
if not has_coin then
|
|
alife_create_item("cynical_coin", db.actor)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function actor_on_item_buy(item)
|
|
if not cur_trader_id then return end
|
|
|
|
local trader = cur_trader_id and level.object_by_id(cur_trader_id)
|
|
if not (trader and trader.alive and trader:alive()) then return end
|
|
|
|
-- charismatic
|
|
talent_charismatic(item, trader)
|
|
|
|
end
|
|
|
|
function actor_on_item_sell(item)
|
|
if not cur_trader_id then return end
|
|
|
|
local trader = cur_trader_id and level.object_by_id(cur_trader_id)
|
|
if not (trader and trader.alive and trader:alive()) then return end
|
|
|
|
local trader_comm
|
|
if trader.section and trader:section() and (trader:section() == "m_trader" or trader:section() == "m_lesnik") then
|
|
trader_comm = "stalker"
|
|
else
|
|
trader_comm = trader:character_community()
|
|
end
|
|
|
|
if item:section() == "inv_update_item_xd" then return end
|
|
|
|
-- cynical
|
|
talent_cynical_sell(item, trader_comm)
|
|
|
|
-- deceitful
|
|
talent_deceitful(item, trader)
|
|
|
|
end
|
|
-------------------------------------
|
|
|
|
-- misc
|
|
function outfit_type(comp_t)
|
|
local outfit = db.actor:item_in_slot(7)
|
|
local kind = outfit and ini_sys:r_string_ex(outfit:section(), "kind")
|
|
if kind and in_ar(comp_t, kind) then
|
|
return true
|
|
end
|
|
end
|
|
|
|
function in_ar(a, v)
|
|
for i, val in ipairs(a) do
|
|
if val == v then return true end
|
|
end
|
|
end
|
|
|
|
function install_random_upgrade(item)
|
|
local upgr_t = utils_item.get_upgrades_tree(item:section(), true)
|
|
if (is_empty(upgr_t)) then return end
|
|
|
|
local to_install = {}
|
|
for row, t in pairs(upgr_t) do
|
|
if t[1] and t[1].section then
|
|
pr("store sec: %s", t[1].section)
|
|
to_install[#to_install + 1] = t[1].section
|
|
end
|
|
end
|
|
|
|
local picked_upgr = #to_install > 0 and to_install[math.random(1, #to_install)]
|
|
if picked_upgr then
|
|
pr("picked and installing upgrade: %s", picked_upgr)
|
|
inventory_upgrades.force_upgrade = true
|
|
item:install_upgrade(picked_upgr)
|
|
inventory_upgrades.force_upgrade = false
|
|
end
|
|
|
|
end
|
|
|
|
function play_evasion_anm()
|
|
local anms = { "hit_left", "hit_right" }
|
|
level.add_cam_effector("camera_effects\\" .. anms[math.random(1, #anms)] .. ".anm", 812415, false, "", 0, true, 2)
|
|
end
|
|
|
|
local collector_table = {
|
|
["mutant"] = {
|
|
tushkano = 1, flesh = 1, zombie = 1, dog = 1, fracture = 2, boar = 2, cat = 2, pseudodog = 2, SM_LURKER = 3, snork = 3, SM_KARLIK = 3, SM_POLTER_G = 4, SM_PYRO_G = 4, SM_PSEUDO_G = 4, burer = 4, bloodsucker = 4, SM_PSYSUCKER = 4, chimera = 7,
|
|
controller = 7, giant = 10
|
|
},
|
|
|
|
["stalker"] = {
|
|
novice = 2, trainee = 2, experienced = 3, professional = 3,
|
|
veteran = 4, expert = 4, master = 5, legend = 5
|
|
},
|
|
}
|
|
|
|
function get_collector_val(npc)
|
|
if IsMonster(npc) then
|
|
local mutant_str = ini_sys:r_string_ex(npc:section(), "kind") or ini_sys:r_string_ex(npc:section(), "species") or nil
|
|
if mutant_str and collector_table["mutant"][mutant_str] then
|
|
return collector_table["mutant"][mutant_str]
|
|
end
|
|
elseif IsStalker(npc) then
|
|
local obj_rank_name = ranks.get_obj_rank_name(npc)
|
|
local rank_val = obj_rank_name and collector_table["stalker"][obj_rank_name]
|
|
if rank_val then
|
|
return collector_table["stalker"][obj_rank_name]
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function actor_has_coin()
|
|
local has_coin = false
|
|
db.actor:iterate_belt( function(owner, item)
|
|
if item:section() == "cynical_coin" then
|
|
has_coin = true
|
|
end
|
|
end)
|
|
|
|
return has_coin
|
|
end
|
|
|
|
function check_mutant_type(npc, typ, str)
|
|
if not npc then return end
|
|
|
|
local mutant_type = ini_sys:r_string_ex(npc:section(), "kind") or ini_sys:r_string_ex(npc:section(), "species")
|
|
if not mutant_type then return end
|
|
|
|
if mutants_t["beast"][mutant_type] then
|
|
return typ and "beast" or mutant_type
|
|
elseif mutants_t["hum"][mutant_type] then
|
|
return typ and "hum" or mutant_type
|
|
end
|
|
end
|
|
|
|
function get_mutant_name(typ)
|
|
local ret_str = ""
|
|
|
|
-- return all of this typ as string
|
|
if typ and mutants_t[typ] then
|
|
for kind, name in pairs(mutants_t[typ]) do
|
|
local s = gt(name)
|
|
ret_str = ret_str .. " " .. s .. ","
|
|
end
|
|
end
|
|
|
|
-- make string out of mutants that werent killed yet
|
|
if typ == "trophies" then
|
|
for mut_typ, t in pairs(mutants_t) do
|
|
for kind, name in pairs(t) do
|
|
if not (monster_hunter_trophies[kind]) then
|
|
local s = gt(name)
|
|
ret_str = ret_str .. " " .. s .. ","
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
ret_str = string.sub(ret_str, 1, -2) -- remove last comma
|
|
|
|
return ret_str
|
|
end
|
|
|
|
function get_left_communities()
|
|
local ret_str = ""
|
|
|
|
-- make string out of communities that werent killed yet
|
|
for comm, _ in pairs(stalker_comms_t) do
|
|
if not (merc_head_hunter_t[comm]) then
|
|
local s = gt("st_faction_" .. comm)
|
|
ret_str = ret_str .. " " .. s .. ","
|
|
end
|
|
end
|
|
|
|
ret_str = string.sub(ret_str, 1, -2) -- remove last comma
|
|
|
|
return ret_str
|
|
end
|
|
|
|
function is_mg_type(sec)
|
|
if not sec then return end
|
|
|
|
for str, _ in pairs(machine_guns_t) do
|
|
if string.find(sec, str) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
-- for stats, calculate cur and max stats (all "cur" are included in "max" too)
|
|
local calc_stats_t = {
|
|
["offensive"] = {
|
|
["sniper"] = {
|
|
cur = { "sniper_rifles", "assault_rifles_novice", "pistols", "experienced_sniper", "steady", "convenience", "gunslinger", "loner", "nocturnal", "carnage" },
|
|
max = { "sharpshooter", "ambush", "soft_spot" },
|
|
},
|
|
["trooper"] = {
|
|
cur = { "assault_rifles", "shotguns_novice", "submachine_guns", "experienced_rifleman", "focus", "thrill", "precision", "barrage", "reckless", "collector" },
|
|
max = { "duelist" },
|
|
},
|
|
["jugger"] = {
|
|
cur = { "shotguns", "machine_guns", "submachine", "heavy_guns_expert", "fury", "selective" },
|
|
max = { "hunter", "bounty_hunter", "merciless", "execution", "frightening" },
|
|
},
|
|
["monster_hunter"] = {
|
|
cur = { "trophies", "fierce", "ranger" },
|
|
max = { "beast_anatomy", "humanoid_anatomy", "beast_rivalry", "humanoid_rivalry" },
|
|
},
|
|
["merc"] = {
|
|
cur = { "firearm_enthusiast", "overdrive", "head_hunter", "bullseye", "assault_rifles_expert", "light_guns_expert", "sniper_rifles_expert" },
|
|
max = { "arrogance", "aim_torso", "aim_limbs" },
|
|
},
|
|
["assassin"] = {
|
|
cur = { "smg_novice", "handgun_novice", "knifeman", "spot_weakness", "tactical", "pragmatic" },
|
|
max = { "undercover", "backstab", "saboteur", "surprise_attack", "vile" },
|
|
},
|
|
},
|
|
|
|
["defensive"] = {
|
|
["sniper"] = {
|
|
cur = { "adaptive", "iron_stomach", "plastic_pads", "aluminum_plates", "nature_resistance", "rad_resistant", "technician", "expert_technician", "agile" },
|
|
max = { },
|
|
},
|
|
["trooper"] = {
|
|
cur = { "ceramic_plates", "isolated", "armorer", "chem_resistant", "suppresion_fire", "cautious", "fireproof", "composure", "enduring", "adorned", "expert_armorer", "last_stand", "leap" },
|
|
max = { },
|
|
},
|
|
["jugger"] = {
|
|
cur = { "steel_plates", "steel_pads", "heavy_armor_expert", "dismorale", "sturdy", "sanity", "anxiety", "bulky", "secure", "lucid" },
|
|
max = { "turmoil" },
|
|
},
|
|
["monster_hunter"] = {
|
|
cur = { "shoulder_pads", "careful", "melee_defense", "special_diet", "clarity", "soft_armor", "stable_mind", "second_skin", "custom_fit" },
|
|
max = { "beast_demeanor", "humanoid_demeanor" },
|
|
},
|
|
["merc"] = {
|
|
cur = { "mercenary_vest", "insensitive", "chem_reliant", "grenadier", "addictive", "flurry", "loose_fit", "reposition" },
|
|
max = { "confusion", "disguise_master" },
|
|
},
|
|
["assassin"] = {
|
|
cur = { "ordinary", "quiet", "evanescent", "lurking", "subtle", "covert", "cunning", "vague", "sneaky", "incognito", "ghost", "elusive" },
|
|
max = { "deception" },
|
|
},
|
|
},
|
|
|
|
["endurance"] = {
|
|
["sniper"] = {
|
|
speed = { "ease", "swifty", "insomnia", "athletic" }, stamina = { "resting", "tireless", "courage" }, weight = { "hoarder", "iron_spine" },
|
|
},
|
|
}
|
|
}
|
|
calc_stats_t["endurance"]["trooper"] = calc_stats_t["endurance"]["sniper"]
|
|
calc_stats_t["endurance"]["jugger"] = calc_stats_t["endurance"]["sniper"]
|
|
calc_stats_t["endurance"]["monster_hunter"] = calc_stats_t["endurance"]["sniper"]
|
|
calc_stats_t["endurance"]["merc"] = calc_stats_t["endurance"]["sniper"]
|
|
calc_stats_t["endurance"]["assassin"] = calc_stats_t["endurance"]["sniper"]
|
|
|
|
local max_mult_t = {
|
|
["sharpshooter"] = 150,
|
|
["focus"] = 100,
|
|
["reckless"] = 75,
|
|
["collector"] = 1500,
|
|
["expert_technician"] = 100,
|
|
["cautious"] = 2,
|
|
["adorned"] = 5,
|
|
["expert_armorer"] = 10,
|
|
["secure"] = 5,
|
|
["lucid"] = 100,
|
|
["athletic"] = 100,
|
|
["iron_spine"] = 100,
|
|
["trophies"] = 20,
|
|
["fierce"] = 5,
|
|
["second_skin"] = 5,
|
|
["head_hunter"] = 11,
|
|
["arrogance"] = 7,
|
|
["spot_weakness"] = 10,
|
|
["sneaky"] = 2,
|
|
}
|
|
|
|
local trooper_def_mults = {
|
|
"ceramic_plates", "isolated", "armorer", "chem_resistant", "fireproof"
|
|
}
|
|
|
|
local monster_hunter_def_mults = {
|
|
"shoulder_pads", "careful", "soft_armor", "clarity"
|
|
}
|
|
|
|
local stats_conv_t = {
|
|
["sniper_rifle"] = "w_sniper", ["assault_rifle"] = "w_rifle",
|
|
["radiation"] = "radi", ["ballistic"] = "bal", ["rupture"] = "rup", ["explosive"] = "expl",
|
|
}
|
|
|
|
function r_stat(v) return round_idp(v, 1) end
|
|
|
|
-- offensive returns { [1] = base_dmg, [2] = max_dmg, [3] = base_crit, [4] = max_crit }
|
|
-- defensive returns { [1] = cur_res, [2] = max_res }
|
|
-- endurance returns { [1] = cur_val, [2] = max_val }
|
|
function calculate_stats(typ, class_name, req_typ)
|
|
if not (typ and class_name) then return end
|
|
|
|
local stat_t = calc_stats_t[typ] and calc_stats_t[typ][class_name]
|
|
if not stat_t then return end
|
|
|
|
-- offensive
|
|
if typ == "offensive" and req_typ then
|
|
return calc_stats_offensive(typ, class_name, req_typ, stat_t)
|
|
end
|
|
|
|
-- defensive
|
|
if typ == "defensive" and req_typ then
|
|
return calc_stats_defensive(typ, class_name, req_typ, stat_t)
|
|
end
|
|
|
|
-- endurance
|
|
if typ == "endurance" and req_typ then
|
|
return calc_stats_endurance(typ, class_name, req_typ, stat_t)
|
|
end
|
|
|
|
end
|
|
|
|
function calc_stats_offensive(typ, class_name, req_typ, stat_t)
|
|
local wpn = stats_conv_t[req_typ] or "w_" .. req_typ
|
|
|
|
-- base/max dmg and crit
|
|
local cur_dmg = 80
|
|
local cur_crit = 0
|
|
local max_dmg = 80
|
|
local max_crit = 0
|
|
for talent, t in pairs(talents_table[typ][class_name]) do
|
|
-- if picked AND wpn fits
|
|
if t.level > 0 and (in_ar(t.wpns, wpn) or in_ar(t.wpns, "all")) then
|
|
|
|
-- mult for max
|
|
local mult = max_mult_t[talent] or 1
|
|
|
|
-- cur
|
|
if in_ar(stat_t.cur, talent) then
|
|
-- damage
|
|
if t.damage then
|
|
local val = t.damage[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](nil, val) or 0
|
|
end
|
|
cur_dmg = cur_dmg + val
|
|
end
|
|
|
|
-- crit
|
|
if t.crit then
|
|
local val = t.crit[t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](nil, val) or 0
|
|
end
|
|
cur_crit = cur_crit + val
|
|
end
|
|
end
|
|
|
|
|
|
-- max
|
|
if in_ar(stat_t.max, talent) or in_ar(stat_t.cur, talent) then
|
|
-- damage
|
|
if t.damage then
|
|
local val = t.damage[t.level] * mult
|
|
max_dmg = max_dmg + val
|
|
|
|
-- jugger mastery
|
|
local mastery_t = talents_table[typ][class_name]["mastery"]
|
|
if mastery_t and mastery_t.level > 0 then
|
|
if talent == "merciless" then
|
|
max_dmg = max_dmg + mastery_t.merciless_bonus
|
|
elseif talent == "fury" then
|
|
max_dmg = max_dmg + mastery_t.fury_bonus
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- crit
|
|
if t.crit then
|
|
local val = t.crit[t.level] * mult
|
|
max_crit = max_crit + val
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
-- ret damage and crit
|
|
return { r_stat(cur_dmg), r_stat(max_dmg), r_stat(cur_crit), r_stat(max_crit) }
|
|
end
|
|
|
|
function calc_stats_defensive(typ, class_name, req_typ, stat_t)
|
|
local res = stats_conv_t[req_typ] or req_typ
|
|
|
|
-- base and max values
|
|
local cur_res = 0
|
|
local max_res = 0
|
|
for talent, t in pairs(talents_table[typ][class_name]) do
|
|
-- talent is picked AND has our stat AND outfit fits
|
|
if t.level > 0 and t[res] and (outfit_type(t.outfits) or in_ar(t.outfits, "all")) then
|
|
|
|
-- mult for max
|
|
local mult = max_mult_t[talent] or 1
|
|
|
|
-- trooper mult for max
|
|
local trooper_mult = 1
|
|
if in_ar(trooper_def_mults, talent) and outfit_type({"o_medium", "o_sci"}) then
|
|
trooper_mult = 2
|
|
end
|
|
|
|
-- monster hunter mult for max
|
|
local monster_hunter_mult = 1
|
|
if in_ar(monster_hunter_def_mults, talent) and outfit_type({"o_medium"}) then
|
|
monster_hunter_mult = 2
|
|
end
|
|
|
|
-- merc mult for cur and max
|
|
local merc_mult = 1
|
|
if class_name == "merc" and outfit_type({"o_heavy"}) then
|
|
local direct_t = talents_table[typ][class_name]["direct"]
|
|
if direct_t.level > 0 then
|
|
local direct_val = direct_t.percents[direct_t.level] / 100
|
|
merc_mult = merc_mult * direct_val
|
|
else
|
|
merc_mult = 0
|
|
end
|
|
end
|
|
|
|
-- inventor mult for both cur and max
|
|
local inventor_mult = 1
|
|
if talents_table[typ][class_name]["inventor"] and talents_table[typ][class_name]["inventor"].level > 0 and outfit_type({"o_light"}) then
|
|
inventor_mult = 2
|
|
end
|
|
|
|
-- covert mult for both cur and max
|
|
local covert_mult = 1
|
|
if res == "disguise" and class_name == "assassin" and outfit_type({"o_medium", "o_sci"}) then
|
|
local covert_t = talents_table[typ][class_name]["covert"]
|
|
if covert_t.level > 0 then
|
|
covert_mult = 1.5
|
|
end
|
|
end
|
|
|
|
-- cunning mult for both cur and max
|
|
local cunning_mult = 1
|
|
if res == "stealth" and class_name == "assassin" and outfit_type({"o_light"}) then
|
|
local cunning_t = talents_table[typ][class_name]["cunning"]
|
|
if cunning_t.level > 0 then
|
|
cunning_mult = 1.5
|
|
end
|
|
end
|
|
|
|
-- cur
|
|
if in_ar(stat_t.cur, talent) then
|
|
local val = t[res][t.level]
|
|
if this[t.func] then
|
|
val = this[t.func](val) or 0
|
|
end
|
|
cur_res = cur_res + val * inventor_mult * merc_mult * covert_mult * cunning_mult
|
|
end
|
|
|
|
-- max
|
|
if in_ar(stat_t.max, talent) or in_ar(stat_t.cur, talent) then
|
|
local val = t[res][t.level] * mult * trooper_mult * monster_hunter_mult * merc_mult * covert_mult * cunning_mult
|
|
|
|
-- anxiety bonus
|
|
local anxiety_t = talents_table[typ][class_name]["anxiety"]
|
|
if talent == "dismorale" and anxiety_t and anxiety_t.level > 0 then
|
|
val = val * 2
|
|
end
|
|
|
|
-- clamp secure
|
|
if talent == "secure" and val > 15 then
|
|
val = 15
|
|
end
|
|
|
|
max_res = max_res + val * inventor_mult
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
-- if not stealth / disguise / evade then return +80
|
|
if res ~= "stealth" and res ~= "disguise" and res ~= "evade" then
|
|
cur_res = cur_res + 80
|
|
max_res = max_res + 80
|
|
end
|
|
|
|
return { r_stat(cur_res), r_stat(max_res) }
|
|
end
|
|
|
|
function calc_stats_endurance(typ, class_name, req_typ, stat_t)
|
|
local cur_val = 0
|
|
local max_val = 0
|
|
for talent, t in pairs(talents_table[typ][class_name]) do
|
|
|
|
if t.level > 0 and stat_t[req_typ] and in_ar(stat_t[req_typ], talent) then
|
|
|
|
local mult = max_mult_t[talent] or 1
|
|
|
|
-- cur value
|
|
local val = t[req_typ][t.level]
|
|
cur_val = cur_val + (this[t.func](val) or 0)
|
|
|
|
-- max value
|
|
max_val = max_val + val * mult
|
|
|
|
end
|
|
end
|
|
|
|
-- stamina
|
|
if req_typ == "stamina" then
|
|
return { r_stat(cur_val), r_stat(max_val) }
|
|
end
|
|
|
|
-- weight
|
|
if req_typ == "weight" then
|
|
-- special
|
|
cur_val = cur_val + class_special_stats[class_name].weight
|
|
max_val = max_val + class_special_stats[class_name].weight
|
|
|
|
return { r_stat(cur_val), r_stat(max_val) }
|
|
end
|
|
|
|
-- speed
|
|
if req_typ == "speed" then
|
|
|
|
-- bulky
|
|
local bulky_t = talents_table["defensive"]["jugger"]["bulky"]
|
|
if bulky_t and bulky_t.level > 0 and outfit_type(bulky_t.outfits) then
|
|
cur_val = cur_val - 15
|
|
max_val = max_val - 15
|
|
end
|
|
|
|
-- elusive
|
|
local elusive_t = talents_table["defensive"]["assassin"]["elusive"]
|
|
if elusive_t and elusive_t.level > 0 and outfit_type({"o_medium", "o_sci", "o_light"}) then
|
|
local in_combat = not (is_empty(xr_combat_ignore.fighting_with_actor_npcs))
|
|
if in_combat then
|
|
cur_val = cur_val + elusive_t.speed[elusive_t.level]
|
|
max_val = max_val + elusive_t.speed[elusive_t.level]
|
|
end
|
|
end
|
|
|
|
-- special
|
|
cur_val = cur_val + class_special_stats[class_name].speed
|
|
max_val = max_val + class_special_stats[class_name].speed
|
|
end
|
|
|
|
cur_val = cur_val + 100
|
|
max_val = max_val + 100
|
|
|
|
-- ret val
|
|
return { r_stat(cur_val), r_stat(max_val) }
|
|
end
|
|
|
|
|
|
-- Tooltip requirements string
|
|
function get_tooltip_req_str(typ, class_name, talent)
|
|
local tal_t = talents_table[typ][class_name][talent]
|
|
|
|
-- "all" points string
|
|
local all_str = gt("talents_tooltip_req") .. " " .. gt("talents_tooltip_req_all") .. " " .. tal_t.req.all
|
|
|
|
-- "all" met
|
|
local all_met = false
|
|
local spent_points = talents_leveling.talent_levels[typ].spent_points
|
|
if spent_points >= tal_t.req.all then
|
|
all_met = true
|
|
end
|
|
|
|
-- if only "all" in table
|
|
if size_table(tal_t.req) <= 1 then
|
|
return all_met and "" or all_str
|
|
end
|
|
|
|
-- check "other talent" requirement
|
|
local req_talent_met = false
|
|
local req_talent, req_level
|
|
for req_k, req_v in pairs(tal_t.req) do
|
|
if req_k ~= "all" then
|
|
req_talent = req_k
|
|
req_level = req_v
|
|
end
|
|
end
|
|
|
|
if talents_table[typ][class_name][req_talent].level >= req_level then
|
|
req_talent_met = true
|
|
end
|
|
|
|
-- if "other talent" requirement met
|
|
if req_talent_met then
|
|
return all_met and "" or all_str
|
|
end
|
|
|
|
-- "other talent" not met
|
|
-- "all" met
|
|
if all_met then
|
|
return gt("talents_tooltip_req") .. " " .. gt(req_talent .. "_name") .. " " .. req_level
|
|
end
|
|
|
|
-- "other talent" and "all" not met
|
|
return all_str .. ", " .. gt(req_talent .. "_name") .. " " .. req_level
|
|
end
|
|
|
|
|
|
-- Tooltip description
|
|
local pos_str_ar = { "damage", "crit", "crit_damage", "reload", "bleed", "burn", "chem", "shock", "radi", "rup", "strike", "bal", "expl", "psi", "speed", "defense", "sell", "evade", "disguise", "stealth", "attack_speed" }
|
|
local pos_str_exc_ar = { "refreshing", "hoarder", "second_wind" }
|
|
local neg_str_ar = { "weight" }
|
|
local none_str_ar = { "buy", "rad_food", "heal", "free", "stalkers_money", "hp_thresh", "radius", "percents" }
|
|
local game_hours_cd = { "artistic", "second_wind", "deadly_blow" }
|
|
|
|
function get_tooltip_descr_str(typ, class_name, talent)
|
|
local class_t = talents_table[typ][class_name]
|
|
local tal_t = talents_table[typ][class_name][talent]
|
|
local descr_ar = tal_t.descr
|
|
|
|
-- if descr field does not exist - return normal xml string
|
|
if not descr_ar then
|
|
return gt(talent .. "_descr")
|
|
end
|
|
|
|
-- collect variables
|
|
local vals_ar = {}
|
|
for idx, name in ipairs(descr_ar) do
|
|
if tal_t[name] then
|
|
-- get level + 1 or level if table and number if number
|
|
if type(tal_t[name]) == "number" then
|
|
if in_ar(game_hours_cd, talent) then -- game hours
|
|
vals_ar[#vals_ar + 1] = tal_t[name] / 600 .. " " .. gt("talents_cd_game_hours")
|
|
elseif name == "cooldown" then -- real seconds
|
|
vals_ar[#vals_ar + 1] = tal_t[name] .. " " .. gt("talents_cd_sec")
|
|
else -- number
|
|
local val = tal_t[name]
|
|
if talent == "dismorale" and class_t["anxiety"].level > 0 then -- dismorale duration
|
|
val = 30
|
|
elseif talent == "execution" and class_t["mastery"].level > 0 then -- execution+mastery
|
|
val = val + class_t["mastery"].execution_bonus
|
|
elseif talent == "frightening" and class_t["mastery"].level > 0 then -- frightening+mastery
|
|
val = val + class_t["mastery"].frightening_bonus
|
|
end
|
|
vals_ar[#vals_ar + 1] = val
|
|
end
|
|
|
|
elseif type(tal_t[name]) == "table" then
|
|
|
|
local cur_val = tal_t[name][tal_t.level] or tal_t[name][tal_t.level + 1]
|
|
local next_val = tal_t[name][tal_t.level + 1] or tal_t[name][tal_t.level]
|
|
|
|
-- dismorale double
|
|
if talent == "dismorale" and class_t["anxiety"].level > 0 then
|
|
cur_val = cur_val * 2
|
|
next_val = next_val * 2
|
|
end
|
|
|
|
-- mastery + merciless
|
|
if talent == "merciless" and class_t["mastery"].level > 0 then
|
|
cur_val = cur_val + class_t["mastery"].merciless_bonus
|
|
next_val = next_val + class_t["mastery"].merciless_bonus
|
|
end
|
|
|
|
-- mastery + fury
|
|
if talent == "fury" and class_t["mastery"].level > 0 then
|
|
cur_val = cur_val + class_t["mastery"].fury_bonus
|
|
next_val = next_val + class_t["mastery"].fury_bonus
|
|
end
|
|
|
|
|
|
-- have points
|
|
if talents_leveling.talent_levels[typ].points > 0 then
|
|
|
|
-- level = 0 (display "next")
|
|
if tal_t.level <= 0 then
|
|
vals_ar[#vals_ar + 1] = next_val
|
|
|
|
-- level > 0 AND level not max (display "current -> next")
|
|
elseif tal_t.level ~= tal_t.max then
|
|
|
|
if in_ar(pos_str_ar, name) or in_ar(pos_str_exc_ar, talent) then -- +%
|
|
vals_ar[#vals_ar + 1] = cur_val .. "% -> +" .. next_val
|
|
elseif in_ar(neg_str_ar, name) then -- -%
|
|
vals_ar[#vals_ar + 1] = cur_val .. "% -> -" .. next_val
|
|
elseif name == "stamina" then -- +%per sec
|
|
vals_ar[#vals_ar + 1] = cur_val .. "%/" .. gt("stat_stamina_per_sec") .. " -> +" .. next_val
|
|
elseif in_ar(none_str_ar, name) then -- nothing%
|
|
vals_ar[#vals_ar + 1] = cur_val .. "% -> " .. next_val
|
|
end
|
|
|
|
-- level is max (display "current")
|
|
else
|
|
vals_ar[#vals_ar + 1] = cur_val
|
|
end
|
|
|
|
-- have no points AND level = 0 (display "next")
|
|
elseif tal_t.level <= 0 then
|
|
vals_ar[#vals_ar + 1] = next_val
|
|
|
|
-- have no points AND level > 0 (display "current")
|
|
else
|
|
vals_ar[#vals_ar + 1] = cur_val
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- add collector stacks
|
|
if talent == "collector" then
|
|
vals_ar[#vals_ar + 1] = collector_var
|
|
end
|
|
|
|
-- add beast descr
|
|
if talent == "beast_anatomy" then
|
|
vals_ar[#vals_ar + 1] = get_mutant_name("beast")
|
|
end
|
|
|
|
-- add humanoid descr
|
|
if talent == "humanoid_anatomy" then
|
|
vals_ar[#vals_ar + 1] = get_mutant_name("hum")
|
|
end
|
|
|
|
-- add trophies descr
|
|
if talent == "trophies" then
|
|
vals_ar[#vals_ar + 1] = size_table(monster_hunter_trophies) * (tal_t.damage[tal_t.level] or tal_t.damage[tal_t.level + 1])
|
|
vals_ar[#vals_ar + 1] = get_mutant_name("trophies")
|
|
end
|
|
|
|
-- add head hunter descr
|
|
if talent == "head_hunter" then
|
|
vals_ar[#vals_ar + 1] = size_table(merc_head_hunter_t) * (tal_t.damage[tal_t.level] or tal_t.damage[tal_t.level + 1])
|
|
vals_ar[#vals_ar + 1] = get_left_communities()
|
|
end
|
|
|
|
-- make and return string
|
|
return strformat(gt(talent .. "_descr"), unpack(vals_ar))
|
|
end
|
|
|
|
function get_tooltip_height_mult(typ, class_name, talent)
|
|
local tal_t = talents_table[typ][class_name][talent]
|
|
return tal_t.descr_size and descr_sizes_t[tal_t.descr_size] or descr_sizes_t[1]
|
|
end
|
|
|
|
-- header for weapon/outfit types
|
|
local kinds_to_display = {
|
|
"w_sniper", "w_rifle", "w_smg", "w_shotgun", "w_pistol", "w_machine_gun",
|
|
"o_light", "o_medium", "o_sci", "o_heavy",
|
|
}
|
|
|
|
BuildDescHeaderBase = ui_item.build_desc_header
|
|
function ui_item.build_desc_header(obj, sec, str)
|
|
str = str or gc(ini_sys:r_string_ex(sec, "description"))
|
|
if (not str) then return "" end
|
|
|
|
local kind = ini_sys:r_string_ex(sec, "kind")
|
|
local purp_clr = utils_xml.get_color("d_purple")
|
|
local grey_clr = utils_xml.get_color("ui_gray_1")
|
|
|
|
if not kind then
|
|
return BuildDescHeaderBase(obj, sec, str)
|
|
end
|
|
|
|
if IsWeapon(obj) or IsOutfit(obj) then
|
|
if is_mg_type(sec) then
|
|
kind = "w_machine_gun"
|
|
end
|
|
if in_ar(kinds_to_display, kind) then
|
|
return " " .. purp_clr .. gt("talents_header_krug") .. " " .. grey_clr .. gt("talents_header_" .. kind) .. "\\n \\n" .. BuildDescHeaderBase(obj, sec, str)
|
|
end
|
|
end
|
|
|
|
return BuildDescHeaderBase(obj, sec, str)
|
|
end
|
|
|
|
function pr(...)
|
|
if talents_dbg then
|
|
printf(...)
|
|
end
|
|
end
|
|
-------------------------------------
|
|
|
|
function actor_on_update()
|
|
duration_update()
|
|
cooldown_update()
|
|
update_leap()
|
|
jugger_regeneration_update()
|
|
deep_wound_update()
|
|
modified_bullets_update()
|
|
reposition_update()
|
|
spot_weakness_update()
|
|
acrobatic_elusive_update()
|
|
endurance_stamina_upd()
|
|
endurance_speed_upd()
|
|
endurance_weight_upd()
|
|
cynical_coin_update()
|
|
end
|
|
|
|
function ActorMenu_on_trade_started()
|
|
RegisterScriptCallback("actor_on_item_take", actor_on_item_buy)
|
|
RegisterScriptCallback("actor_on_item_drop", actor_on_item_sell)
|
|
end
|
|
|
|
function ActorMenu_on_trade_closed()
|
|
UnregisterScriptCallback("actor_on_item_take", actor_on_item_buy)
|
|
UnregisterScriptCallback("actor_on_item_drop", actor_on_item_sell)
|
|
|
|
-- reset cynical id
|
|
if cynical_item_id then
|
|
cynical_item_id = nil
|
|
end
|
|
end
|
|
|
|
function save_state(m_data)
|
|
m_data.picked_class = picked_class
|
|
|
|
for typ, t in pairs(talents_table) do
|
|
for class_name, t2 in pairs(t) do
|
|
for talent, tal_t in pairs(t2) do
|
|
m_data.talents_t = m_data.talents_t or {}
|
|
m_data.talents_t[talent] = tal_t.level
|
|
end
|
|
end
|
|
end
|
|
|
|
m_data.collector_var = collector_var
|
|
m_data.monster_hunter_trophies = monster_hunter_trophies
|
|
m_data.special_diet_var = durations_t.special_diet_var
|
|
m_data.overdrive_var = durations_t.overdrive_var
|
|
m_data.chem_reliant_var = durations_t.chem_reliant_var
|
|
m_data.kindred_cd = kindred_cd
|
|
m_data.kindred_var = kindred_var
|
|
m_data.kindred_prev_lvl = kindred_prev_lvl
|
|
m_data.second_wind_cd = second_wind_cd
|
|
m_data.artistic_cd = artistic_cd
|
|
m_data.cynical_cd = cynical_cd
|
|
m_data.merc_head_hunter_t = merc_head_hunter_t
|
|
end
|
|
|
|
function load_state(m_data)
|
|
picked_class = m_data.picked_class or "null"
|
|
|
|
for typ, t in pairs(talents_table) do
|
|
for class_name, t2 in pairs(t) do
|
|
for talent, tal_t in pairs(t2) do
|
|
talents_table[typ][class_name][talent].level = m_data.talents_t and m_data.talents_t[talent] or 0
|
|
end
|
|
end
|
|
end
|
|
|
|
collector_var = m_data.collector_var or 0
|
|
monster_hunter_trophies = m_data.monster_hunter_trophies or {}
|
|
durations_t.special_diet_var = m_data.special_diet_var or 0
|
|
durations_t.overdrive_var = m_data.overdrive_var or 0
|
|
durations_t.chem_reliant_var = m_data.chem_reliant_var or 0
|
|
kindred_cd = m_data.kindred_cd or nil
|
|
kindred_var = m_data.kindred_var or nil
|
|
kindred_prev_lvl = m_data.kindred_prev_lvl or nil
|
|
second_wind_cd = m_data.second_wind_cd or nil
|
|
artistic_cd = m_data.artistic_cd or nil
|
|
cynical_cd = m_data.cynical_cd or nil
|
|
merc_head_hunter_t = m_data.merc_head_hunter_t or {}
|
|
end
|
|
|
|
function respec_talents()
|
|
picked_class = "null"
|
|
collector_var = 0
|
|
empty_table(monster_hunter_trophies)
|
|
kindred_cd = nil
|
|
second_wind_cd = nil
|
|
artistic_cd = nil
|
|
cynical_cd = nil
|
|
empty_table(merc_head_hunter_t)
|
|
|
|
for k, v in pairs(cooldowns_t) do
|
|
cooldowns_t[k] = 0
|
|
end
|
|
|
|
for k, v in pairs(durations_t) do
|
|
durations_t[k] = 0
|
|
end
|
|
|
|
remove_all_items_weight()
|
|
|
|
for typ, t in pairs(talents_table) do
|
|
for class_name, t2 in pairs(t) do
|
|
for talent, talent_t in pairs(t2) do
|
|
speed.remove_speed(talent)
|
|
weight.remove_weight(talent)
|
|
|
|
if talent_t.level > 0 then
|
|
talents_table[typ][class_name][talent].level = 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for class_name, t in pairs(class_special_stats) do
|
|
speed.remove_speed(class_name .. "_special")
|
|
weight.remove_weight(class_name .. "_special")
|
|
end
|
|
|
|
local coin = db.actor:object("cynical_coin")
|
|
if coin then
|
|
alife_release(coin)
|
|
end
|
|
|
|
local kindred_dogs_squad = get_story_se_object("kindred_dogs")
|
|
if kindred_dogs_squad then
|
|
alife_release(kindred_dogs_squad)
|
|
end
|
|
|
|
end
|
|
|
|
function on_option_change()
|
|
talents_key = talents_mcm.get_config("talents_key")
|
|
talents_dbg = talents_mcm.get_config("talents_dbg")
|
|
end
|
|
|
|
function on_game_start()
|
|
RegisterScriptCallback("save_state", save_state)
|
|
RegisterScriptCallback("load_state", load_state)
|
|
RegisterScriptCallback("npc_on_before_hit", actor_hit_npc)
|
|
RegisterScriptCallback("monster_on_before_hit", actor_hit_npc)
|
|
RegisterScriptCallback("npc_on_before_hit", npc_on_before_hit)
|
|
RegisterScriptCallback("monster_on_before_hit", npc_on_before_hit)
|
|
RegisterScriptCallback("actor_on_before_hit", actor_on_before_hit)
|
|
RegisterScriptCallback("npc_on_death_callback", npc_on_death_callback)
|
|
RegisterScriptCallback("monster_on_death_callback", npc_on_death_callback)
|
|
RegisterScriptCallback("actor_on_item_use", actor_on_item_use)
|
|
RegisterScriptCallback("actor_item_to_ruck", upgrades_to_ruck)
|
|
RegisterScriptCallback("actor_item_to_slot", weight_to_slot)
|
|
RegisterScriptCallback("actor_item_to_ruck", weight_to_ruck)
|
|
RegisterScriptCallback("actor_item_to_belt", weight_to_belt)
|
|
RegisterScriptCallback("actor_on_item_drop", weight_on_drop)
|
|
RegisterScriptCallback("actor_on_first_update", weight_first_update)
|
|
RegisterScriptCallback("server_entity_on_unregister", server_entity_on_unregister)
|
|
RegisterScriptCallback("on_key_press", on_key_leap)
|
|
RegisterScriptCallback("actor_on_hud_animation_play", actor_on_hud_animation_play)
|
|
RegisterScriptCallback("actor_on_weapon_fired", trooper_precision_on_fire)
|
|
RegisterScriptCallback("actor_on_weapon_fired", merc_flurry_on_fire)
|
|
RegisterScriptCallback("actor_on_sleep", on_sleep_kindred)
|
|
RegisterScriptCallback("on_enemy_eval", on_enemy_eval_kindred_our_dogs)
|
|
RegisterScriptCallback("on_enemy_eval", on_enemy_eval_kindred_other_dogs)
|
|
RegisterScriptCallback("monster_on_before_hit", monster_before_hit_kindred)
|
|
RegisterScriptCallback("actor_on_first_update", first_update_kindred)
|
|
RegisterScriptCallback("monster_on_update", monster_update_tracker)
|
|
RegisterScriptCallback("on_get_item_cost", on_get_item_cost)
|
|
RegisterScriptCallback("ActorMenu_on_before_init_mode", ActorMenu_on_before_init_mode)
|
|
RegisterScriptCallback("actor_on_update", actor_on_update)
|
|
RegisterScriptCallback("ActorMenu_on_mode_changed", open_trade)
|
|
RegisterScriptCallback("ActorMenu_on_mode_changed", close_trade)
|
|
RegisterScriptCallback("ActorMenu_on_trade_started", ActorMenu_on_trade_started)
|
|
RegisterScriptCallback("ActorMenu_on_trade_closed", ActorMenu_on_trade_closed)
|
|
RegisterScriptCallback("on_option_change", on_option_change)
|
|
end |