Divergent/mods/PDA Hacking/gamedata/scripts/z_pda_hacking.script

994 lines
38 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
xml = nil
script_table = {}
local gc = game.translate_string
local parse_keys = utils_data.parse_string_keys
local debug = false
local function pr(...)
if debug then
printf(...)
end
end
faction_encyption = {
["army"] = 30 ,
["bandit"] = 0 ,
["csky"] = 20 ,
["dolg"] = 40 ,
["ecolog"] = 30 ,
["freedom"] = 20 ,
["killer"] = 40 ,
["stalker"] = 10 ,
["monolith"] = 50 ,
-- factions below this are commented out in vanilla
["renegade"] = 10 ,
["greh"] = 30 ,
["isg"] = 50,
}
colors_str = {
["decrypt"] = "%c[255, 123, 217, 76]",
["cmdup"] = "%c[255, 63, 192, 255]",
["rescan"] = "%c[255, 54, 217, 136]",
["nuke"] = "%c[255, 255, 255, 255]",
["bruteforce"] = "%c[255, 255, 255, 63]",
["skey"] = "%c[255, 0, 0, 0]",
["extract"] = "%c[255, 255, 184, 28]",
["terminal"] = "%c[255, 150, 150, 150]",
["failed"] = "%c[255, 238, 28, 36]",
}
colors_argb = {
["decrypt"] = GetARGB(255, 123, 217, 76),
["cmdup"] = GetARGB(255, 63, 192, 255),
["rescan"] = GetARGB(255, 54, 217, 136),
["nuke"] = GetARGB(255, 255, 255, 255),
["bruteforce"] = GetARGB(255, 255, 255, 63),
["skey"] = GetARGB(255, 0, 0, 0),
["extract"] = GetARGB(255, 255, 184, 28),
["failed"] = GetARGB(255, 238, 28, 36),
}
pda_rarity_encryption_mult = {
["itm_pda_common"] = 1 , ["itm_pda_uncommon"] = 1.5, ["itm_pda_rare"] = 2,
["itm_pda_encrypted_common"] = 1 , ["itm_pda_encrypted_uncommon"] = 1.5, ["itm_pda_encrypted_rare"] = 2, -- Compatibility with "Better Encryption Overhaul"
}
base_init = ui_pda_npc_tab.pda_npc_tab.InitControls
function ui_pda_npc_tab.pda_npc_tab:InitControls()
base_init(self)
xml = CScriptXmlInit()
xml:ParseFile("ui_pda_hacking.xml")
end
base_reset = ui_pda_npc_tab.pda_npc_tab.Reset
function ui_pda_npc_tab.pda_npc_tab:Reset(info)
self.form_text:Show(false)
base_reset(self, info)
if self.form_hack then
self.form_hack:Show(false)
end
if self.hack_button then
self.hack_button:Show(false)
end
if self.dark_overlay then
self.dark_overlay:Show(false)
end
RemoveTimeEvent("pdahack", "setfail")
RemoveTimeEvent("pdahack", "set_decrypt_bar")
RemoveTimeEvent("pdahack", "unlockpda")
if self.snd_skey and self.snd_skey:playing() then
self.snd_skey:stop()
end
local pda_sec = db.actor:active_item() and db.actor:active_item():section()
if not pda_sec then return end
if not (self.state == "encrypted") then return end
self.pda_info = info
if self.pda_info.attempted_hack then
self.form_text:SetText(gc("ui_st_npc_pda_access_denied") .. "\\n\\n" .. gc("ui_st_pdahacking_failed"))
return
end
self.form_text:SetText(gc("ui_st_npc_pda_access_denied"))
local best_hack_device = { ["section"] = nil, ["commands"] = 0, ["scripts"] = 0, ["extracts"] = 0}
db.actor:iterate_inventory(function(npc, item)
local sec = item:section()
local cmd_points = SYS_GetParam(2, sec, "command_points", 0)
if cmd_points > best_hack_device.commands then
best_hack_device.section = sec
best_hack_device.commands = cmd_points
best_hack_device.scripts = SYS_GetParam(2, sec, "initial_scripts", 0)
best_hack_device.extracts = SYS_GetParam(2, sec, "currency_extracts", 0)
end
end)
if best_hack_device.commands == 0 then return end
self.snd_unlock = xr_sound.get_safe_sound_object( "pda_hacking\\unlocked" )
self.snd_failed = xr_sound.get_safe_sound_object( "pda_hacking\\failed" )
self.snd_skey = xr_sound.get_safe_sound_object( "pda_hacking\\normal_hacking_sound" )
self.hack_button = self.hack_button or xml:Init3tButton("form:hack_button", self)
self:Register(self.hack_button, "hack_button")
self:AddCallback("hack_button", ui_events.BUTTON_CLICKED, self.Start_Hack, self)
self.hack_button:Show(true)
self.cmds_remaining = best_hack_device.commands --20 + (faction_encyption[info.contact.comm] / 5)
local encryption = faction_encyption[self.pda_info.contact.comm]
local rarity_mult = pda_rarity_encryption_mult[pda_sec] or 1
self.encryption_strength_base = clamp(round_idp(encryption * rarity_mult, 1), 0, 100)
self.encryption_strength = self.encryption_strength_base
self.encryption_strength_visual = self.encryption_strength_base
self.data_decrypted = 0
self.data_decrypted_target = 0
self.scripts_to_add = 0
self.extracts_remaining = best_hack_device.extracts - (self.pda_info.extracts_used or 0)
self.skey_unlock = false
self.form_hack = self.form_hack or xml:InitStatic("form", self)
self.form_hack.border = self.form_hack.border or xml:InitFrame("form:border", self.form_hack)
self.cmd_wnd = self.cmd_wnd or xml:InitStatic("form:cmd_wnd", self.form_hack)
self.cmd_wnd.title = self.cmd_wnd.title or xml:InitTextWnd("form:cmd_wnd:title", self.cmd_wnd)
self.cmd_wnd.title:SetFont(get_font("large"))
self.cmd_wnd.icon = self.cmd_wnd.icon or xml:InitStatic("form:cmd_wnd:icon", self.cmd_wnd)
self.cmd_wnd.number = self.cmd_wnd.number or xml:InitTextWnd("form:cmd_wnd:number", self.cmd_wnd)
self.cmd_wnd.number:SetText(self.cmds_remaining)
self.cmd_wnd.number:SetFont(get_font("huge"))
self.cmd_wnd.cost_line = self.cmd_wnd.cost_line or xml:InitStatic("form:cmd_wnd:cost_line", self.cmd_wnd)
self.cmd_wnd.cost_line.cost_text = self.cmd_wnd.cost_line.cost_text or xml:InitStatic("form:cmd_wnd:cost_line:cost_text", self.cmd_wnd.cost_line)
self.cmd_wnd.cost_line.icon = self.cmd_wnd.cost_line.icon or xml:InitStatic("form:cmd_wnd:cost_line:icon", self.cmd_wnd.cost_line)
self.cmd_wnd.cost_line.cost = self.cmd_wnd.cost_line.cost or xml:InitStatic("form:cmd_wnd:cost_line:cost", self.cmd_wnd.cost_line)
self.cmd_wnd.cost_line.cost_text:TextControl():SetFont(get_font("large"))
self.cmd_wnd.cost_line.cost:TextControl():SetFont(get_font("large"))
self.script_wnd = self.script_wnd or xml:InitStatic("form:script_wnd", self.form_hack)
self.script_wnd.title = self.script_wnd.title or xml:InitStatic("form:script_wnd:title", self.script_wnd)
self.script_wnd.title:TextControl():SetFont(get_font("large"))
self.script_wnd.scriptname = self.script_wnd.scriptname or xml:InitTextWnd("form:script_wnd:scriptname", self.script_wnd)
self.script_wnd.scriptname:SetFont(get_font("large"))
self.script_wnd.powertxt = self.script_wnd.powertxt or xml:InitStatic("form:script_wnd:powertxt", self.script_wnd)
self.script_wnd.powertxt:TextControl():SetFont(get_font("large"))
self.script_wnd.powernumber = self.script_wnd.powernumber or xml:InitStatic("form:script_wnd:powernumber", self.script_wnd)
self.script_wnd.powernumber:TextControl():SetFont(get_font("large"))
self.script_wnd:Show(false)
self.script_list_wnd = self.script_list_wnd or xml:InitStatic("form:script_list_wnd", self.form_hack)
self.script_list_wnd.title = self.script_list_wnd.title or xml:InitStatic("form:script_list_wnd:title", self.script_list_wnd)
self.script_list_wnd.title:TextControl():SetFont(get_font("large"))
self.hack_script_list = self.hack_script_list or xml:InitListBox("form:script_list_wnd:list", self.script_list_wnd)
self:Register(self.hack_script_list, "hack_script_list")
self:AddCallback("hack_script_list",ui_events.LIST_ITEM_CLICKED,self.OnSelectScript,self)
self.hack_script_list:RemoveAll()
self.cmd_list_wnd = self.cmd_list_wnd or xml:InitStatic("form:cmd_list_wnd", self.form_hack)
self.cmd_list_wnd.title = self.cmd_list_wnd.title or xml:InitStatic("form:cmd_list_wnd:title", self.cmd_list_wnd)
self.cmd_list_wnd.title:TextControl():SetFont(get_font("large"))
self.command_list = self.command_list or xml:InitListBox("form:cmd_list_wnd:list", self.cmd_list_wnd)
self:Register(self.command_list, "command_list")
self:AddCallback("command_list",ui_events.LIST_ITEM_CLICKED,self.OnSelectCommand,self)
self.command_list:RemoveAll()
self.cmd_list_wnd:Show(false)
self.terminal_form = self.terminal_form or xml:InitStatic("form:terminal_wnd", self.form_hack)
self.terminal = self.terminal or xml:InitStatic("form:terminal_wnd:terminal", self.terminal_form)
self.terminal.prompt_wnd = self.terminal.prompt_wnd or xml:InitTextWnd("form:terminal_wnd:terminal:prompt_wnd", self.terminal)
self.terminal.prompt_wnd:SetFont(get_font("medium"))
self.terminal.prompt_wnd:SetEllipsis(true)
self.terminal.text_wnd = self.terminal.text_wnd or xml:InitTextWnd("form:terminal_wnd:terminal:text_wnd", nil)
self.terminal.text_wnd:SetFont(get_font("medium"))
self.terminal.text_wnd:SetText(gc("ui_st_pdahacking_welcome"))
if not self.terminal_scroll then
self.terminal_scroll = xml:InitScrollView("form:terminal_wnd:terminal:terminal_scroll", self.terminal)
self.terminal_scroll:AddWindow(self.terminal.text_wnd, true)
self.terminal.text_wnd:SetAutoDelete(false)
end
self.decrypt_wnd = self.decrypt_wnd or xml:InitStatic("form:decrypt_wnd", self.form_hack)
self.decrypt_wnd.bg = self.decrypt_wnd.bg or xml:InitStatic("form:decrypt_wnd:prog_back", self.decrypt_wnd)
self.decrypt_wnd.bar = self.decrypt_wnd.bar or xml:InitProgressBar("form:decrypt_wnd:decrypt_bar", self.decrypt_wnd)
self.decrypt_wnd.bar:SetProgressPos( 0 )
self.decrypt_wnd.title = self.decrypt_wnd.title or xml:InitStatic("form:decrypt_wnd:title", self.decrypt_wnd)
self.decrypt_wnd.title:TextControl():SetFont(get_font("large"))
self.decrypt_wnd.encr_str = self.decrypt_wnd.encr_str or xml:InitStatic("form:decrypt_wnd:encr_str", self.decrypt_wnd)
self.decrypt_wnd.encr_str.icon = self.decrypt_wnd.encr_str.icon or xml:InitStatic("form:decrypt_wnd:encr_str:icon", self.decrypt_wnd.encr_str)
self.decrypt_wnd.encr_str.pct = self.decrypt_wnd.encr_str.pct or xml:InitTextWnd("form:decrypt_wnd:encr_str:str_pct", self.decrypt_wnd.encr_str)
self.decrypt_wnd.encr_str.pct:SetText( self.encryption_strength .. "%")
self.decrypt_wnd.encr_str.pct:SetFont(get_font("large"))
self.decrypt_wnd.rarity_title = self.decrypt_wnd.rarity_title or xml:InitStatic("form:decrypt_wnd:rarity_title", self.decrypt_wnd)
self.decrypt_wnd.rarity_title:TextControl():SetFont(get_font("large"))
self.decrypt_wnd.rarity_title:TextControl():SetText( gc("actor_" .. self.pda_info.contact.comm) .. ", " .. gc("st_pdahacking_rarity_" .. pda_sec) .. " " .. gc("st_pda_name"))
self.decrypt_wnd.decrypted = self.decrypt_wnd.decrypted or xml:InitStatic("form:decrypt_wnd:decrypted", self.decrypt_wnd)
self.decrypt_wnd.decrypted.title = self.decrypt_wnd.decrypted.title or xml:InitStatic("form:decrypt_wnd:decrypted:title", self.decrypt_wnd.decrypted)
self.decrypt_wnd.decrypted.title:TextControl():SetFont(get_font("large"))
self.decrypt_wnd.decrypted.pct_bg = self.decrypt_wnd.decrypted.pct_bg or xml:InitStatic("form:decrypt_wnd:decrypted:pct_bg", self.decrypt_wnd.decrypted)
self.decrypt_wnd.decrypted.pct_bg:TextControl():SetFont(get_font("huge"))
self.decrypt_wnd.decrypted.pct_bg:TextControl():SetText( round_idp(self.data_decrypted, 1) .. "%")
self.dark_overlay = self.dark_overlay or xml:InitStatic("dark_overlay", self)
self.dark_overlay.status_title = self.dark_overlay.status_title or xml:InitStatic("dark_overlay:status_title", self.dark_overlay)
self.dark_overlay.status_title:TextControl():SetFont(get_font("huge"))
self.dark_overlay:Show(false)
self.form_hack:Show(false)
-- Build table of scripts from Anomaly install. Includes both vanilla and mod scripts
script_table = {}
local temp_script_t = {} -- keep track of duplicates
for k, v in pairs(_G) do
if type(_G[k]) == "table" then
local script_name = k:gsub("_mcm", "")
script_name = script_name:gsub("^(z+_)+", "")
--script_name = script_name:gsub("^z+_", "")
if not temp_script_t[script_name] and script_name:len() >= 3 then
temp_script_t[script_name] = true
script_table[#script_table+1] = script_name
end
end
end
self:Generate_Scripts(best_hack_device.scripts)
end
base_update = ui_pda_npc_tab.pda_npc_tab.Update
function ui_pda_npc_tab.pda_npc_tab:Update()
base_update(self)
if not (self.form_hack and self.form_hack:IsShown()) then return end
if self.skey_unlock and self.dark_overlay.status_title:IsShown() then
local time = get_time_elapsed()
if round(time * 3) % 3 == 0 then
if round(time * 50) % 5 == 0 then
local scrambled_title = scramble_string(self.skey_title)
self.dark_overlay.status_title:TextControl():SetText(scrambled_title)
--self.dark_overlay.status_title:TextControl():SetTextColor(GetARGB(255, 0, 0, 0))
end
else
self.dark_overlay.status_title:TextControl():SetText(self.skey_title)
--self.dark_overlay.status_title:TextControl():SetTextColor(GetARGB(255, 55, 55, 55))
end
end
local prompt_set = false
if self.command_list then
for i = 0, self.command_list:GetSize() do
local item = self.command_list:GetItemByIndex(i)
if item and item.name == "SkeletonKey" and round(get_time_elapsed() * 55) % 5 == 0 then
item.str_title:SetText(scramble_string(gc("ui_st_pdahacking_skey") .. "#&$"))
end
if item and item:IsCursorOverWindow() then
if item.name == "SkeletonKey" then
if round(get_time_elapsed() * 50) % 5 == 0 then
self.terminal.prompt_wnd:SetText(gc("ui_st_pdahacking_preprompt") .. parse_keys(gc("ui_st_pdahacking_run"),{ ["name"] = scramble_string(gc("ui_st_pdahacking_skey"))}))
end
prompt_set = true
else
self.terminal.prompt_wnd:SetText(gc("ui_st_pdahacking_preprompt") .. parse_keys(gc("ui_st_pdahacking_run"),{ ["name"] = gc("ui_st_pdahacking_" .. name_key[item.name])}))
prompt_set = true
end
end
end
end
if self.terminal and self.terminal.prompt_wnd and not prompt_set then
local mod = round(get_time_elapsed() * 4) % 4
if mod == 0 or mod == 2 then
self.terminal.prompt_wnd:SetText(gc("ui_st_pdahacking_preprompt"))
else
self.terminal.prompt_wnd:SetText(gc("ui_st_pdahacking_preprompt_us"))
end
end
if self.hack_script_list then
for i = 0, self.hack_script_list:GetSize() do
local item = self.hack_script_list:GetItemByIndex(i)
if item and item:IsCursorOverWindow() then
self.terminal.prompt_wnd:SetText(gc("ui_st_pdahacking_preprompt") .. parse_keys(gc("ui_st_pdahacking_load"),{ ["script"] = item.name}))
end
end
end
end
ui_pda_npc_tab.pda_npc_tab.OnSelectScript = function(self)
if self.data_decrypted_target >= 100 then return end
if self.scripts_to_add > 0 then return end
if self.loaded_script and self.loaded_script.name == self.hack_script_list:GetSelectedItem().name then return end
if self.cmds_remaining <= 0 then return end
self.command_list:RemoveAll()
if self.loaded_script then
self.loaded_script.str_title:SetTextColor(GetARGB(255, 255, 255, 255))
end
self.loaded_script = self.hack_script_list:GetSelectedItem()
self.loaded_script.str_title:SetTextColor(colors_argb.rescan)
self.script_wnd.scriptname:SetText(self.loaded_script.name .. ".script")
self.script_wnd.powernumber:TextControl():SetText(self.loaded_script.power)
self.script_wnd:Show(true)
local total_power = self.loaded_script.power
math.randomseed( self.loaded_script.seed )
if math.random() <= 0.40 then
self.command_list:AddExistingItem(command_entry("CommandUp", total_power))
end
if math.random() <= 0.40 then
self.command_list:AddExistingItem(command_entry("Re-Scan", total_power))
end
if math.random() <= 0.40 then
self.command_list:AddExistingItem(command_entry("BruteForce", total_power))
end
if math.random() <= 0.40 and self.encryption_strength > 0 then
self.command_list:AddExistingItem(command_entry("Nuke", total_power, self.encryption_strength))
end
if math.random() <= 0.01 then -- 0.01
self.command_list:AddExistingItem(command_entry("SkeletonKey", total_power))
end
if math.random() <= 0.40 or self.command_list:GetSize() == 0 then
self.command_list:AddExistingItem(command_entry("Decrypt", total_power, self.encryption_strength))
end
if math.random() <= 0.10 and self.extracts_remaining > 0 and settings.extract_reward_mult > 0 then
self.command_list:AddExistingItem(command_entry("Extract", total_power, self.encryption_strength, self.encryption_strength_base))
end
self.cmd_list_wnd:Show(true)
self:Change_Commands(-1)
local text = parse_keys(gc("ui_st_pdahacking_loaded"), {["script"] = self.loaded_script.name})
self:Write_Terminal(text)
end
ui_pda_npc_tab.pda_npc_tab.OnSelectCommand = function(self)
if self.data_decrypted_target >= 100 then return end
local item = self.command_list:GetSelectedItem()
if item.name ~= "CommandUp" and self.cmds_remaining <= 0 then return end
if item.name == "Nuke" then
local nuke_effect = get_nuke_effect(item.power, self.encryption_strength)
self:Change_Encryption(nuke_effect)
self.hack_script_list:RemoveItem(self.loaded_script)
local effect_str = "%" .. colors_str.nuke .. "-" .. round_idp(nuke_effect, 1) .. "%%%" .. colors_str.terminal
self:Write_Terminal(parse_keys(gc("ui_st_pdahacking_running"), {["name"] = gc("ui_st_pdahacking_" .. name_key[item.name]), ["effect"] = effect_str}))
elseif item.name == "Decrypt" then
local decrypt_effect = get_decrypt_effect(item.power, self.encryption_strength)
if decrypt_effect <= 0 then return end
self:Set_Data_Decrypted(self.data_decrypted + decrypt_effect)
self.hack_script_list:RemoveItem(self.loaded_script)
local effect_str = "%" .. colors_str.decrypt .. "+" .. round_idp(decrypt_effect, 1) .. "%%%" .. colors_str.terminal
self:Write_Terminal(parse_keys(gc("ui_st_pdahacking_running"), {["name"] = gc("ui_st_pdahacking_" .. name_key[item.name]), ["effect"] = effect_str}))
elseif item.name == "Re-Scan" then
self.hack_script_list:RemoveItem(self.loaded_script)
local gen_count = get_rescan_count(item.power)
self:Generate_Scripts(gen_count)
self.hack_script_list:Show(true)
local effect_str = "%" .. colors_str.rescan .. "+" .. gen_count .. "%" .. colors_str.terminal
self:Write_Terminal(parse_keys(gc("ui_st_pdahacking_running"), {["name"] = gc("ui_st_pdahacking_" .. name_key[item.name]), ["effect"] = effect_str}))
elseif item.name == "CommandUp" then
local cmd_bonus = get_command_up(item.power)
self:Change_Commands(cmd_bonus)
self.hack_script_list:RemoveItem(self.loaded_script)
local effect_str = "%" .. colors_str.cmdup .. "+" .. cmd_bonus .. "%" .. colors_str.terminal
self:Write_Terminal(parse_keys(gc("ui_st_pdahacking_running"), {["name"] = gc("ui_st_pdahacking_" .. name_key[item.name]), ["effect"] = effect_str}))
elseif item.name =="BruteForce" then
local chance = get_brute_chance(item.power)
local effect_str = "%" .. colors_str.bruteforce .. round_idp(chance, 1) .. "%%%" .. colors_str.terminal
local text = parse_keys(gc("ui_st_pdahacking_running"), {["name"] = gc("ui_st_pdahacking_" .. name_key[item.name]), ["effect"] = effect_str})
if chance > (math.random() * 100) then
self:Set_Data_Decrypted(100)
text = text .. "... " .. colors_str.decrypt .. gc("ui_st_pdahacking_bruteforce_success")
else
text = text .. "... " .. colors_str.failed .. gc("ui_st_pdahacking_bruteforce_fail")
end
self:Write_Terminal(text)
self.hack_script_list:RemoveItem(self.loaded_script)
elseif item.name == "Extract" then
local extracted_ru = get_extract_money(item.power, self.encryption_strength_base)
db.actor:give_money(extracted_ru)
local header = gc("ui_st_pdahacking_extract_header")
local msg = parse_keys(gc("ui_st_pdahacking_extract_msg"), { ["income"] = extracted_ru })
xr_sound.set_sound_play(AC_ID, "pda_tips")
db.actor:give_game_news(header, msg, "ui_inGame2_PD_Sostoyatelniy_klient", 0, 5000)
self.extracts_remaining = self.extracts_remaining - 1
self.pda_info.extracts_used = self.pda_info.extracts_used and (self.pda_info.extracts_used + 1) or 1
self.hack_script_list:RemoveItem(self.loaded_script)
local effect_str = "%" .. colors_str.extract .. "+" .. gc("ui_st_pdahacking_ruble") .. extracted_ru .. "%" .. colors_str.terminal
self:Write_Terminal(parse_keys(gc("ui_st_pdahacking_running"), {["name"] = gc("ui_st_pdahacking_" .. name_key[item.name]), ["effect"] = effect_str}))
elseif item.name == "SkeletonKey" then
self:Set_Data_Decrypted(100)
self.skey_unlock = true
if self.snd_skey and settings.soundfx_volume > 0 then
self.snd_skey.volume = 1 * settings.soundfx_volume
self.snd_skey:play(db.actor, 0, sound_object.s2d)
end
self.dark_overlay:Show(true)
self.dark_overlay.status_title:Show(false)
local skey_used_before = load_var(db.actor, "pdahack_skeys_used", 0)
local str_index = skey_used_before
if str_index >= 7 then
str_index = str_index % 7
end
self.skey_title = gc("ui_st_pdahacking_skey_" .. str_index)
save_var(db.actor, "pdahack_skeys_used", skey_used_before + 1)
CreateTimeEvent("pdahack", "show_skey_status", 1, function()
local h = hit()
h.type = hit.telepatic
h.power = 0.2
h.impulse = 1
h.direction = VEC_Z
h.draftsman = db.actor
db.actor:hit(h)
self.dark_overlay.status_title:TextControl():SetTextColor(GetARGB(255, 0, 0, 0))
--self.dark_overlay.status_title:TextControl():SetTextColor(GetARGB(255, 55, 55, 55))
self.dark_overlay.status_title:TextControl():SetText(self.skey_title)
self.dark_overlay.status_title:Show(true)
return true
end)
end
self.loaded_script = nil
self.command_list:RemoveAll()
if not (item.name == "CommandUp") then
self:Change_Commands(-1)
end
self.cmd_list_wnd:Show(false)
self.script_wnd:Show(false)
--[[ pr("scroll pos " .. self.hack_script_list:GetCurrentScrollPos())
if self.hack_script_list:GetSize() * 27 > self.hack_script_list:GetCurrentScrollPos() then
self.hack_script_list:ScrollToEnd()
end ]]
if self.hack_script_list:GetSize() == 0 and self.data_decrypted_target < 100 and self.scripts_to_add <= 0 then
self:HackFailed()
end
end
function ui_pda_npc_tab.pda_npc_tab:Start_Hack()
self.form_text:Show(false)
self.hack_button:Show(false)
self.form_hack:Show(true)
self.hack_script_list:Show(true)
self.pda_info.attempted_hack = true
end
function ui_pda_npc_tab.pda_npc_tab:Change_Commands(change)
if change < 0 then
self.cmd_wnd.number:SetText(self.cmds_remaining .. " -1")
self.cmds_remaining = self.cmds_remaining + change
CreateTimeEvent("pdahack", "set_cmd_text", 0.4, function()
self.cmd_wnd.number:SetText(self.cmds_remaining)
return true
end)
local cmd_up_in_list = false
for i = 0, self.command_list:GetSize() do
local cmd_item = self.command_list:GetItemByIndex(i)
if cmd_item and cmd_item.name == "CommandUp" then
cmd_up_in_list = true
break
end
end
if self.cmds_remaining <= 0 and self.data_decrypted_target < 100 and not cmd_up_in_list then
self:HackFailed()
end
elseif change > 0 then
self.cmd_wnd.number:SetText(self.cmds_remaining .. " +" .. change)
self.cmds_remaining = self.cmds_remaining + change
CreateTimeEvent("pdahack", "set_cmd_text", 0.4, function()
self.cmd_wnd.number:SetText(self.cmds_remaining)
return true
end)
end
end
function ui_pda_npc_tab.pda_npc_tab:HackFailed()
self.script_wnd:Show(false)
self.hack_script_list:Show(false)
self.cmd_list_wnd:Show(false)
self.hack_button:Show(false)
self.form_text:SetText(gc("ui_st_npc_pda_access_denied") .. "\\n\\n" .. gc("ui_st_pdahacking_failed"))
CreateTimeEvent("pdahack", "darkoverlay", 1, function()
if self.snd_failed and settings.soundfx_volume > 0 then
self.snd_failed.volume = 0.8 * settings.soundfx_volume
self.snd_failed:play(db.actor, 0, sound_object.s2d)
end
self.dark_overlay:Show(true)
self.dark_overlay.status_title:TextControl():SetTextColor(colors_argb.failed)
if self.cmds_remaining <= 0 then
self.dark_overlay.status_title:TextControl():SetText(gc("ui_st_pdahacking_outof_cmd"))
else
self.dark_overlay.status_title:TextControl():SetText(gc("ui_st_pdahacking_outof_scripts"))
end
return true
end)
CreateTimeEvent("pdahack", "setfail", 4, function()
self:Reset(self.pda_info)
end)
end
function ui_pda_npc_tab.pda_npc_tab:Write_Terminal(text)
self.terminal_scroll:RemoveWindow(self.terminal.text_wnd)
self.terminal_scroll:Clear()
local console_text = self.terminal.text_wnd:GetText()
console_text = console_text .. "\\n" .. colors_str.terminal .. text
self.terminal.text_wnd:SetText(console_text)
local scroll_height = self.terminal_scroll:GetHeight()
self.terminal.text_wnd:AdjustHeightToText()
local term_text_height = self.terminal.text_wnd:GetHeight()
if term_text_height < scroll_height then
local width = self.terminal.text_wnd:GetWidth()
self.terminal.text_wnd:SetWndSize(vector2():set(width, scroll_height))
end
self.terminal_scroll:AddWindow(self.terminal.text_wnd, true)
self.terminal.text_wnd:SetAutoDelete(false)
self.terminal_scroll:ScrollToEnd()
end
function lerpup(start, target, step)
return start + step >= target and target or start + step
end
function ui_pda_npc_tab.pda_npc_tab:Set_Data_Decrypted(data)
self.data_decrypted_target = clamp(data, self.data_decrypted_target, 100)
if self.data_decrypted_target == 100 then
self.script_wnd:Show(false)
self.hack_script_list:Show(false)
self.pda_info.state = "default"
end
CreateTimeEvent("pdahack", "set_decrypt_bar", 0, function()
self.data_decrypted = lerpup(self.data_decrypted, self.data_decrypted_target, 0.75)
self.decrypt_wnd.bar:SetProgressPos( self.data_decrypted )
self.decrypt_wnd.decrypted.pct_bg:TextControl():SetText( round_idp(self.data_decrypted, 1) .. "%")
if self.data_decrypted >= 100 then
if not self.skey_unlock then
self.dark_overlay:Show(true)
self.dark_overlay.status_title:TextControl():SetText(gc("ui_st_pdahacking_unlocked"))
self.dark_overlay.status_title:TextControl():SetTextColor(colors_argb.decrypt)
if self.snd_unlock and settings.soundfx_volume > 0 then
self.snd_unlock.volume = 1 * settings.soundfx_volume
self.snd_unlock:play(db.actor, 0, sound_object.s2d)
end
end
local unlock_delay = self.skey_unlock and 5 or 3
CreateTimeEvent("pdahack", "unlockpda", unlock_delay, function()
self:Reset(self.pda_info)
end)
return true
elseif self.data_decrypted >= self.data_decrypted_target then
return true
end
end)
end
function lerpdown(start, target, step)
return start - step <= target and target or start - step
end
function ui_pda_npc_tab.pda_npc_tab:Change_Encryption(change)
self.encryption_strength = self.encryption_strength - change
CreateTimeEvent("pdahack", "set_encrypt", 0, function()
self.encryption_strength_visual = lerpdown(self.encryption_strength_visual, self.encryption_strength, 0.1)
self.decrypt_wnd.encr_str.pct:SetText( round_idp(self.encryption_strength_visual, 1) .. "%")
if self.encryption_strength_visual <= self.encryption_strength then
return true
end
end)
end
function ui_pda_npc_tab.pda_npc_tab:Generate_Scripts(count)
self.scripts_to_add = self.scripts_to_add + count
CreateTimeEvent("pdahack", "genscript", 0.1, function()
local index = math.random(#script_table)
local script_name = script_table[index]
table.remove(script_table, index)
local seed = get_seed_from_string(script_name)
math.randomseed( seed )
local rnd = math.random()
local min_power = settings.script_power_min --25
local max_power = clamp(settings.script_power_max, min_power, settings.script_power_max) --100
local power = (rnd * (max_power - min_power)) + min_power
power = round(power)
local item = script_message_entry(script_name, power, seed)
self.hack_script_list:AddExistingItem(item)
self.scripts_to_add = self.scripts_to_add - 1
if self.scripts_to_add <= 0 then return true end
ResetTimeEvent("pdahack", "genscript", 0.1)
end)
end
function get_time_elapsed()
return (game.get_game_time():diffSec(level.get_start_time()) /level.get_time_factor())
end
function get_seed_from_string(string)
local seed = 0
for i = 1, string:len() do
seed = seed + string.byte(string, i)
end
return seed
end
function scramble_string(str)
local letters = {}
for letter in str:gmatch'.[\128-\191]*' do
table.insert(letters, {letter = letter, rnd = math.random()})
end
table.sort(letters, function(a, b) return a.rnd < b.rnd end)
for i, v in ipairs(letters) do letters[i] = v.letter end
return table.concat(letters)
end
-- HACK FORMULAS
function get_nuke_effect(power, encryption_strength)
return encryption_strength * (power * 0.2 / 100)
end
function get_decrypt_effect(power, encryption_strength)
return power * 0.30 * (1 - (encryption_strength / 100))
end
function get_brute_chance(power)
return (power / 8)
end
function get_command_up(power)
return math.floor(power / 12) + 1
end
function get_rescan_count(power)
return math.floor(power / 20) + 1
end
function get_extract_money(power, base_encryption_strength)
return math.ceil(power * 0.2 * clamp(base_encryption_strength, 20, 100)) * settings.extract_reward_mult
end
-- GUI
class "script_message_entry" (CUIListBoxItem)
function script_message_entry:__init(name, power, seed) super(name, power, seed)
self:GetTextItem():SetText("")
self.name = name
self.power = power
self.seed = seed
math.randomseed( seed + 1 )
self.hidden_power = math.random(0, 75)
local total_w = 155
local padding_h = 3
local padding_w = padding_h * 0.75
local target_h = 30 - padding_h
local icon_h = target_h - (padding_h * 2)
local icon_w = icon_h --[[ * 0.75 ]]
self.background = self:AddIconField(1)
self.background:InitTexture("ui_pdahack_bg_list")
self.background:SetStretchTexture(true)
self.background:SetTextureColor(GetARGB(60, 255, 255, 255))
self.background:SetWndPos(vector2():set(0, padding_h))
self.background:SetWndSize(vector2():set(total_w, target_h))
self.icon = self:AddIconField(1)
self.icon:InitTexture("ui_pdahack_icon_script")
self.icon:SetStretchTexture(true)
self.icon:SetTextureColor(colors_argb.rescan)
self.icon:SetWndPos(vector2():set(padding_w, padding_h * 2))
self.icon:SetWndSize(vector2():set(icon_w, icon_h))
self.str_title = self:AddTextField("",1)
self.str_title:SetWndPos(vector2():set(icon_w + padding_w * 2, padding_h))
self.str_title:SetWndSize(vector2():set(total_w - (padding_w * 2) - icon_w, target_h))
self.str_title:SetTextColor(GetARGB(255, 255, 255, 255))
self.str_title:SetFont(get_font("large"))
self.str_title:SetEllipsis(true)
self.str_title:SetTextAlignment(0)
self.str_title:SetVTextAlignment(2)
self.str_title:SetText(self.name .. ".script")
self.power_nr_bg = self:AddIconField(1)
self.power_nr_bg:InitTexture("ui_pdahack_bg_list")
self.power_nr_bg:SetStretchTexture(true)
self.power_nr_bg:SetTextureColor(GetARGB(120, 0, 0, 0))
self.power_nr_bg:SetWndPos(vector2():set(total_w + padding_w, padding_h))
self.power_nr_bg:SetWndSize(vector2():set(25 - padding_w, target_h))
self.power_nr = self:AddTextField("",1)
self.power_nr:SetWndPos(vector2():set(total_w, padding_h + 1))
self.power_nr:SetWndSize(vector2():set(25, target_h))
self.power_nr:SetTextColor(colors_argb.decrypt)
self.power_nr:SetFont(get_font("large"))
self.power_nr:SetTextAlignment(2)
self.power_nr:SetVTextAlignment(2)
self.power_nr:SetText(self.power)
end
class "command_entry" (CUIListBoxItem)
function command_entry:__init(name, power, encrypt_strength, base_encr_str) super(name, power, encrypt_strength, base_encr_str)
self:GetTextItem():SetText("")
self.name = name
self.power = power
local total_w = 154
local padding_h = 3
local padding_w = padding_h * 0.75
local target_h = 30 - padding_h
local icon_h = target_h - (padding_h * 2)
local icon_w = icon_h --[[ * 0.75 ]]
self.background = self:AddIconField(1)
self.background:InitTexture("ui_pdahack_bg_list")
self.background:SetStretchTexture(true)
self.background:SetTextureColor(GetARGB(60, 255, 255, 255))
self.background:SetWndSize(vector2():set(total_w, target_h))
self.background:SetWndPos(vector2():set(0, padding_h))
self.icon_hack = self:AddIconField(1)
self.icon_hack:InitTexture("ui_pdahack_icon_hack")
self.icon_hack:SetStretchTexture(true)
self.icon_hack:SetTextureColor(GetARGB(255, 191, 191, 191))
self.icon_hack:SetWndSize(vector2():set(icon_w, icon_h))
self.icon_hack:SetWndPos(vector2():set(padding_w, padding_h * 2))
self.str_title = self:AddTextField("",1)
self.str_title:SetWndRect(Frect():set(icon_w + padding_w * 2, padding_h, total_w - padding_w * 2 - icon_w, target_h))
self.str_title:SetTextColor(GetARGB(255, 255, 255, 255))
self.str_title:SetFont(get_font("large"))
self.str_title:SetWndSize(vector2():set(total_w - padding_w * 2 - icon_w, target_h))
self.str_title:SetEllipsis(true)
self.str_title:SetTextAlignment(0)
self.str_title:SetVTextAlignment(2)
self.str_title:SetText( gc("ui_st_pdahacking_" .. name_key[name]) )
self.bonus_text = self:AddTextField("",1)
self.bonus_text:SetWndPos(vector2():set(total_w * 0.65 + target_h * 0.5 * 0.75, padding_h))
self.bonus_text:SetWndSize(vector2():set(total_w * 0.35 - target_h * 0.5 * 0.75, target_h))
self.bonus_text:SetFont(get_font("large"))
self.bonus_text:SetTextAlignment(2)
self.bonus_text:SetVTextAlignment(2)
self.bonus_icon = self:AddIconField(1)
self.bonus_icon:SetStretchTexture(true)
self.bonus_icon:SetWndPos(vector2():set(total_w * 0.65, target_h * 0.25 + padding_h))
self.bonus_icon:SetWndSize(vector2():set(target_h * 0.5 * 0.75, target_h * 0.5))
if name == "Nuke" then
self.bonus_text:SetText("-" .. round_idp(get_nuke_effect(power, encrypt_strength), 1) .. "%" )
self.bonus_icon:InitTexture("ui_pdahack_icon_shield")
self.bonus_text:SetTextColor(colors_argb.nuke)
self.bonus_icon:SetTextureColor(colors_argb.nuke)
elseif name == "Decrypt" then
self.bonus_icon:Show(false)
self.bonus_text:SetWndPos(vector2():set(total_w * 0.65, padding_h))
self.bonus_text:SetWndSize(vector2():set(total_w * 0.35, target_h))
self.bonus_text:SetTextAlignment(2)
self.bonus_text:SetText("+" .. round_idp(get_decrypt_effect(power, encrypt_strength), 1) .. "%" )
self.bonus_text:SetTextColor(colors_argb.decrypt)
elseif name == "Re-Scan" then
self.bonus_text:SetText("+" .. get_rescan_count(power) )
self.bonus_icon:InitTexture("ui_pdahack_icon_script")
self.bonus_icon:SetWndPos(vector2():set(total_w * 0.65 - (target_h / 16), target_h * 0.25 + padding_h))
self.bonus_icon:SetWndSize(vector2():set(target_h * 0.5, target_h * 0.5))
self.bonus_icon:SetTextureColor(colors_argb.rescan)
self.bonus_text:SetTextColor(colors_argb.rescan)
elseif name == "CommandUp" then
self.bonus_text:SetText("+" .. get_command_up(power) )
self.bonus_icon:InitTexture("ui_pdahack_icon_cmd")
self.bonus_text:SetTextColor(colors_argb.cmdup)
self.bonus_icon:SetTextureColor(colors_argb.cmdup)
elseif name == "BruteForce" then
self.bonus_text:SetText( round_idp(get_brute_chance(power), 1) .. "%" )
self.bonus_icon:InitTexture("ui_pdahack_icon_dice")
self.bonus_text:SetTextColor(colors_argb.bruteforce)
self.bonus_icon:SetTextureColor(colors_argb.bruteforce)
elseif name == "SkeletonKey" then
self.bonus_text:Show(false)
self.str_title:SetText(scramble_string(gc("ui_st_pdahacking_skey") .. "#&$"))
self.str_title:SetTextColor(colors_argb.skey)
self.icon_hack:SetTextureColor(colors_argb.skey)
elseif name == "Extract" then
self.bonus_text:SetFont(get_font("medium"))
self.bonus_text:SetText("+" .. gc("ui_st_pdahacking_ruble") .. get_extract_money(power, base_encr_str) )
self.bonus_icon:InitTexture("ui_pdahack_icon_money")
self.bonus_text:SetTextColor(colors_argb.extract)
self.bonus_icon:SetTextureColor(colors_argb.extract)
end
end
fonts = {
["high_res"] = {
["medium"] = GetFontGraffiti22Russian(),
["large"] = GetFontLetterica25(),
["huge"] = GetFontGraffiti50Russian();
},
["normal_res"] = {
["medium"] = GetFontLetterica18Russian(),
["large"] = GetFontGraffiti22Russian(),
["huge"] = GetFontLetterica25(),
}
}
name_key = {
["CommandUp"] = "cmdup",
["Decrypt"] = "decrypt",
["Re-Scan"] = "rescan",
["Nuke"] = "nuke",
["BruteForce"] = "bruteforce",
["SkeletonKey"] = "skey",
["Extract"] = "extract",
}
function get_font(font)
pr( device().width .. "x" .. device().height)
if device().height > 1080 and device().width > 1920 then
return fonts["high_res"][font]
else
return fonts["normal_res"][font]
end
end
-- Hack Device spawning
drop_table = {
["pda_hack_usb_1"] = {
["novice"] = 0.00,
["trainee"] = 0.005,
["experienced"] = 0.01,
["professional"] = 0.01,
["veteran"] = 0.005,
["expert"] = 0.00,
["master"] = 0.00,
["legend"] = 0.00,
},
["pda_hack_usb_2"] = {
["novice"] = 0.00,
["trainee"] = 0.00,
["experienced"] = 0.00,
["professional"] = 0.005,
["veteran"] = 0.01,
["expert"] = 0.01,
["master"] = 0.005,
["legend"] = 0.00,
},
["pda_hack_usb_3"] = {
["novice"] = 0.00,
["trainee"] = 0.00,
["experienced"] = 0.00,
["professional"] = 0.00,
["veteran"] = 0.00,
["expert"] = 0.005,
["master"] = 0.01,
["legend"] = 0.02,
},
}
function npc_drop_hack_device(npc, npc_rank, npc_comm)
for sec, rank_table in pairs(drop_table) do
local chance = rank_table[npc_rank] * settings.hack_device_drop_mult
--[[ if debug and table[npc_rank] > 0 then
chance = 1
end ]]
if (math.random() < chance) then
alife_create_item(sec, npc, nil)
end
end
end
SpawnCosmetics = death_manager.spawn_cosmetics
function death_manager.spawn_cosmetics(npc, npc_id, npc_comm, npc_rank, visual, rand_condition)
SpawnCosmetics(npc, npc_id, npc_comm, npc_rank, visual, rand_condition)
npc_drop_hack_device(npc, npc_rank, npc_comm)
end
-- Lower hack device sell price
function on_get_item_cost(kind, obj, profile, calculated_cost, ret)
if profile.mode == 1 and drop_table[obj:section()] then -- when selling
ret.new_cost = math.floor((ret.new_cost or calculated_cost) * 0.25)
end
end
-- MCM multipliers
if ui_pda_npc_tab.faction_encyption and ui_pda_npc_tab.stash_factor_per_pda then
base_register_pda = ui_pda_npc_tab.register_pda
function ui_pda_npc_tab.register_pda(npc, pda_sec, pda_id)
local temp_encr_tbl = dup_table(ui_pda_npc_tab.faction_encyption)
if settings.locked_chance_mult ~= 1 then
for comm, encr in pairs(ui_pda_npc_tab.faction_encyption) do
ui_pda_npc_tab.faction_encyption[comm] = encr * settings.locked_chance_mult
end
end
local temp_stash_factor_tbl = dup_table(ui_pda_npc_tab.stash_factor_per_pda)
if settings.stash_chance_mult ~= 1 then
for sec, factor in pairs(ui_pda_npc_tab.stash_factor_per_pda) do
ui_pda_npc_tab.stash_factor_per_pda[sec] = factor * settings.stash_chance_mult
end
end
base_register_pda(npc, pda_sec, pda_id)
ui_pda_npc_tab.faction_encyption = dup_table(temp_encr_tbl)
ui_pda_npc_tab.stash_factor_per_pda = dup_table(temp_stash_factor_tbl)
end
else
printf("-PDA Hacking mod: Error modifying locked PDA/stash chance. Please update your Modded Exes")
end
-- MCM
function load_defaults()
local t = {}
local op = pda_hacking_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("pda_hacking/" .. k)
end
end
return settings
end
function on_game_start()
RegisterScriptCallback("actor_on_first_update", load_settings)
RegisterScriptCallback("on_option_change", load_settings)
RegisterScriptCallback("on_get_item_cost", on_get_item_cost)
end