You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

367 lines
13 KiB

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