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