z_mirror/Krastorio2
git clone https://git.raiguard.me/z_mirror/Krastorio2
ad8c3f3a
real-fusion-reactor
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 props =status_properties [ transceiver_data . status ] 227self . elems . status_icon . sprite =props . icon 228self . elems . status_label . caption =props . label 229self . elems . status_label . tooltip =props . tooltip 230self . elems . charge_progressbar . value =charged 231self . elems . charge_progressbar . caption =math . floor ( charged * 100 ) .. " %" 232self . elems . activate_button . enabled =charged == 1 233end 234 235--- @param player LuaPlayer 236--- @param entity LuaEntity 237local function create_gui ( player , entity ) 238local elems =flib_gui . add ( player . gui . screen , { 239type ="frame" , 240name ="kr_intergalactic_transceiver_window" , 241direction ="vertical" , 242elem_mods ={ auto_center =true } , --- @diagnostic disable-line: missing-fields 243handler ={ [ defines . events . on_gui_closed ] =on_gui_window_closed } , 244{ 245type ="flow" , 246style ="flib_titlebar_flow" , 247drag_target ="kr_intergalactic_transceiver_window" , 248{ 249type ="label" , 250style ="flib_frame_title" , 251caption ={ "entity-name.kr-intergalactic-transceiver" } , 252ignored_by_interaction =true , 253} , 254{ type ="empty-widget" , style ="flib_titlebar_drag_handle" , ignored_by_interaction =true } , 255{ 256type ="sprite-button" , 257style ="frame_action_button" , 258sprite ="utility/close" , 259mouse_button_filter ={ "left" } , 260handler ={ [ defines . events . on_gui_click ] =on_gui_window_closed } , 261} , 262} , 263{ 264type ="frame" , 265style ="entity_frame" , 266style_mods ={ width =424 } , --- @diagnostic disable-line: missing-fields 267direction ="vertical" , 268{ 269type ="flow" , 270style_mods ={ vertical_align ="center" } , 271{ type ="sprite" , name ="status_icon" , style ="flib_indicator" } , 272{ type ="label" , name ="status_label" } , 273} , 274{ 275type ="frame" , 276style ="deep_frame_in_shallow_frame" , 277{ type ="entity-preview" , style ="wide_entity_button" , elem_mods ={ entity =entity } } , 278} , 279{ 280type ="progressbar" , 281name ="charge_progressbar" , 282style ="production_progressbar" , 283style_mods ={ horizontally_stretchable =true } , 284value =0 , 285} , 286{ 287type ="button" , 288name ="activate_button" , 289style_mods ={ horizontally_stretchable =true } , 290caption ={ "gui.kr-intergalactic-transceiver-activate" } , 291enabled =false , 292handler ={ [ defines . events . on_gui_click ] =on_gui_activate_button_clicked } , 293} , 294} , 295} ) 296 297--- @class IntergalacticTransceiverGuiData 298storage . intergalactic_transceiver . guis [ player . index ] ={ 299elems =elems , 300entity =entity , 301player =player , 302} 303 304player . opened =elems . kr_intergalactic_transceiver_window 305if not game . is_multiplayer () then 306player . play_sound ( { path ="entity-open/kr-intergalactic-transceiver" } ) 307end 308end 309 310--- @param entity LuaEntity 311local function on_transceiver_destroyed ( entity ) 312local force_index =entity . force . index 313storage . intergalactic_transceiver . forces [ force_index ] =nil 314close_open_guis ( entity ) 315end 316 317--- @param entity LuaEntity 318local function on_inactive_transceiver_destroyed ( entity ) 319storage . intergalactic_transceiver . inactive [ entity . unit_number ] =nil 320end 321 322--- @param e EntityDestroyedEvent 323local function on_entity_destroyed ( e ) 324local entity =e . entity 325if not entity or not entity . valid then 326return 327end 328 329if entity . name == "kr-intergalactic-transceiver" then 330on_transceiver_destroyed ( entity ) 331elseif entity . name == "kr-inactive-intergalactic-transceiver" then 332on_inactive_transceiver_destroyed ( entity ) 333end 334end 335 336--- @param entity LuaEntity 337local function on_transceiver_built ( entity ) 338local existing =storage . intergalactic_transceiver . forces [ entity . force . index ] 339if existing then 340local position =entity . position 341local force =entity . force 342local player =entity . last_user 343local surface =entity . surface 344entity . destroy () 345 346local new_entity =surface . create_entity ( { 347name ="kr-inactive-intergalactic-transceiver" , 348position =position , 349force =force , 350player =player , 351create_build_effect_smoke =false , 352raise_built =true , 353} ) 354if new_entity and new_entity . valid then 355storage . intergalactic_transceiver . inactive [ new_entity . unit_number ] =new_entity 356end 357 358return 359end 360 361--- @class IntergalacticTransceiverForceData 362storage . intergalactic_transceiver . forces [ entity . force . index ] ={ 363activating =false , 364entity =entity , 365last_alert_tick =game . tick , 366last_energy =entity . energy , 367status ="empty" , 368} 369end 370 371--- @param entity LuaEntity 372local function on_active_transceiver_built ( entity ) 373local existing =storage . intergalactic_transceiver . forces [ entity . force . index ] 374if not existing or not existing . activating then 375local position =entity . position 376local force =entity . force 377local player =entity . last_user 378local surface =entity . surface 379entity . destroy () 380 381local new_entity =surface . create_entity ( { 382name ="kr-intergalactic-transceiver" , 383position =position , 384force =force , 385player =player , 386create_build_effect_smoke =false , 387raise_built =true , 388} ) 389 390if new_entity and new_entity . valid then 391on_transceiver_built ( new_entity ) 392end 393end 394end 395 396--- @param e EntityBuiltEvent 397local function on_entity_built ( e ) 398local entity =e . entity or e . destination 399if not entity or not entity . valid then 400return 401end 402 403if entity . name == "kr-intergalactic-transceiver" then 404on_transceiver_built ( entity ) 405elseif entity . name == "kr-activated-intergalactic-transceiver" then 406on_active_transceiver_built ( entity ) 407end 408end 409 410--- @param data IntergalacticTransceiverForceData 411--- @param entity LuaEntity 412local function update_transceiver ( data , entity ) 413if not data . last_energy then 414return 415end 416local current_energy =entity . energy 417local new_energy =current_energy 418local difference =current_energy - data . last_energy 419--- @type IntergalacticTransceiverStatus 420local status ="charging" 421-- If we're not receiving enough power 422if difference < min_charge_delta and current_energy > 0 then 423-- Drain the transceiver at 3 TJ / sec 424new_energy =math . max ( 0 , current_energy - energy_drain ) 425-- Update status 426if new_energy > 0 then 427status ="discharging" 428else 429status ="not_enough_input" 430end 431else 432if current_energy > max_energy then 433-- XXX: If the energy remains constant, the entity status will change to "normal", so give it a random offset. 434new_energy =max_energy + math . random ( 0 , 20 ) * 1000000 435status ="ready" 436else 437local current_status =defines_to_status [ entity . status ] 438if status_properties [ current_status ] then 439status =current_status 440end 441end 442end 443 444if new_energy ~= current_energy then 445entity . energy =new_energy 446end 447local last_status =data . status 448data . last_energy =new_energy 449data . status =status 450 451local status_properties =status_properties [ status ] 452if status_properties and status_properties . alert_label and game . tick - data . last_alert_tick >= 60 then 453data . last_alert_tick =game . tick 454for _ , player in pairs ( entity . force . players ) do 455player . add_custom_alert ( 456entity , 457{ type ="item" , name ="kr-intergalactic-transceiver" } , 458status_properties . alert_label , 459true 460) 461if status == "discharging" and last_status ~= status then 462player . play_sound ( { 463path ="kr-intergalactic-transceiver-discharging-warning" , 464} ) 465end 466end 467end 468end 469 470--- @param e EventData.on_gui_opened 471local function on_gui_opened ( e ) 472if storage . intergalactic_transceiver . guis [ e . player_index ] then 473return 474end 475if e . gui_type ~= defines . gui_type . entity then 476return 477end 478local entity =e . entity 479if not entity or entity . name ~= "kr-intergalactic-transceiver" then 480return 481end 482local player =game . get_player ( e . player_index ) 483if not player then 484return 485end 486create_gui ( player , entity ) 487end 488 489local function on_tick () 490if not storage . intergalactic_transceiver then 491return 492end 493 494local transceivers =storage . intergalactic_transceiver . forces 495for force_index , data in pairs ( transceivers ) do 496local entity =data . entity 497if entity and entity . valid then 498update_transceiver ( data , entity ) 499else 500transceivers [ force_index ] =nil 501end 502end 503 504for _ , self in pairs ( storage . intergalactic_transceiver . guis ) do 505update_gui ( self ) 506end 507end 508 509local function on_180th_tick () 510if not storage . intergalactic_transceiver then 511return 512end 513for unit_number , entity in pairs ( storage . intergalactic_transceiver . inactive ) do 514if entity . valid then 515util . entity_flying_text ( entity , { "message.kr-transceiver-is-inactive" } , { r =1 } ) 516else 517storage . intergalactic_transceiver . inactive [ unit_number ] =nil 518end 519end 520end 521 522--- @class intergalactic_transceiver 523local intergalactic_transceiver ={} 524 525function intergalactic_transceiver . on_init () 526-- Can be already initialised through remote interface 527if storage . intergalactic_transceiver then 528return 529end 530storage . intergalactic_transceiver ={ 531--- @type table<uint, IntergalacticTransceiverForceData> 532forces ={} , 533--- @type table<uint, IntergalacticTransceiverGuiData> 534guis ={} , 535--- @type table<uint, boolean> 536in_cutscene ={} , 537--- @type table<uint, LuaEntity> 538inactive ={} , 539is_victory =true , 540} 541end 542 543intergalactic_transceiver . events ={ 544[ defines . events . on_built_entity ] =on_entity_built , 545[ defines . events . on_cutscene_cancelled ] =on_cutscene_finished , 546[ defines . events . on_cutscene_finished ] =on_cutscene_finished , 547[ defines . events . on_cutscene_waypoint_reached ] =on_cutscene_waypoint_reached , 548[ defines . events . on_entity_cloned ] =on_entity_built , 549[ defines . events . on_entity_died ] =on_entity_destroyed , 550[ defines . events . on_gui_opened ] =on_gui_opened , 551[ defines . events . on_player_mined_entity ] =on_entity_destroyed , 552[ defines . events . on_robot_built_entity ] =on_entity_built , 553[ defines . events . on_robot_mined_entity ] =on_entity_destroyed , 554[ defines . events . on_tick ] =on_tick , 555[ defines . events . script_raised_built ] =on_entity_built , 556[ defines . events . script_raised_destroy ] =on_entity_destroyed , 557[ defines . events . script_raised_revive ] =on_entity_built , 558[ defines . events . on_space_platform_built_entity ] =on_entity_built , 559[ defines . events . on_space_platform_mined_entity ] =on_entity_destroyed , 560} 561 562intergalactic_transceiver . on_nth_tick ={ 563[ 180 ] =on_180th_tick , 564} 565 566-- REMOTE 567 568remote . add_interface ( "kr-intergalactic-transceiver" , { 569get_is_victory =function () 570if storage . intergalactic_transceiver then 571intergalactic_transceiver . on_init () 572end 573return storage . intergalactic_transceiver . is_victory 574end , 575set_is_victory =function ( to_state ) 576if to_state == nil or type ( to_state ) ~= "boolean" then 577error ( "`to_state` must be a boolean" ) 578end 579if not storage . intergalactic_transceiver then 580intergalactic_transceiver . on_init () 581end 582storage . intergalactic_transceiver . is_victory =to_state 583end , 584} ) 585 586flib_gui . add_handlers ( { 587transceiver_on_window_closed =on_gui_window_closed , 588transceiver_on_activate_button_clicked =on_gui_activate_button_clicked , 589} ) 590 591return intergalactic_transceiver