1863 lines
61 KiB
Plaintext
1863 lines
61 KiB
Plaintext
|
--Author: Arszi
|
||
|
--Dynamic and Weather-based radiation module 2.1
|
||
|
|
||
|
|
||
|
-- Vastly edited by demonized
|
||
|
-- Last modified 2023 somewhere
|
||
|
|
||
|
--***SETTINGS**************************************************************************************************************************************
|
||
|
-- MCM
|
||
|
function load_defaults()
|
||
|
local t = {}
|
||
|
local op = arszi_radiation_mcm.op
|
||
|
for i, v in ipairs(op.gr) do
|
||
|
if v.def ~= nil then
|
||
|
t[v.id] = v.def
|
||
|
end
|
||
|
end
|
||
|
return t
|
||
|
end
|
||
|
|
||
|
settings = load_defaults()
|
||
|
|
||
|
function load_settings()
|
||
|
settings = load_defaults()
|
||
|
if ui_mcm then
|
||
|
for k, v in pairs(settings) do
|
||
|
settings[k] = ui_mcm.get("arszi_radiation/" .. k)
|
||
|
end
|
||
|
end
|
||
|
buildRadTable()
|
||
|
return settings
|
||
|
end
|
||
|
|
||
|
local mcm_keybinds = ui_mcm and ui_mcm.key_hold
|
||
|
|
||
|
-- UTILS
|
||
|
local function throttle(func, tg_throttle)
|
||
|
local tg = 0
|
||
|
if not tg_throttle or tg_throttle == 0 then
|
||
|
return function(...)
|
||
|
local t = time_global()
|
||
|
if t ~= tg then
|
||
|
tg = t
|
||
|
return func(...)
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
return function(...)
|
||
|
local t = time_global()
|
||
|
if t < tg then return end
|
||
|
tg = t + tg_throttle
|
||
|
return func(...)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function array_keys(t, sorted, sort_func)
|
||
|
local res = {}
|
||
|
local res_count = 1
|
||
|
for k, v in pairs(t) do
|
||
|
res[res_count] = k
|
||
|
res_count = res_count + 1
|
||
|
end
|
||
|
if sorted then
|
||
|
if sort_func then
|
||
|
table.sort(res, sort_func)
|
||
|
else
|
||
|
table.sort(res)
|
||
|
end
|
||
|
end
|
||
|
return res
|
||
|
end
|
||
|
|
||
|
local function bisect_left(a, x, lo, hi)
|
||
|
local lo = lo or 1
|
||
|
local hi = hi or #a
|
||
|
|
||
|
if lo < 0 then
|
||
|
printf('bisect, lo must be non-negative')
|
||
|
return
|
||
|
end
|
||
|
|
||
|
while lo < hi do
|
||
|
local mid = math.floor((lo + hi) * 0.5)
|
||
|
if a[mid] < x then
|
||
|
lo = mid+1
|
||
|
else
|
||
|
hi = mid
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return lo
|
||
|
end
|
||
|
|
||
|
local function lookup(t, key, tkeys)
|
||
|
if is_empty(t) then return 0 end
|
||
|
|
||
|
if not tkeys then
|
||
|
tkeys = array_keys(t, true)
|
||
|
end
|
||
|
|
||
|
local tkeys_len = #tkeys
|
||
|
if key <= tkeys[1] then return t[tkeys[1]] end
|
||
|
if key >= tkeys[tkeys_len] then return t[tkeys[tkeys_len]] end
|
||
|
|
||
|
local where = bisect_left(tkeys, key)
|
||
|
local lo = tkeys[where-1] or tkeys[where]
|
||
|
local hi = tkeys[where]
|
||
|
if lo == hi then return t[lo] end
|
||
|
|
||
|
local delta = (key - lo) / (hi - lo)
|
||
|
local res = delta * t[hi] + (1 - delta) * t[lo]
|
||
|
|
||
|
--printf(res)
|
||
|
return res
|
||
|
end
|
||
|
|
||
|
local OVERRIDE_MAX_POWER_INCREASEMENT_BY_WIND = true --Overrides difficulty setting-based setting with settings.MAX_POWER_INCREASEMENT_BY_WIND
|
||
|
local ENABLE_WIND_POWER_NOTIFICATIONS = false --Enables notifications about wind power changes (medium and high power)
|
||
|
|
||
|
--Wind alert states
|
||
|
local WIND_ALERT_STATES = {
|
||
|
NoAlert = 0,
|
||
|
Medium = 1,
|
||
|
Strong = 2
|
||
|
}
|
||
|
|
||
|
--settings.MAX_POWER_INCREASEMENT_BY_WIND settings based on difficulty
|
||
|
local DIFFICULTY_WIND_POWER_SETTINGS = {
|
||
|
Easy = 1.42,
|
||
|
Medium = 1.52,
|
||
|
Hard = 1.62
|
||
|
}
|
||
|
|
||
|
local WIND_DIRECTIONS = {
|
||
|
N = "North",
|
||
|
NE = "NorthEast",
|
||
|
E = "East",
|
||
|
SE = "SouthEast",
|
||
|
S = "South",
|
||
|
SW = "SouthWest",
|
||
|
W = "West",
|
||
|
NW = "NorthWest",
|
||
|
No_wind = ""
|
||
|
}
|
||
|
|
||
|
--New radiation zones should not spawn on theese smarts.
|
||
|
--They are either faction bases, or the are too close to faction bases
|
||
|
local blacklisted_smarts = {
|
||
|
l01_escape = {
|
||
|
esc_smart_terrain_3_16 = true,
|
||
|
esc_smart_terrain_2_12 = true,
|
||
|
esc_smart_terrain_5_7 = true,
|
||
|
esc_smart_terrain_4_9 = true
|
||
|
},
|
||
|
y04_pole = {
|
||
|
},
|
||
|
k00_marsh = {
|
||
|
mar_smart_terrain_base = true,
|
||
|
mar_smart_terrain_5_8 = true,
|
||
|
mar_smart_terrain_doc_2 = true
|
||
|
},
|
||
|
k01_darkscape = {
|
||
|
ds2_domik_st = true
|
||
|
},
|
||
|
l02_garbage = {
|
||
|
gar_smart_terrain_3_5 = true,
|
||
|
gar_smart_terrain_6_3 = true
|
||
|
},
|
||
|
l04_darkvalley = {
|
||
|
val_smart_terrain_7_3 = true,
|
||
|
val_smart_terrain_7_4 = true,
|
||
|
val_smart_terrain_7_5 = true,
|
||
|
val_smart_terrain_8_6 = true,
|
||
|
val_smart_terrain_7_8 = true, --Smart Z position is too low
|
||
|
val_smart_terrain_8_9 = true --Smart Z position is too low
|
||
|
},
|
||
|
l03_agroprom = {
|
||
|
agr_smart_terrain_1_6 = true,
|
||
|
agr_smart_terrain_1_6_near_1 = true,
|
||
|
agr_smart_terrain_1_6_near_2 = true
|
||
|
},
|
||
|
l08_yantar = {
|
||
|
yan_smart_terrain_3_6 = true,
|
||
|
yan_smart_terrain_6_4 = true,
|
||
|
},
|
||
|
l06_rostok = {
|
||
|
},
|
||
|
l05_bar = {
|
||
|
bar_dolg_bunker = true,
|
||
|
bar_dolg_general = true,
|
||
|
bar_visitors = true,
|
||
|
bar_zastava = true,
|
||
|
bar_zastava_2 = true,
|
||
|
},
|
||
|
k02_trucks_cemetery = {
|
||
|
trc_sim_20 = true
|
||
|
},
|
||
|
l09_deadcity = {
|
||
|
cit_killers = true,
|
||
|
},
|
||
|
l07_military = {
|
||
|
mil_smart_terrain_7_10 = true,
|
||
|
mil_smart_terrain_7_8 = true,
|
||
|
mil_smart_terrain_7_7 = true,
|
||
|
mil_smart_terrain_7_12 = true
|
||
|
},
|
||
|
l10_red_forest = {
|
||
|
red_smart_terrain_3_2 = true
|
||
|
},
|
||
|
l10_radar = {
|
||
|
},
|
||
|
l10_limansk = {
|
||
|
},
|
||
|
jupiter = {
|
||
|
jup_a6 = true,
|
||
|
jup_b41 = true
|
||
|
},
|
||
|
l11_pripyat = {
|
||
|
mlr_terrain = true,
|
||
|
pri_monolith = true,
|
||
|
},
|
||
|
pripyat = {
|
||
|
pri_a18_smart_terrain = true,
|
||
|
kbo_terrain = true,
|
||
|
pri_b306 = true,
|
||
|
pri_a16 = true,
|
||
|
pri_a16_mlr_copy = true,
|
||
|
pri_a28_base = true,
|
||
|
pri_b36_smart_terrain = true
|
||
|
},
|
||
|
l11_hospital = {
|
||
|
},
|
||
|
zaton = {
|
||
|
zat_b40_smart_terrain = true,
|
||
|
zat_b18 = true,
|
||
|
zat_stalker_base_smart = true,
|
||
|
zat_sim_27 = true
|
||
|
},
|
||
|
l12_stancia = {
|
||
|
},
|
||
|
l12_stancia_2 = {
|
||
|
},
|
||
|
l13_generators = {
|
||
|
},
|
||
|
l12u_sarcofag = {
|
||
|
sar_monolith_general = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
--New radiation zones settings for levels
|
||
|
--Chance_to_spawn is the percentage
|
||
|
--Radius original (minimum) radius of the new radiation zone
|
||
|
local radiation_field_level_settings = {
|
||
|
l01_escape = {
|
||
|
Name = "l01_escape",
|
||
|
Radiation_field = "zone_radioactive_very_weak",
|
||
|
Chance_to_spawn = 25,
|
||
|
Radius = 10
|
||
|
},
|
||
|
y04_pole = {
|
||
|
Name = "y04_pole",
|
||
|
Radiation_field = "zone_radioactive_very_weak",
|
||
|
Chance_to_spawn = 25,
|
||
|
Radius = 10
|
||
|
},
|
||
|
k00_marsh = {
|
||
|
Name = "k00_marsh",
|
||
|
Radiation_field = "zone_radioactive_very_weak",
|
||
|
Chance_to_spawn = 25,
|
||
|
Radius = 10
|
||
|
},
|
||
|
k01_darkscape = {
|
||
|
Name = "k01_darkscape",
|
||
|
Radiation_field = "zone_radioactive_very_weak",
|
||
|
Chance_to_spawn = 25,
|
||
|
Radius = 10
|
||
|
},
|
||
|
l02_garbage = {
|
||
|
Name = "l02_garbage",
|
||
|
Radiation_field = "zone_radioactive_weak",
|
||
|
Chance_to_spawn = 30,
|
||
|
Radius = 12
|
||
|
},
|
||
|
l04_darkvalley = {
|
||
|
Name = "l04_darkvalley",
|
||
|
Radiation_field = "zone_radioactive_weak",
|
||
|
Chance_to_spawn = 30,
|
||
|
Radius = 12
|
||
|
},
|
||
|
l03_agroprom = {
|
||
|
Name = "l03_agroprom",
|
||
|
Radiation_field = "zone_radioactive_weak",
|
||
|
Chance_to_spawn = 30,
|
||
|
Radius = 12
|
||
|
},
|
||
|
l08_yantar = {
|
||
|
Name = "l08_yantar",
|
||
|
Radiation_field = "zone_radioactive_below_average",
|
||
|
Chance_to_spawn = 35,
|
||
|
Radius = 14
|
||
|
},
|
||
|
l06_rostok = {
|
||
|
Name = "l06_rostok",
|
||
|
Radiation_field = "zone_radioactive_below_average",
|
||
|
Chance_to_spawn = 35,
|
||
|
Radius = 14
|
||
|
},
|
||
|
l05_bar = {
|
||
|
Name = "l05_bar",
|
||
|
Radiation_field = "zone_radioactive_below_average",
|
||
|
Chance_to_spawn = 35,
|
||
|
Radius = 10
|
||
|
},
|
||
|
k02_trucks_cemetery = {
|
||
|
Name = "k02_trucks_cemetery",
|
||
|
Radiation_field = "zone_radioactive_below_average",
|
||
|
Chance_to_spawn = 35,
|
||
|
Radius = 14
|
||
|
},
|
||
|
l09_deadcity = {
|
||
|
Name = "l09_deadcity",
|
||
|
Radiation_field = "zone_radioactive_below_average",
|
||
|
Chance_to_spawn = 35,
|
||
|
Radius = 14
|
||
|
},
|
||
|
l07_military = {
|
||
|
Name = "l07_military",
|
||
|
Radiation_field = "zone_radioactive_below_average",
|
||
|
Chance_to_spawn = 35,
|
||
|
Radius = 14
|
||
|
},
|
||
|
l10_red_forest = {
|
||
|
Name = "l10_red_forest",
|
||
|
Radiation_field = "zone_radioactive_average",
|
||
|
Chance_to_spawn = 40,
|
||
|
Radius = 16
|
||
|
},
|
||
|
l10_radar = {
|
||
|
Name = "l10_radar",
|
||
|
Radiation_field = "zone_radioactive_average",
|
||
|
Chance_to_spawn = 40,
|
||
|
Radius = 16
|
||
|
},
|
||
|
l10_limansk = {
|
||
|
Name = "l10_limansk",
|
||
|
Radiation_field = "zone_radioactive_average",
|
||
|
Chance_to_spawn = 40,
|
||
|
Radius = 16
|
||
|
},
|
||
|
jupiter = {
|
||
|
Name = "jupiter",
|
||
|
Radiation_field = "zone_radioactive_above_average",
|
||
|
Chance_to_spawn = 45,
|
||
|
Radius = 18
|
||
|
},
|
||
|
l11_pripyat = {
|
||
|
Name = "l11_pripyat",
|
||
|
Radiation_field = "zone_radioactive_above_average",
|
||
|
Chance_to_spawn = 0,
|
||
|
Radius = 18
|
||
|
},
|
||
|
pripyat = {
|
||
|
Name = "pripyat",
|
||
|
Radiation_field = "zone_radioactive_above_average",
|
||
|
Chance_to_spawn = 45,
|
||
|
Radius = 18
|
||
|
},
|
||
|
l11_hospital = {
|
||
|
Name = "l11_hospital",
|
||
|
Radiation_field = "zone_radioactive_above_average",
|
||
|
Chance_to_spawn = 45,
|
||
|
Radius = 18
|
||
|
},
|
||
|
zaton = {
|
||
|
Name = "zaton",
|
||
|
Radiation_field = "zone_radioactive_above_average",
|
||
|
Chance_to_spawn = 45,
|
||
|
Radius = 18
|
||
|
},
|
||
|
l12_stancia = {
|
||
|
Name = "l12_stancia",
|
||
|
Radiation_field = "zone_radioactive_strong",
|
||
|
Chance_to_spawn = 50,
|
||
|
Radius = 20
|
||
|
},
|
||
|
l12_stancia_2 = {
|
||
|
Name = "l12_stancia_2",
|
||
|
Radiation_field = "zone_radioactive_strong",
|
||
|
Chance_to_spawn = 50,
|
||
|
Radius = 20
|
||
|
},
|
||
|
l13_generators = {
|
||
|
Name = "l13_generators",
|
||
|
Radiation_field = "zone_radioactive_strong",
|
||
|
Chance_to_spawn = 50,
|
||
|
Radius = 20
|
||
|
}
|
||
|
}
|
||
|
|
||
|
--Special radioactive field spawns for NPP interrior
|
||
|
local special_spawn_l12u_sarcofag = {
|
||
|
Name = "l12u_sarcofag",
|
||
|
Radiation_field = "zone_radioactive_lethal",
|
||
|
Chance_to_spawn = 100,
|
||
|
Radius = 20
|
||
|
}
|
||
|
|
||
|
--Some anomlies will affect new game start point, like scientist bunker in Yantar. They should be capped.
|
||
|
local blacklisted_static_radiation_anomalies = {
|
||
|
-- l01_escape
|
||
|
esc_zone_field_radioactive_weak_0000 = 80,
|
||
|
esc_zone_field_radioactive_weak_0011 = 75,
|
||
|
esc_zone_field_radioactive_weak_0012 = 75,
|
||
|
esc_zone_field_radioactive_weak_0013 = 75,
|
||
|
|
||
|
--k00_marsh
|
||
|
mar_zone_field_radioactive_weak_0007 = 80,
|
||
|
|
||
|
--pripyat
|
||
|
pripyat_zone_field_radioactive_weak_0001 = 5,
|
||
|
|
||
|
--l04_darkvalley
|
||
|
val_zone_field_radioactive_weak_baza_freedom_0016 = 3,
|
||
|
val_zone_field_radioactive_weak_baza_freedom_0018 = 3,
|
||
|
val_zone_field_radioactive_weak_baza_freedom_0019 = 3,
|
||
|
|
||
|
--l03_agroprom
|
||
|
agr_zone_field_radioactive_average_000 = 20,
|
||
|
|
||
|
--l07_military
|
||
|
mil_zone_field_radioactive_strong = 25,
|
||
|
mil_zone_field_radioactive_strong_3 = 50,
|
||
|
|
||
|
--zaton
|
||
|
zaton_zone_field_radioactive_average_0007 = 20,
|
||
|
zaton_zone_field_radioactive_average_0008 = 20,
|
||
|
zaton_zone_field_radioactive_average_0013 = 20,
|
||
|
|
||
|
--Name of level: l02_garbage
|
||
|
gar_zone_field_radioactive_weak_0001 = 50,
|
||
|
gar_zone_field_radioactive_weak_0002 = 40
|
||
|
}
|
||
|
|
||
|
--Underground maps should not be affected by dynamic radiations and wind behaviour
|
||
|
local underground_maps = {
|
||
|
l03u_agr_underground = true,
|
||
|
l08u_brainlab = true,
|
||
|
l10u_bunker = true,
|
||
|
jupiter_underground = true,
|
||
|
l04u_labx18 = true,
|
||
|
labx8 = true,
|
||
|
l12u_sarcofag = true,
|
||
|
l12u_control_monolith = true,
|
||
|
l13u_warlab = true
|
||
|
}
|
||
|
|
||
|
--Radiation zones the player is in
|
||
|
local radiation_zones = {}
|
||
|
|
||
|
--Table for state management
|
||
|
local radiation_table = {}
|
||
|
|
||
|
--Hack for a wonderful bug in the LUA interpreter itself possibly. IT IS CURSED!
|
||
|
environmental_radiation = 0
|
||
|
|
||
|
function get_adjusted_radiation()
|
||
|
return normalize(db.actor.radiation, settings.RADIATION_THRESHOLD, 1)
|
||
|
end
|
||
|
|
||
|
function get_adjusted_damage_radiation()
|
||
|
return normalize(db.actor.radiation, settings.RADIATION_DAMAGE_THRESHOLD, 1)
|
||
|
end
|
||
|
|
||
|
|
||
|
--***POWER**************************************************************************************************************************************
|
||
|
function manage_power()
|
||
|
if (not db.actor) then return end
|
||
|
-- if (db.actor.radiation <= settings.RADIATION_THRESHOLD) then return end
|
||
|
if (not radiation_table.last_power) then radiation_table.last_power = db.actor.power end
|
||
|
|
||
|
local last_power = radiation_table.last_power
|
||
|
local difference = db.actor.power - last_power
|
||
|
if (difference > 0) then
|
||
|
db.actor.power = db.actor.power - get_power_difference(difference)
|
||
|
|
||
|
--Cap maximum power
|
||
|
if (db.actor.power > (get_power_total())) then
|
||
|
db.actor.power = get_power_total()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
radiation_table.last_power = db.actor.power
|
||
|
--show_message("power "..db.actor.power)
|
||
|
end
|
||
|
|
||
|
function get_power_difference(difference)
|
||
|
local min = difference / settings.ARSZI_POWER_REGENERATION_REDUCTION_RATIO
|
||
|
local reduce_by_min = difference - min
|
||
|
return reduce_by_min * (get_adjusted_radiation() ^ settings.powerReduceCurve)
|
||
|
end
|
||
|
|
||
|
function get_power_total()
|
||
|
local min = 1 / settings.ARSZI_POWER_CAP_REDUCTION_RATIO
|
||
|
local reduce_by_min = 1 - min
|
||
|
local reduce_by_total = reduce_by_min * (get_adjusted_radiation() ^ settings.powerReduceCurve)
|
||
|
return 1 - reduce_by_total
|
||
|
end
|
||
|
|
||
|
--***SATIETY***********************************************************************************************************************************
|
||
|
function manage_satiety()
|
||
|
if (not db.actor) then return end
|
||
|
|
||
|
local conditions = db.actor:cast_Actor():conditions()
|
||
|
local satiety = conditions and conditions:GetSatiety()
|
||
|
|
||
|
if (not radiation_table.last_satiety) then
|
||
|
radiation_table.last_satiety = satiety
|
||
|
end
|
||
|
|
||
|
local last_satiety = radiation_table.last_satiety
|
||
|
|
||
|
if (satiety > last_satiety) then
|
||
|
local difference = math.abs(satiety - last_satiety)
|
||
|
local new_satiety = satiety - get_satiety_difference(difference)
|
||
|
|
||
|
db.actor.satiety = new_satiety
|
||
|
radiation_table.last_satiety = new_satiety
|
||
|
else
|
||
|
radiation_table.last_satiety = satiety
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- < 1 means more aggressive debuff, > 1 means more tame that will kick later
|
||
|
function get_satiety_difference(difference)
|
||
|
local min = difference / settings.ARSZI_SATIETY_REDUCTION_RATIO
|
||
|
local reduce_by_min = difference - min
|
||
|
return reduce_by_min * (get_adjusted_radiation() ^ settings.satietyReduceCurve)
|
||
|
end
|
||
|
|
||
|
manage_health = throttle(function()
|
||
|
if not db.actor then return end
|
||
|
|
||
|
db.actor:change_health(-get_adjusted_damage_radiation() * settings.RADIATION_MAX_DAMAGE * 0.1)
|
||
|
end, 100)
|
||
|
|
||
|
--***SPAWN RADIATION FIELDS**************************************************************************************************************************************
|
||
|
--Spawns dynamic radiation zones at non-blacklisted smart randomly
|
||
|
function spawn_radiation_fields_at_new_game()
|
||
|
trace_this("spawn_radiation_fields_at_new_game()")
|
||
|
|
||
|
for k, v in pairs (radiation_field_level_settings) do
|
||
|
spawn_radiation_fields_for_level(v)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Creates new radiation fields for the current level
|
||
|
function spawn_radiation_fields_for_level(current_level_settings)
|
||
|
trace_this("spawn_radiation_fields_for_level: "..current_level_settings.Name.." ************************************************")
|
||
|
|
||
|
trace_this("get_smart_terrains_of_level: "..current_level_settings.Name)
|
||
|
local smarts_of_level = get_smart_terrains_of_level(current_level_settings.Name)
|
||
|
|
||
|
trace_this("get blacklisted smarts")
|
||
|
local blacklisted_smarts_for_level = blacklisted_smarts[current_level_settings.Name]
|
||
|
|
||
|
trace_this("Iterate through smarts")
|
||
|
for ksmart, smart in pairs (smarts_of_level) do
|
||
|
|
||
|
local name_of_smart = smart:name()
|
||
|
if (not blacklisted_smarts_for_level[name_of_smart]) then
|
||
|
|
||
|
if (math.random(1, 100) <= current_level_settings.Chance_to_spawn) then
|
||
|
trace_this("Spawn default radiation zone for smart: "..name_of_smart)
|
||
|
|
||
|
create_radiation_field(
|
||
|
current_level_settings.Radiation_field,
|
||
|
smart.position,
|
||
|
smart.m_game_vertex_id,
|
||
|
level.vertex_id(smart.position),
|
||
|
current_level_settings.Radius)
|
||
|
end
|
||
|
|
||
|
else
|
||
|
trace_this("Smart: "..name_of_smart.." is black listed, skip")
|
||
|
end
|
||
|
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Creates a new radiation field
|
||
|
function create_radiation_field(anomaly_type, position, game_vertex_id, level_vertex_id, radius)
|
||
|
trace_this("create_radiation_field: "..anomaly_type.." Radius: "..radius.. " *****************")
|
||
|
|
||
|
trace_this("spawn anomaly")
|
||
|
local se_obj = alife( ):create(anomaly_type, position, level_vertex_id, game_vertex_id)
|
||
|
|
||
|
if (not se_obj) then
|
||
|
trace_this("NO ANOMALY WERE SPAWNED")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local data = utils_stpk.get_anom_zone_data( se_obj )
|
||
|
if (not data) then
|
||
|
trace_this( "Error: Unable to set dynamic anomaly properties" )
|
||
|
return
|
||
|
end
|
||
|
|
||
|
data.shapes[1] = {}
|
||
|
data.shapes[1].shtype = 0
|
||
|
data.shapes[1].offset = vector( ):set( 0, 0, 0 ) -- Leave for compatibility with CoC 1.4.22, delete later
|
||
|
data.shapes[1].center = vector( ):set( 0, 0, 0 )
|
||
|
data.shapes[1].radius = radius
|
||
|
utils_stpk.set_anom_zone_data(data, se_obj)
|
||
|
|
||
|
trace_this(se_obj.id)
|
||
|
trace_this("Radiation field was spawned succesfully")
|
||
|
end
|
||
|
|
||
|
--Get smart terrains if the level
|
||
|
function get_smart_terrains_of_level(level_name)
|
||
|
trace_this("get_smart_terrains_of_level("..level_name..")")
|
||
|
smarts_of_level = {}
|
||
|
|
||
|
for id,smart in pairs(db.smart_terrain_by_id) do
|
||
|
cvertex = smart and game_graph():vertex(smart.m_game_vertex_id)
|
||
|
|
||
|
if (cvertex and alife():level_name(cvertex:level_id()) == level_name) then
|
||
|
table.insert(smarts_of_level, smart)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return smarts_of_level
|
||
|
end
|
||
|
|
||
|
--Resizes every radiation field in the game, except ones from blacklisted maps
|
||
|
function resize_radiation_fields()
|
||
|
trace_this("resize_radiation_fields()")
|
||
|
|
||
|
--Check every objects
|
||
|
for i=1,65534 do
|
||
|
local se_obj = alife():object(i)
|
||
|
|
||
|
if (se_obj) then
|
||
|
|
||
|
if (se_obj["name"]) then
|
||
|
local name = se_obj:name()
|
||
|
if (string.match(name, "zone_field_radioactive") or string.match(name, "zone_radioactive")) then
|
||
|
trace_this("**************************************************")
|
||
|
trace_this(name.." ID: "..i)
|
||
|
|
||
|
local cvertex = game_graph():vertex(se_obj.m_game_vertex_id)
|
||
|
local name_of_level = alife():level_name(cvertex:level_id())
|
||
|
trace_this("Name of level: "..name_of_level)
|
||
|
|
||
|
if (not underground_maps[name_of_level]) then
|
||
|
|
||
|
local data = utils_stpk.get_anom_zone_data(se_obj)
|
||
|
if (data) then
|
||
|
if (data.shapes[1]["radius"]) then
|
||
|
|
||
|
local radius = data.shapes[1].radius
|
||
|
local position = se_obj.position
|
||
|
local game_vertex_id = se_obj.m_game_vertex_id
|
||
|
local level_vertex_id = level.vertex_id(se_obj.position)
|
||
|
|
||
|
local new_radius = get_new_radius_of_static_anomaly_field(name, radius)
|
||
|
trace_this("Original radius: "..radius.." New radius: "..new_radius)
|
||
|
|
||
|
data.shapes[1] = {}
|
||
|
data.shapes[1].shtype = 0
|
||
|
data.shapes[1].offset = vector():set( 0, 0, 0 ) -- Leave for compatibility with CoC 1.4.22, delete later
|
||
|
data.shapes[1].center = vector():set( 0, 0, 0 )
|
||
|
data.shapes[1].radius = new_radius
|
||
|
utils_stpk.set_anom_zone_data( data, se_obj )
|
||
|
|
||
|
alife():set_switch_offline(se_obj.id,false)
|
||
|
alife():set_switch_online(se_obj.id,true)
|
||
|
trace_this("Resizing completed")
|
||
|
else
|
||
|
trace_this("NO RADIUS FOR: "..i)
|
||
|
end
|
||
|
else
|
||
|
trace_this("NO DATA FOR: "..i)
|
||
|
end
|
||
|
else
|
||
|
trace_this("Level is underground level, skip: "..name_of_level)
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
trace_this("No name for: "..i)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Return the new radiation field size, taking blacklist into account
|
||
|
function get_new_radius_of_static_anomaly_field(name_of_radiation_zone, radius)
|
||
|
|
||
|
local override = blacklisted_static_radiation_anomalies[name_of_radiation_zone]
|
||
|
if (override) then
|
||
|
trace_this("Radiation zone is blacklisted by position, return custom max radius: "..override)
|
||
|
return override
|
||
|
else
|
||
|
trace_this("Not a blacklisted radiation zone")
|
||
|
return get_maximal_radius_with_wind_direction(radius)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--***UTILITIES**************************************************************************************************************************************
|
||
|
--Power for radiation damage calculation
|
||
|
function calculate_zone_radiation_power(power, radiation_zone)
|
||
|
--check of level is an underground level
|
||
|
if (is_underground_map()) then
|
||
|
radiation_zones[radiation_zone:id()] = true
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
--Check if the source of radiation damage is water
|
||
|
if (is_player_in_water() and radiation_zone and (radiation_zone:id() == db.actor:id())) then
|
||
|
--Ignore mask protection
|
||
|
local headgear_object = db.actor:item_in_slot(12)
|
||
|
local outfit_object = db.actor:item_in_slot(7)
|
||
|
local headgear_radiation_protection = (headgear_object and get_radiation_protection(headgear_object)) or 0
|
||
|
local outfit_radiation_protection = (outfit_object and get_radiation_protection(outfit_object)) or 0
|
||
|
local helmet_avaliable = is_helmet_integrated()
|
||
|
local total_radiation_protection = headgear_radiation_protection + outfit_radiation_protection
|
||
|
|
||
|
--irradiate material
|
||
|
if (outfit_object) then
|
||
|
local condition = outfit_object:condition()
|
||
|
|
||
|
if (condition > 0.05) then
|
||
|
local armour_radiation_protection_ratio = 0.06 - outfit_radiation_protection
|
||
|
if (armour_radiation_protection_ratio < 0) then armour_radiation_protection_ratio = 0.004 end
|
||
|
|
||
|
outfit_object:set_condition(condition - (power * armour_radiation_protection_ratio / 4))
|
||
|
db.actor.radiation = db.actor.radiation + (power * armour_radiation_protection_ratio)
|
||
|
end
|
||
|
|
||
|
--show_message("RADIATION PROT: "..outfit_radiation_protection.." CONDITION: "..condition.." POWER: "..power)
|
||
|
end
|
||
|
|
||
|
--If helmet is avaiable, increase the radiation of water by (1 + outfit_with_helmet_radiation_protection_reduction_ratio). Else, increase it by the protection ratio of the headgear
|
||
|
if (outfit_object and helmet_avaliable) then
|
||
|
power = power + ((outfit_radiation_protection * settings.OUTFIT_WITH_HELMET_RADIATION_PROTECTION_REDUCTION_RATIO) / 10)
|
||
|
--show_message(tostring(s_hit.power.." "..s_hit.type))
|
||
|
--show_message("outfit with helmet radiation protection: "..outfit_radiation_protection.." reduced: "..(outfit_radiation_protection * 0.3))
|
||
|
else
|
||
|
power = power + (headgear_radiation_protection / 10)
|
||
|
--show_message(tostring(s_hit.power.." "..s_hit.type))
|
||
|
--show_message("Headgear rad protection: "..tostring(headgear_radiation_protection).." Outfit rad protection: "..outfit_radiation_protection.." total: "..total_radiation_protection)
|
||
|
end
|
||
|
|
||
|
radiation_zones[radiation_zone:id()] = true
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
--Disable radiation damage, if the player is not in the current anomaly zone radius
|
||
|
if (not is_actor_in_radiation_zone(radiation_zone:id())) then
|
||
|
radiation_zones[radiation_zone:id()] = nil
|
||
|
return 0
|
||
|
end
|
||
|
|
||
|
--Give 20% radiation resistance boost for suits with integrated helmet
|
||
|
if (settings.ARSZI_INTEGRATED_SUIT_BONUS_ENABLED) then
|
||
|
if (is_helmet_integrated() and db.actor:item_in_slot(7)) then
|
||
|
local original_hit_power = power
|
||
|
power = power * 0.80
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local wind_velocity = get_wind_velocity()
|
||
|
local current_weather = get_current_weather()
|
||
|
|
||
|
--In rain and storm radiation zones won't expand in radius and be weaker
|
||
|
if (current_weather == "rain" or current_weather == "storm") then
|
||
|
--show_message("Reduced by rain. Original: "..s_hit.power.." Reduced: "..(s_hit.power * settings.POWER_REDUCTION_BY_RAIN))
|
||
|
radiation_zones[radiation_zone:id()] = true
|
||
|
return power * settings.POWER_REDUCTION_BY_RAIN
|
||
|
end
|
||
|
|
||
|
--In foggy weather radiation zones won't expand in radius
|
||
|
if (current_weather == "foggy") then
|
||
|
radiation_zones[radiation_zone:id()] = true
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
local power_increasement_factor = (wind_velocity / settings.MAX_WIND_VELOCITY) * (get_max_power_increasement_by_wind() - 1)
|
||
|
--show_message("Wind velocity: "..wind_velocity.." Power incerase factor: "..power_increasement_factor.." Original: "..s_hit.power.." Increased: "..s_hit.power * (1 + power_increasement_factor))
|
||
|
radiation_zones[radiation_zone:id()] = true
|
||
|
return power * (1 + power_increasement_factor)
|
||
|
end
|
||
|
|
||
|
--Power dosimeter-related power calcuation. HUD and ticking.
|
||
|
function get_environmental_radiation_power()
|
||
|
|
||
|
--WATER
|
||
|
if (is_player_in_water()) then
|
||
|
local power = 0
|
||
|
|
||
|
if (is_underground_map()) then
|
||
|
power = math.random(29, 35) / 1000
|
||
|
else
|
||
|
power = math.random(15, 17) / 1000
|
||
|
end
|
||
|
|
||
|
environmental_radiation = power
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
--Check if player is inside a zone
|
||
|
local count = 0
|
||
|
for k, _ in pairs(radiation_zones) do
|
||
|
count = count + 1
|
||
|
end
|
||
|
|
||
|
if (count == 0) then
|
||
|
environmental_radiation = 0
|
||
|
return 0
|
||
|
end
|
||
|
|
||
|
local power = level.get_env_rads()
|
||
|
local min = math.floor(power * 1000 * 0.9)
|
||
|
local max = math.floor(power * 1000 * 1.1)
|
||
|
power = math.random(min, max) / 1000
|
||
|
|
||
|
--UNDERGROUND
|
||
|
if (is_underground_map()) then
|
||
|
environmental_radiation = power
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
local wind_velocity = get_wind_velocity()
|
||
|
local current_weather = get_current_weather()
|
||
|
|
||
|
--RAIN/STORM
|
||
|
if (current_weather == "rain" or current_weather == "storm") then
|
||
|
power = power * settings.POWER_REDUCTION_BY_RAIN
|
||
|
environmental_radiation = power
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
--FOG
|
||
|
if (current_weather == "foggy") then
|
||
|
environmental_radiation = power
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
--NORMAL
|
||
|
local power_increasement_factor = (wind_velocity / settings.MAX_WIND_VELOCITY) * (get_max_power_increasement_by_wind() - 1)
|
||
|
power = power * (1 + power_increasement_factor)
|
||
|
|
||
|
--Hax to make Dosimeter UI work
|
||
|
environmental_radiation = power
|
||
|
|
||
|
return power
|
||
|
end
|
||
|
|
||
|
--Gets maximal power increasement by wind based on game difficulty setting or override
|
||
|
function get_max_power_increasement_by_wind()
|
||
|
--Check if overridden
|
||
|
if (OVERRIDE_MAX_POWER_INCREASEMENT_BY_WIND) then
|
||
|
--show_message("WIND POWER SETTING OVERRIDE: "..settings.MAX_POWER_INCREASEMENT_BY_WIND)
|
||
|
return settings.MAX_POWER_INCREASEMENT_BY_WIND
|
||
|
end
|
||
|
|
||
|
--Return setting based on game-difficulty
|
||
|
local gameplay = alife_storage_manager.get_state().diff_game
|
||
|
local game_num = gameplay and gameplay["type"] or 2
|
||
|
|
||
|
local wind_power_setting = 1.6
|
||
|
if (game_num == 1) then wind_power_setting = DIFFICULTY_WIND_POWER_SETTINGS.Easy end
|
||
|
if (game_num == 2) then wind_power_setting = DIFFICULTY_WIND_POWER_SETTINGS.Medium end
|
||
|
if (game_num == 3) then wind_power_setting = DIFFICULTY_WIND_POWER_SETTINGS.Hard end
|
||
|
|
||
|
--show_message("WIND POWER SETTING: "..wind_power_setting)
|
||
|
return wind_power_setting
|
||
|
end
|
||
|
|
||
|
--Gets if a map is an underground map
|
||
|
function is_underground_map()
|
||
|
local name_of_level = level.name()
|
||
|
if (not name_of_level) then return true end
|
||
|
if (underground_maps[name_of_level]) then
|
||
|
return true
|
||
|
else
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function get_min_radius_multiplier()
|
||
|
local min_radius = 10
|
||
|
local max_radius = min_radius * settings.MAX_SIZE_INCREASEMENT_BY_WIND
|
||
|
local max_width = max_radius * 2
|
||
|
local max_radius_with_wind = max_width - min_radius
|
||
|
return (min_radius / max_radius_with_wind)
|
||
|
end
|
||
|
|
||
|
function get_current_radius_of_radioactive_zone(radius_max)
|
||
|
local min_radius = radius_max / settings.MAX_SIZE_INCREASEMENT_BY_WIND
|
||
|
local percentage_ratio = get_wind_velocity() / settings.MAX_WIND_VELOCITY
|
||
|
return ((radius_max - min_radius) * percentage_ratio) + min_radius
|
||
|
end
|
||
|
|
||
|
function get_original_radius_of_radiation_zone(radius_max)
|
||
|
return radius_max * get_min_radius_multiplier()
|
||
|
end
|
||
|
|
||
|
--Maximum radius without buffer
|
||
|
function get_max_radius_without_wind(radius_max_with_wind)
|
||
|
local radius_min = radius_max_with_wind * get_min_radius_multiplier()
|
||
|
local radius_max = radius_min * settings.MAX_SIZE_INCREASEMENT_BY_WIND
|
||
|
return radius_max
|
||
|
end
|
||
|
|
||
|
--Maximum radius with buffer
|
||
|
function get_maximal_radius_with_wind_direction(min_radius)
|
||
|
local max_radius = min_radius * settings.MAX_SIZE_INCREASEMENT_BY_WIND
|
||
|
local max_width = max_radius * 2
|
||
|
return max_width - min_radius
|
||
|
end
|
||
|
|
||
|
function get_current_center_of_radioactive_zone(radius_max_without_wind, initial_position, direction_of_wind)
|
||
|
local min_radius = radius_max_without_wind / settings.MAX_SIZE_INCREASEMENT_BY_WIND
|
||
|
local current_radius = get_current_radius_of_radioactive_zone(radius_max_without_wind)
|
||
|
local offset = current_radius - min_radius
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.N) then
|
||
|
return vector():set(initial_position.x, initial_position.y, initial_position.z - offset)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.NE) then
|
||
|
return vector():set(initial_position.x - offset, initial_position.y, initial_position.z - offset)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.NW) then
|
||
|
return vector():set(initial_position.x + offset, initial_position.y, initial_position.z - offset)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.S) then
|
||
|
return vector():set(initial_position.x, initial_position.y, initial_position.z + offset)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.SE) then
|
||
|
return vector():set(initial_position.x - offset, initial_position.y, initial_position.z + offset)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.SW) then
|
||
|
return vector():set(initial_position.x + offset, initial_position.y, initial_position.z + offset)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.E) then
|
||
|
return vector():set(initial_position.x - offset, initial_position.y, initial_position.z)
|
||
|
end
|
||
|
|
||
|
if (direction_of_wind == WIND_DIRECTIONS.W) then
|
||
|
return vector():set(initial_position.x + offset, initial_position.y, initial_position.z)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function is_actor_in_radiation_zone(id_of_radiation_zone)
|
||
|
local se_obj = alife():object(id_of_radiation_zone)
|
||
|
local name_of_radiation_zone = se_obj:name()
|
||
|
if (not se_obj) then return false end
|
||
|
|
||
|
local data = utils_stpk.get_anom_zone_data(se_obj)
|
||
|
local maximum_radius = data and data.shapes[1].radius
|
||
|
if (not maximum_radius) then return true end
|
||
|
|
||
|
local maximum_radius_without_wind = get_max_radius_without_wind(maximum_radius)
|
||
|
local original_radius = get_original_radius_of_radiation_zone(maximum_radius) --Just for debug purposes
|
||
|
local original_position = se_obj.position
|
||
|
local actor_distance_from_original_position = get_distance(db.actor:position(), original_position) --Just for debug purposes
|
||
|
local current_radius = get_current_radius_of_radioactive_zone(maximum_radius_without_wind)
|
||
|
|
||
|
local wind_direction = radiation_table.last_wind_direction
|
||
|
local current_center_with_wind = get_current_center_of_radioactive_zone(maximum_radius_without_wind, original_position, wind_direction)
|
||
|
local actor_distance_from_current_position = get_distance(db.actor:position(), current_center_with_wind)
|
||
|
|
||
|
local current_weather = get_current_weather()
|
||
|
if (current_weather == "rain" or current_weather == "storm" or current_weather == "foggy") then
|
||
|
current_radius = original_radius
|
||
|
actor_distance_from_current_position = actor_distance_from_original_position
|
||
|
end
|
||
|
|
||
|
--Uncomment for debugging purposes
|
||
|
--show_message("NAME: "..name_of_radiation_zone.." MAX_R_WIND: "..maximum_radius.." MAX_R :"..maximum_radius_without_wind.." ORIGINAL_R: "..original_radius.." CURRENT_R: "..current_radius.." DISTANCE_O: "..actor_distance_from_original_position.." Distance_C: "..actor_distance_from_current_position)
|
||
|
--show_message("NAME: "..name_of_radiation_zone.." CURRENT_R: "..current_radius.." Distance_C: "..actor_distance_from_current_position.." IN ZONE: "..tostring(actor_distance_from_current_position <= current_radius))
|
||
|
|
||
|
return actor_distance_from_current_position <= current_radius
|
||
|
end
|
||
|
|
||
|
function get_distance(position_1, position_2)
|
||
|
local x = math.abs(position_1.x - position_2.x)
|
||
|
local z = math.abs(position_1.z - position_2.z)
|
||
|
local distance = math.sqrt((x * x) + (z * z))
|
||
|
return distance
|
||
|
end
|
||
|
|
||
|
function is_player_in_water()
|
||
|
return load_var(db.actor,"grw_in_water") == true
|
||
|
end
|
||
|
|
||
|
function get_radiation_protection(object)
|
||
|
local section_object = object and object:section()
|
||
|
local id_object = object and object:id()
|
||
|
local total_radiation_protection = 0
|
||
|
local helmet_avaliable = false
|
||
|
|
||
|
local se_object = id_object and alife_object(id_object)
|
||
|
if se_object then
|
||
|
local data = utils_stpk.get_item_data(se_object)
|
||
|
|
||
|
for k,v in pairs(data) do
|
||
|
local radiation_protection = utils_data.read_from_ini(ini_sys, section_object, "radiation_protection", "float", 0)
|
||
|
total_radiation_protection = radiation_protection * object:condition()
|
||
|
helmet_avaliable = utils_data.read_from_ini(ini_sys, section_object, "helmet_avaliable", "bool", false)
|
||
|
break
|
||
|
end
|
||
|
|
||
|
for k,v in pairs(data.upgrades) do
|
||
|
local upgrade_sect = utils_data.read_from_ini(ini_sys, tostring(v), "section", "string", nil)
|
||
|
if upgrade_sect then
|
||
|
local upgrade_radiation_protection = utils_data.read_from_ini(ini_sys, upgrade_sect, "radiation_protection", "float", 0)
|
||
|
if (upgrade_radiation_protection) then
|
||
|
total_radiation_protection = total_radiation_protection + upgrade_radiation_protection
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return total_radiation_protection
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function is_helmet_integrated()
|
||
|
local object = db.actor:item_in_slot(7)
|
||
|
|
||
|
local section_object = object and object:section()
|
||
|
local id_object = object and object:id()
|
||
|
local helmet_avaliable = false
|
||
|
|
||
|
local se_object = id_object and alife_object(id_object)
|
||
|
if se_object then
|
||
|
local data = utils_stpk.get_item_data(se_object)
|
||
|
|
||
|
for k,v in pairs(data) do
|
||
|
helmet_avaliable = utils_data.read_from_ini(ini_sys, section_object, "helmet_avaliable", "bool", false)
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return not helmet_avaliable
|
||
|
end
|
||
|
|
||
|
function play_random_geiger_sound()
|
||
|
local radiation = math.random(8)
|
||
|
local snd = xr_sound.get_safe_sound_object("detectors\\geiger_" .. radiation)
|
||
|
snd:play(db.actor, 0, sound_object.s2d)
|
||
|
snd.volume = radiation_table.dosimeter_volume
|
||
|
end
|
||
|
|
||
|
function show_message_news(news_id, icon)
|
||
|
if news_id == nil then return false end
|
||
|
|
||
|
xr_sound.set_sound_play(AC_ID, "pda_tips")
|
||
|
|
||
|
-- play script sound with matching section name to news id string
|
||
|
if (sound_theme.theme[news_id]) then
|
||
|
xr_sound.set_sound_play(AC_ID, news_id)
|
||
|
if (sound_theme.theme[news_id].snd_obj) then
|
||
|
local length = sound_theme.theme[news_id].snd_obj:length()
|
||
|
showtime = showtime < length and length or showtime
|
||
|
end
|
||
|
end
|
||
|
|
||
|
db.actor:give_game_news(game.translate_string("st_tip"), game.translate_string(news_id), icon, 0, 10000, 0)
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function show_message(msg,time)
|
||
|
local hud = get_hud()
|
||
|
hud:AddCustomStatic("can_use_weapon_now", true)
|
||
|
hud:GetCustomStatic("can_use_weapon_now"):wnd():TextControl():SetTextST(msg)
|
||
|
ShowMessage = true
|
||
|
ShowMessageInit = game.get_game_time()
|
||
|
ShowMessageTime = time
|
||
|
end
|
||
|
|
||
|
function player_has_operational_dosimeter()
|
||
|
--Return if player does not have a geiger counter or battery is dead
|
||
|
local itm_geiger = item_device.device_geiger
|
||
|
local obj_geiger = itm_geiger and db.actor:object(itm_geiger)
|
||
|
if not (obj_geiger and obj_geiger:condition() > 0.09) then
|
||
|
return false
|
||
|
else
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Tracing, comment out method body for release
|
||
|
function trace_this(to_trace)
|
||
|
--local log_file = io.open("log_arszi_radiation.txt", "a")
|
||
|
--log_file:write(to_trace.."\n")
|
||
|
--log_file:close(log_file)
|
||
|
end
|
||
|
|
||
|
function initialize_radiation_table()
|
||
|
if (not radiation_table.dosimeter_volume) then
|
||
|
radiation_table.dosimeter_volume = 1
|
||
|
trace_this("INITIALIZE - dosimeter_volume")
|
||
|
end
|
||
|
|
||
|
if (not radiation_table.wind_alert_state) then
|
||
|
radiation_table.wind_alert_state = WIND_ALERT_STATES.NoAlert
|
||
|
trace_this("INITIALIZE - wind_alert_state")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
savedIcons = {}
|
||
|
actor_status_get_radiation = actor_status.get_radiation
|
||
|
actor_status.get_radiation = function(visual)
|
||
|
local radiation = db.actor.radiation
|
||
|
if settings.displayRadIcon == 1 then
|
||
|
if radiation > settings.RADIATION_DAMAGE_THRESHOLD then
|
||
|
return actor_status_get_radiation(visual)
|
||
|
else
|
||
|
return 0
|
||
|
end
|
||
|
else
|
||
|
return 0
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function toggleInventoryIcon(icoKey, icoEl)
|
||
|
if not savedIcons[icoKey] then
|
||
|
savedIcons[icoKey] = {
|
||
|
wnd_pos = vector2():set(icoEl:GetWndPos().x, icoEl:GetWndPos().y)
|
||
|
}
|
||
|
end
|
||
|
|
||
|
if settings.displayRadIcon == 1 then
|
||
|
if db.actor.radiation > settings.RADIATION_DAMAGE_THRESHOLD then
|
||
|
icoEl:SetWndPos(savedIcons[icoKey].wnd_pos)
|
||
|
else
|
||
|
icoEl:SetWndPos(vector2():set(0,-1000))
|
||
|
end
|
||
|
else
|
||
|
icoEl:SetWndPos(vector2():set(0,-1000))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
remove_actor_status_icon_radiation = throttle(function()
|
||
|
local inventory_ui = ui_inventory.GUI
|
||
|
if inventory_ui and inventory_ui.stat["radia"] then
|
||
|
local ico_n = inventory_ui.stat["radia"].ico_n
|
||
|
if ico_n then
|
||
|
toggleInventoryIcon("ico_n", ico_n)
|
||
|
end
|
||
|
|
||
|
local ico_p = inventory_ui.stat["radia"].ico_p
|
||
|
if ico_p then
|
||
|
toggleInventoryIcon("ico_p", ico_p)
|
||
|
end
|
||
|
end
|
||
|
end, 300)
|
||
|
|
||
|
--***CALLBACKS**************************************************************************************************************************************
|
||
|
function on_game_start()
|
||
|
RegisterScriptCallback("on_option_change", load_settings)
|
||
|
RegisterScriptCallback("actor_on_first_update", load_settings)
|
||
|
RegisterScriptCallback("actor_on_before_hit",actor_on_before_hit)
|
||
|
RegisterScriptCallback("actor_on_update", actor_on_update)
|
||
|
RegisterScriptCallback("on_key_press", on_key_press)
|
||
|
RegisterScriptCallback("on_key_hold", on_key_hold)
|
||
|
RegisterScriptCallback("on_game_load", on_game_load)
|
||
|
RegisterScriptCallback("save_state", save_state)
|
||
|
RegisterScriptCallback("load_state", load_state)
|
||
|
end
|
||
|
|
||
|
function save_state(m_data)
|
||
|
m_data.radiation_table = radiation_table
|
||
|
end
|
||
|
|
||
|
function load_state(m_data)
|
||
|
radiation_table = m_data.radiation_table or {}
|
||
|
end
|
||
|
|
||
|
function on_game_load()
|
||
|
--Initialize radiation_table of not initialized
|
||
|
initialize_radiation_table()
|
||
|
|
||
|
if (settings.ENABLE_DYNAMIC_RADIATION_ZONES) then
|
||
|
if (not radiation_table.dynamic_radiation_zones_generated) then
|
||
|
--spawn dynamic radiation fields
|
||
|
spawn_radiation_fields_at_new_game()
|
||
|
radiation_table.dynamic_radiation_zones_generated = true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (not radiation_table.static_radiation_fields_resized) then
|
||
|
--Resize both original and new radiation fields
|
||
|
resize_radiation_fields()
|
||
|
radiation_table.static_radiation_fields_resized = true
|
||
|
end
|
||
|
|
||
|
if (settings.ENABLE_DYNAMIC_RADIATION_ZONES_NPP) then
|
||
|
if (not radiation_table.dynamic_radiation_zones_generated_npp) then
|
||
|
--Spawn special radiation fields for NPP interrior
|
||
|
spawn_radiation_fields_for_level(special_spawn_l12u_sarcofag)
|
||
|
radiation_table.dynamic_radiation_zones_generated_npp = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function actor_on_before_hit(s_hit)
|
||
|
--Check if damage source is radiation
|
||
|
if (s_hit.type ~= 3) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
--Calculate new power
|
||
|
s_hit.power = calculate_zone_radiation_power(s_hit.power, s_hit.draftsman)
|
||
|
end
|
||
|
|
||
|
-- Less effective boosters the less rads player has
|
||
|
-- Full effectiveness of rads when it goes past settings.RADIATION_DAMAGE_THRESHOLD, then it drops linearly to at least 33% at settings.RADIATION_THRESHOLD
|
||
|
function buildRadTable()
|
||
|
-- LUT with meds effectiveness depending on rads
|
||
|
radTable = {
|
||
|
[settings.RADIATION_THRESHOLD] = settings.minMedEfficiency,
|
||
|
[settings.RADIATION_DAMAGE_THRESHOLD] = 1,
|
||
|
}
|
||
|
radTableKeys = array_keys(radTable, true)
|
||
|
end
|
||
|
|
||
|
local currentBoosters = {}
|
||
|
local boosterCounters = {}
|
||
|
function manage_boosters()
|
||
|
local c_obj = db.actor:cast_Actor()
|
||
|
local function restoreState()
|
||
|
if currentBoosters[BoosterID["RadiationRestore"]] then
|
||
|
currentBoosters[BoosterID["RadiationRestore"]] = nil
|
||
|
c_obj:conditions():BoostRadiationRestore(-(boosterCounters[BoosterID["RadiationRestore"]] or 0))
|
||
|
boosterCounters[BoosterID["RadiationRestore"]] = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if not settings.medDigestibility then
|
||
|
restoreState()
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if not radTable then buildRadTable() end
|
||
|
local foundRadiationBooster = false
|
||
|
c_obj:conditions():BoosterForEach(function(booster_type, booster_time, booster_value)
|
||
|
if booster_type == BoosterID["RadiationRestore"] then
|
||
|
if boosterCounters[booster_type] then
|
||
|
c_obj:conditions():BoostRadiationRestore(-boosterCounters[booster_type])
|
||
|
else
|
||
|
boosterCounters[booster_type] = 0
|
||
|
end
|
||
|
|
||
|
-- If booster is too low - don't apply penalty
|
||
|
if booster_value < 0.00015 then
|
||
|
-- printf("booster_value %s < 0.00015, too low", booster_value)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
currentBoosters[booster_type] = booster_value
|
||
|
boosterCounters[booster_type] = booster_value * lookup(radTable, db.actor.radiation, radTableKeys) - booster_value
|
||
|
c_obj:conditions():BoostRadiationRestore(boosterCounters[booster_type])
|
||
|
|
||
|
-- printf("adjust radiation restore from %s to %s", booster_value, booster_value + boosterCounters[booster_type])
|
||
|
-- printf("rad level %s, booster_value %s time %s", db.actor.radiation, booster_value + boosterCounters[booster_type], booster_time)
|
||
|
|
||
|
foundRadiationBooster = true
|
||
|
end
|
||
|
end)
|
||
|
if not foundRadiationBooster then
|
||
|
restoreState()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function actor_on_update()
|
||
|
--Removes the radiation indicator from the actor status icons
|
||
|
remove_actor_status_icon_radiation()
|
||
|
|
||
|
manage_radiation_zones()
|
||
|
|
||
|
manage_weather()
|
||
|
|
||
|
manage_power()
|
||
|
|
||
|
manage_satiety()
|
||
|
|
||
|
manage_health()
|
||
|
|
||
|
manage_boosters()
|
||
|
|
||
|
manage_geiger_sound()
|
||
|
end
|
||
|
|
||
|
local last_check_time = nil
|
||
|
function manage_radiation_zones()
|
||
|
--Remove of player is not in the water anymore
|
||
|
if (not is_player_in_water()) then
|
||
|
if (radiation_zones[AC_ID]) then
|
||
|
radiation_zones[AC_ID] = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--If not underground, it will be handled elsewhere
|
||
|
if (not is_underground_map()) then return end
|
||
|
|
||
|
--If there is environmental radiation still, the player is in a radiation zone
|
||
|
if (level.get_env_rads() > 0) then return end
|
||
|
if (not last_check_time) then last_check_time = game.get_game_time() end
|
||
|
|
||
|
local current_time = game.get_game_time()
|
||
|
if (current_time:diffSec(last_check_time) > 6) then
|
||
|
for k, _ in pairs(radiation_zones) do
|
||
|
radiation_zones[k] = nil
|
||
|
last_check_time = current_time
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local last_tick_time = nil
|
||
|
function manage_geiger_sound()
|
||
|
--Return if player does not have a geiger counter or battery is dead
|
||
|
if (not player_has_operational_dosimeter()) then return end
|
||
|
|
||
|
if (not last_tick_time) then last_tick_time = game.get_game_time() end
|
||
|
local current_time = game.get_game_time()
|
||
|
|
||
|
local power = get_environmental_radiation_power()
|
||
|
|
||
|
if (power > 0.07) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 0.1) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.06 and power <= 0.07) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 0.25) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.055 and power <= 0.06) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 0.5) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.05 and power <= 0.055) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 1) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.045 and power <= 0.05) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 2) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.04 and power <= 0.045) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 3) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.035 and power <= 0.04) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 4) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.03 and power <= 0.035) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 5) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.025 and power <= 0.03) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 6) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.02 and power <= 0.025) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 7) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.015 and power <= 0.02) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 8) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.01 and power <= 0.015) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 9) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
elseif (power > 0.001 and power <= 0.01) then
|
||
|
if (current_time:diffSec(last_tick_time) >= 10) then
|
||
|
play_random_geiger_sound()
|
||
|
last_tick_time = current_time
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Issues wind alert
|
||
|
function issue_wind_alert(wind_power, previous_wind_power)
|
||
|
--If notifications are not enabled, do not send them
|
||
|
if (not ENABLE_WIND_POWER_NOTIFICATIONS) then return end
|
||
|
|
||
|
--Return if PDA is not charged
|
||
|
if (not item_device.is_pda_charged(true)) then return end
|
||
|
|
||
|
--Return if surge or psy storm have started
|
||
|
if (xr_conditions.surge_started()) then return end
|
||
|
|
||
|
--If underground map, do not send notification
|
||
|
if (is_underground_map()) then return end
|
||
|
|
||
|
wind_power = math.floor(wind_power)
|
||
|
previous_wind_power = math.floor(previous_wind_power)
|
||
|
|
||
|
if (wind_power > max_wind_power_percent(60) and wind_power > previous_wind_power) then
|
||
|
if (radiation_table.wind_alert_state ~= WIND_ALERT_STATES.Strong) then
|
||
|
show_message_news("st_warning_medium_to_strong", "ui_inGame2_V_zone_nedavno_proshel_vibros")
|
||
|
radiation_table.wind_alert_state = WIND_ALERT_STATES.Strong
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (wind_power > max_wind_power_percent(30) and wind_power <= max_wind_power_percent(60) and wind_power > previous_wind_power) then
|
||
|
if (radiation_table.wind_alert_state ~= WIND_ALERT_STATES.Medium) then
|
||
|
show_message_news("st_warning_none_to_medium", "ui_inGame2_V_zone_nedavno_proshel_vibros")
|
||
|
radiation_table.wind_alert_state = WIND_ALERT_STATES.Medium
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (wind_power <= max_wind_power_percent(60) and wind_power > max_wind_power_percent(30) and previous_wind_power > wind_power) then
|
||
|
if (radiation_table.wind_alert_state ~= WIND_ALERT_STATES.Medium) then
|
||
|
show_message_news("st_warning_strong_to_medium", "ui_inGame2_V_zone_nedavno_proshel_vibros")
|
||
|
radiation_table.wind_alert_state = WIND_ALERT_STATES.Medium
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (wind_power <= max_wind_power_percent(30) and previous_wind_power > wind_power) then
|
||
|
if (radiation_table.wind_alert_state ~= WIND_ALERT_STATES.NoAlert) then
|
||
|
show_message_news("st_warning_medium_to_none", "ui_inGame2_V_zone_nedavno_proshel_vibros")
|
||
|
radiation_table.wind_alert_state = WIND_ALERT_STATES.NoAlert
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
--Issues weather report
|
||
|
function issue_weather_report(current_weather, previous_weather)
|
||
|
--If notifications are not enabled, do not send them
|
||
|
if (not settings.ENABLE_WEATHER_REPORTS) then return end
|
||
|
|
||
|
--Return if PDA is not charged
|
||
|
if (not item_device.is_pda_charged(true)) then return end
|
||
|
|
||
|
--Return if surge or psy storm have started
|
||
|
if (xr_conditions.surge_started()) then return end
|
||
|
|
||
|
--If underground map, do not send notification
|
||
|
if (is_underground_map()) then return end
|
||
|
|
||
|
local weather_text = ""
|
||
|
|
||
|
--clear
|
||
|
if (current_weather == "clear" and previous_weather == "partly") then weather_text = "st_weather_clear_from_partly" end
|
||
|
if (current_weather == "clear" and previous_weather == "foggy") then weather_text = "st_weather_clear_from_foggy" end
|
||
|
if (current_weather == "clear" and previous_weather == "cloudy") then weather_text = "st_weather_clear_from_cloudy" end
|
||
|
if (current_weather == "clear" and previous_weather == "rain") then weather_text = "st_weather_clear_from_rain" end
|
||
|
if (current_weather == "clear" and previous_weather == "storm") then weather_text = "st_weather_clear_from_storm" end
|
||
|
|
||
|
--partly
|
||
|
if (current_weather == "partly" and previous_weather == "clear") then weather_text = "st_weather_partly_from_clear" end
|
||
|
if (current_weather == "partly" and previous_weather == "foggy") then weather_text = "st_weather_partly_from_foggy" end
|
||
|
if (current_weather == "partly" and previous_weather == "cloudy") then weather_text = "st_weather_partly_from_cloudy" end
|
||
|
if (current_weather == "partly" and previous_weather == "rain") then weather_text = "st_weather_partly_from_rain" end
|
||
|
if (current_weather == "partly" and previous_weather == "storm") then weather_text = "st_weather_partly_from_storm" end
|
||
|
|
||
|
--foggy
|
||
|
if (current_weather == "foggy" and previous_weather == "clear") then weather_text = "st_weather_foggy_from_clear" end
|
||
|
if (current_weather == "foggy" and previous_weather == "partly") then weather_text = "st_weather_foggy_from_partly" end
|
||
|
if (current_weather == "foggy" and previous_weather == "cloudy") then weather_text = "st_weather_foggy_from_cloudy" end
|
||
|
if (current_weather == "foggy" and previous_weather == "rain") then weather_text = "st_weather_foggy_from_rain" end
|
||
|
if (current_weather == "foggy" and previous_weather == "storm") then weather_text = "st_weather_foggy_from_storm" end
|
||
|
|
||
|
--cloudy
|
||
|
if (current_weather == "cloudy" and previous_weather == "clear") then weather_text = "st_weather_cloudy_from_clear" end
|
||
|
if (current_weather == "cloudy" and previous_weather == "partly") then weather_text = "st_weather_cloudy_from_partly" end
|
||
|
if (current_weather == "cloudy" and previous_weather == "foggy") then weather_text = "st_weather_cloudy_from_foggy" end
|
||
|
if (current_weather == "cloudy" and previous_weather == "rain") then weather_text = "st_weather_cloudy_from_rain" end
|
||
|
if (current_weather == "cloudy" and previous_weather == "storm") then weather_text = "st_weather_cloudy_from_storm" end
|
||
|
|
||
|
--rain
|
||
|
if (current_weather == "rain" and previous_weather == "clear") then weather_text = "st_weather_rain_from_clear" end
|
||
|
if (current_weather == "rain" and previous_weather == "partly") then weather_text = "st_weather_rain_from_partly" end
|
||
|
if (current_weather == "rain" and previous_weather == "cloudy") then weather_text = "st_weather_rain_from_cloudy" end
|
||
|
if (current_weather == "rain" and previous_weather == "foggy") then weather_text = "st_weather_rain_from_foggy" end
|
||
|
if (current_weather == "rain" and previous_weather == "storm") then weather_text = "st_weather_rain_from_storm" end
|
||
|
|
||
|
--storm
|
||
|
if (current_weather == "storm" and previous_weather == "clear") then weather_text = "st_weather_storm_from_clear" end
|
||
|
if (current_weather == "storm" and previous_weather == "partly") then weather_text = "st_weather_storm_from_partly" end
|
||
|
if (current_weather == "storm" and previous_weather == "cloudy") then weather_text = "st_weather_storm_from_cloudy" end
|
||
|
if (current_weather == "storm" and previous_weather == "rain") then weather_text = "st_weather_storm_from_rain" end
|
||
|
if (current_weather == "storm" and previous_weather == "foggy") then weather_text = "st_weather_storm_from_foggy" end
|
||
|
|
||
|
--Override some texts with sunlight in them during night
|
||
|
local hour = level.get_time_hours()
|
||
|
if (hour <= 6 or hour >= 20) then
|
||
|
if (current_weather == "clear" and previous_weather == "partly") then weather_text = "st_weather_clear_from_partly_night" end
|
||
|
if (current_weather == "clear" and previous_weather == "foggy") then weather_text = "st_weather_clear_from_foggy_night" end
|
||
|
if (current_weather == "clear" and previous_weather == "storm") then weather_text = "st_weather_clear_from_storm_night" end
|
||
|
if (current_weather == "storm" and previous_weather == "clear") then weather_text = "st_weather_storm_from_clear_night" end
|
||
|
end
|
||
|
|
||
|
--Select icon
|
||
|
local current_icon = "" --"ui_inGame2_Radiopomehi"
|
||
|
if (current_weather == "clear") then current_icon = "ui_weather_clear" end
|
||
|
if (current_weather == "partly") then current_icon = "ui_weather_partly" end
|
||
|
if (current_weather == "foggy") then current_icon = "ui_weather_foggy" end
|
||
|
if (current_weather == "cloudy") then current_icon = "ui_weather_cloudy" end
|
||
|
if (current_weather == "rain") then current_icon = "ui_weather_rain" end
|
||
|
if (current_weather == "storm") then current_icon = "ui_weather_storm" end
|
||
|
|
||
|
show_message_news(weather_text, current_icon)
|
||
|
end
|
||
|
|
||
|
--Get information about the current weather by pressing key "9"
|
||
|
function getWeatherInfo()
|
||
|
local name_of_level = level.name()
|
||
|
if (not name_of_level) then return end
|
||
|
|
||
|
if (underground_maps[name_of_level]) then
|
||
|
show_message("st_underground", 1000)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local current_weather = get_weather_text(get_current_weather())
|
||
|
local wind_velocity = get_reported_wind_velocity()
|
||
|
local wind_direction = radiation_table.last_wind_direction
|
||
|
|
||
|
if (wind_velocity ~= nil and wind_direction ~= nil) then
|
||
|
show_message(game.translate_string("st_weather_is").." "..current_weather..". "..get_wind_power_text(wind_velocity).." "..get_wind_direction_text(wind_direction), 1000)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Dosimeter volume up
|
||
|
function dosimeterVolumeUp()
|
||
|
if (not player_has_operational_dosimeter()) then return end
|
||
|
|
||
|
if (radiation_table.dosimeter_volume > 1.9) then
|
||
|
show_message("Dosimeter volume: MAX")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
radiation_table.dosimeter_volume = radiation_table.dosimeter_volume + 0.2
|
||
|
|
||
|
if (radiation_table.dosimeter_volume > 1.9) then
|
||
|
show_message("Dosimeter volume: MAX")
|
||
|
else
|
||
|
show_message("Dosimeter volume: "..radiation_table.dosimeter_volume)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Dosimeter volume down
|
||
|
function dosimeterVolumeDown()
|
||
|
if (not player_has_operational_dosimeter()) then return end
|
||
|
|
||
|
if (radiation_table.dosimeter_volume < 0.1) then
|
||
|
show_message("Dosimeter volume: MUTED")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
radiation_table.dosimeter_volume = radiation_table.dosimeter_volume - 0.2
|
||
|
|
||
|
if (radiation_table.dosimeter_volume < 0.1) then
|
||
|
show_message("Dosimeter volume: MUTED")
|
||
|
else
|
||
|
show_message("Dosimeter volume: "..radiation_table.dosimeter_volume)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function on_key_press(key)
|
||
|
local function mcmKeyPress(settings_key, func, ...)
|
||
|
local k = settings_key
|
||
|
local mode = settings_key .. "_mode"
|
||
|
local modifier = settings_key .. "_modifier"
|
||
|
local keyname = "arszi_radiation_" .. k
|
||
|
if (key == settings[k]) then
|
||
|
if not mcm_keybinds then
|
||
|
func(...)
|
||
|
else
|
||
|
if settings[mode] == 0 then
|
||
|
ui_mcm.simple_press(keyname, key, func, ...)
|
||
|
elseif settings[mode] == 1 and ui_mcm.get_mod_key(settings[modifier]) and ui_mcm.double_tap(keyname, key) then
|
||
|
func(...)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Weather
|
||
|
mcmKeyPress("WEATHER_INFO_KEY", getWeatherInfo)
|
||
|
|
||
|
--Dosimeter volume up
|
||
|
mcmKeyPress("DOSIMETER_VOLUME_UP_KEY", dosimeterVolumeUp)
|
||
|
|
||
|
--Dosimeter volume down
|
||
|
mcmKeyPress("DOSIMETER_VOLUME_DOWN", dosimeterVolumeDown)
|
||
|
end
|
||
|
|
||
|
function on_key_hold(key)
|
||
|
local function mcmKeyHold(settings_key, func, ...)
|
||
|
local k = settings_key
|
||
|
local mode = settings_key .. "_mode"
|
||
|
local modifier = settings_key .. "_modifier"
|
||
|
local keyname = "arszi_radiation_" .. k
|
||
|
|
||
|
if mcm_keybinds and key == settings[k] and settings[mode] == 2 and ui_mcm.get_mod_key(settings[modifier]) and ui_mcm.key_hold(keyname, key) then
|
||
|
func(...)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Weather
|
||
|
mcmKeyHold("WEATHER_INFO_KEY", getWeatherInfo)
|
||
|
|
||
|
--Dosimeter volume up
|
||
|
mcmKeyHold("DOSIMETER_VOLUME_UP_KEY", dosimeterVolumeUp)
|
||
|
|
||
|
--Dosimeter volume down
|
||
|
mcmKeyHold("DOSIMETER_VOLUME_DOWN_KEY", dosimeterVolumeDown)
|
||
|
end
|
||
|
|
||
|
--***WEATHER**************************************************************************************************************************************
|
||
|
function manage_weather()
|
||
|
local name_of_level = level.name()
|
||
|
if (not name_of_level) then return end
|
||
|
|
||
|
--Check if weather changed, and calculate new wind direction, if yes
|
||
|
if (not radiation_table.last_weather) then
|
||
|
radiation_table.last_weather = get_current_weather()
|
||
|
end
|
||
|
|
||
|
if (not radiation_table.last_wind_direction) then
|
||
|
radiation_table.last_wind_direction = get_new_wind_direction()
|
||
|
end
|
||
|
|
||
|
if (not radiation_table.last_wind_velocity) then
|
||
|
radiation_table.last_wind_velocity = get_wind_velocity()
|
||
|
end
|
||
|
|
||
|
local current_weather = get_current_weather()
|
||
|
|
||
|
--Issue wind warning if necessary
|
||
|
if (current_weather == "clear" or current_weather == "partly" or current_weather == "cloudy") then
|
||
|
local current_wind_velocity = get_wind_velocity()
|
||
|
--Issues wind alert if wind velocity is too high
|
||
|
issue_wind_alert(current_wind_velocity, radiation_table.last_wind_velocity)
|
||
|
|
||
|
radiation_table.last_wind_velocity = current_wind_velocity
|
||
|
end
|
||
|
|
||
|
--Change wind direction on weather change
|
||
|
if (current_weather ~= radiation_table.last_weather) then
|
||
|
--Issue new weather report
|
||
|
issue_weather_report(current_weather, radiation_table.last_weather)
|
||
|
|
||
|
radiation_table.last_weather = current_weather
|
||
|
radiation_table.last_wind_direction = get_new_wind_direction()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Gets a new wind direction randomly, favouring western directions mostly
|
||
|
function get_new_wind_direction()
|
||
|
local rand = math.random(100)
|
||
|
|
||
|
if (rand >= 60) then
|
||
|
local direction = math.random(100)
|
||
|
if (direction >= 60) then
|
||
|
return WIND_DIRECTIONS.W
|
||
|
elseif (direction >= 30) then
|
||
|
return WIND_DIRECTIONS.SW
|
||
|
else
|
||
|
return WIND_DIRECTIONS.NW
|
||
|
end
|
||
|
|
||
|
elseif (rand >= 40) then
|
||
|
local direction = math.random(100)
|
||
|
if (direction >= 60) then
|
||
|
return WIND_DIRECTIONS.N
|
||
|
elseif (direction >= 30) then
|
||
|
return WIND_DIRECTIONS.NW
|
||
|
else
|
||
|
return WIND_DIRECTIONS.NE
|
||
|
end
|
||
|
|
||
|
elseif (rand >= 20) then
|
||
|
local direction = math.random(100)
|
||
|
if (direction >= 60) then
|
||
|
return WIND_DIRECTIONS.E
|
||
|
elseif (direction >= 30) then
|
||
|
return WIND_DIRECTIONS.NE
|
||
|
else
|
||
|
return WIND_DIRECTIONS.SE
|
||
|
end
|
||
|
|
||
|
else
|
||
|
local direction = math.random(100)
|
||
|
if (direction >= 60) then
|
||
|
return WIND_DIRECTIONS.S
|
||
|
elseif (direction >= 30) then
|
||
|
return WIND_DIRECTIONS.SE
|
||
|
else
|
||
|
return WIND_DIRECTIONS.SW
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Gets the text for the corresponding wind power
|
||
|
function get_wind_power_text(wind_velocity)
|
||
|
if (wind_velocity == 0) then
|
||
|
return game.translate_string("st_wind_power_no_wind")
|
||
|
elseif (wind_velocity < max_wind_power_percent(15)) then
|
||
|
return game.translate_string("st_wind_power_slight_breeze")
|
||
|
elseif (wind_velocity < max_wind_power_percent(30)) then
|
||
|
return game.translate_string("st_wind_power_weak")
|
||
|
elseif (wind_velocity < max_wind_power_percent(40)) then
|
||
|
return game.translate_string("st_wind_power_moderate")
|
||
|
elseif (wind_velocity < max_wind_power_percent(55)) then
|
||
|
return game.translate_string("st_wind_power_strong")
|
||
|
elseif (wind_velocity < max_wind_power_percent(70)) then
|
||
|
return game.translate_string("st_wind_power_very_strong")
|
||
|
else
|
||
|
return game.translate_string("st_wind_power_storm")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--Gets the text for the corresponding wind direction
|
||
|
function get_wind_direction_text(wind_direction)
|
||
|
if (wind_direction == WIND_DIRECTIONS.No_wind) then
|
||
|
return ""
|
||
|
end
|
||
|
|
||
|
local text = game.translate_string("st_direction").." "
|
||
|
if (wind_direction == WIND_DIRECTIONS.N) then
|
||
|
return text..game.translate_string("st_direction_north")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.NE) then
|
||
|
return text..game.translate_string("st_direction_north_east")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.E) then
|
||
|
return text..game.translate_string("st_direction_east")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.SE) then
|
||
|
return text..game.translate_string("st_direction_south_east")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.S) then
|
||
|
return text..game.translate_string("st_direction_south")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.SW) then
|
||
|
return text..game.translate_string("st_direction_south_west")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.W) then
|
||
|
return text..game.translate_string("st_direction_west")
|
||
|
elseif (wind_direction == WIND_DIRECTIONS.NW) then
|
||
|
return text..game.translate_string("st_direction_north_west")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function get_weather_text(current_weather)
|
||
|
if (current_weather == "clear") then
|
||
|
return game.translate_string("st_clear")
|
||
|
end
|
||
|
|
||
|
if (current_weather == "partly") then
|
||
|
return game.translate_string("st_partly_clear")
|
||
|
end
|
||
|
|
||
|
if (current_weather == "foggy") then
|
||
|
return game.translate_string("st_foggy")
|
||
|
end
|
||
|
|
||
|
if (current_weather == "cloudy") then
|
||
|
return game.translate_string("st_cloudy")
|
||
|
end
|
||
|
|
||
|
if (current_weather == "rain") then
|
||
|
return game.translate_string("st_rain")
|
||
|
end
|
||
|
|
||
|
if (current_weather == "storm") then
|
||
|
return game.translate_string("st_storm")
|
||
|
end
|
||
|
|
||
|
return ""
|
||
|
end
|
||
|
|
||
|
function get_current_weather()
|
||
|
local current_weather = level_weathers.get_weather_manager().weather_file
|
||
|
local weather_file_to_weather = {
|
||
|
w_clear = "clear",
|
||
|
w_partly = "partly",
|
||
|
w_cloudy = "cloudy",
|
||
|
w_rain = "rain",
|
||
|
w_storm = "storm",
|
||
|
w_foggy = "foggy",
|
||
|
tuman = "foggy"
|
||
|
}
|
||
|
|
||
|
local found = false
|
||
|
for k, v in pairs(weather_file_to_weather) do
|
||
|
if string.find(current_weather, k) then
|
||
|
current_weather = v
|
||
|
found = true
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
if not found then
|
||
|
current_weather = nil
|
||
|
end
|
||
|
return current_weather or level_weathers.get_weather_manager():get_curr_weather() or ""
|
||
|
end
|
||
|
|
||
|
function get_wind_velocity()
|
||
|
local wind_velocity = weather.get_value_numric("wind_velocity") or 0
|
||
|
if (wind_velocity > 0) then
|
||
|
return wind_velocity
|
||
|
else
|
||
|
return 1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Prints wind velocity from weather ltx, independednt on actual wind
|
||
|
function get_reported_wind_velocity()
|
||
|
local current_weather = level_weathers.get_weather_manager().weather_file
|
||
|
local hr = level.get_time_hours()
|
||
|
local ini = current_weather and ini_file("environment\\weathers\\" .. current_weather .. ".ltx")
|
||
|
local hr_string = level_weathers.get_weather_manager():get_hour_as_string(hr)
|
||
|
local wind_velocity = ini and hr_string and ini:section_exist(hr_string) and ini:r_string_ex(hr_string,"wind_velocity")
|
||
|
if not wind_velocity then
|
||
|
return get_wind_velocity()
|
||
|
end
|
||
|
return tonumber(wind_velocity)
|
||
|
end
|
||
|
|
||
|
function max_wind_power_percent(percentage)
|
||
|
return settings.MAX_WIND_VELOCITY * (percentage / 100)
|
||
|
end
|
||
|
|
||
|
-- CHEATS
|
||
|
-- Give dosimeter on game start
|
||
|
new_game_equippment = itms_manager.new_game_equippment
|
||
|
itms_manager.new_game_equippment = function()
|
||
|
new_game_equippment()
|
||
|
if settings.giveDosimeterOnStart and not db.actor:object("detector_geiger") then
|
||
|
alife_create_item("detector_geiger", db.actor)
|
||
|
end
|
||
|
return true
|
||
|
end
|