local Landingpad = {} -- constants Landingpad.name_rocket_landing_pad = mod_prefix.."rocket-landing-pad" Landingpad.name_rocket_landing_pad_settings = mod_prefix.."rocket-landing-pad-settings" ---@class LandingPad All data necessary to maintain the state of a delivery cannon --- Gets the Landingpad for this unit_number ---@param unit_number number function Landingpad.from_unit_number (unit_number) if not unit_number then Log.trace("Landingpad.from_unit_number: invalid unit_number: nil") return end unit_number = tonumber(unit_number) -- NOTE: only supports container as the entity if global.rocket_landing_pads[unit_number] then return global.rocket_landing_pads[unit_number] else Log.trace("Landingpad.from_unit_number: invalid unit_number: " .. unit_number) end end --- Gets the Landingpad for this entity ---@param entity LuaEntity function Landingpad.from_entity (entity) if not(entity and entity.valid) then Log.trace("Landingpad.from_entity: invalid entity") return end -- NOTE: only suppors container as the entity return Landingpad.from_unit_number(entity.unit_number) end -- returns the available struct function Landingpad.get_force_landing_pads_availability(force_name, landing_pad_name) local empty_landing_pads = {} local filled_landing_pads = {} local blocked_landing_pads = {} if global.forces[force_name] and global.forces[force_name].rocket_landing_pad_names and global.forces[force_name].rocket_landing_pad_names[landing_pad_name] then local landing_pads = global.forces[force_name].rocket_landing_pad_names[landing_pad_name] for _, landing_pad in pairs(landing_pads) do if landing_pad.container and landing_pad.container.valid then if landing_pad.inbound_rocket then table.insert(blocked_landing_pads, landing_pad) elseif landing_pad.container.get_inventory(defines.inventory.chest).is_empty() then table.insert(empty_landing_pads, landing_pad) else table.insert(filled_landing_pads, landing_pad) end else Landingpad.destroy(landing_pad) end end end return { empty_landing_pads = empty_landing_pads, filled_landing_pads = filled_landing_pads, blocked_landing_pads = blocked_landing_pads, } end -- returns the available struct function Landingpad.get_zone_landing_pads_availability(force_name, zone, landing_pad_name) local empty_landing_pads = {} local filled_landing_pads = {} local blocked_landing_pads = {} local zone_assets = Zone.get_force_assets(force_name, zone.index) if zone_assets.rocket_landing_pad_names and zone_assets.rocket_landing_pad_names[landing_pad_name] then local landing_pads = zone_assets.rocket_landing_pad_names[landing_pad_name] for _, landing_pad in pairs(landing_pads) do if landing_pad.container and landing_pad.container.valid then if landing_pad.inbound_rocket then table.insert(blocked_landing_pads, landing_pad) elseif landing_pad.container.get_inventory(defines.inventory.chest).is_empty() then table.insert(empty_landing_pads, landing_pad) else table.insert(filled_landing_pads, landing_pad) end else Landingpad.destroy(landing_pad) end end end return { empty_landing_pads = empty_landing_pads, filled_landing_pads = filled_landing_pads, blocked_landing_pads = blocked_landing_pads, } end function Landingpad.on_entity_created(event) local entity if event.entity and event.entity.valid then entity = event.entity end if event.created_entity and event.created_entity.valid then entity = event.created_entity end if not entity then return end if entity.name == Landingpad.name_rocket_landing_pad then local zone = Zone.from_surface(entity.surface) if not zone then return cancel_entity_creation(entity, event.player_index, "Invalid landing pad location") end local fn = entity.force.name local default_name = zone.name .. " Landing Pad" local struct = { type = Landingpad.name_rocket_landing_pad, valid = true, force_name = fn, unit_number = entity.unit_number, container = entity, name = default_name, zone = zone } global.rocket_landing_pads = global.rocket_landing_pads or {} global.rocket_landing_pads[entity.unit_number] = struct -- set settings if event.tags and event.tags.name then struct.name = event.tags.name end Landingpad.name(struct) -- assigns to zone_assets if event.player_index and game.players[event.player_index] and game.players[event.player_index].connected then LandingpadGUI.gui_open(game.players[event.player_index], struct) end end end Event.addListener(defines.events.on_entity_cloned, Landingpad.on_entity_created) Event.addListener(defines.events.on_built_entity, Landingpad.on_entity_created) Event.addListener(defines.events.on_robot_built_entity, Landingpad.on_entity_created) Event.addListener(defines.events.script_raised_built, Landingpad.on_entity_created) Event.addListener(defines.events.script_raised_revive, Landingpad.on_entity_created) function Landingpad.remove_struct_from_tables(struct) -- force local force_data = global.forces[struct.force_name] force_data.rocket_landing_pad_names = force_data.rocket_landing_pad_names or {} local force_type_table = force_data.rocket_landing_pad_names if not force_type_table[struct.name] then return end force_type_table[struct.name][struct.unit_number] = nil local count_remaining = 0 for _, remaining in pairs(force_type_table[struct.name]) do count_remaining = count_remaining + 1 end if count_remaining == 0 then force_type_table[struct.name] = nil end -- zone local zone_assets = Zone.get_force_assets(struct.force_name, struct.zone.index) zone_assets.rocket_landing_pad_names = zone_assets.rocket_landing_pad_names or {} local zone_type_table = zone_assets.rocket_landing_pad_names if not zone_type_table[struct.name] then return end zone_type_table[struct.name][struct.unit_number] = nil local count_remaining = 0 for _, remaining in pairs(zone_type_table[struct.name]) do count_remaining = count_remaining + 1 end if count_remaining == 0 then zone_type_table[struct.name] = nil end end function Landingpad.destroy_sub(struct, key) if struct[key] and struct[key].valid then struct[key].destroy() struct[key] = nil end end function Landingpad.destroy(struct) if not struct then Log.trace("Landingpad.destroy: no struct") return end struct.valid = false Landingpad.destroy_sub(struct, 'container') Landingpad.remove_struct_from_tables(struct) global.rocket_landing_pads[struct.unit_number] = nil -- if a player has this gui open then close it local gui_name = LandingpadGUI.name_rocket_landing_pad_gui_root for _, player in pairs(game.connected_players) do local root = player.gui.relative[gui_name] if root and root.tags and root.tags.unit_number == struct.unit_number then player.gui.relative[gui_name].destroy() end end end function Landingpad.name(struct, new_name) struct.name = (new_name or struct.name) -- force local force_data = global.forces[struct.force_name] force_data.rocket_landing_pad_names = force_data.rocket_landing_pad_names or {} local force_type_table = force_data.rocket_landing_pad_names force_type_table[struct.name] = force_type_table[struct.name] or {} force_type_table[struct.name][struct.unit_number] = struct local zone_assets = Zone.get_force_assets(struct.force_name, struct.zone.index) zone_assets.rocket_landing_pad_names = zone_assets.rocket_landing_pad_names or {} local zone_type_table = zone_assets.rocket_landing_pad_names zone_type_table[struct.name] = zone_type_table[struct.name] or {} zone_type_table[struct.name][struct.unit_number] = struct end function Landingpad.rename(struct, new_name) local old_name = struct.name Landingpad.remove_struct_from_tables(struct) Landingpad.name(struct, new_name) end function Landingpad.dropdown_list_zone_landing_pad_names(force_name, zone, current) local selected_index local list = {} -- names with optional [count] local values = {} -- raw names table.insert(list, "None - General vicinity") table.insert(values, "") -- not sure if nil would work if zone and zone.type ~= "spaceship" then local zone_assets = Zone.get_force_assets(force_name, zone.index) for name, sites in pairs(zone_assets["rocket_landing_pad_names"]) do local count = 0 for _, struct in pairs(sites) do count = count + 1 end if count == 1 then table.insert(list, name) table.insert(values, name) if name == current then selected_index = #list end elseif count > 1 then table.insert(list, name .. " ["..count.."]") table.insert(values, name) if name == current then selected_index = #list end end end end return list, (selected_index or 1), values end function Landingpad.dropdown_list_force_landing_pad_names(force_name, current) local selected_index local list = {} -- names with optional [count] local values = {} -- raw names table.insert(list, "None - Cannot launch") table.insert(values, "") -- not sure if nil would work local force_data = global.forces[force_name] if force_data.rocket_landing_pad_names then for name, sites in pairs(force_data.rocket_landing_pad_names) do local count = 0 for _, struct in pairs(sites) do count = count + 1 end if count == 1 then table.insert(list, name) table.insert(values, name) if name == current then selected_index = #list end elseif count > 1 then table.insert(list, name .. " ["..count.."]") table.insert(values, name) if name == current then selected_index = #list end end end end return list, (selected_index or 1), values end function Landingpad.on_entity_removed(event) local entity = event.entity if entity and entity.valid and entity.name == Landingpad.name_rocket_landing_pad then Landingpad.destroy(Landingpad.from_entity(entity)) end end Event.addListener(defines.events.on_entity_died, Landingpad.on_entity_removed) Event.addListener(defines.events.on_robot_mined_entity, Landingpad.on_entity_removed) Event.addListener(defines.events.on_player_mined_entity, Landingpad.on_entity_removed) Event.addListener(defines.events.script_raised_destroy, Landingpad.on_entity_removed) --- Handles the player creating a blueprint by setting tags to store the state of landing pads ---@param event any function Landingpad.on_player_setup_blueprint(event) local player_index = event.player_index if player_index and game.players[player_index] and game.players[player_index].connected then local player = game.players[player_index] -- this setup code and checks is a workaround for the fact that the event doesn't specify the blueprint on the event -- and the player.blueprint_to_setup isn't actually set in the case of copy/paste or blueprint library or select new contents local blueprint = nil if player and player.blueprint_to_setup and player.blueprint_to_setup.valid_for_read then blueprint = player.blueprint_to_setup elseif player and player.cursor_stack.valid_for_read and player.cursor_stack.is_blueprint then blueprint = player.cursor_stack end if blueprint and blueprint.is_blueprint_setup() then local mapping = event.mapping.get() local blueprint_entities = blueprint.get_blueprint_entities() if blueprint_entities then for _, blueprint_entity in pairs(blueprint_entities) do if blueprint_entity.name == Landingpad.name_rocket_landing_pad then local entity = mapping[blueprint_entity.entity_number] if entity then local landing_pad = Landingpad.from_entity(entity) if landing_pad then local tags = {} tags.name = landing_pad.name blueprint.set_blueprint_entity_tags(blueprint_entity.entity_number, tags) end end end end end end end end Event.addListener(defines.events.on_player_setup_blueprint, Landingpad.on_player_setup_blueprint) --- Handles the player copy/pasting settings between landing pads ---@param event any function Landingpad.on_entity_settings_pasted(event) local player_index = event.player_index if player_index and game.players[player_index] and game.players[player_index].connected and event.source and event.source.valid and event.destination and event.destination.valid then if not (event.source.name == Landingpad.name_rocket_landing_pad) then return end if not (event.destination.name == Landingpad.name_rocket_landing_pad) then return end local player = game.players[player_index] local landing_pad_from = Landingpad.from_entity(event.source) local landing_pad_to = Landingpad.from_entity(event.destination) if landing_pad_from and landing_pad_to then -- actual settings copy Landingpad.rename(landing_pad_to, landing_pad_from.name) end end end Event.addListener(defines.events.on_entity_settings_pasted, Landingpad.on_entity_settings_pasted) function Landingpad.on_init(event) global.rocket_landing_pads = {} -- all landing pads sorted by struct[unit_unumber] end Event.addListener("on_init", Landingpad.on_init, true) return Landingpad