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