Divergent/mods/Weapon Parts Overhaul/gamedata/scripts/zzzz_arti_jamming_repairs.s...

1081 lines
33 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
local ini_parts = itms_manager.ini_parts
local disassembly_chance
local max_con_obj = 0.999
local min_con_obj = 0.001
local spare_parts = utils_data.collect_section(ini_parts,"weapons_spare_parts")
local parts_info = {}
ini_drop = wpo_loot.ini_drops
parts_list = a_wpo_parts.parts_list
local ini_custom_repair = ini_file("repair\\importer.ltx")
gc = game.translate_string
has_parts = arti_jamming.has_parts
sec_has_parts = arti_jamming.sec_has_parts
is_part = arti_jamming.is_part
print_dbg = arti_jamming.print_dbg
print_ws = ui_workshop.print_ws
get_config = a_arti_jamming_mcm.get_config
math_floor = math.floor
math_random = math.random
current_id = arti_jamming.current_id
reset_cgd = arti_jamming.reset_cgd
SetTip = ui_workshop.SetTip
is_barrel = arti_jamming.is_barrel
is_trigger = arti_jamming.is_trigger
-- below here is all mostly copied from item repair and workshop, due to difficulty of patching
-- monkey patches
EvaluateParts = item_parts.evaluate_parts
function item_parts.evaluate_parts(obj)
local sec = obj:section()
sec = SYS_GetParam(0, sec,"parent_section") or sec
local id = obj:id()
local parts = ini_parts:r_string_ex("con_parts_list",sec)
if parts then
parts = str_explode(parts,",")
local data = se_load_var(id, obj:name(), "parts")
-- premature terminate if data exists
if has_parts(obj) and data then return
else
EvaluateParts(obj)
end
end
end
-- original functions are local reeeee
local function disassembly_weapon_spare_parts(sec, condition)
local single_handed = ini_sys:r_float_ex(sec,"single_handed") or 1
local weight = ini_sys:r_float_ex(sec,"inv_weight") or 2
local num,finale = 0,0
num = math_floor(weight)
if (single_handed == 1) then num = 1 end
for i=1,num do
if (math.random(100) < condition) then
finale = finale + 1
end
end
return finale
end
local function timer_disassembly_weapon(npc_id, result_details, result_conditions, name)
-- Who is item owner? we must give them the parts
local npc = npc_id and get_object_by_id(npc_id)
if (not npc) then
printf("~ item_parts timer | no owner found")
return
end
-- Send messages to the actor
if #result_details > 0 then
local parts_text = item_parts.create_disassemble_list(result_details)
actor_menu.set_item_news('success', 'weapon_ammo', "st_dis_text_7", name, game.translate_string('st_dis_text_9'), parts_text)
else
actor_menu.set_item_news('fail', 'weapon', "st_dis_text_2", name)
end
-- Creating parts in inventory
for i=1,#result_details do
if result_conditions[i] and result_conditions[i] > 0 then
local se_result = alife_create(result_details[i], npc:position(), npc:level_vertex_id(), npc:game_vertex_id(), npc:id(), false)
local data_result = utils_stpk.get_item_data(se_result)
data_result.condition = clamp( (result_conditions[i]/100) , min_con_obj , max_con_obj )
utils_stpk.set_item_data(data_result,se_result)
alife():register(se_result)
else
alife_create_item(result_details[i], npc)
end
end
return true
end
DisassemblyWeapon = item_parts.disassembly_weapon
function item_parts.disassembly_weapon(obj, obj_d)
if has_parts(obj) then
custom_disassembly_weapon(obj, obj_d)
else
DisassemblyWeapon(obj, obj_d)
end
end
function custom_disassembly_weapon(obj, obj_d)
-- Defines
local id = obj:id()
local se_obj = alife_object(id)
local sec = obj:section()
local sec_p = ini_sys:r_string_ex(sec,"parent_section") or sec
local name = ui_item.get_sec_name(sec)
local con = obj:condition()
local parts = item_parts.get_parts_con(obj, nil, true)
local npc = obj:parent()
if (not npc) then
printf("~ item_parts | no owner found for [%s]", obj:name())
return
end
local result_details = {}
local result_conditions = {}
local get_ammo = {}
local with_scope = nil
local delay = actor_effects.is_animations_on() and 3 or 0
-- Collect attachments
with_scope = utils_item.has_scope(sec)
if with_scope then
with_scope = string.format('_%s', utils_item.has_scope(sec))
table.insert(result_details, with_scope:sub(2))
sec = sec:gsub(with_scope, "")
end
if (obj:weapon_scope_status() == 2) and (utils_item.addon_attached(obj,"sc")) then
local scope_section = utils_data.read_from_ini(nil,sec,"scopes_sect","string",nil)
local scope = utils_data.read_from_ini(nil,scope_section,"scope_name","string",nil)
table.insert(result_details, scope)
end
if (obj:weapon_silencer_status() == 2) and (utils_item.addon_attached(obj,"sl")) then
local sil = utils_data.read_from_ini(nil,sec,"silencer_name","string",nil)
table.insert(result_details, sil)
end
if (obj:weapon_grenadelauncher_status() == 2) and (utils_item.addon_attached(obj,"gl")) then
local gl = utils_data.read_from_ini(nil,sec,"grenade_launcher_name","string",nil)
table.insert(result_details, gl)
end
-- Unload mag and get ammo
mag_support.eject_mag(obj)
-- Collect weapon parts
for k,v in pairs(parts) do
if (k ~= sec_p) and is_part(k) and v > 0 then
local index = #result_details
result_details[index + 1] = k
result_conditions[index + 1] = IsItem("part",k) and utils_item.get_cond_static(v) or v
end
end
-- Collect weapon spare parts
for i=1,#spare_parts do
local num = disassembly_weapon_spare_parts(sec, disassembly_chance/2)
if (num > 0) then
for j=1,num do
table.insert(result_details,spare_parts[i])
end
end
end
-- Collect installed upgrades
local installed_upgrades = utils_item.get_upgrades_installed(obj)
local upgr_tools = {}
for i=1,#installed_upgrades do
local tool = utils_item.get_upgrade_sect_tool(sec, installed_upgrades[i])
table.insert(upgr_tools,tool)
end
for i=1,#upgr_tools do
if math.random(100) < con*50 then
table.insert(result_details,upgr_tools[i])
end
end
-- Release weapon
alife_release(se_obj)
-- Degrade disassemble tool
local diss_tools = GetItemList("disassemble")
local degr_val = diss_tools[obj_d:section()]
utils_item.degrade( obj_d , degr_val )
-- Increase Statistic
game_statistics.increment_statistic("items_disassembled")
item_parts.clear_parts_con(id)
-- Play animation
actor_effects.play_item_fx("disassemble_metal_fast")
-- Process
CreateTimeEvent(0,"delay_disassembly" .. id, delay, timer_disassembly_weapon, npc:id(), result_details, result_conditions, name)
end
-- here we go again
WorkshopReplacePart = ui_workshop.UIWorkshopRepair.ReplacePart
function ui_workshop.UIWorkshopRepair:ReplacePart()
local obj = self.CC["inventory"]:GetCell_Selected(true)
if (not obj) then
return
end
if not has_parts(obj) then
WorkshopReplacePart(self)
return
end
local obj_part = self.CC["parts"]:GetCell_Selected(true)
if not (obj_part and self.selected_btn) then
return
end
self.itm_selected[self.selected_btn]:InitTexture("ui_button_inv_t")
print_ws("- UIWorkshopRepair:ReplacePart() | picked replacement part (%s) [%s] - condition: %s", obj_part:id(), obj_part:section(), obj_part:condition())
-- Save the condition of selected part
local new_part_con = math.ceil(obj_part:condition()*100)
self.new_con[self.selected_btn] = {}
self.new_con[self.selected_btn].id = obj_part:id()
self.new_con[self.selected_btn].con = new_part_con
-- Calculate individual condition and update text
self.btn_repair:Enable(true)
local cnt = 0
for i=1,6 do
if self.parts[i] and self.parts[i].sec and (self.parts[i].sec ~= "na") then
local t = self.parts[i].con
if self.new_con[i] then
t = self.new_con[i].con
end
local clr = utils_xml.get_color_con(t)
local str = t < 0 and "[X]" or t .. "%"
self.itm_con[i]:SetText(clr .. str)
end
end
-- Calculate total condition and update text
local tot_con = clamp(math.ceil(obj:condition() * 100),0,100)
self.itm_con_r:SetText(tot_con .. "%")
-- Update remaining toolkit count
-- weapon_parts_overhaul do not consume toolkit charges for swapping parts
--self:UpdateToolkits(active)
-- Hide part inventory
self.CC["parts"]:Reset()
utils_obj.play_sound("interface\\items\\inv_items_cloth_" .. tostring(math.random(2,3)))
SetTip("repair_tip_4", nil, nil, nil, true, self.info_text)
-- Reset
self.selected_btn = nil
self.highlight_btn = nil
end
WorkshopPartScheme = ui_workshop.UIWorkshopRepair.ListPartScheme
function ui_workshop.UIWorkshopRepair:ListPartScheme()
WorkshopPartScheme(self)
for i=1,6 do
if self.parts[i] and self.parts[i].sec and (self.parts[i].sec ~= "na") and self.parts[i].con < 0 then
self.itm_con[i]:SetText(utils_xml.get_color("d_red") .. "[X]")
end
end
end
WorkshopRepair = ui_workshop.UIWorkshopRepair.Repair
function ui_workshop.UIWorkshopRepair:Repair()
local obj = self.CC["inventory"]:GetCell_Selected(true)
if (not obj) then
return
end
if not has_parts(obj) then
print_dbg("Repair: Not weapon, going through original route")
WorkshopRepair(self)
return
end
local tot_con = 0
local cnt = 0
if is_not_empty(self.new_con) then
for i=1,6 do
if self.parts[i] and self.parts[i].sec and (self.parts[i].sec ~= "na") then
if self.new_con[i] and self.new_con[i].id then
-- weapon_parts_overhaul refund parts
local temp = self.parts[i].con
self.parts[i].con = self.new_con[i].con
print_dbg("- UIWorkshopRepair:Repair() | replaced part spotted | part: %s - condition: %s - order: %s - id: %s", self.parts[i].sec, self.new_con[i].con, i, self.new_con[i].id)
-- Release replacement parts
if temp < 0 then
alife_release_id(self.new_con[i].id)
else
local part = get_object_by_id(self.new_con[i].id)
part:set_condition(temp/100)
end
-- weapon_parts_overhaul end
end
tot_con = tot_con + self.parts[i].con --AdjustCon(self.parts[i].sec, self.parts[i].con, #self.parts, IsOutfit(self.object))
cnt = cnt + 1
print_ws("/ UIWorkshopRepair:Repair() | total condition calculation | part: %s - condition: %s - order: %s - total sum: %s", self.parts[i].sec, self.parts[i].con, i, tot_con)
end
end
else
print_ws("! UIWorkshopRepair:Repair() | no new parts have been replaced")
return
end
-- Discharge tools
for i=1,#self.toolkit_pick do
local obj_tool = level.object_by_id(self.toolkit_pick[i])
if obj_tool then
utils_item.discharge(obj_tool)
print_ws("/ UIWorkshopRepair:Repair() | discharged toolkit (%s)", self.toolkit_pick[i])
else
printe("!ERROR UIWorkshopRepair:Repair() | can't discharge toolkit with id (%s). Object not found!", self.toolkit_pick[i])
end
end
-- Apply condition changes
local id = obj:id()
local sec = obj:section()
sec = ini_sys:r_string_ex(sec,"parent_section") or sec -- for weapons with scopes
local final_con = (cnt > 0) and (clamp(math.ceil(tot_con/cnt),1,100)/100)
local weapon = level.object_by_id(id)
if weapon and final_con and (final_con >= 0) and (final_con <= 1) then
-- weapon_parts_overhaul do not update condition
if not has_parts(weapon) then
weapon:set_condition(final_con)
print_ws("- UIWorkshopRepair:Repair() | object with id (%s) is set to a new condition: %s", id, final_con)
end
else
printe("! UIWorkshopRepair:Repair() | object with id (%s) is either not found. Or didn't register new condition (%s)!", id, final_con)
end
local result_part_tbl = {}
result_part_tbl[sec] = has_parts(weapon) and math.ceil(weapon:condition() * 100) or math.ceil(tot_con/cnt)
for i=1,#self.parts do
result_part_tbl[self.parts[i].sec] = self.parts[i].con
end
item_parts.set_parts_con(id, result_part_tbl)
for k,v in pairs(result_part_tbl) do
print_dbg("~ UIWorkshopRepair:Repair() | item's new part table [%s] = %s",k,v)
end
-- Effect
actor_effects.play_item_fx("craft_dummy")
self:Close()
end
--[[
0 = clean only
1 = clean parts (req. basic)
2 = full service
]]
-- check the existing weapon and what services are available for it
function analyze_weapon(npc, wpn)
local parts = item_parts.get_parts_con(wpn, nil, true)
local to_do = {}
for k,v in pairs(parts) do
if not string.find(k, "prt_w") or v > 98 then
-- weapon part or something, do nothing
elseif v >= 60 then
-- also add pricing
to_do[#to_do + 1] = {
part = k,
name = get_part_short_name(k),
action = "clean",
cost = get_service_cost(wpn:section())
}
elseif npc:object(k) ~= nil and v < 60 then
to_do[#to_do + 1] = {
part = k,
name = get_part_short_name(k),
action = "replace",
cost = get_service_cost(wpn:section(), k)
}
end
end
return to_do
end
-- up to 5 buttons, one for each thing
-- create all buttons on the left that hides everything
-- button_sort_tab_6
function ui_inventory.UIInventory:InitRepairPartsButton()
self.repair_xml = CScriptXmlInit()
self.repair_xml:ParseFile("ui_wpo_repair.xml")
self.repair_parts_togglebtn = self.repair_xml:Init3tButton("button", self.npc_up_dialog)
self:Register(self.repair_parts_togglebtn, "repair_parts_toggle")
-- to the left of the repair button
self.repair_parts_togglebtn:SetWndPos(vector2():set(
self.npc_up_repair:GetWndPos().x - self.repair_parts_togglebtn:GetWidth(),
self.npc_up_repair:GetWndPos().y
))
self:AddCallback("repair_parts_toggle", ui_events.BUTTON_CLICKED, self.repair_parts_toggle, self)
self.repair_parts_togglebtn:Show(false)
-- bg
self.repair_parts_bkg = self.repair_xml:InitStatic("background", self.npc_up_dialog)
self:Register(self.repair_parts_bkg, "repair_parts_bkg")
self.repair_parts_bkg:SetWndPos(vector2():set(
self.repair_parts_togglebtn:GetWndPos().x,
self.repair_parts_togglebtn:GetWndPos().y
))
self.repair_parts_buttons = {}
self.repair_parts_texts = {}
self.repair_parts_info = {} -- this holds the info the buttons use
starting_y = self.repair_parts_togglebtn:GetWndPos().y + self.repair_parts_togglebtn:GetHeight()
for i=1,5 do
self.repair_parts_buttons[i] = self.repair_xml:Init3tButton("button2", self.npc_up_dialog)
self:Register(self.repair_parts_buttons[i], "repair_parts_buttons"..i)
self.repair_parts_texts[i] = self.repair_xml:InitTextWnd("txt_box", self.npc_up_dialog)
self:Register(self.repair_parts_texts[i], "repair_parts_texts"..i)
self.repair_parts_buttons[i]:SetWndPos(vector2():set(
self.repair_parts_togglebtn:GetWndPos().x,
starting_y
))
self.repair_parts_texts[i]:SetWndPos(vector2():set(
self.repair_parts_togglebtn:GetWndPos().x + self.repair_parts_buttons[i]:GetWidth(),
starting_y
))
starting_y = starting_y + self.repair_parts_buttons[i]:GetHeight()
end
self:AddCallback("repair_parts_buttons1", ui_events.BUTTON_CLICKED, self.repair_part1, self)
self:AddCallback("repair_parts_buttons2", ui_events.BUTTON_CLICKED, self.repair_part2, self)
self:AddCallback("repair_parts_buttons3", ui_events.BUTTON_CLICKED, self.repair_part3, self)
self:AddCallback("repair_parts_buttons4", ui_events.BUTTON_CLICKED, self.repair_part4, self)
self:AddCallback("repair_parts_buttons5", ui_events.BUTTON_CLICKED, self.repair_part5, self)
-- message box
self.message_box_parts = CUIMessageBoxEx()
self:Register(self.message_box_parts, "mbpr")
self:AddCallback("mbpr", ui_events.MESSAGE_BOX_YES_CLICKED, self.repair_part_confirm, self)
self:AddCallback("mbpr", ui_events.MESSAGE_BOX_NO_CLICKED, self.Discard, self)
end
function ui_inventory.UIInventory:repair_parts_purge()
self.repair_parts_info = {}
self.repair_parts_bkg:Show(false)
for i=1,5 do
self.repair_parts_buttons[i]:Enable(false)
self.repair_parts_buttons[i]:Show(false)
self.repair_parts_texts[i]:SetText("")
self.repair_parts_texts[i]:Show(false)
end
end
-- show/hide the five small buttons
function ui_inventory.UIInventory:repair_parts_toggle()
-- show and hide the five sub-buttons
if (not self.upgr.id) then return end
local wpn = level.object_by_id(self.upgr.id)
if (not wpn) then
return
end
-- hide all buttons
for i=1,5 do
self.repair_parts_buttons[i]:Enable(false)
self.repair_parts_buttons[i]:Show(false)
self.repair_parts_texts[i]:SetText("")
self.repair_parts_texts[i]:Show(false)
end
if self.repair_parts_bkg:IsShown() then
self.repair_parts_bkg:Show(false)
return
else
self.repair_parts_bkg:Show(true)
end
local actions = analyze_weapon(mob_trade.GetTalkingNpc(), wpn)
if is_empty(actions) then return end
self.repair_parts_info = actions
for k,v in pairs(self.repair_parts_info) do
self.repair_parts_texts[k]:SetText(v.action .. " " .. game.translate_string("st_name_"..v.name))
self.repair_parts_texts[k]:Show(true)
self.repair_parts_buttons[k]:Enable(true)
self.repair_parts_buttons[k]:Show(true)
end
end
function ui_inventory.UIInventory:repair_part1()
self:repair_part(1)
end
function ui_inventory.UIInventory:repair_part2()
self:repair_part(2)
end
function ui_inventory.UIInventory:repair_part3()
self:repair_part(3)
end
function ui_inventory.UIInventory:repair_part4()
self:repair_part(4)
end
function ui_inventory.UIInventory:repair_part5()
self:repair_part(5)
end
function ui_inventory.UIInventory:repair_part(ind)
local wpn = level.object_by_id(self.upgr.id)
if (not wpn) then
return
end
-- calculate cost and part
local part = self.repair_parts_info[ind].action == "replace" and self.repair_parts_info[ind].part
local cost = self.repair_parts_info[ind].cost
local str = game.translate_string("st_wpn_service_cost").." " .. cost .. " RU"
if db.actor:money() >= cost then
self.repair_part_idx = ind
-- cost will be xxx
self.message_box_parts:InitMessageBox("message_box_yes_no")
else
self.message_box_parts:InitMessageBox("message_box_ok")
end
self.message_box_parts:SetText(str)
self.message_box_parts:ShowDialog(true)
-- reset ui
end
function ui_inventory.UIInventory:repair_part_confirm()
local wpn = level.object_by_id(self.upgr.id)
if (not wpn) then
printf("No weapon")
return
end
local parts = item_parts.get_parts_con(wpn, nil, true)
local part_to_fix = self.repair_parts_info[self.repair_part_idx]
if part_to_fix.action == "replace" then
local tech = mob_trade.GetTalkingNpc()
local tech_part = tech:object(part_to_fix.part)
alife_release_id(tech_part:id())
end
parts[part_to_fix.part] = 99
item_parts.set_parts_con(self.upgr.id, parts)
local cost = part_to_fix.cost
db.actor:give_money(-cost)
reset_cgd()
-- reset
self.repair_part_idx = nil
self:repair_parts_purge()
self:repair_parts_toggle()
end
-- B
InitItem = ui_inventory.UIInventory.RMode_InitItem
function ui_inventory.UIInventory:RMode_InitItem(obj, bag, idx)
InitItem(self, obj, bag, idx)
-- initial creation
if not self.repair_parts_togglebtn then self:InitRepairPartsButton() end
-- purge
local is_wep = IsWeapon(obj) and sec_has_parts(obj:section())
-- re render
self:repair_parts_purge()
self.repair_parts_togglebtn:Show(is_wep)
end
-- Close = ui_inventory.UIInventory.Close
-- function ui_inventory.UIInventory:Close()
-- self:repair_parts_purge()
-- Close(self)
-- end
HowMuch = inventory_upgrades_mp.how_much_repair
function inventory_upgrades_mp.how_much_repair( item_name, item_condition )
local base_cost = HowMuch(item_name, item_condition)
local cof = sec_has_parts(item_name) and 0.25 or 1
return math_floor(base_cost * cof)
end
local clean_costs = {
["pistol"] = 950,
["shotgun"] = 1050,
["rifle_5"] = 1200,
["rifle_7"] = 1500,
}
function get_service_cost(item_name, part)
local cof = game_difficulties.get_eco_factor("repair") or 1.67
cof = 0.5 + cof/2
local repair_type = SYS_GetParam(0, item_name, "repair_type")
if not part then
return cof * (clean_costs[repair_type])
else
-- pay 1.5x cleaning cost plus part
-- should lock behind toolkit? maybe later
return cof * (1.5 * clean_costs[repair_type] + (parts_list[part] or 0))
end
end
quality_map = {
[0] = "broken",
[1] = "damaged",
[2] = "worn",
[3] = "dirty",
}
unique_mapping = {
["prt_w_gas_tube_9"] = "extractor",
["prt_w_gas_tube_10"] = "extractor",
["prt_w_gas_tube_8"] = "ejector",
["prt_w_gas_tube_2"] = "pump",
["prt_w_bolt_11"] = "hammers",
["prt_w_bolt_12"] = "hammers",
["prt_w_bolt_carrier_8"] = "chock",
["prt_w_bolt_carrier_9"] = "chock",
}
name_mapping = {
"spring",
"bolt_carrier",
"bolt",
"gas_tube",
"barrel",
"trigger"
}
--Patching ui_item.script
local string_find = string.find
local math_ceil = math.ceil
local gc = game.translate_string
local clr_r = utils_xml.get_color("d_red")
local clr_o = utils_xml.get_color("d_orange")
local clr_y = utils_xml.get_color("yellow")
local clr_g = utils_xml.get_color("d_green")
local clr_p = utils_xml.get_color("d_purple")
function get_part_short_name(name)
if not string_find(name, "prt_w") then return "" end
local i = 1
local part_name = ""
if unique_mapping[name] then
part_name = unique_mapping[name]
else
while part_name == "" and i < 7 do
-- print_dbg("it %s, name %s", i, name)
if string_find(name, name_mapping[i]) then
part_name = name_mapping[i]
end
i = i + 1
end
end
return part_name
end
function process_part(name, condition)
local severity = condition == -1 and "missing" or math_floor(condition / 20)
local clr = clr_o
if condition > 79 then clr = clr_g
elseif condition > 59 then clr = clr_y
elseif condition == -1 then clr = clr_r end
local part_name = get_part_short_name(name)
if part_name == "" then return "" end
print_dbg("part %s, category %s, sev %s", name, part_name, severity)
return " " .. clr .. gc("st_dot") .. " " .. gc("st_damage_"..severity) .. " " .. gc("st_name_"..part_name) .. "\\n"
end
local clr_o = utils_xml.get_color("d_orange")
local clr_1 = utils_xml.get_color("ui_gray_2")
local clr_2 = utils_xml.get_color("ui_gray_1")
original_build_desc_footer = ui_item.build_desc_footer
function ui_item.build_desc_footer(obj, sec, str)
str = original_build_desc_footer(obj, sec, str)
local threshold_repair = ini_custom_repair:section_exist(sec) and ini_custom_repair:r_float_ex(sec, "repair_threshold") or 0
if threshold_repair > 0 then
str = str .. clr_o .. " ".. gc("st_dot") .." " .. clr_1 .. " " .. gc("st_clean_half") .. ": " .. clr_2 .. " " .. tostring(math_ceil(threshold_repair*100)) .. "% \\n"
end
return str
end
original_build_desc_header = ui_item.build_desc_header
function ui_item.build_desc_header(obj, sec, str)
local _str = ""
local _str2 = original_build_desc_header(obj, sec, str)
if get_config("display") and obj and IsWeapon(obj) and not IsAmmo(obj) and arti_jamming.has_parts(obj) then
local parts = item_parts.get_parts_con(obj, nil, true)
local display_str = ""
for k,v in pairs(parts) do
display_str = display_str .. process_part(k, v)
end
if display_str ~= "" then
display_str = " " .. clr_p .. gc("st_name_issues") .. "\\n" .. display_str
_str = _str .. display_str
end
end
_str = _str .. _str2
return _str
end
-- cheap lookup to map repair types to toolkits for weapons
local toolkit_map = {
["pistol"] = {"cleaning_kit_p","toolkit_p"},
["shotgun"] = {"cleaning_kit_s","toolkit_s"},
["rifle_5"] = {"cleaning_kit_r5","toolkit_r5"},
["rifle_7"] = {"cleaning_kit_r7","toolkit_r7"},
}
-- check for suitable repair kit as well as if weapon is suitable to have parts replaced
function has_suitable_kit(obj)
local repair_sec = SYS_GetParam(0, obj:section(), "repair_type") or "none"
local toolkit_sec = toolkit_map[repair_sec] or "none"
print_dbg("checking suitability for %s, repair %s", obj:section(), repair_sec)
local has_clean = db.actor:object(toolkit_sec[1]) or db.actor:object("cleaning_kit_u")
local has_repair = db.actor:object(toolkit_sec[2])
if not (has_clean or has_repair) then return false end
local parts = item_parts.get_parts_con(obj)
local allow = false
for k,v in pairs(parts) do
if is_part(k) then
-- allow for repair
if has_repair and v < 98 then
allow = true
-- allow for clean
elseif v >= 60 and has_clean then
allow = true
end
end
end
return allow
end
function get_suitable_kit(obj, clean)
local repair_sec = SYS_GetParam(0, obj:section(), "repair_type") or "none"
local toolkit_sec = toolkit_map[repair_sec] or "none"
if clean then
return db.actor:object(toolkit_sec[1]) or db.actor:object("cleaning_kit_u")
else return db.actor:object(toolkit_sec[2]) end
end
function replace_name(name, clean)
local part_name = ""
if unique_mapping[name] then
part_name = unique_mapping[name]
else
if not string_find(name, "prt_w") then return "" end
local i = 1
while part_name == "" and i < 7 do
-- print_dbg("it %s, name %s", i, name)
if string_find(name, name_mapping[i]) then
part_name = name_mapping[i]
end
i = i + 1
end
end
local repair_logic = get_config("repairkit") and "st_wpo_replace" or "st_wpo_repair"
return (clean and gc("st_wpo_clean") or gc(repair_logic)) .. " " .. gc("st_name_"..part_name)
end
function replace_part(id, part, kit_name, clean)
print_dbg("replacing part %s on item %s", part, id)
local kit = db.actor:object(kit_name)
local parts = se_load_var(id, nil, "parts")
for k,v in pairs(parts) do
print_dbg(k..": "..v)
end
if parts and parts[part] then
if clean then
parts[part] = 99
news_manager.send_tip(db.actor, gc("st_news_cleaned") .. " " .. ui_item.get_sec_name(part), nil, "swiss_knife", 6000)
else
parts[part] = get_config("repairkit") and 99 or clamp(parts[part] + 25, 1, 99)
news_manager.send_tip(db.actor, gc("st_news_parts_repair_success") .. " " .. ui_item.get_sec_name(part).. " to " ..parts[part].."%", nil, "swiss_knife", 6000)
end
utils_item.discharge(kit)
se_save_var(id, nil, "parts", parts)
-- restore condition too
local wpn = level.object_by_id(id)
wpn:set_condition(clamp(wpn:condition() + 0.1, 0, 0.999) )
if id == current_id() then
reset_cgd()
end
end
GetActorMenu():UpdateItems()
GetActorMenu():UpdateSlots()
end
local maint_gui
function check_maintain(obj)
return has_parts(obj) and has_suitable_kit(obj)
end
function name_maintain(obj)
return gc("st_wpo_replace_parts")
end
function init_maintenance_menu(obj)
local context_str = {}
local context_action = {}
local context_params = {}
local parts = item_parts.get_parts_con(obj)
local clean_kit = get_suitable_kit(obj, true)
local repair_kit = get_suitable_kit(obj, false)
for k,v in pairs(parts) do
if is_part(k) then
if v < 60 and repair_kit then
table.insert(context_str, replace_name(k, false))
table.insert(context_action, "replace_part")
table.insert(context_params, {obj:id(), k, repair_kit:section(), false})
elseif v >= 60 and v < 98 then
if clean_kit then
table.insert(context_str, replace_name(k, true))
table.insert(context_action, "clean_part")
table.insert(context_params, {obj:id(), k, clean_kit:section(), true})
else
table.insert(context_str, replace_name(k, true))
table.insert(context_action, "replace_part")
table.insert(context_params, {obj:id(), k, repair_kit:section(), true})
end
end
end
end
if not maint_gui then maint_gui = utils_ui_custom.UICellPropertiesCustom(replace_part) end
maint_gui:Reset(GetCursorPosition(), context_action, context_str, context_params)
ui_inventory.GUI:PlaySND(sound_object([[interface\inv_properties_2]]))
end
-- custom parts replacement shit
function can_remove(sec)
if is_part(sec) then
if is_barrel(sec) and not get_config("easybarrel") then
elseif is_trigger(sec) and not get_config("easytrigger") then
else
return true
end
end
return false
end
function name_fieldstrip(obj)
return gc("st_field_strip")
end
local allowed_modes = {
["inventory"] = true,
["loot"] = true
}
local allowed_bags = {
["actor_equ"] = true,
["actor_belt"] = true,
["actor_bag"] = true,
["npc_bag"] = true,
}
function has_parts_fieldstrip(wpn, bag, mode)
if not (allowed_modes[mode] and allowed_bags[bag]) then
return false
end
if not has_parts(wpn) then return false end
local parts = item_parts.get_parts_con(wpn, nil, true)
local has_parts = false
for k,v in pairs(parts) do
if v > 0 and can_remove(k) then has_parts = true end
end
return has_parts
end
function remove_name(name)
local part_name = ""
if not name then
part_name = "all"
elseif unique_mapping[name] then
part_name = unique_mapping[name]
else
if not string_find(name, "prt_w") then return "" end
local i = 1
while part_name == "" and i < 7 do
-- print_dbg("it %s, name %s", i, name)
if string_find(name, name_mapping[i]) then
part_name = name_mapping[i]
end
i = i + 1
end
end
return gc("st_wpo_remove") .. " " .. gc("st_name_"..part_name)
end
local fs_gui
function init_fieldstrip_menu(obj)
local context_str = {}
local context_action = {}
local context_params = {}
local parts = item_parts.get_parts_con(obj)
local num = 0
for k,v in pairs(parts) do
if can_remove(k) and v > 0 then
table.insert(context_str, remove_name(k))
table.insert(context_action, "remove_part")
table.insert(context_params, {obj:id(), k, v})
num = num + 1
end
end
-- remove all
if num > 1 then
table.insert(context_str, remove_name(nil))
table.insert(context_action, "remove_part")
table.insert(context_params, {obj:id(), k, v})
end
if not fs_gui then fs_gui = utils_ui_custom.UICellPropertiesCustom(act_fieldstrip) end
fs_gui:Reset(GetCursorPosition(), context_action, context_str, context_params)
ui_inventory.GUI:PlaySND(sound_object([[interface\inv_properties_2]]))
end
-- remove the given part
-- if no part is specified, remove all
function act_fieldstrip(id, part_name)
print_dbg("Try remove %s from %s", part_name, id)
local parts = se_load_var(id, nil, "parts")
local parts_removed = false
local str_msg = gc("st_news_fieldstrip")
-- eject mag if applicable
print_dbg("Before eject")
mag_support.eject_mag(get_object_by_id(id))
print_dbg("Removed ammo")
if part_name and parts[part_name] then
str_msg = str_msg .. "\\n - " .. ui_item.get_sec_name(part_name)
remove_part(part_name, parts)
parts_removed = true
else
for k,v in pairs(parts) do
if can_remove(k) and v ~= -1 then
str_msg = str_msg .. "\\n - " .. ui_item.get_sec_name(k)
remove_part(k, parts)
parts_removed = true
end
end
end
item_parts.set_parts_con(id, parts)
if arti_jamming.get_jammed(id) then
set_jam_status(id, nil)
end
-- send msg
if parts_removed then
news_manager.send_tip(db.actor, str_msg, nil, "swiss_knife", 6000)
end
reset_cgd()
end
function remove_part(part_name, part_list)
print_dbg("Removing part %s", part_name)
if part_list[part_name] and part_list[part_name] ~= -1 then
local part_to_spawn = alife_create(part_name, db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(), db.actor:id(), false)
local data = utils_stpk.get_item_data(part_to_spawn)
data.condition = part_list[part_name]/100
utils_stpk.set_item_data(data,part_to_spawn)
alife():register(part_to_spawn)
part_list[part_name] = -1
end
end
-- SECTION field stripping and part replacement --
-- condition under which parts cannot be field replaced
local REPLACE_THRESHOLD = 0.2
function try_replace_part(wpn, tbl, part, using_tool)
local pid = part:id()
local wid = wpn:id()
local sec = part:section()
local pcon = part:condition()
local wcon = wpn:condition()
if is_barrel(sec) and not get_config("easybarrel") then
news_manager.send_tip(db.actor, gc("st_news_parts_barrel"), nil, "swiss_knife", 6000)
elseif is_trigger(sec) and not get_config("easytrigger") then
news_manager.send_tip(db.actor, gc("st_news_parts_trigger"), nil, "swiss_knife", 6000)
elseif (wcon < REPLACE_THRESHOLD) then
news_manager.send_tip(db.actor, gc("st_news_parts_rusty"), nil, "swiss_knife", 6000)
else
print_dbg("Replacing %s", sec)
local tmp = tbl[sec]
tbl[sec] = math_floor(pcon * 100)
if tmp == -1 then
-- part missing, do not swap conditions
alife_release_id(pid)
else
part:set_condition(tmp/100)
end
item_parts.set_parts_con(wid, tbl)
if (wid == current_id()) then
reset_cgd()
end
-- lose a little condition when replacing parts
if wcon > 0.6 then
local range = using_tool and 3 or 13
wpn:set_condition(wcon - (math_random(3, 3 + range)/100))
end
mag_support.eject_mag(wpn)
news_manager.send_tip(db.actor, gc("st_news_parts_success") .. " " .. ui_item.get_sec_name(sec), nil, "swiss_knife", 6000)
end
end
local function on_item_drag_dropped(obj_1, obj_2, slot_from, slot_to)
if not (slot_from == EDDListType.iActorBag and (slot_to == EDDListType.iActorBag or slot_to == EDDListType.iActorSlot)) then
return
end
local part = obj_1 and obj_1:section() or nil
local wpn = obj_2 and obj_2:section() or nil
print_dbg("Dragging %s onto %s", part, wpn)
if is_part(part) and has_parts(obj_2) then
local parts = item_parts.get_parts_con(obj_2, nil, true)
if parts and parts[part] then
try_replace_part(obj_2, parts, obj_1, item_parts.get_suitable_dtool(obj_2))
else
news_manager.send_tip(db.actor, gc("st_news_parts_incompatible"), nil, "swiss_knife", 6000)
end
end
end
RepairOnItemSelect = item_repair.UIRepair.OnItemSelect
function item_repair.UIRepair:OnItemSelect(n)
local obj_1 = self.CC[1]:GetCell_Selected(true) -- item to be fixed
local obj_n = self.CC[n]:GetCell_Selected(true) -- support item
if not (obj_1 and obj_n) then
return
end
local cond_1 = obj_1:condition()
local sec_r = self.obj:section()
local threshold_repair = ini_custom_repair:section_exist(sec_r) and ini_custom_repair:r_float_ex(sec_r, "repair_threshold") or 0
print_dbg("Cond of item is %s, threshold %s", cond_1, threshold_repair)
local repair_add = a_repair_override.get_prop(sec_r,"repair_add_condition", 2) or 0
self.add_condition = threshold_repair < cond_1 and repair_add or repair_add/2
RepairOnItemSelect(self, n)
end
function on_game_start()
disassembly_chance = ini_parts:r_float_ex("settings","disassembly_chance") or 40
custom_functor_autoinject.add_functor("arti_fieldstrip", has_parts_fieldstrip, name_fieldstrip, nil, init_fieldstrip_menu, true)
custom_functor_autoinject.add_functor("arti_maintain", check_maintain, name_maintain, nil, init_maintenance_menu, false)
RegisterScriptCallback("ActorMenu_on_item_drag_drop",on_item_drag_dropped)
end