z_mirror/Krastorio2
git clone https://git.raiguard.me/z_mirror/Krastorio2
2d58b091
discrete-steel-pipes
1local flib_gui =require ( "__flib__.gui" ) 2local flib_math =require ( "__flib__.math" ) 3local flib_position =require ( "__flib__.position" ) 4 5local util =require ( "scripts.util" ) 6 7--- @alias IntergalacticTransceiverStatus 8--- | "charging" 9--- | "discharging" 10--- | "not_enough_input" 11--- | "ready" 12 13local defines_to_status ={ 14[ defines . entity_status . charging ] ="charging" , 15[ defines . entity_status . discharging ] ="discharging" , 16} 17 18--- @type table<IntergalacticTransceiverStatus, { label: LocalisedString, sprite: SpritePath }> 19local status_properties ={ 20charging ={ 21label ={ "entity-status.charging" } , 22icon ="utility/status_working" , 23} , 24discharging ={ 25alert_label ={ "gui.kr-intergalactic-transceiver-alert-discharging" } , 26label ={ "entity-status.discharging" } , 27icon ="utility/status_not_working" , 28} , 29not_enough_input ={ 30alert_label ={ "gui.kr-intergalactic-transceiver-alert-not-enough-input" } , 31label ={ "entity-status.kr-not-enough-input" } , 32icon ="utility/status_not_working" , 33tooltip ={ "entity-status-tooltip.kr-not-enough-input" } , 34} , 35no_power ={ 36alert_label ={ "gui.kr-intergalactic-transceiver-alert-no-power" } , 37label ={ "entity-status.no-power" } , 38icon ="utility/status_not_working" , 39} , 40ready ={ 41alert_label ={ "gui.kr-intergalactic-transceiver-alert-ready" } , 42label ={ "entity-status.kr-ready" } , 43icon ="utility/status_working" , 44} , 45} 46 47local min_charge_delta =2.5e8 -- Requires charge rate of 15 GW 48local max_charge_delta =2e9 49local max_energy =prototypes . entity [ "kr-intergalactic-transceiver" ]. electric_energy_source_prototype . buffer_capacity 50- max_charge_delta 51local energy_drain =50e9 52 53--- @param transceiver_data IntergalacticTransceiverForceData 54local function activate_transceiver ( transceiver_data ) 55local entity =transceiver_data . entity 56local entity_surface =entity . surface 57local entity_position =entity . position 58for _ , player in pairs ( entity . force . connected_players ) do 59if player . surface == entity_surface and flib_position . distance ( entity_position , player . position ) <= 100 then 60if player . controller_type == defines . controllers . character then 61player . character . destructible =false 62end 63player . set_controller ( { 64type =defines . controllers . cutscene , 65waypoints ={ 66{ 67position =entity . position , 68zoom =1.2 , 69transition_time =70 , 70time_to_wait =30 , 71} , 72{ 73position =entity . position , 74zoom =1.2 , 75transition_time =0 , 76time_to_wait =15 , 77} , 78{ 79position =entity . position , 80zoom =0.8 , 81transition_time =130 , 82time_to_wait =10 , 83} , 84{ 85position =entity . position , 86zoom =1.2 , 87transition_time =200 , 88time_to_wait =60 , 89} , 90} , 91final_transition_time =60 , 92} ) 93end 94end 95storage . intergalactic_transceiver . in_cutscene [ entity . force_index ] =true 96end 97 98--- @param e EventData.on_cutscene_finished 99local function on_cutscene_finished ( e ) 100local player =game . get_player ( e . player_index ) 101if not player then 102return 103end 104if not storage . intergalactic_transceiver or not storage . intergalactic_transceiver . in_cutscene [ player . force_index ] then 105return 106end 107storage . intergalactic_transceiver . in_cutscene [ player . force_index ] =nil 108player . force . technologies [ "kr-logo" ]. enabled =true 109if player . character then 110player . character . destructible =true 111end 112if not storage . intergalactic_transceiver . is_victory then 113return 114end 115 116game . set_win_ending_info ( { 117title ={ "gui.kr-victory-title" } , 118message ={ "gui.kr-victory-message" } , 119bullet_points ={ 120{ "victory-bullet-point-1" } , 121{ "victory-bullet-point-2" } , 122{ "victory-bullet-point-3" } , 123{ "victory-bullet-point-4" } , 124} , 125final_message ={ "gui.kr-victory-final-message" } , 126} ) 127game . set_game_state ( { 128can_continue =true , 129game_finished =true , 130victorious_force =player . force , 131player_won =true , 132} ) 133player . force . play_sound ( { path ="kr-win-joke-voice" } ) 134end 135 136--- @param e EventData.on_cutscene_waypoint_reached 137local function on_cutscene_waypoint_reached ( e ) 138if not storage . intergalactic_transceiver then 139return 140end 141local player =game . get_player ( e . player_index ) 142if not player or not storage . intergalactic_transceiver . in_cutscene [ player . force_index ] then 143return 144end 145 146local transceiver_data =storage . intergalactic_transceiver . forces [ player . force_index ] 147if not transceiver_data then 148return 149end 150local entity =transceiver_data . entity 151if not entity . valid then 152return 153end 154 155if e . waypoint_index == 1 then 156entity . surface . create_entity ( { 157type ="projectile" , 158name ="kr-intergalactic-transceiver-wave" , 159force =entity . force , 160position =entity . position , 161speed =0 , 162max_range =100 , 163target =entity , 164create_build_effect_smoke =false , 165raise_built =true , 166} ) 167elseif e . waypoint_index == 2 then 168local force =entity . force 169local player =entity . last_user 170local position =entity . position 171local surface =entity . surface 172entity . destroy () 173transceiver_data . activating =true 174local new_entity =surface . create_entity ( { 175name ="kr-activated-intergalactic-transceiver" , 176position =position , 177force =force , 178player =player , 179create_build_effect_smoke =false , 180raise_built =true , 181} ) 182transceiver_data . activating =false 183if not new_entity or not new_entity . valid then 184return 185end 186storage . intergalactic_transceiver . forces [ force . index ] ={ entity =new_entity } 187end 188end 189 190--- @param e EventData.on_gui_click 191local function on_gui_window_closed ( e ) 192local self =storage . intergalactic_transceiver . guis [ e . player_index ] 193if not self then 194return 195end 196self . elems . kr_intergalactic_transceiver_window . destroy () 197self . player . opened =nil 198self . player . play_sound ( { path ="entity-close/kr-intergalactic-transceiver" } ) 199storage . intergalactic_transceiver . guis [ e . player_index ] =nil 200end 201 202--- @param entity LuaEntity 203local function close_open_guis ( entity ) 204for player_index , self in pairs ( storage . intergalactic_transceiver . guis ) do 205if self . entity == entity or not self . entity . valid then 206on_gui_window_closed ( { player_index =player_index } ) 207end 208end 209end 210 211--- @param e EventData.on_gui_click 212local function on_gui_activate_button_clicked ( e ) 213local player =game . get_player ( e . player_index ) 214if not player then 215return 216end 217local transceiver_data =storage . intergalactic_transceiver . forces [ player . force_index ] 218close_open_guis ( transceiver_data . entity ) 219activate_transceiver ( transceiver_data ) 220end 221 222--- @param self IntergalacticTransceiverGuiData 223local function update_gui ( self ) 224local charged =flib_math . round ( self . entity . energy / max_energy , 0.01 ) 225local transceiver_data =storage . intergalactic_transceiver . forces [ self . player . force_index ] 226local status_properties =status_properties [ transceiver_data . status ] 227self . elems . status_icon . sprite =status_properties . icon 228self . elems . status_label . caption =status_properties . label 229self . elems . charge_progressbar . value =charged 230self . elems . charge_progressbar . caption =math . floor ( charged * 100 ) .. " %" 231self . elems . activate_button . enabled =charged == 1 232end 233 234--- @param player LuaPlayer 235--- @param entity LuaEntity 236local function create_gui ( player , entity ) 237local elems =flib_gui . add ( player . gui . screen , { 238type ="frame" , 239name ="kr_intergalactic_transceiver_window" , 240direction ="vertical" , 241elem_mods ={ auto_center =true } , 242handler ={ [ defines . events . on_gui_closed ] =on_gui_window_closed } , 243{ 244type ="flow" , 245style ="flib_titlebar_flow" , 246{ 247type ="label" , 248style ="frame_title" , 249caption ={ "entity-name.kr-intergalactic-transceiver" } , 250ignored_by_interaction =true , 251} , 252{ type ="empty-widget" , style ="flib_titlebar_drag_handle" , ignored_by_interaction =true } , 253{ 254type ="sprite-button" , 255style ="frame_action_button" , 256sprite ="utility/close" , 257mouse_button_filter ={ "left" } , 258handler ={ [ defines . events . on_gui_click ] =on_gui_window_closed } , 259} , 260} , 261{ 262type ="frame" , 263style ="entity_frame" , 264direction ="vertical" , 265{ 266type ="flow" , 267style_mods ={ vertical_align ="center" } , 268{ type ="sprite" , name ="status_icon" , style ="flib_indicator" } , 269{ type ="label" , name ="status_label" } , 270} , 271{ 272type ="frame" , 273style ="deep_frame_in_shallow_frame" , 274{ type ="entity-preview" , style ="wide_entity_button" , elem_mods ={ entity =entity } } , 275} , 276{ 277type ="progressbar" , 278name ="charge_progressbar" , 279style ="production_progressbar" , 280style_mods ={ horizontally_stretchable =true } , 281value =0 , 282} , 283{ 284type ="button" , 285name ="activate_button" , 286style_mods ={ horizontally_stretchable =true } , 287caption ={ "gui.kr-intergalactic-transceiver-activate" } , 288enabled =false , 289handler ={ [ defines . events . on_gui_click ] =on_gui_activate_button_clicked } , 290} , 291} , 292} ) 293 294--- @class IntergalacticTransceiverGuiData 295storage . intergalactic_transceiver . guis [ player . index ] ={ 296elems =elems , 297entity =entity , 298player =player , 299} 300 301player . opened =elems . kr_intergalactic_transceiver_window 302if not game . is_multiplayer () then 303player . play_sound ( { path ="entity-open/kr-intergalactic-transceiver" } ) 304end 305end 306 307--- @param entity LuaEntity 308local function on_transceiver_destroyed ( entity ) 309local force_index =entity . force . index 310storage . intergalactic_transceiver . forces [ force_index ] =nil 311close_open_guis ( entity ) 312end 313 314--- @param entity LuaEntity 315local function on_inactive_transceiver_destroyed ( entity ) 316storage . intergalactic_transceiver . inactive [ entity . unit_number ] =nil 317end 318 319--- @param e EntityDestroyedEvent 320local function on_entity_destroyed ( e ) 321local entity =e . entity 322if not entity or not entity . valid then 323return 324end 325 326if entity . name == "kr-intergalactic-transceiver" then 327on_transceiver_destroyed ( entity ) 328elseif entity . name == "kr-inactive-intergalactic-transceiver" then 329on_inactive_transceiver_destroyed ( entity ) 330end 331end 332 333--- @param entity LuaEntity 334local function on_transceiver_built ( entity ) 335local existing =storage . intergalactic_transceiver . forces [ entity . force . index ] 336if existing then 337local position =entity . position 338local force =entity . force 339local player =entity . last_user 340local surface =entity . surface 341entity . destroy () 342 343local new_entity =surface . create_entity ( { 344name ="kr-inactive-intergalactic-transceiver" , 345position =position , 346force =force , 347player =player , 348create_build_effect_smoke =false , 349raise_built =true , 350} ) 351if new_entity and new_entity . valid then 352storage . intergalactic_transceiver . inactive [ new_entity . unit_number ] =new_entity 353end 354 355return 356end 357 358--- @class IntergalacticTransceiverForceData 359storage . intergalactic_transceiver . forces [ entity . force . index ] ={ 360activating =false , 361entity =entity , 362last_alert_tick =game . tick , 363last_energy =entity . energy , 364status ="empty" , 365} 366end 367 368--- @param entity LuaEntity 369local function on_active_transceiver_built ( entity ) 370local existing =storage . intergalactic_transceiver . forces [ entity . force . index ] 371if not existing or not existing . activating then 372local position =entity . position 373local force =entity . force 374local player =entity . last_user 375local surface =entity . surface 376entity . destroy () 377 378local new_entity =surface . create_entity ( { 379name ="kr-intergalactic-transceiver" , 380position =position , 381force =force , 382player =player , 383create_build_effect_smoke =false , 384raise_built =true , 385} ) 386 387if new_entity and new_entity . valid then 388on_transceiver_built ( new_entity ) 389end 390end 391end 392 393--- @param e EntityBuiltEvent 394local function on_entity_built ( e ) 395local entity =e . entity or e . destination 396if not entity or not entity . valid then 397return 398end 399 400if entity . name == "kr-intergalactic-transceiver" then 401on_transceiver_built ( entity ) 402elseif entity . name == "kr-activated-intergalactic-transceiver" then 403on_active_transceiver_built ( entity ) 404end 405end 406 407--- @param data IntergalacticTransceiverForceData 408--- @param entity LuaEntity 409local function update_transceiver ( data , entity ) 410if not data . last_energy then 411return 412end 413local current_energy =entity . energy 414local new_energy =current_energy 415local difference =current_energy - data . last_energy 416--- @type IntergalacticTransceiverStatus 417local status ="charging" 418-- If we're not receiving enough power 419if difference < min_charge_delta and current_energy > 0 then 420-- Drain the transceiver at 3 TJ / sec 421new_energy =math . max ( 0 , current_energy - energy_drain ) 422-- Update status 423if new_energy > 0 then 424status ="discharging" 425else 426status ="not_enough_input" 427end 428else 429if current_energy > max_energy then 430-- XXX: If the energy remains constant, the entity status will change to "normal", so give it a random offset. 431new_energy =max_energy + math . random ( 0 , 20 ) * 1000000 432status ="ready" 433else 434local current_status =defines_to_status [ entity . status ] 435if status_properties [ current_status ] then 436status =current_status 437end 438end 439end 440 441if new_energy ~= current_energy then 442entity . energy =new_energy 443end 444local last_status =data . status 445data . last_energy =new_energy 446data . status =status 447 448local status_properties =status_properties [ status ] 449if status_properties and status_properties . alert_label and game . tick - data . last_alert_tick >= 60 then 450data . last_alert_tick =game . tick 451for _ , player in pairs ( entity . force . players ) do 452player . add_custom_alert ( 453entity , 454{ type ="item" , name ="kr-intergalactic-transceiver" } , 455status_properties . alert_label , 456true 457) 458if status == "discharging" and last_status ~= status then 459player . play_sound ( { 460path ="kr-intergalactic-transceiver-discharging-warning" , 461} ) 462end 463end 464end 465end 466 467--- @param e EventData.on_gui_opened 468local function on_gui_opened ( e ) 469if storage . intergalactic_transceiver . guis [ e . player_index ] then 470return 471end 472if e . gui_type ~= defines . gui_type . entity then 473return 474end 475local entity =e . entity 476if not entity or entity . name ~= "kr-intergalactic-transceiver" then 477return 478end 479local player =game . get_player ( e . player_index ) 480if not player then 481return 482end 483create_gui ( player , entity ) 484end 485 486local function on_tick () 487if not storage . intergalactic_transceiver then 488return 489end 490 491local transceivers =storage . intergalactic_transceiver . forces 492for force_index , data in pairs ( transceivers ) do 493local entity =data . entity 494if entity and entity . valid then 495update_transceiver ( data , entity ) 496else 497transceivers [ force_index ] =nil 498end 499end 500 501for _ , self in pairs ( storage . intergalactic_transceiver . guis ) do 502update_gui ( self ) 503end 504end 505 506local function on_180th_tick () 507if not storage . intergalactic_transceiver then 508return 509end 510for unit_number , entity in pairs ( storage . intergalactic_transceiver . inactive ) do 511if entity . valid then 512util . entity_flying_text ( entity , { "message.kr-transceiver-is-inactive" } , { r =1 } ) 513else 514storage . intergalactic_transceiver . inactive [ unit_number ] =nil 515end 516end 517end 518 519--- @class intergalactic_transceiver 520local intergalactic_transceiver ={} 521 522function intergalactic_transceiver . on_init () 523-- Can be already initialised through remote interface 524if storage . intergalactic_transceiver then 525return 526end 527storage . intergalactic_transceiver ={ 528--- @type table<uint, IntergalacticTransceiverForceData> 529forces ={} , 530--- @type table<uint, IntergalacticTransceiverGuiData> 531guis ={} , 532--- @type table<uint, boolean> 533in_cutscene ={} , 534--- @type table<uint, LuaEntity> 535inactive ={} , 536is_victory =true , 537} 538end 539 540intergalactic_transceiver . events ={ 541[ defines . events . on_built_entity ] =on_entity_built , 542[ defines . events . on_cutscene_cancelled ] =on_cutscene_finished , 543[ defines . events . on_cutscene_finished ] =on_cutscene_finished , 544[ defines . events . on_cutscene_waypoint_reached ] =on_cutscene_waypoint_reached , 545[ defines . events . on_entity_cloned ] =on_entity_built , 546[ defines . events . on_entity_died ] =on_entity_destroyed , 547[ defines . events . on_gui_opened ] =on_gui_opened , 548[ defines . events . on_player_mined_entity ] =on_entity_destroyed , 549[ defines . events . on_robot_built_entity ] =on_entity_built , 550[ defines . events . on_robot_mined_entity ] =on_entity_destroyed , 551[ defines . events . on_tick ] =on_tick , 552[ defines . events . script_raised_built ] =on_entity_built , 553[ defines . events . script_raised_destroy ] =on_entity_destroyed , 554[ defines . events . script_raised_revive ] =on_entity_built , 555[ defines . events . on_space_platform_built_entity ] =on_entity_built , 556[ defines . events . on_space_platform_mined_entity ] =on_entity_destroyed , 557} 558 559intergalactic_transceiver . on_nth_tick ={ 560[ 180 ] =on_180th_tick , 561} 562 563-- REMOTE 564 565remote . add_interface ( "kr-intergalactic-transceiver" , { 566get_is_victory =function () 567if storage . intergalactic_transceiver then 568intergalactic_transceiver . on_init () 569end 570return storage . intergalactic_transceiver . is_victory 571end , 572set_is_victory =function ( to_state ) 573if to_state == nil or type ( to_state ) ~= "boolean" then 574error ( "`to_state` must be a boolean" ) 575end 576if not storage . intergalactic_transceiver then 577intergalactic_transceiver . on_init () 578end 579storage . intergalactic_transceiver . is_victory =to_state 580end , 581} ) 582 583flib_gui . add_handlers ( { 584transceiver_on_window_closed =on_gui_window_closed , 585transceiver_on_activate_button_clicked =on_gui_activate_button_clicked , 586} ) 587 588return intergalactic_transceiver