1003 lines
27 KiB
Plaintext
1003 lines
27 KiB
Plaintext
|
--[[
|
||
|
Tronex
|
||
|
2019/12/7
|
||
|
Device binder
|
||
|
|
||
|
This script manage devices power consumption, by binding online devices to the device_binder below. Providing consistant check/management
|
||
|
Engine handles turning devices functionality off when their power go below a certain point (power_critical), while some are script conrtrolled
|
||
|
See "items\settings\devices.ltx" for configurations
|
||
|
--]]
|
||
|
|
||
|
|
||
|
local enable_debug = false
|
||
|
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Controls
|
||
|
--------------------------------------------------------------------------------
|
||
|
dev_consumption = {} -- [sec] = {...} | Power consumption of device per stage
|
||
|
dev_consumption_act = {} -- [sec] = {...} | Power consumption of device per action (stage 2)
|
||
|
dev_consumption_tot = {} -- [sec] = num | Total power consumption of a device
|
||
|
dev_critical = {} -- [sec] = num | Power limit, devices go off when their power go below this value
|
||
|
local dev_condition = {} -- [id] = con | Table to store/reload condition of online devices
|
||
|
local dev_event_loss = {} -- [id] = num | Table to trace power lost on device event activity
|
||
|
local dev_inv = {} -- [id] = bool | Table to trace devices in inventory
|
||
|
local dev_slot = {} -- [id] = bool | Table to trace devices in slots
|
||
|
local dev_type = {
|
||
|
["D_PDA"] = "PDA",
|
||
|
["DET_SIMP"] = "DET",
|
||
|
["DET_ADVA"] = "DET",
|
||
|
["DET_ELIT"] = "DET",
|
||
|
["DET_SCIE"] = "DET",
|
||
|
["D_DSMETR"] = "DET",
|
||
|
["D_CUSTOM"] = "DET",
|
||
|
["D_FLALIT"] = "FLASH",
|
||
|
["TORCH_S"] = "TORCH",
|
||
|
["II_ATTCH"] = "OTHR",
|
||
|
}
|
||
|
|
||
|
-- device sections
|
||
|
devices = {}
|
||
|
device_battery = nil
|
||
|
device_geiger = nil
|
||
|
device_npc_pda = {}
|
||
|
|
||
|
local amper_factor
|
||
|
|
||
|
|
||
|
-- Condition States
|
||
|
local c_limit
|
||
|
local c_zero
|
||
|
local c_full
|
||
|
local temp_con = {}
|
||
|
|
||
|
-- PDA
|
||
|
local pda_active = false
|
||
|
|
||
|
-- Torch
|
||
|
local torch_active = false
|
||
|
|
||
|
-- Night-visions
|
||
|
nv_state = false -- make nv_state a global variable so we can access it from beefs_nvgs
|
||
|
nv_discharged = false
|
||
|
|
||
|
local KEYBIND_NV_UP
|
||
|
local KEYBIND_NV_DOWN
|
||
|
|
||
|
local w_multi = 1024/(device().width)
|
||
|
local h_multi = 768/(device().height)
|
||
|
local nv_eff = {}
|
||
|
|
||
|
local nv_hud = { x = 0, y = 0, height = (device().height * h_multi), weight = (device().width * w_multi) } -- HUD dimensions/position
|
||
|
local ppe_effects = { -- PPE effects used for each NV. lum and id used for their ppe effects
|
||
|
["nightvision_1"] = { id = 3301, lum = 0.2 , ui = "wpn\\hud_nvg_gen1" },
|
||
|
["nightvision_2"] = { id = 3302, lum = 0.5 , ui = "wpn\\hud_nvg_gen2" },
|
||
|
["nightvision_3"] = { id = 3303, lum = 1.0 , ui = "wpn\\hud_nvg_gen3" },
|
||
|
--["nightvision_bluer"] = { id = 3304, lum = 1.0 , ui = "wpn\\hud_nvg_gen1_test" },
|
||
|
}
|
||
|
|
||
|
-- Dosimeter
|
||
|
local rad_unit = game.translate_string("st_msv")
|
||
|
local holding_shift = false
|
||
|
dosimeter_env_rads_mode = false
|
||
|
local rad_zones = {
|
||
|
["zone_field_radioactive"] = true,
|
||
|
["zone_field_radioactive_weak"] = true,
|
||
|
["zone_field_radioactive_average"] = true,
|
||
|
["zone_field_radioactive_strong"] = true,
|
||
|
["zone_radioactive"] = true,
|
||
|
["zone_radioactive_weak"] = true,
|
||
|
["zone_radioactive_average"] = true,
|
||
|
["zone_radioactive_strong"] = true,
|
||
|
}
|
||
|
|
||
|
function initialize()
|
||
|
local ini_device = ini_file("items\\settings\\devices.ltx")
|
||
|
|
||
|
device_battery = ini_device:r_sec_ex("settings","battery","batteries_dead")
|
||
|
device_geiger = ini_device:r_sec_ex("settings","geiger","detector_geiger")
|
||
|
|
||
|
-- We clear battery from devices list, and put it in its own category
|
||
|
_ITM["battery"] = {}
|
||
|
_ITM["battery"][device_battery] = true
|
||
|
if _ITM["device"][device_battery] then
|
||
|
_ITM["device"][device_battery] = nil
|
||
|
end
|
||
|
|
||
|
c_limit = ini_device:r_float_ex("settings","condition_limit") or 0.05
|
||
|
c_zero = ini_device:r_float_ex("settings","condition_zero") or 0.0001
|
||
|
c_full = ini_device:r_float_ex("settings","condition_full") or 0.9999
|
||
|
amper_factor = ini_device:r_float_ex("settings","amper_factor") or 1000
|
||
|
|
||
|
local n = ini_device:line_count("power_consumption")
|
||
|
for i=0,n-1 do
|
||
|
local result, sec, value = ini_device:r_line_ex("power_consumption",i,"","")
|
||
|
if ini_sys:section_exist(sec) and value then
|
||
|
dev_critical[sec] = ini_sys:r_float_ex(sec,"power_critical") or c_limit
|
||
|
|
||
|
dev_consumption[sec] = {}
|
||
|
local t = str_explode(value,",")
|
||
|
for j=1,#t do
|
||
|
local cons = tonumber(t[j]) or 0
|
||
|
if (t[j] == "p") or (cons > 0) then
|
||
|
dev_consumption[sec][j] = cons
|
||
|
dev_consumption_tot[sec] = dev_consumption_tot[sec] and (dev_consumption_tot[sec] + cons) or cons
|
||
|
print_dbg("dev_consumption [%s][%s] = %s", sec, j, cons)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
dev_consumption_act[sec] = {}
|
||
|
local value_e = ini_device:r_string_ex("power_consumption_event",sec)
|
||
|
if value_e then
|
||
|
t = str_explode(value_e,",")
|
||
|
for j=1,#t do
|
||
|
local cons = tonumber(t[j]) or 0
|
||
|
if cons > 0 then
|
||
|
dev_consumption_act[sec][j] = cons
|
||
|
dev_consumption_tot[sec] = dev_consumption_tot[sec] and (dev_consumption_tot[sec] + cons) or cons
|
||
|
print_dbg("dev_consumption_act [%s][%s] = %s", sec, j, cons)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
printe("!ERROR item_device | reading from items\\settings\\devices.ltx | section [%s] or power consumption values don't exist", sec)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
n = ini_device:line_count("npc_pda")
|
||
|
for i=0,n-1 do
|
||
|
local result, sec, value = ini_device:r_line_ex("npc_pda",i,"","")
|
||
|
if ini_sys:section_exist(sec) then
|
||
|
device_npc_pda[sec] = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function print_dbg(txt, ...)
|
||
|
if enable_debug then
|
||
|
printf("item_device | %s | " .. txt, time_global(), ...)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Callbacks
|
||
|
--------------------------------------------------------------------------------
|
||
|
local function process_pda_info(npc,info_id)
|
||
|
-- add this function to bind_stalker info_callback function
|
||
|
-- battery.process_pda_infoportion(info_id)
|
||
|
local actor = db.actor
|
||
|
if actor then
|
||
|
if info_id == "ui_pda" then
|
||
|
-- PDA opened
|
||
|
-- set flag to indicate PDA is open
|
||
|
pda_active = true
|
||
|
hide_hud_inventory()
|
||
|
--news_manager.send_tip(actor, "Opening PDA", nil, nil, 5000)
|
||
|
actor:disable_info_portion("ui_pda_hide")
|
||
|
elseif info_id == "ui_pda_hide" then
|
||
|
-- PDA closed
|
||
|
-- remove flag to indicate PDA is closed
|
||
|
pda_active = false
|
||
|
--news_manager.send_tip(actor, "Closing PDA", nil, nil, 5000)
|
||
|
actor:disable_info_portion("ui_pda")
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function on_item_drag_dropped(obj_b, obj_d, slot_from, slot_to)
|
||
|
|
||
|
-- Check capability
|
||
|
if not (slot_from == EDDListType.iActorBag and (slot_to == EDDListType.iActorBag or slot_to == EDDListType.iActorSlot)) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local sec_b = obj_b:section() -- battery
|
||
|
local sec_d = obj_d:section() -- device
|
||
|
if (sec_b == device_battery) and dev_consumption[sec_d] then
|
||
|
local con_b = obj_b:condition()
|
||
|
local con_d = obj_d:condition()
|
||
|
|
||
|
if (con_d > con_b) then
|
||
|
actor_menu.set_msg(1, game.translate_string("ui_st_battery_more_power"))
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (con_d > dev_critical[sec_d]) then
|
||
|
alife_create_item(device_battery, db.actor, {cond = con_d})
|
||
|
end
|
||
|
|
||
|
alife_release(obj_b)
|
||
|
obj_d:set_condition(con_b)
|
||
|
|
||
|
actor_effects.play_item_fx(device_battery)
|
||
|
utils_obj.play_sound("interface\\inv_batt")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function on_key_hold(key)
|
||
|
if (key == DIK_keys["DIK_LSHIFT"]) then
|
||
|
holding_shift = true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function on_key_release(key)
|
||
|
|
||
|
if (key == DIK_keys["DIK_LSHIFT"]) then
|
||
|
holding_shift = false
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local bind = dik_to_bind(key)
|
||
|
|
||
|
-- Night-Vision
|
||
|
if (bind == key_bindings.kNIGHT_VISION) then
|
||
|
local torch = db.actor:item_in_slot(10)
|
||
|
set_nightvision(torch and torch:section(), not nv_state)
|
||
|
|
||
|
elseif (key == KEYBIND_NV_UP) then
|
||
|
z_beefs_nvgs.brightness_up()
|
||
|
|
||
|
elseif (key == KEYBIND_NV_DOWN) then
|
||
|
z_beefs_nvgs.brightness_down()
|
||
|
|
||
|
-- Dosimeter keybind
|
||
|
elseif (bind == key_bindings.kCUSTOM14) then
|
||
|
local obj_geiger = db.actor:object(device_geiger)
|
||
|
if obj_geiger and drain_device_on_event(obj_geiger, device_geiger, 2) then
|
||
|
|
||
|
if (holding_shift) then
|
||
|
dosimeter_env_rads_mode = not dosimeter_env_rads_mode
|
||
|
dosimeter_mode_string = dosimeter_env_rads_mode and game.translate_string("st_environment_rads") or game.translate_string("st_actor_rads")
|
||
|
actor_menu.set_msg(1, game.translate_string("st_dosimeter_mode")..": "..dosimeter_mode_string , 3)
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local rads = dosimeter_env_rads_mode and math.floor(level.get_env_rads()*2500) or math.floor(db.actor.radiation*10000*0.387)
|
||
|
actor_menu.set_msg(1, tostring(rads) .. " " .. rad_unit , 2 )
|
||
|
utils_obj.play_sound("detectors\\geiger_1")
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function on_anomaly_touch(obj, flags)
|
||
|
if obj then
|
||
|
|
||
|
-- Don't play gieger sound if player don't have a geiger counter
|
||
|
if rad_zones[obj:section()] then
|
||
|
if game_difficulties.get_game_factor("notify_geiger") then
|
||
|
local obj_geiger = device_geiger and db.actor:object(device_geiger)
|
||
|
if not (obj_geiger and drain_device_on_event(obj_geiger, device_geiger, 1)) then
|
||
|
flags.ret_value = false
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Anomaly detector
|
||
|
elseif (not game_difficulties.get_game_factor("notify_anomaly")) then
|
||
|
flags.ret_value = false
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
flags.ret_value = true
|
||
|
end
|
||
|
|
||
|
local function on_trade_opened()
|
||
|
local function search(temp , item)
|
||
|
if IsItem("device",item:section()) then
|
||
|
temp_con[item:id()] = item:condition()
|
||
|
item:set_condition(c_full)
|
||
|
end
|
||
|
end
|
||
|
db.actor:iterate_inventory(search,nil)
|
||
|
|
||
|
local pda = db.actor:item_in_slot(8)
|
||
|
if pda and (not temp_con[pda:id()]) then
|
||
|
temp_con[pda:id()] = pda:condition()
|
||
|
pda:set_condition(c_full)
|
||
|
end
|
||
|
|
||
|
local det = db.actor:item_in_slot(9)
|
||
|
if det and (not temp_con[det:id()]) then
|
||
|
temp_con[det:id()] = det:condition()
|
||
|
det:set_condition(c_full)
|
||
|
end
|
||
|
|
||
|
local torch = db.actor:item_in_slot(10)
|
||
|
if torch and (not temp_con[torch:id()]) then
|
||
|
temp_con[torch:id()] = torch:condition()
|
||
|
torch:set_condition(c_full)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function on_trade_closed()
|
||
|
for k,v in pairs(temp_con) do
|
||
|
local obj = level.object_by_id(k)
|
||
|
if obj then
|
||
|
local p = obj:parent()
|
||
|
if (p and p:id() == AC_ID) then
|
||
|
alife_process_item(obj:section(),k, {cond = v})
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
temp_con = empty_table(temp_con)
|
||
|
end
|
||
|
|
||
|
local function item_rack(obj)
|
||
|
local sec = obj:section()
|
||
|
if dev_consumption[sec] then
|
||
|
local id = obj:id()
|
||
|
dev_inv[id] = true
|
||
|
dev_slot[id] = nil
|
||
|
print_dbg("[%s] moved to inv", obj:name())
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function item_slot(obj)
|
||
|
local sec = obj:section()
|
||
|
if dev_consumption[sec] then
|
||
|
local id = obj:id()
|
||
|
dev_inv[id] = nil
|
||
|
dev_slot[id] = true
|
||
|
print_dbg("[%s] moved to slot", obj:name())
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function item_out(obj)
|
||
|
local sec = obj:section()
|
||
|
if dev_consumption[sec] then
|
||
|
local id = obj:id()
|
||
|
|
||
|
-- Hack to turn off dropped NV device. TODO: adjust the device process function to overcome this hack
|
||
|
if dev_slot[id] and nv_state then
|
||
|
local cls = ini_sys:r_string_ex(sec,"class")
|
||
|
if (dev_type[cls] == "TORCH") then
|
||
|
set_nightvision(sec,false)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
dev_inv[id] = nil
|
||
|
dev_slot[id] = nil
|
||
|
print_dbg("[%s] moved out", obj:name())
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function actor_on_first_update()
|
||
|
for i=1,13 do
|
||
|
local obj = db.actor:item_in_slot(i)
|
||
|
if obj and IsItem("device",obj:section()) then
|
||
|
item_slot(obj)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Restore torch state
|
||
|
local torch = db.actor:item_in_slot(10)
|
||
|
if torch and torch:torch_enabled() then
|
||
|
torch_active = torch:id()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function se_device_on_unregister(se_obj, typ)
|
||
|
--local sec = se_obj:section_name()
|
||
|
--if ini_sys:r_string_ex(sec,"script_binding") == "item_device.bind" then
|
||
|
local id = se_obj.id
|
||
|
dev_condition[id] = nil
|
||
|
dev_inv[id] = nil
|
||
|
dev_slot[id] = nil
|
||
|
--end
|
||
|
end
|
||
|
|
||
|
local function save_state(mdata)
|
||
|
mdata.device_condition = dev_condition
|
||
|
end
|
||
|
|
||
|
local function load_state(mdata)
|
||
|
dev_condition = mdata.device_condition or {}
|
||
|
end
|
||
|
|
||
|
local function on_option_change()
|
||
|
KEYBIND_NV_UP = z_beefs_nvgs_mcm.get_config("keybind_nv_up")
|
||
|
KEYBIND_NV_DOWN = z_beefs_nvgs_mcm.get_config("keybind_nv_down")
|
||
|
end
|
||
|
|
||
|
function on_game_start()
|
||
|
if (USE_MARSHAL) then
|
||
|
RegisterScriptCallback("save_state",save_state)
|
||
|
RegisterScriptCallback("load_state",load_state)
|
||
|
end
|
||
|
|
||
|
RegisterScriptCallback("server_entity_on_unregister",se_device_on_unregister)
|
||
|
RegisterScriptCallback("actor_on_feeling_anomaly",on_anomaly_touch)
|
||
|
|
||
|
RegisterScriptCallback("on_key_press",on_key_press)
|
||
|
RegisterScriptCallback("on_key_release",on_key_release)
|
||
|
RegisterScriptCallback("on_key_hold",on_key_hold)
|
||
|
|
||
|
RegisterScriptCallback("ActorMenu_on_item_drag_drop",on_item_drag_dropped)
|
||
|
RegisterScriptCallback("actor_on_info_callback",process_pda_info)
|
||
|
RegisterScriptCallback("ActorMenu_on_trade_started",on_trade_opened)
|
||
|
RegisterScriptCallback("ActorMenu_on_trade_closed",on_trade_closed)
|
||
|
|
||
|
RegisterScriptCallback("actor_on_first_update",actor_on_first_update)
|
||
|
RegisterScriptCallback("actor_on_item_take",item_rack)
|
||
|
RegisterScriptCallback("actor_item_to_ruck",item_rack)
|
||
|
RegisterScriptCallback("actor_on_item_drop",item_out)
|
||
|
RegisterScriptCallback("actor_item_to_slot",item_slot)
|
||
|
|
||
|
RegisterScriptCallback("on_option_change", on_option_change)
|
||
|
on_option_change()
|
||
|
|
||
|
initialize()
|
||
|
end
|
||
|
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Item property
|
||
|
--------------------------------------------------------------------------------
|
||
|
function menu_battery(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then return end
|
||
|
|
||
|
local sec = obj:section()
|
||
|
local con = obj:condition()
|
||
|
if (sec ~= device_battery) and dev_critical[sec] and (con > dev_critical[sec]) then
|
||
|
return game.translate_string("st_item_unpack_battery")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function func_battery(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local sec = obj:section()
|
||
|
local con = obj:condition()
|
||
|
|
||
|
if ini_sys:section_exist(device_battery) then
|
||
|
alife_create_item(device_battery, db.actor, {cond = con})
|
||
|
alife_process_item(sec, obj:id(), {cond = c_zero})
|
||
|
|
||
|
-- Play effect
|
||
|
actor_effects.play_item_fx(device_battery)
|
||
|
utils_obj.play_sound("interface\\inv_batt")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Utilities
|
||
|
--------------------------------------------------------------------------------
|
||
|
function drain_device_on_event(obj, sec, idx, custom_loss)
|
||
|
if (not obj) then
|
||
|
--callstack()
|
||
|
--printe("! bind device.drain_device_on_event | no object pass")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
sec = sec or obj:section()
|
||
|
if (not dev_critical[sec]) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- Device goes off on critical power, no draining
|
||
|
local cond = obj:condition()
|
||
|
if cond < dev_critical[sec] then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local loss = dev_consumption_act[sec] and dev_consumption_act[sec][idx] or 0
|
||
|
if custom_loss then
|
||
|
loss = custom_loss
|
||
|
end
|
||
|
|
||
|
local id = obj:id()
|
||
|
if dev_event_loss[id] then
|
||
|
dev_event_loss[id] = dev_event_loss[id] + loss
|
||
|
else
|
||
|
dev_event_loss[id] = loss
|
||
|
end
|
||
|
|
||
|
return true -- if drain happened
|
||
|
end
|
||
|
|
||
|
function drain_device(obj, sec, loss, loss_add)
|
||
|
if not (obj and loss and loss ~= 0) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
sec = sec or obj:section()
|
||
|
|
||
|
-- Device goes off on critical power, no draining
|
||
|
local cond = obj:condition()
|
||
|
if cond < dev_critical[sec] then
|
||
|
--print_dbg("[%s] power is too low: %s", obj:name(), cond)
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
if loss_add then
|
||
|
loss = loss + loss_add
|
||
|
end
|
||
|
|
||
|
-- Set up new power
|
||
|
local factor = game_difficulties.get_eco_factor("battery_consumption")
|
||
|
loss = loss * factor
|
||
|
loss = loss / amper_factor
|
||
|
|
||
|
local cond_n = cond - loss
|
||
|
cond_n = clamp(cond_n, c_zero, c_full)
|
||
|
|
||
|
obj:set_condition(cond_n)
|
||
|
--print_dbg("[%s] | power loss: %s | remaining power: %s", obj:name(), loss, cond_n)
|
||
|
|
||
|
return true -- if drain happened
|
||
|
end
|
||
|
|
||
|
|
||
|
function can_toggle_torch()
|
||
|
return db.actor:item_in_slot(10) and true or false
|
||
|
end
|
||
|
|
||
|
function toggle_torch()
|
||
|
local torch = db.actor:item_in_slot(10)
|
||
|
local id = torch and torch:id()
|
||
|
if id then
|
||
|
if torch_active == id then
|
||
|
torch_active = false
|
||
|
else
|
||
|
torch_active = id
|
||
|
end
|
||
|
else
|
||
|
torch_active = false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function is_torch_active()
|
||
|
return torch_active
|
||
|
end
|
||
|
|
||
|
|
||
|
function set_nightvision_HUD(bShow)
|
||
|
local hud = get_hud()
|
||
|
local drk = hud:GetCustomStatic("nv_tunnel")
|
||
|
local wnd
|
||
|
|
||
|
if (bShow == false) or (db.actor:is_talking()) then
|
||
|
if (drk ~= nil) then
|
||
|
hud:RemoveCustomStatic("nv_tunnel")
|
||
|
drk = nil
|
||
|
end
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if (drk == nil) then
|
||
|
hud:AddCustomStatic("nv_tunnel",true)
|
||
|
drk = hud:GetCustomStatic("nv_tunnel")
|
||
|
wnd = drk:wnd()
|
||
|
if (wnd ~= nil) then
|
||
|
wnd:SetWndPos(vector2():set(nv_hud.x , nv_hud.y))
|
||
|
wnd:SetWndSize(vector2():set(nv_hud.weight , nv_hud.height))
|
||
|
wnd:SetAutoDelete(true)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if (drk ~= nil) then
|
||
|
wnd = drk:wnd()
|
||
|
local torch = db.actor:item_in_slot(10)
|
||
|
local torch_sec = torch and torch:section()
|
||
|
if torch_sec then
|
||
|
nv_eff[torch_sec] = nv_eff[torch_sec] or ini_sys:r_string_ex(torch_sec,"nv_effect")
|
||
|
local ppe = ppe_effects[nv_eff[torch_sec]]
|
||
|
if ppe then
|
||
|
--wnd:InitTexture(ppe.ui)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function set_nightvision(section,state)
|
||
|
if (not section) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
nv_eff[section] = nv_eff[section] or ini_sys:r_string_ex(section,"nv_effect")
|
||
|
|
||
|
if ppe_effects[nv_eff[section]] then
|
||
|
|
||
|
if state and (not nv_state) then
|
||
|
local ppe = ppe_effects[nv_eff[section]]
|
||
|
level.add_pp_effector(nv_eff[section] .. ".ppe", ppe.id, true)
|
||
|
utils_obj.play_sound("interface\\inv_nv_start")
|
||
|
nv_state = true
|
||
|
game.set_nv_lumfactor(ppe.lum)
|
||
|
elseif (not state) and nv_state then
|
||
|
local ppe = ppe_effects[nv_eff[section]]
|
||
|
level.remove_pp_effector(ppe.id)
|
||
|
utils_obj.play_sound("interface\\inv_nv_off")
|
||
|
nv_state = false
|
||
|
game.set_nv_lumfactor(0)
|
||
|
end
|
||
|
|
||
|
set_nightvision_HUD(nv_state)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function is_nv_active()
|
||
|
return nv_state
|
||
|
end
|
||
|
|
||
|
|
||
|
function set_pda_glitch(obj, val)
|
||
|
if (not obj) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local curr_val = obj:psy_factor()
|
||
|
obj:set_psy_factor( clamp( (curr_val + val), 0, 1 ) )
|
||
|
|
||
|
end
|
||
|
|
||
|
function is_pda_active()
|
||
|
return pda_active
|
||
|
end
|
||
|
|
||
|
function is_pda_charged(actor_only)
|
||
|
local pda = db.actor:item_in_slot(8)
|
||
|
if (not pda) then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
if actor_only and device_npc_pda[pda:section()] then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
return is_device_charged( pda )
|
||
|
end
|
||
|
|
||
|
|
||
|
function is_device_charged(obj, sec, cond)
|
||
|
sec = obj and obj:section() or sec
|
||
|
cond = obj and obj:condition() or cond
|
||
|
return sec and cond and dev_critical[sec] and cond > dev_critical[sec]
|
||
|
end
|
||
|
|
||
|
function get_power_critical(sec)
|
||
|
return dev_critical[sec]
|
||
|
end
|
||
|
|
||
|
function get_power_consumption(sec)
|
||
|
return dev_consumption[sec] and dev_consumption[sec][1]
|
||
|
end
|
||
|
|
||
|
function pda_warning()
|
||
|
--Return true if PDA is equipped and works
|
||
|
if (is_pda_charged() or is_pda_active()) then return true end
|
||
|
|
||
|
--Show not equipped or battery empty message
|
||
|
local obj = db.actor:item_in_slot(8)
|
||
|
if (obj) then
|
||
|
actor_menu.set_msg(1, game.translate_string("st_pda_no_power") , 3 )
|
||
|
else
|
||
|
actor_menu.set_msg(1, game.translate_string("st_pda_no_active") , 3 )
|
||
|
end
|
||
|
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Class "device_binder"
|
||
|
--------------------------------------------------------------------------------
|
||
|
function bind(obj)
|
||
|
obj:bind_object(device_binder(obj))
|
||
|
end
|
||
|
|
||
|
class "device_binder" (object_binder)
|
||
|
|
||
|
function device_binder:__init(obj) super(obj)
|
||
|
self.first_update = nil
|
||
|
end
|
||
|
|
||
|
local nv_glitch_state = false
|
||
|
|
||
|
function device_binder:update(delta)
|
||
|
object_binder.update(self, delta)
|
||
|
|
||
|
local obj = self.object
|
||
|
local id = obj:id()
|
||
|
local sec = obj:section()
|
||
|
local parent = obj:parent()
|
||
|
|
||
|
if not self.first_update then
|
||
|
self.first_update = true
|
||
|
|
||
|
-- Process devices belongs to others
|
||
|
if parent and (parent:id() ~= AC_ID) then
|
||
|
|
||
|
-- Process devices belongs to stalkers
|
||
|
if IsStalker(parent) then
|
||
|
|
||
|
-- Set up profiles of spawned PDA
|
||
|
if device_npc_pda[sec] and (not se_load_var(id, obj:name(), "info")) then
|
||
|
ui_pda_npc_tab.register_pda(parent, sec, id)
|
||
|
end
|
||
|
|
||
|
--[[ if stalker is not a trader, set custom power
|
||
|
if utils_obj.is_trader(parent) then
|
||
|
local cond_set = (math.random(30,70)/100)
|
||
|
alife_process_item( sec , id , {cond = cond_set} )
|
||
|
end
|
||
|
--]]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- set up stored power for device on first update
|
||
|
if dev_condition[id] then
|
||
|
obj:set_condition(dev_condition[id])
|
||
|
--print_dbg("[%s] power restored from save: %s", obj:name(), dev_condition[id])
|
||
|
end
|
||
|
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- store updated device power
|
||
|
local cond = obj:condition()
|
||
|
dev_condition[id] = cond
|
||
|
|
||
|
-- only process device power if it belongs to actor
|
||
|
if (parent and (parent:id() == AC_ID) and (self.type ~= false)) then
|
||
|
|
||
|
-- set type
|
||
|
if (self.type == nil) then
|
||
|
local cls = ini_sys:r_string_ex(sec,"class")
|
||
|
if dev_type[cls] then
|
||
|
self.type = dev_type[cls]
|
||
|
--print_dbg("[%s] type detected: %s", obj:name(), dev_type[cls])
|
||
|
else
|
||
|
self.type = false
|
||
|
--print_dbg("[%s] type not found", obj:name())
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- night-vision/light management
|
||
|
if (self.type == "TORCH") then
|
||
|
self:process_torch(id, sec, cond)
|
||
|
end
|
||
|
|
||
|
if dev_consumption[sec] and (cond > dev_critical[sec]) then
|
||
|
|
||
|
-- No need to process unequipped devices
|
||
|
if dev_slot[id] then
|
||
|
|
||
|
if (self.N_V) then
|
||
|
-- Emissions: the closer the wave, the higher the psi influnces
|
||
|
if GetEvent("surge", "state") then
|
||
|
local surge_time = GetEvent("surge", "time") or 0
|
||
|
local val_glitch = (surge_time > 168) and normalize(surge_time, 220, 168) or normalize(surge_time, 20, 168)
|
||
|
z_beefs_nvgs.nvg_glitch( clamp(val_glitch,0.0,0.9) )
|
||
|
nv_glitch_state = true
|
||
|
|
||
|
-- Psi-storms: huge spike when a vortex hits the ground
|
||
|
elseif GetEvent("psi_storm", "state") then
|
||
|
if GetEvent("psi_storm", "vortex") then
|
||
|
z_beefs_nvgs.nvg_glitch(0.9)
|
||
|
nv_glitch_state = true
|
||
|
else
|
||
|
z_beefs_nvgs.nvg_glitch(0.2)
|
||
|
nv_glitch_state = true
|
||
|
end
|
||
|
-- NVG doesn't get affected in normal cases
|
||
|
else
|
||
|
if nv_glitch_state then
|
||
|
z_beefs_nvgs.nvg_glitch(0.0)
|
||
|
nv_glitch_state = false
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Light flicker on event for flashlights
|
||
|
if (self.type == "FLASH") then
|
||
|
self:process_flicker()
|
||
|
end
|
||
|
|
||
|
-- Glitch effect on event for PDA
|
||
|
if (self.type == "PDA") then
|
||
|
self:process_glitch(id, sec, cond)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- drain device power if its active
|
||
|
self:process_power(id, sec, cond)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function device_binder:process_power(id, section, condition)
|
||
|
local power_loss = 0
|
||
|
local in_hand = false
|
||
|
|
||
|
for num, consumption in pairs(dev_consumption[section]) do
|
||
|
|
||
|
-- Device active in hand
|
||
|
if num == 1 then
|
||
|
local obj = db.actor:active_detector() or db.actor:active_item()
|
||
|
if obj and obj:id() == id then
|
||
|
power_loss = power_loss + consumption
|
||
|
in_hand = true
|
||
|
end
|
||
|
|
||
|
-- Device active in slot
|
||
|
elseif num == 2 and dev_slot[id] and (not in_hand) then
|
||
|
power_loss = power_loss + consumption
|
||
|
|
||
|
-- Device is emitting light
|
||
|
if dev_consumption_act[section] and (self.type == "TORCH") and self.object:torch_enabled() then
|
||
|
power_loss = power_loss + (dev_consumption_act[section][1] or 0)
|
||
|
end
|
||
|
|
||
|
-- Device has night vision active
|
||
|
if dev_consumption_act[section] and self.N_V and nv_state then
|
||
|
power_loss = power_loss + (dev_consumption_act[section][2] or 0)
|
||
|
end
|
||
|
|
||
|
-- Device active in inventory
|
||
|
elseif num == 3 and dev_inv[id] and (not in_hand) then
|
||
|
power_loss = power_loss + consumption
|
||
|
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Power loss on event
|
||
|
if dev_event_loss[id] and (dev_event_loss[id] > 0) then
|
||
|
power_loss = power_loss + dev_event_loss[id]
|
||
|
dev_event_loss[id] = 0
|
||
|
end
|
||
|
|
||
|
-- Consume power
|
||
|
drain_device(self.object, section, power_loss)
|
||
|
end
|
||
|
|
||
|
function device_binder:process_torch(id, section, condition)
|
||
|
|
||
|
-- Night-Vision state management
|
||
|
if nv_state then
|
||
|
if dev_slot[id] then
|
||
|
self.N_V = true -- indicator for device having active NV
|
||
|
else
|
||
|
-- if NV device moved out of slot, turn NV off
|
||
|
if self.N_V then
|
||
|
set_nightvision(section,false)
|
||
|
end
|
||
|
self.N_V = nil
|
||
|
end
|
||
|
|
||
|
-- if NV device ran out of power, turn NV off
|
||
|
if (self.N_V and (condition <= dev_critical[section])) then
|
||
|
nv_discharged = true
|
||
|
set_nightvision(section,false)
|
||
|
self.N_V = nil
|
||
|
end
|
||
|
|
||
|
-- disable NV info if NV is off
|
||
|
elseif self.N_V and (not nv_state) then
|
||
|
set_nightvision(section,false)
|
||
|
self.N_V = nil
|
||
|
end
|
||
|
|
||
|
-- Torch state management
|
||
|
if torch_active == id then
|
||
|
if dev_slot[id] and (condition > dev_critical[section]) then
|
||
|
if (not self.object:torch_enabled()) then
|
||
|
self.object:enable_torch(true)
|
||
|
end
|
||
|
else
|
||
|
if (self.object:torch_enabled()) then
|
||
|
self.object:enable_torch(false)
|
||
|
end
|
||
|
torch_active = false
|
||
|
--actor_menu.set_notification(nil, "ui_inGame2_notify_low_battery", 10)
|
||
|
end
|
||
|
else
|
||
|
if (self.object:torch_enabled()) then
|
||
|
self.object:enable_torch(false)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Torch light flicker on event
|
||
|
if self.object:torch_enabled() then
|
||
|
self:process_flicker()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function device_binder:process_glitch(id, section, condition)
|
||
|
|
||
|
-- Emissions: the closer the wave, the higher the psi influnces
|
||
|
if GetEvent("surge", "state") then
|
||
|
local surge_time = GetEvent("surge", "time") or 0
|
||
|
local val_glitch = (surge_time > 168) and normalize(surge_time, 220, 168) or normalize(surge_time, 20, 168)
|
||
|
self.object:set_psy_factor( clamp(val_glitch,0,1) )
|
||
|
|
||
|
-- Psi-storms: huge spike when a vortex hits the ground
|
||
|
elseif GetEvent("psi_storm", "state") then
|
||
|
if GetEvent("psi_storm", "vortex") then
|
||
|
self.object:set_psy_factor(1)
|
||
|
else
|
||
|
self.object:set_psy_factor(0.2)
|
||
|
end
|
||
|
|
||
|
-- PDA doesn't get affected in normal cases
|
||
|
else
|
||
|
self.object:set_psy_factor(0)
|
||
|
end
|
||
|
|
||
|
|
||
|
end
|
||
|
|
||
|
function device_binder:process_flicker(force)
|
||
|
local flicker = level_environment.get_light_flicker()
|
||
|
if force then
|
||
|
flicker = force
|
||
|
end
|
||
|
if (self.flicker == nil) then
|
||
|
self.flicker = false
|
||
|
end
|
||
|
if (self.flicker ~= flicker) then
|
||
|
--print_dbg("[%s] flicker: %s", self.object:name(), flicker)
|
||
|
if flicker then
|
||
|
local ca = "light_flicker"
|
||
|
local on_off_chance = math.random(25,50)
|
||
|
local on_off_time = math.random(0.3,0.9)
|
||
|
local fps = math.random(20,30)
|
||
|
self.object:set_color_animator(ca,true,on_off_chance,on_off_time,fps)
|
||
|
--printf("~ Torch set anim")
|
||
|
else
|
||
|
--printf("~ Torch reset anim")
|
||
|
self.object:reset_color_animator()
|
||
|
end
|
||
|
end
|
||
|
self.flicker = flicker
|
||
|
end
|
||
|
|
||
|
function device_binder:reload(section)
|
||
|
object_binder.reload(self, section)
|
||
|
end
|
||
|
|
||
|
function device_binder:reinit()
|
||
|
object_binder.reinit(self)
|
||
|
end
|
||
|
|
||
|
function device_binder:net_spawn(se_abstract)
|
||
|
if not(object_binder.net_spawn(self, se_abstract)) then
|
||
|
return false
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function device_binder:net_destroy()
|
||
|
-- if NV device disappear, turn off NV. duh?
|
||
|
if self.N_V then
|
||
|
self.N_V = nil
|
||
|
set_nightvision(self.object:section(),false)
|
||
|
end
|
||
|
|
||
|
-- if working torch device disappear, clear id
|
||
|
if torch_active == self.object:id() then
|
||
|
torch_active = false
|
||
|
end
|
||
|
|
||
|
-- if light source is flickering, turn it off
|
||
|
if self.flicker then
|
||
|
self:process_flicker(false)
|
||
|
end
|
||
|
|
||
|
--print_dbg("[%s] destroyed: %s", self.object:name())
|
||
|
object_binder.net_destroy(self)
|
||
|
end
|
||
|
|
||
|
function device_binder:save(stpk)
|
||
|
end
|
||
|
|
||
|
function device_binder:load(stpk)
|
||
|
end
|