z_mirror/Krastorio2
git clone https://git.raiguard.me/z_mirror/Krastorio2
c13e3614
trunk
1local flib_gui =require ( "__flib__.gui" ) 2local flib_math =require ( "__flib__.math" ) 3local flib_table =require ( "__flib__.table" ) 4 5local util =require ( "scripts.util" ) 6 7-- the full energy buffer on normal quality 8local energy_per_teleport = 9prototypes . entity [ "kr-planetary-teleporter" ]. electric_energy_source_prototype . buffer_capacity 10 11--- @class PlanetaryTeleporterGui 12--- @field elems table<string, LuaGuiElement> 13--- @field entity LuaEntity 14--- @field data PlanetaryTeleporterData 15--- @field player LuaPlayer 16--- @field search_open boolean 17--- @field search_query string 18 19--- @param player LuaPlayer 20--- @param from LuaEntity 21--- @param to LuaEntity 22local function teleport_player ( player , from , to ) 23from . energy =from . energy - energy_per_teleport 24to . energy =to . energy - energy_per_teleport 25local position =to . position 26position . y =position . y + 1.1 27player . teleport ( position , to . surface ) 28from . surface . play_sound ( { 29path ="kr-planetary-teleporter-effect-sound" , 30volume_modifier =0.8 , 31position =from . position , 32} ) 33to . surface . play_sound ( { 34path ="kr-planetary-teleporter-effect-sound" , 35position =to . position , 36volume_modifier =1 , 37} ) 38end 39 40--- @param self PlanetaryTeleporterGui 41local function filter_destinations ( self ) 42local search_query =self . search_query 43for _ , frame in pairs ( self . elems . destinations_table . children ) do 44if search_query == "" then 45frame . visible =true 46goto continue 47end 48local unit_number =tonumber ( frame . name ) --[[@as uint]] 49local data =storage . planetary_teleporter [ unit_number ] 50if not data then 51frame . destroy () 52goto continue 53end 54if data . name and string . find ( string . lower ( data . name ), search_query , nil , true ) then 55frame . visible =true 56else 57frame . visible =false 58end 59:: continue :: 60end 61end 62 63--- @param self PlanetaryTeleporterGui 64--- @param by_click boolean 65local function toggle_search ( self , by_click ) 66local search_button =self . elems . search_button 67if not by_click then 68search_button . toggled =not search_button . toggled 69end 70self . search_open =search_button . toggled 71 72local search_textfield =self . elems . search_textfield 73search_textfield . visible =self . search_open 74if self . search_open then 75search_textfield . focus () 76search_textfield . select_all () 77else 78self . search_query ="" 79end 80 81filter_destinations ( self ) 82end 83 84--- @param self PlanetaryTeleporterGui 85local function destroy_gui ( self ) 86local window =self . elems . kr_planetary_teleporter_window 87if window . valid then 88window . destroy () 89if self . player . opened == window then 90self . player . opened =nil 91end 92end 93storage . planetary_teleporter_gui [ self . player . index ] =nil 94end 95 96--- @param self PlanetaryTeleporterGui 97local function update_name_label ( self ) 98self . elems . name_label . caption =self . data . name or { "gui.kr-planetary-teleporter-unnamed" } 99end 100 101--- @param e EventData.on_gui_closed|EventData.on_gui_click 102local function on_window_closed ( e ) 103local self =storage . planetary_teleporter_gui [ e . player_index ] 104if not self then 105return 106end 107if self . search_open then 108toggle_search ( self , false ) 109self . player . opened =self . elems . kr_planetary_teleporter_window 110return 111end 112destroy_gui ( self ) 113end 114 115--- @param e EventData.on_gui_text_changed 116local function on_search_textfield_text_changed ( e ) 117local self =storage . planetary_teleporter_gui [ e . player_index ] 118if not self then 119return 120end 121self . search_query =e . element . text 122filter_destinations ( self ) 123end 124 125--- @param e EventData.on_gui_click|EventData.CustomInputEvent 126local function on_search_button_clicked ( e ) 127local self =storage . planetary_teleporter_gui [ e . player_index ] 128if not self then 129return 130end 131toggle_search ( self , true ) 132end 133 134--- @param e EventData.on_gui_click 135local function on_rename_button_clicked ( e ) 136local self =storage . planetary_teleporter_gui [ e . player_index ] 137if not self or not self . elems . kr_planetary_teleporter_window . valid then 138return 139end 140local textfield =self . elems . name_textfield 141local label =self . elems . name_label 142if textfield . visible then 143local text =textfield . text 144if # text > 0 then 145self . data . name =text 146else 147self . data . name =nil 148end 149update_name_label ( self ) 150label . visible =true 151textfield . visible =false 152else 153label . visible =false 154textfield . text =self . data . name or "" 155textfield . visible =true 156textfield . select_all () 157textfield . focus () 158end 159end 160 161--- @param e EventData.on_gui_confirmed 162local function on_name_textfield_confirmed ( e ) 163-- Just pass through the logic because it's all the same anyway. 164on_rename_button_clicked ( e --[[@as EventData.on_gui_click]] ) 165end 166 167--- @param e EventData.on_gui_click 168local function on_teleport_button_clicked ( e ) 169local self =storage . planetary_teleporter_gui [ e . player_index ] 170if not self or not self . elems . kr_planetary_teleporter_window . valid then 171return 172end 173local target_unit_number =e . element . tags . unit_number --[[@as UnitNumber]] 174assert ( target_unit_number , "target_unit_number is nil" ) 175local target_data =storage . planetary_teleporter [ target_unit_number ] 176assert ( target_data , "target_data is nil" ) 177local players =storage . planetary_teleporter_players [ 178target_data . entities . turret . unit_number --[[@as uint]] 179] 180if players then 181util . flying_text_with_sound ( self . player , { "gui.kr-planetary-teleporter-destination-blocked" } ) 182return 183end 184local source_unit_number =self . entity . unit_number 185assert ( source_unit_number , "source_unit_number is nil" ) 186local source_data =storage . planetary_teleporter [ source_unit_number ] 187teleport_player ( self . player , source_data . entities . base , target_data . entities . base ) 188destroy_gui ( self ) 189end 190 191--- @type table<defines.entity_status, { sprite: SpritePath, label: LocalisedString }> 192local status_sprites ={ 193[ defines . entity_status . charging ] ={ label ={ "entity-status.charging" } , sprite ="utility/status_yellow" } , 194[ defines . entity_status . fully_charged ] ={ 195label ={ "entity-status.fully-charged" } , 196sprite ="utility/status_working" , 197} , 198[ defines . entity_status . no_power ] ={ label ={ "entity-status.no-power" } , sprite ="utility/status_not_working" } , 199[ defines . entity_status . normal ] ={ label ={ "entity-status.normal" } , sprite ="utility/status_working" } , 200[ defines . entity_status . marked_for_deconstruction ] ={ 201label ={ "entity-status.marked-for-deconstruction" } , 202sprite ="utility/status_not_working" , 203} , 204} 205 206--- @param self PlanetaryTeleporterGui 207local function update_gui ( self ) 208if not self . entity . valid then 209destroy_gui ( self ) 210return 211end 212local buffer_capacity =self . entity . prototype . electric_energy_source_prototype . buffer_capacity 213local status =self . entity . status 214local status_properties =status_sprites [ status ] or {} 215self . elems . status_icon . sprite =status_properties . sprite 216self . elems . status_label . caption =status_properties . label 217 218local charge =self . entity . energy / buffer_capacity 219self . elems . charge_progressbar . value =charge 220self . elems . charge_progressbar . caption ={ "format-percent" , flib_math . round ( charge * 100 ) } 221 222local players =storage . planetary_teleporter_players [ self . data . entities . turret . unit_number ] 223local standing_on_teleporter =players and flib_table . find ( players , self . player . index ) or false 224local too_many_players =players and # players > 1 or false 225local source_fully_charged =flib_math . round ( charge , 0.1 ) >= 1 226local fully_operational =standing_on_teleporter and source_fully_charged and not too_many_players 227 228if fully_operational then 229self . elems . destinations_status_icon . sprite ="flib_indicator_green" 230self . elems . destinations_status_label . caption ={ "gui.kr-planetary-teleporter-ready" } 231else 232self . elems . destinations_status_icon . sprite ="flib_indicator_red" 233if not standing_on_teleporter then 234self . elems . destinations_status_label . caption ={ "gui.kr-planetary-teleporter-not-on-teleporter" } 235elseif too_many_players then 236self . elems . destinations_status_label . caption ={ "gui.kr-planetary-teleporter-too-many-players" } 237else 238self . elems . destinations_status_label . caption ={ "gui.kr-planetary-teleporter-not-fully-charged" } 239end 240end 241 242update_name_label ( self ) 243 244for _ , frame in pairs ( self . elems . destinations_table . children ) do 245local unit_number =tonumber ( frame . name ) --[[@as uint]] 246local dest_data =storage . planetary_teleporter [ unit_number ] 247if not dest_data then 248goto continue 249end 250local dest_entity =dest_data . entities . base 251local dest_charge =dest_entity . energy / buffer_capacity 252local dest_fully_charged =flib_math . round ( dest_charge , 0.1 ) >= 1 253local minimap =frame . minimap_frame . minimap --[[@as LuaGuiElement]] 254minimap . progressbar . value =dest_charge 255minimap . progressbar . visible =not dest_fully_charged 256minimap . minimap_button . enabled =fully_operational and dest_fully_charged 257if not dest_fully_charged then 258minimap . minimap_button . tooltip ={ "format-percent" , flib_math . round ( dest_charge * 100 ) } 259elseif fully_operational then 260minimap . minimap_button . tooltip ={ "gui.kr-planetary-teleporter-teleport" } 261else 262minimap . minimap_button . tooltip =nil 263end 264:: continue :: 265end 266end 267 268local function on_tick () 269for _ , gui in pairs ( storage . planetary_teleporter_gui ) do 270update_gui ( gui ) 271end 272end 273 274--- @param e EventData.on_gui_opened 275local function on_gui_opened ( e ) 276local entity =e . entity 277if not entity or not entity . valid or entity . name ~= "kr-planetary-teleporter" then 278return 279end 280 281local player =game . get_player ( e . player_index ) 282if not player then 283return 284end 285 286local destination_buttons ={} 287for unit_number , data in pairs ( storage . planetary_teleporter ) do 288if unit_number == entity . unit_number or data . force ~= entity . force or data . surface . valid ~= true then 289goto continue 290end 291--Limit to same surface if space age is not enabled 292if not script . active_mods [ "space-age" ] and data . surface ~= entity . surface then 293goto continue 294end 295destination_buttons [ # destination_buttons + 1 ] ={ 296type ="frame" , 297name =unit_number , 298style ="train_with_minimap_frame" , 299direction ="vertical" , 300{ 301type ="frame" , 302name ="minimap_frame" , 303style ="deep_frame_in_shallow_frame" , 304{ 305type ="minimap" , 306name ="minimap" , 307style ="kr_planetary_teleporter_destination_minimap" , 308position =data . entities . base . position , 309surface_index =data . surface . index , 310{ 311type ="button" , 312name ="minimap_button" , 313style ="kr_planetary_teleporter_destination_minimap_button" , 314tags ={ unit_number =unit_number } , 315handler ={ [ defines . events . on_gui_click ] =on_teleport_button_clicked } , 316} , 317{ 318type ="progressbar" , 319name ="progressbar" , 320style ="kr_planetary_teleporter_destination_charge_bar" , 321ignored_by_interaction =true , 322} , 323{ 324type ="label" , 325style ="kr_planetary_teleporter_destination_label" , 326style_mods ={ top_margin =8 , right_margin =8 } , 327caption =data . name or { "gui.kr-planetary-teleporter-unnamed" } , 328ignored_by_interaction =true , 329} , 330} , 331} , 332} 333:: continue :: 334end 335 336local elems =flib_gui . add ( player . gui . screen , { 337type ="frame" , 338name ="kr_planetary_teleporter_window" , 339direction ="vertical" , 340elem_mods ={ auto_center =true } , 341handler ={ [ defines . events . on_gui_closed ] =on_window_closed } , 342{ 343type ="flow" , 344style ="flib_titlebar_flow" , 345drag_target ="kr_planetary_teleporter_window" , 346{ 347type ="label" , 348style ="flib_frame_title" , 349caption ={ "entity-name.kr-planetary-teleporter" } , 350ignored_by_interaction =true , 351} , 352{ type ="empty-widget" , style ="flib_titlebar_drag_handle" , ignored_by_interaction =true } , 353{ 354type ="textfield" , 355name ="search_textfield" , 356style ="flib_titlebar_search_textfield" , 357visible =false , 358handler ={ [ defines . events . on_gui_text_changed ] =on_search_textfield_text_changed } , 359} , 360{ 361type ="sprite-button" , 362name ="search_button" , 363style ="frame_action_button" , 364sprite ="utility/search" , 365tooltip ={ "gui.flib-search-instruction" } , 366auto_toggle =true , 367handler ={ [ defines . events . on_gui_click ] =on_search_button_clicked } , 368} , 369{ 370type ="sprite-button" , 371style ="frame_action_button" , 372sprite ="utility/close" , 373tooltip ={ "gui.close-instruction" } , 374handler ={ [ defines . events . on_gui_click ] =on_window_closed } , 375} , 376} , 377{ 378type ="flow" , 379style_mods ={ horizontal_spacing =12 } , 380{ 381type ="frame" , 382style ="inside_shallow_frame" , 383direction ="vertical" , 384{ 385type ="frame" , 386style ="subheader_frame" , 387style_mods ={ width =424 } , 388{ 389type ="label" , 390name ="name_label" , 391style ="subheader_caption_label" , 392style_mods ={ horizontally_squashable =true } , 393} , 394{ 395type ="textfield" , 396name ="name_textfield" , 397visible =false , 398handler ={ [ defines . events . on_gui_confirmed ] =on_name_textfield_confirmed } , 399} , 400{ 401type ="sprite-button" , 402style ="mini_button_aligned_to_text_vertically_when_centered" , 403sprite ="utility/rename_icon" , 404handler ={ [ defines . events . on_gui_click ] =on_rename_button_clicked } , 405} , 406} , 407{ 408type ="flow" , 409style_mods ={ padding =12 , top_padding =8 , vertical_spacing =8 } , 410direction ="vertical" , 411{ 412type ="flow" , 413style_mods ={ vertical_align ="center" } , 414{ type ="sprite" , name ="status_icon" , style ="flib_indicator" } , 415{ type ="label" , name ="status_label" } , 416} , 417{ 418type ="frame" , 419style ="deep_frame_in_shallow_frame" , 420{ type ="entity-preview" , style ="wide_entity_button" , elem_mods ={ entity =entity } } , 421} , 422{ 423type ="progressbar" , 424name ="charge_progressbar" , 425style ="production_progressbar" , 426style_mods ={ horizontally_stretchable =true } , 427caption ="foo" , 428value =0.2 , 429} , 430{ type ="empty-widget" , style ="entity_frame_filler" } , 431} , 432} , 433{ 434type ="frame" , 435style ="inside_deep_frame" , 436direction ="vertical" , 437{ 438type ="frame" , 439style ="subheader_frame" , 440{ 441type ="label" , 442style ="subheader_caption_label" , 443caption ={ "gui.kr-planetary-teleporter-destinations" } , 444} , 445{ type ="empty-widget" , style ="flib_horizontal_pusher" } , 446{ 447type ="flow" , 448name ="destinations_status_flow" , 449style_mods ={ vertical_align ="center" , right_margin =8 } , 450{ 451type ="sprite" , 452name ="destinations_status_icon" , 453style ="flib_indicator" , 454sprite ="flib_indicator_red" , 455} , 456{ 457type ="label" , 458name ="destinations_status_label" , 459caption ={ "gui.kr-planetary-teleporter-not-on-teleporter" } , 460} , 461} , 462} , 463{ 464type ="scroll-pane" , 465style ="kr_planetary_teleporter_destinations_scroll_pane" , 466vertical_scroll_policy ="auto-and-reserve-space" , 467{ 468type ="table" , 469name ="destinations_table" , 470style ="slot_table" , 471column_count =3 , 472children =destination_buttons , 473} , 474} , 475} , 476} , 477} ) 478 479player . opened =elems . kr_planetary_teleporter_window 480 481--- @type PlanetaryTeleporterGui 482storage . planetary_teleporter_gui [ e . player_index ] ={ 483elems =elems , 484data =storage . planetary_teleporter [ entity . unit_number ], 485entity =entity , 486player =player , 487search_open =false , 488search_query ="" , 489} 490end 491 492--- @param e EventData.CustomInputEvent 493local function on_focus_search ( e ) 494local self =storage . planetary_teleporter_gui [ e . player_index ] 495if self then 496toggle_search ( self , false ) 497end 498end 499 500--- @class PlanetaryTeleporterGuiHandler : event_handler 501local planetary_teleporter_gui ={} 502 503function planetary_teleporter_gui . on_init () 504--- @type table<uint, PlanetaryTeleporterGui> 505storage . planetary_teleporter_gui ={} 506end 507 508function planetary_teleporter_gui . on_configuration_changed () 509for _ , gui in pairs ( storage . planetary_teleporter_gui or {} ) do 510if gui . elems . window . valid then 511gui . elems . window . destroy () 512end 513end 514storage . planetary_teleporter_gui ={} 515end 516 517planetary_teleporter_gui . events ={ 518[ defines . events . on_gui_opened ] =on_gui_opened , 519[ defines . events . on_tick ] =on_tick , 520[ "kr-linked-focus-search" ] =on_focus_search , 521} 522 523flib_gui . add_handlers ( { 524planetary_teleporter_on_name_textfield_confirmed =on_name_textfield_confirmed , 525planetary_teleporter_on_rename_button_clicked =on_rename_button_clicked , 526planetary_teleporter_on_search_button_clicked =on_search_button_clicked , 527planetary_teleporter_on_search_textfield_text_changed =on_search_textfield_text_changed , 528planetary_teleporter_on_teleport_button_clicked =on_teleport_button_clicked , 529planetary_teleporter_on_window_closed =on_window_closed , 530} ) 531 532return planetary_teleporter_gui