1810 lines
47 KiB
Plaintext
1810 lines
47 KiB
Plaintext
--[[
|
|
Quick Action Wheel
|
|
Author: HarukaSai
|
|
04/01/2024
|
|
]]
|
|
|
|
local XMLOP = CScriptXmlInit()
|
|
XMLOP:ParseFile("ui_haru_wheel_option.xml")
|
|
wheel_states = haru_arm.States
|
|
GUI = nil
|
|
TAB_MANAGER = nil
|
|
MCM_SETTINGS = {}
|
|
|
|
override_quick_keys = {
|
|
[61] = 4,
|
|
[62] = 5
|
|
}
|
|
|
|
add_to_qaw_functor = {
|
|
"add_to_qaw",
|
|
function(obj, bag, mode) -- cond
|
|
return (not TAB_MANAGER.dynamic_tabs[TAB_MANAGER.current_tab]) and TAB_MANAGER:CanAddItem(obj, obj:section())
|
|
end,
|
|
function(obj, bag, mode) -- name
|
|
return "ui_haru_add_item_to_qaw"
|
|
end,
|
|
nil,
|
|
function(obj) -- function
|
|
TAB_MANAGER:AddItem(TAB_MANAGER.current_tab, obj, obj:section())
|
|
end
|
|
}
|
|
|
|
-- defaults for dynamic item tabs
|
|
tab_categories = {[2] = "devices", [3] = "attachments", [4] = "food", [5] = "meds", [6] = "grenades", [7] = "ammo", [8] = "slots"}
|
|
|
|
-- cache sound objects
|
|
local snd_attach_addon = sound_object([[interface\inv_attach_addon]])
|
|
local snd_detach_addon = sound_object([[interface\inv_detach_addon]])
|
|
|
|
-- HarukaSai: special function to init elements with proper scaling automatically
|
|
function InitEx(xml, type, path, parent, scale, offset)
|
|
local element = xml["Init" .. type](xml, path, parent)
|
|
|
|
local pos = element:GetWndPos()
|
|
|
|
scale = scale or {["w"] = 1, ["h"] = 1}
|
|
offset = offset or {["x"] = 0, ["y"] = 0}
|
|
|
|
local ratio = (device().height / device().width) / 0.75
|
|
|
|
element:SetWndSize(vector2():set(
|
|
element:GetWidth() * ratio * scale.w,
|
|
element:GetHeight() * scale.h
|
|
))
|
|
|
|
element:SetWndPos(vector2():set(
|
|
pos.x * ratio * scale.w + offset.x,
|
|
pos.y * scale.h + offset.y
|
|
))
|
|
|
|
return element
|
|
end
|
|
|
|
function get_max_key(t)
|
|
local keys = {}
|
|
for k in pairs(t) do keys[#keys+1] = k end
|
|
return math.max(unpack(keys))
|
|
end
|
|
|
|
function get_empty_tab()
|
|
return {__sections = {}, __ids = {}}
|
|
end
|
|
|
|
-- taken from ui_inventory
|
|
local K_Timer = false
|
|
function keybind_pass()
|
|
if (K_Timer and (time_global() > K_Timer + 200)) or (not K_Timer) then
|
|
K_Timer = time_global()
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- wrappers for mags functions
|
|
is_magazine = magazine_binder and magazine_binder.is_magazine and function(obj)
|
|
return magazine_binder.is_magazine(obj)
|
|
end or function(obj) return false end
|
|
|
|
supports_magazines = magazine_binder and magazine_binder.is_supported_weapon and function(obj)
|
|
return magazine_binder.is_supported_weapon(obj)
|
|
end or function(obj) return false end
|
|
|
|
|
|
class "DynamicTab"
|
|
|
|
function DynamicTab:__init(manager, category)
|
|
self.category = category
|
|
self.ui_texture = "ui_qaw_category_" .. self.category
|
|
end
|
|
|
|
function DynamicTab:GetItemTable()
|
|
return {}
|
|
end
|
|
|
|
function DynamicTab:GetUITexture()
|
|
return self.ui_texture
|
|
end
|
|
|
|
|
|
class "DynamicItemTab" (DynamicTab)
|
|
|
|
function DynamicItemTab:__init(manager, category, spawn_tables, classes) -- reuse debug spawn tables, since they already parse everything we need usually
|
|
super(manager, category)
|
|
|
|
self.valid_sections = {}
|
|
|
|
for _, table in ipairs(spawn_tables) do
|
|
local spawn_table = ui_debug_main.get_spawn_table(table)
|
|
for _, sec in ipairs(spawn_table) do
|
|
for _, class in ipairs(classes) do
|
|
if manager:CanAddItem(nil, sec) == class then
|
|
self.valid_sections[sec] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function DynamicItemTab:GetItemTable()
|
|
local t = {}
|
|
|
|
for sec, _ in pairs(self.valid_sections) do
|
|
local obj = db.actor:object(sec)
|
|
if obj then
|
|
t[#t + 1] = {sec = sec, obj = obj}
|
|
end
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
|
|
class "DynamicFoodTab" (DynamicItemTab)
|
|
|
|
function DynamicFoodTab:__init(manager, category, spawn_tables, classes)
|
|
super(manager, category, spawn_tables, classes)
|
|
|
|
for sec, _ in pairs(self.valid_sections) do
|
|
if ini_sys:r_string_ex(sec, "kind") == "i_mutant_raw" then -- don't want that on quick access
|
|
self.valid_sections[sec] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
class "DynamicAttachmentTab" (DynamicTab)
|
|
|
|
function DynamicAttachmentTab:__init(manager, category)
|
|
super(manager, category)
|
|
|
|
self.addon_params = {
|
|
["scopes_sect"] = true,
|
|
["silencer_name"] = true,
|
|
["grenade_launcher_name"] = true
|
|
}
|
|
|
|
self.attachment_getters = {
|
|
["sil"] = utils_item.get_attached_silencer,
|
|
["gl"] = utils_item.get_attached_gl,
|
|
["scope"] = utils_item.get_attached_scope
|
|
}
|
|
end
|
|
|
|
function DynamicAttachmentTab:GetItemTable()
|
|
local wpn = db.actor:active_item()
|
|
|
|
if (not wpn) or (not IsWeapon(wpn)) or IsItem("fake_ammo_wpn",wpn:section()) then
|
|
return {}
|
|
end
|
|
|
|
local t = {}
|
|
local sec = wpn:section()
|
|
|
|
local scopes = {}
|
|
local scopes = ini_sys:r_string_ex(sec, "scopes")
|
|
|
|
if (scopes) then
|
|
scopes = str_explode(scopes, ",")
|
|
|
|
for _, scope in pairs(scopes) do
|
|
if scope ~= "none" then
|
|
local obj = db.actor:object(scope)
|
|
if obj then
|
|
t[#t + 1] = {sec = scope, obj = obj}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for param, _ in pairs(self.addon_params) do
|
|
local addon = utils_item.get_wpn_param(wpn, sec, param)
|
|
if addon then
|
|
local obj = db.actor:object(addon)
|
|
if obj then
|
|
t[#t + 1] = {sec = addon, obj = obj}
|
|
end
|
|
end
|
|
end
|
|
|
|
for name, getter in pairs(self.attachment_getters) do
|
|
local attachment = getter(wpn)
|
|
|
|
if attachment then
|
|
t[#t + 1] = {sec = attachment}
|
|
end
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
|
|
class "DynamicAmmoTab" (DynamicTab)
|
|
|
|
function DynamicAmmoTab:__init(manager, category)
|
|
super(manager, category)
|
|
end
|
|
|
|
function DynamicAmmoTab:GetItemTable()
|
|
local wpn = db.actor:active_item()
|
|
|
|
if (not wpn) or (not IsWeapon(wpn)) or IsItem("fake_ammo_wpn",wpn:section()) then
|
|
return {}
|
|
end
|
|
|
|
local t = {}
|
|
|
|
if (supports_magazines(wpn)) and (not wpn:weapon_in_grenade_mode()) then
|
|
local carried_mags = {}
|
|
|
|
magazine_binder.get_carried_mags(carried_mags)
|
|
|
|
for id, data in pairs(carried_mags) do
|
|
if magazine_binder.is_compatible(wpn, data.section) then
|
|
local obj = level.object_by_id(id)
|
|
local p = obj and obj:parent()
|
|
|
|
if p and p:id() == AC_ID then -- I'd rather not need to check this, but we all want something in life
|
|
t[#t + 1] = {obj = obj, sec = data.section}
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local cwpn = wpn:cast_Weapon()
|
|
|
|
cwpn:AmmoTypeForEach(function(i, ammo_sec)
|
|
local obj = db.actor:object(ammo_sec)
|
|
|
|
if obj then
|
|
t[#t + 1] = {obj = obj, sec = ammo_sec}
|
|
end
|
|
end)
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
class "DynamicSlotsTab" (DynamicTab)
|
|
|
|
function DynamicSlotsTab:__init(manager, category)
|
|
super(manager, category)
|
|
|
|
self.valid_slots = {
|
|
[1] = true, -- knife
|
|
[2] = true, -- pistol
|
|
[3] = true, -- rifle
|
|
[4] = true, -- grenade
|
|
[5] = true, -- binoc
|
|
[6] = true, -- bolt
|
|
[8] = true, -- PDA
|
|
[9] = true -- device
|
|
}
|
|
end
|
|
|
|
function DynamicSlotsTab:GetItemTable()
|
|
local t = {}
|
|
|
|
for slot, _ in pairs(self.valid_slots) do
|
|
local obj = db.actor:item_in_slot(slot)
|
|
if obj then
|
|
t[#t + 1] = {obj = obj, sec = obj:section()}
|
|
end
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
|
|
class "TabManager"
|
|
|
|
function TabManager:__init()
|
|
self.current_tab = 1
|
|
self.temp_tab = false
|
|
self.tab_count = get_max_key(tab_categories)
|
|
self.tabs = {}
|
|
for i = 1, self.tab_count do self.tabs[i] = get_empty_tab() end
|
|
|
|
self:UpdateTabs()
|
|
|
|
RegisterScriptCallback("load_state", self)
|
|
RegisterScriptCallback("save_state", self)
|
|
|
|
self:InitClasses()
|
|
end
|
|
|
|
function TabManager:InitClasses()
|
|
self.classes = {}
|
|
|
|
--self.classes.QSlotWheelOption = QSlotWheelOption
|
|
self.classes.QGrenadeWheelOption = QGrenadeWheelOption
|
|
self.classes.QDetectorWheelOption = QDetectorWheelOption
|
|
self.classes.QConsumableWheelOption = QConsumableWheelOption
|
|
|
|
self.classes.QGrenadeLauncherWheelOption = QGrenadeLauncherWheelOption
|
|
self.classes.QSilencerWheelOption = QSilencerWheelOption
|
|
self.classes.QScopeWheelOption = QScopeWheelOption
|
|
|
|
self.classes.QMagazineWheelOption = QMagazineWheelOption
|
|
self.classes.QAmmoWheelOption = QAmmoWheelOption
|
|
|
|
self.classes.QEquipWheelOption = QEquipWheelOption
|
|
|
|
self.cache = {} -- [sec] = class
|
|
|
|
self.dynamic_tabs = dup_table(tab_categories)
|
|
|
|
self.dynamic_tab_handlers = {}
|
|
|
|
self.dynamic_tab_handlers.devices = DynamicItemTab(self, "devices", {"Items (Device)"}, {"QDetectorWheelOption"})
|
|
self.dynamic_tab_handlers.grenades = DynamicItemTab(self, "grenades", {"Weapons (Explosive)"}, {"QGrenadeWheelOption"})
|
|
self.dynamic_tab_handlers.meds = DynamicItemTab(self, "meds", {"Items (Medical)"}, {"QConsumableWheelOption"})
|
|
|
|
self.dynamic_tab_handlers.food = DynamicFoodTab(self, "food", {"Items (Drink)", "Items (Food)"}, {"QConsumableWheelOption"})
|
|
|
|
self.dynamic_tab_handlers.attachments = DynamicAttachmentTab(self, "attachments")
|
|
self.dynamic_tab_handlers.ammo = DynamicAmmoTab(self, "ammo")
|
|
|
|
self.dynamic_tab_handlers.slots = DynamicSlotsTab(self, "slots")
|
|
|
|
self.tab_by_handler = invert_table(self.dynamic_tabs) -- used for shortcuts
|
|
end
|
|
|
|
function TabManager:GetDynamicTabHandler(tab)
|
|
return self.dynamic_tab_handlers[self.dynamic_tabs[tab]]
|
|
end
|
|
|
|
function TabManager:SetTabCount(i)
|
|
self.tab_count = i
|
|
self:UpdateTabs()
|
|
end
|
|
|
|
function TabManager:UpdateTabs()
|
|
-- mrrrmewmewmewm
|
|
if #self.tabs > self.tab_count then
|
|
for i = #self.tabs, self.tab_count + 1, -1 do
|
|
table.remove(self.tabs, i)
|
|
end
|
|
elseif #self.tabs < self.tab_count then
|
|
for i = #self.tabs + 1, self.tab_count do
|
|
table.insert(self.tabs, i, get_empty_tab())
|
|
end
|
|
end
|
|
end
|
|
|
|
function TabManager:CanAddItem(obj, sec)
|
|
if self.cache[sec] then
|
|
return self.cache[sec]
|
|
end
|
|
|
|
for name, class in pairs(self.classes) do
|
|
if class.CanAddToTab(obj, sec) then
|
|
if (not class.is_obj) then
|
|
self.cache[sec] = name
|
|
end
|
|
|
|
return name
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function TabManager:AddItem(tab, obj, sec)
|
|
local name = self:CanAddItem(obj, sec)
|
|
|
|
if (not name) then
|
|
return
|
|
end
|
|
|
|
if self.tabs[tab].__sections[sec] or (obj and self.tabs[tab].__ids[obj:id()]) then
|
|
return
|
|
end
|
|
|
|
if self.classes[name].is_obj then
|
|
self.tabs[tab].__ids[obj:id()] = true
|
|
else
|
|
self.tabs[tab].__sections[sec] = true
|
|
end
|
|
|
|
table.insert(self.tabs[tab], {
|
|
class = name,
|
|
args = self.classes[name].GetTabParams(obj, sec),
|
|
section = sec,
|
|
id = obj and obj:id()
|
|
})
|
|
end
|
|
|
|
function TabManager:RemoveItem(tab, i)
|
|
local item = self.tabs[tab][i]
|
|
|
|
if item.section then
|
|
self.tabs[tab].__sections[item.section] = nil
|
|
end
|
|
|
|
if item.id then
|
|
self.tabs[tab].__ids[item.id] = nil
|
|
end
|
|
|
|
table.remove(self.tabs[tab], i)
|
|
end
|
|
|
|
function TabManager:LoadDynamicTab(tab)
|
|
local to_add = self:GetDynamicTabHandler(tab):GetItemTable()
|
|
|
|
for i = #self.tabs[tab], 1, -1 do
|
|
self:RemoveItem(tab, i)
|
|
end
|
|
|
|
for i = 1, #to_add do
|
|
self:AddItem(tab, to_add[i].obj, to_add[i].sec)
|
|
end
|
|
end
|
|
|
|
function TabManager:SetCurrentTab(tab, temp)
|
|
if (not self.tabs[tab]) then -- something went wrong
|
|
self:SetTabCount(self.tab_count)
|
|
return
|
|
end
|
|
|
|
if (temp) then
|
|
self.temp_tab = tab
|
|
else
|
|
self.current_tab = tab
|
|
self.temp_tab = false
|
|
end
|
|
|
|
if self.dynamic_tabs[tab] then
|
|
self:LoadDynamicTab(tab)
|
|
end
|
|
|
|
if GUI then
|
|
GUI:UpdateItems()
|
|
end
|
|
end
|
|
|
|
function TabManager:save_state(m_data)
|
|
m_data.haru_qaw = {
|
|
current_tab = self.current_tab,
|
|
tabs = self.tabs
|
|
}
|
|
end
|
|
|
|
function TabManager:load_state(m_data)
|
|
if (not m_data.haru_qaw) then
|
|
return
|
|
end
|
|
|
|
self.tabs = m_data.haru_qaw.tabs
|
|
|
|
for idx, tab in pairs(self.tabs) do
|
|
if (not tab.__sections) then -- something went wrong
|
|
self.tabs[idx] = get_empty_tab()
|
|
else
|
|
for i = #tab, 1, -1 do
|
|
-- pop if class or section like that no longer exists
|
|
if (not self.classes[tab[i].class]) or (not ini_sys:section_exist(tab[i].section)) then
|
|
self:RemoveItem(idx, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
if (not tab.__ids) then -- update
|
|
tab.__ids = {}
|
|
end
|
|
end
|
|
|
|
self.current_tab = m_data.haru_qaw.current_tab
|
|
end
|
|
|
|
|
|
-- Generic Inventory Item Wheel Option
|
|
class "ItemWheelOption" (haru_arm.WheelOption)
|
|
|
|
function ItemWheelOption:__init(xml, parent, section)
|
|
super(xml, parent)
|
|
|
|
self.section = section
|
|
self.count = 0
|
|
|
|
self.count_itr = false
|
|
end
|
|
|
|
function ItemWheelOption:Draw(x, y)
|
|
haru_arm.WheelOption.Draw(self, x, y)
|
|
|
|
self.hover_bg = InitEx(self.xml, "Static", "hover_bg", self.container)
|
|
self:CenterElement(self.hover_bg)
|
|
self.hover_bg:Show(false)
|
|
|
|
self.selected_bg = InitEx(self.xml, "Static", "selected_bg", self.container)
|
|
self:CenterElement(self.selected_bg)
|
|
self.selected_bg:Show(false)
|
|
|
|
-- his name is item_cont and he is a single celled organism
|
|
self.item_cont = utils_ui.UICellContainer("item_wheel_option_" .. self.section, self, "cont_item", "cont_item", self.container, true)
|
|
|
|
function self.item_cont:Callback() -- disable all callbacks, don't need them
|
|
return
|
|
end
|
|
|
|
self.item_cont.disable_drag = true
|
|
self.item_cont.disable_info = true
|
|
self.item_cont.showcase = true
|
|
|
|
self.item_cont:AddItemManual(nil, nil, 1)
|
|
self:CenterElement(self.item_cont.prof)
|
|
|
|
self:ResizeAndCenterCell()
|
|
|
|
if self.count_itr then -- only do this once
|
|
db.actor:iterate_inventory(self.count_itr, db.actor)
|
|
end
|
|
|
|
self:UpdateItemCell()
|
|
end
|
|
|
|
function ItemWheelOption:GetCellItem()
|
|
return self.item_cont.cell[1]
|
|
end
|
|
|
|
function ItemWheelOption:UpdateItemCell()
|
|
self.item_cont:AddItemManual(nil, self.section, 1)
|
|
|
|
if self.count_itr then
|
|
local ci = self:GetCellItem()
|
|
|
|
if (not ci.cnt) then
|
|
ci.cnt = InitEx(XMLOP, "Static", "cont_item:cell:cnt", ci.cell)
|
|
ci.cnt:Show(false)
|
|
end
|
|
|
|
self:UpdateCounter()
|
|
end
|
|
end
|
|
|
|
function ItemWheelOption:UpdateCounter()
|
|
local ci = self:GetCellItem()
|
|
|
|
if (self.count <= 0) then
|
|
ci.cnt:Show(false)
|
|
return
|
|
end
|
|
|
|
ci.cnt:Show(true)
|
|
ci.cnt:TextControl():SetText("x" .. self.count)
|
|
end
|
|
|
|
function ItemWheelOption:OnState(prev_state, state)
|
|
self.hover_bg:Show(state == wheel_states.TOUCHED)
|
|
|
|
if state == wheel_states.TOUCHED then
|
|
GUI:SetTitle(ui_item.get_sec_name(self.section))
|
|
self:AnimateCell(100, 41, 11)
|
|
else
|
|
self:AnimateCell(100, 52, -11)
|
|
end
|
|
end
|
|
|
|
function ItemWheelOption:AnimateCell(duration, num, add)
|
|
local anim_state = 0
|
|
local start_time = time_global()
|
|
local end_time = time_global() + (duration * (MCM_SETTINGS.slowmo and MCM_SETTINGS.slowmo_factor or 1))
|
|
|
|
RemoveTimeEvent(script_name(), "animate_cell_" .. self.section .. self.idx)
|
|
|
|
CreateTimeEvent(script_name(), "animate_cell_" .. self.section .. self.idx, 0, function()
|
|
local scale = num + (add * anim_state)
|
|
|
|
self:GetCellItem().grid_size = scale
|
|
self:UpdateItemCell()
|
|
|
|
if anim_state == 1 then
|
|
return true
|
|
end
|
|
|
|
anim_state = math.min(1, normalize(time_global(), start_time, end_time))
|
|
end)
|
|
end
|
|
|
|
function ItemWheelOption:ResizeAndCenterCell()
|
|
self:UpdateItemCell()
|
|
local ci = self:GetCellItem()
|
|
|
|
ci.cell:SetWndSize(vector2():set(ci.W * utils_xml.screen_ratio(), ci.H))
|
|
local st_x = (self.item_cont.prof:GetWidth() /2) - (ci.cell:GetWidth() /2)
|
|
local st_y = (self.item_cont.prof:GetHeight() /2) - (ci.cell:GetHeight() /2)
|
|
ci.cell:SetWndPos(vector2():set(st_x , st_y))
|
|
end
|
|
|
|
function ItemWheelOption:Destroy()
|
|
haru_arm.WheelOption.Destroy(self)
|
|
RemoveTimeEvent(script_name(), "animate_cell_" .. self.section .. self.idx)
|
|
end
|
|
|
|
function ItemWheelOption:Update()
|
|
local obj = db.actor:object(self.section)
|
|
self:GetCellItem():Colorize(obj and "def" or "hide")
|
|
|
|
return obj
|
|
end
|
|
|
|
function ItemWheelOption:OnClick()
|
|
local obj = db.actor:object(self.section)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
return obj
|
|
end
|
|
|
|
function ItemWheelOption:OnRightClick()
|
|
if (not TAB_MANAGER.dynamic_tabs[TAB_MANAGER.current_tab]) then
|
|
TAB_MANAGER:RemoveItem(TAB_MANAGER.current_tab, self.idx)
|
|
GUI:UpdateItems()
|
|
end
|
|
end
|
|
|
|
-- sorta like static methods (don't use the instance)
|
|
function ItemWheelOption.CanAddToTab(obj, sec)
|
|
return false
|
|
end
|
|
|
|
function ItemWheelOption.GetTabParams(obj, sec)
|
|
return {sec}
|
|
end
|
|
|
|
|
|
-- Like item wheel option, but works with objects
|
|
class "ObjectWheelOption" (ItemWheelOption)
|
|
ObjectWheelOption.is_obj = true -- flag for classes that work specifically with objects (ignore cache and section dupes)
|
|
function ObjectWheelOption:__init(xml, parent, section, id)
|
|
super(xml, parent, section)
|
|
self.item_id = id
|
|
end
|
|
|
|
function ObjectWheelOption:Draw(x, y)
|
|
ItemWheelOption.Draw(self, x, y)
|
|
self.item_cont.showcase = false
|
|
|
|
-- make progress bar act like it's not manual
|
|
local ci = self:GetCellItem()
|
|
_add_progress = ci.Add_ProgressBar
|
|
ci.Add_ProgressBar = function(_ci, xml, obj, sec, clsid)
|
|
_ci.manual = false
|
|
_add_progress(_ci, xml, obj, sec, clsid)
|
|
_ci.manual = true
|
|
end
|
|
|
|
self:UpdateItemCell()
|
|
end
|
|
|
|
function ObjectWheelOption:Update()
|
|
local obj = self:GetObject()
|
|
self:GetCellItem():Colorize(obj and "def" or "hide")
|
|
return obj
|
|
end
|
|
|
|
function ObjectWheelOption:UpdateItemCell()
|
|
local obj = self:GetObject()
|
|
local ci = self:GetCellItem()
|
|
ci.showcase = obj and 0 or 1
|
|
self.item_cont:AddItemManual(obj, self.section, 1)
|
|
end
|
|
|
|
function ObjectWheelOption:OnClick()
|
|
local obj = self:GetObject()
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
return obj
|
|
end
|
|
|
|
function ObjectWheelOption:GetObject()
|
|
local obj = level.object_by_id(self.item_id)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
local p = obj:parent()
|
|
|
|
if p and p:id() == AC_ID then
|
|
return obj
|
|
end
|
|
end
|
|
|
|
function ObjectWheelOption.GetTabParams(obj, sec)
|
|
return {sec, obj:id()}
|
|
end
|
|
|
|
-- Quick Consumable
|
|
class "QConsumableWheelOption" (ItemWheelOption)
|
|
|
|
function QConsumableWheelOption:__init(xml, parent, section)
|
|
super(xml, parent, section)
|
|
|
|
self.is_multiuse = IsItem("multiuse", self.section)
|
|
|
|
self.count_itr = self.is_multiuse and function(_, obj)
|
|
if (obj:section() == self.section) then
|
|
self.count = self.count + obj:get_remaining_uses()
|
|
end
|
|
end or function(_, obj)
|
|
if (obj:section() == self.section) then
|
|
self.count = self.count + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function QConsumableWheelOption:OnClick()
|
|
local obj = ItemWheelOption.OnClick(self)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
GUI:Close()
|
|
|
|
if (CInventory__eat(obj) == false) then
|
|
return
|
|
end
|
|
|
|
db.actor:eat(obj)
|
|
end
|
|
|
|
function QConsumableWheelOption.CanAddToTab(obj, sec)
|
|
return IsItem("consumable", sec) and (not IsItem("scope", sec))
|
|
end
|
|
|
|
function get_obj_inv_slot(obj)
|
|
if (not obj) then
|
|
return false
|
|
end
|
|
|
|
local id = obj:id()
|
|
|
|
for slot, _ in pairs(SCANNED_SLOTS) do
|
|
local item_in_slot = db.actor:item_in_slot(slot)
|
|
|
|
if item_in_slot and item_in_slot:id() == id then
|
|
return slot
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Quick Slot
|
|
class "QSlotWheelOption" (ItemWheelOption)
|
|
|
|
function QSlotWheelOption:__init(xml, parent, section, slot)
|
|
super(xml, parent, section)
|
|
|
|
self.slot = slot
|
|
end
|
|
|
|
function QSlotWheelOption:Update()
|
|
local obj = ItemWheelOption.Update(self)
|
|
|
|
local item_in_slot = db.actor:item_in_slot(self.slot)
|
|
self.selected_bg:Show(item_in_slot and item_in_slot:section() == self.section)
|
|
|
|
return obj
|
|
end
|
|
|
|
function QSlotWheelOption:OnClick()
|
|
local obj = ItemWheelOption.OnClick(self)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
local item_in_slot = db.actor:item_in_slot(self.slot)
|
|
|
|
if item_in_slot and item_in_slot:section() == self.section then
|
|
return obj
|
|
end
|
|
|
|
db.actor:move_to_slot(obj, self.slot)
|
|
|
|
return obj
|
|
end
|
|
|
|
function QSlotWheelOption.CanAddToTab(obj, sec)
|
|
return false
|
|
end
|
|
|
|
function QSlotWheelOption.GetTabParams(obj, sec)
|
|
local t = ItemWheelOption.GetTabParams(obj, sec)
|
|
t[#t + 1] = ini_sys:r_float_ex(sec, "slot") + 1
|
|
return t
|
|
end
|
|
|
|
class "QGrenadeWheelOption" (QSlotWheelOption)
|
|
|
|
function QGrenadeWheelOption:__init(xml, parent, section, slot)
|
|
super(xml, parent, section, slot)
|
|
|
|
self.count_itr = function(_, obj)
|
|
if (obj:section() == self.section) then
|
|
self.count = self.count + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function QGrenadeWheelOption:OnClick()
|
|
local obj = QSlotWheelOption.OnClick(self)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
if self.selected_bg:IsShown() then
|
|
db.actor:activate_slot(self.slot)
|
|
end
|
|
end
|
|
|
|
function QGrenadeWheelOption.CanAddToTab(obj, sec)
|
|
if ini_sys:r_float_ex(sec, "slot") == 3 then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Quick Device
|
|
class "QDetectorWheelOption" (QSlotWheelOption)
|
|
|
|
function QDetectorWheelOption:__init(xml, parent, section, slot)
|
|
super(xml, parent, section, slot)
|
|
end
|
|
|
|
function QDetectorWheelOption:OnClick()
|
|
local device = ItemWheelOption.OnClick(self)
|
|
|
|
if (not device) then
|
|
return
|
|
end
|
|
|
|
local is_fast = is_fast_anim()
|
|
local device_in_slot = db.actor:item_in_slot(self.slot)
|
|
local active_device = db.actor:active_detector()
|
|
|
|
if device_in_slot and device_in_slot:section() == self.section then
|
|
if active_device then
|
|
db.actor:hide_detector(is_fast)
|
|
else
|
|
db.actor:show_detector(is_fast)
|
|
end
|
|
|
|
GUI:Close()
|
|
return device
|
|
end
|
|
|
|
if active_device then
|
|
cycle_detector(active_device, device, is_fast)
|
|
else
|
|
equip_and_show_detector(device)
|
|
end
|
|
|
|
GUI:Close()
|
|
return device
|
|
end
|
|
|
|
function QDetectorWheelOption.CanAddToTab(obj, sec)
|
|
if ini_sys:r_float_ex(sec, "slot") == 8 then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function is_fast_anim()
|
|
return db.actor:active_item() and true or false
|
|
end
|
|
|
|
function cycle_detector(prev, next, is_fast)
|
|
db.actor:hide_detector(is_fast)
|
|
|
|
CreateTimeEvent(script_name(), "hide_device", 0, function()
|
|
if prev:get_state() == 3 then
|
|
equip_and_show_detector(next)
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end)
|
|
end
|
|
|
|
function equip_and_show_detector(obj)
|
|
db.actor:move_to_slot(obj, 9)
|
|
|
|
CreateTimeEvent(script_name(), "show_device", 0.1, function()
|
|
db.actor:show_detector(is_fast_anim())
|
|
return true
|
|
end)
|
|
end
|
|
|
|
|
|
-- Generic Weapon Attachment
|
|
class "AttachmentWheelOption" (ItemWheelOption)
|
|
|
|
function AttachmentWheelOption:__init(xml, parent, section)
|
|
super(xml, parent, section)
|
|
end
|
|
|
|
function AttachmentWheelOption:IsAttached()
|
|
-- do something to check if attachment is attached
|
|
return false
|
|
end
|
|
|
|
function AttachmentWheelOption:Attach(obj, wpn)
|
|
snd_attach_addon:play(db.actor,0,sound_object.s2d)
|
|
-- do something to attach the attachment
|
|
end
|
|
|
|
function AttachmentWheelOption:Detach(wpn)
|
|
snd_detach_addon:play(db.actor,0,sound_object.s2d)
|
|
-- do something to detach the attachment
|
|
end
|
|
|
|
function AttachmentWheelOption:CanAttach(obj, wpn)
|
|
-- do something to check if we can attach
|
|
end
|
|
|
|
function AttachmentWheelOption:Update()
|
|
local obj = ItemWheelOption.Update(self)
|
|
local wpn = db.actor:active_item()
|
|
|
|
self.selected_bg:Show(false)
|
|
local ci = self:GetCellItem()
|
|
if wpn then
|
|
if self:IsAttached(wpn) then
|
|
ci:Colorize("def")
|
|
self.selected_bg:Show(true)
|
|
elseif obj and (not self:CanAttach(obj, wpn)) then
|
|
ci:Colorize("red")
|
|
end
|
|
else
|
|
if obj then
|
|
ci:Colorize("red")
|
|
end
|
|
end
|
|
end
|
|
|
|
function AttachmentWheelOption:OnClick()
|
|
local wpn = db.actor:active_item()
|
|
|
|
if (not wpn) then
|
|
return
|
|
end
|
|
|
|
if self:IsAttached(wpn) then
|
|
self:Detach(wpn)
|
|
else
|
|
local obj = db.actor:object(self.section)
|
|
|
|
if obj then
|
|
if self:CanAttach(obj, wpn) then
|
|
self:Attach(obj, wpn)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Quick Underbarrel Grenade Launcher
|
|
class "QGrenadeLauncherWheelOption" (AttachmentWheelOption)
|
|
|
|
function QGrenadeLauncherWheelOption:__init(xml, parent, section)
|
|
super(xml, parent, section)
|
|
end
|
|
|
|
function QGrenadeLauncherWheelOption:IsAttached(wpn)
|
|
return utils_item.get_attached_gl(wpn) == self.section
|
|
end
|
|
|
|
function QGrenadeLauncherWheelOption:CanAttach(obj, wpn)
|
|
return utils_item.can_attach_gl(wpn, obj)
|
|
end
|
|
|
|
function QGrenadeLauncherWheelOption:Attach(obj, wpn)
|
|
AttachmentWheelOption.Attach(self, obj, wpn)
|
|
|
|
utils_item.attach_addon(wpn, obj, "gl", true)
|
|
end
|
|
|
|
function QGrenadeLauncherWheelOption:Detach(wpn)
|
|
AttachmentWheelOption.Detach(self, wpn)
|
|
|
|
utils_item.detach_addon(wpn, nil, "gl")
|
|
end
|
|
|
|
function QGrenadeLauncherWheelOption.CanAddToTab(obj, sec)
|
|
return IsItem("gl", sec)
|
|
end
|
|
|
|
|
|
-- Quick Silencer Attach
|
|
class "QSilencerWheelOption" (AttachmentWheelOption)
|
|
|
|
function QSilencerWheelOption:__init(xml, parent, section)
|
|
super(xml, parent, section)
|
|
end
|
|
|
|
function QSilencerWheelOption:IsAttached(wpn)
|
|
return utils_item.get_attached_silencer(wpn) == self.section
|
|
end
|
|
|
|
function QSilencerWheelOption:CanAttach(obj, wpn)
|
|
return utils_item.can_attach_silencer(wpn, obj)
|
|
end
|
|
|
|
function QSilencerWheelOption:Attach(obj, wpn)
|
|
AttachmentWheelOption.Attach(self, obj, wpn)
|
|
|
|
utils_item.attach_addon(wpn, obj, "sil", true)
|
|
end
|
|
|
|
function QSilencerWheelOption:Detach(wpn)
|
|
AttachmentWheelOption.Detach(self, wpn)
|
|
|
|
utils_item.detach_addon(wpn, nil, "sil")
|
|
end
|
|
|
|
function QSilencerWheelOption.CanAddToTab(obj, sec)
|
|
return IsItem("sil", sec)
|
|
end
|
|
|
|
|
|
-- Quick Scope Switch
|
|
class "QScopeWheelOption" (AttachmentWheelOption)
|
|
|
|
function QScopeWheelOption:__init(xml, parent, section)
|
|
super(xml, parent, section)
|
|
end
|
|
|
|
function QScopeWheelOption:IsAttached(wpn)
|
|
return utils_item.get_attached_scope(wpn) == self.section
|
|
end
|
|
|
|
function QScopeWheelOption:CanAttach(obj, wpn)
|
|
local scopes = parse_list(ini_sys, wpn:section(), "scopes", true)
|
|
|
|
return scopes[obj:section()] or utils_item.can_attach_scope(wpn, obj)
|
|
end
|
|
|
|
function QScopeWheelOption:ReturnWeaponToSlot(slot)
|
|
local fun = nil
|
|
|
|
fun = function(item)
|
|
if IsWeapon(item) then
|
|
db.actor:move_to_slot(item, slot)
|
|
UnregisterScriptCallback("actor_on_item_take", fun)
|
|
end
|
|
end
|
|
|
|
RegisterScriptCallback("actor_on_item_take", fun)
|
|
end
|
|
|
|
function QScopeWheelOption:Attach(obj, wpn)
|
|
if wpn:get_state() ~= 0 then
|
|
return
|
|
end
|
|
|
|
AttachmentWheelOption.Attach(self, obj, wpn)
|
|
local cwpn = wpn:cast_Weapon()
|
|
|
|
if cwpn:GetScopeName() then
|
|
utils_item.attach_addon(wpn, obj, "scope", true)
|
|
return
|
|
end
|
|
|
|
-- attach function for DRX scopes is buggy + we can't cycle to new scope with it, so we write our own
|
|
local parent_section = ini_sys:r_string_ex(wpn:section(),"parent_section")
|
|
local sec = wpn:section()
|
|
|
|
if (sec ~= parent_section) then
|
|
local scope_sec = sec:gsub(parent_section .. "_", "")
|
|
|
|
if ini_sys:section_exist(scope_sec) then
|
|
alife_create_item(scope_sec, db.actor)
|
|
end
|
|
end
|
|
|
|
local child_section = (parent_section .. "_" .. obj:section())
|
|
|
|
if not (ini_sys:section_exist(child_section)) then
|
|
return
|
|
end
|
|
|
|
local slot = get_obj_inv_slot(wpn)
|
|
|
|
local old_wpn = alife_object(wpn:id())
|
|
local new_wpn = old_wpn and alife_clone_weapon(old_wpn, child_section)
|
|
|
|
if (not new_wpn) then
|
|
return
|
|
end
|
|
|
|
alife_release(obj)
|
|
|
|
if slot then
|
|
self:ReturnWeaponToSlot(slot)
|
|
end
|
|
|
|
return new_wpn
|
|
end
|
|
|
|
function QScopeWheelOption:Detach(wpn)
|
|
if wpn:get_state() ~= 0 then
|
|
return
|
|
end
|
|
|
|
AttachmentWheelOption.Detach(self, wpn)
|
|
|
|
local cwpn = wpn:cast_Weapon()
|
|
|
|
if cwpn:GetScopeName() then
|
|
utils_item.detach_addon(wpn, self.section, "scope")
|
|
return
|
|
end
|
|
|
|
-- detach function is also buggy, so we write this too
|
|
local parent_section = ini_sys:r_string_ex(wpn:section(),"parent_section")
|
|
local sec = wpn:section()
|
|
|
|
if (parent_section == sec) then -- something is not right
|
|
return
|
|
end
|
|
|
|
alife_create_item(self.section, db.actor)
|
|
|
|
local slot = get_obj_inv_slot(wpn)
|
|
|
|
local old_wpn = alife_object(wpn:id())
|
|
local new_wpn = old_wpn and alife_clone_weapon(old_wpn, parent_section)
|
|
|
|
if (not new_wpn) then
|
|
return
|
|
end
|
|
|
|
if slot then
|
|
self:ReturnWeaponToSlot(slot)
|
|
end
|
|
|
|
return new_wpn
|
|
end
|
|
|
|
function QScopeWheelOption.CanAddToTab(obj, sec)
|
|
return IsItem("scope", sec)
|
|
end
|
|
|
|
|
|
class "QMagazineWheelOption" (ObjectWheelOption)
|
|
|
|
function QMagazineWheelOption:__init(xml, parent, section, id)
|
|
super(xml, parent, section, id)
|
|
end
|
|
|
|
function QMagazineWheelOption:OnClick()
|
|
local obj = ObjectWheelOption.OnClick(self)
|
|
|
|
if (not self:CanLoadInActiveWeapon(obj)) then
|
|
return
|
|
end
|
|
|
|
GUI:Close()
|
|
|
|
self:LoadInActiveWeapon(obj)
|
|
end
|
|
|
|
function QMagazineWheelOption:Update()
|
|
local obj = ObjectWheelOption.Update(self)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
self:GetCellItem():Colorize(self:CanLoadInActiveWeapon(obj) and "def" or "red")
|
|
end
|
|
|
|
function QMagazineWheelOption:CanLoadInActiveWeapon(obj)
|
|
return magazine_binder.is_carried_mag(self.item_id) and magazine_binder.is_compatible(db.actor:active_item(), obj)
|
|
end
|
|
|
|
function QMagazineWheelOption:LoadInActiveWeapon(obj)
|
|
local wpn = db.actor:active_item()
|
|
local pre_table = magazines.count_ammo(wpn)
|
|
|
|
wpn:switch_state(7)
|
|
|
|
local first_round = nil
|
|
|
|
if magazines_mcm.get_config("retain_round") and wpn:get_ammo_in_magazine() > 0 then
|
|
first_round = magazines.get_sec_chambered(wpn)
|
|
end
|
|
|
|
magazines.action_start_reload()
|
|
|
|
magazines.create_time_event(
|
|
"Mag_redux",
|
|
"delay_weapon"..wpn:id(),
|
|
0.1,
|
|
magazines.delay_load_weapon,
|
|
MCM_SETTINGS.old_mags_compat and wpn or wpn:id(),
|
|
obj,
|
|
pre_table,
|
|
first_round
|
|
)
|
|
end
|
|
|
|
function QMagazineWheelOption.CanAddToTab(obj, sec)
|
|
return is_magazine(obj)
|
|
end
|
|
|
|
|
|
class "QAmmoWheelOption" (ItemWheelOption)
|
|
|
|
function QAmmoWheelOption:__init(xml, parent, section)
|
|
super(xml, parent, section)
|
|
|
|
self.can_load_in_weapon = false
|
|
self.ammo_type = false
|
|
self.is_loaded = false
|
|
|
|
self.count_itr = function(_, obj)
|
|
if obj:section() == self.section then
|
|
self.count = self.count + obj:ammo_get_count()
|
|
end
|
|
end
|
|
end
|
|
|
|
function QAmmoWheelOption:Draw(x, y)
|
|
ItemWheelOption.Draw(self, x, y)
|
|
self:UpdateCounter()
|
|
end
|
|
|
|
function QAmmoWheelOption:Update()
|
|
local obj = ItemWheelOption.Update(self)
|
|
local ci = self:GetCellItem()
|
|
|
|
self:UpdateCanLoad()
|
|
|
|
self.selected_bg:Show(self.is_loaded)
|
|
|
|
ci:Colorize(self.can_load_in_weapon and "def" or "red")
|
|
end
|
|
|
|
function QAmmoWheelOption:OnClick()
|
|
local obj = ItemWheelOption.OnClick(self)
|
|
|
|
if (not self.can_load_in_weapon) or self.is_loaded then
|
|
return
|
|
end
|
|
|
|
GUI:Close()
|
|
|
|
self:LoadInActiveWeapon()
|
|
end
|
|
|
|
function QAmmoWheelOption:UpdateCanLoad()
|
|
local wpn = db.actor:active_item()
|
|
|
|
if (not wpn) or (not IsWeapon(wpn)) or IsItem("fake_ammo_wpn",wpn:section()) then
|
|
self:ResetCanLoad()
|
|
return
|
|
end
|
|
|
|
local cwpn = wpn:cast_Weapon()
|
|
|
|
if supports_magazines(wpn) and (not wpn:weapon_in_grenade_mode()) then
|
|
self:ResetCanLoad()
|
|
return
|
|
end
|
|
|
|
local ammos = {}
|
|
|
|
cwpn:AmmoTypeForEach(function(i, ammo_sec)
|
|
ammos[ammo_sec] = i
|
|
end)
|
|
|
|
if ammos[self.section] then
|
|
self.can_load_in_weapon = true
|
|
self.ammo_type = ammos[self.section]
|
|
self.is_loaded = self.ammo_type == cwpn:GetAmmoType()
|
|
else
|
|
self:ResetCanLoad()
|
|
end
|
|
end
|
|
|
|
function QAmmoWheelOption:ResetCanLoad()
|
|
self.can_load_in_weapon = false
|
|
self.ammo_type = false
|
|
self.is_loaded = false
|
|
end
|
|
|
|
function QAmmoWheelOption:LoadInActiveWeapon()
|
|
local wpn = db.actor:active_item()
|
|
wpn:unload_magazine(true)
|
|
wpn:set_ammo_type(self.ammo_type)
|
|
db.actor:reload_weapon()
|
|
end
|
|
|
|
function QAmmoWheelOption.CanAddToTab(obj, sec)
|
|
return IsItem("ammo", sec) or IsItem("grenade_ammo", sec)
|
|
end
|
|
|
|
|
|
class "QEquipWheelOption" (ObjectWheelOption)
|
|
|
|
function QEquipWheelOption:__init(xml, parent, section, id, slot)
|
|
super(xml, parent, section, id)
|
|
self.slot = slot
|
|
end
|
|
|
|
function QEquipWheelOption:OnClick()
|
|
local obj = ObjectWheelOption.OnClick(self)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
if db.actor:active_slot() ~= self.slot then
|
|
db.actor:activate_slot(self.slot)
|
|
end
|
|
|
|
local item_in_slot = db.actor:item_in_slot(self.slot)
|
|
|
|
if (not item_in_slot) or item_in_slot:id() ~= self.item_id then
|
|
db.actor:move_to_slot(obj, self.slot)
|
|
end
|
|
|
|
CreateTimeEvent(script_name(), "close_gui_delay_" .. self.slot, 0.1, function()
|
|
if GUI then
|
|
GUI:Close()
|
|
end
|
|
return true
|
|
end)
|
|
|
|
return obj
|
|
end
|
|
|
|
function QEquipWheelOption:Update()
|
|
local obj = ObjectWheelOption.Update(self)
|
|
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
if self:IsEquipped() then
|
|
self:GetCellItem():Colorize("def")
|
|
self.selected_bg:Show(db.actor:active_slot() == self.slot)
|
|
end
|
|
|
|
self.selected_bg:Show(false)
|
|
end
|
|
|
|
function QEquipWheelOption:IsEquipped()
|
|
local item_in_slot = db.actor:item_in_slot(self.slot)
|
|
return item_in_slot and item_in_slot:id() == self.item_id
|
|
end
|
|
|
|
function QEquipWheelOption.GetTabParams(obj, sec)
|
|
local t = ObjectWheelOption.GetTabParams(obj, sec)
|
|
table.insert(t, get_obj_inv_slot(obj))
|
|
return t
|
|
end
|
|
|
|
function QEquipWheelOption.CanAddToTab(obj, sec)
|
|
if (not obj) then
|
|
return
|
|
end
|
|
|
|
local slot = get_obj_inv_slot(obj)
|
|
|
|
return slot and slot ~= 4
|
|
end
|
|
|
|
|
|
function patch_GUI(GUI)
|
|
GUI.stay_open = false
|
|
GUI.tab_category = InitEx(GUI.xml, "Static", "tab_category", GUI.bg)
|
|
GUI.title = InitEx(GUI.xml, "TextWnd", "title", GUI.bg)
|
|
GUI.tab_icons = {}
|
|
GUI.tab_colors = {
|
|
def = GetARGB(127, 0, 0, 0),
|
|
cur = GetARGB(255, 0, 0, 0)
|
|
}
|
|
|
|
for i = 1, TAB_MANAGER.tab_count do
|
|
GUI.tab_icons[i] = InitEx(GUI.xml, "Static", "tab_icon", GUI.bg)
|
|
|
|
local pos = GUI.tab_icons[i]:GetWndPos()
|
|
local w = GUI.tab_icons[i]:GetWidth()
|
|
|
|
GUI.tab_icons[i]:SetWndPos(vector2():set(pos.x + ((w + 5) * (i - 1)), pos.y))
|
|
end
|
|
|
|
function GUI:UpdateCallback()
|
|
if MCM_SETTINGS.hold and key_state(MCM_SETTINGS.keybind) == 0 and (not self.stay_open) then
|
|
self:Close()
|
|
end
|
|
end
|
|
|
|
local num_keys = {}
|
|
for i = 0, 9 do num_keys[DIK_keys["DIK_" .. i]] = i end
|
|
|
|
function GUI:OnKeyboardCallback(dik, keyboard_action)
|
|
if keyboard_action ~= ui_events.WINDOW_KEY_PRESSED then
|
|
return false
|
|
end
|
|
local bind = dik_to_bind(dik)
|
|
if num_keys[dik] then
|
|
local i = num_keys[dik]
|
|
|
|
if TAB_MANAGER.tabs[i] then
|
|
TAB_MANAGER:SetCurrentTab(i)
|
|
end
|
|
|
|
return true
|
|
elseif dik == DIK_keys.MOUSE_2 then
|
|
if is_not_empty(self.options) and self.options[self.hovered_idx] then
|
|
self.options[self.hovered_idx]:OnRightClick()
|
|
|
|
return true
|
|
end
|
|
elseif (
|
|
(dik == MCM_SETTINGS.keybind and (not MCM_SETTINGS.hold)) or
|
|
(MCM_SETTINGS.override_ammo_wheel and bind == key_bindings.kWPN_NEXT) or
|
|
override_quick_keys[bind]
|
|
) and keybind_pass() then
|
|
self:Close()
|
|
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function GUI:SetTitle(str)
|
|
self.title:SetText(str)
|
|
self.title:AdjustHeightToText()
|
|
end
|
|
|
|
function GUI:UpdateItems()
|
|
self:Reset()
|
|
|
|
local current_tab = TAB_MANAGER.temp_tab or TAB_MANAGER.current_tab
|
|
|
|
for i, item in ipairs(TAB_MANAGER.tabs[current_tab]) do
|
|
self:AddOption(TAB_MANAGER.classes[item.class](XMLOP, self, unpack(item.args)))
|
|
end
|
|
|
|
if TAB_MANAGER.dynamic_tabs[current_tab] then
|
|
self.tab_category:Show(true)
|
|
self.tab_category:InitTexture(TAB_MANAGER:GetDynamicTabHandler(current_tab):GetUITexture())
|
|
else
|
|
self.tab_category:Show(false)
|
|
end
|
|
|
|
for i, tab in ipairs(self.tab_icons) do
|
|
tab:SetTextureColor(i == current_tab and self.tab_colors.cur or self.tab_colors.def)
|
|
end
|
|
|
|
self:DrawOptions()
|
|
end
|
|
|
|
local _Reset = GUI.Reset
|
|
function GUI:Reset()
|
|
_Reset(self)
|
|
self.title:SetText("")
|
|
end
|
|
|
|
function GUI:Close()
|
|
self.stay_open = false
|
|
|
|
self:HideDialog()
|
|
|
|
if MCM_SETTINGS.slowmo then
|
|
exec_console_cmd("time_factor 1")
|
|
end
|
|
|
|
Unregister_UI("UIHaruQAW")
|
|
end
|
|
|
|
return GUI
|
|
end
|
|
|
|
function create_GUI()
|
|
GUI = patch_GUI(haru_arm.UIAdvancedRadialMenu("ui_haru_radial_menu.xml"))
|
|
|
|
GUI:UpdateItems()
|
|
end
|
|
|
|
function open_radial_menu(temp_tab)
|
|
hide_hud_inventory()
|
|
|
|
if (not GUI) then
|
|
create_GUI()
|
|
end
|
|
|
|
if (GUI) and (not GUI:IsShown()) and (((not temp_tab) and MCM_SETTINGS.hold) or keybind_pass()) then
|
|
GUI.stay_open = temp_tab
|
|
|
|
TAB_MANAGER.temp_tab = temp_tab or false
|
|
|
|
TAB_MANAGER:SetCurrentTab(TAB_MANAGER.temp_tab or TAB_MANAGER.current_tab, TAB_MANAGER.temp_tab)
|
|
GUI:ShowDialog(true)
|
|
|
|
if MCM_SETTINGS.slowmo then
|
|
exec_console_cmd("time_factor " .. MCM_SETTINGS.slowmo_factor)
|
|
end
|
|
|
|
_GUIs_keyfree["UIHaruQAW"] = true
|
|
Register_UI("UIHaruQAW", script_name())
|
|
end
|
|
end
|
|
|
|
-- even GUIs need to be cleaned up like this, not just HUDs
|
|
function actor_on_net_destroy()
|
|
if GUI and GUI:IsShown() then
|
|
GUI:Close()
|
|
end
|
|
|
|
GUI = nil
|
|
end
|
|
|
|
local mcm_keybinds = ui_mcm and ui_mcm.key_hold
|
|
|
|
function get_options_for_category(category, default)
|
|
return {
|
|
{ id = "divider", type = "line" },
|
|
{ id = "desc" .. category, type = "desc", text = "ui_mcm_qaw_" .. category},
|
|
{ id = category .. "_enabled", type = "check", hint = "qaw_enabled", val = 1, def = true },
|
|
{ id = category .. "_tab", type = "input", hint = "qaw_tab", val = 2, min = 1, max = 9, def = default}
|
|
}
|
|
end
|
|
|
|
function get_options_for_quick_use_key(key, def_enabled, def_val)
|
|
return {
|
|
{ id = "divider", type = "line" },
|
|
{ id = "desc" .. key, type = "desc", text = game.translate_string("ui_haru_qaw_override") .. " " .. game.translate_string("kb_quick_use_" .. key)},
|
|
{ id = "quck_use_" .. key .. "_enabled", type = "check", hint = "qaw_enabled", val = 1, def = def_enabled },
|
|
{ id = "quck_use_" .. key .. "_tab", type = "input", hint = "qaw_tab", val = 2, min = 1, max = 9, def = def_val}
|
|
}
|
|
end
|
|
|
|
local options = {id = script_name(), gr = {
|
|
{id = "controls", text="ui_mcm_qaw_controls", sh = true, gr = {
|
|
{ id = "title", type= "slide", link= "ui_qaw_mcm_banner", text= "", spacing = 20 },
|
|
{ id = "keybind", type = "key_bind", val = 2, def = DIK_keys.DIK_B },
|
|
{ id = "modifier", type = ui_mcm.kb_mod_radio, val = 2, def = 0, hint = "mcm_kb_mode",
|
|
content = {
|
|
-- excluded double tap
|
|
{ 0, "mcm_kb_mode_press"},
|
|
{ 1, "mcm_kb_mode_hold"}
|
|
}
|
|
},
|
|
{ id = "second_key", type = ui_mcm.kb_mod_radio, val = 2, def = 0, hint = "mcm_kb_modifier",
|
|
content = {
|
|
{0,"mcm_kb_mod_none"},
|
|
{1,"mcm_kb_mod_shift"} ,
|
|
{2,"mcm_kb_mod_ctrl"},
|
|
{3,"mcm_kb_mod_alt"}
|
|
}
|
|
},
|
|
{ id = "hold", type = "check", val = 1, def = true },
|
|
{ id = "divider", type = "line" },
|
|
{ id = "override_ammo_wheel", type = "check", val = 1, def = true},
|
|
}},
|
|
|
|
{id = "tabs", text="ui_mcm_qaw_tabs", sh = true, gr = {
|
|
{ id = "title", type= "slide", link= "ui_qaw_mcm_banner", text= "", spacing = 20 },
|
|
{ id = "tab_count", type = "input", val = 2, min = 1, max = 9, def = get_max_key(tab_categories) },
|
|
}},
|
|
|
|
{id ="misc", text="ui_mcm_qaw_misc", sh = true, gr = {
|
|
{ id = "title", type= "slide", link= "ui_qaw_mcm_banner", text= "", spacing = 20 },
|
|
{ id = "slowmo", type = "check", val = 1, def = false},
|
|
{ id = "slowmo_factor", type = "track", val = 2, step = 0.05, def = 0.5, max = 1, min = 0.1},
|
|
{ id = "old_mags_compat", type = "check", val = 1, def = false}
|
|
}}
|
|
}}
|
|
for default, category in pairs(tab_categories) do
|
|
local category_options = get_options_for_category(category, default)
|
|
for i = 1, #category_options do
|
|
table.insert(options.gr[2].gr, category_options[i])
|
|
end
|
|
end
|
|
for key = 1, 4 do
|
|
local quick_keys = get_options_for_quick_use_key(key, override_quick_keys[key + 60] and true or false ,override_quick_keys[key + 60] or 1)
|
|
for i = 1, #quick_keys do
|
|
table.insert(options.gr[1].gr, quick_keys[i])
|
|
end
|
|
end
|
|
|
|
function load_defaults()
|
|
for _, subtab in pairs(options.gr) do
|
|
for _, option in pairs(subtab.gr) do
|
|
MCM_SETTINGS[option.id] = option.def
|
|
end
|
|
end
|
|
end
|
|
|
|
function on_mcm_load()
|
|
return options
|
|
end
|
|
|
|
function on_mouse_wheel(dir)
|
|
if GUI and GUI:IsShown() then
|
|
local res = TAB_MANAGER.current_tab + (dir == 0 and 1 or -1)
|
|
|
|
TAB_MANAGER:SetCurrentTab(
|
|
(res < 1 and TAB_MANAGER.tab_count) or
|
|
(res > TAB_MANAGER.tab_count and 1)
|
|
or res
|
|
)
|
|
end
|
|
end
|
|
|
|
function on_key_press(key)
|
|
if key ~= MCM_SETTINGS.keybind then return end
|
|
|
|
if (not mcm_keybinds) then
|
|
open_radial_menu()
|
|
return
|
|
end
|
|
|
|
if ui_mcm.get_mod_key(MCM_SETTINGS.second_key) then
|
|
open_radial_menu()
|
|
end
|
|
end
|
|
|
|
function on_key_release(key)
|
|
if key ~= MCM_SETTINGS.keybind then return end
|
|
|
|
if ui_mcm.get_mod_key(MCM_SETTINGS.second_key) then
|
|
open_radial_menu()
|
|
end
|
|
end
|
|
|
|
function on_key_hold(key)
|
|
if key ~= MCM_SETTINGS.keybind then return end
|
|
|
|
if ui_mcm.get_mod_key(MCM_SETTINGS.second_key) then
|
|
if ui_mcm.key_hold(script_name(), key) then
|
|
open_radial_menu()
|
|
end
|
|
end
|
|
end
|
|
|
|
function on_before_key_press(key, bind, dis, flags)
|
|
if bind == key_bindings.kWPN_NEXT and (MCM_SETTINGS.override_ammo_wheel) and (TAB_MANAGER.tab_by_handler.grenades) then
|
|
local active_item = db.actor:active_item()
|
|
|
|
if active_item and IsGrenade(active_item) then
|
|
flags.ret_value = false -- disable engine grenade switch
|
|
end
|
|
elseif override_quick_keys[bind] then
|
|
if TAB_MANAGER.tabs[override_quick_keys[bind]] then
|
|
open_radial_menu(override_quick_keys[bind])
|
|
end
|
|
end
|
|
end
|
|
|
|
function start_ammo_wheel_override()
|
|
local wpn = db.actor:active_item()
|
|
|
|
local tbh = TAB_MANAGER.tab_by_handler
|
|
|
|
if (not wpn) then
|
|
if db.actor:active_detector() and tbh.devices then
|
|
open_radial_menu(tbh.devices)
|
|
elseif tbh.slots then
|
|
open_radial_menu(tbh.slots)
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
if IsWeapon(wpn) and (not IsItem("fake_ammo_wpn",wpn:section())) then
|
|
if key_state(DIK_keys.DIK_LSHIFT) ~= 0 and tbh.attachments then
|
|
open_radial_menu(tbh.attachments)
|
|
elseif key_state(DIK_keys.DIK_LMENU) ~= 0 and tbh.devices then
|
|
open_radial_menu(tbh.devices)
|
|
elseif tbh.ammo then
|
|
open_radial_menu(tbh.ammo)
|
|
end
|
|
elseif (IsBolt(wpn) or db.actor:active_detector()) and tbh.devices then
|
|
open_radial_menu(tbh.devices)
|
|
elseif IsGrenade(wpn) and tbh.grenades then
|
|
open_radial_menu(tbh.grenades)
|
|
elseif ((IsWeapon(wpn) and IsItem("fake_ammo_wpn",wpn:section())) or (IsItem("device", nil, wpn))) and tbh.slots then
|
|
open_radial_menu(tbh.slots)
|
|
end
|
|
end
|
|
|
|
function on_option_change(mcm)
|
|
if (not mcm) then return end
|
|
|
|
for _, subtab in pairs(options.gr) do
|
|
for _, option in pairs(subtab.gr) do
|
|
if option.val then
|
|
MCM_SETTINGS[option.id] = ui_mcm.get(strformat("%s/%s/%s", script_name(), subtab.id, option.id))
|
|
end
|
|
end
|
|
end
|
|
|
|
local simple_press = MCM_SETTINGS.hold and "on_key_press" or "on_key_release"
|
|
|
|
local to_register = MCM_SETTINGS.modifier == 0 and simple_press or "on_key_hold"
|
|
local to_unregister = MCM_SETTINGS.modifier == 0 and "on_key_hold" or simple_press
|
|
|
|
RegisterScriptCallback(to_register, this[to_register])
|
|
UnregisterScriptCallback(to_unregister, this[to_unregister])
|
|
|
|
if MCM_SETTINGS.hold then
|
|
UnregisterScriptCallback("on_key_release", on_key_release)
|
|
else
|
|
UnregisterScriptCallback("on_key_press", on_key_press)
|
|
end
|
|
|
|
TAB_MANAGER:SetTabCount(MCM_SETTINGS.tab_count)
|
|
|
|
TAB_MANAGER.dynamic_tabs = {}
|
|
|
|
for _, category in pairs(tab_categories) do
|
|
if MCM_SETTINGS[category .. "_enabled"] then
|
|
local tab = MCM_SETTINGS[category .. "_tab"]
|
|
|
|
if TAB_MANAGER.tabs[tab] then
|
|
TAB_MANAGER.dynamic_tabs[tab] = category
|
|
end
|
|
end
|
|
end
|
|
|
|
for i = 1, 4 do
|
|
if MCM_SETTINGS["quck_use_" .. i .. "_enabled"] then
|
|
override_quick_keys[60 + i] = MCM_SETTINGS["quck_use_" .. i .. "_tab"]
|
|
else
|
|
override_quick_keys[60 + i] = nil
|
|
end
|
|
end
|
|
|
|
TAB_MANAGER.tab_by_handler = invert_table(TAB_MANAGER.dynamic_tabs)
|
|
|
|
GUI = nil -- refresh GUI
|
|
end
|
|
|
|
function on_game_start()
|
|
load_defaults()
|
|
TAB_MANAGER = TabManager()
|
|
|
|
RegisterScriptCallback("on_key_press", on_key_press)
|
|
RegisterScriptCallback("on_before_key_press", on_before_key_press)
|
|
|
|
RegisterScriptCallback("on_option_change", on_option_change)
|
|
|
|
RegisterScriptCallback("actor_on_net_destroy", actor_on_net_destroy)
|
|
|
|
if MODDED_EXES_VERSION and (MODDED_EXES_VERSION >= 20231209) then
|
|
RegisterScriptCallback("on_mouse_wheel", throttle(on_mouse_wheel, 50))
|
|
end
|
|
|
|
custom_functor_autoinject.add_functor(unpack(add_to_qaw_functor))
|
|
|
|
_start_ammo_wheel = item_weapon.start_ammo_wheel
|
|
|
|
function item_weapon.start_ammo_wheel()
|
|
if (not MCM_SETTINGS.override_ammo_wheel) then
|
|
_start_ammo_wheel()
|
|
return
|
|
end
|
|
start_ammo_wheel_override()
|
|
end
|
|
|
|
on_option_change(mcm_keybinds)
|
|
end |