480 lines
21 KiB
Lua

-------------------------------------------------------------------------------
---Class to build PropertiesPanel panel
---@class PropertiesPanel
PropertiesPanel = newclass(Form,function(base,classname)
Form.init(base,classname)
base.add_special_button = true
end)
-------------------------------------------------------------------------------
---On initialization
function PropertiesPanel:onInit()
self.panelCaption = ({"helmod_result-panel.tab-button-properties"})
self.help_button = false
end
-------------------------------------------------------------------------------
---On bind
function PropertiesPanel:onBind()
Dispatcher:bind("on_gui_refresh", self, self.updateData)
end
-------------------------------------------------------------------------------
---Get Button Sprites
---@return string, string
function PropertiesPanel:getButtonSprites()
return defines.sprites.database_schema.white,defines.sprites.database_schema.black
end
-------------------------------------------------------------------------------
---Is visible
---@return boolean
function PropertiesPanel:isVisible()
return User.getModGlobalSetting("hidden_panels")
end
-------------------------------------------------------------------------------
---Is special
---@return boolean
function PropertiesPanel:isSpecial()
return true
end
-------------------------------------------------------------------------------
---Get or create menu panel
---@return LuaGuiElement
function PropertiesPanel:getMenuPanel()
local flow_panel, content_panel, menu_panel = self:getPanel()
local panel_name = "menu-panel"
if content_panel[panel_name] ~= nil and content_panel[panel_name].valid then
return content_panel[panel_name]
end
local panel = GuiElement.add(content_panel, GuiFrameH(panel_name))
panel.style.horizontally_stretchable = true
--panel.style.vertically_stretchable = true
return panel
end
-------------------------------------------------------------------------------
---Get or create header panel
---@return LuaGuiElement
function PropertiesPanel:getHeaderPanel()
local flow_panel, content_panel, menu_panel = self:getPanel()
local panel_name = "header-panel"
if content_panel[panel_name] ~= nil and content_panel[panel_name].valid then
return content_panel[panel_name]
end
local panel = GuiElement.add(content_panel, GuiFrameV(panel_name))
panel.style.horizontally_stretchable = true
--panel.style.vertically_stretchable = true
return panel
end
-------------------------------------------------------------------------------
---Get or create content panel
---@return LuaGuiElement
function PropertiesPanel:getContentPanel()
local flow_panel, content_panel, menu_panel = self:getPanel()
local panel_name = "content"
local scroll_name = "data-panel"
if content_panel[panel_name] ~= nil and content_panel[panel_name].valid then
return content_panel[panel_name][scroll_name]
end
local panel = GuiElement.add(content_panel, GuiFrameV(panel_name))
panel.style.horizontally_stretchable = true
panel.style.vertically_stretchable = true
local scroll_panel = GuiElement.add(panel, GuiScroll(scroll_name))
scroll_panel.style.horizontally_stretchable = true
scroll_panel.style.vertically_stretchable = true
return scroll_panel
end
-------------------------------------------------------------------------------
---On event
---@param event LuaEvent
function PropertiesPanel:onEvent(event)
if event.action == "element-delete" then
local prototype_compare = User.getParameter("prototype_compare") or {}
local index = nil
for i,prototype in pairs(prototype_compare) do
if prototype.name == event.item1 then
index = i
end
end
if index ~= nil then
table.remove(prototype_compare, index)
end
User.setParameter("prototype_compare", prototype_compare)
self:updateData(event)
end
if event.action == "filter-nil-property-switch" then
local switch_nil = event.element.switch_state == "right"
User.setParameter("filter-nil-property", switch_nil)
self:updateData(event)
end
if event.action == "filter-difference-property-switch" then
local switch_nil = event.element.switch_state == "right"
User.setParameter("filter-difference-property", switch_nil)
self:updateData(event)
end
if event.action == "technology-search" then
local state = event.element.state
Player.getForce().technologies[event.item1].researched = state
self:updateData(event)
end
if event.action == "filter-property" then
local filter = event.element.text
User.setParameter("filter-property", filter)
self:updateData(event)
end
end
-------------------------------------------------------------------------------
---Update data
---@param event LuaEvent
function PropertiesPanel:onUpdate(event)
local flow_panel, content_panel, menu_panel = self:getPanel()
local display_width, display_height, scale = User.getMainSizes()
local width_main = display_width/scale
local height_main = display_height/scale
flow_panel.style.height = height_main
flow_panel.style.width = width_main
self:updateMenu(event)
self:updateHeader(event)
self:updateData(event)
end
-------------------------------------------------------------------------------
---Update menu
---@param event LuaEvent
function PropertiesPanel:updateMenu(event)
local action_panel = self:getMenuPanel()
action_panel.clear()
GuiElement.add(action_panel, GuiButton("HMEntitySelector", "OPEN", "HMPropertiesPanel"):caption({"helmod_result-panel.select-button-entity"}))
GuiElement.add(action_panel, GuiButton("HMItemSelector", "OPEN", "HMPropertiesPanel"):caption({"helmod_result-panel.select-button-item"}))
GuiElement.add(action_panel, GuiButton("HMFluidSelector", "OPEN", "HMPropertiesPanel"):caption({"helmod_result-panel.select-button-fluid"}))
GuiElement.add(action_panel, GuiButton("HMRecipeSelector", "OPEN", "HMPropertiesPanel"):caption({"helmod_result-panel.select-button-recipe"}))
GuiElement.add(action_panel, GuiButton("HMTechnologySelector", "OPEN", "HMPropertiesPanel"):caption({"helmod_result-panel.select-button-technology"}))
end
-------------------------------------------------------------------------------
---Update data
---@param event LuaEvent
function PropertiesPanel:updateData(event)
if not(self:isOpened()) then return end
---data
local content_panel = self:getContentPanel()
content_panel.clear()
---data
local filter = User.getParameter("filter-property")
local prototype_compare = User.getParameter("prototype_compare")
if prototype_compare ~= nil then
local data = {}
for _,prototype in pairs(prototype_compare) do
local data_prototype = self:getPrototypeData(prototype)
local key = string.format("%s_%s", prototype.type, prototype.name)
for _,properties in pairs(data_prototype) do
if data[properties.name] == nil then data[properties.name] = {} end
data[properties.name][key] = properties
end
end
local result_table = GuiElement.add(content_panel, GuiTable("table-resources"):column(#prototype_compare+1):style("helmod_table-rule-odd"))
self:addTableHeader(result_table, prototype_compare)
for property, values in pairs(data) do
if filter == nil or filter == "" or string.find(property, filter, 0, true) then
if not(User.getParameter("filter-nil-property") == true and self:isNilLine(values, prototype_compare)) then
if not(User.getParameter("filter-difference-property") == true and self:isSameLine(values, prototype_compare)) then
local cell_name = GuiElement.add(result_table, GuiFrameH("property", property):style(helmod_frame_style.hidden))
GuiElement.add(cell_name, GuiLabel("label"):caption(property))
for index,prototype in pairs(prototype_compare) do
---col value
local cell_value = GuiElement.add(result_table, GuiFrameH(property, prototype.name, index):style(helmod_frame_style.hidden))
local key = string.format("%s_%s", prototype.type, prototype.name)
if values[key] ~= nil then
local chmod = values[key].chmod
local value = self:tableToString(values[key].value)
GuiElement.add(cell_value, GuiLabel("prototype_chmod"):caption(string.format("[%s]:", chmod)))
local label_value = GuiElement.add(cell_value, GuiLabel("prototype_value"):caption(value):style("helmod_label_max_600"))
label_value.style.width = 400
end
end
end
end
end
end
end
end
-------------------------------------------------------------------------------
---Add cell header
---@param guiTable LuaGuiElement
---@param name string
---@param caption string
---@param sorted any
function PropertiesPanel:addCellHeader(guiTable, name, caption, sorted)
if (name ~= "index" and name ~= "id" and name ~= "name" and name ~= "type") or User.getModGlobalSetting("display_data_col_"..name) then
local cell = GuiElement.add(guiTable, GuiFrameH("header", name):style(helmod_frame_style.hidden))
GuiElement.add(cell, GuiLabel("label"):caption(caption))
end
end
-------------------------------------------------------------------------------
---Add table header
---@param itable LuaGuiElement
---@param prototype_compare table
function PropertiesPanel:addTableHeader(itable, prototype_compare)
self:addCellHeader(itable, "property", {"helmod_result-panel.col-header-name"})
for index,prototype in pairs(prototype_compare) do
local icon_type = nil
local localised_name = nil
if prototype.type == "entity" then
local entity_prototype = EntityPrototype(prototype)
icon_type = "entity"
localised_name = entity_prototype:getLocalisedName()
elseif prototype.type == "item" then
local item_prototype = ItemPrototype(prototype)
icon_type = "item"
localised_name = item_prototype:getLocalisedName()
elseif prototype.type == "fluid" then
local fluid_prototype = FluidPrototype(prototype)
icon_type = "fluid"
localised_name = fluid_prototype:getLocalisedName()
elseif string.find(prototype.type, "recipe") then
local recipe_protoype = RecipePrototype(prototype)
icon_type = recipe_protoype:getType()
localised_name = recipe_protoype:getLocalisedName()
elseif prototype.type == "technology" then
local technology_protoype = Technology(prototype)
icon_type = "technology"
localised_name = technology_protoype:getLocalisedName()
end
local cell_header = GuiElement.add(itable, GuiFlowH("header", prototype.name, index))
GuiElement.add(cell_header, GuiButtonSprite(self.classname, "element-delete", prototype.name, index):sprite(icon_type, prototype.name):tooltip(localised_name))
if prototype.type == "technology" then
GuiElement.add(cell_header, GuiCheckBox(self.classname, "technology-search", prototype.name, index):state(Technology(prototype):isResearched()):tooltip("isResearched"))
end
end
self:addCellHeader(itable, "property_type", "Element Type")
for index,prototype in pairs(prototype_compare) do
GuiElement.add(itable, GuiLabel("element_type", prototype.name, index):caption(prototype.type))
end
self:addCellHeader(itable, "property_name", "Element Name")
for index,prototype in pairs(prototype_compare) do
local textfield = GuiElement.add(itable, GuiTextField("element_name", prototype.name, index):text(prototype.name))
textfield.style.width = 300
end
end
-------------------------------------------------------------------------------
---Update header
---@param event LuaEvent
function PropertiesPanel:updateHeader(event)
local info_panel = self:getHeaderPanel()
info_panel.clear()
local options_table = GuiElement.add(info_panel, GuiTable("options-table"):column(2))
---nil values
local switch_nil = "left"
if User.getParameter("filter-nil-property") == true then
switch_nil = "right"
end
GuiElement.add(options_table, GuiLabel("filter-nil-property"):caption("Hide nil values:"))
local filter_switch = GuiElement.add(options_table, GuiSwitch(self.classname, "filter-nil-property-switch"):state(switch_nil):leftLabel("Off"):rightLabel("On"))
---difference values
local switch_nil = "left"
if User.getParameter("filter-difference-property") == true then
switch_nil = "right"
end
GuiElement.add(options_table, GuiLabel("filter-difference-property"):caption("Show differences:"))
local filter_switch = GuiElement.add(options_table, GuiSwitch(self.classname, "filter-difference-property-switch"):state(switch_nil):leftLabel("Off"):rightLabel("On"))
GuiElement.add(options_table, GuiLabel("filter-property-label"):caption("Filter:"))
local filter_value = User.getParameter("filter-property")
local filter_field = GuiElement.add(options_table, GuiTextField(self.classname, "filter-property", "onchange"):text(filter_value))
filter_field.style.width = 300
end
-------------------------------------------------------------------------------
---Parse Properties
---@param prototype table
---@param level number
---@param prototype_type table
---@return table
function PropertiesPanel:parseProperties(prototype, level, prototype_type)
if prototype == nil then return "nil" end
if level > 2 then
return prototype
--return string.match(serpent.dump(prototype),"do local _=(.*);return _;end")
end
---special
local isluaobject, error = pcall(function() local test = prototype:help() return true end)
local object_type = type(prototype)
if isluaobject then
local properties = {}
local lua_type = string.match(prototype:help(), "Help for%s([^:]*)")
if lua_type == "LuaEntityPrototype" and prototype.name == "character" then
table.insert(properties, {name = "PLAYER.character_crafting_speed_modifier", chmod = "RW", value = Player.native().character_crafting_speed_modifier})
table.insert(properties, {name = "PLAYER.character_mining_speed_modifier", chmod = "RW", value = Player.native().character_mining_speed_modifier})
table.insert(properties, {name = "PLAYER.character_additional_mining_categories", chmod = "RW", value = string.match(serpent.dump(Player.native().character_additional_mining_categories),"do local _=(.*);return _;end")})
table.insert(properties, {name = "PLAYER.character_running_speed_modifier", chmod = "RW", value = Player.native().character_running_speed_modifier})
table.insert(properties, {name = "PLAYER.character_build_distance_bonus", chmod = "RW", value = Player.native().character_build_distance_bonus})
table.insert(properties, {name = "PLAYER.character_item_drop_distance_bonus", chmod = "RW", value = Player.native().character_item_drop_distance_bonus})
table.insert(properties, {name = "PLAYER.character_reach_distance_bonus", chmod = "RW", value = Player.native().character_reach_distance_bonus})
table.insert(properties, {name = "PLAYER.character_resource_reach_distance_bonus", chmod = "RW", value = Player.native().character_resource_reach_distance_bonus})
table.insert(properties, {name = "PLAYER.character_item_pickup_distance_bonus", chmod = "RW", value = Player.native().character_item_pickup_distance_bonus})
table.insert(properties, {name = "PLAYER.character_loot_pickup_distance_bonus", chmod = "RW", value = Player.native().character_loot_pickup_distance_bonus})
table.insert(properties, {name = "PLAYER.character_inventory_slots_bonus", chmod = "RW", value = Player.native().character_inventory_slots_bonus})
table.insert(properties, {name = "PLAYER.character_logistic_slot_count_bonus", chmod = "RW", value = Player.native().character_logistic_slot_count_bonus})
table.insert(properties, {name = "PLAYER.character_trash_slot_count_bonus", chmod = "RW", value = Player.native().character_trash_slot_count_bonus})
table.insert(properties, {name = "PLAYER.character_maximum_following_robot_count_bonus", chmod = "RW", value = Player.native().character_maximum_following_robot_count_bonus})
table.insert(properties, {name = "PLAYER.character_health_bonus", chmod = "RW", value = Player.native().character_health_bonus})
end
if (lua_type == "LuaEntityPrototype" or lua_type == "LuaItemPrototype") and prototype.type == "inserter" then
table.insert(properties, {name = "FORCE.inserter_stack_size_bonus", chmod = "RW", value = Player.getForce().inserter_stack_size_bonus})
table.insert(properties, {name = "FORCE.stack_inserter_capacity_bonus", chmod = "RW", value = Player.getForce().stack_inserter_capacity_bonus})
end
if lua_type == "LuaFluidBoxPrototype" then
return FluidboxPrototype(prototype):toData()
end
local help_string = string.gmatch(prototype:help(),"(%S+) [[](RW?)[]]")
local properties = {}
for key, chmod in help_string do
local value = nil
pcall( function()
value = self:parseProperties(prototype[key], level + 1, nil)
end)
if level == 0 then
table.insert(properties, {name = key, chmod = chmod, value = value})
else
properties[key]=value
end
end
return properties
elseif object_type == "table" then
local properties = {}
for key,value in pairs(prototype) do
properties[key] = self:parseProperties(value, level + 1, nil)
end
return properties
else
return prototype
end
end
-------------------------------------------------------------------------------
---Table to string
---@param value table
function PropertiesPanel:tableToString(value)
if type(value) == "table" then
local key2,_ = next(value)
if type(key2) ~= "number" then
local message = "{\n"
local first = true
for key,content in pairs(value) do
local mask = "%s%s%s=%s%s"
if not(first) then
message = message..",\n"
end
if type(content) == "table" then
message = string.format(mask, message, helmod_tag.color.orange, key, helmod_tag.color.close, string.match(serpent.dump(content),"do local _=(.*);return _;end"))
else
message = string.format(mask, message, helmod_tag.color.orange, key, helmod_tag.color.close, content)
end
first = false
end
value = message.."\n}"
else
local message = "{"
local first = true
for key,content in pairs(value) do
if not(first) then
message = message..","
end
message = message..tostring(self:tableToString(content))
first = false
end
value = message.."}"
end
end
return value
end
-------------------------------------------------------------------------------
---Is nil line
---@param values table
---@param prototype_compare any
---@return boolean
function PropertiesPanel:isNilLine(values, prototype_compare)
local is_nil = true
for index,prototype in pairs(prototype_compare) do
local key = string.format("%s_%s", prototype.type, prototype.name)
if values[key] ~= nil and values[key].value ~= "nil" then is_nil = false end
end
return is_nil
end
-------------------------------------------------------------------------------
---Is same line
---@param values table
---@param prototype_compare any
---@return boolean
function PropertiesPanel:isSameLine(values, prototype_compare)
local is_same = true
local compare = nil
for index,prototype in pairs(prototype_compare) do
local key = string.format("%s_%s", prototype.type, prototype.name)
if values[key] ~= nil then
if compare == nil then
compare = values[key].value
else
if values[key].value ~= compare then is_same = false end
end
end
end
return is_same
end
-------------------------------------------------------------------------------
---Get prototype data
---@param prototype table
function PropertiesPanel:getPrototypeData(prototype)
---data
if prototype ~= nil then
local lua_prototype = nil
if prototype.type == "entity" then
lua_prototype = EntityPrototype(prototype):native()
elseif prototype.type == "item" then
lua_prototype = ItemPrototype(prototype):native()
elseif prototype.type == "fluid" then
lua_prototype = FluidPrototype(prototype):native()
elseif string.find(prototype.type, "recipe") then
local recipe_prototype = RecipePrototype(prototype)
lua_prototype = recipe_prototype:native()
if recipe_prototype:getType() ~= "recipe" then
function lua_prototype:help()
local help = "Help for LuaRecipePrototype:Methods:help(...)Values:"
for key,_ in pairs(lua_prototype) do
help = string.format("%s %s [R]", help, key)
end
return help
end
end
elseif prototype.type == "technology" then
lua_prototype = Technology(prototype):native()
end
if lua_prototype ~= nil then
return self:parseProperties(lua_prototype, 0, prototype.type)
end
end
return {}
end