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.
1582 lines
69 KiB
1582 lines
69 KiB
local Launchpad = {}
|
|
-- launchpad, cargo rockeet, and rocket landing / crashing sequences
|
|
|
|
--custon event
|
|
Launchpad.on_cargo_rocket_launched_event = script.generate_event_name()
|
|
|
|
-- constants
|
|
Launchpad.name_rocket_launch_pad = mod_prefix.."rocket-launch-pad"
|
|
Launchpad.name_rocket_launch_pad_section_input = mod_prefix.."rocket-launch-pad-_-section-input"
|
|
Launchpad.name_rocket_launch_pad_tank = mod_prefix.."rocket-launch-pad-tank"
|
|
Launchpad.name_rocket_launch_pad_combinator = mod_prefix.."rocket-launch-pad-combinator"
|
|
Launchpad.name_rocket_launch_pad_silo = mod_prefix.."rocket-launch-pad-silo"
|
|
Launchpad.name_rocket_launch_pad_silo_dummy = mod_prefix.."rocket-launch-pad-silo-dummy-ingredient-item"
|
|
Launchpad.name_rocket_launch_pad_seat = mod_prefix.."rocket-launch-pad-_-seat"
|
|
Launchpad.name_rocket_section = mod_prefix.."cargo-rocket-section"
|
|
|
|
Launchpad.name_cargo_rocket = mod_prefix.."cargo-rocket"
|
|
Launchpad.name_cargo_pod = mod_prefix.."cargo-rocket-cargo-pod"
|
|
|
|
Launchpad.name_tech_rocket_cargo_safety = mod_prefix.."rocket-cargo-safety"
|
|
Launchpad.name_tech_rocket_reusability = mod_prefix.."rocket-reusability"
|
|
Launchpad.name_tech_rocket_survivability = mod_prefix.."rocket-survivability"
|
|
|
|
Launchpad.rocket_capacity = 500
|
|
Launchpad.fuel_per_delta_v = 8 -- maybe this should be an energy value?
|
|
Launchpad.rocket_sections_per_rocket = 100
|
|
Launchpad.crew_capsules_per_rocket = 1
|
|
Launchpad.signal_for_launch = {type = "virtual", name = "signal-green"}
|
|
Launchpad.signal_for_rocket_complete = {type = "virtual", name = mod_prefix.."cargo-rocket"}
|
|
|
|
Launchpad.trigger_options = {
|
|
{ name = "none", display = {"space-exploration.trigger-none"}},
|
|
{ name = "fuel-full", display = {"space-exploration.trigger-fuel-full"}},
|
|
{ name = "cargo-full", display = {"space-exploration.trigger-cargo-full"}},
|
|
{ name = "fuel-full-signal", display = {"space-exploration.trigger-fuel-full-signal"}},
|
|
{ name = "cargo-full-signal", display = {"space-exploration.trigger-cargo-full-signal"}},
|
|
{ name = "cargo-full-or-signal", display = {"space-exploration.trigger-cargo-full-or-signal"}},
|
|
}
|
|
|
|
Launchpad.time_takeoff = 440 -- the delay before the rocket gets going
|
|
Launchpad.time_takeoff_finish_ascent = 1100 --1164 -- just before the rocket launch event would fire
|
|
|
|
Launchpad.time_landing_capsule_touchdown = 6 * 60 -- when the capsule lands
|
|
Launchpad.time_landing_cargopod_first = 3 * 60 -- when the first cargopod lands
|
|
Launchpad.time_landing_cargopod_last = 5 * 60 -- when the last cargopod lands
|
|
Launchpad.time_landing_debris_first = 2 * 60 -- when the first debris lands
|
|
Launchpad.time_landing_debris_last = 7 * 60 -- when the last debris lands
|
|
Launchpad.landing_start_altitude = 128
|
|
|
|
Launchpad.safety_delta_v_base = 1000
|
|
Launchpad.safety_delta_v_factor = 100000
|
|
|
|
Launchpad.names_cargo_fragment = { -- these are the base names, actual names are "se-falling-"
|
|
"cargo-fragment-a",
|
|
"cargo-fragment-b",
|
|
"cargo-fragment-c",
|
|
"cargo-fragment-d",
|
|
}
|
|
|
|
Launchpad.rocket_fragments_large = 4
|
|
Launchpad.names_rocket_fragments_large = {
|
|
"rocket-fragment-big-d",
|
|
"rocket-fragment-big-a",
|
|
"rocket-fragment-big-b",
|
|
"rocket-fragment-big-c"
|
|
}
|
|
Launchpad.rocket_fragments_medium = 6
|
|
Launchpad.names_rocket_fragments_medium = {
|
|
"rocket-fragment-medium-a",
|
|
"rocket-fragment-medium-b",
|
|
"rocket-fragment-medium-c"
|
|
}
|
|
Launchpad.rocket_fragments_small = 20
|
|
Launchpad.names_rocket_fragments_small = {
|
|
"rocket-fragment-small-a",
|
|
"rocket-fragment-small-b",
|
|
"rocket-fragment-small-c",
|
|
"rocket-fragment-small-d",
|
|
"rocket-fragment-small-e",
|
|
"rocket-fragment-small-f",
|
|
"rocket-fragment-small-g",
|
|
"rocket-fragment-small-h",
|
|
"rocket-fragment-small-i",
|
|
"rocket-fragment-small-j"
|
|
}
|
|
Launchpad.rocket_fragments_tiny = 20
|
|
Launchpad.names_rocket_fragments_tiny = {
|
|
"rocket-fragment-tiny-a",
|
|
"rocket-fragment-tiny-b",
|
|
"rocket-fragment-tiny-c",
|
|
"rocket-fragment-tiny-d",
|
|
"rocket-fragment-tiny-e",
|
|
"rocket-fragment-tiny-f"
|
|
}
|
|
|
|
function Launchpad.get_force_cargo_loss_modifier(force)
|
|
local researched_levels = 0
|
|
local i = 1
|
|
local current_level = force.technologies[Launchpad.name_tech_rocket_cargo_safety.."-"..i]
|
|
while current_level and (current_level.researched or current_level.level > i) do
|
|
local more_levels = (current_level.level or i) - i
|
|
i = i + 1 + more_levels
|
|
researched_levels = researched_levels + (1 + more_levels)
|
|
current_level = force.technologies[Launchpad.name_tech_rocket_cargo_safety.."-"..i]
|
|
end
|
|
return 0.9 ^ researched_levels
|
|
end
|
|
|
|
function Launchpad.get_cargo_loss(force, delta_v) -- percentage of cargo lost
|
|
delta_v = (delta_v or Launchpad.safety_delta_v_factor) + Launchpad.safety_delta_v_base -- 50%
|
|
local loss = delta_v/(delta_v+Launchpad.safety_delta_v_factor)
|
|
--local loss = 0.5 * global.forces[force.name].cargo_rockets_launched / (global.forces[force.name].cargo_rockets_launched + 500)
|
|
return loss *Launchpad.get_force_cargo_loss_modifier(force)
|
|
end
|
|
|
|
function Launchpad.get_reusability(force) -- percentage of parts not recoverable (all at start)
|
|
local recovery = 0.2
|
|
local i = 1
|
|
local current_level = force.technologies[Launchpad.name_tech_rocket_reusability.."-"..i]
|
|
while current_level and (current_level.researched or current_level.level > i) do
|
|
local more_levels = (current_level.level or i) - i
|
|
i = i + 1 + more_levels
|
|
recovery = recovery + 0.04 * (1 + more_levels)
|
|
current_level = force.technologies[Launchpad.name_tech_rocket_reusability.."-"..i]
|
|
end
|
|
return recovery
|
|
end
|
|
|
|
function Launchpad.get_force_survivability_loss_modifier(force) -- chance to fail to land on rocket launch pad
|
|
local multiplier = 1
|
|
local i = 1
|
|
while force.technologies[Launchpad.name_tech_rocket_survivability.."-"..i]
|
|
and force.technologies[Launchpad.name_tech_rocket_survivability.."-"..i].researched do
|
|
i = i + 1
|
|
multiplier = multiplier * 0.9
|
|
end
|
|
if force.technologies[Launchpad.name_tech_rocket_survivability.."-"..i] then
|
|
local tech = force.technologies[Launchpad.name_tech_rocket_survivability.."-"..i]
|
|
if tech.prototype.max_level > 10 then -- infinite
|
|
for j = i, tech.level do
|
|
multiplier = multiplier * 0.9
|
|
end
|
|
end
|
|
end
|
|
return multiplier
|
|
end
|
|
|
|
function Launchpad.get_survivability_loss(force, delta_v) -- chance to fail to land on rocket launch pad
|
|
delta_v = (delta_v or Launchpad.safety_delta_v_factor) + Launchpad.safety_delta_v_base -- 50%
|
|
local loss = delta_v/(delta_v+Launchpad.safety_delta_v_factor)
|
|
--local loss = 0.5 * global.forces[force.name].cargo_rockets_launched / (global.forces[force.name].cargo_rockets_launched + 500)
|
|
return loss * Launchpad.get_force_survivability_loss_modifier(force)
|
|
end
|
|
|
|
---@class Launchpad All data necessary to maintain the state of a launchpad
|
|
|
|
--- Gets the Launchpad for this unit_number
|
|
---@param unit_number number
|
|
function Launchpad.from_unit_number (unit_number)
|
|
if not unit_number then Log.trace("Launchpad.from_unit_number: invalid unit_number: nil") return end
|
|
unit_number = tonumber(unit_number)
|
|
-- NOTE: only supports container as the entity
|
|
if global.rocket_launch_pads[unit_number] then
|
|
return global.rocket_launch_pads[unit_number]
|
|
else
|
|
Log.trace("Launchpad.from_unit_number: invalid unit_number: " .. unit_number)
|
|
end
|
|
end
|
|
|
|
--- Gets the Launchpad for this entity
|
|
---@param entity any
|
|
function Launchpad.from_entity (entity)
|
|
if not(entity and entity.valid) then
|
|
Log.trace("Launchpad.from_entity: invalid entity")
|
|
return
|
|
end
|
|
-- NOTE: only supports container as the entity
|
|
return Launchpad.from_unit_number(entity.unit_number)
|
|
end
|
|
|
|
--- Computes the cost to launch a rocket
|
|
---@param origin Zone launch zone
|
|
---@param destination Zone destination zone
|
|
function Launchpad.get_fuel_cost(origin, destination)
|
|
if origin and destination then
|
|
return Launchpad.fuel_per_delta_v * (Zone.get_launch_delta_v(origin) + Zone.get_travel_delta_v(origin, destination))
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Input the destination as either:
|
|
{ type = "zone", zone_index = name }
|
|
{ type = "landing-pad", zone_index = name, landing_pad_name = name }
|
|
Output the destination as:
|
|
{ type = "zone", zone_index = name, zone = Zone, position = {x=#,y=#} }
|
|
{ type = "landing-pad", landing_pad_name = name, landing_pad = rocket_landing_pad}
|
|
]]--
|
|
function Launchpad.lock_destination (struct, allow_non_empty)
|
|
-- force launch can go to non-empty landings
|
|
|
|
if struct.destination then
|
|
if struct.destination.zone then
|
|
-- can't have people changing destination mid-launch
|
|
local force_name = struct.force_name
|
|
-- destination to locked destination
|
|
local locked_destination = { -- don't use deepcopy be
|
|
zone = struct.destination.zone,
|
|
landing_pad_name = struct.destination.landing_pad_name,
|
|
}
|
|
|
|
local zone = locked_destination.zone
|
|
if not zone then return false end
|
|
|
|
if locked_destination.landing_pad_name then
|
|
|
|
local landing_pads = Landingpad.get_zone_landing_pads_availability(force_name, zone, locked_destination.landing_pad_name)
|
|
|
|
if #landing_pads.empty_landing_pads > 0 then
|
|
local landing_pad = landing_pads.empty_landing_pads[math.random(#landing_pads.empty_landing_pads)]
|
|
locked_destination.landing_pad = landing_pad
|
|
Zone.get_make_surface(zone)
|
|
return locked_destination
|
|
elseif allow_non_empty and #landing_pads.filled_landing_pads > 0 then
|
|
local landing_pad = landing_pads.filled_landing_pads[math.random(#landing_pads.filled_landing_pads)]
|
|
locked_destination.landing_pad = landing_pad
|
|
Zone.get_make_surface(zone)
|
|
return locked_destination
|
|
end
|
|
|
|
else -- not landing pad name
|
|
|
|
Zone.get_make_surface(zone)
|
|
locked_destination.position = Zone.find_zone_landing_position(zone)
|
|
|
|
--this is the first cargo rocket to space, go to the space platform satellite
|
|
if (zone.type == "orbit" and zone.parent.name == "Nauvis") then
|
|
if (global.forces[force_name].cargo_rockets_launched or 0) == 0 and global.forces[force_name].nauvis_satellite then
|
|
local satellite_blueprint = Ruin.ruins["satellite"]
|
|
locked_destination.position = {
|
|
x = global.forces[force_name].nauvis_satellite.x
|
|
+ (satellite_blueprint.landing_offset.x or satellite_blueprint.landing_offset[1])
|
|
- (satellite_blueprint.center.x or satellite_blueprint.center[1]),
|
|
y = global.forces[force_name].nauvis_satellite.y
|
|
+ (satellite_blueprint.landing_offset.y or satellite_blueprint.landing_offset[2])
|
|
- (satellite_blueprint.center.y or satellite_blueprint.center[2])
|
|
}
|
|
end
|
|
end
|
|
return locked_destination
|
|
end
|
|
elseif struct.destination.landing_pad_name then
|
|
-- can't have people changing destination mid-launch
|
|
local force_name = struct.force_name
|
|
|
|
local locked_destination = { -- don't use deepcopy be
|
|
landing_pad_name = struct.destination.landing_pad_name,
|
|
}
|
|
|
|
local landing_pads = Landingpad.get_force_landing_pads_availability(force_name, locked_destination.landing_pad_name)
|
|
|
|
if #landing_pads.empty_landing_pads > 0 then
|
|
local landing_pad = landing_pads.empty_landing_pads[math.random(#landing_pads.empty_landing_pads)]
|
|
locked_destination.zone = landing_pad.zone
|
|
locked_destination.landing_pad = landing_pad
|
|
return locked_destination
|
|
elseif allow_non_empty and #landing_pads.filled_landing_pads > 0 then
|
|
local landing_pad = landing_pads.filled_landing_pads[math.random(#landing_pads.filled_landing_pads)]
|
|
locked_destination.zone = landing_pad.zone
|
|
locked_destination.landing_pad = landing_pad
|
|
return locked_destination
|
|
end
|
|
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function Launchpad.add_section_input(struct)
|
|
-- spawn section input
|
|
struct.section_input = struct.container.surface.create_entity{
|
|
name = Launchpad.name_rocket_launch_pad_section_input,
|
|
force = struct.container.force,
|
|
position = {struct.container.position.x, struct.container.position.y}}
|
|
local inv = struct.section_input.get_inventory(defines.inventory.car_trunk)
|
|
inv.set_filter(1, Launchpad.name_rocket_section)
|
|
inv.set_filter(2, Capsule.name_space_capsule)
|
|
end
|
|
|
|
function Launchpad.update_combinator(struct, main_inv)
|
|
if struct.combinator then
|
|
local comb = struct.combinator.get_or_create_control_behavior()
|
|
-- comb.set_signal(1, {signal={type="fluid", name=name_fluid_rocket_fuel}, count=struct.total_fuel})
|
|
comb.set_signal(1, {signal={type="fluid", name=name_fluid_rocket_fuel}, count=struct.lua_fuel})
|
|
comb.set_signal(2, {signal={type="item", name=Launchpad.name_rocket_section}, count=struct.rocket_sections})
|
|
comb.set_signal(3, {signal={type="item", name=Capsule.name_space_capsule}, count=struct.crew_capsules})
|
|
if struct.rocket_sections >= Launchpad.rocket_sections_per_rocket and struct.crew_capsules >= Launchpad.crew_capsules_per_rocket then
|
|
comb.set_signal(4, {signal=Launchpad.signal_for_rocket_complete, count=1})
|
|
else
|
|
comb.set_signal(4, {signal=Launchpad.signal_for_rocket_complete, count=0})
|
|
end
|
|
|
|
local slots_free = main_inv.count_empty_stacks()
|
|
|
|
comb.set_signal(5, {signal={type = "virtual", name = "signal-E"}, count= slots_free})
|
|
comb.set_signal(6, {signal={type = "virtual", name = "signal-F"}, count= #main_inv - slots_free})
|
|
comb.set_signal(7, {signal={type = "virtual", name = "signal-L"}, count= struct.required_fuel or 0})
|
|
end
|
|
end
|
|
|
|
function Launchpad.prep(struct)
|
|
|
|
--local profiler = game.create_profiler()
|
|
|
|
if not (struct.container and struct.container.valid) then
|
|
return Launchpad.destroy(struct)
|
|
end
|
|
|
|
struct.required_fuel = nil -- invalid
|
|
if struct.destination then
|
|
local origin_zone = struct.zone
|
|
if struct.destination.landing_pad_name and not struct.destination.zone then
|
|
|
|
if global.forces[struct.force_name]
|
|
and global.forces[struct.force_name].rocket_landing_pad_names
|
|
and global.forces[struct.force_name].rocket_landing_pad_names[struct.destination.landing_pad_name] then
|
|
local required_fuel_max = 0
|
|
local landing_pads_list = global.forces[struct.force_name].rocket_landing_pad_names[struct.destination.landing_pad_name]
|
|
for _, landing_pad in pairs(landing_pads_list) do
|
|
required_fuel_max = math.max(required_fuel_max, Launchpad.get_fuel_cost(origin_zone, landing_pad.zone))
|
|
end
|
|
if required_fuel_max > 0 then
|
|
struct.required_fuel = required_fuel_max
|
|
end
|
|
end
|
|
else
|
|
local destination_zone = struct.destination.zone
|
|
struct.required_fuel = Launchpad.get_fuel_cost(origin_zone, destination_zone); -- self-validates
|
|
end
|
|
else
|
|
struct.required_fuel = nil -- invalid
|
|
end
|
|
|
|
-- balance tank fuel and lua fuel
|
|
-- fuel tank % should match total %
|
|
local fluidbox
|
|
local effective_required_fuel = (struct.required_fuel or 0) + 1000
|
|
if not (struct.tank and struct.tank.valid) then
|
|
struct.tank = struct.container.surface.create_entity{
|
|
name = Launchpad.name_rocket_launch_pad_tank,
|
|
force = struct.container.force,
|
|
position = {struct.container.position.x, struct.container.position.y + 1}} -- 1 tile down to be in front of silo
|
|
struct.tank.fluidbox.set_filter(1, {name = name_fluid_rocket_fuel, force = true})
|
|
struct.container.connect_neighbour({wire = defines.wire_type.red, target_entity = struct.tank})
|
|
struct.container.connect_neighbour({wire = defines.wire_type.green, target_entity = struct.tank})
|
|
struct.tank.destructible = false
|
|
end
|
|
if struct.tank and #struct.tank.fluidbox > 0 then
|
|
fluidbox = struct.tank.fluidbox[1] or {name = name_fluid_rocket_fuel, amount = 0}
|
|
struct.total_fuel = (struct.lua_fuel or 0) + fluidbox.amount
|
|
if struct.total_fuel > 0 then
|
|
local percent_of_required = 1 -- overflow
|
|
percent_of_required = struct.total_fuel / effective_required_fuel -- leave 10% available to fill faster
|
|
if percent_of_required < 1 then
|
|
percent_of_required = percent_of_required * 0.5
|
|
end
|
|
fluidbox.amount = math.min(struct.total_fuel, math.min(1, percent_of_required) * struct.tank.fluidbox.get_capacity(1))
|
|
if fluidbox.amount < 1 then
|
|
fluidbox.amount = 1
|
|
end
|
|
struct.lua_fuel = struct.total_fuel - fluidbox.amount
|
|
struct.tank.fluidbox[1] = fluidbox
|
|
end
|
|
end
|
|
|
|
local main_inv = struct.container.get_inventory(defines.inventory.chest)
|
|
|
|
struct.crew_capsules = struct.crew_capsules or 0
|
|
struct.rocket_sections = struct.rocket_sections or 0
|
|
|
|
-- take from section input first
|
|
if (struct.crew_capsules or 0) < Launchpad.crew_capsules_per_rocket and struct.section_input and struct.section_input.valid then
|
|
local inv = struct.section_input.get_inventory(defines.inventory.car_trunk)
|
|
local items = inv.get_item_count(Capsule.name_space_capsule)
|
|
if items > 0 then
|
|
local take = 1
|
|
inv.remove({name=Capsule.name_space_capsule, count=take})
|
|
struct.crew_capsules = struct.crew_capsules + take
|
|
end
|
|
end
|
|
|
|
-- otherwise take from main container
|
|
if (struct.crew_capsules or 0) < Launchpad.crew_capsules_per_rocket then
|
|
local items = main_inv.get_item_count(Capsule.name_space_capsule)
|
|
if items > 0 then
|
|
local take = math.min(items, Launchpad.crew_capsules_per_rocket - struct.crew_capsules)
|
|
main_inv.remove({name=Capsule.name_space_capsule, count=take})
|
|
struct.crew_capsules = struct.crew_capsules + take
|
|
end
|
|
end
|
|
|
|
-- take from section input first
|
|
if struct.rocket_sections < Launchpad.rocket_sections_per_rocket and struct.section_input and struct.section_input.valid then
|
|
local inv = struct.section_input.get_inventory(defines.inventory.car_trunk)
|
|
local items = inv.get_item_count(Launchpad.name_rocket_section)
|
|
if items > 0 then
|
|
local take = math.min(items, Launchpad.rocket_sections_per_rocket - struct.rocket_sections)
|
|
inv.remove({name=Launchpad.name_rocket_section, count=take})
|
|
struct.rocket_sections = struct.rocket_sections + take
|
|
end
|
|
end
|
|
|
|
-- otherwise take from main container
|
|
if struct.rocket_sections < Launchpad.rocket_sections_per_rocket then
|
|
local items = main_inv.get_item_count(Launchpad.name_rocket_section)
|
|
if items > 0 then
|
|
local take = math.min(items, Launchpad.rocket_sections_per_rocket - struct.rocket_sections)
|
|
main_inv.remove({name=Launchpad.name_rocket_section, count=take})
|
|
struct.rocket_sections = struct.rocket_sections + take
|
|
end
|
|
end
|
|
|
|
-- not enough parts, add a section input to collect parts
|
|
-- this will probably prevent input of other parts.
|
|
if struct.rocket_sections < Launchpad.rocket_sections_per_rocket
|
|
and not main_inv.can_insert{name = Launchpad.name_rocket_section, count = 1} then
|
|
if not(struct.section_input and struct.section_input.valid) then
|
|
Launchpad.add_section_input(struct)
|
|
end
|
|
else
|
|
Launchpad.destroy_sub(struct, "section_input")
|
|
end
|
|
|
|
Launchpad.update_combinator(struct, main_inv)
|
|
|
|
if struct.rocket_sections >= Launchpad.rocket_sections_per_rocket and struct.crew_capsules >= Launchpad.crew_capsules_per_rocket
|
|
and not struct.has_inserted_dummy then
|
|
local inserted = struct.silo.insert{name = Launchpad.name_rocket_launch_pad_silo_dummy, count = 1}
|
|
if inserted == 1 then
|
|
struct.has_inserted_dummy = true
|
|
end
|
|
end
|
|
|
|
--game.print(profiler)
|
|
|
|
end
|
|
|
|
function Launchpad.launch(struct, skip_prep, allow_non_empty)
|
|
|
|
if struct.launch_status > -1 then return end -- already launching
|
|
if skip_prep ~= true then
|
|
Launchpad.prep(struct)
|
|
end
|
|
|
|
local destination = Launchpad.lock_destination(struct, allow_non_empty)
|
|
|
|
if destination then
|
|
local current_zone = struct.zone
|
|
local destination_zone = destination.zone
|
|
struct.launching_to_destination = destination
|
|
|
|
local required_fuel = Launchpad.get_fuel_cost(current_zone, destination_zone)
|
|
struct.required_fuel = required_fuel
|
|
|
|
-- the destination is locked so let's go
|
|
if destination.landing_pad then
|
|
destination.position = Util.vectors_add(destination.landing_pad.container.position, {x = 0, y = 2})
|
|
destination.landing_pad.inbound_rocket = true
|
|
end
|
|
|
|
-- set the contents
|
|
local main_inv = struct.container.get_inventory(defines.inventory.chest)
|
|
local main_contents = main_inv.get_contents()
|
|
struct.launched_contents = main_contents
|
|
-- make a global temp inventory.
|
|
-- swap items to that inventory.
|
|
-- make sure to destroy later
|
|
struct.launched_inventory = game.create_inventory(#main_inv)
|
|
Util.swap_inventories(main_inv, struct.launched_inventory)
|
|
main_inv.clear() -- chould already be empty anyway.
|
|
Log.trace("make launched_inventory with size" .. #struct.launched_inventory)
|
|
|
|
-- consume the fuel
|
|
local lua_fuel_consumed = math.min(struct.lua_fuel, required_fuel)
|
|
struct.lua_fuel = struct.lua_fuel - lua_fuel_consumed
|
|
if lua_fuel_consumed < required_fuel then
|
|
local fluidbox = struct.tank.fluidbox[1]
|
|
local tank_fuel_consumed = math.min(required_fuel - lua_fuel_consumed, fluidbox.amount)
|
|
fluidbox.amount = math.max(1, fluidbox.amount - tank_fuel_consumed)
|
|
struct.tank.fluidbox[1] = fluidbox
|
|
struct.total_fuel = struct.lua_fuel + fluidbox.amount
|
|
end
|
|
if struct.tank and struct.tank.fluidbox and struct.tank.fluidbox[1] and struct.tank.fluidbox[1].name then
|
|
struct.container.force.fluid_production_statistics.on_flow(struct.tank.fluidbox[1].name, -required_fuel)
|
|
end
|
|
|
|
-- consume rocket parts
|
|
struct.rocket_sections = struct.rocket_sections - Launchpad.rocket_sections_per_rocket
|
|
-- struct.container.force.item_production_statistics.on_flow(Launchpad.name_rocket_section, -Launchpad.rocket_sections_per_rocket)
|
|
|
|
-- consume crew capsule
|
|
struct.crew_capsules = struct.crew_capsules - Launchpad.crew_capsules_per_rocket
|
|
-- struct.container.force.item_production_statistics.on_flow(Capsule.name_space_capsule, -Launchpad.crew_capsules_per_rocket)
|
|
|
|
-- start charting the landing
|
|
if destination_zone.type ~= "spaceship" then
|
|
local range = Zone.discovery_scan_radius
|
|
struct.container.force.chart(Zone.get_make_surface(destination_zone), {
|
|
{destination.position.x - range, destination.position.y - range - Launchpad.landing_start_altitude},
|
|
{destination.position.x + range, destination.position.y + range}
|
|
})
|
|
end
|
|
-- set launch status
|
|
struct.launch_status = 1
|
|
|
|
script.raise_event(Launchpad.on_cargo_rocket_launched_event, {struct = struct})
|
|
|
|
Launchpad.update_combinator(struct, main_inv)
|
|
end
|
|
end
|
|
|
|
|
|
function Launchpad.tick(struct)
|
|
-- this should only be called once per 60 ticks during prep and every tick during launch
|
|
-- actually, launch should be handles seperatly.
|
|
-- make launches and capsule launches in to tick tasks
|
|
-- Launchpad.prep can be called every tick for gui updates
|
|
if not (struct.container and struct.container.valid) then
|
|
return Launchpad.destroy(struct)
|
|
end
|
|
|
|
if struct.launch_status < 1 then
|
|
|
|
Launchpad.prep(struct)
|
|
|
|
-- if struct.required_fuel then the destination must be valid
|
|
if struct.required_fuel and struct.total_fuel >= struct.required_fuel
|
|
and struct.rocket_sections >= Launchpad.rocket_sections_per_rocket
|
|
and struct.crew_capsules >= Launchpad.crew_capsules_per_rocket then
|
|
-- can launch
|
|
-- TRIGGERS
|
|
|
|
local destination = nil
|
|
if struct.launch_trigger == "fuel-full" then
|
|
-- we already know fuel is full so launch
|
|
Launchpad.launch(struct, true)
|
|
elseif struct.launch_trigger == "cargo-full" then
|
|
local main_inv = struct.container.get_inventory(defines.inventory.chest)
|
|
if main_inv then
|
|
if not main_inv.can_insert({name = Launchpad.name_rocket_section, count = 1}) then
|
|
-- cargo is full so try launch
|
|
Launchpad.launch(struct, true)
|
|
end
|
|
end
|
|
elseif struct.launch_trigger == "fuel-full-signal" then
|
|
if struct.container.get_circuit_network(defines.wire_type.red).get_signal(Launchpad.signal_for_launch) > 0
|
|
or struct.container.get_circuit_network(defines.wire_type.green).get_signal(Launchpad.signal_for_launch) > 0
|
|
then
|
|
Launchpad.launch(struct, true)
|
|
end
|
|
elseif struct.launch_trigger == "cargo-full-signal" then
|
|
local main_inv = struct.container.get_inventory(defines.inventory.chest)
|
|
if main_inv then
|
|
if not main_inv.can_insert({name = Launchpad.name_rocket_section, count = 1}) then
|
|
if struct.container.get_circuit_network(defines.wire_type.red).get_signal(Launchpad.signal_for_launch) > 0
|
|
or struct.container.get_circuit_network(defines.wire_type.green).get_signal(Launchpad.signal_for_launch) > 0
|
|
then
|
|
Launchpad.launch(struct, true)
|
|
end
|
|
end
|
|
end
|
|
elseif struct.launch_trigger == "cargo-full-or-signal" then
|
|
local main_inv = struct.container.get_inventory(defines.inventory.chest)
|
|
if main_inv then
|
|
if not main_inv.can_insert({name = Launchpad.name_rocket_section, count = 1}) then
|
|
-- cargo is full so try launch
|
|
Launchpad.launch(struct, true)
|
|
else
|
|
if struct.container.get_circuit_network(defines.wire_type.red).get_signal(Launchpad.signal_for_launch) > 0
|
|
or struct.container.get_circuit_network(defines.wire_type.green).get_signal(Launchpad.signal_for_launch) > 0
|
|
then
|
|
-- signal so try launch
|
|
Launchpad.launch(struct, true)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
elseif struct.launch_status == 1 then
|
|
-- the rocket should be ready
|
|
if struct.silo.launch_rocket() then
|
|
--Log.trace("launching")
|
|
struct.launch_status = 2
|
|
struct.rocket_entity = struct.container.surface.find_entities_filtered{name=Launchpad.name_cargo_rocket, area=util.position_to_area(struct.container.position, 0.5)}[1]
|
|
-- the launch pad could get removed at this point and the rocket would need to keep going.
|
|
local tick_task = new_tick_task("launchpad-journey")
|
|
tick_task.struct = struct
|
|
tick_task.force_name = struct.force_name
|
|
tick_task.rocket_entity = struct.rocket_entity
|
|
tick_task.launch_timer = 0
|
|
tick_task.launched_contents = struct.launched_contents
|
|
tick_task.launched_inventory = struct.launched_inventory
|
|
Log.trace("Move launched_inventory to tick task " .. #tick_task.launched_inventory)
|
|
struct.launched_inventory = nil
|
|
tick_task.launching_to_destination = struct.launching_to_destination
|
|
tick_task.delta_v = Zone.get_travel_delta_v(tick_task.struct.zone, tick_task.launching_to_destination.zone)
|
|
|
|
local target_surface = Zone.get_make_surface(tick_task.launching_to_destination.zone)
|
|
target_surface.request_to_generate_chunks(Util.vectors_add(tick_task.launching_to_destination.position, {x = 0, y = Launchpad.landing_start_altitude}),
|
|
Launchpad.landing_start_altitude / 32 / 2)
|
|
-- start the launch animation
|
|
-- move the player seats over the rocket
|
|
-- follow rocket launch
|
|
-- if not landing_pad clear destination: remove biters
|
|
-- teleport over landing zone
|
|
-- if not landing_pad start descent / crash sequence
|
|
-- place landfill, place scaffold, place cargo Pods
|
|
-- place recoverable sections
|
|
-- destroy the seats to release the players
|
|
end
|
|
else
|
|
-- stays at struct.launch_status 2 until tick_task sets it back to -1
|
|
end
|
|
end
|
|
|
|
|
|
function Launchpad.drop_rocket_fragment(zone, size, name, position, start_position_x_offset)
|
|
local target_surface = Zone.get_make_surface(zone)
|
|
local falling_time
|
|
if math.random() < 0.5 then -- biggest first
|
|
falling_time = Launchpad.time_landing_debris_first
|
|
+ (size - math.random()) / 4 * (Launchpad.time_landing_debris_last - Launchpad.time_landing_debris_first)
|
|
else -- or just random
|
|
falling_time = Launchpad.time_landing_debris_first + math.random() * (Launchpad.time_landing_debris_last - Launchpad.time_landing_debris_first)
|
|
end
|
|
local landing_start_altitude = Launchpad.landing_start_altitude
|
|
if Zone.is_space(zone) then
|
|
landing_start_altitude = landing_start_altitude * 0.5
|
|
end
|
|
local random_start_offset = {x = 8 * (math.random() - 0.5), y = 8 * (math.random() - 0.5)}
|
|
local start_position = Util.vectors_add(position, {
|
|
x = random_start_offset.x + start_position_x_offset,
|
|
y = random_start_offset.y - landing_start_altitude})
|
|
local start_position_shadow = Util.vectors_add(position, {
|
|
x = random_start_offset.x + landing_start_altitude + start_position_x_offset,
|
|
y = random_start_offset.y})
|
|
|
|
local land_position = Util.vectors_add(position, {
|
|
x = random_start_offset.x + 32 * (math.random() - 0.5),
|
|
y = random_start_offset.y + 32 * (math.random() - 0.5)})
|
|
|
|
local flat_distance = Util.vectors_delta_length(start_position, land_position)
|
|
local flat_distance_shadow = Util.vectors_delta_length(start_position_shadow, land_position)
|
|
local speed = flat_distance / falling_time
|
|
local speed_shadow = flat_distance_shadow / falling_time
|
|
|
|
if Zone.is_space(zone) then
|
|
target_surface.create_entity{
|
|
name = mod_prefix .. "space-" ..name,
|
|
position = start_position,
|
|
target = land_position,
|
|
speed = speed,
|
|
--max_rage = flat_distance,
|
|
}
|
|
else
|
|
target_surface.create_entity{
|
|
name = mod_prefix .. "falling-" ..name,
|
|
position = start_position,
|
|
target = land_position,
|
|
speed = speed,
|
|
max_rage = flat_distance,
|
|
}
|
|
target_surface.create_entity{
|
|
name = mod_prefix .. "shadow-" ..name,
|
|
position = start_position_shadow,
|
|
target = land_position,
|
|
speed = speed_shadow,
|
|
max_rage = flat_distance_shadow,
|
|
}
|
|
end
|
|
end
|
|
|
|
function Launchpad.tick_journey(tick_task)
|
|
-- from tick_task.type = "launchpad-journey"
|
|
if tick_task.launch_timer then
|
|
tick_task.launch_timer = tick_task.launch_timer + 1
|
|
|
|
local struct = tick_task.struct
|
|
if struct and struct.valid then
|
|
if tick_task.launch_timer <= Launchpad.time_takeoff then
|
|
-- collect passengers while rocket is low
|
|
tick_task.passengers = tick_task.passengers or {} -- characters not players
|
|
for _, seat in pairs(struct.seats) do
|
|
if seat.get_driver() then
|
|
table.insert(tick_task.passengers, seat.get_driver())
|
|
if remote.interfaces["jetpack"] and remote.interfaces["jetpack"]["block_jetpack"] then
|
|
remote.call("jetpack", "block_jetpack", {character=seat.get_driver()})
|
|
end
|
|
seat.set_driver(nil)
|
|
end
|
|
if seat.get_passenger() then
|
|
table.insert(tick_task.passengers, seat.get_passenger())
|
|
if remote.interfaces["jetpack"] and remote.interfaces["jetpack"]["block_jetpack"] then
|
|
remote.call("jetpack", "block_jetpack", {character=seat.get_passenger()})
|
|
end
|
|
seat.set_passenger(nil)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if tick_task.rocket_entity and tick_task.rocket_entity.valid then
|
|
-- move passengers with the rocket
|
|
if tick_task.passengers then
|
|
for _, passenger in pairs(tick_task.passengers) do
|
|
if passenger and passenger.valid then
|
|
if remote.interfaces["jetpack"] and remote.interfaces["jetpack"]["block_jetpack"] then
|
|
remote.call("jetpack", "block_jetpack", {character=passenger})
|
|
end
|
|
local target_position = {
|
|
x = tick_task.rocket_entity.position.x,
|
|
y = tick_task.rocket_entity.position.y - 2 - math.max(0, tick_task.launch_timer - Launchpad.time_takeoff) / 64}
|
|
passenger.teleport(target_position)
|
|
passenger.destructible = false
|
|
--passenger.teleport(Util.move_to(passenger.position, target_position, tick_task.launch_timer / 100) )
|
|
end
|
|
end
|
|
end
|
|
|
|
if tick_task.launch_timer >= Launchpad.time_takeoff_finish_ascent then
|
|
-- skip to next phase
|
|
tick_task.launch_timer = nil
|
|
end
|
|
|
|
else
|
|
-- skip to next phase
|
|
tick_task.launch_timer = nil
|
|
end
|
|
|
|
else
|
|
if not tick_task.land_timer then
|
|
tick_task.land_timer = 0
|
|
-- setup next landing sequence
|
|
|
|
if tick_task.rocket_entity and tick_task.rocket_entity.valid then
|
|
tick_task.rocket_entity.destroy()
|
|
tick_task.rocket_entity = nil
|
|
end
|
|
-- reset the landing pad
|
|
local struct = tick_task.struct
|
|
if struct and struct.valid then
|
|
struct.launched_contents = nil
|
|
if struct.launched_inventory and struct.launched_inventory.valid then
|
|
struct.launched_inventory.destroy()
|
|
struct.launched_inventory = nil
|
|
end
|
|
struct.launch_status = -1
|
|
struct.launching_to_destination = nil
|
|
struct.has_inserted_dummy = nil
|
|
global.forces[tick_task.force_name].cargo_rockets_launched = (global.forces[tick_task.force_name].cargo_rockets_launched or 0) + 1
|
|
end
|
|
|
|
--game.print("Dev note: Show capsule landing sequence and discarded rocket part debris falling from the sky.")
|
|
|
|
-- determine success rate
|
|
|
|
-- for landing pad:
|
|
-- show capsule descent over the pad, then put capsule inside the pad and player outside.
|
|
-- show up to 10 cargo pods the fly past to the landing pad first.
|
|
-- failed cargo pods are added to crash debris.
|
|
-- crash debris based on recovery tech.
|
|
-- put some recovered parts in the landing pad based on recovery tech.
|
|
-- no fire from spilt fuel
|
|
|
|
--for general location or guidance failure
|
|
-- show capsule descent at random locaiton then put player outside.
|
|
-- show up to 10 cargo pods the fly past to random locations
|
|
-- failed cargo pods are added to crash debris.
|
|
-- all rocket parts added to crash debris regardless of recovery tech.
|
|
-- add fire from spilt fuel
|
|
-- add some wider area explosive damage
|
|
|
|
local is_landingpad = (tick_task.launching_to_destination.landing_pad and tick_task.launching_to_destination.landing_pad.container and tick_task.launching_to_destination.landing_pad.container.valid)
|
|
if is_landingpad then
|
|
local error_chance = Launchpad.get_survivability_loss(game.forces[tick_task.force_name], tick_task.delta_v)
|
|
if math.random() < error_chance then
|
|
-- off course
|
|
is_landingpad = false
|
|
local force = tick_task.launching_to_destination.landing_pad.container.force
|
|
force.item_production_statistics.on_flow(Launchpad.name_rocket_section, -Launchpad.rocket_sections_per_rocket)
|
|
tick_task.launching_to_destination.landing_pad.inbound_rocket = nil
|
|
tick_task.launching_to_destination.attempted_position = tick_task.launching_to_destination.landing_pad.container.position
|
|
tick_task.launching_to_destination.attempted_landing_pad = tick_task.launching_to_destination.landing_pad
|
|
tick_task.launching_to_destination.landing_pad = nil
|
|
local new_pos = tick_task.launching_to_destination.position
|
|
new_pos.x = new_pos.x + (math.random() - 0.5) * 150
|
|
new_pos.y = new_pos.y + (math.random() - 0.5) * 128
|
|
tick_task.launching_to_destination.position = Zone.find_zone_landing_position(tick_task.launching_to_destination.zone, new_pos)
|
|
force.print({"space-exploration.rocket_survivability_fail", "[gps="..math.floor(new_pos.x)..","..math.floor(new_pos.y)..","..Zone.get_surface_name(tick_task.launching_to_destination.zone).."]"})
|
|
|
|
end
|
|
end
|
|
|
|
local cargo_loss = Launchpad.get_cargo_loss(game.forces[tick_task.force_name], tick_task.delta_v)
|
|
if is_landingpad then
|
|
cargo_loss = cargo_loss / 2
|
|
end
|
|
local keep_percent = 1 - cargo_loss
|
|
if tick_task.launched_inventory and tick_task.launched_inventory.valid then
|
|
Log.trace("Loss launched_inventory")
|
|
-- remove items from the inventory directly
|
|
|
|
for item_name, item_count in pairs(tick_task.launched_contents) do
|
|
local item_type = game.item_prototypes[item_name]
|
|
if item_type and item_count > 1 and item_type.stack_size > 1 then
|
|
local loss = cargo_loss * 2 * math.random() -- +/- 100%
|
|
local lost_items = math.floor(item_count * loss)
|
|
if lost_items > 0 then
|
|
tick_task.launched_inventory.remove({name = item_name, count = lost_items})
|
|
end
|
|
end
|
|
end
|
|
|
|
else -- old method for legacy launches in progress.
|
|
Log.trace("Loss safe_contents")
|
|
tick_task.safe_contents = {}
|
|
for item_type, item_count in pairs(tick_task.launched_contents) do
|
|
local item_type = game.item_prototypes[item_name]
|
|
if item_type and item_count > 1 and item_type.stack_size > 1 then
|
|
local loss = 2 * cargo_loss * math.random() -- +/- 100%
|
|
-- keep 10% at a minimum
|
|
tick_task.safe_contents[item_type] = math.max(math.ceil(item_count / 10), item_count - item_count * loss)
|
|
else
|
|
tick_task.safe_contents[item_type] = item_count
|
|
end
|
|
end
|
|
end
|
|
|
|
-- cargo loss should never go above 90%, it can be below 10% round up for number of crashing pods.
|
|
local total_pods = 10
|
|
local crashing_pods = math.floor(total_pods * cargo_loss + 0.49)
|
|
local safe_pods = total_pods - crashing_pods
|
|
local pods = {}
|
|
|
|
local target_surface = Zone.get_make_surface(tick_task.launching_to_destination.zone)
|
|
Zone.apply_markers(tick_task.launching_to_destination.zone) -- in case the surface exists
|
|
|
|
for i = 1, safe_pods do
|
|
table.insert(pods, {type = "safe"})
|
|
end
|
|
for i = 1, crashing_pods do
|
|
table.insert(pods, {type = "crash"})
|
|
end
|
|
pods = Util.shuffle(pods)
|
|
|
|
local landing_start_altitude = Launchpad.landing_start_altitude
|
|
if Zone.is_space(tick_task.launching_to_destination.zone) then
|
|
landing_start_altitude = landing_start_altitude * 0.5
|
|
end
|
|
local start_position_x_offset = landing_start_altitude * (math.random() - 0.5) -- gives an angle to all pieces
|
|
local start_position = Util.vectors_add(tick_task.launching_to_destination.position, {
|
|
x = start_position_x_offset,
|
|
y = - landing_start_altitude})
|
|
local start_position_shadow = Util.vectors_add(tick_task.launching_to_destination.position, {
|
|
x = start_position_x_offset + landing_start_altitude,
|
|
y = 0})
|
|
|
|
tick_task.safe_pods = {}
|
|
for i, pod in pairs(pods) do
|
|
|
|
local falling_time = Launchpad.time_landing_cargopod_first
|
|
+ (Launchpad.time_landing_cargopod_last - Launchpad.time_landing_cargopod_first) * (i - 1) / (total_pods - 1)
|
|
local land_position
|
|
if pod.type == "safe" and is_landingpad then
|
|
land_position = tick_task.launching_to_destination.position
|
|
else
|
|
land_position = Util.vectors_add(tick_task.launching_to_destination.position, {x = 32 * (math.random() - 0.5), y = 32 * (math.random() - 0.5)})
|
|
end
|
|
local flat_distance = Util.vectors_delta_length(start_position, land_position)
|
|
local flat_distance_shadow = Util.vectors_delta_length(start_position_shadow, land_position)
|
|
local speed = flat_distance / falling_time
|
|
local speed_shadow = flat_distance_shadow / falling_time
|
|
|
|
if pod.type == "safe" then
|
|
-- track the safe ones for adding items
|
|
local entity = target_surface.create_entity{
|
|
name = mod_prefix .. "falling-cargo-pod",
|
|
position = start_position,
|
|
target = land_position,
|
|
speed = speed,
|
|
max_rage = flat_distance,
|
|
}
|
|
entity.orientation = 0
|
|
table.insert(tick_task.safe_pods, {falling_entity=entity, land_position = land_position})
|
|
target_surface.create_entity{
|
|
name = mod_prefix .. "shadow-cargo-pod",
|
|
position = start_position_shadow,
|
|
target = land_position,
|
|
speed = speed_shadow,
|
|
max_rage = flat_distance_shadow,
|
|
}
|
|
else
|
|
target_surface.create_entity{
|
|
name = mod_prefix .. "falling-" .. Launchpad.names_cargo_fragment[(i % #Launchpad.names_cargo_fragment) + 1],
|
|
position = start_position,
|
|
target = land_position,
|
|
speed = speed,
|
|
max_rage = flat_distance,
|
|
}
|
|
target_surface.create_entity{
|
|
name = mod_prefix .. "shadow-" .. Launchpad.names_cargo_fragment[(i % #Launchpad.names_cargo_fragment) + 1],
|
|
position = start_position_shadow,
|
|
target = land_position,
|
|
speed = speed_shadow,
|
|
max_rage = flat_distance_shadow,
|
|
}
|
|
end
|
|
end
|
|
|
|
if not is_landingpad then
|
|
for i = 1, Launchpad.rocket_fragments_large do
|
|
Launchpad.drop_rocket_fragment(tick_task.launching_to_destination.zone, 4, Launchpad.names_rocket_fragments_large[(i % #Launchpad.names_rocket_fragments_large) + 1],
|
|
tick_task.launching_to_destination.position,
|
|
start_position_x_offset)
|
|
end
|
|
for i = 1, Launchpad.rocket_fragments_medium do
|
|
Launchpad.drop_rocket_fragment(tick_task.launching_to_destination.zone, 3, Launchpad.names_rocket_fragments_medium[(i % #Launchpad.names_rocket_fragments_medium) + 1],
|
|
tick_task.launching_to_destination.position,
|
|
start_position_x_offset)
|
|
end
|
|
for i = 1, Launchpad.rocket_fragments_small do
|
|
Launchpad.drop_rocket_fragment(tick_task.launching_to_destination.zone, 2, Launchpad.names_rocket_fragments_small[(i % #Launchpad.names_rocket_fragments_small) + 1],
|
|
tick_task.launching_to_destination.position,
|
|
start_position_x_offset)
|
|
end
|
|
for i = 1, Launchpad.rocket_fragments_tiny do
|
|
Launchpad.drop_rocket_fragment(tick_task.launching_to_destination.zone, 1, Launchpad.names_rocket_fragments_tiny[(i % #Launchpad.names_rocket_fragments_tiny) + 1],
|
|
tick_task.launching_to_destination.position,
|
|
start_position_x_offset)
|
|
end
|
|
end
|
|
|
|
-- capsule
|
|
tick_task.capsule = target_surface.create_entity{
|
|
name = Capsule.name_space_capsule_vehicle,
|
|
position=start_position,
|
|
force = tick_task.force_name
|
|
}
|
|
tick_task.capsule.destructible = false
|
|
tick_task.capsule_shadow = target_surface.create_entity{name = Capsule.name_space_capsule_vehicle_shadow, position=start_position_shadow, force = "neutral"}
|
|
tick_task.capsule_light = target_surface.create_entity{name = Capsule.name_space_capsule_vehicle_light, position=start_position,
|
|
speed = 0, target = tick_task.capsule}
|
|
|
|
if tick_task.passengers then
|
|
for _, passenger in pairs(tick_task.passengers) do
|
|
if passenger and passenger.valid then
|
|
-- this disassociates the character
|
|
if passenger.player then
|
|
LaunchpadGUI.gui_close(passenger.player)
|
|
end
|
|
tick_task.passengers[_] = teleport_character_to_surface(passenger, target_surface, tick_task.capsule.position)
|
|
if tick_task.passengers[_] then
|
|
tick_task.passengers[_].destructible = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- end landing sequence setup
|
|
else
|
|
-- continuation for landing sqequence
|
|
tick_task.land_timer = tick_task.land_timer + 1
|
|
|
|
local time_remaining = math.max(1, Launchpad.time_landing_capsule_touchdown - tick_task.land_timer)
|
|
local target_pos = tick_task.launching_to_destination.position
|
|
local capsule_target_pos = tick_task.launching_to_destination.attempted_position or target_pos
|
|
|
|
--local travel = Util.vector_multiply(Util.vectors_delta(tick_task.capsule.position, target_pos), 1 / time_remaining)
|
|
--local travel_shadow = Util.vector_multiply(Util.vectors_delta(tick_task.capsule_shadow.position, target_pos), 1 / time_remaining)
|
|
|
|
if not(tick_task.capsule and tick_task.capsule.valid) then
|
|
local target_surface = Zone.get_make_surface(tick_task.launching_to_destination.zone)
|
|
tick_task.capsule = target_surface.create_entity{
|
|
name = Capsule.name_space_capsule_vehicle,
|
|
position={x = capsule_target_pos.x, y = capsule_target_pos.y - Launchpad.landing_start_altitude * time_remaining / Launchpad.time_landing_capsule_touchdown},
|
|
force = tick_task.force_name
|
|
}
|
|
end
|
|
tick_task.capsule.destructible = true
|
|
|
|
local travel = Util.vector_multiply(Util.vectors_delta(tick_task.capsule.position, capsule_target_pos), math.min(0.9, 2 / time_remaining))
|
|
local animation_speed = 1/3
|
|
local animation_frames = 24
|
|
local animation_frame = math.max(math.min(math.floor(time_remaining * animation_speed), animation_frames), 1)
|
|
|
|
tick_task.capsule.teleport(Util.vectors_add(tick_task.capsule.position, travel))
|
|
tick_task.capsule.orientation = (animation_frame - 1) / animation_frames
|
|
|
|
if tick_task.capsule_light and tick_task.capsule_light.valid then
|
|
tick_task.capsule_light.teleport(tick_task.capsule.position)
|
|
end
|
|
|
|
if tick_task.capsule_shadow and tick_task.capsule_shadow.valid then
|
|
local travel_shadow = Util.vector_multiply(Util.vectors_delta(tick_task.capsule_shadow.position, capsule_target_pos), math.min(0.9, 2 / time_remaining))
|
|
tick_task.capsule_shadow.teleport(Util.vectors_add(tick_task.capsule_shadow.position, travel_shadow))
|
|
tick_task.capsule_shadow.graphics_variation = animation_frame
|
|
end
|
|
|
|
if tick_task.passengers then
|
|
for _, passenger in pairs(tick_task.passengers) do
|
|
if passenger and passenger.valid then
|
|
passenger.teleport({x = tick_task.capsule.position.x, y = tick_task.capsule.position.y - 0.2})
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, safe_pod in pairs(tick_task.safe_pods) do
|
|
if safe_pod.falling_entity then
|
|
if not safe_pod.falling_entity.valid then
|
|
safe_pod.falling_entity = nil
|
|
if tick_task.launching_to_destination.landing_pad and tick_task.launching_to_destination.landing_pad.valid then
|
|
--tick_task.launching_to_destination.landing_pad.container.insert{name = Launchpad.name_cargo_pod, count = 1} -- we have rocket parts instead
|
|
--tick_task.launching_to_destination.landing_pad.container.force.item_production_statistics.on_flow(Launchpad.name_cargo_pod, 1)
|
|
else
|
|
safe_pod.cargo_entity = tick_task.capsule.surface.create_entity{
|
|
name = Launchpad.name_cargo_pod,
|
|
position = safe_pod.land_position,
|
|
force = tick_task.force_name
|
|
}
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
if tick_task.land_timer > Launchpad.time_landing_capsule_touchdown then
|
|
|
|
if tick_task.launching_to_destination.landing_pad and tick_task.launching_to_destination.landing_pad.valid then
|
|
-- going to a landing pad
|
|
tick_task.launching_to_destination.landing_pad.container.insert{name = Capsule.name_space_capsule, count = 1}
|
|
--tick_task.launching_to_destination.landing_pad.container.force.item_production_statistics.on_flow(Capsule.name_space_capsule, 1)
|
|
tick_task.capsule.destroy()
|
|
|
|
if tick_task.capsule_light and tick_task.capsule_light.valid then
|
|
tick_task.capsule_light.destroy()
|
|
end
|
|
|
|
if tick_task.capsule_shadow and tick_task.capsule_shadow.valid then
|
|
tick_task.capsule_shadow.destroy()
|
|
end
|
|
|
|
if tick_task.launched_inventory and tick_task.launched_inventory.valid then
|
|
Log.trace("Insert to pad launched_inventory")
|
|
local inv = tick_task.launching_to_destination.landing_pad.container.get_inventory(defines.inventory.chest)
|
|
for i = 1, #tick_task.launched_inventory do
|
|
local stack = tick_task.launched_inventory[i]
|
|
if stack and stack.valid and stack.valid_for_read and stack.count then
|
|
inv.insert(tick_task.launched_inventory[i])
|
|
end
|
|
end
|
|
tick_task.launched_inventory.clear()
|
|
tick_task.launched_inventory.destroy()
|
|
tick_task.launched_inventory = nil
|
|
elseif tick_task.safe_contents then -- legacy method
|
|
Log.trace("Insert to pad safe_contents")
|
|
for name, count in pairs(tick_task.safe_contents) do
|
|
tick_task.launching_to_destination.landing_pad.container.insert{name=name, count=count}
|
|
end
|
|
end
|
|
|
|
local reusability = Launchpad.get_reusability(game.forces[tick_task.force_name])
|
|
local reusable_parts = math.floor(math.min(Launchpad.rocket_sections_per_rocket, Launchpad.rocket_sections_per_rocket * reusability * ( 0.9 + 0.2 * math.random())))
|
|
if reusable_parts > 0 then
|
|
tick_task.launching_to_destination.landing_pad.container.insert{name = Launchpad.name_rocket_section, count = reusable_parts}
|
|
-- tick_task.launching_to_destination.landing_pad.container.force.item_production_statistics.on_flow(Launchpad.name_rocket_section, reusable_parts)
|
|
end
|
|
if reusable_parts < Launchpad.rocket_sections_per_rocket then
|
|
tick_task.launching_to_destination.landing_pad.container.force.item_production_statistics.on_flow(Launchpad.name_rocket_section, -(Launchpad.rocket_sections_per_rocket - reusable_parts))
|
|
end
|
|
|
|
tick_task.launching_to_destination.landing_pad.inbound_rocket = nil
|
|
else
|
|
|
|
if tick_task.launched_inventory and tick_task.launched_inventory.valid then
|
|
Log.trace("Distribute to pods launched_inventory")
|
|
|
|
local spidertrons = {"spidertron-spidertron-rocket-launcher-1", "spidertron"}
|
|
for _, spidertron_name in pairs(spidertrons) do
|
|
if tick_task.launched_contents[spidertron_name] and tick_task.launched_contents[spidertron_name] > 0 then
|
|
local pod = tick_task.safe_pods[#tick_task.safe_pods]
|
|
local spider = pod.cargo_entity.surface.create_entity{
|
|
name = spidertron_name,
|
|
position = pod.cargo_entity.position,
|
|
force = pod.cargo_entity.force,
|
|
raise_built = true
|
|
}
|
|
if spider then
|
|
tick_task.launched_contents[spidertron_name] = tick_task.launched_contents[spidertron_name] - 1
|
|
local spider_item = tick_task.launched_inventory.find_item_stack(spidertron_name)
|
|
if spider_item.grid then
|
|
Util.transfer_equipment_grid(spider_item, spider)
|
|
end
|
|
local inventory = spider_item.get_inventory(defines.inventory.item_main) or spider_item.get_inventory(defines.inventory.car_trunk)
|
|
local reserve_item_stacks = {
|
|
"construction-robot",
|
|
"logistic-chest-storage",
|
|
"roboport",
|
|
}
|
|
local take_item_stacks = {
|
|
"construction-robot",
|
|
"logistic-chest-storage",
|
|
"roboport",
|
|
"substation",
|
|
mod_prefix .. "pylon-substation",
|
|
"solar-panel",
|
|
mod_prefix .. "space-solar-panel-1",
|
|
mod_prefix .. "space-solar-panel-2",
|
|
mod_prefix .. "space-solar-panel-3",
|
|
"accumulator",
|
|
"laser-turret",
|
|
}
|
|
if inventory then
|
|
Util.transfer_inventory_filters_direct(spider_item.get_inventory(defines.inventory.item_main), spider.get_inventory(defines.inventory.car_trunk))
|
|
else -- item inv not supported yet?
|
|
local spider_inv = spider.get_inventory(defines.inventory.car_trunk)
|
|
for i, item in pairs(reserve_item_stacks) do
|
|
spider_inv.set_filter(i, item)
|
|
end
|
|
end
|
|
tick_task.launched_inventory.remove({name = spidertron_name, count = 1})
|
|
for _, item in pairs(take_item_stacks) do
|
|
if tick_task.launched_contents[item] and tick_task.launched_contents[item] > 0 then
|
|
local remove = math.min(tick_task.launched_contents[item], 50)
|
|
tick_task.launched_contents[item] = tick_task.launched_contents[item] - remove
|
|
tick_task.launched_inventory.remove({name = item, count = remove})
|
|
spider.insert({name = item, count = remove})
|
|
end
|
|
end
|
|
-- the auto-deconstruction is a problem for the spider, clear nearby deconstruction orders.
|
|
tick_task.spider = spider
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
for i = 1, #tick_task.launched_inventory do
|
|
for j = 0, #tick_task.safe_pods do
|
|
local pod = tick_task.safe_pods[((i + j) % #tick_task.safe_pods) + 1]
|
|
local stack = tick_task.launched_inventory[i]
|
|
if pod and pod.cargo_entity and pod.cargo_entity.valid and stack and stack.valid and stack.valid_for_read and stack.count then
|
|
local inserted = pod.cargo_entity.insert(tick_task.launched_inventory[i])
|
|
if inserted == stack.count then
|
|
tick_task.launched_inventory[i].clear()
|
|
else
|
|
tick_task.launched_inventory[i].count = stack.count - inserted
|
|
end
|
|
end
|
|
end
|
|
end
|
|
tick_task.launched_inventory.clear()
|
|
tick_task.launched_inventory.destroy()
|
|
tick_task.launched_inventory = nil
|
|
else -- legacy version
|
|
Log.trace("Distribute to pods safe_contents")
|
|
local i = 1
|
|
for name, count in pairs(tick_task.safe_contents) do
|
|
local remaining = count
|
|
for _, pod in pairs(tick_task.safe_pods) do
|
|
if pod and pod.cargo_entity and pod.cargo_entity.valid and remaining >= 1 then
|
|
remaining = remaining - pod.cargo_entity.insert{name=name, count=remaining}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, pod in pairs(tick_task.safe_pods) do
|
|
-- HERE
|
|
if pod and pod.cargo_entity and pod.cargo_entity.valid and not tick_task.spider then
|
|
pod.cargo_entity.order_deconstruction(tick_task.force_name)
|
|
end
|
|
end
|
|
|
|
if tick_task.launching_to_destination.attempted_landing_pad and tick_task.launching_to_destination.attempted_landing_pad.valid then
|
|
tick_task.launching_to_destination.attempted_landing_pad.container.insert{name = Capsule.name_space_capsule, count = 1}
|
|
-- tick_task.launching_to_destination.attempted_landing_pad.container.force.item_production_statistics.on_flow(Capsule.name_space_capsule, 1)
|
|
tick_task.capsule.destroy()
|
|
|
|
if tick_task.capsule_light and tick_task.capsule_light.valid then
|
|
tick_task.capsule_light.destroy()
|
|
end
|
|
|
|
if tick_task.capsule_shadow and tick_task.capsule_shadow.valid then
|
|
tick_task.capsule_shadow.destroy()
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
if tick_task.passengers then
|
|
for _, passenger in pairs(tick_task.passengers) do
|
|
if passenger and passenger.valid then
|
|
teleport_non_colliding(passenger, capsule_target_pos)
|
|
passenger.destructible = true
|
|
if passenger.player then
|
|
local playerdata = get_make_playerdata(passenger.player)
|
|
playerdata.zero_velocity = true
|
|
passenger.player.print({"space-exploration.respawn-if-stranded"})
|
|
end
|
|
if remote.interfaces["jetpack"] and remote.interfaces["jetpack"]["unblock_jetpack"] then
|
|
remote.call("jetpack", "unblock_jetpack", {character=passenger}) -- last otherwise the teleport breacks the chatacter reference
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if tick_task.capsule and tick_task.capsule.valid then tick_task.capsule.destructible = false end
|
|
tick_task.launched_contents = nil
|
|
if tick_task.launched_inventory and tick_task.launched_inventory.valid then
|
|
tick_task.launched_inventory.destroy()
|
|
end
|
|
tick_task.launched_inventory = nil
|
|
tick_task.safe_contents = nil
|
|
|
|
tick_task.valid = false -- close tick task
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
function Launchpad.on_player_driving_changed_state(event)
|
|
local player = game.players[event.player_index]
|
|
if player then
|
|
if player.vehicle and player.vehicle.name == Launchpad.name_rocket_launch_pad_seat then
|
|
|
|
if player.character then
|
|
remote.call("jetpack", "stop_jetpack_immediate", {character = player.character})
|
|
end
|
|
|
|
local armor_inv = player.get_inventory(defines.inventory.character_armor)
|
|
if not(armor_inv and armor_inv[1] and armor_inv[1].valid_for_read and Util.table_contains(name_thruster_suits, armor_inv[1].name)) then
|
|
player.print({"space-exploration.launch-suit-warning"})
|
|
end
|
|
|
|
local launch_pad_entity = player.surface.find_entities_filtered{
|
|
limit = 1,
|
|
area = util.position_to_area(util.vectors_add(player.position, {x = 0, y = -5}), 5),
|
|
name = Launchpad.name_rocket_launch_pad
|
|
}[1]
|
|
if launch_pad_entity then
|
|
LaunchpadGUI.gui_open(player, Launchpad.from_entity(launch_pad_entity))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_player_driving_changed_state, Launchpad.on_player_driving_changed_state)
|
|
|
|
--- Creates the composite entity when the laund pad is made
|
|
--- Does not handle clone_area in any acceptable manner
|
|
--- (it doesn't listen to on_cloned and even if it did it
|
|
--- would start duplicated entities)
|
|
--- TODO: maybe make it support cloning by get_make the entities
|
|
--- instead of making them every time
|
|
---@param event any
|
|
function Launchpad.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 == Launchpad.name_rocket_launch_pad then
|
|
local force_name = entity.force.name
|
|
|
|
local zone = Zone.from_surface(entity.surface)
|
|
|
|
if not zone then
|
|
return cancel_entity_creation(entity, event.player_index, "Invalid launch pad location")
|
|
end
|
|
|
|
local default_name = zone.name
|
|
local default_destination_zone = zone
|
|
if zone.orbit then
|
|
default_destination_zone = zone.orbit
|
|
elseif zone.parent and zone.parent.type ~= "star" then
|
|
default_destination_zone = zone.parent
|
|
end
|
|
local struct = {
|
|
type = Launchpad.name_rocket_launch_pad,
|
|
valid = true,
|
|
force_name = force_name,
|
|
unit_number = entity.unit_number,
|
|
container = entity,
|
|
name = default_name,
|
|
rocket_sections = 0,
|
|
crew_capsules = 0,
|
|
launch_trigger = "none",
|
|
lua_fuel = 0,
|
|
total_fuel = 0,
|
|
zone = zone,
|
|
destination = {
|
|
zone = default_destination_zone
|
|
},
|
|
launch_status = -1
|
|
}
|
|
|
|
global.rocket_launch_pads[entity.unit_number] = struct
|
|
Log.trace("Launchpad: launch_pad added")
|
|
|
|
Launchpad.name(struct) -- assigns to zone_assets
|
|
|
|
Launchpad.add_section_input(struct)
|
|
-- spawn combinator
|
|
struct.combinator = entity.surface.create_entity{
|
|
name = Launchpad.name_rocket_launch_pad_combinator,
|
|
force = entity.force,
|
|
position = {entity.position.x, entity.position.y}}
|
|
entity.connect_neighbour({wire = defines.wire_type.red, target_entity = struct.combinator})
|
|
entity.connect_neighbour({wire = defines.wire_type.green, target_entity = struct.combinator})
|
|
struct.combinator.destructible = false
|
|
|
|
-- spawn silo
|
|
struct.silo = entity.surface.create_entity{
|
|
name = Launchpad.name_rocket_launch_pad_silo,
|
|
force = entity.force,
|
|
position = {entity.position.x, entity.position.y + 1/32}} -- 1 px down to be in front of container
|
|
struct.silo.destructible = false
|
|
|
|
|
|
-- spawn storage tank
|
|
struct.tank = entity.surface.create_entity{
|
|
name = Launchpad.name_rocket_launch_pad_tank,
|
|
force = entity.force,
|
|
position = {entity.position.x, entity.position.y + 1}} -- 1 tile down to be in front of silo
|
|
struct.tank.fluidbox.set_filter(1, {name = name_fluid_rocket_fuel, force = true})
|
|
entity.connect_neighbour({wire = defines.wire_type.red, target_entity = struct.tank})
|
|
entity.connect_neighbour({wire = defines.wire_type.green, target_entity = struct.tank})
|
|
struct.tank.destructible = false
|
|
|
|
struct.seats = {}
|
|
-- spawn passenger seats
|
|
for i = -2, 2, 1 do
|
|
local seat = entity.surface.create_entity{
|
|
name = Launchpad.name_rocket_launch_pad_seat,
|
|
force = entity.force,
|
|
position = {entity.position.x + i, entity.position.y + 5.9}}
|
|
seat.destructible = false
|
|
table.insert(struct.seats, seat)
|
|
end
|
|
|
|
-- set settings
|
|
if event.tags then
|
|
if event.tags.name then
|
|
Launchpad.rename(struct, event.tags.name)
|
|
end
|
|
struct.launch_trigger = event.tags.launch_trigger
|
|
if event.tags.zone_name then
|
|
struct.destination.zone = Zone.from_name(event.tags.zone_name)
|
|
else
|
|
struct.destination.zone = nil
|
|
end
|
|
if event.tags.landing_pad_name then
|
|
struct.destination.landing_pad_name = event.tags.landing_pad_name
|
|
end
|
|
end
|
|
|
|
if event.player_index and game.players[event.player_index] and game.players[event.player_index].connected then
|
|
LaunchpadGUI.gui_open(game.players[event.player_index], struct)
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_built_entity, Launchpad.on_entity_created)
|
|
Event.addListener(defines.events.on_robot_built_entity, Launchpad.on_entity_created)
|
|
Event.addListener(defines.events.script_raised_built, Launchpad.on_entity_created)
|
|
Event.addListener(defines.events.script_raised_revive, Launchpad.on_entity_created)
|
|
|
|
function Launchpad.get_struct_type_table(struct)
|
|
local zone_assets = Zone.get_force_assets(struct.force_name, struct.zone.index)
|
|
if struct.type == Launchpad.name_rocket_launch_pad then
|
|
zone_assets.rocket_launch_pad_names = zone_assets.rocket_launch_pad_names or {}
|
|
return zone_assets.rocket_launch_pad_names
|
|
end
|
|
end
|
|
|
|
function Launchpad.remove_struct_from_table(struct)
|
|
local type_table = Launchpad.get_struct_type_table(struct)
|
|
if not type_table[struct.name] then return end
|
|
type_table[struct.name][struct.unit_number] = nil
|
|
local count_remaining = 0
|
|
for _, remaining in pairs(type_table[struct.name]) do
|
|
count_remaining = count_remaining + 1
|
|
end
|
|
if count_remaining == 0 then
|
|
type_table[struct.name] = nil
|
|
end
|
|
end
|
|
|
|
function Launchpad.destroy_sub(struct, key)
|
|
if struct[key] and struct[key].valid then
|
|
struct[key].destroy()
|
|
struct[key] = nil
|
|
end
|
|
end
|
|
|
|
function Launchpad.destroy(struct, player_index)
|
|
if not struct then
|
|
Log.trace("struct_destroy: no struct")
|
|
return
|
|
end
|
|
|
|
struct.valid = false
|
|
|
|
local capsules = struct.crew_capsules or 0
|
|
local sections = struct.rocket_sections or 0
|
|
|
|
if player_index then
|
|
local player = game.players[player_index]
|
|
if player and player.connected then
|
|
if capsules > 0 then
|
|
local inserted = player.insert{name = Capsule.name_space_capsule, count = capsules}
|
|
capsules = capsules - inserted
|
|
end
|
|
if sections > 0 then
|
|
local inserted = player.insert{name = Launchpad.name_rocket_section, count = sections}
|
|
sections = sections - inserted
|
|
end
|
|
end
|
|
end
|
|
|
|
if struct.container and struct.container.valid then
|
|
|
|
local position = struct.container.position
|
|
local surface = struct.container.surface
|
|
|
|
if capsules > 0 then
|
|
surface.spill_item_stack(position, {name = Capsule.name_space_capsule, count = capsules}, true, struct.container.force, false)
|
|
end
|
|
|
|
if sections > 0 then
|
|
surface.spill_item_stack(position, {name = Launchpad.name_rocket_section, count = sections}, true, struct.container.force, false)
|
|
end
|
|
end
|
|
|
|
Launchpad.destroy_sub(struct, 'container')
|
|
Launchpad.destroy_sub(struct, 'section_input')
|
|
Launchpad.destroy_sub(struct, 'tank')
|
|
Launchpad.destroy_sub(struct, 'silo')
|
|
Launchpad.destroy_sub(struct, 'combinator')
|
|
for _, seat in pairs(struct.seats or {}) do
|
|
if seat and seat.valid then seat.destroy() end
|
|
end
|
|
struct.seats = nil
|
|
Launchpad.remove_struct_from_table(struct)
|
|
global.rocket_launch_pads[struct.unit_number] = nil
|
|
|
|
-- if a player has this gui open then close it
|
|
local gui_name = LaunchpadGUI.name_rocket_launch_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
|
|
root.destroy()
|
|
end
|
|
end
|
|
end
|
|
|
|
function Launchpad.name(struct, new_name)
|
|
struct.name = (new_name or struct.name)
|
|
local type_table = Launchpad.get_struct_type_table(struct)
|
|
type_table[struct.name] = type_table[struct.name] or {}
|
|
type_table[struct.name][struct.unit_number] = struct
|
|
end
|
|
|
|
function Launchpad.rename(struct, new_name)
|
|
local old_name = struct.name
|
|
Launchpad.remove_struct_from_table(struct)
|
|
Launchpad.name(struct, new_name)
|
|
end
|
|
|
|
function Launchpad.on_entity_removed(event)
|
|
local entity = event.entity
|
|
if entity and entity.valid and entity.name == Launchpad.name_rocket_launch_pad then
|
|
Launchpad.destroy(Launchpad.from_entity(entity), event.player_index )
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_entity_died, Launchpad.on_entity_removed)
|
|
Event.addListener(defines.events.on_robot_mined_entity, Launchpad.on_entity_removed)
|
|
Event.addListener(defines.events.on_player_mined_entity, Launchpad.on_entity_removed)
|
|
Event.addListener(defines.events.script_raised_destroy, Launchpad.on_entity_removed)
|
|
|
|
function Launchpad.on_tick(struct)
|
|
-- handle launchpads
|
|
for _, struct in pairs(global.rocket_launch_pads) do
|
|
if (struct.launch_status and struct.launch_status > 0) or (game.tick + struct.unit_number) % 60 == 0 then
|
|
Launchpad.tick(struct)
|
|
end
|
|
end
|
|
|
|
-- update guis
|
|
if game.tick % 60 == 0 then
|
|
for _, player in pairs(game.connected_players) do
|
|
LaunchpadGUI.gui_update(player)
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_tick, Launchpad.on_tick)
|
|
|
|
--- Handles the player creating a blueprint by setting tags to store the state of launch pads
|
|
---@param event any
|
|
function Launchpad.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 == Launchpad.name_rocket_launch_pad then
|
|
local entity = mapping[blueprint_entity.entity_number]
|
|
if entity then
|
|
local launch_pad = Launchpad.from_entity(entity)
|
|
if launch_pad then
|
|
local tags = {}
|
|
tags.name = launch_pad.name
|
|
tags.launch_trigger = launch_pad.launch_trigger
|
|
if launch_pad.destination.zone then
|
|
tags.zone_name = launch_pad.destination.zone.name
|
|
end
|
|
if launch_pad.destination.landing_pad_name then
|
|
tags.landing_pad_name = launch_pad.destination.landing_pad_name
|
|
end
|
|
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, Launchpad.on_player_setup_blueprint)
|
|
|
|
|
|
--- Handles the player copy/pasting settings between launch pads
|
|
---@param event any
|
|
function Launchpad.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 == Launchpad.name_rocket_launch_pad) then return end
|
|
if not (event.destination.name == Launchpad.name_rocket_launch_pad) then return end
|
|
local player = game.players[player_index]
|
|
local launch_pad_from = Launchpad.from_entity(event.source)
|
|
local launch_pad_to = Launchpad.from_entity(event.destination)
|
|
if launch_pad_from and launch_pad_to then
|
|
-- actual settings copy
|
|
Launchpad.rename(launch_pad_to, launch_pad_from.name)
|
|
launch_pad_to.launch_trigger = launch_pad_from.launch_trigger
|
|
launch_pad_to.destination.zone = launch_pad_from.destination.zone
|
|
launch_pad_to.destination.landing_pad_name = launch_pad_from.destination.landing_pad_name
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_entity_settings_pasted, Launchpad.on_entity_settings_pasted)
|
|
|
|
function Launchpad.on_init(event)
|
|
global.rocket_launch_pads = {}
|
|
end
|
|
Event.addListener("on_init", Launchpad.on_init, true)
|
|
|
|
return Launchpad
|
|
|