z_mirror/EditorExtensions
git clone https://git.raiguard.me/z_mirror/EditorExtensions
22e08fb
trunk
1--- @class LabState 2--- @field lab StateData 3--- @field normal StateData 4--- @field player LuaPlayer 5--- @field refresh boolean? 6 7--- @class StateData 8--- @field force LuaForce 9--- @field position MapPosition 10--- @field surface LuaSurface 11--- @field vehicle LuaEntity? 12--- @field is_driver boolean? 13 14--- @alias LabSetting 15--- | "force" 16--- | "off" 17--- | "personal" 18 19local empty_map_gen_settings ={ 20default_enable_all_autoplace_controls =false , 21property_expression_names ={ cliffiness =0 } , 22autoplace_settings ={ 23tile ={ settings ={ [ "out-of-map" ] ={ frequency ="normal" , size ="normal" , richness ="normal" } } } , 24} , 25starting_area ="none" , 26} 27 28--- @param player LuaPlayer 29--- @return string 30local function get_testing_lab_key ( player ) 31local lab_setting =player . mod_settings [ "ee-testing-lab" ]. value --[[@as LabSetting]] 32if lab_setting == "personal" then 33return tostring ( player . index ) 34elseif game . forces [ "EE_TESTSURFACE_shared" ] then 35return "shared" 36else 37-- gsub has two return values, but we only care about the first one. 38local key =string . gsub ( player . force . name , "EE_TESTFORCE_" , "" ) 39return key 40end 41end 42 43--- @param player LuaPlayer 44--- @return string 45local function get_testing_force_name ( player ) 46return "EE_TESTFORCE_" .. get_testing_lab_key ( player ) 47end 48 49--- @param player LuaPlayer 50--- @return string 51local function get_testing_surface_name ( player ) 52return "EE_TESTSURFACE_" .. get_testing_lab_key ( player ) 53end 54 55--- @param player LuaPlayer 56--- @return LabState? 57local function create_lab ( player ) 58local force_name =get_testing_force_name ( player ) 59local surface_name =get_testing_surface_name ( player ) 60 61local surface =game . get_surface ( surface_name ) 62if not surface then 63surface =game . create_surface ( surface_name , empty_map_gen_settings ) 64for force_name , force in pairs ( game . forces ) do 65if not string . find ( force_name , "EE_TESTFORCE_" ) then 66force . set_surface_hidden ( surface , true ) 67end 68end 69player . force . set_surface_hidden ( surface , true ) 70if not surface then 71player . ( "Could not create test surface" ) 72return 73end 74-- Lab conditions 75surface . generate_with_lab_tiles =true 76surface . freeze_daytime =true 77surface . show_clouds =false 78surface . daytime =0 79surface . ignore_surface_conditions =true 80-- Warn the player about the passage of time 81player . ( { "message.ee-time-passes-in-lab" } ) 82end 83 84local force =game . forces [ force_name ] 85if not game . forces [ force_name ] then 86if # game . forces == 64 then 87player . ( 88"Cannot create a testing lab force. Factorio only supports up to 64 forces at once. Please use a shared lab." 89) 90return 91end 92 93force =game . create_force ( force_name ) 94if settings . global [ "ee-testing-lab-match-research" ]. value then 95-- Sync research techs with the parent force 96for name , tech in pairs ( player . force . technologies ) do 97force . technologies [ name ]. researched =tech . researched 98end 99else 100force . research_all_technologies () 101end 102end 103 104return { 105lab ={ force =force , position ={ x =0 , y =0 } , surface =surface } , 106normal ={ force =player . force , position =player . position , surface =player . surface } , 107player =player , 108} 109end 110 111--- @param player LuaPlayer 112--- @param to_state StateData 113local function transfer_player ( player , to_state ) 114if not to_state . force . valid or not to_state . surface . valid then 115return 116end 117 118-- Change force first to avoid spilling items into the real world on inventory size change - see #143 119player . force =to_state . force 120player . teleport ( to_state . position , to_state . surface ) 121end 122 123--- @param player LuaPlayer 124local function enter_lab ( player ) 125local lab_state =storage . testing_lab_state [ player . index ] 126if not lab_state then 127return 128end 129 130local normal_data =lab_state . normal 131normal_data . force =player . force --[[@as LuaForce]] 132normal_data . position =player . position 133normal_data . surface =player . surface 134 135transfer_player ( player , lab_state . lab ) 136end 137 138--- @param lab_state LabState 139local function exit_lab ( lab_state ) 140local lab_data =lab_state . lab 141if lab_state . player . surface == lab_data . surface then 142lab_data . position =lab_state . player . position 143end 144 145transfer_player ( lab_state . player , lab_state . normal ) 146end 147 148--- @param player LuaPlayer 149--- @return LabSetting 150local function get_lab_setting ( player ) 151return player . mod_settings [ "ee-testing-lab" ]. value --[[@as LabSetting]] 152end 153 154--- @param e EventData.on_pre_player_toggled_map_editor 155local function on_pre_player_toggled_map_editor ( e ) 156local player =game . get_player ( e . player_index ) 157if not player then 158return 159end 160 161local lab_setting =get_lab_setting ( player ) 162if lab_setting == "off" then 163return 164end 165 166local in_editor =player . physical_controller_type == defines . controllers . editor 167local lab_state =storage . testing_lab_state [ e . player_index ] 168if not lab_state and not in_editor then 169lab_state =create_lab ( player ) 170storage . testing_lab_state [ e . player_index ] =lab_state 171end 172if not lab_state then 173return 174end 175 176local current_state =in_editor and lab_state . lab or lab_state . normal 177current_state . vehicle =player . vehicle 178current_state . is_driver =player . driving 179 180if in_editor then 181exit_lab ( lab_state ) 182end 183end 184 185--- @param player LuaPlayer 186local function sync_vehicle_state ( player ) 187local lab_state =storage . testing_lab_state [ player . index ] 188if not lab_state then 189return 190end 191 192local in_editor =player . physical_controller_type == defines . controllers . editor 193local new_state =in_editor and lab_state . lab or lab_state . normal 194 195local vehicle =new_state . vehicle 196if not vehicle or not vehicle . valid then 197return 198end 199 200if new_state . is_driver and not vehicle . get_driver () then 201new_state . vehicle . set_driver ( player ) 202elseif not new_state . is_driver and not vehicle . get_passenger () then 203new_state . vehicle . set_passenger ( player ) 204end 205end 206 207--- @param e EventData.on_player_toggled_map_editor 208local function on_player_toggled_map_editor ( e ) 209local player =game . get_player ( e . player_index ) 210if not player then 211return 212end 213 214if get_lab_setting ( player ) == "off" then 215return 216end 217 218if player . physical_controller_type == defines . controllers . editor then 219enter_lab ( player ) 220end 221 222sync_vehicle_state ( player ) 223end 224 225--- @param e EventData.on_force_reset 226local function on_force_reset ( e ) 227local parent_force =e . force 228if string . find ( parent_force . name , "EE_TESTFORCE_" ) then 229return 230end 231if not settings . global [ "ee-testing-lab-match-research" ]. value then 232return 233end 234 235local testing_force =game . forces [ "EE_TESTFORCE_" .. parent_force . name ] 236if not testing_force then 237return 238end 239 240for name , tech in pairs ( parent_force . technologies ) do 241testing_force . technologies [ name ]. researched =tech . researched 242end 243testing_force . reset_technology_effects () 244end 245 246--- @param e EventData.on_research_reversed 247local function on_research_reversed ( e ) 248local parent_force =e . research . force 249if string . find ( parent_force . name , "EE_TESTFORCE_" ) then 250return 251end 252if not settings . global [ "ee-testing-lab-match-research" ]. value then 253return 254end 255 256local testing_force =game . forces [ "EE_TESTFORCE_" .. parent_force . name ] 257if testing_force then 258testing_force . technologies [ e . research . name ]. researched =false 259end 260 261for i in pairs ( parent_force . players ) do 262local testing_force =game . forces [ "EE_TESTFORCE_" .. i ] 263if testing_force then 264testing_force . technologies [ e . research . name ]. researched =false 265end 266end 267end 268 269--- @param e EventData.on_research_finished 270local function on_research_finished ( e ) 271local parent_force =e . research . force 272if string . find ( parent_force . name , "EE_TESTFORCE_" ) then 273return 274end 275if not settings . global [ "ee-testing-lab-match-research" ]. value then 276return 277end 278 279local testing_force =game . forces [ "EE_TESTFORCE_" .. parent_force . name ] 280if testing_force then 281testing_force . technologies [ e . research . name ]. researched =true 282end 283 284for i in pairs ( parent_force . players ) do 285local testing_force =game . forces [ "EE_TESTFORCE_" .. i ] 286if testing_force then 287testing_force . technologies [ e . research . name ]. researched =true 288end 289end 290end 291 292local function on_match_research_setting_changed () 293for _ , force in pairs ( game . forces ) do 294local _ , _ , force_key =string . find ( force . name , "EE_TESTFORCE_(.*)" ) 295if not force_key then 296goto continue 297end 298if not settings . global [ "ee-testing-lab-match-research" ]. value then 299force . research_all_technologies () 300goto continue 301end 302 303local parent_force 304local force_key_num =tonumber ( force_key ) --- @cast force_key_num uint? 305if force_key_num then 306local player =game . get_player ( force_key_num ) --[[@as LuaPlayer]] 307parent_force =remote . call ( "EditorExtensions" , "get_player_proper_force" , player ) 308else 309parent_force =game . forces [ force_key ] 310end 311if parent_force then 312-- Sync research techs with the parent force 313for name , tech in pairs ( parent_force . technologies ) do 314force . technologies [ name ]. researched =tech . researched 315end 316end 317force . reset_technology_effects () 318:: continue :: 319end 320end 321 322--- @param e EventData.on_runtime_mod_setting_changed 323local function on_testing_lab_setting_changed ( e ) 324local lab_state =storage . testing_lab_state [ e . player_index ] 325if lab_state then 326lab_state . refresh =true 327end 328end 329 330--- @param e EventData.on_runtime_mod_setting_changed 331local function on_runtime_mod_setting_changed ( e ) 332if e . setting == "ee-testing-lab-match-research" then 333on_match_research_setting_changed () 334elseif e . setting == "ee-testing-lab" then 335on_testing_lab_setting_changed ( e ) 336end 337end 338 339local testing_lab ={} 340 341testing_lab . on_init =function () 342--- @type table<uint, LabState?> 343storage . testing_lab_state ={} 344end 345 346function testing_lab . on_configuration_changed () 347for player_index , state in pairs ( storage . testing_lab_state ) do 348if not state . player . valid then 349storage . testing_lab_state [ player_index ] =nil 350goto continue 351end 352 353if not state . normal . force . valid then 354state . normal . force =game . forces . player 355end 356if not state . normal . surface . valid then 357state . normal . surface =game . surfaces . nauvis 358end 359if not state . lab . force . valid then 360local testing_force =game . forces [ get_testing_force_name ( state . player )] 361if not testing_force then 362storage . testing_lab_state [ player_index ] =nil 363goto continue 364end 365state . lab . force =testing_force 366end 367if not state . lab . surface . valid then 368local testing_surface =game . surfaces [ get_testing_surface_name ( state . player )] 369if not testing_surface then 370storage . testing_lab_state [ player_index ] =nil 371goto continue 372end 373state . lab . surface =testing_surface 374end 375 376:: continue :: 377end 378end 379 380testing_lab . events ={ 381[ defines . events . on_force_reset ] =on_force_reset , 382[ defines . events . on_player_toggled_map_editor ] =on_player_toggled_map_editor , 383[ defines . events . on_pre_player_toggled_map_editor ] =on_pre_player_toggled_map_editor , 384[ defines . events . on_research_finished ] =on_research_finished , 385[ defines . events . on_research_reversed ] =on_research_reversed , 386[ defines . events . on_runtime_mod_setting_changed ] =on_runtime_mod_setting_changed , 387} 388 389testing_lab . add_remote_interface =function () 390remote . add_interface ( "EditorExtensions" , { 391--- Get the force that the player is actually on, ignoring the testing lab force. 392--- @param player LuaPlayer 393--- @return ForceID 394get_player_proper_force =function ( player ) 395if not player or not player . valid then 396error ( "Did not pass a valid LuaPlayer" ) 397end 398local in_editor =player . physical_controller_type == defines . controllers . editor 399local ts_setting =player . mod_settings [ "ee-testing-lab" ]. value 400if 401ts_setting == "off" 402or not in_editor 403or not storage . testing_lab_state 404or not storage . testing_lab_state [ player . index ] 405then 406return player . force 407end 408 409return storage . testing_lab_state [ player . index ]. normal . force 410end , 411} ) 412end 413 414return testing_lab