z_mirror/RecipeBook
git clone https://git.raiguard.me/z_mirror/RecipeBook
a949b6c
trunk
1local flib_gui =require ( "__flib__.gui" ) 2local flib_gui_templates =require ( "__flib__.gui-templates" ) 3local flib_technology =require ( "__flib__.technology" ) 4 5local gui_util =require ( "scripts.gui.util" ) 6local info_description =require ( "scripts.gui.info-description" ) 7local info_section =require ( "scripts.gui.info-section" ) 8 9local collectors =require ( "scripts.database.collectors" ) 10local grouped =require ( "scripts.database.grouped" ) 11local researched =require ( "scripts.database.researched" ) 12local util =require ( "scripts.util" ) 13 14--- @class InfoPane 15--- @field context MainGuiContext 16--- @field title_label LuaGuiElement 17--- @field type_label LuaGuiElement 18--- @field content_pane LuaGuiElement 19local info_pane ={} 20local mt ={ __index =info_pane } 21script . register_metatable ( "info_pane" , mt ) 22 23--- @type function? 24info_pane . on_result_clicked =nil 25 26--- @param parent LuaGuiElement 27--- @param context MainGuiContext 28function info_pane . build ( parent , context ) 29local frame =parent . add ( { 30type ="frame" , 31style ="inside_shallow_frame" , 32direction ="vertical" , 33} ) 34frame . style . width =( 40 * 10 ) + 24 + 12 35 36local subheader =frame . add ( { type ="frame" , style ="subheader_frame" } ) 37local title_label =subheader . add ( { 38type ="sprite-button" , 39name ="page_header_title" , 40style ="rb_subheader_caption_button" , 41caption ={ "gui.rb-welcome-title" } , 42} ) 43subheader . add ( { type ="empty-widget" , style ="flib_horizontal_pusher" } ) 44local type_label =subheader . add ( { 45type ="label" , 46name ="page_header_type_label" , 47style ="rb_info_label" , 48} ) 49type_label . style . right_margin =8 50 51local content_pane =frame . add ( { 52type ="scroll-pane" , 53style ="rb_info_scroll_pane" , 54horizontal_scroll_policy ="never" , 55vertical_scroll_policy ="always" , 56} ) 57 58-- Will be deleted when the first page is shown 59content_pane . add ( { 60type ="label" , 61name ="welcome_label" , 62caption ={ "gui.rb-welcome-text" } , 63} ). style . single_line = 64false 65 66local self ={ 67context =context , 68title_label =title_label , 69type_label =type_label , 70content_pane =content_pane , 71} 72setmetatable ( self , mt ) 73return self 74end 75 76--- @param prototype GenericPrototype 77--- @return boolean? updated 78function info_pane : show ( prototype ) 79local profiler =game . create_profiler () 80 81-- Subheader 82do 83local title_label =self . title_label 84title_label . caption ={ "" , " " , prototype . localised_name } --- @diagnostic disable-line:assign-type-mismatch 85title_label . sprite =util . get_path ( prototype ) 86local style ="rb_subheader_caption_button" 87-- TODO: Technology runtime enable/disable 88if prototype . hidden_in_factoriopedia then 89style ="rb_subheader_caption_button_hidden" 90elseif not researched . is ( prototype , self . context . player . force_index ) then 91style ="rb_subheader_caption_button_unresearched" 92end 93title_label . style =style 94title_label . style . horizontally_squashable =true 95end 96 97local prototype_path =util . get_path ( prototype ) 98--- @type DatabaseID 99local prototype_id ={ type =util . object_name_to_type [ prototype . object_name ], name =prototype . name } 100 101-- TODO: Refactor this trash 102local recipe =prototype . object_name == "LuaRecipePrototype" and prototype --[[@as LuaRecipePrototype]] 103or ( self . context . grouping_mode == "all" and grouped . recipe [ prototype_path ] or nil ) 104local item =prototype . object_name == "LuaItemPrototype" and prototype --[[@as LuaItemPrototype]] 105or ( 106self . context . grouping_mode == "all" 107and grouped . material [ prototype_path ] 108and grouped . material [ prototype_path ]. object_name == "LuaItemPrototype" 109and grouped . material [ prototype_path ] 110or nil 111) 112local entity =prototype . object_name == "LuaEntityPrototype" and prototype --[[@as LuaEntityPrototype]] 113or ( self . context . grouping_mode ~= "none" and grouped . entity [ prototype_path ] or nil ) 114 115--- @type LuaEntityPrototype? 116local grouped_entity 117--- @type LuaEquipmentPrototype? 118local grouped_equipment 119--- @type LuaRecipePrototype? 120local grouped_recipe 121--- @type LuaTilePrototype? 122local grouped_tile 123if self . context . grouping_mode == "all" then 124grouped_recipe =grouped . recipe [ prototype_path ] 125end 126if self . context . grouping_mode ~= "none" then 127grouped_entity =grouped . entity [ prototype_path ] 128grouped_equipment =grouped . equipment [ prototype_path ] 129grouped_tile =grouped . tile [ prototype_path ] 130end 131 132do 133--- @type LocalisedString 134local type_caption ={ "" } 135--- @param sub_prototype GenericPrototype 136local function add_type_locale ( sub_prototype ) 137type_caption [ # type_caption + 1 ] =gui_util . type_locale [ sub_prototype . object_name ] --- @diagnostic disable-line:assign-type-mismatch 138type_caption [ # type_caption + 1 ] ="/" 139end 140 141if self . context . grouping_mode == "all" then 142local grouped_recipe =grouped . recipe [ prototype_path ] 143if grouped_recipe then 144add_type_locale ( grouped_recipe ) 145end 146end 147add_type_locale ( prototype ) 148if grouped_entity then 149add_type_locale ( grouped_entity ) 150end 151if grouped_equipment then 152add_type_locale ( grouped_equipment ) 153end 154if grouped_tile then 155add_type_locale ( grouped_tile ) 156end 157type_caption [ # type_caption ] =nil 158self . type_label . caption =type_caption 159end 160 161-- Contents 162 163local force_index =self . context . player . force_index 164 165--- @param id DatabaseID 166--- @param holder LuaGuiElement 167--- @return LuaGuiElement 168local function make_list_box_item ( id , holder ) 169local prototype =util . get_prototype ( id ) 170local style ="rb_list_box_item" 171if prototype . hidden_in_factoriopedia then 172style ="rb_list_box_item_hidden" 173elseif not researched . is ( prototype , force_index ) then 174style ="rb_list_box_item_unresearched" 175end 176 177return holder . add ( { 178type ="sprite-button" , 179style =style , 180sprite =id . type .. "/" .. id . name , 181caption ={ "" , " " , gui_util . format_caption ( id ) } , 182tooltip =gui_util . format_tooltip ( id ), 183elem_tooltip ={ type =id . type , name =id . name } , 184tags =flib_gui . format_handlers ( { [ defines . events . on_gui_click ] =info_pane . on_result_clicked } ), 185} ) 186end 187 188--- @param id DatabaseID 189--- @param holder LuaGuiElement 190--- @return LuaGuiElement 191local function make_slot_button ( id , holder ) 192local prototype =util . get_prototype ( id ) 193local style ="flib_slot_button_default" 194if prototype . hidden_in_factoriopedia then 195style ="flib_slot_button_grey" 196elseif not researched . is ( prototype , force_index ) then 197style ="flib_slot_button_red" 198end 199 200local button_holder =holder . add ( { type ="empty-widget" , style ="rb_slot_button_holder" } ) 201 202local bottom_remark , top_remark =gui_util . format_slot_button_remarks ( id ) 203 204local button =button_holder . add ( { 205type ="sprite-button" , 206style =style , 207sprite =id . type .. "/" .. id . name , 208tooltip ={ "" , { "gui.rb-control-hint" } , "\n" , { "gui.rb-pipette-control-hint" } } , 209elem_tooltip ={ type =id . type , name =id . name } , 210tags =flib_gui . format_handlers ( { [ defines . events . on_gui_click ] =info_pane . on_result_clicked } ), 211} ) 212 213if bottom_remark then 214button_holder . add ( { 215type ="label" , 216style ="rb_slot_remark_label" , 217caption =bottom_remark , 218ignored_by_interaction =true , 219} ) 220end 221if top_remark then 222button_holder . add ( { 223type ="label" , 224style ="rb_slot_remark_label_top" , 225caption =top_remark , 226ignored_by_interaction =true , 227} ) 228end 229 230return button 231end 232 233local content_pane =self . content_pane 234content_pane . clear () 235content_pane . scroll_to_top () 236if content_pane . welcome_label then 237content_pane . welcome_label . destroy () 238end 239 240local general_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 241if self . context . player . mod_settings [ "rb-show-mod-owners" ]. value then 242general_desc : add_prototype_history ( prototype ) 243end 244general_desc : add_description ( prototype ) 245if recipe then 246general_desc : add_recipe_properties ( recipe ) 247end 248if prototype . object_name == "LuaItemPrototype" then 249general_desc : add_item_properties ( prototype ) 250end 251if prototype . object_name == "LuaFluidPrototype" then 252general_desc : add_fluid_properties ( prototype ) 253end 254if entity then 255general_desc : add_entity_properties ( entity ) 256end 257if item then 258local place_result =item . place_result 259if place_result and place_result ~= grouped_entity then 260general_desc : add_id_row ( 261{ "description.rb-place-result" } , 262{ type ="entity" , name =place_result . name } , 263force_index 264) 265end 266end 267general_desc : finalize () 268 269if prototype . object_name == "LuaItemPrototype" then 270local spoil_ticks =prototype . get_spoil_ticks () 271if spoil_ticks > 0 then 272local spoil_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 273spoil_desc : add_category_header ( "tooltip-category-spoilable" , { "tooltip-category.spoilable" } ) 274 275spoil_desc : add_generic_row ( { "description.spoil-time" } , gui_util . format_spoil_time ( spoil_ticks )) 276local spoil_result =prototype . spoil_result 277if spoil_result then 278spoil_desc : add_id_row ( { "description.spoil-result" } , { type ="item" , name =spoil_result . name } , force_index ) 279end 280-- TODO: Trigger spoil effect garbage 281end 282end 283 284if entity then 285local consumption_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 286local consumption_id =collectors . material_consumption ( entity ) 287if consumption_id then 288consumption_desc : add_consumption ( consumption_id ) 289end 290consumption_desc : finalize () 291 292local power_consumption_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 293local power_consumption =collectors . power_consumption ( entity ) 294if power_consumption then 295power_consumption_desc : add_category_header ( 296"tooltip-category-electricity" , 297{ "" , { "tooltip-category.consumes" } , " " , { "tooltip-category.electricity" } } 298) 299if power_consumption . min == power_consumption . max then 300power_consumption_desc : add_generic_row ( 301{ "description.energy-consumption" } , 302gui_util . format_power ( power_consumption . max ) 303) 304else 305power_consumption_desc : add_generic_row ( 306{ "description.max-energy-consumption" } , 307gui_util . format_power ( power_consumption . max ) 308) 309power_consumption_desc : add_generic_row ( 310{ "description.min-energy-consumption" } , 311gui_util . format_power ( power_consumption . min ) 312) 313end 314end 315power_consumption_desc : finalize () 316 317local power_production_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 318local power_production =collectors . power_production ( entity ) 319if power_production then 320power_production_desc : add_category_header ( 321"tooltip-category-electricity" , 322{ "" , { "tooltip-category.generates" } , " " , { "tooltip-category.electricity" } } 323) 324power_production_desc : add_generic_row ( 325{ "description.maximum-power-output" } , 326gui_util . format_power ( power_production ) 327) 328end 329power_production_desc : finalize () 330 331local production_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 332local production_id =collectors . material_production ( entity ) 333if production_id then 334production_desc : add_production ( production_id ) 335end 336production_desc : finalize () 337 338local vehicle_desc =info_description . new ( content_pane , self . context , info_pane . on_result_clicked ) 339vehicle_desc : add_vehicle_properties ( entity ) 340vehicle_desc : finalize () 341end 342 343local lists_everywhere =self . context . player . mod_settings [ "rb-lists-everywhere" ]. value --[[@as boolean]] 344local grid_builder =lists_everywhere and make_list_box_item or make_slot_button 345local grid_column_count =lists_everywhere and 1 or 10 346 347if recipe then 348info_section . build ( 349content_pane , 350self . context , 351{ "factoriopedia.ingredients" } , 352collectors . ingredients ( recipe ), 353{ always_show =true , remark =gui_util . format_crafting_time ( recipe . energy ) } , 354make_list_box_item 355) 356info_section . build ( 357content_pane , 358self . context , 359{ "factoriopedia.products" } , 360collectors . products ( recipe ), 361{ always_show =true } , 362make_list_box_item 363) 364info_section . build ( 365content_pane , 366self . context , 367{ "factoriopedia.made-in" } , 368collectors . made_in ( recipe ), 369{ column_count =grid_column_count } , 370--- @param id DatabaseID 371--- @param holder LuaGuiElement 372--- @return LuaGuiElement 373function ( id , holder ) 374local elem =grid_builder ( id , holder ) 375local id_prototype =util . get_prototype ( id ) 376if id_prototype . type ~= "character" then 377elem . tooltip ={ "" , { "gui.rb-control-hint" } , "\n" , { "gui.rb-crafter-blueprint-control-hint" } } 378gui_util . add_tag ( elem , "rb_context_id" , prototype_id ) 379end 380return elem 381end 382) 383end 384 385if prototype . object_name == "LuaFluidPrototype" or prototype . object_name == "LuaItemPrototype" then 386info_section . build ( 387content_pane , 388self . context , 389{ "factoriopedia.gathered-from" } , 390collectors . gathered_from ( prototype ), 391{ column_count =grid_column_count } , 392grid_builder 393) 394info_section . build ( 395content_pane , 396self . context , 397grouped_recipe and { "factoriopedia.alternative-recipes" } or { "description.rb-recipes" } , 398collectors . alternative_recipes ( prototype , grouped_recipe ), 399{ column_count =grid_column_count } , 400grid_builder 401) 402info_section . build ( 403content_pane , 404self . context , 405{ "factoriopedia.used-in" } , 406collectors . used_in ( prototype , grouped_recipe ), 407{ column_count =grid_column_count } , 408grid_builder 409) 410end 411 412if prototype . object_name == "LuaItemPrototype" then 413info_section . build ( 414content_pane , 415self . context , 416{ "factoriopedia.spoilage-from" } , 417collectors . spoilage_from ( prototype ), 418{ column_count =grid_column_count } , 419grid_builder 420) 421info_section . build ( 422content_pane , 423self . context , 424{ "description.rocket-launch-products" } , 425collectors . rocket_launch_products ( prototype ), 426{ column_count =grid_column_count } , 427grid_builder 428) 429info_section . build ( 430content_pane , 431self . context , 432{ "description.rb-rocket-launch-product-of" } , 433collectors . rocket_launch_product_of ( prototype ), 434{ column_count =grid_column_count } , 435grid_builder 436) 437end 438 439if prototype . object_name == "LuaFluidPrototype" then 440info_section . build ( 441content_pane , 442self . context , 443{ "factoriopedia.generated-by" } , 444collectors . generated_by ( prototype ), 445{ column_count =grid_column_count } , 446grid_builder 447) 448info_section . build ( 449content_pane , 450self . context , 451{ "description.rb-mining-fluid-for" } , 452collectors . mining_fluid_for ( prototype ), 453{ column_count =grid_column_count } , 454grid_builder 455) 456end 457 458if prototype . object_name == "LuaFluidPrototype" or prototype . object_name == "LuaItemPrototype" then 459info_section . build ( 460content_pane , 461self . context , 462{ "factoriopedia.burned-in" } , 463collectors . burned_in ( prototype ), 464{ column_count =grid_column_count } , 465grid_builder 466) 467end 468 469if entity then 470info_section . build ( 471content_pane , 472self . context , 473{ "description.rb-place-result-of" } , 474collectors . place_result_of ( entity , prototype . object_name == "LuaItemPrototype" and prototype or nil ), 475{ column_count =grid_column_count } , 476grid_builder 477) 478info_section . build ( 479content_pane , 480self . context , 481{ "description.mined-by" } , 482collectors . mined_by ( entity ), 483{ column_count =grid_column_count } , 484grid_builder 485) 486info_section . build ( 487content_pane , 488self . context , 489{ "factoriopedia.can-mine" } , 490collectors . can_mine ( entity ), 491{ column_count =grid_column_count } , 492grid_builder 493) 494info_section . build ( 495content_pane , 496self . context , 497{ "description.rb-can-burn" } , 498collectors . can_burn ( entity ), 499{ column_count =grid_column_count } , 500grid_builder 501) 502local item =prototype . object_name == "LuaItemPrototype" and prototype --[[@as LuaItemPrototype]] 503or nil 504info_section . build ( 505content_pane , 506self . context , 507{ "factoriopedia.yields" } , 508collectors . yields ( entity , item ), 509{} , 510make_list_box_item 511) 512end 513 514if recipe then 515info_section . build ( 516content_pane , 517self . context , 518{ "factoriopedia.unlocked-by" } , 519collectors . unlocked_by ( recipe ), 520{ style ="rb_technology_slot_deep_frame" , column_count =5 , always_show =true } , 521--- @param id DatabaseID 522--- @param holder LuaGuiElement 523function ( id , holder ) 524local id_prototype =util . get_prototype ( id ) 525if id_prototype . hidden_in_factoriopedia and not self . context . show_hidden then 526return 527end 528local research_state 529if not researched . is ( id_prototype , force_index ) then 530research_state =flib_technology . research_state . not_available 531else 532research_state =flib_technology . research_state . researched 533end 534local technology =self . context . player . force . technologies [ id . name ] 535local button =flib_gui_templates . technology_slot ( 536holder , 537technology , 538technology . level , 539research_state , 540info_pane . on_result_clicked 541) 542-- button.tooltip = { "", { "gui.rb-control-hint" }, "\n", { "gui.rb-technology-control-hint" } } 543button . tooltip ={ "gui.rb-control-hint" } 544 545return button 546end 547) 548end 549 550if entity then 551info_section . build ( 552content_pane , 553self . context , 554{ "factoriopedia.can-craft" } , 555collectors . can_craft ( entity ), 556{ column_count =grid_column_count } , 557grid_builder 558) 559info_section . build ( 560content_pane , 561self . context , 562{ "factoriopedia.can-extract-from" } , 563collectors . can_extract_from ( entity ), 564{ column_count =grid_column_count } , 565grid_builder 566) 567end 568 569local tile =prototype . object_name == "LuaTilePrototype" and prototype --[[@as LuaTilePrototype]] 570or ( self . context . grouping_mode ~= "none" and grouped . tile [ prototype_path ] or nil ) 571if prototype . object_name == "LuaTilePrototype" then 572info_section . build ( 573content_pane , 574self . context , 575{ "factoriopedia.source-of" } , 576collectors . source_of ( tile ), 577{ column_count =grid_column_count } , 578grid_builder 579) 580info_section . build ( 581content_pane , 582self . context , 583{ "factoriopedia.extracted-by" } , 584collectors . extracted_by ( tile ), 585{ column_count =grid_column_count } , 586grid_builder 587) 588end 589 590profiler . stop () 591log ( { "" , "[" , util . get_path ( prototype ), "] " , profiler } ) 592 593return true 594end 595 596return info_pane