------------------------------------------------------------------------------- ---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