z_mirror/RecipeBook
git clone https://git.raiguard.me/z_mirror/RecipeBook
9eaa7d2
trunk
1local flib_dictionary =require ( "__flib__.dictionary" ) 2local flib_format =require ( "__flib__.format" ) 3local flib_gui =require ( "__flib__.gui" ) 4local flib_math =require ( "__flib__.math" ) 5local util =require ( "scripts.util" ) 6 7local gui_util =require ( "scripts.gui.util" ) 8local researched =require ( "scripts.database.researched" ) 9 10local rocket_lift_weight =prototypes . utility_constants . rocket_lift_weight --[[@as double]] 11 12--- @class InfoDescription 13--- @field context MainGuiContext 14--- @field callback flib.GuiElemHandler 15--- @field frame LuaGuiElement 16--- @field has_content boolean 17local info_description ={} 18local mt ={ __index =info_description } 19script . register_metatable ( "info_description" , mt ) 20 21--- @param parent LuaGuiElement 22--- @param context MainGuiContext 23--- @param callback flib.GuiElemHandler 24--- @return InfoDescription 25function info_description . new ( parent , context , callback ) 26local frame =parent . add ( { 27type ="frame" , 28style ="deep_frame_in_shallow_frame_for_description" , 29horizontal_scroll_policy ="never" , 30direction ="vertical" , 31} ) 32local self ={ 33context =context , 34frame =frame , 35callback =callback , 36has_content =false , 37} 38setmetatable ( self , mt ) 39return self 40end 41 42--- @param prototype GenericPrototype 43--- @return string 44local function get_prototype_type ( prototype ) 45local object_name =prototype . object_name 46if 47object_name == "LuaEquipmentPrototype" 48or object_name == "LuaEntityPrototype" 49or object_name == "LuaItemPrototype" 50then 51return prototype . type 52end 53return util . object_name_to_type [ object_name ] 54end 55 56--- @return LocalisedString 57local function mod_name_string ( internal_name ) 58return { "?" , { "mod-name." .. internal_name } , internal_name } 59end 60 61function info_description : add_separator () 62self . new_section =true 63end 64 65--- @private 66--- @param args LuaGuiElement.add_param 67--- @return LuaGuiElement 68function info_description : add_internal ( args ) 69if self . new_section then 70self . new_section =false 71if # self . frame . children_names > 0 then 72self . frame . add ( { type ="line" , style ="rb_description_line" , direction ="horizontal" } ) 73end 74end 75return self . frame . add ( args ) 76end 77 78--- @param icon SpritePath 79--- @param caption LocalisedString 80--- @param id DatabaseID? 81function info_description : add_category_header ( icon , caption , id ) 82if id then 83local flow =self : add_internal ( { type ="flow" } ) 84flow . style . vertical_align ="center" 85flow . add ( { 86type ="label" , 87style ="tooltip_heading_label_category" , 88caption ={ "" , "[img=" .. icon .. "] " , caption } , 89} ) 90flow . add ( { 91type ="button" , 92style ="rb_description_heading_id_button" , 93caption ={ "" , "[img=" .. id . type .. "/" .. id . name .. "] " , util . get_prototype ( id ). localised_name } , --- @diagnostic disable-line:assign-type-mismatch 94elem_tooltip ={ type =id . type , name =id . name } , 95tags =flib_gui . format_handlers ( 96{ [ defines . events . on_gui_click ] =self . callback } , 97{ rb_id ={ type =id . type , name =id . name } } 98), 99} ) 100flow . style . top_margin =- 4 101flow . style . bottom_margin =- 4 102else 103self : add_internal ( { 104type ="label" , 105style ="tooltip_heading_label_category" , 106caption ={ "" , "[img=" .. icon .. "] " , caption } , 107} ) 108end 109local line =self : add_internal ( { type ="line" , style ="tooltip_category_line" } ) 110line . style . left_margin =- 8 111line . style . right_margin =- 8 112end 113 114local mod_name_separator =" › " 115 116--- @param prototype GenericPrototype 117function info_description : add_prototype_history ( prototype ) 118local history =prototypes . get_history ( get_prototype_type ( prototype ), prototype . name ) 119if history . created ~= "base" or # history . changed > 0 then 120--- @type LocalisedString 121local output ={ "" , mod_name_string ( history . created ) } 122local x =2 123for _ , changed in pairs ( history . changed ) do 124local mod_name =mod_name_string ( changed ) 125x =x + 2 126if x > 20 then 127x =4 128output ={ "" , output , mod_name_separator , mod_name } 129else 130output [ # output + 1 ] =mod_name_separator 131output [ # output + 1 ] =mod_name 132end 133end 134self : add_internal ( { type ="label" , style ="info_label" , caption =output } ) 135self : add_separator () 136end 137end 138 139--- @param prototype GenericPrototype 140function info_description : add_description ( prototype ) 141local descriptions =flib_dictionary . get ( self . context . player . index , "description" ) or {} 142local path =util . get_path ( prototype ) 143local description =descriptions [ path ] 144if description then 145local label =self : add_internal ( { type ="label" , caption =description } ) 146label . style . single_line =false 147self : add_separator () 148end 149end 150 151--- @param label LocalisedString 152--- @param value LocalisedString 153--- @return LuaGuiElement 154function info_description : add_generic_row ( label , value ) 155local flow =self : add_internal ( { type ="flow" } ) 156flow . add ( { type ="label" , style ="semibold_caption_label" , caption ={ "" , label , ":" } } ) 157flow . add ( { type ="label" , caption =value } ) 158return flow 159end 160 161--- @param label LocalisedString 162--- @param id DatabaseID 163--- @param force_index uint 164--- @return LuaGuiElement 165function info_description : add_id_row ( label , id , force_index ) 166local flow =self : add_internal ( { type ="flow" } ) 167flow . style . vertical_align ="center" 168flow . add ( { type ="label" , style ="semibold_caption_label" , caption ={ "" , label , ":" } } ) 169 170local id_prototype =util . get_prototype ( id ) 171local style ="rb_description_id_button" 172if id_prototype . hidden_in_factoriopedia then 173style ="rb_description_id_button_hidden" 174elseif not researched . is ( id_prototype , force_index ) then 175style ="rb_description_id_button_unresearched" 176end 177 178flow . add ( { 179type ="button" , 180style =style , 181caption ={ "" , "[img=" .. id . type .. "/" .. id . name .. "] " , gui_util . format_caption ( id ) } , 182elem_tooltip ={ type =id . type , name =id . name } , 183tags =flib_gui . format_handlers ( 184{ [ defines . events . on_gui_click ] =self . callback } , 185{ rb_id ={ type =id . type , name =id . name } } 186), 187} ) 188return flow 189end 190 191--- @param recipe LuaRecipePrototype 192function info_description : add_recipe_properties ( recipe ) 193local pollution =recipe . emissions_multiplier 194if pollution ~= 1 then 195self : add_generic_row ( { "description.recipe-pollution" } , flib_format . number ( pollution * 100 , false , 0 ) .. "%" ) 196end 197end 198 199--- @param item LuaItemPrototype 200function info_description : add_item_properties ( item ) 201local fuel_category =item . fuel_category 202if fuel_category then 203self : add_internal ( { 204type ="label" , 205style ="semibold_caption_label" , 206caption =prototypes . fuel_category [ fuel_category ]. localised_name , --- @diagnostic disable-line:assign-type-mismatch 207} ) 208end 209 210local fuel_value =item . fuel_value 211if fuel_value > 0 then 212self : add_generic_row ( 213{ "description.fuel-value" } , 214{ "" , flib_format . number ( flib_math . round ( fuel_value , 0.01 ), true ), { "si-unit-symbol-joule" } } 215) 216end 217 218local fuel_pollution =item . fuel_emissions_multiplier 219if fuel_pollution ~= 1 then 220self : add_generic_row ( 221{ "description.fuel-pollution" } , 222{ "format-percent" , flib_format . number ( flib_math . round ( fuel_pollution * 100 , 0.01 ), true ) } 223) 224end 225 226local fuel_acceleration_multiplier =item . fuel_acceleration_multiplier 227if fuel_acceleration_multiplier ~= 1 then 228self : add_generic_row ( 229{ "description.fuel-acceleration" } , 230{ "format-percent" , flib_format . number ( flib_math . round ( fuel_acceleration_multiplier * 100 , 0.01 ), true ) } 231) 232end 233 234local fuel_top_speed_multiplier =item . fuel_top_speed_multiplier 235if fuel_top_speed_multiplier ~= 1 then 236self : add_generic_row ( 237{ "description.fuel-top-speed" } , 238{ "format-percent" , flib_format . number ( flib_math . round ( fuel_top_speed_multiplier * 100 , 0.01 ), true ) } 239) 240end 241 242local burnt_result =item . burnt_result 243if burnt_result then 244self : add_id_row ( 245{ "description.spent-result" } , 246{ type ="item" , name =burnt_result . name } , 247self . context . player . force_index 248) 249end 250 251local module_effects =item . module_effects 252if module_effects then 253for key , value in pairs ( module_effects ) do 254if key == "quality" and # prototypes . quality == 2 then -- "Normal" and "unknown" 255goto continue 256end 257local caption =flib_format . number ( value * 100 , false , 2 ) .. "%" 258if value > 0 then 259caption ="+" .. caption 260end 261self : add_generic_row ( { "description." .. key .. "-bonus" } , caption ) 262:: continue :: 263end 264end 265 266local stack_size =item . stack_size 267if stack_size > 0 then 268self : add_generic_row ( { "description.stack-size" } , flib_format . number ( stack_size , true )) 269end 270 271if script . feature_flags . space_travel and item . weight > 0 then 272local rocket_capacity =math . floor ( rocket_lift_weight / item . weight ) 273if rocket_capacity > 0 then 274--- @type LocalisedString 275local caption ={ "description.rocket-capacity-value-short" , util . format_number ( rocket_capacity ) } 276if stack_size > 0 and rocket_capacity ~= stack_size then 277local stack_count =rocket_capacity / stack_size 278if stack_count >= 0.1 then 279caption [ 1 ] ="description.rocket-capacity-value" 280caption [ # caption + 1 ] =stack_count 281caption [ # caption + 1 ] =flib_format . number ( flib_math . round ( stack_count , 0.1 )) 282end 283end 284self : add_generic_row ( { "description.rocket-capacity" } , caption ) 285else 286self : add_generic_row ( { "description.rocket-capacity" } , { "description.too-heavy-for-rocket" } ) 287end 288end 289end 290 291--- @param fluid LuaFluidPrototype 292function info_description : add_fluid_properties ( fluid ) 293local fuel_value =fluid . fuel_value 294if fuel_value > 0 then 295self : add_generic_row ( 296{ "description.fuel-value" } , 297{ "" , flib_format . number ( flib_math . round ( fuel_value , 0.01 ), true ), { "si-unit-symbol-joule" } } 298) 299end 300 301local min_temperature =flib_math . round ( fluid . default_temperature , 0.01 ) 302local max_temperature =flib_math . round ( fluid . max_temperature , 0.01 ) 303 304--- @param temperature double 305local function format_temperature ( temperature ) 306local output =flib_format . number ( temperature , true ) 307if temperature < 1000 then 308output =output .. " " 309end 310return { "" , output , { "si-unit-degree-celsius" } } 311end 312 313if min_temperature ~= max_temperature then 314self : add_generic_row ( { "description.minimum-temperature" } , format_temperature ( min_temperature )) 315self : add_generic_row ( { "description.maximum-temperature" } , format_temperature ( max_temperature )) 316self : add_generic_row ( { "description.heat-capacity" } , { 317"" , 318flib_format . number ( flib_math . round ( fluid . heat_capacity , 0.01 ), true ), 319" " , 320{ "si-unit-symbol-joule" } , 321"/" , 322{ "si-unit-degree-celsius" } , 323} ) 324end 325end 326 327local container_types ={ 328[ "container" ] =true , 329[ "logistic-container" ] =true , 330[ "infinity-container" ] =true , 331[ "cargo-wagon" ] =true , 332} 333 334--- @param entity LuaEntityPrototype 335function info_description : add_entity_properties ( entity ) 336local max_underground_distance =entity . max_underground_distance 337if max_underground_distance then 338self : add_generic_row ( 339{ "description.maximum-length" } , 340flib_format . number ( flib_math . round ( max_underground_distance , 0.01 ), true ) 341) 342end 343 344local belt_speed =entity . belt_speed 345if belt_speed then 346self : add_generic_row ( { "description.belt-speed" } , { 347"" , 348flib_format . number ( flib_math . round ( belt_speed * 8 * 60 , 0.01 ), true ), 349" " , 350{ "description.belt-items" } , 351{ "per-second-suffix" } , 352} ) 353end 354 355if container_types [ entity . type ] then 356local storage_size =entity . get_inventory_size ( defines . inventory . chest ) 357if storage_size then 358self : add_generic_row ( { "description.storage-size" } , flib_format . number ( storage_size , true )) 359end 360end 361 362if entity . type == "storage-tank" or entity . type == "fluid-wagon" then 363self : add_generic_row ( { "description.fluid-capacity" } , flib_format . number ( entity . fluid_capacity , true )) 364end 365 366local rotation_speed =entity . get_inserter_rotation_speed () -- TODO: Quality 367if rotation_speed then 368self : add_generic_row ( 369{ "description.rotation-speed" } , 370{ "" , { "format-degrees" , flib_format . number ( rotation_speed * 360 * 60 , false , 0 ) } , { "per-second-suffix" } } 371) 372end 373 374if entity . type == "inserter" then 375local force_bonus =0 376local force =self . context . player . force 377if entity . bulk then 378force_bonus =force . bulk_inserter_capacity_bonus 379else 380force_bonus =force . inserter_stack_size_bonus 381end 382local stack_size =1 + entity . inserter_stack_size_bonus --[[@as uint]] 383local label =flib_format . number ( stack_size ) 384if force_bonus ~= 0 then 385label =label .. " + " .. flib_format . number ( force_bonus ) 386end 387self : add_generic_row ( { "description.hand-stack-size" } , label ) 388if entity . filter_count > 0 then 389self : add_internal ( { 390type ="label" , 391style ="semibold_caption_label" , 392caption ={ "description.can-filter-items" } , 393} ) 394end 395end 396 397if entity . type == "electric-pole" then 398self : add_generic_row ( { "description.wire-reach" } , flib_format . number ( entity . get_max_wire_distance ())) -- TODO: Quality 399local supply_area =flib_format . number ( entity . get_supply_area_distance () * 2 ) -- TODO: Quality 400self : add_generic_row ( { "description.supply-area" } , supply_area .. "×" .. supply_area ) 401end 402 403if entity . type == "pump" or entity . type == "offshore-pump" then 404local pumping_speed =entity . pumping_speed 405if pumping_speed then 406self : add_generic_row ( 407{ "description.pumping-speed" } , 408{ "" , flib_format . number ( pumping_speed * 60 , true ), { "per-second-suffix" } } 409) 410end 411end 412 413if entity . type == "resource" then 414local mineable_properties =entity . mineable_properties 415local fluid_name =mineable_properties . required_fluid 416if fluid_name then 417local fluid_id ={ type ="fluid" , name =fluid_name , amount =mineable_properties . fluid_amount / 10 } 418if fluid_id then 419self : add_id_row ( { "description.rb-mining-fluid" } , fluid_id , self . context . player . force_index ) 420end 421end 422end 423 424local burner =entity . burner_prototype 425if burner then 426for pollutant_name , pollution in pairs ( burner . emissions_per_joule ) do 427if pollution ~= 0 then 428self : add_generic_row ( 429prototypes . airborne_pollutant [ pollutant_name ]. localised_name , --- @diagnostic disable-line:param-type-mismatch 430{ 431"" , 432flib_format . number ( pollution * entity . get_max_energy_usage () * 60 * 60 , false ), -- TODO: Quality 433{ "per-minute-suffix" } , 434} 435) 436end 437end 438end 439 440local electric_energy_source_prototype =entity . electric_energy_source_prototype 441if electric_energy_source_prototype then 442for pollutant_name , pollution in pairs ( electric_energy_source_prototype . emissions_per_joule ) do 443if pollution ~= 0 then 444self : add_generic_row ( 445prototypes . airborne_pollutant [ pollutant_name ]. localised_name , --- @diagnostic disable-line:param-type-mismatch 446{ 447"" , 448flib_format . number ( pollution * entity . get_max_energy_usage () * 60 * 60 , false ), -- TODO: Quality 449{ "per-minute-suffix" } , 450} 451) 452end 453end 454end 455 456if entity . type == "rocket-silo" then 457self : add_generic_row ( { "description.rb-rocket-parts-required" } , util . format_number ( entity . rocket_parts_required )) 458end 459 460if entity . type == "mining-drill" then 461self : add_generic_row ( 462{ "description.mining-speed" } , 463{ "" , util . format_number ( entity . mining_speed ), { "per-second-suffix" } } 464) 465local mining_radius =util . format_number ( math . ceil ( entity . mining_drill_radius * 2 )) 466self : add_generic_row ( { "description.mining-area" } , { "" , mining_radius , "×" , mining_radius } ) 467end 468end 469 470--- @param id DatabaseID 471function info_description : add_consumption ( id ) 472self : add_category_header ( gui_util . get_tooltip_category_sprite ( id , "consumes" ), { "tooltip-category.consumes" } , id ) 473assert ( id . amount ) 474self : add_generic_row ( 475{ "description.energy-consumption" } , 476{ "" , flib_format . number ( id . amount ), { "per-second-suffix" } } 477) 478if id . minimum_temperature then 479self : add_generic_row ( 480{ "description.minimum-temperature" } , 481{ "" , flib_format . number ( id . minimum_temperature ), " " , { "si-unit-degree-celsius" } } 482) 483end 484if id . maximum_temperature then 485self : add_generic_row ( 486{ "description.maximum-temperature" } , 487{ "" , flib_format . number ( id . maximum_temperature ), " " , { "si-unit-degree-celsius" } } 488) 489end 490end 491 492--- @param id DatabaseID 493function info_description : add_production ( id ) 494self : add_category_header ( gui_util . get_tooltip_category_sprite ( id , "generates" ), { "tooltip-category.generates" } , id ) 495assert ( id . amount ) 496self : add_generic_row ( { "description.fluid-output" } , { "" , flib_format . number ( id . amount ), { "per-second-suffix" } } ) 497if id . temperature then 498self : add_generic_row ( 499{ "description.temperature" } , 500{ "" , flib_format . number ( id . temperature ), " " , { "si-unit-degree-celsius" } } 501) 502end 503if id . minimum_temperature then 504self : add_generic_row ( 505{ "description.minimum-temperature" } , 506{ "" , flib_format . number ( id . minimum_temperature ), " " , { "si-unit-degree-celsius" } } 507) 508end 509if id . maximum_temperature then 510self : add_generic_row ( 511{ "description.maximum-temperature" } , 512{ "" , flib_format . number ( id . maximum_temperature ), " " , { "si-unit-degree-celsius" } } 513) 514end 515end 516 517--- @param entity LuaEntityPrototype 518function info_description : add_vehicle_properties ( entity ) 519if not gui_util . vehicles [ entity . type ] then 520return 521end 522 523self : add_category_header ( "tooltip-category-vehicle" , { "tooltip-category.vehicle" } ) 524if not string . find ( entity . type , "wagon" ) then 525local max_speed =entity . speed 526if max_speed then 527self : add_generic_row ( 528{ "description.max-speed" } , 529{ "si-unit-kilometer-per-hour" , flib_format . number ( max_speed * 60 * 60 * 60 / 1000 , false , 0 ) } 530) 531end 532-- TODO: Add read for vehicle max acceleration power 533-- local burner = entity.burner_prototype 534-- if burner then 535-- end 536-- if acceleration_power then 537-- self:add_generic_row( 538-- { "description.max-speed" }, 539-- { "", flib_format.number(acceleration_power * 60 * 60 * 60 / 1000, false, 0), { "si-unit-kilometer-per-hour" } } 540-- ) 541-- end 542end 543local weight =entity . weight 544if weight then 545self : add_generic_row ( { "description.weight" } , flib_format . number ( weight )) 546end 547end 548 549function info_description : finalize () 550if # self . frame . children_names == 0 then 551self . frame . destroy () 552end 553end 554 555return info_description