Divergent/mods/Zone Customization Project/gamedata/scripts/bind_awr.script

502 lines
14 KiB
Plaintext
Raw Permalink Normal View History

--------------------------------------------------------------------
-- Reworked by Tonex
-- Last edit: 2019/5/18
-- Adapted new changes for the system
-- Cleaned a lot of bloated code that was previously used for updating vice meshes
--------------------------------------------------------------------
local MDATA = {}
local settings_list = {}
local angles_t = {}
local mech_list_key = {}
local debug_mode = false
local debug_show_tables = false
local ini_manager, settings, mesh_list, delay, ui
-- Cache vice and their lamps in table to reuse
local vice_id = {}
local lamp_id = {}
function access(obj) --| Access to a vice, taking into account possible death of the mechanic
if not (obj) then
return
end
--// You should be able to use any workshop in Warfare mode
if _G.WARFARE or (smr_amain_mcm.get_config("smr_enabled") and smr_stalkers_mcm.get_config("base_population") == "sim_smr_none") then
return true
end
local name = obj:name()
dout(nil, "Request access for %s", name)
--// Mechanic is dead - access is unlimited
if angles_t[name] then
if angles_t[name][3] == 'dead' then
dout(nil, "Mechanic is dead. Full access")
return true
end
end
--// Access was granted by a mechanic.
if db.actor:has_info(string.format("awr_%s_access",angles_t[name][1])) then
dout(nil, "Mechanic gave access")
return true
end
--// Debugging
local npc = get_story_object(angles_t[name][1])
if npc then
if npc:alive() then
dout(nil, "Access not granted. NPC %s by id %s is alive", angles_t[name][1], npc:id())
else
dout(nil, "![ERROR] NPC %s by id %s bugged, because he is online and is not alive, but function OnDeath was not called", angles_t[name][1], npc:id())
end
end
end
function OnDeath(npc) --| Callback at the death of a mechanic
dout(nil, "Called for NPC, %s", npc:section())
if IsStalker(npc) and (not npc:alive()) then
local npc_s = npc:section()
dout(nil, "NPC %s exist and dead", npc:section())
for key, val in pairs(angles_t) do
if angles_t[key][1] == npc_s then
local story_obj = get_story_object(angles_t[key][1])
if story_obj then
level.map_remove_object_spot(story_obj:id(), "ui_pda2_mechanic_location")
end
--// Add a marker
SetMarker(key)
full_access(key,npc_s)
break
end
end
end
end
function SetMarker(name) --| The function of adding a marker to the map
local id = name and vice_id[name]
local s_obj = id and alife_object(id)
if s_obj then
dout(nil, "Add marker on parent object %s with ID %s", name, s_obj.id)
level.map_add_object_spot_ser(s_obj.id, "ui_pda2_mechanic_location", "st_mech_tiski")
end
end
function CloseDl() -- The closing function of the UI AWR at the end of time (provided that the UI is called and the actor is within 2 meters of the mesh)
if (ui) and ui:IsShown() then
dout(nil, "UI exist and already open")
ui:Close()
end
end
--=======================================< Callbacks >=======================================--
function physic_object_on_use_callback(_obj,who) -- Binder function using vise mesh
if not (_obj) or not (string.match(_obj:name(), "awr")) then
return
end
--// The condition for the "parent" vise, spawn through all.spawn
if access(_obj) and string.match(_obj:name(), "awr_tiski") and _obj:position():distance_to(db.actor:position()) < 1.5 then
--// Checking the table for "empty"
r_unused()
--// Gather mechanic info
local flag_1,flag_2,flag_3,flag_4 = false,false,false,false
local name = _obj:name()
local mechanic = name and angles_t[name] and angles_t[name][1]
if mechanic then
flag_1 = db.actor:has_info(mechanic .. "_upgrade_tier_1")
flag_2 = db.actor:has_info(mechanic .. "_upgrade_tier_2")
flag_3 = db.actor:has_info(mechanic .. "_upgrade_tier_3")
local drugkit_done = ini_manager:r_string_ex("drugkit_access",mechanic)
if drugkit_done and db.actor:has_info(drugkit_done) then
flag_4 = true
end
end
local function start_ui()
--// Call UI
local hud = get_hud()
--if ui then
--ui:HideDialog()
--end
ui = ui_workshop and ui_workshop.get_workshop_ui(hud, mechanic, {flag_1,flag_2,flag_3,flag_4,false})
if (ui) then
dout(nil, "call UI")
ui:ShowDialog(true)
end
return true
end
local delay = actor_effects.is_animations_on() and 2 or 0
actor_effects.play_item_fx("workshop_dummy")
CreateTimeEvent(0,"delay_workshop",delay,start_ui)
end
end
function actor_on_first_update() --| Callback of the first Update actor. It is executed one-time after loading, _after_ spawn of all objects from all.spawn, unlike on_game_load
local smatch = string.match
local sim = alife()
for i=1, 65534 do
local s_obj = sim:object(i)
if s_obj then
local name = s_obj:name()
--// If the vise is spawned all.spawn - save their values in the store
if smatch(name, '%w+%_awr%_tiski%_%d+') then
--// Cache vice id
vice_id[name] = i
angles_t[name] = l_v(name, angles_t[name])
if debug_mode then
printf("/ Registered vice [%s] = %s", name, i)
end
--// Cache vice lamps IDs
elseif smatch(name, '_awr_lamp') then
lamp_id[name] = i
if debug_mode then
printf("/ Registered vice lamp [%s] = %s", name, i)
end
end
end
end
--// Turn on lamps for all mechanics with a dead flag, otherwise it should be off
for key, val in pairs(angles_t) do
--// Mechanic is dead -> grant full acces + turn on lamp
if angles_t[key][3] == 'dead' then
dout('actor_on_first_update', "%s is dead. Enable lamp(s) which assigned for %s", angles_t[key][1], key)
Lamp(angles_t[key][1], true)
full_access(key,npc_s)
--// Mechanic is unmarked for death -> turn off lamp
else
dout('actor_on_first_update', "%s is alive. Disable lamp(s) which assigned for %s", angles_t[key][1], key)
Lamp(angles_t[key][1], false)
--[[
// If Mechanic isn't around, grant unlimited access
local npc_s = angles_t[key][1]
local se_npc = get_story_se_object(npc_s)
if (not se_npc) then
full_access(key,npc_s)
end
--]]
end
if db.actor:has_info(string.format('awr_%s_access', angles_t[key][1])) then
dout('actor_on_first_update', "%s gave access. Enable lamp(s) which assigned for %s", angles_t[key][1], key)
Lamp(angles_t[key][1], true)
end
end
--// Remove extra tables for weapons whose parts have not been replaced.
r_unused()
end
function npc_on_death_callback(victim, who) -- Callback, NPC caused by death
if not (victim and who) then
return
end
local name = victim:section()
local killer_name
if mech_list_key[name] then
dout('npc_on_death_callback', "NPC %s was killed by %s", victim:name(), who:name())
OnDeath(victim)
if IsStalker(who) then
killer_name = who:character_name()
else
killer_name = nil
end
if who:id() == AC_ID then
local alife = alife()
local se_actor = alife:actor()
killer_name = se_actor:character_name()
end
actor_menu.set_item_news('success', 'npc', "st_awr_dead_mechanic", victim:character_name(), killer_name or game.translate_string("st_by_unknown"))
else
-- if IsStalker(victim) then
-- awr_sf.dout('npc_on_death_callback', "NPC %s is not in list -> return", victim:name()) -- Отрабатывает для всех смертей, включать при необходимости
-- end
return
end
end
function save_state(m_data)
m_data.workshop = MDATA
end
function load_state(m_data)
MDATA = m_data.workshop or {}
end
function on_game_start()
ini_manager = itms_manager.ini_manager
settings = utils_data.collect_section(ini_manager,"workshop_settings")
mesh_list = utils_data.collect_section(ini_manager,"workshop_angles")
--for _, k in ipairs(settings) do
--settings_list[k] = ini_manager:r_float_ex("workshop_settings", k)
--end
for _, k in ipairs(mesh_list) do
local t = parse_list(ini_manager,"workshop_angles", k)
angles_t[k] = {}
for _, v in ipairs(t) do
table.insert(angles_t[k], v)
end
end
--// We enter data into the table keys for quick indexing by key, without using a loop
for key, val in pairs(angles_t) do
mech_list_key[val[1]] = 0
end
RegisterScriptCallback("physic_object_on_use_callback", physic_object_on_use_callback)
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
RegisterScriptCallback("npc_on_death_callback", npc_on_death_callback)
RegisterScriptCallback("save_state", save_state)
RegisterScriptCallback("load_state", load_state)
end
--=======================================< Utility >=======================================--
function Lamp(npc_name, state) --| Toggle online workshop lamps on/off
for lamp_name,id in pairs(lamp_id) do
if string.match(lamp_name, string.format('_awr_lamp_%s', npc_name)) then
local se_lamp = alife_object(id)
if se_lamp then
local lamp = level.object_by_id(id)
if lamp then
if (state == true) then
lamp:get_hanging_lamp():turn_on()
dout(nil, "Lamp %s was turned on", lamp_name)
elseif (state == false) then
lamp:get_hanging_lamp():turn_off()
dout(nil, "Lamp %s was turned off", lamp_name)
end
end
end
end
end
end
function full_access(vice,npc_s) --| Give unlimited access to a workshop
--// 'Dead' flag in the table
angles_t[vice][3] = 'dead'
--// Delete information if, before the death of a mechanic, he had access
if db.actor:has_info(string.format('awr_%s_access', npc_s)) then
db.actor:disable_info_portion(string.format('awr_%s_access', npc_s))
dout(nil, 'NPC is dead. Infoportion awr_%s_access has been removed', npc_s)
end
--// Issuance of information
db.actor:give_info_portion(string.format("awr_%s_dead", npc_s))
--// Turn on the lamps
Lamp(npc_s, true)
--// Store data
s_v(vice, angles_t[vice])
end
function s_v(name,val) --|
--// Функция сохранения данных в Store
dout(nil, "Save data to Store, table %s on value %s", name, val)
MDATA[name] = val
if debug_show_tables then print_table(MDATA, 'On Save') end
end
function l_v(name, def) --|
--// Функция загрузки данных из Store
local function len(t)
local i = 0
for _ in pairs(t) do i = i + 1 end
return i
end
dout(nil, 'Trying to load %s from Store', name)
local m_data = alife_storage_manager.get_state()
if MDATA[name] and len(MDATA[name]) > 0 then
dout(nil, "Table %s with %s keys was loaded", name, len(MDATA[name]))
if debug_show_tables then print_table(MDATA, 'On Load') end
return MDATA[name] or def
else
dout(nil, 'Table %s does not exist or is empty. Skipped', name)
end
return def or nil
end
function r_unused() --|
--// Функция удаления "пустых" таблиц с замененными деталями для оружия и флагами
local chk = 0
dout(nil, 'Searching unused tables in AWR Store table...')
if MDATA then
for k, _ in pairs(MDATA) do
if k:match('_upg') then
dout(nil, 'Checking %s table...', k)
local count = 0
for key, val in pairs(MDATA[k]) do
count = count + val
end
if count == 5 then
local flags = string.format('%s%s', k:gsub('[^%d+]', ''), '_flags')
dout(nil, '%s table have default values, tables %s and %s will be removed', k, k, flags)
MDATA[k] = nil
MDATA[flags] = nil
chk = chk + 2
else
dout(nil, 'Data in table %s is used. Skipped', k)
end
end
end
if chk == 0 then
dout(nil, 'AWR Store table have no unused tables')
else
dout(nil, 'Removed %s tables', chk)
end
else
dout(nil, 'AWR table does not exist')
end
if debug_show_tables then print_table(MDATA, 'On Remove Unused') end
end
function dout(call,fmt,...) --|
--// Функция отладочного вывода (включен при debug_mode = true в awr_settings.ltx)
if not (debug_mode) then return end
if not (fmt) then return end
local fmt = tostring(fmt)
--// Пытаемся определить из какой функции произошел вызов целевой функции
local caller_n = debug.getinfo(3, "n") and debug.getinfo(3, "n").name or "not specified"
local f_name = debug.getinfo(2, "n") and debug.getinfo(2, "n").name or "not specified"
if call then
caller_n = tostring(call)
end
if (select('#',...) >= 1) then
local i = 0
local p = {...}
local function sr(a)
i = i + 1
if (type(p[i]) == 'userdata') then
if (p[i].x and p[i].y) then
return vec_to_str(p[i])
end
return 'userdata'
end
return tostring(p[i])
end
fmt = string.gsub(fmt,"%%s",sr)
end
if (log) then
local str = string.format('[AWR]{%s->%s} %s', caller_n, f_name, fmt)
log(str)
--exec_console_cmd("flush")
else
exec_console_cmd("load ~#debug msg:"..str)
end
end
function print_table(tbl,header,format_only) --|
--// Функция для вывода содержимого таблицы в строковом виде
local txt = header and ("-- " .. tostring(header) .. "\n{\n\n") or "{\n\n"
local depth = 1
local function tab(amt)
local str = ""
for i=1,amt, 1 do
str = str .. "\t"
end
return str
end
local function table_to_string(tbl)
local size = 0
for k,v in pairs(tbl) do
size = size + 1
end
local key
local i = 1
for k,v in pairs(tbl) do
if (type(k) == "number") then
key = "[" .. k .. "]"
elseif (type(k) == "function" or type(k) == "string" or type(k) == "boolean" or type(k) == "table") then
key = "[\""..tostring(k) .. "\"]"
else
key = "[____unknown_type]"
end
if (type(v) == "table") then
txt = txt .. tab(depth) .. key .. " =\n"..tab(depth).."{\n"
depth = depth + 1
table_to_string(v,tab(depth))
depth = depth - 1
txt = txt .. tab(depth) .. "}"
elseif (type(v) == "number" or type(v) == "boolean") then
txt = txt .. tab(depth) .. key .. " = " .. tostring(v)
elseif (type(v) == "userdata") then
if (v.diffSec) then
local Y, M, D, h, m, s, ms = 0,0,0,0,0,0,0
Y, M, D, h, m, s, ms = v:get(Y, M, D, h, m, s, ms)
txt = strformat("%s%s%s = { Y=%s, M=%s, D=%s, h=%s, m=%s, s=%s, ms=%s } ",txt,tab(depth),key,Y, M, D, h, m, s, ms)
else
txt = txt .. tab(depth) .. key .. " = \"userdata\""
end
elseif (type(v) == "function") then
txt = txt .. tab(depth) .. key .. " = \"" .. tostring(v) .. "\""
elseif (type(v) == "string") then
txt = txt .. tab(depth) .. key .. " = '" .. v .. "'"
else
txt = txt .. tab(depth) .. key
end
if (i == size) then
txt = txt .. "\n"
else
txt = txt .. ",\n"
end
i = i + 1
end
end
table_to_string(tbl)
txt = txt .. "\n}"
if (format_only) then
return txt
end
printf(txt)
local file = io.open("gamedata\\awr_table.txt","a+")
file:write(txt.."\n\n")
file:close()
end