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

502 lines
14 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--------------------------------------------------------------------
-- 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