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