Divergent/mods/UI Rework G.A.M.M.A. Style/gamedata/scripts/utils_ui.script

3546 lines
105 KiB
Plaintext
Raw Normal View History

--[[
- Tronex
- 2020/2/3
- Item cell utilities
- Simulation to engine CUI classes in LUA, have a full control over how things work
Item cell:
Item icon and its related elements (Icon layers, attachments, condition bar, stack counter, upgrade indicator, color)
Support: set/update function to adapt to changes, highlight area with custom color
Item container:
A place to holds and organize item cells together, easy to set or remove item cells with indexing methods
Support for cell hover/highlight, drag and drop and callbacks with owner classes
Item properties:
Show a list of context that player can set and customize for the item cell
Item info box:
For item cells
Consist of: name, weight, price/points (based on mode), description, stats list (icon / text), ammo list
Support stats comparison with equipped items of the same slot
Upgrade info box:
For item upgrade cells
Consist of: name, description, state or pre-requirements, stats list (icon + text)
------------------
TODO:
- Upgrades support for info box
- info box pos relative to mouse need improvements
--]]
local ratio = utils_xml.screen_ratio()
local uwide_ratio = ((ratio < 0.75) and 0.75 or 1) -- Added by Sota
local XMLP = CScriptXmlInit()
XMLP:ParseFile("utils.xml")
-- Icon coloring base on inventory mode and interaction
local clr_list = {
["def"] = GetARGB(255, 255, 255, 255),
["red"] = GetARGB(255, 255, 50, 50),
["green"] = GetARGB(255, 100, 255, 150),
["blue"] = GetARGB(255, 100, 150, 255),
["black"] = GetARGB(255, 0, 0, 0),
["drag"] = GetARGB(150, 255, 255, 255),
["hide"] = GetARGB(70, 255, 255, 255),
["shadow"] = GetARGB(200, 20, 20, 20),
["hide_shadow"] = GetARGB(70, 20, 20, 20),
["p1"] = GetARGB(255, 170, 170, 170),
["n1"] = GetARGB(255, 200, 50, 50),
["p2"] = GetARGB(255, 51, 255, 102),
["n2"] = GetARGB(255, 204, 0, 51),
["info_def"] = GetARGB(200, 255, 255, 255),
["info_p"] = GetARGB(255, 56, 209, 115),
["info_n"] = GetARGB(255, 238, 28, 36),
["info_p_txt"] = GetARGB(200, 100, 255, 100),
["info_n_txt"] = GetARGB(200, 255, 100, 100),
}
local clr_list_hl = {
["def"] = GetARGB(25, 255, 255, 255),
["green"] = GetARGB(30, 100, 255, 150),
["blue"] = GetARGB(40, 100, 200, 255),
}
-- Progress bar profiles based on "condition_bar" parameter in item section
local bar_list = {
["condition_progess_bar"] = { min= {255,196,18,18,0}, mid= {255,255,255,118,0.5}, max= {255,107,207,119,1}, background= true},
["power_progess_bar"] = { def= GetARGB(255,86,196,209), background= true },
["uses_progess_bar"] = { def= GetARGB(255,255,255,255), background= false },
}
-- UI events
local K_M1 = DIK_keys.MOUSE_1
local K_M2 = DIK_keys.MOUSE_2
local K_SHFT = DIK_keys.DIK_LSHIFT
local E_PRESS = ui_events.WINDOW_KEY_PRESSED
local E_RELEASE = ui_events.WINDOW_KEY_RELEASED
-- Utils
local has_upgrades = utils_item.has_upgrades
local sync_element = utils_xml.sync_element
local sync_cursor = utils_xml.sync_cursor
local get_item_axis = utils_xml.get_item_axis
local get_item_trade_status = utils_item.get_item_trade_status
local get_item_cost = utils_item.get_item_cost
local lerp_color = utils_xml.lerp_color
-- Main Menu
local mm_active = true
function main_menu_on()
mm_active = true
end
function main_menu_off()
mm_active = false
end
function on_game_start()
RegisterScriptCallback("main_menu_on_init", main_menu_on)
RegisterScriptCallback("main_menu_on_quit", main_menu_off)
end
function get_time()
return mm_active and time_continual() or time_global()
end
-- Item order
local item_order = {}
function set_item_order()
local n = ini_sys:line_count("item_kind_order")
for i=0,n-1 do
local result, kind, order = ini_sys:r_line_ex("item_kind_order",i,"","")
if kind and order then
item_order[kind] = tonumber(order) or 30
end
end
item_order["na"] = size_table(item_order) + 1
end
set_item_order()
local ab_w, ab_h, ab_k = {}, {}, {}
local a_sec, a_w, a_h, a_k
local b_sec, b_w, b_h, b_k
function sort_info(asec, bsec)
-- A
a_sec = asec
if (not ab_w[a_sec]) then ab_w[a_sec] = SYS_GetParam(2,a_sec,"inv_grid_width",1) end
if (not ab_h[a_sec]) then ab_h[a_sec] = SYS_GetParam(2,a_sec,"inv_grid_height",1) end
if (not ab_k[a_sec]) then
ab_k[a_sec] = SYS_GetParam(0,a_sec,"kind","na")
if (not item_order[ab_k[a_sec]]) then
ab_k[a_sec] = "na"
end
end
a_w = ab_w[a_sec]
a_h = ab_h[a_sec]
a_k = item_order[ab_k[a_sec]]
-- B
b_sec = bsec
if (not ab_w[b_sec]) then ab_w[b_sec] = SYS_GetParam(2,b_sec,"inv_grid_width",1) end
if (not ab_h[b_sec]) then ab_h[b_sec] = SYS_GetParam(2,b_sec,"inv_grid_height",1) end
if (not ab_k[b_sec]) then
ab_k[b_sec] = SYS_GetParam(0,b_sec,"kind","na")
if (not item_order[ab_k[b_sec]]) then
ab_k[b_sec] = "na"
end
end
b_w = ab_w[b_sec]
b_h = ab_h[b_sec]
b_k = item_order[ab_k[b_sec]]
end
function sort_by_size(t,a,b)
if (type(t[a]) == "string") then
sort_info(t[a], t[b])
else
sort_info(t[a]:section(), t[b]:section())
end
-- Comparison
--printf("%s - %s", a_sec, b_sec)
if (a_w == b_w) then
if (a_h == b_h) then
if (a_sec == b_sec) then
if (type(t[a]) == "string") then
return false --true
end
return t[a]:id() > t[b]:id()
end
return a_sec < b_sec -- alphaptic order
end
return a_h > b_h
end
return a_w > b_w
end
function sort_by_kind(t,a,b)
if (type(t[a]) == "string") then
sort_info(t[a], t[b])
else
sort_info(t[a]:section(), t[b]:section())
end
if a_k == b_k then
return sort_by_size(t,a,b)
end
return a_k < b_k
end
function sort_by_index(t,a,b)
return t[a].index < t[b].index
end
function sort_by_sizekind(t,a,b)
if (type(t[a]) == "string") then
sort_info(t[a], t[b])
else
sort_info(t[a]:section(), t[b]:section())
end
-- Comparison
--printf("%s - %s", a_sec, b_sec)
--\\ bigger width wins
if (a_w == b_w) then
--\\ bigger height wins
if (a_h == b_h) then
--\\ important kind wins
if a_k == b_k then
--\\ alphaptic order wins
if (a_sec == b_sec) then
--\\ better condition wins
if (type(t[a]) == "string") then
return false --true
end
return t[a]:condition() > t[b]:condition()
end
return a_sec < b_sec
end
return a_k < b_k
end
return a_h > b_h
end
return a_w > b_w
end
function sort_by_props(t,a,b)
-- Only for objects with same sections
local sec = t[a]:section()
-- For ammo, bigger ammo counts wins
if IsItem("ammo",sec) and (not IsItem("grenade_ammo",sec)) then
return t[a]:ammo_get_count() > t[b]:ammo_get_count()
-- Upgraded items wins
elseif utils_item.has_upgrades(t[a]) and (not utils_item.has_upgrades(t[b])) then
return true
end
-- Better condition wins
return t[a]:condition() > t[b]:condition()
end
-- Item stats
stats_table = {}
function prepare_stats_table()
--[[
index : showing stat by numric order
typ : parameter value type, important for parsing the value out of parameter section
name : translation string
icon_p : icon to show on positive value
icon_n : icon to show on negavtive value (empty means use positive icon for all occasions)
track : should stat be represnted by a progress bar or text
magnitude : multiplier for parameter value, to represent it to the player for better readings
unit :
compare : allow state comparison with different items (for item info box)
sign : show number sign +/-
show_always : should we show state even if value was 0
value_functor: calculate paramter value by using a special function
--]]
if is_not_empty(stats_table) then return end
stats_table["weapon"] = {
["accuracy"] = { index= 1, typ= "float", name= "ui_inv_accuracy", icon_p= "ui_wp_prop_tochnost", icon_n= "", track= true, magnitude= 1, unit= "", compare= false, sign= false, show_always= true, value_functor= {"utils_ui","prop_accuracry"} },
["handling"] = { index= 2, typ= "float", name= "ui_inv_handling", icon_p= "ui_wp_prop_ergonomics", icon_n= "", track= true, magnitude= 1, unit= "", compare= false, sign= true, show_always= true, value_functor= {"utils_ui","prop_handling"} },
["damage"] = { index= 3, typ= "float", name= "ui_inv_damage", icon_p= "ui_wp_prop_damage", icon_n= "", track= true, magnitude= 1, unit= "", compare= false, sign= true, show_always= true , value_functor= {"utils_ui","prop_damage"} },
["fire_rate"] = { index= 4, typ= "float", name= "ui_inv_rate_of_fire", icon_p= "ui_wp_prop_skorostrelnost", icon_n= "", track= true, magnitude= 1, unit= "", compare= false, sign= false, show_always= true, value_functor= {"utils_ui","prop_rpm"} },
["ammo_mag_size"] = { index= 5, typ= "float", name= "ui_ammo_count", icon_p= "ui_wp_propery_07", icon_n= "", track= false, magnitude= 1, unit= "", compare= false, sign= false ,show_always= true },
}
stats_table["outfit"] = {
["fire_wound_protection"] = { index= 1, typ= "float", name= "ui_inv_outfit_fire_wound_protection", icon_p= "ui_am_propery_01", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","max_fire_wound_protection")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "FireWound"}},
["burn_protection"] = { index= 2, typ= "float", name= "ui_inv_outfit_burn_protection", icon_p= "ui_am_prop_thermo", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","fire_zone_max_power")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Burn"} },
["shock_protection"] = { index= 3, typ= "float", name= "ui_inv_outfit_shock_protection", icon_p= "ui_am_prop_electro", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","electra_zone_max_power")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Shock"}},
["chemical_burn_protection"] = { index= 4, typ= "float", name= "ui_inv_outfit_chemical_burn_protection", icon_p= "ui_am_prop_chem", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","acid_zone_max_power")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "ChemicalBurn"}},
["radiation_protection"] = { index= 5, typ= "float", name= "ui_inv_outfit_radiation_protection", icon_p= "ui_am_propery_09", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","radio_zone_max_power")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Radiation"}},
["telepatic_protection"] = { index= 6, typ= "float", name= "ui_inv_outfit_telepatic_protection", icon_p= "ui_am_propery_11", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","psi_zone_max_power")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Telepatic"}},
["wound_protection"] = { index= 7, typ= "float", name= "ui_inv_outfit_wound_protection", icon_p= "ui_am_prop_wound", icon_n= "", track= true, magnitude= (1/SYS_GetParam(2,"actor_condition","max_wound_protection")), unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Wound"}},
["strike_protection"] = { index= 8, typ= "float", name= "ui_inv_outfit_strike_protection", icon_p= "ui_am_prop_strike", icon_n= "", track= true, magnitude= 1, unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Strike"}},
["explosion_protection"] = { index= 9, typ= "float", name= "ui_inv_outfit_explosion_protection", icon_p= "ui_am_prop_explo", icon_n= "", track= true, magnitude= 1, unit= "", condition= true, compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_protection", "Explosion"}},
["artefact_count"] = { index= 10, typ= "float", name= "ui_inv_outfit_artefact_count", icon_p= "ui_am_prop_artefact",icon_n= "", track= false, magnitude= 1, unit= "", compare= false, sign= false,show_always= true , value_functor= {"utils_item","get_outfit_belt_size"}},
["additional_inventory_weight"]= { index= 11, typ= "float", name= "ui_inv_outfit_additional_weight", icon_p= "ui_am_propery_08", icon_n= "ui_am_prop_carry_weight_negative", track= false, magnitude= 1, unit= "st_kg", compare= false, sign= true, show_always= true , value_functor= {"utils_item","get_outfit_property", "additional_inventory_weight"}},
}
stats_table["backpack"] = {
["additional_inventory_weight"]= { index= 1, typ= "float", name= "ui_inv_outfit_additional_weight", icon_p= "ui_am_propery_08", icon_n= "ui_am_prop_carry_weight_negative", track= false, magnitude= 1, unit= "st_kg", compare= false, sign= true, show_always= true},
}
stats_table["artefact"] = {
["condition"] = { index= 1, typ= "float", name= "ui_inv_af_condition", icon_p= "ui_am_condition", icon_n= "", track= false, magnitude= 100, unit= "st_perc", compare= false, sign= true, show_always= true , value_functor= {"utils_ui","prop_condition"} },
["health_restore_speed"] = { index= 2, typ= "float", name= "ui_inv_health", icon_p= "ui_am_propery_05", icon_n= "ui_am_prop_health_negative", track= false, magnitude= 6600, unit= "", condition= true, compare= false, sign= true, show_always= false },
["radiation_restore_speed"] = { index= 3, typ= "float", name= "ui_inv_radiation", icon_p= "ui_am_propery_09", icon_n= "ui_am_prop_radio_restore", track= false, magnitude= 47000, unit= "st_msv_sec", condition= true, compare= false, sign= true, show_always= false, sign_inverse= true },
["satiety_restore_speed"] = { index= 4, typ= "float", name= "ui_inv_satiety", icon_p= "ui_am_prop_satiety_restore_speed", icon_n= "ui_am_prop_satiety", track= false, magnitude= 100, unit= "", condition= true, compare= false, sign= true, show_always= false },
["power_restore_speed"] = { index= 5, typ= "float", name= "ui_inv_power", icon_p= "ui_am_propery_07", icon_n= "ui_am_prop_power_restore", track= false, magnitude= 30000, unit= "st_perc", condition= true, compare= false, sign= true, show_always= false },
["bleeding_restore_speed"] = { index= 6, typ= "float", name= "ui_inv_bleeding", icon_p= "ui_am_prop_restore_bleeding", icon_n= "ui_am_prop_bleeding_restore", track= false, magnitude= 15000, unit= "st_ml_min", condition= true, compare= false, sign= true, show_always= false },
["burn_immunity"] = { index= 7, typ= "float", name= "ui_inv_outfit_burn_protection", icon_p= "ui_am_prop_thermo", icon_n= "ui_am_prop_burn_immunity", track= false, magnitude= 84800, unit= "st_perc", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["shock_immunity"] = { index= 8, typ= "float", name= "ui_inv_outfit_shock_protection", icon_p= "ui_am_prop_electro", icon_n= "ui_am_prop_shock_immunity", track= false, magnitude= 8000, unit= "st_kv", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["radiation_immunity"] = { index= 9, typ= "float", name= "ui_inv_outfit_radiation_protection", icon_p= "ui_am_propery_09", icon_n= "ui_am_prop_radiation_immunity", track= false, magnitude= 750, unit= "st_perc", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["telepatic_immunity"] = { index= 10, typ= "float", name= "ui_inv_outfit_telepatic_protection", icon_p= "ui_am_propery_11", icon_n= "ui_am_prop_telepat_immunity", track= false, magnitude= 5800, unit= "st_perc", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["chemical_burn_immunity"] = { index= 11, typ= "float", name= "ui_inv_outfit_chemical_burn_protection", icon_p= "ui_am_prop_chem", icon_n= "ui_am_prop_chemburn_immunity", track= false, magnitude= 19100, unit= "st_perc", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["wound_immunity"] = { index= 12, typ= "float", name= "ui_inv_outfit_wound_protection", icon_p= "ui_am_prop_wound", icon_n= "ui_am_prop_wound_minus", track= false, magnitude= 2500, unit= "st_j", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["fire_wound_immunity"] = { index= 13, typ= "float", name= "ui_inv_outfit_fire_wound_protection", icon_p= "ui_am_propery_01", icon_n= "ui_am_prop_fire_wound_negative", track= false, magnitude= 2500, unit= "st_j", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["explosion_immunity"] = { index= 14, typ= "float", name= "ui_inv_outfit_explosion_protection", icon_p= "ui_am_prop_explo", icon_n= "ui_am_prop_explo_minus", track= false, magnitude= 2500, unit= "st_j", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["strike_immunity"] = { index= 15, typ= "float", name= "ui_inv_outfit_strike_protection", icon_p= "ui_am_prop_strike", icon_n= "ui_am_prop_strike_minus", track= false, magnitude= 2500, unit= "st_j", condition= true, compare= false, sign= true, show_always= false ,section= "hit_absorbation_sect" },
["additional_inventory_weight"]= { index= 16, typ= "float", name= "ui_inv_outfit_additional_weight", icon_p= "ui_am_propery_08", icon_n= "ui_am_prop_carry_weight_negative", track= false, magnitude= 1000, unit= "st_g", compare= false, sign= true, show_always= false },
}
stats_table["booster"] = {
["boost_time"] = { index= 1, typ= "float", name= "ui_inv_effect_time", icon_p= "ui_am_prop_time_period", icon_n= "", track= false, magnitude= 1, unit= "ui_inv_seconds", compare= false, sign= true, show_always= false },
["boost_health_restore"] = { index= 2, typ= "float", name= "ui_inv_health", icon_p= "ui_am_propery_05", icon_n= "", track= false, magnitude= 1000, unit= "st_perc", compare= false, sign= true, show_always= false },
["boost_radiation_restore"] = { index= 3, typ= "float", name= "ui_inv_radiation", icon_p= "ui_am_propery_09", icon_n= "", track= false, magnitude= 30000, unit= "st_msv_sec", compare= false, sign= true, show_always= false, sign_inverse_txt= true },
["eat_satiety"] = { index= 4, typ= "float", name= "ui_inv_satiety", icon_p= "ui_am_prop_satiety_restore_speed", icon_n= "ui_am_prop_satiety", track= false, magnitude= 1000, unit= "st_kcal", compare= false, sign= true, show_always= false },
["boost_anabiotic"] = { index= 5, typ= "float", name= "", icon_p= "ui_am_prop_Vibros", icon_n= "ui_am_prop_anabiotic", track= false, magnitude= 1000, unit= "", compare= false, sign= true, show_always= false },
["boost_power_restore"] = { index= 6, typ= "float", name= "ui_inv_power", icon_p= "ui_am_propery_07", icon_n= "ui_am_prop_power_restore", track= false, magnitude= 100000, unit= "st_microg", compare= false, sign= true, show_always= false },
["boost_bleeding_restore"] = { index= 7, typ= "float", name= "ui_inv_bleeding", icon_p= "ui_am_prop_restore_bleeding", icon_n= "ui_am_prop_bleeding_restore", track= false, magnitude= 150000, unit= "st_ml_min", compare= false, sign= true, show_always= false },
["boost_radiation_protection"] = { index= 8, typ= "float", name= "ui_inv_outfit_radiation_protection", icon_p= "ui_am_propery_09", icon_n= "ui_am_prop_radiation_protection", track= false, magnitude= 12000, unit= "st_msv", compare= false, sign= true, show_always= false },
["boost_telepat_protection"] = { index= 9, typ= "float", name= "ui_inv_outfit_telepatic_protection", icon_p= "ui_am_propery_11", icon_n= "ui_am_prop_telepat_protection", track= false, magnitude= 1500, unit= "st_mt", compare= false, sign= true, show_always= false },
["boost_chemburn_protection"] = { index= 10, typ= "float", name= "ui_inv_outfit_chemical_burn_protection", icon_p= "ui_am_prop_chem", icon_n= "ui_am_prop_chemburn_protection", track= false, magnitude= 24750, unit= "st_perc", compare= false, sign= true, show_always= false },
["boost_burn_immunity"] = { index= 11, typ= "float", name= "ui_inv_outfit_burn_protection", icon_p= "ui_am_prop_thermo", icon_n= "ui_am_prop_burn_immunity", track= false, magnitude= 300, unit= "st_kw_m2", compare= false, sign= true, show_always= false },
["boost_shock_immunity"] = { index= 12, typ= "float", name= "ui_inv_outfit_shock_protection", icon_p= "ui_am_prop_electro", icon_n= "ui_am_prop_shock_immunity", track= false, magnitude= 480, unit= "st_v", compare= false, sign= true, show_always= false },
["boost_radiation_immunity"] = { index= 13, typ= "float", name= "ui_inv_outfit_radiation_protection", icon_p= "ui_am_propery_09", icon_n= "ui_am_prop_radiation_immunity", track= false, magnitude= 3000, unit= "st_msv", compare= false, sign= true, show_always= false },
["boost_telepat_immunity"] = { index= 14, typ= "float", name= "ui_inv_outfit_telepatic_protection", icon_p= "ui_am_propery_11", icon_n= "ui_am_prop_telepat_immunity", track= false, magnitude= 300, unit= "st_mt", compare= false, sign= true, show_always= false },
["boost_chemburn_immunity"] = { index= 15, typ= "float", name= "ui_inv_outfit_chemical_burn_protection", icon_p= "ui_am_prop_chem", icon_n= "ui_am_prop_chemburn_immunity", track= false, magnitude= 380000, unit= "st_perc", compare= false, sign= true, show_always= false },
["boost_max_weight"] = { index= 16, typ= "float", name= "ui_inv_outfit_additional_weight", icon_p= "ui_am_propery_08", icon_n= "ui_am_prop_max_weight", track= false, magnitude= 1000, unit= "st_g", compare= false, sign= true, show_always= false },
}
end
function add_stats_table(k1,k2,v)
if is_not_empty(stats_table) then return end
if (not stats_table[k1]) then
stats_table[k1] = {}
end
stats_table[k1][k2] = v
end
function get_stats_table(sec)
prepare_stats_table()
if ini_sys:r_string_ex(sec, "ammo_class") and (not IsItem("fake_ammo_wpn", sec)) then
return stats_table["weapon"]
elseif IsItem("outfit", sec) or IsItem("helmet", sec) then
return stats_table["outfit"]
elseif IsItem("artefact", sec) then
return stats_table["artefact"]
elseif IsItem("consumable", sec) then
return stats_table["booster"]
elseif IsItem("backpack", sec) then
return stats_table["backpack"]
end
return nil
end
function get_stats_value(obj, sec, gr, stat)
prepare_stats_table()
if type(gr) == "string" then
gr = stats_table[gr]
end
if (not gr) then
return
end
local sect = gr.section and SYS_GetParam(0, sec, gr.section) or sec
local value = gr.value_functor and get_stats_func_value(obj, sec, unpack(gr.value_functor))
if (not value) then
value = utils_item.get_param(sect, obj and obj:id(), stat, (gr.typ or "float"), true)
end
if obj and value and gr.condition then
value = value * obj:condition()
end
return (value ~= 0) and value
end
function get_stats_func_value(obj, sec, file, func, ...)
if file and func and _G[file] and _G[file][func] then
return _G[file][func](obj,sec,...)
end
end
function get_stats_string_value(obj, sec, gr, stat, to_text)
if type(gr) == "string" then
gr = stats_table[gr]
end
if (not gr) then
return
end
local value = get_stats_value(obj, sec, gr, stat)
if (not value) then
return
end
if gr.track then
local valbar = clamp((math.abs(value) * gr.magnitude), 0, 1)
return to_text and (math.ceil(valbar * 100) .. "%") or valbar
else
local valbar = value * gr.magnitude
local unit = gr.unit and gr.unit ~= "" and game.translate_string(gr.unit) or ""
local clr = valbar >= 0 and clr_list["p1"] or clr_list["n1"]
valbar = math.ceil(valbar)
if gr.sign_inverse then
clr = valbar < 0 and clr_list["p1"] or clr_list["n1"]
end
local sign = gr.sign and valbar > 0 and "+" or ""
if gr.sign_inverse_txt then
if valbar > 0 then
sign = "-"
elseif valbar < 0 then
valbar = -1 * valbar
sign = "+"
end
end
return ( sign .. valbar .. " " .. unit ), (clr == clr_list["n1"] and true or false)
end
end
function get_stats_xml(handler, obj, sec, gr, stat)
if type(gr) == "string" then
gr = stats_table[gr]
end
if (not gr) then
return
end
local value, nclr = get_stats_string_value(obj, sec, gr, stat)
if (not value) then
return
end
local st = XMLP:InitStatic("item_info:stats_box", handler)
-- Set up icon
local st_icon = XMLP:InitStatic("item_info:stats_box:icon", st)
local icon_p = gr.icon_p
local icon_n = (gr.icon_n ~= "") and gr.icon_n or icon_p
local icon = icon_p
if (type(value) == "number") then
icon = value < 0 and icon_n or icon_p
if gr.sign_inverse then
icon = value < 0 and icon_p or icon_n
end
end
st_icon:InitTexture( icon )
-- Set up name
local st_cap = XMLP:InitTextWnd("item_info:stats_box:cap", st)
st_cap:SetText( game.translate_string(gr.name) )
-- Set up bar
if gr.track then
local st_bar = XMLP:InitProgressBar("item_info:stats_box:prog_bar", st)
st_bar:SetProgressPos( clamp(value,0,1) )
st_bar:SetColor( clr_list["info_def"] )
-- Set up text
else
local st_txt = XMLP:InitTextWnd("item_info:stats_box:prog_txt", st)
st_txt:SetText( value )
st_txt:SetTextColor( nclr and clr_list["n1"] or clr_list["p1"] )
end
st:SetAutoDelete(true)
return st
end
-------------------------------------------------------------------
-- Item Cell
-------------------------------------------------------------------
class "UICellItem"
function UICellItem:__init(container, st, indx, manual)
-- To define free item cell:
-- UICellItem( { path= <string> , xml= <xml_file> , grid_size= <number>, grid_line= <number> } , { path= <string>, base= <xml_element>} )
self.manual = manual -- manual placement on container, some properties are disabled
self.container = container
self.path = container.path
self.indx = indx
self.showcase = 0 -- [0] attached to object / [1] attached to section (showcase) / [2] same as 1 with no cell room adjust
self.cx = manual and ("cell_" .. indx) or "cell" -- in manual placement, cells are specified by their index in xml file
self.grid_size = container.grid_size or 41
self.grid_line = container.grid_line or 0
self.flags = {} -- you can pass custom info here
self.disable_bar = container.disable_bar and true or false
self:InitControls(self.path, st)
self:Print(nil, "__init for path (%s)", self.path)
end
function UICellItem:InitControls(path, st)
local xml = self:GetXML()
-- Free cell
local free_cell = (type(st) == "table")
if free_cell then
st = self.container.xml:InitStatic(st.path, st.base)
self.st = st
end
self.hl = xml:InitStatic(path .. ":" .. self.cx .. ":highlight", st)
self.cell = xml:InitStatic(path .. ":" .. self.cx, st)
self.shadow = xml:InitStatic(path .. ":" .. self.cx .. ":pic", self.cell)
self.ico = xml:InitStatic(path .. ":" .. self.cx .. ":pic", self.cell)
-- Highlight texture
--self.hl:InitTexture("ui_button_inv_h")
self.hl:SetStretchTexture(true)
self.hl:Show(false)
sync_element(self.hl, self.cell)
end
function UICellItem:Set(obj, area)
local xml = self:GetXML()
local path = self.path
local is_obj = (self.showcase == 0)
local grid_size = self.grid_size
local sec = is_obj and obj:section() or obj
local clsid = is_obj and obj:clsid()
local x = SYS_GetParam(2,sec, "inv_grid_x")
local y = SYS_GetParam(2,sec, "inv_grid_y")
local w = SYS_GetParam(2,sec, "inv_grid_width")
local h = SYS_GetParam(2,sec, "inv_grid_height")
if not x or not y or not w or not h then
return false
end
x = x * grid_size
y = y * grid_size
w = w * grid_size
h = h * grid_size
self.ID = is_obj and obj:id() or nil
self.section = sec
self.area = area
--self.X = x
--self.Y = y
self.W = w
self.H = h
-- Trade mode
--if self:Check_TradeMode(obj, sec) then
--return false
--end
-- Cell pos
if area and (not self.manual) then
local area_x = ((area.x * grid_size) - grid_size) * ratio
local area_y = (area.y * grid_size) - grid_size
-- Edited by Sota
--local area_xl = self.grid_line * area.x
local area_xl = self.grid_line * area.x * ratio
local area_yl = self.grid_line * area.y
self.cell:SetWndPos(vector2():set( area_x + area_xl , area_y + area_yl ))
self.cell:SetWndSize(vector2():set(w * ratio , h))
sync_element(self.hl, self.cell)
self:Print(nil, "Set for [%s] in (%s,%s,%s,%s)", sec, area_x, area_y, w, h)
-- Cell pos (free cell)
elseif self.st then
local st_x = ((self.st:GetWidth() /2) - ((w * ratio) /2))
local st_y = ((self.st:GetHeight() /2) - (h /2))
self.cell:SetWndPos(vector2():set( st_x , st_y ))
self.cell:SetWndSize(vector2():set(w * ratio , h))
sync_element(self.hl, self.cell)
self:Print(nil, "Set for [%s] in free cell", sec)
end
-- Icon
self:Add_Icon(sec, w, h)
self:Add_Shadow(sec, w, h)
-- Icon layers
self:Add_Layers(xml, obj, sec, clsid)
-- Update cell
local is_updated = self:Update(is_obj and obj)
if (not is_updated) then
self:Print(nil, "Updating failed for [%s] | Reset cell", sec)
self:Reset()
return false
end
self.cell:Show(true)
return true
end
function UICellItem:Update(obj)
obj = obj or (self.ID and level.object_by_id(self.ID))
if (self.showcase == 0) and (not obj) then
self:ResetToChild()
return false
end
self:Print(nil, "Update for [%s]", sec)
local sec = self.section
local xml = self:GetXML()
if obj then
local clsid = obj:clsid()
-- Update condition bar
self:Add_ProgressBar(xml, obj, sec, clsid)
-- Update attachments
self:Add_Attachements(xml, obj, sec, clsid)
end
-- Update stack counter
self:Add_Counter(xml, obj, sec)
-- Update upgrade indicator
self:Add_Upgrade(xml, obj, sec)
-- Update trade mode
if self:Check_TradeMode(obj, sec) then
return false
end
-- Update color
self:Colorize( self.flags.no_trade and "red" or "def" )
return true
end
function UICellItem:Add_Icon(sec, w, h)
local rot = self.ico:GetHeading() > 0
self.ico:InitTexture( utils_xml.get_icons_texture(sec) )
self.ico:SetTextureRect(Frect():set( get_item_axis(sec, nil, true) ))
self.ico:SetStretchTexture(true)
self.ico:SetWndSize(vector2():set( w * (rot and 1 or ratio) , h * (rot and ratio or 1) ))
self.ico:Show(true)
utils_xml.align_to_center(self.ico, self.cell)
self:Print(nil, "Show icon")
end
function UICellItem:Add_Shadow(sec, w, h)
local rot = self.shadow:GetHeading() > 0
self.shadow:InitTexture( utils_xml.get_icons_texture(sec) )
self.shadow:SetTextureRect(Frect():set( get_item_axis(sec, nil, true) ))
self.shadow:SetStretchTexture(true)
self.shadow:SetWndSize(vector2():set( w * (rot and 1 or ratio) , h * (rot and ratio or 1) ))
self.shadow:Show(true)
self.shadow:Show(true)
utils_xml.align_to_center(self.shadow, self.cell)
local pos = self.ico:GetWndPos()
self.shadow:SetWndPos( vector2():set( pos.x + 1 , pos.y + 2 ) )
self.shadow:SetTextureColor( clr_list["shadow"] )
self:Print(nil, "Show shadow")
end
function UICellItem:Add_Layers(xml, obj, sec, clsid)
-- Hide all layers
if self.layer then
for i=1,#self.layer do
self.layer[i]:Show(false)
end
end
-- Add all possible layers
local ii = 0
while (SYS_GetParam(0, sec, (ii+1).."icon_layer") ~= nil) do
ii = ii + 1
if (not self.layer) then
self.layer = {}
end
if (not self.layer[ii]) then
if (not xml) then
xml = self:GetXML()
end
self.layer[ii] = xml:InitStatic(self.path .. ":" .. self.cx .. ":pic", self.ico)
end
local icon_layer = SYS_GetParam(0, sec, ii.."icon_layer")
self:Create_Layer(self.layer[ii], self.ico, sec, icon_layer, ii.."icon_layer_x", ii.."icon_layer_y", ii.."icon_layer_scale")
self:Print(nil, "Show layer (%s) [%s]", ii, icon_layer)
end
end
function UICellItem:Add_ProgressBar(xml, obj, sec, clsid)
-- Hide all bars
if self.bar then
self.bar:Show(false)
end
-- On full stacking, hide bar
if self.container.stack_all and (self.childs and is_not_empty(self.childs)) then
return
end
-- No bar on showcase cells
if (self.showcase ~= 0) or self.disable_bar then
return
end
-- Bar is applied only to classes that support conditon
local has_cond = SYS_GetParam(1,sec, "use_condition") or IsWeapon(nil,clsid) or IsOutfit(nil,clsid) or IsHeadgear(nil,clsid)
if (not has_cond) then
return
end
-- Get bar type
local str = SYS_GetParam(0,sec, "condition_bar","condition_progess_bar")
-- Init if it doesn't exist
if (not self.bar) then
if (not xml) then
xml = self:GetXML()
end
self.bar = xml:InitProgressBar(self.path .. ":" .. self.cx .. ":bar", self.cell)
--self.bar:UseColor( true )
end
-- Not manual -> bar is set at the left-bottom corner
if (not self.manual) then
local h_bar = self.bar:GetHeight()
self.bar:SetWndPos(vector2():set( 0 , (self.H - h_bar)))
end
-- Set progress
local cond = obj:condition()
if IsItem("multiuse",sec) then
cond = (obj:get_remaining_uses()/10)
else
cond = round_idp(cond,1)
end
self.bar:SetProgressPos( clamp(cond,0,1) )
-- Bar properties
local props = bar_list[str]
local color = props.def or lerp_color( cond , props.min , props.mid , props.max )
if color then
self.bar:SetColor( color )
end
self.bar:ShowBackground( props.background )
self.bar:Show(true)
self:Print(nil, "Show progress bar: %s - cond: %s", str, cond)
end
function UICellItem:Add_Counter(xml, obj, sec)
-- Hide counter
if self.cnt then
self.cnt:Show(false)
end
-- We don't need counter for showcase icons
if (self.showcase ~= 0) then
return
end
-- Bar is applied only to ammo or stacked items
local is_ammo = IsItem("ammo",sec) and (not IsItem("grenade_ammo",sec))
local has_childs = self:HasChild()
if not (is_ammo or has_childs) then
return
end
-- Init if it doesn't exist
if (not self.cnt) then
if (not xml) then
xml = self:GetXML()
end
self.cnt = xml:InitStatic(self.path .. ":" .. self.cx .. ":cnt", self.cell)
end
-- Ammo count
if is_ammo then
local cnt = 0
if self.childs then
for id,c in pairs(self.childs) do
cnt = cnt + c
end
end
local ammo_cnt = ((self.showcase ~= 0) and IsItem("ammo",sec)) or (obj and obj:ammo_get_count()) or 0
self.cnt:TextControl():SetText("x" .. (ammo_cnt + cnt) )
self.cnt:Show(true)
-- Stacked items
elseif has_childs then
self.cnt:TextControl():SetText("x" .. (size_table(self.childs) + 1) )
self.cnt:Show(true)
end
self:Print(nil, "Show counter | ammo: %s - stacked: %s", is_ammo, has_childs)
end
function UICellItem:Add_Upgrade(xml, obj, sec)
-- Hide upgrade indicator
if self.upgr then
self.upgr:Show(false)
end
-- On full stacking, hide upgrade indicator
if self.container.stack_all and (self.childs and is_not_empty(self.childs)) then
return
end
-- Bar is applied only to upgraded items
if (not has_upgrades(obj, sec)) then
return
end
-- Init if it doesn't exist
if (not self.upgr) then
if (not xml) then
xml = self:GetXML()
end
self.upgr = xml:InitStatic(self.path .. ":cell:upgrade", self.ico)
end
-- Positioning
local ico = self.ico
local xx = 1
local yy = 1
local rot = ico:GetHeading() > 0
if rot then
local w_b, h_b = ico:GetWidth(), ico:GetHeight()
local x_st = (w_b/2) - (h_b/2)
local y_st = h_b + x_st
xx = xx + x_st
yy = yy + (y_st - w_b)
end
self.upgr:SetWndPos(vector2():set( xx , yy ))
self.upgr:Show(true)
self:Print(nil, "Show upgrade indicator")
end
function UICellItem:Add_Attachements(xml, obj, sec, clsid)
-- Hide attachments
if self.ico_scope then self.ico_scope:Show(false) end
if self.ico_sil then self.ico_sil:Show(false) end
if self.ico_gl then self.ico_gl:Show(false) end
-- On full stacking, hide counter
if self.container.stack_all and (self.childs and is_not_empty(self.childs)) then
return
end
-- No attachments on showcase cells
if (self.showcase ~= 0) then
return
end
-- No attachments for non-firearms
local magazined_wpn = IsWeapon(nil,clsid) and (not IsItem("fake_ammo_wpn",sec))
if (not magazined_wpn) then
return
end
-- Scope
local scope = (not utils_item.has_scope(sec)) and utils_item.get_attached_scope(obj)
if scope then
if (not self.ico_scope) then
if (not xml) then
xml = self:GetXML()
end
self.ico_scope = xml:InitStatic(self.path .. ":" .. self.cx .. ":pic", self.ico)
end
local scopes_sect = utils_item.get_param(sec, obj:id(), "scopes_sect", "string", false)
self:Create_Layer(self.ico_scope, self.ico, scopes_sect, scope, "scope_x", "scope_y")
self:Print(nil, "Show scope")
end
-- Silencer
local sil = utils_item.get_attached_silencer(obj)
if sil then
if (not self.ico_sil) then
if (not xml) then
xml = self:GetXML()
end
self.ico_sil = xml:InitStatic(self.path .. ":" .. self.cx .. ":pic", self.ico)
end
self:Create_Layer(self.ico_sil, self.ico, sec, sil, "silencer_x", "silencer_y")
self:Print(nil, "Show silencer")
end
-- Grenade Launcher
local gl = utils_item.get_attached_gl(obj)
if gl then
if (not self.ico_gl) then
if (not xml) then
xml = self:GetXML()
end
self.ico_gl = xml:InitStatic(self.path .. ":" .. self.cx .. ":pic", self.ico)
end
self:Create_Layer(self.ico_gl, self.ico, sec, gl, "grenade_launcher_x", "grenade_launcher_y")
self:Print(nil, "Show grenade launcher")
end
end
function UICellItem:Add_CustomText(txt, align_h, align_v, clr, fnt)
if self.ctxt then
self.ctxt:Show(false)
self.ctxts:Show(false)
end
if (not txt) then
return
end
if (not self.ctxt) then
local xml = self:GetXML()
self.ctxts = xml:InitTextWnd(self.path .. ":" .. self.cx .. ":ctxt", self.cell)
self.ctxt = xml:InitTextWnd(self.path .. ":" .. self.cx .. ":ctxt", self.cell)
self.ctxts:SetTextColor(clr_list["black"])
end
utils_xml.sync_size(self.cell, self.ctxt)
utils_xml.sync_size(self.ctxt, self.ctxts)
local pos = self.ctxt:GetWndPos()
self.ctxts:SetWndPos(vector2():set( pos.x+1 , pos.y+1 ))
self.ctxt:SetText(txt)
self.ctxts:SetText(txt)
if align_h then -- [2] center / [4] right
self.ctxt:SetTextAlignment(align_h)
self.ctxts:SetTextAlignment(align_h)
end
if align_v then
self.ctxt:SetVTextAlignment(align_v)
self.ctxts:SetVTextAlignment(align_v)
end
if fnt then
self.ctxt:SetFont(fnt)
self.ctxts:SetFont(fnt)
end
self.ctxt:SetTextColor(clr or clr_list["def"])
self.ctxt:Show(true)
self.ctxts:Show(true)
self:Print(nil, "Show custom text | txt: %s", txt)
end
function UICellItem:Create_Layer(ele, base, sec_m, sec_l, str_x, str_y, str_scale)
local grid_size = self.grid_size
local x = str_x and SYS_GetParam(2,sec_m, str_x) or 0
local y = str_y and SYS_GetParam(2,sec_m, str_y) or 0
local w = SYS_GetParam(2,sec_l, "inv_grid_width",1) * grid_size
local h = SYS_GetParam(2,sec_l, "inv_grid_height",1) * grid_size
local scale = str_scale and SYS_GetParam(2,sec_m, str_scale) or 1
local scale_pos = scale * (grid_size/50)
local rot = ele:GetHeading() > 0
local x_s = x * ratio * scale_pos
local y_s = y * scale_pos
local w_s = w * ratio * scale
local h_s = h * scale
local w_off = (w_s/2)
local h_off = (h_s/2)
if rot then
-- despite rotation, movement for x and y stays normal!
-- Move start pos to match the one for rotated base icon
local w_b, h_b = base:GetWidth(), base:GetHeight()
local x_st = (w_b/2) - (h_b/2)
local y_st = h_b + x_st
-- On 90 rotation, x and y are inverted, y axis goes negative simulate normal x movement
x_s = x_st + (y * ratio * scale_pos)
y_s = y_st - (x * scale_pos)
w_s = w * scale
h_s = h * ratio * scale
w_off = (h_s * (1/ratio))/2
h_off = -w_s/2
end
ele:InitTexture( utils_xml.get_icons_texture(sec_l) )
ele:SetTextureRect(Frect():set( get_item_axis(sec_l, nil, true) ))
ele:SetStretchTexture(true)
ele:SetWndPos(vector2():set( x_s + w_off , y_s + h_off ))
ele:SetWndSize(vector2():set( w_s , h_s ))
ele:Show(true)
end
function UICellItem:Colorize(clr_id)
self:Print(nil, "Colorize [%s]", clr_id)
local clr = clr_list[clr_id]
if self.ico then self.ico:SetTextureColor( clr ) end
if self.ico_scope then self.ico_scope:SetTextureColor( clr ) end
if self.ico_sil then self.ico_sil:SetTextureColor( clr ) end
if self.ico_gl then self.ico_gl:SetTextureColor( clr ) end
if self.layer then
for i=1,#self.layer do
self.layer[i]:SetTextureColor( clr )
end
end
if self.shadow then
self.shadow:SetTextureColor( (clr_id == "hide") and clr_list["hide_shadow"] or clr_list["shadow"] )
end
end
function UICellItem:Highlight(state, clr_id, main_clr)
if state and (not self:IsShown()) and (not self.manual) then
return
end
self.hl:Show(state)
if (not state) then
return
end
if main_clr then
self.hl:SetTextureColor( clr_id and clr_list[clr_id] or clr_list["def"] )
else
self.hl:SetTextureColor( clr_id and clr_list_hl[clr_id] or clr_list_hl["def"] )
end
end
function UICellItem:Check_TradeMode(obj, sec)
if (self.showcase ~= 0) then
return false
end
-- Reset
self.flags.value = nil
self.flags.value_str = nil
self.flags.note = nil
self.flags.no_trade = nil
self.flags.note_str = nil
local profile = self.container.trade_profile
if not (profile and profile.mode) then
return false
end
local status = get_item_trade_status(obj, profile)
if status == 4 then
local cost = get_item_cost(obj, profile)
self.flags.value = math.floor(cost)
self.flags.value_str = (game.translate_string("st_base_cost") .. ": " .. math.floor(cost) .. " RU")
elseif status == 3 or (not status) then
self:Print(nil, "Removed [%s] for NPC. Not proper item", sec)
return true
elseif status == 1 or status == 2 then
self.flags.note = status
self.flags.no_trade = true
self.flags.note_str = game.translate_string("st_no_trade_tip_" .. status)
end
return false
end
function UICellItem:GetCost()
end
function UICellItem:AddChild(obj)
-- no need for childs in manual cells
if self.manual then
return false
end
if (not self.childs) then
self.childs = {}
end
-- Real
local sec = obj:section()
if (self.showcase == 0) then
local id = obj:id()
if (id ~= self.ID) then
-- Trade mode for child
local profile = self.container.trade_profile
if (profile and profile.mode == 2) then
local status = get_item_trade_status(obj, profile)
if (status == 3) then
self:Print(nil, "AddChild [%s] | not tradable by NPC!", obj:name())
return false
end
end
self.childs[id] = IsAmmo(obj) and obj:ammo_get_count() or 1
self:Update()
return true
end
return false
-- Dummy
else
table.insert(self.childs, IsItem("ammo",sec) or 1)
end
return true
end
function UICellItem:PopChild(obj, id)
if self.childs then
-- Real
if (self.showcase == 0) then
self.childs[ obj and obj:id() or id ] = nil
self:Update()
-- Dummy
elseif #self.childs > 0 then
self.childs[ #self.childs ] = nil
end
end
end
function UICellItem:HasChild(obj, id)
if obj or id then
return self.childs and self.childs[ obj and obj:id() or id ]
end
return self.childs and is_not_empty(self.childs)
end
function UICellItem:CountChilds()
return self.childs and table_size(self.childs) or 0
end
function UICellItem:ResetToChild()
local id_child
if self.childs then
for id,cnt in pairs(self.childs) do
id_child = id
break
end
end
if id_child then
-- Real
if (self.showcase == 0) then
local obj = level.object_by_id(id_child)
self.childs[id_child] = nil
self.ID = id_child
self.section = obj and obj:section()
self:Update(obj)
-- Dummy
else
self.childs[id_child] = nil
self:Update()
end
return false
else
self:Reset()
return true
end
end
function UICellItem:Reset()
self.ID = nil
self.section = nil
if self.childs then
empty_table(self.childs)
end
empty_table(self.flags)
self:Highlight(false)
if (self.manual) then
self.ico:Show(false)
self.shadow:Show(false)
if self.bar then self.bar:Show(false) end
if self.cnt then self.cnt:Show(false) end
if self.upgr then self.upgr:Show(false) end
if self.ctxt then self.ctxt:Show(false) end
if self.ctxts then self.ctxts:Show(false) end
else
if (self.showcase ~= 2) and self.container["FreeRoom"] then
-- Freeing cell area
local a = self.area
self.container:FreeRoom(a.y, a.x, a.w, a.h)
end
self:Show(false)
end
end
function UICellItem:Show(state, obj)
if state then
self:Update(obj)
end
self.cell:Show(state)
end
function UICellItem:IsShown()
if self.manual then
return self.ico:IsShown()
end
return self.cell:IsShown()
end
function UICellItem:IsCursorOverWindow()
return self.cell:IsCursorOverWindow()
end
function UICellItem:GetXML()
return self.manual and self.container.xml or XMLP
end
function UICellItem:Print(mark, fmt,...)
--printf( (mark or "=") .. " UICellItem [%s] | " .. fmt, self.section, ...)
end
-------------------------------------------------------------------
-- Container for item cells
-------------------------------------------------------------------
class "UICellContainer"
function UICellContainer:__init(id, owner, path, prof, ele_base, manual, use_frame)
self.ID = id -- unique id for class instance, so we can deal with many instances outside
self.owner = owner
self.path = path or "container"
self.xml = manual and owner.xml or XMLP
-- Item cells
self.grid_size = 41
self.grid_line = 2
self.grid = {} -- [row][col] = bool ( true = unoccupied , false = occupied )
self.cell = {}
self.line = {}
self.line_cnt = 0
self.indx_id = {} --[id] = idx
self.indx_sec = {} --[sec][id] = idx
self.idxer = 0
self.row_end = 0
self.col_end = 0
self.scroll_pos = 0
self.cell_vis_offset = 20 --[px]
self.rKind = { last = false , current = false , row = 1 }
-- Properties
self.sort_method = "sizekind"
self.showcase = false
self.manual = manual
self.can_select = false
self.stack_all = false
self.ignore_scroll = false
self.disable_scroll = manual
self.disable_scroll_dragdrop = false
self.disable_drag = false
self.disable_highlight = false
self.disable_info = false
self.disable_stack = false
self.disable_bar = false
self.disable_callback = {} --[name] = true
self.scolling_power_up = 3
-- Cell management
self.selected = nil
self.hover = { idx = false }
self.hold = { idx = false , ico = false , tg = 0 , w = 0 , h = 0 }
self.db = { idx = false , tg = 0 }
self.drag_area = { up = 0 , down = 0 , step = 200 }
self.pd = { update = false , off = 0 , start = 0 , hold = false , power = 1}
self.scolling_power = 1
self.use_frame = use_frame
self:InitControls(owner, prof, ele_base)
self:EnableScrolling(not manual)
self:Print(nil, "__init for path (%s)", path)
end
function UICellContainer:InitControls(owner, prof, ele_base)
local xml = self.xml
local path = self.path
if self.use_frame then
self.prof = owner.xml:InitFrame(prof, ele_base)
else
self.prof = owner.xml:InitStatic(prof, ele_base)
end
-- Drag up area
self.drag_up = xml:InitStatic(path .. ":st", self.prof)
self.drag_up:SetWndPos(vector2():set( 0 , 0 ))
self.drag_up:SetWndSize(vector2():set( self.prof:GetWidth() , self.grid_size ))
-- Drag down area
self.drag_down = xml:InitStatic(path .. ":st", self.prof)
self.drag_down:SetWndPos(vector2():set( 0 , self.prof:GetHeight() - self.grid_size ))
self.drag_down:SetWndSize(vector2():set( self.prof:GetWidth() , self.grid_size ))
self.scroll = xml:InitScrollView(path .. ":scroll", self.prof)
utils_xml.sync_size( self.prof , self.scroll)
self.st = xml:InitStatic(path .. ":st", nil)
utils_xml.sync_size( self.prof , self.st)
-- Scroll pad
self.pad = xml:InitStatic(path .. ":st", self.prof)
self.pad:InitTexture("ui_inGame2_white_rect")
self.pad:SetTextureColor( GetARGB(100,255,255,255) )
self.pad:SetWndPos(vector2():set( self.prof:GetWidth() , 0 ))
-- Edited by Sota
--self.pad:SetWndSize(vector2():set( 5 , self.prof:GetHeight() ))
self.pad:SetWndSize(vector2():set( 5 * uwide_ratio , self.prof:GetHeight() ))
self.pad:Show(false)
self:SetGridSpecs()
end
function UICellContainer:Reinit(t, tf)
self:Reset()
-- If no inventory is passed, this function will just clear cells
if (not t) then
return
end
-- Create cells
self.ignore_scroll = true
self:Print(nil, "Reset | START Number of cells: %s", #self.cell)
local sort_order = self:GetSortMethod()
if self.showcase then
for i,sec in spairs(t, sort_order) do
self:AddItem(nil,sec, tf and tf[i])
end
else
for i,obj in spairs(t, sort_order) do
self:AddItem(obj, nil, tf and tf[i])
end
end
self.ignore_scroll = false
self:Print(nil, "Reset | END Number of cells: %s", #self.cell)
self:Scroll_Reinit()
end
function UICellContainer:AddIndex(id, sec, indx)
self:Print(nil, "AddIndex | id: %s - sec: %s - indx: %s", id, sec, indx)
self.indx_id[id] = indx
if (not sec) then
return
end
if (not self.indx_sec[sec]) then
self.indx_sec[sec] = {}
end
self.indx_sec[sec][id] = indx
end
function UICellContainer:RemoveIndex(id, sec, indx)
self:Print(nil, "RemoveIndex | id: %s - sec: %s - indx: %s", id, sec, indx)
if (not id) then
return
end
-- Deselect
if (self.selected == self.indx_id[id]) then
self:On_Select(false)
end
self.indx_id[id] = nil
if (not sec) then
for s,t in pairs(self.indx_sec) do
if t[id] then
sec = s
break
end
end
if (not sec) then
return
end
end
if self.indx_sec[sec] then
self.indx_sec[sec][id] = nil
if is_empty(self.indx_sec[sec]) then
self.indx_sec[sec] = nil
end
end
end
function UICellContainer:GetCell_ID(id, only_indx)
local indx = self.indx_id[id]
if only_indx then
return indx
end
return indx and self.cell[indx]
end
function UICellContainer:GetCell_SEC(sec)
local indxes = self.indx_sec[sec]
if indxes then
for id,idx in pairs(indxes) do
self:Print(nil, "GetCell_SEC | sec: %s | found cell at index: %s", sec, idx)
return idx and self.cell[idx]
end
end
self:Print(nil, "GetCell_SEC | sec: %s | no cell found", sec)
return false
end
function UICellContainer:GetCell_Selected(only_obj)
if (not self.can_select) then
return
end
local ci = self.selected and self.cell[self.selected]
if only_obj and (not self.showcase) then
return ci and ci.ID and level.object_by_id(ci.ID)
end
return (not only_obj) and ci
end
function UICellContainer:GetCell_Focused(only_cell)
if self:IsShown() and self:IsCursorOverWindow() then
for idx, ci in pairs(self.cell) do
if (self.manual or ci:IsShown()) and ci:IsCursorOverWindow() then
if only_cell then
return ci
else
return self.ID, idx, self:GetObj(idx)
end
end
end
end
end
function UICellContainer:GetObj(idx)
local ci = self.cell[idx]
return ci and ci.ID and level.object_by_id(ci.ID)
end
function UICellContainer:GetID(obj, sec, create)
local id = obj and obj:id()
if (not id) then
if (not self.showcase) then
return
end
id = create and (#self.indx_id + 1) or random_key_table(self.indx_sec[sec])
end
return id
end
function UICellContainer:AddItemInCell(obj, sec, indx, area)
if (not self.cell[indx]) then
self:Print(nil, "AddItemInCell | Creating new cell | obj: %s - index: %s", obj and obj:name(), indx)
self.cell[indx] = UICellItem(self, self.st, indx)
if self.showcase then
self.cell[indx].showcase = self.manual and 2 or 1
end
end
local set = self.cell[indx]:Set(obj or sec, area)
self:Print(nil, "AddItemInCell | Set in cell (%s) | obj: %s - index: %s", set, obj and obj:name(), indx)
self:Scroll_Reinit(true)
return set
end
function UICellContainer:AddItem(obj, sec, info)
if (not sec) then
if (not obj) then
return
end
sec = obj and obj:section()
end
local id = self:GetID(obj, sec, true)
if self.indx_id[id] then
self:Print(nil, "AddItem | ID [%s] already exists", id)
return
end
local ci = self:FindSimilar(obj, sec)
if ci then
self:Print(nil, "AddItem | Found similar cell [%s]", ci.section)
if ci:AddChild( obj ) then
self:AddIndex(id, sec, ci.indx)
self:Callback( "On_CC_Add", self.ID, ci.indx, false )
return ci.indx
end
else
local area = self:FindFreeCell(obj, sec)
if area then
self:Print(nil, "AddItem | New cell [%s]", sec)
local indx = self.idxer + 1
if self:AddItemInCell(obj, sec, indx, area) then
self:AddIndex(id, sec, indx)
self.idxer = indx
if info then
ci = self.cell[indx]
ci.flags.info = info
end
self:Callback( "On_CC_Add", self.ID, indx, true )
return indx
end
end
end
end
function UICellContainer:AddItemManual(obj, sec, indx)
if (not self.cell[indx]) then
self:Print(nil, "AddItemManual | Creating new cell | obj: %s - index: %s", obj and obj:name() or sec, indx)
self.cell[indx] = UICellItem(self, self.st, indx, true)
if self.showcase then
self.cell[indx].showcase = self.manual and 2 or 1
end
end
if not (obj or sec) then
self.cell[indx]:Reset()
return
end
local set = self.cell[indx]:Set(obj or sec)
if set and obj then
local id = self:GetID(obj, sec)
if (not id) then
return
end
sec = sec or (obj and obj:section())
self:AddIndex(id, sec, indx)
self:Callback( "On_CC_Add", self.ID, indx, true )
self:Print(nil, "AddItemManual | Set in cell (%s) | obj: %s - index: %s", set, obj and obj:name() or sec, indx)
end
self:Scroll_Reinit(true)
return set
end
function UICellContainer:RemoveItem(obj, sec)
if (not sec) then
if (not obj) then
return
end
sec = obj and obj:section()
end
local id = self:GetID(obj, sec)
if (not id) then
return
end
local ci = self:GetCell_ID(id)
if ci then
if ci:HasChild(obj) then
self:Print(nil, "RemoveItem [%s] | Removing inner child", obj and obj:name() or sec)
ci:PopChild(obj)
self:RemoveIndex(id, sec, ci.indx)
self:Callback( "On_CC_Remove", self.ID, ci.indx, true )
elseif (id == ci.ID) or (sec and (not obj)) then
local cell_removed = ci:ResetToChild()
if cell_removed then
self:Print(nil, "RemoveItem [%s] | Removing base", obj and obj:name() or sec)
self:RemoveIndex(id, sec, ci.indx)
self:Callback( "On_CC_Remove", self.ID, ci.indx, false )
else
self:Print(nil, "RemoveItem [%s] | Removing outer child", obj and obj:name() or sec)
self:RemoveIndex(id, sec, ci.indx)
self:Callback( "On_CC_Remove", self.ID, ci.indx, true )
end
end
end
end
function UICellContainer:RemoveItem_byID(id)
-- Used if object no longer exists (non-showcase type)
if (not id) then
return
end
local ci = self:GetCell_ID(id)
if ci then
if ci:HasChild(nil,id) then
self:Print(nil, "RemoveItem_byID [%s] | Removing inner child", id)
ci:PopChild(nil,id)
self:RemoveIndex(id, nil, ci.indx)
self:Callback( "On_CC_Remove", self.ID, ci.indx, true )
elseif (id == ci.ID) then
local cell_removed = ci:ResetToChild()
if cell_removed then
self:Print(nil, "RemoveItem_byID [%s] | Removing base", id)
self:RemoveIndex(id, nil, ci.indx)
self:Callback( "On_CC_Remove", self.ID, ci.indx, false )
else
self:Print(nil, "RemoveItem_byID [%s] | Removing outer child", id)
self:RemoveIndex(id, nil, ci.indx)
self:Callback( "On_CC_Remove", self.ID, ci.indx, true )
end
else
self:Print(nil, "RemoveItem_byID [%s] | No active cell found!", id)
self:RemoveIndex(id)
end
end
end
function UICellContainer:RemoveItemManual(indx)
local ci = self.cell[indx]
if ci then
local obj = ci.ID and self:GetObj(indx)
local sec = obj and obj:section() or ci.section
local id = self:GetID(obj, sec)
if ci.showcase ~= 0 and (not obj) then
printe("!ERROR UICellContainer:RemoveItemManual | can't get game object for [%s]", sec)
return
end
local cell_removed = ci:ResetToChild()
if cell_removed and (obj or sec) then
self:Print(nil, "RemoveItem [%s] | Removing base", obj and obj:name() or sec)
self:RemoveIndex(id, sec, indx)
self:Callback( "On_CC_Remove", self.ID, indx, false )
self:Scroll_Reinit(true)
end
end
end
function UICellContainer:TransferItem(cont_to, obj, sec)
self:Print(nil, "TransferItem [%s]", obj and obj:name() or sec)
if not (obj or sec) then
return
end
local id = self:GetID(obj, sec)
if (not id) then
return
end
local ci_from = self:GetCell_ID(id)
if ci_from then
-- Create object in new container
local indx = cont_to:AddItem(obj, sec)
local ci_to = indx and cont_to.cell[indx]
-- Transfer trade data in trade mode
ci_to.flags = dup_table(ci_from.flags)
-- Remove object from old container
self:RemoveItem(obj, sec)
self:Callback( "On_CC_Trasfer", self.ID, cont_to.ID, ci_from.indx, ci_to.indx , obj or sec)
return ci_to
end
end
function UICellContainer:UpdateItem(obj, sec)
local id = self:GetID(obj, sec)
local ci = id and self:GetCell_ID(id)
if ci then
ci:Update() -- force it to get game object again for updated stats
end
end
function UICellContainer:FindFreeCell(obj, sec)
if (not sec) then
if (not obj) then
return false
end
sec = obj and obj:section()
end
local w = SYS_GetParam(2,sec, "inv_grid_width",1)
local h = SYS_GetParam(2,sec, "inv_grid_height",1)
-- Avoid icons that don't fit
if w > self.cols then
return false
end
-- Sorting by kind: when sorting a new kind, always start from last row taken by previous kind
if self.sort_method == "kind" then
self.rKind.current = item_order[ab_k[sec]]
if (self.rKind.last ~= self.rKind.current) then
--[[
local cnt = self.line_cnt + 1
if (not self.line[cnt]) then
self.line[cnt] = self.xml:InitStatic(self.path .. ":line", self.st)
end
local y = (self.row_end) * (self.grid_size + self.grid_line)
self.line[cnt]:SetWndPos( vector2():set(0,y - 2.5) )
self.line[cnt]:SetWndSize( vector2():set(self.prof:GetWidth(),6) )
self.line[cnt]:Show(true)
self.line_cnt = cnt
--]]
self.rKind.last = self.rKind.current
self.rKind.row = self.row_end + 1
end
end
local row_s = self.rKind.row
local rows = #self.grid
local cols = self.cols + 1 - w
self:Print(nil, "FindFreeCell for [%s] (rows: %s, cols: %s, W: %s, H: %s)", sec, rows,cols,w,h)
for r=row_s, rows do
for c=1,cols do
if self:IsFreeRoom(r,c,w,h) then
return self:TakeRoom(r,c,w,h)
end
end
end
self:Grow()
return self:FindFreeCell(obj, sec)
end
function UICellContainer:IsFreeRoom(r,c,w,h)
for row = r, r+(h-1) do
for col = c, c+(w-1) do
if (not self.grid[row]) then
self:Print(nil, "IsFreeRoom row missing: %s", row)
return false
end
if self.grid[row][col] == false then
self:Print(nil, "IsFreeRoom | row: %s - col: %s is occupied", row, col)
return false
end
end
end
self:Print(nil, "IsFreeRoom (%s,%s,%s,%s) = %s", r,c,w,h, true)
return true
end
function UICellContainer:TakeRoom(r,c,w,h)
self:Print(nil, "TakeRoom (%s,%s,%s,%s)", r,c,w,h)
local r_end = r+(h-1)
local c_end = c+(w-1)
for row = r, r_end do -- -1 because starting row/coloumn cell should be counted
for col = c, c_end do
self.grid[row][col] = false
end
end
if (r_end > self.row_end) then
self.row_end = r_end
end
if (c_end > self.col_end) then
self.col_end = c_end
end
return { y=r , x=c , w=w , h=h } -- rows are Y, coloumns are X
end
function UICellContainer:FreeRoom(r,c,w,h)
self:Print(nil, "FreeRoom (%s,%s,%s,%s)", r,c,w,h)
for row = r, r+(h-1) do
for col = c, c+(w-1) do
self.grid[row][col] = true
end
end
end
function UICellContainer:FindSimilar(obj, sec)
if not (obj or sec) then
printe("!ERROR UICellContainer:FindSimilar | no data recieved!")
return false
end
if self.disable_stack then
return false
end
if self.showcase or self.stack_all then
return self:GetCell_SEC(sec)
end
local sec = obj:section()
-- Ignore search if item isn't meant to stack
if SYS_GetParam(1,sec,"dont_stack") then
return false
end
-- Ignore search if multiuse item is used
local max_uses = IsItem("multiuse",sec)
if max_uses and obj:get_remaining_uses() ~= max_uses then
return false
end
-- items with no condition can stack, return a cell with same section
local clsid = obj:clsid()
local use_cond = SYS_GetParam(1,sec,"use_condition") or IsWeapon(nil,clsid) or IsOutfit(nil,clsid) or IsHeadgear(nil,clsid)
if (not use_cond) then
return self:GetCell_SEC(sec)
end
-- Ignore search if item has upgrades
if has_upgrades(obj) then
return false
end
-- items with condition, full search
for idx,ci in pairs(self.cell) do
if (ci.section == sec) then
-- full multiuse item can stack
if max_uses then
return ci
-- item with similar condition can stack
else
local cond = obj:condition()
local obj_2 = ci.ID and level.object_by_id(ci.ID)
if obj_2 and math.abs(obj_2:condition() - cond) < 0.1 then
return ci
end
end
end
end
-- no similar item found
return false
end
function UICellContainer:Grow()
local rows = #self.grid
rows = rows + 1
self.grid[rows] = {}
for i=1,self.cols do
self.grid[rows][i] = true
end
self:Print(nil, "Grow | new row: %s", rows)
end
function UICellContainer:IsTradable(obj)
local ci = obj and self:GetCell_ID(obj:id())
return ci and ci:IsShown() and (get_item_trade_status(obj, self.trade_profile) == 4)
end
function UICellContainer:GetCellCost(ci)
if not (ci and ci:IsShown() and self.trade_profile) then
return 0
end
local t_id = {}
t_id[ci.ID] = true
if ci:HasChild() then
for id,_ in pairs(ci.childs) do t_id[id] = true end
end
local tot_cost = 0
for id,_ in pairs(t_id) do
local obj = level.object_by_id(id)
if obj and (get_item_trade_status(obj, self.trade_profile) == 4) then
tot_cost = tot_cost + get_item_cost(obj, self.trade_profile)
end
end
return tot_cost
end
function UICellContainer:On_Select(idx)
-- Forced deselect
if (not self.can_select) or (idx == false) then
local ci = self.selected and self.cell[self.selected]
if ci then
ci:Highlight(false)
end
self.selected = nil
return
end
idx = idx or self.selected
-- Deselect previous cell
if self.selected and (self.selected ~= idx) then
local ci = self.cell[self.selected]
if ci then
ci:Highlight(false)
end
end
-- Select new cell
local ci = idx and self.cell[idx]
if ci then
ci:Highlight(true,"green")
self.selected = idx
end
end
function UICellContainer:On_Drag(idx, tg, set)
-- Hold icon should be declared after everything else, so it stays on top
-- Also it must be assigned on the base UI because we need x and y to start from absolute screen corner
if (not self.hold.ico) then
self.hold.ico = self.xml:InitStatic(self.path .. ":cell:pic", self.owner)
self.hold.ico:Show(false)
end
if (self.disable_drag) then
return
end
-- Drag start / Drop
if set then
self:Print(nil, "Drag | hold_idx: %s - hold_tg: %s", idx, tg)
-- Callback
if (not idx) and self.hold.idx and (self.hold.idx ~= self.hover.idx) then -- IF released mouse + drag item is captured + drag item is not hovered item
self:Callback( "On_CC_DragDrop", self.ID, self.hold.idx )
end
-- Useful to prevent some actions for owner when there's a dragged item, by checking on (self.item_in_hold)
if self.owner.item_in_hold == self.ID then
self.owner.item_in_hold = false
end
if idx then
self.owner.item_in_hold = self.ID
-- Hold the scroll and stop moving on dragging
self.scroll_pos = self.scroll:GetCurrentScrollPos()
-- No point of holding empty cells
if (not self.cell[idx]:IsShown()) then
return
end
end
self.hold.idx = idx
self.hold.tg = tg
self.hold.ico:Show(false)
return
end
-- Drag
if self.hold.idx and tg - self.hold.tg > 100 then
-- Set up drag item icon
if (not self.hold.ico:IsShown()) then
self:Print(nil, "Drag | set up icon")
local sec = self.cell[self.hold.idx].section
if sec then
self.hold.w = SYS_GetParam(2,sec, "inv_grid_width") * self.grid_size * ratio
self.hold.h = SYS_GetParam(2,sec, "inv_grid_height") * self.grid_size
self.hold.ico:InitTexture( utils_xml.get_icons_texture(sec) )
self.hold.ico:SetTextureRect(Frect():set( get_item_axis(sec, nil, true) ))
self.hold.ico:SetTextureColor( clr_list["drag"] )
self.hold.ico:SetStretchTexture(true)
self.hold.ico:SetWndSize(vector2():set(self.hold.w , self.hold.h))
self.hold.ico:Show(true)
end
end
-- Sync with cursor pos
local pos = GetCursorPosition()
self.hold.ico:SetWndPos( pos )
end
end
function UICellContainer:On_Hover(idx)
if (not idx) then
-- UnHighlight old cell
if self.hover.idx then
local ci = self.cell[self.hover.idx]
if ci then
ci:Highlight(false)
end
end
-- Unmark hovering
self.hover.idx = false
elseif self.hover.idx ~= idx then
-- UnHighlight old cell
if self.hover.idx then
local ci = self.cell[self.hover.idx]
if ci then
ci:Highlight(false)
end
end
-- Mark hovered cell
self.hover.idx = idx
-- Highlight new cell (on hover, cell must be shown)
local ci = self.cell[idx]
if ci and ci:IsShown() then
ci:Highlight(true)
end
end
-- Callback, only if mouse on bag
if self:IsCursorOverWindow() then
self:Callback( "On_CC_Hover", self.ID, self.hover.idx )
end
end
function UICellContainer:On_Mouse1(idx)
-- Anticipate double click
local tg = get_time()
if (tg - self.db.tg < 400) then
self.db.idx = idx --false
self.db.tg = tg
local db_continue = self:On_Mouse1_DB(idx)
-- when there's a value returned, allow consecutive db clicks
if (not db_continue) then
self.db.idx = false
end
-- db_continue is false if no db callback is found in owner class, so we can pass the return and treat db click as normal click
if (db_continue ~= false) then
return
end
end
self.db.idx = idx
self.db.tg = tg
self:On_Select(idx)
self:Callback( "On_CC_Mouse1", self.ID, idx )
end
function UICellContainer:On_Mouse1_DB(idx)
return self:Callback( "On_CC_Mouse1_DB", self.ID, idx ) -- Returning true means no cooldown on double clicking if you keep clicking!
end
function UICellContainer:On_Mouse2(idx)
self:On_Select(idx)
self:Callback( "On_CC_Mouse2", self.ID, idx )
end
function UICellContainer:On_Scroll()
if (self.disable_scroll) then
self:Scroll_SetPos(0)
return
end
if (not self.scroll_pos) then
self.scroll_pos = self.scroll:GetCurrentScrollPos()
return
end
-- Setting scroll pad size
if self.pd.update then
self:Scroll_Pad_Ctrl(true)
self.pd.update = false
end
-- Prevent scrolling when it's paused
if self.scroll_pause then
self:Scroll_DragDrop_Ctrl()
self:Scroll_Pad_Ctrl()
self:Scroll_SetPos()
return
end
self.pd.off = false
self.pd.hold = false
-- Prevent scrolling when holding item
if self.hold.idx or self.owner.item_in_hold then
self:Scroll_DragDrop_Ctrl()
self:Scroll_SetPos()
return
end
-- small delay
if (not self.scroll_tg) then
self.scroll_tg = get_time()
end
-- Alt Tab Scroll Issue fix by DPurple
-- get_time() may unreliable , try reset delay
if get_time() - self.scroll_tg < 0 then
self.scroll_tg = get_time()
end
if self.scroll_tg > (get_time() + 25) then
return
end
self.scroll_tg = get_time()
-- Amplify scrolling
local curr_pos = self.scroll:GetCurrentScrollPos()
if (self.scroll_pos ~= curr_pos) then
if (curr_pos > self.scroll_pos) then
self:Scroll_SetPos( self.scroll_pos + ((self.grid_size + self.grid_line) * self.scolling_power) )
else
self:Scroll_SetPos( self.scroll_pos - ((self.grid_size + self.grid_line) * self.scolling_power) )
end
--printf("curr pos: %s - scroll pos: %s", curr_pos, self.scroll_pos)
end
end
function UICellContainer:Update(item_info, no_info, hide)
self:On_Drag(false, get_time())
self:On_Select()
self:On_Scroll()
if hide then
self:On_Hover(false)
return false
end
if self:IsCursorOverWindow() then -- For performance: There's no need to iterate cells, if the container is not focused in the first place
for i=1,#self.cell do
local ci = self.cell[i]
-- Only update for hovered cell
-- Manual cells are special cases because we want to have hover interaction with them
if ci and ci:IsCursorOverWindow() and (ci.manual or ci:IsShown()) and self:IsCellVisible(ci) then
-- No info if drag is active
if no_info or self.hold.ico:IsShown() then
if item_info then
item_info:Update()
end
elseif item_info and (not self.disable_info) and ci:IsShown() then
local obj = ci.ID and level.object_by_id(ci.ID)
item_info:Update( obj, ci.section, ci.flags)
end
self:On_Hover(i)
return true
end
end
end
self:On_Hover(false)
return false
end
function UICellContainer:OnKeyboard(dik, keyboard_action)
-- Pause scrolling when holding M1
if (dik == K_M1) then
if (keyboard_action == E_PRESS) then
self.scroll_pause = true
elseif (keyboard_action == E_RELEASE) then
self.scroll_pause = false
end
end
-- Interaction with cells
local idx = self.hover.idx
if idx then
-- Mouse 1
if (dik == K_M1) then
if (keyboard_action == E_PRESS) then
self:On_Drag(idx, get_time(), true)
elseif (keyboard_action == E_RELEASE) then
self:On_Drag(false, 0, true)
self:On_Mouse1(idx)
end
-- Mouse 2
elseif (dik == K_M2) then
if (keyboard_action == E_RELEASE) then
self:On_Mouse2(idx)
end
end
-- We disable drag on releasing regardless of highlighted cell
elseif (dik == K_M1) and (keyboard_action == E_RELEASE) then
self:On_Drag(false, 0, true)
end
-- Left shift
if (dik == K_SHFT) then
self.scolling_power = (keyboard_action == E_PRESS) and self.scolling_power_up or 1
end
end
function UICellContainer:Callback(func, ...)
-- This is needed for inventory UI to avoid endless loop for slots managements
if self.disable_callback[func] then
return
end
if self.owner[func] then
return self.owner[func](self.owner,...)
elseif (func == "On_CC_Mouse1_DB") then
return false
end
end
function UICellContainer:Reset()
self:Print(nil, "Reset")
self.hover.idx = false
for idx,ci in pairs(self.cell) do
ci:Reset()
end
for cnt, ele in pairs(self.line) do
ele:Show(false)
end
self:On_Select(false)
self.idxer = 0
self.row_end = 0
self.col_end = 0
self.scroll_pos = 0
self.rKind.last = false
self.rKind.current = false
self.rKind.row = 1
self.line_cnt = 0
empty_table(self.indx_id)
empty_table(self.indx_sec)
self.pad:Show(false)
self.selected = nil
self.hold.idx = false
self.hold.tg = 0
self.hold.w = 0
self.hold.h = 0
if self.hold.ico then
self.hold.ico:Show(false)
end
self.db.idx = false
self.db.tg = 0
-- Reset Grid
for row,v in pairs(self.grid) do
for col,state in pairs(v) do
self.grid[row][col] = true
end
end
end
function UICellContainer:IsCellVisible(ci)
-- This is important because we don't want to interact with cells that are outside of scroll view window
local pos_start = self.scroll_pos
local pos_start_ci = ci.cell:GetWndPos().y + self.cell_vis_offset
local pos_end = pos_start + self.scroll:GetHeight()
local pos_end_ci = pos_start_ci + ci.cell:GetHeight() - (self.cell_vis_offset * 2)
--printf("IsCellVisible | start: %s - start_ci: %s - end: %s - end_ci: %s - top visible: %s - end visible: %s", pos_start, pos_start_ci, pos_end, pos_end_ci, (pos_start_ci >= pos_start) , (pos_end_ci <= pos_end))
return (pos_start_ci >= pos_start) and (pos_end_ci <= pos_end)
end
function UICellContainer:Scroll_Reinit(keep_pos)
-- It's important to reset the scroll after cell changes because we want to adapt new cells
-- TODO: to prevent spawn of items flooding in container, we need to time this in update function
-- Used when reiniting cells, to prevent unneeded scroll adjusting when there's a mass amount of cells being recreated
if self.ignore_scroll then
return
end
local cpos = (self.disable_scroll and 0) or (keep_pos and self.scroll:GetCurrentScrollPos())
self.scroll:Clear()
-- We decide container size according to the row with first unoccupied cell
if (not self.disable_scroll) then
-- Determine scroll height
local h = self:Scroll_GetHeight(1)
h = h - self.scroll:GetHeight()
h = h > 0 and h or 0
self.st:SetWndSize(vector2():set( self.scroll:GetWidth() , h ))
--printf("scroll: %s - st: %s", self.scroll:GetHeight(), self.st:GetHeight())
end
self.scroll:AddWindow(self.st, true)
self.st:SetAutoDelete(false)
self.pd.update = true
--[[
if cpos then
self.scroll:SetScrollPos(cpos)
end
--]]
self:Scroll_SetPos(cpos, true)
end
function UICellContainer:Scroll_SetPos(Y, force)
if (not Y) then
Y = self.scroll_pos or 0
end
-- Prevent scrolling if scroll area is shorter than scoll height
if (((self.grid_size + self.grid_line) * self.row_end) < self.scroll:GetHeight()) then
Y = 0
end
-- Keep scroll pos in range
if force then
-- This trick is used because for some reason the scroll doesn't have full scale on resetting, unless if we set its pos at least once
self.scroll:SetScrollPos(1)
end
Y = clamp( math.floor(Y), self.scroll:GetMinScrollPos(), self.scroll:GetMaxScrollPos())
self.scroll:SetScrollPos(Y)
self.scroll_pos = Y
-- Adjust scrolling pad
if self.pad:IsShown() then
self.pad:SetWndPos(vector2():set( self.prof:GetWidth() , Y / self.pd.power ))
end
--if (self.ID == "actor_bag") then printf("ID: %s - POS: %s - MIN: %s - MAX: %s - FROM: %s", self.ID, Y, self.scroll:GetMinScrollPos(), self.scroll:GetMaxScrollPos(), callstack(true,true)) end
end
function UICellContainer:Scroll_GetHeight(num, ele)
if ele then
return (self.scroll:GetHeight() + self.scroll:GetMaxScrollPos())
end
return (self.row_end + (num or 0)) * (self.grid_size + self.grid_line)
end
function UICellContainer:Scroll_DragDrop_Ctrl()
if self.disable_scroll_dragdrop then
return false
end
if not (self.hold.idx or self.owner.item_in_hold) then
return false
end
local dir = (self.drag_up:IsCursorOverWindow() and 1) or (self.drag_down:IsCursorOverWindow() and 2) or 0
if dir == 0 then
return false
end
if (dir == 1) and (get_time() - self.drag_area.up > self.drag_area.step) then
self.drag_area.up = get_time()
self:Scroll_SetPos( self.scroll_pos - ((self.grid_size + self.grid_line) * self.scolling_power) )
--printf("On_Scroll_DragDrop UP | old: %s - new: %s", self.scroll_pos, new_pos)
return true
elseif (dir == 2) and (get_time() - self.drag_area.down > self.drag_area.step) then
self.drag_area.down = get_time()
self:Scroll_SetPos( self.scroll_pos + ((self.grid_size + self.grid_line) * self.scolling_power) )
--printf("On_Scroll_DragDrop DOWN | old: %s - new: %s", self.scroll_pos, new_pos)
return true
end
return false
end
function UICellContainer:Scroll_Pad_Ctrl(update)
-- Scroll pad state and size
if update then
local h_view = self.scroll:GetHeight()
local h_max = h_view + self.scroll:GetMaxScrollPos()
if (h_max <= h_view + 1) or (self.disable_scroll) then
self.pad:Show(false)
else
local h_scroll = (h_max - h_view)
local ratio = (h_view / h_max)
local h_pad = h_view * ratio
self.pd.power = h_scroll/(h_view - h_pad)
--printf("h_view: %s - h_scroll: %s - h_max: %s - h_pad: %s - ratio: %s - power: %s", h_view, h_scroll, h_max, h_pad, ratio, self.pd.power)
self.pad:SetWndPos(vector2():set( self.prof:GetWidth() , self.scroll_pos / self.pd.power ))
-- Edited by Sota
--self.pad:SetWndSize(vector2():set( 5 , math.floor(h_pad) ))
self.pad:SetWndSize(vector2():set( 5 * uwide_ratio , math.floor(h_pad) ))
self.pad:Show(true)
end
return
end
-- No interaction if pad isn't shown
if (not self.pad:IsShown()) then
return
end
-- No interaction if pad is not focused
if not (self.pd.hold or self.pad:IsCursorOverWindow()) then
return
end
-- Storing start pos for the first time on holding
if (not self.pd.off) then
self.pd.off = GetCursorPosition().y
self.pd.start = self.scroll_pos
self.pd.hold = true
return
end
-- Set scroll pos according to mouse pos
local offset = GetCursorPosition().y - self.pd.off
self.scroll_pos = self.pd.start + (offset * self.pd.power)
--printf("offset: %s - scroll_pos: %s", offset, self.scroll_pos)
end
function UICellContainer:GetSortMethod()
if self.sort_method == "sizekind" then
return sort_by_sizekind
elseif self.sort_method == "props" then
return sort_by_props
elseif self.sort_method == "kind" then
return sort_by_kind
end
return sort_by_sizekind
end
function UICellContainer:AdjustWnd(x, y, w, h)
local pos = x and y and vector2():set(x,y) or self.prof:GetWndPos()
self.prof:SetWndPos( pos )
self.prof:SetWndSize(vector2():set( (w or self.prof:GetWidth()) , (h or self.prof:GetHeight()) ))
utils_xml.sync_size( self.prof , self.scroll )
end
function UICellContainer:AdjustHeightToCell()
local h = self.row_end * (self.grid_size + self.grid_line)
if self.use_frame then
h = h + 30
if h < 36 then h = 36 end
self.scroll:SetWndPos(vector2():set( 15 , 15 ))
end
self.prof:SetWndSize(vector2():set( self.prof:GetWidth() , h ))
utils_xml.sync_size( self.prof , self.scroll )
end
function UICellContainer:AdjustWidthToCell()
local w = self.col_end * (self.grid_size + self.grid_line)
w = w * ratio
if self.use_frame then
w = w + 30
if w < 36 then w = 36 end
self.scroll:SetWndPos(vector2():set( 15 , 15 ))
end
self.prof:SetWndSize(vector2():set( w , self.prof:GetHeight() ))
utils_xml.sync_size( self.prof , self.scroll )
end
function UICellContainer:SetBackground(path, clr)
if (type(path) == "string") then
self.prof:InitTexture(path)
end
self.prof:SetTextureColor(clr)
end
function UICellContainer:SetGridSpecs(size, line)
self.grid_size = size or self.grid_size
self.grid_line = line or self.grid_line
self.cols = math.floor( (self.scroll:GetWidth() * (1/ratio)) / (self.grid_size + self.grid_line) )
self:Print(nil, "SetGridSpecs | size: %s - line: %s - num of colomns: %s", self.grid_size, self.grid_line, self.cols)
end
function UICellContainer:Show(state)
self.prof:Show(state)
if (not state) then
-- Hide hold icon
self.hold.idx = false
if self.hold.ico then
self.hold.ico:Show(false)
end
self.hover.idx = false
self.db.idx = false
end
end
function UICellContainer:IsShown()
return self.prof:IsShown()
end
function UICellContainer:EnableScrolling(state)
self.disable_scroll = (not state)
self.scroll:Enable(state)
end
function UICellContainer:IsCursorOverWindow()
return self.prof:IsShown() and self.prof:IsCursorOverWindow()
end
function UICellContainer:Print(mark, fmt,...)
--printf( (mark or "+") .. " UICellContainer [%s] | " .. fmt, self.ID, ...)
end
-------------------------------------------------------------------
-- Item info box
-------------------------------------------------------------------
class "UIInfoItem"
function UIInfoItem:__init(owner, delay)
self.owner = owner
self.id = nil
self.section = nil
self.can_compare = false
self.timer = 0
self.delay = delay or 700
self.ammo_parse = { [1]=true , [4] = true , [7] = true , [10] = true }
prepare_stats_table()
self:InitControls()
self:Show(false)
end
function UIInfoItem:InitControls()
--[[
self.xml = CScriptXmlInit()
local xml = self.xml
xml:ParseFile ("ui_item_info.xml")
--]]
self.dialog = XMLP:InitStatic("item_info", self.owner)
self.frame = XMLP:InitFrame("item_info:background_frame", self.dialog)
self.name = XMLP:InitTextWnd("item_info:name", self.dialog)
self.weight = XMLP:InitTextWnd("item_info:weight", self.dialog)
self.value = XMLP:InitTextWnd("item_info:cost", self.dialog)
self.note = XMLP:InitTextWnd("item_info:no_trade", self.dialog)
self.desc = XMLP:InitTextWnd("item_info:description", self.dialog)
-- Stats
self.stats_dialog = XMLP:InitStatic("item_info:stats_dialog", self.dialog)
self.stats = {}
for i=1,16 do
self.stats[i] = {}
self.stats[i].base = XMLP:InitStatic("item_info:stats_box", self.stats_dialog)
self.stats[i].icon = XMLP:InitStatic("item_info:stats_box:icon", self.stats[i].base)
self.stats[i].cap = XMLP:InitTextWnd("item_info:stats_box:cap", self.stats[i].base)
self.stats[i].bar2 = XMLP:InitProgressBar("item_info:stats_box:prog_bar", self.stats[i].base)
self.stats[i].bar1 = XMLP:InitProgressBar("item_info:stats_box:prog_bar", self.stats[i].base)
self.stats[i].txt = XMLP:InitTextWnd("item_info:stats_box:prog_txt", self.stats[i].base)
self.stats[i].comp = XMLP:InitTextWnd("item_info:stats_box:compare", self.stats[i].base)
end
-- Ammo type
self.ammo = XMLP:InitStatic("item_info:ammo_type", self.dialog)
self.ammo_cap = XMLP:InitStatic("item_info:ammo_type:cap_ammo_types", self.ammo)
self.ammo_txt = XMLP:InitTextWnd("item_info:ammo_type:cap_ammo_used_type", self.ammo)
self.ammo_ico = {}
self.ammo_ico_temp = {}
for i=1,12 do
if self.ammo_parse[i] then
self.ammo_ico[i] = XMLP:InitStatic("item_info:ammo_type:static_ammo_type" .. i, self.ammo)
self.ammo_ico_temp[i] = XMLP:InitStatic("item_info:ammo_type:static_ammo_type" .. i, self.ammo)
end
end
end
function UIInfoItem:Update(obj, sec, flags)
if not self:Pass(obj, sec) then
self:Show(false)
return
end
-- item info box is shown at cursor
sync_cursor(self.dialog, self.frame, 0, 0)
-- no need to process if it's same item
if obj then
if obj:id() == self.id then
self:Show(true)
return
end
elseif sec then
if sec == self.section then
self:Show(true)
return
end
end
-- gather basic info
sec = obj and obj:section() or sec
local typ = self:GetType(sec)
--printf("-updating | section [%s] - type: %s", sec, typ)
self.id = obj and obj:id() or nil
self.section = sec
-- Name
local name = obj and ui_item.get_obj_name(obj) or ui_item.get_sec_name(sec)
self.name:SetText( name )
-- Weight
local weight = obj and obj:weight() or ini_sys:r_float_ex(sec,"inv_weight")
self.weight:SetText( round_100(weight) .. " " .. game.translate_string("st_kg") )
-- Cost
if flags and flags.value_str and (not flags.note_str) then
self.value:SetText( flags.value_str )
self.value:Show(true)
else
self.value:Show(false)
end
-- Note
if flags and flags.note_str then
self.note:SetText( flags.note_str )
self.note:Show(true)
else
self.note:Show(false)
end
-- Description
local desc = obj and ui_item.get_obj_desc(obj) or ui_item.get_sec_desc(sec)
self.desc:SetText( desc )
self.desc:AdjustHeightToText()
-- Stop here?
if self:Sync_Finale( (not stats_table[typ]), nil, self.desc, self.frame, 10 ) then
self.stats_dialog:Show(false)
self.ammo:Show(false)
return
end
-- Stats
for i=1,#self.stats do
self.stats[i].base:Show(false)
end
-- Comparison
local obj_b
if self.can_compare and obj then
local cls = obj:clsid()
local slot = SYS_GetParam(2,sec,"slot",-1) + 1
obj_b = slot > 0 and db.actor:item_in_slot(slot)
end
local v = stats_table[typ]
local cnt, y = 0, 0
local cnt_last
for stat,gr in spairs(v,sort_by_index) do
-- get stat value
local val_a = get_stats_value(obj, sec, gr, stat)
if val_a then
if gr.show_always or ((not gr.show_always ) and val_a ~= 0) then
cnt = cnt + 1
local ele = self.stats[cnt]
-- Set up icon
local icon_p = gr.icon_p
local icon_n = (gr.icon_n ~= "") and gr.icon_n or icon_p
local icon = val_a < 0 and icon_n or icon_p
if gr.sign_inverse then
icon = val_a < 0 and icon_p or icon_n
end
ele.icon:InitTexture( icon )
-- Set up name
ele.cap:SetText( game.translate_string(gr.name) )
-- Reset
ele.bar2:Show(false)
ele.bar1:Show(false)
ele.txt:Show(false)
ele.comp:Show(false)
-- Progress bar
if gr.track then
local valbar_a = clamp((math.abs(val_a) * gr.magnitude), 0, 1)
-- Comparison item
local val_b = obj_b and get_stats_value(obj_b, obj_b:section(), gr, stat)
local valbar_b = val_b and clamp((math.abs(val_b) * gr.magnitude), 0, 1)
if valbar_b and (valbar_a ~= valbar_b) then
-- If focued item's value is bigger than slot item's value -> focued item's bar: green + 2nd layer
if valbar_a > valbar_b then
ele.bar1:SetProgressPos( valbar_b )
ele.bar1:SetColor( clr_list["info_def"] )
ele.bar2:SetProgressPos( valbar_a )
ele.bar2:SetColor( clr_list["info_p"] )
ele.comp:SetTextColor( clr_list["info_p_txt"] )
-- If focued item's value is smaller than slot item's value -> focued item's bar: red + 1nd layer
else
ele.bar1:SetProgressPos( valbar_a )
ele.bar1:SetColor( clr_list["info_def"] )
ele.bar2:SetProgressPos( valbar_b )
ele.bar2:SetColor( clr_list["info_n"] )
ele.comp:SetTextColor( clr_list["info_n_txt"] )
end
local diff_val = math.ceil( (valbar_a - valbar_b) * 100 )
ele.comp:SetText( (diff_val > 0 and "+" or "") .. diff_val .. "%" )
ele.comp:Show(true)
ele.bar1:Show(true)
ele.bar2:Show(true)
ele.bar1:ShowBackground(false)
-- No comparison
else
ele.bar1:SetProgressPos( valbar_a )
ele.bar1:SetColor( clr_list["info_def"] )
ele.bar1:Show(true)
ele.bar1:ShowBackground(true)
end
-- Text
elseif ele.txt then
local valbar_a = val_a * gr.magnitude
local unit = gr.unit and gr.unit ~= "" and game.translate_string(gr.unit) or ""
local clr = valbar_a >= 0 and clr_list["p1"] or clr_list["n1"]
valbar_a = math.ceil(valbar_a)
if gr.sign_inverse then
clr = valbar_a < 0 and clr_list["p1"] or clr_list["n1"]
--valbar_a = -1 * valbar_a -- invert sign again if needed
end
local sign = gr.sign and valbar_a > 0 and "+" or ""
-- Comparison item
local val_b = obj_b and get_stats_value(obj_b, obj_b:section(), gr, stat)
local valbar_b = val_b and val_b * gr.magnitude
if valbar_b and (valbar_a ~= valbar_b) then
local diff_val = math.ceil(valbar_a - valbar_b)
ele.comp:SetText( "(" .. (diff_val > 0 and "+" or "") .. diff_val .. ")" )
ele.comp:Show(true)
clr = (valbar_a > valbar_b) and clr_list["info_p_txt"] or clr_list["info_n_txt"]
end
if gr.sign_inverse_txt then
if valbar_a > 0 then
sign = "-"
elseif valbar_a < 0 then
valbar_a = -1 * valbar_a
sign = "+"
end
end
ele.txt:SetText( sign .. valbar_a .. " " .. unit )
ele.txt:SetTextColor( clr )
ele.txt:Show(true)
end
y = y + ele.base:GetHeight()
--printf("stat ele [%s] [%s] | cnt: %s - y: %s - value: %s", sec, stat, cnt, y, val_a)
local ele_prev = cnt_last and self.stats[cnt_last]
self:Sync_Y(ele_prev and ele_prev.base, ele.base, 0)
cnt_last = cnt
ele.base:Show(true)
end
end
end
self.stats_dialog:SetWndSize(vector2():set( self.stats_dialog:GetWidth() , y + 10 ))
self.stats_dialog:Show(true)
-- Stop here?
if self:Sync_Finale( (typ ~= "weapon"), self.desc, self.stats_dialog, self.frame, 10 ) then
self.ammo:Show(false)
return
end
-- Ammo type
if (typ == "weapon") then
local ammo_list = utils_item.get_ammo(sec, self.id, nil)
local ammo_name = ini_sys:r_string_ex(ammo_list[1], "inv_name_short") or ""
self.ammo_txt:SetText( game.translate_string(ammo_name) )
for i=1,12 do
if self.ammo_ico[i] then
if ammo_list[i] and (ammo_list[i] ~= "ammo_12x70_buck_self") then
utils_xml.set_icon(ammo_list[i], nil, self.ammo_ico[i], self.ammo_ico_temp[i])
self.ammo_ico[i]:Show(true)
self.ammo_ico_temp[i]:Show(true)
else
self.ammo_ico[i]:Show(false)
self.ammo_ico_temp[i]:Show(false)
end
end
end
self.ammo:Show(true)
else
self.ammo:Show(false)
end
-- Finale
self:Sync_Finale( true, self.stats_dialog, self.ammo, self.frame, 10 )
return
end
function UIInfoItem:Pass(obj, sec)
-- hide when no info is passed
if not (obj or sec) then
self:Reset()
return false
end
-- anticipate different object
if self.id then
if (not obj) then
self:Reset()
return false
else
if (obj:id() == self.id) then
return true
else
self:Reset()
return false
end
end
end
-- anticipate different section
if self.section then
if (not sec) then
self:Reset()
return false
else
if (sec == self.section) then
return true
else
self:Reset()
return false
end
end
end
-- delay
local tg = get_time()
if (tg < self.timer + self.delay) then
return false
end
self.timer = tg
return true
end
function UIInfoItem:GetType(sec)
if ini_sys:r_string_ex(sec, "ammo_class") and (not IsItem("fake_ammo_wpn", sec)) then
return "weapon"
elseif IsItem("outfit", sec) or IsItem("helmet", sec) then
return "outfit"
elseif IsItem("artefact", sec) then
return "artefact"
elseif IsItem("consumable", sec) then
return "booster"
elseif IsItem("backpack", sec) then
return "backpack"
end
return "none"
end
function UIInfoItem:GetUpgrades(obj)
if obj and IsWeapon(obj) and has_upgrades(obj) then
return utils_item.get_upgrades_installed(nil, obj:id())
end
return nil
end
function UIInfoItem:Sync_Finale(cond, ele_syncer, ele_resizer, ele_adapter, offset)
-- Stage 1: syncing
-- Adjust element position (ele_resizer) to go below another (ele_syncer)
if ele_syncer then
self:Sync_Y(ele_syncer, ele_resizer, offset)
end
-- Stage 2: checking
-- if condition is true, go to stage 3 to finilize the info box
if (not cond) then
return false
end
-- Stage 3: resizng
-- Adjust elemet height (ele_adapter) to contain another (ele_resizer)
self:Sync_H(ele_resizer, ele_adapter, offset)
self:Show(true)
return true
end
function UIInfoItem:Sync_Y(parent, child, offset)
local pos_c = child:GetWndPos()
if (not parent) then
local y = 0 + (offset or 10)
child:SetWndPos(vector2():set( pos_c.x , y ))
return y
end
local pos_p = parent:GetWndPos()
local h_p = parent:GetHeight()
local y = pos_p.y + h_p + (offset or 10)
child:SetWndPos(vector2():set( pos_c.x , y ))
return y
end
function UIInfoItem:Sync_H(parent, child, offset)
local pos_p = parent:GetWndPos()
local h_p = parent:GetHeight()
local h = pos_p.y + h_p + offset
if h < 300 then h = 300 end
child:SetWndSize(vector2():set( child:GetWidth() , h ))
return h
end
function UIInfoItem:Reset_Y(ele)
ele:SetWndPos(vector2():set( ele:GetWndPos().x , 0 ))
end
function UIInfoItem:Reset()
self.id = nil
self.section = nil
self.timer = get_time()
end
function UIInfoItem:IsShown()
return self.dialog:IsShown()
end
function UIInfoItem:Show(state)
if state == false then
-- hide all extended info
self.stats_dialog:Show(false)
self.ammo:Show(false)
end
self.dialog:Show(state)
end
-------------------------------------------------------------------
-- Upgrade info box
-------------------------------------------------------------------
class "UIInfoUpgr"
function UIInfoUpgr:__init(owner, delay)
self.owner = owner
self.section = nil
self.timer = 0
self.delay = delay or 500
self:InitControls()
self:Show(false)
end
function UIInfoUpgr:InitControls()
self.dialog = XMLP:InitStatic("upgrade_info", self.owner)
self.frame = XMLP:InitFrame("upgrade_info:background_frame", self.dialog)
self.name = XMLP:InitTextWnd("upgrade_info:info_name", self.dialog)
self.cost = XMLP:InitTextWnd("upgrade_info:info_cost", self.dialog)
self.desc = XMLP:InitTextWnd("upgrade_info:info_desc", self.dialog)
self.prereq = XMLP:InitTextWnd("upgrade_info:info_prerequisites", self.dialog)
self.prop_frame = XMLP:InitStatic("upgrade_info:properties", self.dialog)
-- Added by Sota
self.upgr_line = XMLP:InitStatic("upgrade_info:properties:upgr_line", self.prop_frame)
self.prop = {}
self.prop_h = self.prop_frame:GetHeight()
local pos
for i=1,5 do
self.prop[i] = {}
self.prop[i].ico = XMLP:InitStatic("upgrade_info:properties:icon", self.prop_frame)
pos = self.prop[i].ico:GetWndPos()
self.prop[i].ico:SetWndPos(vector2():set( pos.x , pos.y + (self.prop_h * i) ))
self.prop[i].txt = XMLP:InitTextWnd("upgrade_info:properties:txt", self.prop_frame)
pos = self.prop[i].txt:GetWndPos()
self.prop[i].txt:SetWndPos(vector2():set( pos.x , pos.y + (self.prop_h * i) ))
end
end
function UIInfoUpgr:Update(upgr, prereq, installed)
if not self:Pass(upgr) then
self:Show(false)
return
end
-- info box is shown at cursor
sync_cursor(self.dialog, self.frame, 0, 0)
-- no need to process if it's same item
if (upgr and upgr == self.section) then
self:Show(true)
return
end
self.section = upgr
local sec = ini_sys:r_string_ex(upgr,"section")
local name = ini_sys:r_string_ex(upgr,"name")
self.name:SetText( game.translate_string(name) )
self.name:AdjustHeightToText()
self.cost:SetText( inventory_upgrades.get_upgrade_cost(sec) .. " RU" )
self:Sync_Y(self.name, self.cost, 10)
local desc = ini_sys:r_string_ex(upgr,"description")
self.desc:SetText( game.translate_string(desc) )
self.desc:AdjustHeightToText()
self:Sync_Y(self.cost, self.desc, 10)
self.prereq:SetText(prereq or "")
self.prereq:SetTextColor(installed and clr_list["p2"] or clr_list["n2"])
self.prereq:AdjustHeightToText()
self:Sync_Y(self.desc, self.prereq, 10)
local props = parse_list(ini_sys, upgr, "property")
for i,prop in ipairs(props) do
local icon = ini_sys:r_string_ex(prop,"icon")
self.prop[i].ico:InitTexture(icon)
self.prop[i].ico:Show(true)
local value = self:ExtractFunctor(prop, "functor", sec, prop) or ""
self.prop[i].txt:SetText(value)
self.prop[i].txt:Show(true)
--printf("$UIInfoUpgr | i: %s - prop: %s - icon: %s - value: %s", i, prop, icon, value)
end
for i=(#props + 1), 5 do
self.prop[i].ico:Show(false)
self.prop[i].txt:Show(false)
end
self:Sync_Y(self.prereq, self.prop_frame, 10)
-- Edited by Sota
--self:Sync_H(self.prop_frame, self.frame, (self.prop_h * #props) )
self:Sync_H(self.prop_frame, self.frame, (self.prop_h * #props) + 20 )
return
end
function UIInfoUpgr:Pass(sec)
-- hide when no info is passed
local tg = get_time()
if (not sec) then
self.timer = tg
self.section = nil
return false
end
-- anticipate different section
if self.section then
if (not sec) then
self:Reset()
return false
elseif sec and (sec == self.section) then
return true
end
end
-- delay
if (tg < self.timer + self.delay) then
return false
end
self.timer = tg
return true
end
function UIInfoUpgr:ExtractFunctor(sec, param, ...)
local func = ini_sys:r_string_ex(sec, param)
func = str_explode(func,"%.")
return func and _G[func[1]] and _G[func[1]][func[2]] and _G[func[1]][func[2]](...) or nil
end
function UIInfoUpgr:Sync_Y(parent, child, offset)
local pos_c = child:GetWndPos()
if (not parent) then
local y = 0 + (offset or 10)
child:SetWndPos(vector2():set( pos_c.x , y ))
return y
end
local y = parent:GetWndPos().y + parent:GetHeight() + (offset or 10)
child:SetWndPos(vector2():set( pos_c.x , y ))
return y
end
function UIInfoUpgr:Sync_H(parent, child, offset)
local h = parent:GetWndPos().y + parent:GetHeight() + offset
-- Edited by Sota
--if (h < 265) then h = 265 end
child:SetWndSize(vector2():set( child:GetWidth() , h ))
return h
end
function UIInfoUpgr:Reset()
self.section = nil
self.timer = get_time()
end
function UIInfoUpgr:IsShown()
return self.dialog:IsShown()
end
function UIInfoUpgr:Show(state)
self.dialog:Show(state)
end
-------------------------------------------------------------------
-- Properties list for item cells
-------------------------------------------------------------------
class "UICellProperties" (CUIScriptWnd)
function UICellProperties:__init(owner) super()
self.owner = owner
-- frame size limit, game will crash if the frame size is less that it's min defines. Always check for frame corners defines in textures_desc
self.W_L = 58
self.H_L = 40
self.PDW = 10
self.PDH = 10
self.action_moment = false
self:InitControls()
self:InitCallBacks()
end
function UICellProperties:__finalize()
if (self.owner) then
--self.owner.disabled = false
end
end
function UICellProperties:InitControls()
self:SetWndRect(Frect():set(0,0,1024,768))
self:SetAutoDelete(true)
-- these windows are just to get width/height from xml to be use for context item
local ctrl = CUIWindow()
ctrl:SetAutoDelete(true)
XMLP:InitWindow("properties:file_item:main",0,ctrl)
self.file_item_main_sz = vector2():set(ctrl:GetWidth(),ctrl:GetHeight())
XMLP:InitWindow("properties:file_item:fn",0,ctrl)
self.file_item_fn_sz = vector2():set(ctrl:GetWidth(),ctrl:GetHeight())
-- form background
self.form = XMLP:InitStatic("properties:form",self)
-- List Box
self.frame = XMLP:InitFrame("properties:form:list_frame",self.form)
self.highlight = XMLP:InitStatic("properties:highlight",self.form)
self.list_box = XMLP:InitListBox("properties:form:list",self.form)
self.list_box:ShowSelectedItem(true)
self:Register(self.list_box, "UICellProperties_list_view")
end
function UICellProperties:Update()
CUIScriptWnd.Update(self)
--[[
local pos = GetCursorPosition()
local rect = Frect():set(0,0,0,0)
self.form:GetAbsoluteRect(rect)
if not (utils_data.pos_in_rect(pos,rect)) then
if (self:IsShown()) then
self:OnHide()
return
end
end
if (self.owner) then
self.owner.disabled = true
end
--]]
for i=0,self.list_box:GetSize() do
local _itm = self.list_box:GetItemByIndex(i)
if _itm and _itm:IsCursorOverWindow() then
local pos = _itm:GetWndPos()
self.highlight:SetWndPos( vector2():set( pos.x + self.PDH/2 , pos.y + self.PDW ) )
self.highlight:SetWndSize(vector2():set( _itm.textControl:GetWidth() , _itm.textControl:GetHeight() ))
self.highlight:Show(true)
return
end
end
self.highlight:Show(false)
-- self:AllowMovement(ui_inventory.GUI.mode == "inventory")
end
function UICellProperties:Reset(pos_override, action_list, name_list, params_list)
self.H = 0
self.W = 0
-- Reset list
self:FillList(action_list, name_list, params_list)
self.action_moment = false
-- Cursor
local pos = GetCursorPosition() -- because ShowDialog moves mouse cursor to center
self:ShowDialog()
SetCursorPosition(pos)
sync_cursor(self.form)
--[[
pos = pos_override and vector2():set(pos_override.x,pos_override.y) or GetCursorPosition()
pos.x = pos.x - self.form:GetWidth() --/2
--pos.y = pos.y - 22
if (pos.x < 0) then
pos.x = 0
end
self.form:SetWndPos(pos)
--]]
end
function UICellProperties:FillList(action_list, name_list, params_list)
self.list_box:RemoveAll()
for i,str_id in pairs(name_list) do
self:AddItemToList(i, str_id, action_list[i], params_list[i])
end
if self.H < self.H_L then self.H = self.H_L end
if self.W < self.W_L then self.W = self.W_L end
local sz = vector2():set(self.W , self.H)
self.form:SetWndSize(sz)
self.frame:SetWndSize(sz)
self.list_box:SetWndSize(sz)
self.highlight:Show(false)
end
function UICellProperties:OnListItemClicked()
if self.list_box:GetSize()==0 then return end
local item = self.list_box:GetSelectedItem()
if not (item) then
return
end
end
function UICellProperties:OnListItemDbClicked()
if self.list_box:GetSize()==0 then return end
local item = self.list_box:GetSelectedItem()
if not (item) then
return
end
if self.owner and (item.func) then
if item.params then
self.owner[item.func](self.owner, unpack(item.params))
else
self.owner[item.func](self.owner)
end
self.action_moment = get_time()
end
if (self:IsShown()) then
self:OnHide()
end
end
function UICellProperties:InitCallBacks()
self:AddCallback("button_ok",ui_events.BUTTON_CLICKED,self.OnButton_ok,self)
self:AddCallback("UICellProperties_list_view",ui_events.LIST_ITEM_CLICKED,self.OnListItemDbClicked,self)
--self:AddCallback("UICellProperties_list_view",ui_events.WINDOW_LBUTTON_DB_CLICK,self.OnListItemDbClicked,self)
end
function UICellProperties:AddItemToList(index, str_id, func, params)
local _itm = UICellProperties_item()
_itm:SetWndSize(self.file_item_main_sz)
_itm.textControl:SetWndPos(vector2():set(0,0))
_itm.textControl:SetWndSize(self.file_item_fn_sz)
_itm.textControl:SetText(game.translate_string(str_id))
_itm.textControl:AdjustWidthToText()
local h = self.file_item_fn_sz.y
local w = math.ceil(_itm.textControl:GetWidth())
w = w + self.PDW
local sz = vector2():set( w , h )
_itm.textControl:SetWndSize(sz)
_itm:SetWndSize(sz)
if w > self.W then self.W = w + self.PDW end
if self.H == 0 then self.H = h end
self.H = self.H + h
_itm.func = func
_itm.params = params
self.list_box:AddExistingItem(_itm)
end
function UICellProperties:OnKeyboard(dik, keyboard_action)
CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if (keyboard_action == ui_events.WINDOW_KEY_PRESSED) then
if (dik == DIK_keys.MOUSE_1) or (dik == DIK_keys.MOUSE_2) then
if (not self.form:IsCursorOverWindow()) then
if (self:IsShown()) then
self:OnHide()
end
end
elseif (dik == DIK_keys.DIK_ESCAPE) then
if (self:IsShown()) then
self:OnHide()
end
end
end
return true
end
function UICellProperties:OnHide()
if (self.owner) then
--self.owner.disabled = false
end
self:HideDialog()
end
----
class "UICellProperties_item" (CUIListBoxItem)
function UICellProperties_item:__init() super()
self:SetTextColor(GetARGB(255, 170, 170, 170))
self.textControl = self:GetTextItem()
self.textControl:SetFont(GetFontLetterica16Russian())
self.textControl:SetEllipsis(true)
end
function UICellProperties_item:__finalize()
end
-------------------------------------------------------------------
-- Hint window
-------------------------------------------------------------------
class "UIHint"
function UIHint:__init(owner, delay, path)
self.owner = owner
self.txt = ""
self.path = path or "hint_wnd"
self.timer = 0
self.delay = delay or 500 --[ms]
self:InitControls()
self:Show(false)
end
function UIHint:InitControls()
self.dialog = XMLP:InitFrame(self.path .. ":background",self.owner)
self.dialog_text = XMLP:InitTextWnd(self.path .. ":text",self.dialog)
end
function UIHint:Update(txt)
if not self:Pass(txt) then
self:Show(false)
return
end
-- no need to process if it's same txt
if (txt == self.txt) then
sync_cursor(self.dialog)
self:Show(true)
return
end
-- Set text
self.dialog_text:SetText(txt)
self.dialog_text:AdjustHeightToText()
-- Edited by Sota
-- Set wnd size
local w = self.dialog:GetWidth()
--w = w >= 96 and w or 96
--local h = self.dialog_text:GetHeight()+40
local h = self.dialog_text:GetHeight() + self.dialog_text:GetWndPos().y * 2
--h = h >= 96 and h or 96
self.dialog:SetWndSize(vector2():set(w,h))
FitInRect(self.dialog,Frect():set(0,0,1024,768),0,100)
-- box is shown at cursor
sync_cursor(self.dialog)
self.txt = txt
return
end
function UIHint:Pass(txt)
-- hide when no info is passed
local tg = get_time()
if not (txt and txt ~= "") then
self.timer = tg
self.txt = nil
return false
end
if txt == self.txt then
return true
end
-- delay
if (tg < self.timer + self.delay) then
return false
end
self.timer = tg
return true
end
function UIHint:Show(state)
self.dialog:Show(state)
end
------------------------------------------------------------
-- Utilities
------------------------------------------------------------
function prop_accuracry(obj,sec)
local fire_dispersion_base = utils_item.get_wpn_param(obj, sec, "fire_dispersion_base", 0)
return normalize(fire_dispersion_base, 1.5, 0)
end
function prop_handling(obj,sec)
local id = obj and obj:id()
local PDM_disp_base = utils_item.get_wpn_param(obj, sec, "PDM_disp_base", 0)
local control_inertion_factor = utils_item.get_wpn_param(obj, sec, "control_inertion_factor", 0)
return (normalize(PDM_disp_base, 2.1, 0) + normalize(control_inertion_factor, 3, 1))/2
end
function prop_damage(obj,sec)
local id = obj and obj:id()
local hit_power = utils_item.get_wpn_param(obj, sec, "hit_power", 0)
local ammo_list = utils_item.get_ammo(sec, id)
if ammo_list[1] then
local k_hit = ini_sys:r_float_ex(ammo_list[1], "k_hit") or 1
hit_power = (hit_power ~= 0) and (k_hit*hit_power) or k_hit
end
return normalize(hit_power, 0, 1.5)
end
function prop_rpm(obj,sec)
local rpm = utils_item.get_wpn_param(obj, sec, "rpm", 0)
return normalize(rpm, 0, 1000)
end
function prop_condition(obj,sec)
return obj and obj:condition() or 1
end
function get_utils_xml()
return XMLP
end