3546 lines
105 KiB
Plaintext
3546 lines
105 KiB
Plaintext
|
|
--[[
|
|
|
|
- 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
|