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.

3719 lines
156 KiB

local Spaceship = {}
Spaceship.names_tech_integrity = {
{name = mod_prefix.."spaceship-integrity", bonus_per_level = 100, infinite = false},
{name = mod_prefix.."factory-spaceship", bonus_per_level = 500, infinite = true}
}
Spaceship.integrity_base = 300
Spaceship.name_spaceship_console = mod_prefix .. "spaceship-console"
Spaceship.name_spaceship_console_output = mod_prefix .. "spaceship-console-output"
Spaceship.console_output_offset = {x = 1.5, y = -1}
Spaceship.name_spaceship_clamp_keep = mod_prefix .. "spaceship-clamp"
Spaceship.name_spaceship_clamp_place = mod_prefix .. "spaceship-clamp-place"
Spaceship.engines = {
[mod_prefix .. "spaceship-rocket-engine"] = { name = mod_prefix .. "spaceship-rocket-engine", thrust = 100 / 5, max_energy = 1837 },
[mod_prefix .. "spaceship-antimatter-engine"]= { name = mod_prefix .. "spaceship-antimatter-engine", thrust = 500 / 5, max_energy = 18370 }
}
Spaceship.names_engines = {}
Spaceship.names_booster_tanks = {
mod_prefix .. "spaceship-rocket-booster-tank",
mod_prefix .. "spaceship-antimatter-booster-tank"
}
--[[
Spaceship.name_spaceship_rocket_engine = mod_prefix .. "spaceship-rocket-engine"
Spaceship.name_spaceship_rocket_booster_tank = mod_prefix .. "spaceship-rocket-booster-tank"
Spaceship.rocket_engine_thrust = 100
Spaceship.rocket_engine_thrust_full_energy = 1837 -- the energy the furnaces contains when fully powered
Spaceship.name_spaceship_antimatter_engine = mod_prefix .. "spaceship-antimatter-engine"
Spaceship.name_spaceship_antimatter_booster_tank = mod_prefix .. "spaceship-antimatter-booster-tank"
Spaceship.antimatter_engine_thrust = 200
Spaceship.antimatter_engine_thrust_full_energy = 18370 -- the energy the furnaces contains when fully powered
]]--
Spaceship.names_spaceship_floors = {mod_prefix .. "spaceship-floor"}
Spaceship.names_spaceship_walls = {mod_prefix .. "spaceship-wall"}
Spaceship.names_spaceship_gates = {mod_prefix .. "spaceship-gate"}
Spaceship.names_spaceship_bulkheads = {
mod_prefix .. "spaceship-wall",
mod_prefix .. "spaceship-gate",
Spaceship.name_spaceship_clamp_keep,
}
for _, engine in pairs(Spaceship.engines) do
table.insert(Spaceship.names_engines, engine.name)
table.insert(Spaceship.names_spaceship_bulkheads, engine.name)
end
Spaceship.signal_for_own_spaceship_id = {type = "item", name = Spaceship.name_spaceship_console}
Spaceship.signal_for_destination_spaceship = {type = "virtual", name = mod_prefix.."spaceship"}
Spaceship.signal_for_speed = {type = "virtual", name = "signal-speed"}
Spaceship.signal_for_distance = {type = "virtual", name = "signal-distance"}
Spaceship.signal_for_launch = {type = "virtual", name = mod_prefix.."spaceship-launch"}
Spaceship.signal_for_anchor_using_left = {type = "virtual", name = mod_prefix.."anchor-using-left-clamp"}
Spaceship.signal_for_anchor_using_right = {type = "virtual", name = mod_prefix.."anchor-using-right-clamp"}
Spaceship.signal_for_anchor_to_left = {type = "virtual", name = mod_prefix.."anchor-to-left-clamp"}
Spaceship.signal_for_anchor_to_right = {type = "virtual", name = mod_prefix.."anchor-to-right-clamp"}
Spaceship.energy_per_launch_integrity_delta_v = 150 * 1000
Spaceship.name_spaceship_gui_root = mod_prefix.."spaceship-gui"
Spaceship.name_window_close = "spaceship_close_button"
Spaceship.types_to_restore = {-- after surface change/area clone
"inserter",
"pump",
--"transport-belt" -- entity.active does not work on belts
}
-- Note: production machines should NOT be included as some are supposed to be disabled on specific surfaces.
Spaceship.time_to_restore = 1
Spaceship.particle_spawn_range = 24 -- same as laser turret range
Spaceship.ice_particle = { speed = 1/300 }
Spaceship.particles = {
["speck"] = { multiplier = 1/1000, speed = 1/100, min_speed = 0,
sprite_name = mod_prefix.."spaceship-speck-graphic"
},
["rock-small"] = { multiplier = 1/3000, speed = 1/300, min_speed = 5,
projectile_name = mod_prefix.."spaceship-obstacle-rock-small-projectile",
graphic_name = mod_prefix.."spaceship-obstacle-rock-small-graphic",
targetable_name = mod_prefix.."spaceship-obstacle-rock-small-targetable",
},
["rock-medium"] = { multiplier = 1/20000, speed = 1/400, min_speed = 15, destroys_floor = true,
projectile_name = mod_prefix.."spaceship-obstacle-rock-medium-projectile",
graphic_name = mod_prefix.."spaceship-obstacle-rock-medium-graphic",
targetable_name = mod_prefix.."spaceship-obstacle-rock-medium-targetable",
},
["rock-large"] = { multiplier = 1/2000000, speed = 1/1000, min_speed = 50, min_size = 1000, destroys_floor = true,
projectile_name = mod_prefix.."spaceship-obstacle-rock-large-projectile",
graphic_name = mod_prefix.."spaceship-obstacle-rock-large-graphic",
targetable_name = mod_prefix.."spaceship-obstacle-rock-large-targetable",
},
}
Spaceship.all_targetables = {}
for _, particle in pairs(Spaceship.particles) do
if particle.targetable_name then
table.insert(Spaceship.all_targetables, particle.targetable_name)
end
end
Spaceship.obstacle_damage_multipliers = {
["electric-turret"] = 20,
["ammo-turret"] = 3
}
Spaceship.particle_speed_power = 0.75 -- 0.5 would be sqrt, 0 is static, 1 is linear with speed.
Spaceship.space_drag = 1/100 / 5
Spaceship.minimum_impulse = 1/100
Spaceship.minimum_mass = 1
Spaceship.speed_taper = 200
Spaceship.travel_speed_multiplier = 1/200
Spaceship.integrity_pulse_interval = 60 * 60 * 10
Spaceship.tile_status = {}
--[[
change to:
{
outer = 0 or 1. outer skin of tiles including diagonals
floor = 0 or 1, has floor otherwise exterior
exposed = 0 for contained, or higher for any tile exposed to space
wall = 0 or 1, walls only
bulkhead = 0 or 1, any bulkhead
connection nil or distance to console.
}
]]--
Spaceship.tile_status.exterior = 1 -- any tile not with flooring (without bulkhead)
Spaceship.tile_status.wall_exterior = 2 -- bulkeahd outside of flooring
Spaceship.tile_status.bulkhead_exterior = 3 -- bulkeahd outside of flooring
Spaceship.tile_status.floor = 4 -- unknown floor
Spaceship.tile_status.wall = 5 -- unknown bulkhead
Spaceship.tile_status.bulkhead = 6 -- unknown bulkhead
Spaceship.tile_status.floor_exterior = 7 -- outside floor
Spaceship.tile_status.floor_interior = 8 -- contained floor
Spaceship.tile_status.floor_console_disconnected = 9 -- disconnected floor
Spaceship.tile_status.wall_console_disconnected = 10 -- disconnected bulkhead
Spaceship.tile_status.bulkhead_console_disconnected = 11 -- disconnected bulkhead
Spaceship.tile_status.floor_console_connected = 12 -- connected floor
Spaceship.tile_status.wall_console_connected = 13 -- connected bulkhead
Spaceship.tile_status.bulkhead_console_connected = 14 -- connected bulkhead
--[[ tile statuses
1 = exterior
2 = floor_pending (on the edge of checking, used for next tick)
3 = unknown floor (exists but unknown containment statis)
4 = exterior floor
5 = bulkhead (floor with wall or gate)
6 = interior (contained) floor
]]--
--[[
console sends out a pule over all connected spaceship tiles (with a max based on tech)
then consider all tiles with wall or gate.
divide tiles into groups, ones that touch the outside are not part of the ship.
]]--
Spaceship.names = {
"Abaddon", "Ackbar", "Aegis", "Albatross", "Alchemist", "Albion", "Alexander",
"Angler", "Apparition", "ArchAngel", "Assassin", "Avenger", "Axe",
"Bade", "Bardiche", "Battleth", "Blackbird", "Bounty Hunter", "Breaker",
"Brigandine","Bullfinch", "Buzzard",
"Cartographer", "Catface", "Calamari", "Canary", "Caravel", "Carrak", "Citadel", "Clockwerk",
"Chimera", "Coot", "Cormorant", "Crane", "Crossbill", "Crow", "Cuckoo",
"Darkstar", "Dauntless", "Desby", "Dragon", "Drake", "Dream", "Doombringer",
"Dolphin", "Devourer", "Dunn",
"Eagle", "Earthshaker", "Earl Grey", "Egret", "Eider", "Ember", "Enigma", "Eris", "Excalibur",
"Falcon", "Falx", "Feral Pigeon", "Firecrest", "Firefly", "Flying Duckman",
"Fountain", "Fulmar",
"Gadwall", "Gannet", "Garganey", "Gigantosaurus", "Ghast", "Ghoul", "Ghost",
"Glaive", "Goldcrest", "Goldeneye", "Goldfinch", "Goosander", "Goose",
"Goshawk", "Grasshopper", "Greenfinch", "Griffon", "Grouse", "Guillemot",
"Halberd", "Hammer", "Hammerhead", "Harrier", "Hawk", "Harking", "Heron", "Hippogryph", "Honeybadger", "Honeybear",
"Iron Cordon", "Ingot", "Intrepid", "Invoker", "Isabella",
"Jack Snipe", "Jackdaw", "Jay",
"Kamsta", "Katherine", "Kestrel", "Kingfisher", "Kite", "Knight", "Kraken",
"Lapwing", "Lance", "Lancer", "Lick", "Linnet", "Lucas",
"Magi", "Magpie", "Mallard", "Mangonel", "Medusa", "Memento", "Merlin",
"Mistress", "Mocking Jay", "Monstrosity", "Moorhen", "Musk",
"Naga", "Narwhal", "Nebulon", "Nemesis", "Newton", "Nexela", "Nial", "Nicholas",
"Nightjar", "Nissa", "Nightingale", "Night Stalker",
"Oracle", "Orca", "Ostricth", "Outrider", "Owl",
"Partridge", "Pangolin", "Penguin", "Peregrine", "Petrel", "Phantom",
"Pheasant", "Phoenix", "Piccard", "Pintail", "Pioneer",
"Pipit", "Plover", "Prophet", "Prowler", "Pochard", "Puffin",
"Quail",
"Radiance", "Raptor", "Raven", "Razor", "Razorbill", "Red Kite", "Redshank",
"Redstart", "Redwing", "Requiem",
"Riccardo", "Robin", "Roc", "Rook", "Rossi", "Rogue", "Ruff",
"Sanderling", "Sawfish", "Scythe", "Seraph", "Serenity", "Sickle", "Shadow",
"Shag", "Sharknado", "Shelduck", "Sherrif", "Shoveler", "Sin Eater", "Siren",
"Siskin", "Skylark", "Skyshark", "Skywalker", "Skywrath", "Smew", "Snek",
"Snipe", "Sparrowhawk", "Spear", "Spectre", "Spinosaur", "Spynx",
"Starchaser", "Starling", "Stonechat", "Swallow", "Swan", "Swift", "Swordfish",
"Tachyon", "Tali", "Tantive", "Teal", "Templar", "Terrorblade", "Tesla", "Thanatos",
"Throne", "Thrush", "Tigress", "Tin Can", "Titan", "Trebuchet",
"Trimaran", "Turnstone", "Turing", "Tusk", "Twite",
"Ursa", "Undertaker", "Undying Dodo", "Underlord",
"Vengeance", "Viper", "Virtue", "Visage", "Void Hunter", "Volt", "Vulture",
"Wagtail", "Warbird", "Warbler", "Warcry", "Warden", "Warlock", "Warlord", "Warrunner",
"Waxwing", "Weaver", "Wheatear", "Whimbrel", "Whinchat", "Whitestar",
"Wigeon", "Windranger", "Woodcock", "Wraith", "Wrath", "Wren", "Wyvern", "Wyrm",
"Xena", "Xenon", "Xylem",
"Yacht", "Yellowhammer", "Yettie",
"Zenith", "Zilla", "Zombie", "Zweihander"
}
function Spaceship.is_floor(tile_name)
return Util.table_contains(Spaceship.names_spaceship_floors, tile_name)
end
function Spaceship.is_wall(entity_name)
return Util.table_contains(Spaceship.names_spaceship_walls, entity_name)
end
function Spaceship.is_gate(entity_name)
return Util.table_contains(Spaceship.names_spaceship_gates, entity_name)
end
function Spaceship.is_wall_or_gate(entity_name)
return Spaceship.is_wall(entity_name) or Spaceship.is_gate(entity_name)
end
function Spaceship.from_index(spaceship_index)
if global.spaceships then return global.spaceships[tonumber(spaceship_index)] end
end
function Spaceship.from_entity(entity)
for _, spaceship in pairs(global.spaceships) do
if spaceship.console and spaceship.console.valid and spaceship.console.unit_number == entity.unit_number then
return spaceship
end
end
end
function Spaceship.from_surface_position(surface, position)
local x = math.floor(position.x or position[1])
local y = math.floor(position.y or position[2])
-- TODO allow multiple spaceships per surface
for _, spaceship in pairs(global.spaceships) do
if spaceship.own_surface_index then
if spaceship.own_surface_index == surface.index then
return spaceship
end
elseif spaceship.console and spaceship.console.valid and spaceship.console.surface == surface then
-- check tiles
if spaceship.known_tiles and spaceship.known_tiles[x] and spaceship.known_tiles[x][y] and
(spaceship.known_tiles[x][y] == Spaceship.tile_status.floor_console_connected
or spaceship.known_tiles[x][y] == Spaceship.tile_status.bulkhead_console_connected) then
return spaceship
end
end
end
end
function Spaceship.from_name(name)
for _, spaceship in pairs(global.spaceships) do
if spaceship.name == name then
return spaceship
end
end
end
function Spaceship.from_own_surface_index(surface_index) -- can't be a zone
if global.spaceships then
for _, spaceship in pairs(global.spaceships) do
if spaceship.own_surface_index == surface_index then
return spaceship
end
end
end
end
function Spaceship.get_own_surface(spaceship)
return game.surfaces["spaceship-"..spaceship.index]
end
function Spaceship.get_current_surface(spaceship)
if spaceship.zone_index then
local zone = Zone.from_zone_index(spaceship.zone_index)
if zone then
return Zone.get_make_surface(zone)
end
end
return Spaceship.get_own_surface(spaceship)
end
function Spaceship.get_integrity_limit(force)
local integrity = Spaceship.integrity_base
for _, tech_branch in pairs(Spaceship.names_tech_integrity) do
local i = 1
while i > 0 do
local tech = force.technologies[tech_branch.name.."-"..i]
if (not tech) and i == 1 then
tech = force.technologies[tech_branch.name]
end
if not tech then
i = -1
else
local levels = tech.level - tech.prototype.level + (tech.researched and 1 or 0)
integrity = integrity + levels * tech_branch.bonus_per_level
i = i + 1
end
end
end
return integrity
end
function Spaceship.get_launch_energy_cost(spaceship)
if spaceship.zone_index and spaceship.integrity_stress then
local zone = Zone.from_zone_index(spaceship.zone_index)
if zone then
local delta_v = Zone.get_launch_delta_v(zone)
local energy_cost = delta_v * spaceship.integrity_stress * Spaceship.energy_per_launch_integrity_delta_v
return energy_cost
end
end
end
function Spaceship.get_launch_energy(spaceship)
--spaceship.launch_energy = nil
spaceship.launch_energy = nil
if spaceship.zone_index and spaceship.console and spaceship.console.valid and spaceship.known_tiles then
spaceship.launch_energy = 0
local surface = spaceship.console.surface
local tanks = surface.find_entities_filtered{name = Spaceship.names_booster_tanks }
for _, tank in pairs(tanks) do
local tank_x = math.floor(tank.position.x)
local tank_y = math.floor(tank.position.y)
if spaceship.known_tiles[tank_x] and spaceship.known_tiles[tank_x][tank_y]
and spaceship.known_tiles[tank_x][tank_y] == Spaceship.tile_status.floor_console_connected
and #tank.fluidbox > 0 then
local fluidbox = tank.fluidbox[1] or {amount = 0}
if fluidbox.amount > 0 then
spaceship.launch_energy = spaceship.launch_energy + fluidbox.amount * game.fluid_prototypes[fluidbox.name].fuel_value
end
end
end
end
return spaceship.launch_energy
end
function Spaceship.get_console_or_middle_position(spaceship)
if spaceship.console and spaceship.console.valid then
return spaceship.console.position
end
if spaceship.known_tiles_average_x and spaceship.known_tiles_average_y then
return {x = known_tiles_average_x, y = known_tiles_average_y}
end
end
function Spaceship.get_boarding_position(spaceship)
if spaceship.known_tiles_average_x and spaceship.known_bounds then
return {
x = spaceship.known_bounds.left_top.x + math.random() * (spaceship.known_bounds.right_bottom.x - spaceship.known_bounds.left_top.x),
y = spaceship.known_bounds.right_bottom.y + 32
}
end
if spaceship.console and spaceship.console.valid then
return Util.vectors_add(spaceship.console.position, {x = 64 * ( math.random() - 0.5), y = 64})
end
end
function Spaceship.gui_close(player)
if player.gui.left[Spaceship.name_spaceship_gui_root] then
player.gui.left[Spaceship.name_spaceship_gui_root].destroy()
end
Spaceship.stop_anchor_scouting(player)
end
function Spaceship.destroy(spaceship)
if spaceship.zone_index or not spaceship.own_surface_index then -- don't remove the whole surface if in space
global.spaceships[spaceship.index] = nil
spaceship.valid = false
-- if a player has this gui open then close it
local gui_name = Spaceship.name_spaceship_gui_root
for _, player in pairs(game.connected_players) do
if player.gui.left[gui_name] and player.gui.left[gui_name].spaceship_index.children_names[1] == (""..spaceship.index) then
player.gui.left[gui_name].destroy()
end
end
if spaceship.own_surface_index then
game.delete_surface(spaceship.own_surface_index)
spaceship.own_surface_index = nil
end
-- make sure that history references to this spaceship are cleaned up
for _, player in pairs(game.players) do
RemoteView.make_history_valid(player)
end
end
end
function Spaceship.get_destination_zone(spaceship)
if spaceship.destination then
if spaceship.destination.type == "spaceship" then
return Spaceship.from_index(spaceship.destination.index)
else
return Zone.from_zone_index(spaceship.destination.index)
end
end
end
function Spaceship.is_near_destination(spaceship)
if spaceship.near then
if not spaceship.destination then
return true
elseif spaceship.near.type == spaceship.destination.type
and spaceship.near.index == spaceship.destination.index then
return true
end
end
return false
end
function Spaceship.is_at_destination(spaceship)
if spaceship.destination and spaceship.destination.type ~= "spaceship" and spaceship.zone_index and spaceship.zone_index == spaceship.destination.index then
return true
end
return false
end
function Spaceship.compute_extent(spaceship)
local min_x = nil
local max_x = nil
local min_y = nil
local max_y = nil
for x, x_tiles in pairs(spaceship.known_tiles) do
if min_x == nil or x < min_x then min_x = x end
if max_x == nil or x > max_x then max_x = x end
for y, status in pairs(x_tiles) do
if status == Spaceship.tile_status.floor_console_connected
or status == Spaceship.tile_status.bulkhead_console_connected then
if min_y == nil or y < min_y then min_y = y end
if max_y == nil or y > max_y then max_y = y end
end
end
end
max_x = max_x + 1 -- whole tile
max_y = max_y + 1 -- whole tile
return {min_x=min_x,max_x=max_x,min_y=min_y,max_y=max_y}
end
function Spaceship.launch(spaceship)
if not spaceship.is_launching then Log.trace("Abort launch not is_launching") return end
spaceship.is_launching = false
if not spaceship.zone_index then Log.trace("Abort launch no zone_index") return end
if not spaceship.integrity_valid then Log.trace("Abort launch not integrity_valid") return end
if not spaceship.known_tiles then Log.trace("Abort launch not known_tiles") return end
if not spaceship.console and spaceship.console.valid then Log.trace("Abort launch not known_tiles") return end
local current_surface = spaceship.console.surface
local current_zone = Zone.from_surface(current_surface)
local required_energy = Spaceship.get_launch_energy_cost(spaceship)
--Spaceship.get_launch_energy(spaceship)
-- same code but keep tanks references
spaceship.launch_energy = 0
local tanks = current_surface.find_entities_filtered{name = Spaceship.names_booster_tanks }
for _, tank in pairs(tanks) do
local tank_x = math.floor(tank.position.x)
local tank_y = math.floor(tank.position.y)
if spaceship.known_tiles[tank_x] and spaceship.known_tiles[tank_x][tank_y]
and spaceship.known_tiles[tank_x][tank_y] == Spaceship.tile_status.floor_console_connected
and #tank.fluidbox > 0 then
local fluidbox = tank.fluidbox[1] or {amount = 0}
if fluidbox.amount > 0 then
spaceship.launch_energy = spaceship.launch_energy + fluidbox.amount * game.fluid_prototypes[fluidbox.name].fuel_value
end
end
end
if not (required_energy and spaceship.launch_energy and spaceship.launch_energy >= required_energy) then return end
local ship_surface
if spaceship.own_surface_index then
ship_surface = game.surfaces["spaceship-" .. spaceship.index]
end
if not ship_surface then
local map_gen_settings = {
autoplace_controls = {
["planet-size"] = { frequency = 1/1000, size = 1 }
}
}
map_gen_settings.autoplace_settings={
["decorative"]={
treat_missing_as_default=false,
settings={
}
},
["entity"]={
treat_missing_as_default=false,
settings={
}
},
["tile"]={
treat_missing_as_default=false,
settings={
["se-space"]={}
}
},
}
ship_surface = game.create_surface("spaceship-"..spaceship.index, map_gen_settings)
ship_surface.freeze_daytime = true
ship_surface.daytime = 0.4 -- dark but not midnight
spaceship.own_surface_index = ship_surface.index
end
if not ship_surface then
game.print("Error creating ship surface")
return
end
if current_surface == ship_surface then
game.print("Same surface")
return
end
-- point of no return
log("spaceship launch start")
local linked_containers = current_surface.find_entities_filtered{type="linked-container"}
for _, linked_container in pairs(linked_containers) do
linked_container.link_id = 0
end
local valid_tanks = {}
local valid_tank_fuel = {}
local valid_tank_energy_per_fuel = {}
local total_energy = 0
for _, tank in pairs(tanks) do
local tank_x = math.floor(tank.position.x)
local tank_y = math.floor(tank.position.y)
if spaceship.known_tiles[tank_x] and spaceship.known_tiles[tank_x][tank_y]
and spaceship.known_tiles[tank_x][tank_y] == Spaceship.tile_status.floor_console_connected
and #tank.fluidbox > 0 then
local fluidbox = tank.fluidbox[1] or {amount = 0}
if fluidbox.amount > 0 and required_energy > 0 then
local amount = fluidbox.amount
local energy_per_fuel = game.fluid_prototypes[fluidbox.name].fuel_value
table.insert(valid_tanks, tank)
table.insert(valid_tank_fuel, fluidbox.amount)
table.insert(valid_tank_energy_per_fuel, energy_per_fuel)
total_energy = total_energy + amount * energy_per_fuel
end
end
end
for i, tank in pairs(valid_tanks) do
local consume = math.ceil(math.min(valid_tank_fuel[i], required_energy / total_energy * valid_tank_fuel[i]))
required_energy = required_energy - consume * valid_tank_energy_per_fuel[i]
total_energy = total_energy - consume * valid_tank_energy_per_fuel[i]
local fluidbox = tank.fluidbox[1] or {amount = 0}
fluidbox.amount = fluidbox.amount - consume
if fluidbox.amount > 0 then
tank.fluidbox[1] = fluidbox
else
tank.fluidbox[1] = nil
end
end
-- copy ship to surface
local extent = Spaceship.compute_extent(spaceship)
local max_extent = math.max(extent.max_x - extent.min_x, extent.max_y, extent.min_y)
ship_surface.request_to_generate_chunks({x = (extent.min_x + extent.max_x / 2), y = (extent.min_y + extent.max_y / 2)} )
ship_surface.force_generate_chunk_requests()
local area = {
left_top = {x = extent.min_x, y = extent.min_y},
right_bottom = {x = extent.max_x, y = extent.max_y},
}
-- get characters out of vehicles
local vehicle_drivers = {}
local vehicle_passengers = {}
local vehicles = current_surface.find_entities_filtered{
type = {"car", "spider-vehicle", "locomotive", "cargo-wagon"},
area = area
}
for _, vehicle in pairs(vehicles) do
local vehicle_x = math.floor(vehicle.position.x)
local vehicle_y = math.floor(vehicle.position.y)
if spaceship.known_tiles[vehicle_x] and spaceship.known_tiles[vehicle_x][vehicle_y]
and spaceship.known_tiles[vehicle_x][vehicle_y] == Spaceship.tile_status.floor_console_connected then
local driver = vehicle.get_driver()
vehicle.set_driver(nil)
if driver then
if driver.is_player()then driver = driver.character end -- sets to nil if required
end
if driver and driver.valid then
table.insert(vehicle_drivers, {
vehicle_name = vehicle.name,
vehicle_position = vehicle.position,
driver_name = driver.name,
driver_position = driver.position
})
end
if vehicle.type == "car" then
local passenger = vehicle.get_passenger()
vehicle.set_passenger(nil)
if passenger then
if passenger.is_player()then passenger = passenger.character end -- sets to nil if required
end
if passenger and passenger.valid then
table.insert(vehicle_drivers, {
vehicle_name = vehicle.name,
vehicle_position = vehicle.position,
passenger_name = passenger.name,
passenger_position = passenger.position
})
end
end
end
end
local locomotive_settings = {}
for _, locomotive in pairs(current_surface.find_entities_filtered{type = "locomotive", area = area}) do
local locomotive_x = math.floor(locomotive.position.x)
local locomotive_y = math.floor(locomotive.position.y)
if spaceship.known_tiles[locomotive_x] and spaceship.known_tiles[locomotive_x][locomotive_y]
and spaceship.known_tiles[locomotive_x][locomotive_y] == Spaceship.tile_status.floor_console_connected then
table.insert(locomotive_settings, {name = locomotive.name, position = locomotive.position, manual_mode = locomotive.train.manual_mode})
end
end
current_surface.clone_area{
destination_surface = ship_surface,
source_area = area,
destination_area = table.deepcopy(area),
clone_tiles = true,
clone_entities = true,
clone_decoratives = false,
clear_destination = true,
expand_map = true
}
-- Pause inserters, workaround for https://forums.factorio.com/viewtopic.php?f=58&t=89035
local condition_entities = ship_surface.find_entities_filtered{type = Spaceship.types_to_restore, area = area}
spaceship.entities_to_restore = spaceship.entities_to_restore or {}
spaceship.entities_to_restore_tick = game.tick + Spaceship.time_to_restore
for _, entity in pairs(condition_entities) do
table.insert(spaceship.entities_to_restore, {entity = entity, active=entity.active})
entity.active = false
end
local bad_tiles = ship_surface.find_tiles_filtered{ name = {name_out_of_map_tile} }
local set_tiles = {}
for _, tile in pairs(bad_tiles) do
table.insert(set_tiles, {position = tile.position, name=name_space_tile})
ship_surface.set_hidden_tile(tile.position, name_space_tile)
end
ship_surface.set_tiles(set_tiles)
local change_tiles_zone = {} -- surface set_tiles specification
local change_tiles_ship = {}
-- transfer the console
local old_console = spaceship.console
local console_clone = ship_surface.find_entity(Spaceship.name_spaceship_console, spaceship.console.position)
spaceship.console = console_clone
spaceship.console_output = nil
old_console.destroy()
for x = extent.min_x, extent.max_x do
for y = extent.min_y, extent.max_y do
if spaceship.known_tiles[x] and spaceship.known_tiles[x][y] and
(spaceship.known_tiles[x][y] == Spaceship.tile_status.floor_console_connected
or spaceship.known_tiles[x][y] == Spaceship.tile_status.bulkhead_console_connected) then
-- valid tile remove from zone
local under_tile = current_surface.get_hidden_tile({x=x,y=y})
if under_tile == nil or Spaceship.is_floor(under_tile) then
under_tile = "landfill" -- fallback
end
table.insert(change_tiles_zone, {name = under_tile, position = {x=x,y=y}})
local entities = current_surface.find_entities_filtered{
area = {left_top={x=x,y=y}, right_bottom={x=x+1,y=y+1}}
}
for _, entity in pairs(entities) do
if entity.valid and entity.type == "character" then
local clone = ship_surface.find_entity(entity.name, entity.position)
if clone and entity.player then
entity.player.teleport(clone.position, ship_surface)
util.safe_destroy(clone)
else
for _, playerdata in pairs(global.playerdata) do
if playerdata.character == entity then
playerdata.character = clone
end
end
util.safe_destroy(entity)
end
else
util.safe_destroy(entity)
end
end
else
-- invalid tile remove from ship
table.insert(change_tiles_ship, {name = name_space_tile, position = {x=x,y=y}})
local entities = ship_surface.find_entities_filtered{
area = {left_top={x=x,y=y}, right_bottom={x=x+1,y=y+1}}
}
for _, entity in pairs(entities) do
if entity.valid then
if entity.type == "straight-rail"
or entity.name == Spaceship.name_spaceship_clamp_keep then
-- only remove if it is not overlapping valid tiles.
local valid = false
for x2 = math.floor(entity.position.x)-1, math.floor(entity.position.x) do
if not valid then
for y2 = math.floor(entity.position.y)-1, math.floor(entity.position.y) do
if spaceship.known_tiles[x2] and spaceship.known_tiles[x2][y2] and
(spaceship.known_tiles[x2][y2] == Spaceship.tile_status.floor_console_connected
or spaceship.known_tiles[x2][y2] == Spaceship.tile_status.bulkhead_console_connected) then
valid = true break
end
end
end
end
if not valid then
util.safe_destroy(entity)
end
else
util.safe_destroy(entity)
end
end
end
end
ship_surface.set_hidden_tile({x=x,y=y}, name_space_tile)
end
end
current_surface.set_tiles(change_tiles_zone, true)
ship_surface.set_tiles(change_tiles_ship, true)
local cars = ship_surface.find_entities_filtered{type = {"car"}}
for _, car in pairs(cars) do
if not string.find(car.name, mod_prefix.."space") then
car.active = false
end
end
spaceship.near = {type="zone", index= spaceship.zone_index}
spaceship.stopped = true
spaceship.zone_index = nil
spaceship.near_star = Zone.get_star_from_child(current_zone) or Zone.get_star_from_position(spaceship)
Spaceship.set_light(spaceship, ship_surface)
Spaceship.deactivate_engines(spaceship)
CondenserTurbine.reset_surface(ship_surface)
Nexus.reset_surface(ship_surface)
LinkedContainer.update()
if not Zone.is_space(current_zone) then -- started from land, space zome entities
-- space the ground entities
-- unground the space entities
local names_for_spaced = {}
local names_grounded = {}
for name, prototype in pairs(game.entity_prototypes) do
if string.find(name, name_suffix_spaced, 1, true) then
table.insert(names_for_spaced, util.replace(name, name_suffix_spaced, ""))
end
if string.find(name, name_suffix_grounded, 1, true) then
table.insert(names_grounded, name)
end
end
local entities_for_spaced = ship_surface.find_entities_filtered{name = names_for_spaced}
for _, entity in pairs(entities_for_spaced) do
swap_structure(entity, entity.name..name_suffix_spaced)
end
local entities_grounded = ship_surface.find_entities_filtered{name = names_grounded}
for _, entity in pairs(entities_grounded) do
swap_structure(entity, util.replace(entity.name, name_suffix_grounded, ""))
end
end
local destroy_names = {}
for _, name in pairs(remote.call("shield-projector", "get_sub_entity_names", event)) do
table.insert(destroy_names, name)
end
local destroy_entities = ship_surface.find_entities_filtered{name = destroy_names}
for _, entity in pairs(destroy_entities) do
util.safe_destroy(entity)
end
-- put characters back in vehicles
for _, vehicle_driver in pairs(vehicle_drivers) do
local vehicle = ship_surface.find_entity(vehicle_driver.vehicle_name, vehicle_driver.vehicle_position)
local driver = ship_surface.find_entity(vehicle_driver.driver_name, vehicle_driver.driver_position)
if vehicle and driver then
vehicle.set_driver(driver)
end
end
for _, vehicle_passenger in pairs(vehicle_passengers) do
local vehicle = ship_surface.find_entity(vehicle_passenger.vehicle_name, vehicle_passenger.vehicle_position)
local passenger = ship_surface.find_entity(vehicle_passenger.passenger_name, vehicle_passenger.passenger_position)
if vehicle and passenger then
vehicle.set_passenger(passenger)
end
end
-- switch on trains
for _, locomotive_setting in pairs(locomotive_settings) do
local locomotive = ship_surface.find_entity(locomotive_setting.name, locomotive_setting.position)
if locomotive then
locomotive.train.manual_mode = locomotive_setting.manual_mode
end
end
remote.call("shield-projector", "find_on_surface", {surface = ship_surface})
for _, player in pairs(game.connected_players) do
if player.surface.index == current_surface.index or player.surface.index == ship_surface.index then
player.play_sound{path = "se-spaceship-woosh", volume = 1}
end
end
Spaceship.update_output_combinator(spaceship)
Spaceship.start_integrity_check(spaceship)
log("spaceship launch end")
end
function Spaceship.land_at_position(spaceship, position, ignore_average)
if spaceship.own_surface_index and spaceship.near and spaceship.near.type == "zone" and spaceship.known_tiles then
local destination_zone = Zone.from_zone_index(spaceship.near.index)
if not destination_zone then return end
local ship_surface = game.surfaces["spaceship-" .. spaceship.index]
local target_surface = Zone.get_make_surface(destination_zone)
local offset_x = util.to_rail_grid(position.x - spaceship.known_tiles_average_x)
local offset_y = util.to_rail_grid(position.y - spaceship.known_tiles_average_y)
if ignore_average then
offset_x = util.to_rail_grid(position.x)
offset_y = util.to_rail_grid(position.y)
end
local extent = Spaceship.compute_extent(spaceship)
local source_area = {
left_top = {
x = extent.min_x,
y = extent.min_y
},
right_bottom = {
x = extent.max_x,
y = extent.max_y
},
}
local destination_area = {
left_top = {
x = extent.min_x + offset_x,
y = extent.min_y + offset_y
},
right_bottom = {
x = extent.max_x + offset_x,
y = extent.max_y + offset_y
},
}
local dont_land_on = table.deepcopy(Ancient.vault_entrance_structures)
table.insert(dont_land_on, Ancient.name_gate_blocker)
table.insert(dont_land_on, Ancient.name_gate_blocker_void)
for name, stuff in pairs(Ancient.gate_fragments) do
table.insert(dont_land_on, name)
end
if target_surface.count_entities_filtered{name = dont_land_on, area = destination_area} > 0 then
return
end
local landing_area_entities = {}
for x = extent.min_x, extent.max_x do
for y = extent.min_y, extent.max_y do
if spaceship.known_tiles[x] and spaceship.known_tiles[x][y] and
(spaceship.known_tiles[x][y] == Spaceship.tile_status.floor_console_connected
or spaceship.known_tiles[x][y] == Spaceship.tile_status.bulkhead_console_connected) then
local area = {
left_top={
x=x + offset_x,
y=y + offset_y},
right_bottom={
x=x+31/32 + offset_x,
y=y+31/32 + offset_y}}
local entities = target_surface.find_entities_filtered{ area = area }
for _, entity in pairs(entities) do
if entity.force.name ~= "neutral" and entity.type ~= "entity-ghost" and entity.type ~= "tile-ghost"
and entity.type ~= "logistic-robot" and entity.type ~= "construction-robot" and entity.type ~= "deconstructible-tile-proxy" then
ship_surface.print({"cant-build-reason.entity-in-the-way", {"entity-name."..entity.name}})
return
end
table.insert(landing_area_entities, entity)
end
end
end
end
-- point of no return
local linked_containers = ship_surface.find_entities_filtered{type="linked-container"}
for _, linked_container in pairs(linked_containers) do
linked_container.link_id = 0
end
if spaceship.particle_object_ids then
for _, ids in pairs(spaceship.particle_object_ids) do
for _, id in pairs(ids) do
rendering.destroy(id)
end
end
spaceship.particle_object_ids = nil
end
local destroy_names = {}
table.insert(destroy_names, mod_prefix.."spaceship-travel-anchor")
for _, particle in pairs(Spaceship.particles) do
if particle.graphic_name then
table.insert(destroy_names, particle.graphic_name)
end
if particle.projectile_name then
table.insert(destroy_names, particle.projectile_name)
end
if particle.targetable_name then
table.insert(destroy_names, particle.targetable_name)
end
end
for _, name in pairs(remote.call("shield-projector", "get_sub_entity_names", event)) do
table.insert(destroy_names, name)
end
local destroy_entities = ship_surface.find_entities_filtered{name = destroy_names}
for _, entity in pairs(destroy_entities) do
util.safe_destroy(entity)
end
Zone.apply_markers(destination_zone) -- in case the surface exists
-- get characters out of vehicles
local vehicle_drivers = {}
local vehicle_passengers = {}
local vehicles = ship_surface.find_entities_filtered{
type = {"car", "spider-vehicle", "locomotive", "cargo-wagon"},
area = source_area
}
for _, vehicle in pairs(vehicles) do
local vehicle_x = math.floor(vehicle.position.x)
local vehicle_y = math.floor(vehicle.position.y)
if spaceship.known_tiles[vehicle_x] and spaceship.known_tiles[vehicle_x][vehicle_y]
and spaceship.known_tiles[vehicle_x][vehicle_y] == Spaceship.tile_status.floor_console_connected then
local driver = vehicle.get_driver()
vehicle.set_driver(nil)
if driver then
if driver.is_player()then driver = driver.character end -- sets to nil if required
end
if driver and driver.valid then
table.insert(vehicle_drivers, {
vehicle_name = vehicle.name,
vehicle_position = vehicle.position,
driver_name = driver.name,
driver_position = driver.position
})
end
if vehicle.type == "car" then
local passenger = vehicle.get_passenger()
vehicle.set_passenger(nil)
if passenger then
if passenger.is_player()then passenger = passenger.character end -- sets to nil if required
end
if passenger and passenger.valid then
table.insert(vehicle_drivers, {
vehicle_name = vehicle.name,
vehicle_position = vehicle.position,
passenger_name = passenger.name,
passenger_position = passenger.position
})
end
end
end
end
local locomotive_settings = {}
for _, locomotive in pairs(ship_surface.find_entities_filtered{type = "locomotive"}) do
table.insert(locomotive_settings, {name = locomotive.name, position = locomotive.position, manual_mode = locomotive.train.manual_mode})
end
-- copy ship to surface
local change_tiles_zone = {}
for x, x_tiles in pairs(spaceship.known_tiles) do
for y, status in pairs(x_tiles) do
if status == Spaceship.tile_status.floor_console_connected
or status == Spaceship.tile_status.bulkhead_console_connected then
local tile = ship_surface.get_tile({x,y})
if Spaceship.is_floor(tile.name) then
table.insert(change_tiles_zone, {name = tile.name, position = {
x = x + offset_x,
y = y + offset_y}})
end
end
end
end
target_surface.set_tiles(change_tiles_zone, true)
-- clear the target area
for _, entity in pairs(landing_area_entities) do
if entity.valid then
if entity.type == "character" then
entity.health = entity.health * 0.1
else
util.safe_destroy(entity)-- maybe use die?
end
end
end
-- do this before cloning
if not Zone.is_space(destination_zone) then -- started from space (spacehsip), ending on land
-- un-space the ground entities
-- ground the space entities
local names_for_grounded = {}
local names_spaced = {}
for name, prototype in pairs(game.entity_prototypes) do
if string.find(name, name_suffix_grounded, 1, true) then
table.insert(names_for_grounded, util.replace(name, name_suffix_grounded, ""))
end
if string.find(name, name_suffix_spaced, 1, true) then
table.insert(names_spaced, name)
end
end
local entities_for_grounded = ship_surface.find_entities_filtered{name = names_for_grounded}
for _, entity in pairs(entities_for_grounded) do
swap_structure(entity, entity.name..name_suffix_grounded)
end
local entities_spaced = ship_surface.find_entities_filtered{name = names_spaced}
for _, entity in pairs(entities_spaced) do
swap_structure(entity, util.replace(entity.name, name_suffix_spaced, ""))
end
end
ship_surface.clone_area{
destination_surface = target_surface,
source_area = source_area,
destination_area = destination_area,
clone_tiles = false,
clone_entities = true,
clone_decoratives = false,
clear_destination_entities = false,
clear_destination_decoratives = false,
expand_map = true
}
-- Pause inserters, workaround for https://forums.factorio.com/viewtopic.php?f=58&t=89035
local condition_entities = target_surface.find_entities_filtered{type = Spaceship.types_to_restore, area = destination_area}
spaceship.entities_to_restore = spaceship.entities_to_restore or {}
spaceship.entities_to_restore_tick = game.tick + Spaceship.time_to_restore
for _, entity in pairs(condition_entities) do
table.insert(spaceship.entities_to_restore, {entity = entity, active=entity.active})
entity.active = false
end
local characters = ship_surface.find_entities_filtered{type = "character"}
for _, character in pairs(characters) do
local target_position = {
x = character.position.x + offset_x,
y = character.position.y + offset_y,
}
local clone = target_surface.find_entity( character.name, target_position ) -- find equivalent character on target surface
if character.player then -- player is attached
local player = character.player
if clone then
--test clone destruction method instead of swap (swap causes some issues)
--player.set_controller{type = defines.controllers.ghost} -- detatch from character
--player.teleport(character.position, target_surface)
--player.set_controller{type = defines.controllers.character, character = clone} -- attach clone
--remote.call("jetpack", "stop_jetpack_immediate", {character = clone})
player.teleport(target_position, target_surface)
remote.call("jetpack", "stop_jetpack_immediate", {character = character})
util.safe_destroy(clone)
else
player.teleport(target_position, target_surface)
remote.call("jetpack", "stop_jetpack_immediate", {character = character})
end
else -- no player attached, try to preserve detatched connection
if clone then
for player_index, playerdata in pairs(global.playerdata) do
local player = game.players[player_index]
if playerdata.character and playerdata.character == character then
playerdata.character = clone
end
end
remote.call("jetpack", "stop_jetpack_immediate", {character = clone})
end
end
end
-- transfer the console
local old_console = spaceship.console
local console_clone = target_surface.find_entity(Spaceship.name_spaceship_console, {
x = spaceship.console.position.x + offset_x,
y = spaceship.console.position.y + offset_y,
})
if not console_clone then
Log.trace("Error finding console clone")
end
spaceship.console = console_clone
spaceship.console_output = nil
old_console.destroy()
for x = extent.min_x, extent.max_x do
for y = extent.min_y, extent.max_y do
if spaceship.known_tiles[x] and spaceship.known_tiles[x][y] and
(spaceship.known_tiles[x][y] == Spaceship.tile_status.floor_console_connected
or spaceship.known_tiles[x][y] == Spaceship.tile_status.bulkhead_console_connected) then
local entities = ship_surface.find_entities_filtered{
area = {
left_top={
x=x + offset_x,
y=y + offset_y},
right_bottom={
x=x+1 + offset_x,
y=y+1 + offset_y}}
}
for _, entity in pairs(entities) do
util.safe_destroy(entity)
end
end
end
end
-- move non-character players
for _, player in pairs(game.connected_players) do
if player.surface == ship_surface then
player.teleport({x = player.position.x + offset_x, y = player.position.y + offset_y}, target_surface)
RemoteView.gui_update(player)
end
end
game.delete_surface(ship_surface)
spaceship.own_surface_index = nil
spaceship.particles = {}
spaceship.zone_index = spaceship.near.index
spaceship.near = nil
spaceship.stopped = true
spaceship.is_moving = false
spaceship.speed = 0
local engines = target_surface.find_entities_filtered{name = Spaceship.names_engines}
for _, engine in pairs(engines) do
engine.active = false
end
-- put characters back in vehicles
for _, vehicle_driver in pairs(vehicle_drivers) do
local vehicle = target_surface.find_entity(vehicle_driver.vehicle_name, {
x = vehicle_driver.vehicle_position.x + offset_x,
y = vehicle_driver.vehicle_position.y + offset_y,
})
local driver = target_surface.find_entity(vehicle_driver.driver_name, {
x = vehicle_driver.driver_position.x + offset_x,
y = vehicle_driver.driver_position.y + offset_y,
})
if vehicle and driver then
vehicle.set_driver(driver)
end
end
for _, vehicle_passenger in pairs(vehicle_passengers) do
local vehicle = target_surface.find_entity(vehicle_passenger.vehicle_name, {
x = vehicle_passenger.vehicle_position.x + offset_x,
y = vehicle_passenger.vehicle_position.y + offset_y,
})
local passenger = target_surface.find_entity(vehicle_passenger.passenger_name, {
x = vehicle_passenger.passenger_position.x + offset_x,
y = vehicle_passenger.passenger_position.y + offset_y,
})
if vehicle and passenger then
vehicle.set_passenger(passenger)
end
end
-- switch on trains
for _, locomotive_setting in pairs(locomotive_settings) do
local locomotive = target_surface.find_entity(locomotive_setting.name, {
x = locomotive_setting.position.x + offset_x,
y = locomotive_setting.position.y + offset_y,
})
if locomotive then
locomotive.train.manual_mode = locomotive_setting.manual_mode
end
end
CondenserTurbine.reset_surface(target_surface)
Nexus.reset_surface(target_surface)
LinkedContainer.update()
remote.call("shield-projector", "find_on_surface", {surface = target_surface})
local cars = target_surface.find_entities_filtered{type = {"car"}}
for _, car in pairs(cars) do
if Zone.is_space(destination_zone) then
if not string.find(car.name, mod_prefix.."space") then
car.active = false
end
else
car.active = true
end
end
Spaceship.start_integrity_check(spaceship)
for _, player in pairs(game.connected_players) do
if player.surface.index == target_surface.index or player.surface.index == ship_surface.index then
player.play_sound{path = "se-spaceship-woosh", volume = 1}
end
end
Spaceship.update_output_combinator(spaceship)
end
end
function Spaceship.anchor_scouting_tick(player, spaceship)
local playerdata = get_make_playerdata(player)
if not playerdata.anchor_scouting_cache then
playerdata.anchor_scouting_cache = {}
if spaceship.known_tiles then
local extent = Spaceship.compute_extent(spaceship)
local aabb
for x = extent.min_x, extent.max_x do
for y = extent.min_y, extent.max_y do
local value = spaceship.known_tiles[x] and spaceship.known_tiles[x][y]
if value == Spaceship.tile_status.floor_console_connected
or value == Spaceship.tile_status.bulkhead_console_connected then
if not aabb then
aabb = {min_x=x,max_x=x+1,min_y=y,max_y=y+1}
else
aabb.max_y = aabb.max_y + 1
end
else
if aabb then
table.insert(playerdata.anchor_scouting_cache, aabb)
aabb = nil
end
end
end
if aabb then
table.insert(playerdata.anchor_scouting_cache, aabb)
aabb = nil
end
end
end
end
if playerdata.anchor_scouting_cache then
local offset_x = util.to_rail_grid(player.position.x - spaceship.known_tiles_average_x)
local offset_y = util.to_rail_grid(player.position.y - spaceship.known_tiles_average_y)
for _, aabb in pairs(playerdata.anchor_scouting_cache) do
rendering.draw_rectangle{
color = {r = 0.125, g = 0.125, b = 0, a = 0.01},
filled = true,
left_top = {x=aabb.min_x+offset_x,y=aabb.min_y+offset_y},
right_bottom = {x=aabb.max_x+offset_x,y=aabb.max_y+offset_y},
surface = player.surface,
time_to_live = 2
}
end
end
end
function Spaceship.start_anchor_scouting(spaceship, player)
if not spaceship.near and spaceship.near.type == "zone" then return end
local zone = Zone.from_zone_index(spaceship.near.index)
if not zone then return end
local playerdata = get_make_playerdata(player)
-- enter remote view
playerdata.anchor_scouting_for_spaceship_index = spaceship.index
local character = player.character
if character then
playerdata.character = character
end
player.set_controller{type = defines.controllers.ghost}
--player.set_controller{type = defines.controllers.spectator}
if character then
-- stop the character from continuing input action (running to doom)
character.walking_state = {walking = false, direction = defines.direction.south}
character.riding_state = {acceleration = defines.riding.acceleration.braking, direction = defines.riding.direction.straight}
character.shooting_state = {state = defines.shooting.not_shooting, position=character.position}
end
local surface = Zone.get_make_surface(zone)
local position = {x=0,y=0}
if playerdata.surface_positions and playerdata.surface_positions[surface.index] then
position = playerdata.surface_positions[surface.index]
end
player.teleport(position, surface)
end
function Spaceship.stop_anchor_scouting(player)
local playerdata = get_make_playerdata(player)
if playerdata.anchor_scouting_for_spaceship_index then
playerdata.anchor_scouting_for_spaceship_index = nil
if playerdata.remote_view_active then
local surface = player.surface
local position = player.position
RemoteView.stop(player)
RemoteView.start(player)
player.teleport(position, surface)
else
if playerdata.character and playerdata.character.valid then
player.teleport(playerdata.character.position, playerdata.character.surface)
player.set_controller{type = defines.controllers.character, character = playerdata.character}
elseif not player.character then
Respawn.die(player)
end
end
end
playerdata.anchor_scouting_cache = nil
end
function Spaceship.on_gui_click(event)
if not (event.element and event.element.valid) then return end
local element = event.element
local player = game.players[event.player_index]
root = gui_element_or_parent(element, Spaceship.name_spaceship_gui_root)
if not root then return end
local playerdata = get_make_playerdata(player)
local spaceship = Spaceship.from_index(root.spaceship_index.children_names[1])
if not spaceship then
if playerdata.anchor_scouting_for_spaceship_index then
Spaceship.stop_anchor_scouting(player)
end
return
end
if element.name == "launch" then
if spaceship.zone_index then
spaceship.is_launching = true
spaceship.is_landing = false
Spaceship.start_integrity_check(spaceship)
end
elseif element.name == "button_anchor" then
if spaceship.near and spaceship.near.type == "zone" then
if playerdata.anchor_scouting_for_spaceship_index then
local position = table.deepcopy(player.position)
Spaceship.stop_anchor_scouting(player)
Spaceship.land_at_position(spaceship, position)
else
spaceship.is_launching = false
spaceship.is_landing = true
Spaceship.start_anchor_scouting(spaceship, player)
Spaceship.start_integrity_check(spaceship)
end
elseif playerdata.anchor_scouting_for_spaceship_index then
Spaceship.stop_anchor_scouting(player)
end
elseif element.name == "board" then
if spaceship.near and spaceship.near.type == "spaceship" then
local othership = Spaceship.from_index(spaceship.near.index)
if othership and Zone.get_travel_delta_v_sub(spaceship, othership) < 1 then
if not othership.own_surface_index then
player.print({"space-exploration.fail-board-target-anchored"})
else
local character = player.character
if not character then
local playerdata = get_make_playerdata(player)
character = playerdata.character
end
if not character then
player.print({"space-exploration.fail-board-no-character"})
else
if character.surface.index ~= spaceship.own_surface_index then
player.print({"space-exploration.fail-board-remote-character"})
else
local surface = Spaceship.get_current_surface(othership)
local boarding_position = Spaceship.get_boarding_position(othership)
--teleport_non_colliding_player(player, boarding_position, surface)
teleport_character_to_surface(character, surface, boarding_position)
Spaceship.gui_close(player)
end
end
end
end
end
elseif element.name == "stop" then
spaceship.stopped = true
spaceship.target_speed = nil
elseif element.name == "start" then
spaceship.stopped = false
spaceship.target_speed = nil
Spaceship.start_integrity_check(spaceship)
elseif element.name == "cancel_anchor_scouting" then
Spaceship.stop_anchor_scouting(player)
elseif element.name == "start-integrity-check" then
spaceship.max_speed = 0
Spaceship.start_integrity_check(spaceship, 0.1)
elseif element.name == "rename" then
local name_flow = element.parent
element.destroy()
name_flow["show-name"].destroy()
local name_label = name_flow.add{ type = "textfield", name="write-name", text=spaceship.name, style="space_platform_textfield_short"}
local rename_button = name_flow.add{ type = "sprite-button", name="rename-confirm", sprite="utility/enter",
tooltip={"space-exploration.rename-something", {"space-exploration.spaceship"}}, style="space_platform_sprite_button_small"}
elseif element.name == "rename-confirm" then
local name_flow = element.parent
element.destroy()
local new_name = string.trim(name_flow["write-name"].text)
if newname ~= "" and new_name ~= spaceship.name then
--do change name stuff
spaceship.name = new_name
end
name_flow["write-name"].destroy()
local name_label = name_flow.add{ type = "label", name="show-name", caption="The " ..spaceship.name, style="space_platform_title_short"}
local rename_button = name_flow.add{ type = "sprite-button", name="rename", sprite="utility/rename_icon_normal",
tooltip={"space-exploration.rename-something", {"space-exploration.spaceship"}}, style="space_platform_sprite_button_small"}
elseif element.name == "spaceship-list-zones" then
local value = player_get_dropdown_value(player, element.name, element.selected_index)
if type(value) == "table" then
if value.type == "zone" then
local zone_index = value.index
local zone = Zone.from_zone_index(zone_index)
if zone then
spaceship.destination = {type = "zone", index = zone_index}
spaceship.travel_message = {"space-exploration.spaceship-travel-message-new-course-plotted"}
--spaceship.destination_zone_index = zone_index
Log.trace("set destination to location: " .. zone.name )
end
elseif value.type == "spaceship" then
local spaceship_index = value.index
local destination_spaceship = Spaceship.from_index(spaceship_index)
if destination_spaceship == spaceship then
player.print({"space-exploration.spaceship-cannot-set-destination-to-self"})
else
spaceship.destination = {type = "spaceship", index = spaceship_index}
spaceship.travel_message = {"space-exploration.spaceship-travel-message-new-course-plotted"}
Log.trace("set destination to spaceship : " .. spaceship.name )
end
end
Spaceship.update_output_combinator(spaceship)
else
Spaceship.gui_close(player)
Log.trace("Error: Non-table value ")
end
elseif element.name == "clear_filter" then
element.parent.filter_list.text = ""
Spaceship.gui_update_destinations_list(player)
elseif element.name == Spaceship.name_window_close then
Spaceship.gui_close(player)
end
end
Event.addListener(defines.events.on_gui_click, Spaceship.on_gui_click)
Event.addListener(defines.events.on_gui_selection_state_changed, Spaceship.on_gui_click)
function Spaceship.on_gui_checked_state_changed(event)
if not (event.element and event.element.valid) then return end
local element = event.element
local player = game.players[event.player_index]
root = gui_element_or_parent(element, Spaceship.name_spaceship_gui_root)
if not root then return end
local spaceship = Spaceship.from_index(root.spaceship_index.children_names[1])
if not spaceship then return end
if element.name == "list-zones-alphabetical" then
local playerdata = get_make_playerdata(player)
playerdata.zones_alphabetical = element.state
Spaceship.gui_update_destinations_list(player)
end
end
Event.addListener(defines.events.on_gui_checked_state_changed, Spaceship.on_gui_checked_state_changed)
function Spaceship.gui_update_destinations_list(player)
local playerdata = get_make_playerdata(player)
local root = player.gui.left[Spaceship.name_spaceship_gui_root]
if not root then return end
local spaceship = Spaceship.from_index(root.spaceship_index.children_names[1])
if not spaceship then return end
if root then
local filter = nil
if root.filter_flow and root.filter_flow.filter_list then
filter = string.trim(root.filter_flow.filter_list.text)
if filter == "" then
filter = nil
end
end
-- update the list
local destination_zone = Spaceship.get_destination_zone(spaceship)
if not destination_zone then destination_zone = Zone.from_zone_index(spaceship.zone_index) end
local list, selected_index, values = Zone.dropdown_list_zone_destinations(spaceship.force_name, destination_zone, playerdata.zones_alphabetical, filter)
root["spaceship-list-zones"].items = list
root["spaceship-list-zones"].selected_index = selected_index or 1
player_set_dropdown_values(player, "spaceship-list-zones", values)
end
end
function Spaceship.on_gui_text_changed(event)
if not (event.element and event.element.valid) then return end
local element = event.element
local player = game.players[event.player_index]
local root = gui_element_or_parent(element, Spaceship.name_spaceship_gui_root)
if root then -- remote view
if element.name == "filter_list" then
Spaceship.gui_update_destinations_list(player)
end
end
end
Event.addListener(defines.events.on_gui_text_changed, Spaceship.on_gui_text_changed)
function Spaceship.gui_update(player)
local root = player.gui.left[Spaceship.name_spaceship_gui_root]
if root then
local spaceship = Spaceship.from_index(root.spaceship_index.children_names[1])
if spaceship then
local playerdata = get_make_playerdata(player)
--local inv = spaceship.container.get_inventory(defines.inventory.chest)
--local inv_used = count_inventory_slots_used(inv)
--root["cargo_capacity"].caption="Cargo: " .. math.min(inv_used, #inv) .. " / " .. #inv
--root["cargo_capacity_progress"].value=math.min(inv_used, #inv) / #inv
local energy_required = Spaceship.get_launch_energy_cost(spaceship)
if root["launch_energy"] then
if spaceship.zone_index then
if energy_required and spaceship.launch_energy then
--root["launch_energy"].caption="Launch energy: " .. Util.format_energy(spaceship.launch_energy) .. " / " .. Util.format_energy(energy_required, true)
root["launch_energy"].caption={"space-exploration.spaceship-launch-energy", Util.format_energy(spaceship.launch_energy) .. " / " .. Util.format_energy(energy_required, true)}
root["launch_energy_progress"].value = math.min(1, spaceship.launch_energy / energy_required)
else
--root["launch_energy"].caption="Launch energy: Requires valid integrity check."
root["launch_energy"].caption={"space-exploration.spaceship-launch-energy-invalid"}
root["launch_energy_progress"].value = 0
end
else
-- repurpose for speed
if spaceship.speed > 0 then
--root["launch_energy"].caption="Speed: " .. string.format("%.2f", spaceship.speed or 0) .. " / " .. string.format("%.2f", spaceship.max_speed or 0)
root["launch_energy"].caption={"space-exploration.spaceship-speed", string.format("%.2f", spaceship.speed or 0) .. " / " .. string.format("%.2f", spaceship.max_speed or 0)}
root["launch_energy_progress"].value = math.min(1, spaceship.speed / (spaceship.max_speed or spaceship.speed))
else
--root["launch_energy"].caption="Speed: 0 / ".. string.format("%.2f", spaceship.max_speed or 0)
root["launch_energy"].caption={"space-exploration.spaceship-speed", " 0 / " .. string.format("%.2f", spaceship.max_speed or 0)}
root["launch_energy_progress"].value = 0
end
end
end
if root["streamline"] and root["streamline_progress"] then
if spaceship.streamline then
root["streamline"].caption={"space-exploration.spaceship-streamline", string.format("%.2f",(spaceship.streamline or 0) * 100).."%"}
root["streamline_progress"].value = spaceship.streamline or 0
end
end
if root["structural_integrity"] and root["structural_integrity_progress"] then
if spaceship.integrity_stress_structure and spaceship.integrity_limit then
--root["structural_integrity"].caption="Structural Stress (Hull): " .. spaceship.integrity_stress_structure .. " / " .. spaceship.integrity_limit
root["structural_integrity"].caption={"space-exploration.spaceship-structural-stress-hull", spaceship.integrity_stress_structure .. " / " .. spaceship.integrity_limit}
root["structural_integrity_progress"].value = math.min(1, spaceship.integrity_stress_structure / spaceship.integrity_limit)
else
--root["structural_integrity"].caption="Structural Stress (Hull): NA (Invalid containment)."
root["structural_integrity"].caption={"space-exploration.spaceship-structural-stress-hull-invalid"}
root["structural_integrity_progress"].value = 0
end
end
if root["container_integrity"] and root["container_integrity_progress"] then
if spaceship.integrity_stress_container and spaceship.integrity_limit then
--root["container_integrity"].caption="Structural Stress (Containers): " .. spaceship.integrity_stress_container .. " / " .. spaceship.integrity_limit
root["container_integrity"].caption={"space-exploration.spaceship-structural-stress-container", spaceship.integrity_stress_container .. " / " .. spaceship.integrity_limit}
root["container_integrity_progress"].value = math.min(1, spaceship.integrity_stress_container / spaceship.integrity_limit)
else
--root["container_integrity"].caption="Structural Stress (Containers): NA (Invalid containment)."
root["container_integrity"].caption={"space-exploration.spaceship-structural-stress-container-invalid"}
root["container_integrity_progress"].value = 0
end
end
if game.tick % 60 == 0 then
spaceship.distance_to_destination = Spaceship.get_distance_to_destination(spaceship)
spaceship.distance_to_destination_tick = game.tick
end
if spaceship.distance_to_destination and spaceship.speed then
if spaceship.speed == 0 then
if not spaceship.max_speed or spaceship.max_speed == 0 then
--root["travel-time"].caption = "Travel time: Unknown. Test max speed for estimate."
root["travel-time"].caption = {"space-exploration.spaceship-travel-time-unknown"}
else
--root["travel-time"].caption = "Travel time: "..
-- Util.seconds_to_clock(spaceship.distance_to_destination / ((spaceship.max_speed or 1) * Spaceship.travel_speed_multiplier) / 60)
-- .. "s at max speed"
root["travel-time"].caption = {"space-exploration.spaceship-travel-time-max", Util.seconds_to_clock(spaceship.distance_to_destination / ((spaceship.max_speed or 1) * Spaceship.travel_speed_multiplier) / 60)}
end
else
root["travel-time"].caption = {"space-exploration.spaceship-travel-time-current", Util.seconds_to_clock(spaceship.distance_to_destination / ((spaceship.speed or 1) * Spaceship.travel_speed_multiplier) / 60)}
end
else
root["travel-time"].caption = {"space-exploration.spaceship-travel-time-current", 0}
end
if (game.tick + 30) % 60 == 0 and root["closest-location"] then
local closest = Zone.find_nearest_zone(
spaceship.space_distortion,
spaceship.stellar_position,
spaceship.star_gravity_well,
spaceship.planet_gravity_well)
--root["closest-location"].caption="Closest Location: "..closest.name
root["closest-location"].caption = {"space-exploration.spaceship-closest-location", closest.name}
end
if spaceship.space_distortion > 0 then
--root["anomaly-distance"].caption = "Spacial Distortion: " .. string.format("%.2f", spaceship.space_distortion * Zone.travel_cost_space_distortion)
root["anomaly-distance"].caption = {"space-exploration.spaceship-location-spatial-distortion", string.format("%.2f", spaceship.space_distortion * Zone.travel_cost_space_distortion)}
else
root["anomaly-distance"].caption = ""
end
if spaceship.space_distortion > 0.05 then
root["stellar-x"].caption = ""
root["stellar-y"].caption = ""
root["star-gravity"].caption = ""
root["planet-gravity"].caption = ""
else
--root["stellar-x"].caption = "Quadrant X: " .. string.format("%.2f", spaceship.stellar_position.x * Zone.travel_cost_interstellar)
--root["stellar-y"].caption = "Quadrant Y: " .. string.format("%.2f", spaceship.stellar_position.y * Zone.travel_cost_interstellar)
root["stellar-x"].caption = {"space-exploration.spaceship-location-stellar-x", string.format("%.2f", spaceship.stellar_position.x * Zone.travel_cost_interstellar)}
root["stellar-y"].caption = {"space-exploration.spaceship-location-stellar-y", string.format("%.2f", spaceship.stellar_position.y * Zone.travel_cost_interstellar)}
if spaceship.star_gravity_well > 0 then
--root["star-gravity"].caption = "Star gravity well: " .. string.format("%.2f", spaceship.star_gravity_well * Zone.travel_cost_star_gravity)
root["star-gravity"].caption = {"space-exploration.spaceship-location-star-gravity-well", string.format("%.2f", spaceship.star_gravity_well * Zone.travel_cost_star_gravity)}
else
root["star-gravity"].caption = ""
end
if spaceship.planet_gravity_well > 0 then
--root["planet-gravity"].caption = "Planet gravity well: " .. string.format("%.2f", spaceship.planet_gravity_well * Zone.travel_cost_planet_gravity)
root["planet-gravity"].caption = {"space-exploration.spaceship-location-planet-gravity-well", string.format("%.2f", spaceship.planet_gravity_well * Zone.travel_cost_planet_gravity)}
else
root["planet-gravity"].caption = ""
end
end
--if root["current-speed"] then
-- root["current-speed"].caption="Speed: " .. string.format("%.2f", spaceship.speed or 0)
--end
if root["travel-status"] then
--root["travel-status"].caption="Travel Status: " .. (spaceship.travel_message or "")
root["travel-status"].caption={"space-exploration.spaceship-travel-status", (spaceship.travel_message or "")}
end
if root["integrity-status"] then
--root["integrity-status"].caption="Integrity Status: "
-- .. (spaceship.integrity_valid and "Valid: " or "Invalid: ")
-- .. (spaceship.check_message or "")
if spaceship.integrity_valid then
root["integrity-status"].caption = {"space-exploration.spaceship-integrity-status-valid", (spaceship.check_message or "")}
else
root["integrity-status"].caption = {"space-exploration.spaceship-integrity-status-invalid", (spaceship.check_message or "")}
end
end
-- button modes:
--[[
launch when on a surface
Anchor when near a surface the is the destination
Stop when moving.
Engage when in space and:
a destination is selected
or not near a surface
]]--
local button
if spaceship.zone_index then
-- launch
if not root["action-flow"]["launch"] then
root["action-flow"].clear()
root["action-flow"].add{ type="button", name="launch", caption={"space-exploration.spaceship-button-launch"}, style="confirm_button"}
end
button = root["action-flow"]["launch"]
if spaceship.integrity_valid then
if energy_required and spaceship.launch_energy and spaceship.launch_energy >= energy_required then
--button.caption = "Launch"
button.caption = {"space-exploration.spaceship-button-launch"}
--button.tooltip = "Ready to launch"
button.tooltip = {"space-exploration.spaceship-button-launch-tooltip"}
button.style = "confirm_button"
else
--button.caption = "Launch (disabled)"
button.caption = {"space-exploration.spaceship-button-launch-disabled"}
--button.tooltip = "Requires fuel in booster tanks"
button.tooltip = {"space-exploration.spaceship-button-launch-disabled-fuel-tooltip"}
button.style = "red_confirm_button"
end
else
--button.caption = "Launch (disabled)"
button.caption = {"space-exploration.spaceship-button-launch-disabled"}
--button.tooltip = "Requires valid integrity check"
button.tooltip = {"space-exploration.spaceship-button-launch-disabled-integrity-tooltip"}
button.style = "red_confirm_button"
end
elseif spaceship.near and
(spaceship.destination == nil
or (spaceship.near.type == "zone" and spaceship.destination.index == spaceship.near.index)) then
-- anchor
local zone = Zone.from_zone_index(spaceship.near.index)
if not root["action-flow"]["button_anchor"] then
root["action-flow"].clear()
root["action-flow"].add{ type="button", name="button_anchor", caption={"space-exploration.spaceship-button-anchor"}, style="confirm_button"}
end
button = root["action-flow"]["button_anchor"]
if playerdata.anchor_scouting_for_spaceship_index then
--button.caption = "Confirm Anchor"
button.caption = {"space-exploration.spaceship-button-confirm-anchor"}
button.style = "confirm_button"
else
--local context = Zone.is_solid(zone) and "on" or "to"
--button.caption = "Anchor "..context.." "..zone.name
if Zone.is_solid(zone) then
button.caption = {"space-exploration.spaceship-button-anchor-on", zone.name}
else
button.caption = {"space-exploration.spaceship-button-anchor-to", zone.name}
end
button.style = "confirm_button"
end
elseif spaceship.near and spaceship.near.type == "spaceship" and spaceship.destination
and spaceship.destination.type == "spaceship" and spaceship.destination.index == spaceship.near.index then
-- board
local othership = Spaceship.from_index(spaceship.near.index)
if not othership then
spaceship.destination = nil
end
local name = othership and othership.name or "MISSING"
if not root["action-flow"]["board"] then
root["action-flow"].clear()
root["action-flow"].add{ type="button", name="board", caption={"space-exploration.spaceship-button-board", name}, style="confirm_button"}
end
button = root["action-flow"]["board"]
elseif spaceship.destination and not spaceship.stopped then
if not root["action-flow"]["stop"] then
root["action-flow"].clear()
root["action-flow"].add{ type="button", name="stop", caption={"space-exploration.spaceship-button-stop"}, style="confirm_button"}
end
button = root["action-flow"]["stop"]
else
if not root["action-flow"]["start"] then
root["action-flow"].clear()
root["action-flow"].add{ type="button", name="start", caption={"space-exploration.spaceship-button-start"}, style="confirm_button"}
end
button = root["action-flow"]["start"]
end
button.style.top_margin = 10
button.style.horizontally_stretchable = true
button.style.horizontal_align = "left"
if playerdata.anchor_scouting_for_spaceship_index then
if not root["back-flow"]["cancel_anchor_scouting"] then
local back = root["back-flow"].add{type = "button", name = "cancel_anchor_scouting",
caption = {"space-exploration.spaceship-button-scouting-back"}, style="back_button", tooltip={"space-exploration.spaceship-button-scouting-back-tooltip"}}
back.style.top_margin = 10
end
elseif root["back-flow"]["cancel_anchor_scouting"] then
root["back-flow"]["cancel_anchor_scouting"].destroy()
end
end
end
end
function Spaceship.gui_open(player, spaceship)
if not spaceship then
player.print('Spaceship not found. Try replacing the console')
return
end
local gui = player.gui.left
close_own_guis(player)
local playerdata = get_make_playerdata(player)
local container = gui.add{ type = "frame", name = Spaceship.name_spaceship_gui_root, style="space_platform_container", direction="vertical"}
local title_table = container.add{type="table", name="spaceship_index", column_count=2, draw_horizontal_lines=false}
title_table.style.horizontally_stretchable = true
title_table.style.column_alignments[1] = "left"
title_table.style.column_alignments[2] = "right"
-- NOTE: [Spaceship.name_spaceship_gui_root].spaceship_index.child_names()[1] gets spaceship index
local title_frame = title_table.add{type="frame", name=spaceship.index,
caption = {"space-exploration.simple-a-b", "[img=virtual-signal/se-spaceship] ", {"space-exploration.simple-a-b", {"space-exploration.spaceship"}, " "..spaceship.index}},
style="informatron_title_frame"}
title_frame.style.right_padding = -5
local right_flow = title_table.add{type="flow", name="title_flow_right"}
--local close = right_flow.add{type="button", name=Lifesupport.name_window_close, caption="✖", style="informatron_close_button"}
local close = right_flow.add{type="sprite-button", name=Spaceship.name_window_close, sprite = "utility/close_white", style="informatron_close_button"}
close.style.width = 28
close.style.height = 28
--[[local title_flow = container.add{ type="flow", name="spaceship_index", direction="horizontal"}
-- NOTE: [Spaceship.name_spaceship_gui_root].spaceship_index.child_names()[1] gets spaceship index
local title = title_flow.add{ type="label", name=spaceship.index, caption={"space-exploration.spaceship"}, style="space_platform_title"}
]]
local name_flow = container.add{ type="flow", name="name-flow", direction="horizontal"}
local name_label = name_flow.add{ type = "label", name="show-name", caption={"space-exploration.spaceship-name-the", spaceship.name}, style="space_platform_title_short"}
local rename_button = name_flow.add{ type = "sprite-button", name="rename", sprite="utility/rename_icon_normal",
tooltip={"space-exploration.rename-something", {"entity-name.se-spaceship-console"}}, style="space_platform_sprite_button_small"}
local bar = container.add{ type="progressbar", name="launch_energy_progress", size = 300, value=0, style="spaceship_progressbar_energy"}
bar.style.horizontally_stretchable = true
local label = container.add{ type="label", name="launch_energy", caption={"space-exploration.spaceship-launch-energy", ""}}
label.style.top_margin = -26
label.style.left_margin = 5
label.style.font = "default-bold"
label.style.font_color = {}
local bar = container.add{ type="progressbar", name="streamline_progress", size = 300, value=0, style="spaceship_progressbar_streamline"}
bar.style.horizontally_stretchable = true
local label = container.add{ type="label", name="streamline", caption={"space-exploration.spaceship-streamline", ""}}
label.style.top_margin = -26
label.style.left_margin = 5
label.style.font = "default-bold"
label.style.font_color = {}
local bar = container.add{ type="progressbar", name="structural_integrity_progress", size = 300, value=0, style="spaceship_progressbar_integrity"}
bar.style.horizontally_stretchable = true
local label = container.add{ type="label", name="structural_integrity", caption={"space-exploration.spaceship-structural-stress-hull", ""}}
label.style.top_margin = -26
label.style.left_margin = 5
label.style.font = "default-bold"
label.style.font_color = {}
local bar = container.add{ type="progressbar", name="container_integrity_progress", size = 300, value=0, style="spaceship_progressbar_integrity"}
bar.style.horizontally_stretchable = true
local label = container.add{ type="label", name="container_integrity", caption={"space-exploration.spaceship-structural-stress-container"}}
label.style.top_margin = -26
label.style.left_margin = 5
label.style.font = "default-bold"
label.style.font_color = {}
container.add{ type="button", name="start-integrity-check", caption={"space-exploration.spaceship-button-start-integrity-check"}}
local integrity_status = container.add{ type="label", name="integrity-status", caption=""}
integrity_status.style.width = 300
integrity_status.style.single_line = false
local space_distortion = container.add{ type="label", name="anomaly-distance", caption=""}
space_distortion.style.width = 300
space_distortion.style.single_line = false
local stellar_x = container.add{ type="label", name="stellar-x", caption=""}
stellar_x.style.width = 300
stellar_x.style.single_line = false
local stellar_y = container.add{ type="label", name="stellar-y", caption=""}
stellar_y.style.width = 300
stellar_y.style.single_line = false
local star_gravity = container.add{ type="label", name="star-gravity", caption=""}
star_gravity.style.width = 300
star_gravity.style.single_line = false
local planet_gravity = container.add{ type="label", name="planet-gravity", caption=""}
planet_gravity.style.width = 300
planet_gravity.style.single_line = false
--local current_speed = container.add{ type="label", name="current-speed", caption="Speed: "}
--current_speed.style.width = 300
--current_speed.style.single_line = false
local closest_location = container.add{ type="label", name="closest-location", caption=""}
closest_location.style.width = 300
closest_location.style.single_line = false
container.add{ type="label", name="destination-label", caption={"space-exploration.spaceship-heading-destination"}, style="space_platform_title"}
container.add{type="checkbox", name="list-zones-alphabetical", caption={"space-exploration.list-destinations-alphabetically"}, state=playerdata.zones_alphabetical and true or false}
local filter_container = container.add{ type="flow", name="filter_flow", direction="horizontal"}
local filter_field = filter_container.add{ type="textfield", name="filter_list"}
filter_field.style.width = 275
local filter_button = filter_container.add{ type = "sprite-button", name="clear_filter", sprite="utility/search_icon", tooltip={"space-exploration.clear-filter"},}
filter_button.style.left_margin = 5
filter_button.style.width = 28
filter_button.style.height = 28
local destination_zone = Spaceship.get_destination_zone(spaceship)
if not destination_zone then destination_zone = Zone.from_zone_index(spaceship.zone_index) end
if not destination_zone then destination_zone = Zone.find_nearest_zone(
spaceship.space_distortion,
spaceship.stellar_position,
spaceship.star_gravity_well,
spaceship.planet_gravity_well)
end
local list, selected_index, values = Zone.dropdown_list_zone_destinations(spaceship.force_name, destination_zone, playerdata.zones_alphabetical)
local zones_dropdown = container.add{ type="drop-down", name="spaceship-list-zones", items=list, selected_index=selected_index}
zones_dropdown.style.horizontally_stretchable = true
player_set_dropdown_values(player, "spaceship-list-zones", values)
container.add{ type="label", name="travel-time", caption=""}
local travel_status = container.add{ type="label", name="travel-status", caption=""}
travel_status.style.width = 300
travel_status.style.single_line = false
spaceship.distance_to_destination = Spaceship.get_distance_to_destination(spaceship)
container.add{ type="flow", name="action-flow", direction="horizontal"}
container.add{ type="flow", name="back-flow", direction="horizontal"}
Spaceship.gui_update(player)
end
function Spaceship.on_gui_opened(event)
local player = game.players[event.player_index]
if event.entity and event.entity.valid and event.entity.name == Spaceship.name_spaceship_console then
Spaceship.gui_open(player, Spaceship.from_entity(event.entity))
player.opened = nil
else
-- the trick here is opeining the craft menu to cancel the other menu, then exiting the craft menu
-- means that pressing e exits the custom gui
if player.opened_gui_type and player.opened_gui_type ~= defines.gui_type.none then
if player.gui.left[Spaceship.name_spaceship_gui_root] then
if player.opened_self then
player.opened = nil
end
Spaceship.gui_close(player)
end
end
end
end
Event.addListener(defines.events.on_gui_opened, Spaceship.on_gui_opened)
function Spaceship.get_make_console_output(console)
if not (console and console.valid) then return end
local output_position = util.vectors_add(console.position, Spaceship.console_output_offset)
local output = console.surface.find_entity(Spaceship.name_spaceship_console_output, output_position)
if output then
return output
end
local output_ghosts = console.surface.find_entities_filtered{
ghost_name = Spaceship.name_spaceship_console_output,
position = output_position}
if output_ghosts[1] and output_ghosts[1].valid then
local collisions, output = output_ghosts[1].revive({})
if output then
return output
end
end
output = console.surface.create_entity{
name = Spaceship.name_spaceship_console_output,
position = util.vectors_add(console.position, Spaceship.console_output_offset),
force = console.force
}
return output
end
function Spaceship.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.type == "entity-ghost" or entity.type == "tile-ghost" then return end
local surface = entity.surface
local spaceship = Spaceship.from_own_surface_index(entity.surface.index)
for _, name in pairs(Spaceship.names_engines) do
if entity.name == name then
if spaceship and spaceship.is_moving then
entity.active = true
Spaceship.find_own_surface_engines(spaceship)
else
entity.active = false
end
end
end
if entity.name == Spaceship.name_spaceship_console then
global.spaceships = global.spaceships or {}
local console = entity
if spaceship then
if not (spaceship.console and spaceship.console.valid) then
spaceship.console = entity
spaceship.console_output = nil
if event.player_index and game.players[event.player_index] then
Spaceship.gui_open(game.players[event.player_index], spaceship)
end
Spaceship.start_integrity_check(spaceship, 0.1)
end
else
local zone = Zone.from_surface(entity.surface)
if not zone then
entity.surface.create_entity{
name = "item-on-ground",
position = entity.position,
["item-entity"] = {name = Spaceship.name_spaceship_console, count = 1}
}
entity.destroy()
game.print({"space-exploration.construction-denied-se-surface"})
return
end
local fn = entity.force.name
local spaceship_index = global.next_spaceship_index or 1
global.next_spaceship_index = spaceship_index + 1
local available_names = {}
for _, name in pairs(Spaceship.names) do
local found = false
for _, spaceship in pairs(global.spaceships) do
if name == spaceship.name then
found = true
break
end
end
if not found then
table.insert(available_names, name)
end
end
local name = "Spaceship " .. spaceship_index
if #available_names > 0 then
name = available_names[math.random(#available_names)]
end
local spaceship = {
type = "spaceship",
index = spaceship_index,
valid = true,
force_name = fn,
unit_number = entity.unit_number,
console = entity,
name = name,
zone_index = zone.index, -- this is dynamic and can be nil
speed = 1,
destination_zone_index = zone.index,
space_distortion = Zone.get_space_distortion(zone),
stellar_position = Zone.get_stellar_position(zone),
star_gravity_well = Zone.get_star_gravity_well(zone),
planet_gravity_well = Zone.get_planet_gravity_well(zone),
near_stellar_object = Zone.get_stellar_object_from_child(zone),
}
global.spaceships[spaceship_index] = spaceship
Spaceship.start_integrity_check(spaceship, 0.1)
if event.player_index and game.players[event.player_index] then
Spaceship.gui_open(game.players[event.player_index], spaceship)
end
end
end
if entity.name == Spaceship.name_spaceship_clamp_place then
-- find spaceship at tile
local direction = (entity.direction == defines.direction.east or entity.direction == defines.direction.west ) and defines.direction.west or defines.direction.east
local check_positions = {}
if direction == defines.direction.west then
table.insert(check_positions, util.vectors_add(entity.position, {x=0, y=-1})) -- top right over
table.insert(check_positions, util.vectors_add(entity.position, {x=0, y=0})) -- bottom right behind
else
table.insert(check_positions, util.vectors_add(entity.position, {x=-1, y=-1})) -- top left over
table.insert(check_positions, util.vectors_add(entity.position, {x=-1, y=0})) -- bottom left behind
end
local space_tiles = 0
for _, pos in pairs(check_positions) do
if tile_is_space(surface.get_tile(pos)) then
space_tiles = space_tiles + 1
Spaceship.flash_tile(surface, pos, {r=255,g=0,b=0, a = 0.25}, 10)
end
end
if space_tiles >= 1 then
cancel_entity_creation(entity, event.player_index, "Back cannot be on Empty space")
return
end
local keep = entity.surface.create_entity{
name = Spaceship.name_spaceship_clamp_keep,
position = entity.position,
force = entity.force,
direction = direction
}
entity.destroy()
keep.rotatable = false
local id = Spaceship.find_unique_clamp_id(direction, keep.surface, keep)
local comb = keep.get_or_create_control_behavior()
if direction == defines.direction.west then
comb.set_signal(1, {signal={type="virtual", name=mod_prefix.."anchor-using-left-clamp"}, count=id})
else
comb.set_signal(1, {signal={type="virtual", name=mod_prefix.."anchor-using-right-clamp"}, count=id})
end
end
if spaceship then
Spaceship.check_integrity_stress(spaceship)
end
end
Event.addListener(defines.events.on_built_entity, Spaceship.on_entity_created)
Event.addListener(defines.events.on_robot_built_entity, Spaceship.on_entity_created)
Event.addListener(defines.events.script_raised_built, Spaceship.on_entity_created)
Event.addListener(defines.events.script_raised_revive, Spaceship.on_entity_created)
function Spaceship.find_unique_clamp_id(direction, surface, exclude)
local entities = surface.find_entities_filtered{name=Spaceship.name_spaceship_clamp_keep}
local used_ids = {}
for _, entity in pairs(entities) do
if entity ~= exclude then
if entity.direction == direction then
local value = 0
local comb = entity.get_or_create_control_behavior()
local signal = comb.get_signal(1)
if signal then value = signal.count end
used_ids[value] = value
end
end
end
local i = 1
while used_ids[i] do
i = i + 1
end
return i
end
function Spaceship.validate_clamp_signal(entity)
-- make sure it still has the correct signal.
local value = 1
local comb = entity.get_or_create_control_behavior()
local signal = comb.get_signal(1)
if signal then value = signal.count end
if entity.direction == defines.direction.west then
comb.set_signal(1, {signal={type="virtual", name=mod_prefix.."anchor-using-left-clamp"}, count=value})
else
comb.set_signal(1, {signal={type="virtual", name=mod_prefix.."anchor-using-right-clamp"}, count=value})
end
end
function Spaceship.on_gui_closed(event)
if event.entity and event.entity.valid and event.entity.name == Spaceship.name_spaceship_clamp_keep then
Spaceship.validate_clamp_signal(event.entity)
end
end
Event.addListener(defines.events.on_gui_closed, Spaceship.on_gui_closed)
function Spaceship.on_entity_settings_pasted(event)
if event.destination and event.destination .valid and event.destination .name == Spaceship.name_spaceship_clamp_keep then
Spaceship.validate_clamp_signal(event.destination)
end
end
Event.addListener(defines.events.on_entity_settings_pasted, Spaceship.on_entity_settings_pasted)
function Spaceship.on_entity_damaged(event)
if event.cause and event.cause.valid and event.entity and event.entity.valid then
if Util.table_contains(Spaceship.all_targetables, event.entity.name) then
if Spaceship.obstacle_damage_multipliers[event.cause.type] then
event.entity.health = event.entity.health - event.final_damage_amount * (Spaceship.obstacle_damage_multipliers[event.cause.type] - 1)
end
end
end
end
Event.addListener(defines.events.on_entity_damaged, Spaceship.on_entity_damaged)
function Spaceship.on_built_tile(event)
local surface = game.surfaces[event.surface_index]
if surface and string.find(surface.name, "spaceship-") then
local spaceship = Spaceship.from_own_surface_index(surface.index)
Spaceship.check_integrity_stress(spaceship)
Spaceship.start_integrity_check(spaceship)
end
end
Event.addListener(defines.events.on_player_built_tile, Spaceship.on_built_tile)
Event.addListener(defines.events.on_robot_built_tile, Spaceship.on_built_tile)
function Spaceship.on_removed_tile(event)
local surface
if event.player_index and game.players[event.player_index] and game.players[event.player_index].connected then
surface = game.players[event.player_index].surface
end
if event.robot and event.robot.valid then
surface = event.robot.surface
end
if surface and string.find(surface.name, "spaceship-") then
local spaceship = Spaceship.from_own_surface_index(surface.index)
Spaceship.check_integrity_stress(spaceship)
Spaceship.start_integrity_check(spaceship)
end
end
Event.addListener(defines.events.on_robot_mined_tile, Spaceship.on_removed_tile)
Event.addListener(defines.events.on_player_mined_tile, Spaceship.on_removed_tile)
function Spaceship.on_removed_entity(event)
if event.entity and event.entity.valid then
if event.entity.name == Spaceship.name_spaceship_console then
local outputs = event.entity.surface.find_entities_filtered{name = Spaceship.name_spaceship_console_output, area = util.position_to_area(event.entity.position, 2)}
for _, output in pairs(outputs) do
output.destroy()
end
elseif event.entity.surface and Util.table_contains(Spaceship.names_spaceship_bulkheads, event.entity.name) then
if string.find(event.entity.surface.name, "spaceship-") then
local spaceship = Spaceship.from_own_surface_index(event.entity.surface.index)
spaceship.speed = spaceship.speed * 0.9
Spaceship.check_integrity_stress(spaceship)
Spaceship.start_integrity_check(spaceship)
end
end
end
end
Event.addListener(defines.events.on_entity_died, Spaceship.on_removed_entity)
Event.addListener(defines.events.on_robot_mined_entity, Spaceship.on_removed_entity)
Event.addListener(defines.events.on_player_mined_entity, Spaceship.on_removed_entity)
Event.addListener(defines.events.script_raised_destroy, Spaceship.on_removed_entity)
function Spaceship.get_distance_to_destination(spaceship)
if (not spaceship.destination) or Spaceship.is_near_destination(spaceship) then
return 0
end
local target_zone = Spaceship.get_destination_zone(spaceship)
if target_zone then
local destination_space_distorion = Zone.get_space_distortion(target_zone)
local destination_stellar_position = Zone.get_stellar_position(target_zone)
local destination_star_gravity_well = Zone.get_star_gravity_well(target_zone)
local destination_planet_gravity_well = Zone.get_planet_gravity_well(target_zone)
local distortion_distance = 0
local interstellar_distance = 0
local star_gravity_distance = 0
local planet_gravity_distance = 0
distortion_distance = math.abs(spaceship.space_distortion - destination_space_distorion)
interstellar_distance = Util.vectors_delta_length(spaceship.stellar_position, destination_stellar_position)
--if distortion_distance == 1 then
if distortion_distance >= 1 or (spaceship.space_distortion == 1 and destination_space_distorion == 1) then
interstellar_distance = 0
end
if interstellar_distance == 0 then
-- same solar system
star_gravity_distance = math.abs(spaceship.star_gravity_well - destination_star_gravity_well)
else
star_gravity_distance = spaceship.star_gravity_well + destination_star_gravity_well
end
if star_gravity_distance == 0 then
-- same solar system
planet_gravity_distance = math.abs(spaceship.planet_gravity_well - destination_planet_gravity_well)
else
planet_gravity_distance = spaceship.planet_gravity_well + destination_planet_gravity_well
end
return distortion_distance * Zone.travel_cost_space_distortion
+ interstellar_distance * Zone.travel_cost_interstellar
+ star_gravity_distance * Zone.travel_cost_star_gravity
+ planet_gravity_distance * Zone.travel_cost_planet_gravity
end
end
function Spaceship.find_own_surface_engines(spaceship)
spaceship.engines = nil
local surface = Spaceship.get_own_surface(spaceship)
if surface and spaceship.known_tiles and spaceship.known_bounds then
spaceship.engines = {}
local engines = surface.find_entities_filtered{name = Spaceship.names_engines}
for _, entity in pairs(engines) do
local efficiency = 0.95
local box = entity.bounding_box
local x = math.floor((box.left_top.x + box.right_bottom.x)/2)
local y = math.floor(box.right_bottom.y)
local first_y = nil
if spaceship.known_tiles[x] then
for y = spaceship.known_bounds.right_bottom.y, spaceship.known_bounds.left_top.y, -1 do
if spaceship.known_tiles[x][y]
and (spaceship.known_tiles[x][y] == Spaceship.tile_status.floor_console_connected
or spaceship.known_tiles[x][y] == Spaceship.tile_status.bulkhead_console_connected) then
first_y = y
break
end
end
end
--if first_y then
-- Log.trace(y .. " vs " .. first_y)
--end
if first_y and first_y <= y then
efficiency = 1.2
end
--Log.trace(efficiency)
table.insert(spaceship.engines, {entity = entity, efficiency = efficiency})
end
end
end
function Spaceship.activate_engines(spaceship, probability)
if not spaceship.engines then
Spaceship.find_own_surface_engines(spaceship)
end
if spaceship.engines then
for _, engine in pairs(spaceship.engines) do
if engine.entity and engine.entity.valid then
engine.entity.active = true
else
spaceship.engines = nil
Spaceship.activate_engines(spaceship, probability)
return
end
end
end
end
function Spaceship.deactivate_engines(spaceship, probability)
if not spaceship.engines then
Spaceship.find_own_surface_engines(spaceship)
end
if spaceship.engines then
for _, engine in pairs(spaceship.engines) do
if engine.entity and engine.entity.valid then
engine.entity.active = false
else
spaceship.engines = nil
Spaceship.deactivate_engines(spaceship, probability)
return
end
end
end
end
function Spaceship.add_as_particle(spaceship, entity, particle_template)
if spaceship and entity then
if not particle_template then particle_template = Spaceship.particles["rock-small"] end
spaceship.particles = spaceship.particles or {}
local particle = table.deepcopy(particle_template)
particle.valid = true
particle.graphic = entity
table.insert(spaceship.particles, particle)
end
end
function Spaceship.on_trigger_created_entity(event)
if event.entity and event.entity.valid and event.entity.name == mod_prefix.."trigger-movable-debris" then
local surface = event.entity.surface
local spaceship = Spaceship.from_own_surface_index(surface.index)
if spaceship then
local entities = surface.find_entities_filtered{type = "simple-entity", area = Util.position_to_area(event.entity.position, 0.5)}
for _, entity in pairs(entities) do
if string.find(entity.name, "meteor") then
Spaceship.add_as_particle(spaceship, entity, Spaceship.particles["rock-large"])
else
Spaceship.add_as_particle(spaceship, entity, Spaceship.particles["rock-small"])
end
end
end
end
end
Event.addListener(defines.events.on_trigger_created_entity, Spaceship.on_trigger_created_entity)
function Spaceship.surface_tick(spaceship)
-- actions that apply to maintaining a spaceship surface
spaceship.speed = spaceship.speed or 0
local surface = Spaceship.get_own_surface(spaceship)
if spaceship.speed > 1 then
surface.wind_orientation = 0.5
end
local speed_factor = spaceship.speed > 0 and math.pow(spaceship.speed / Spaceship.speed_taper, Spaceship.particle_speed_power) * Spaceship.speed_taper or 0
surface.wind_speed = 0.01 + 0.005 * speed_factor
if spaceship.particles then
for _, particle in pairs(spaceship.particles) do
if not (particle.graphic and particle.graphic.valid) then -- killed or mined
particle.valid = false
elseif particle.graphic.position.y > spaceship.known_bounds.right_bottom.y + Spaceship.particle_spawn_range + 32 then -- out of range
particle.valid = false
elseif particle.projectile and particle.projectile.valid == false then -- projectile was there but detonated
particle.valid = false
elseif particle.targetable and particle.targetable.valid == false then -- projectile detonated
particle.valid = false
end
if not particle.valid then
if particle.graphic and particle.graphic.valid then
if particle.graphic.unit_number
and spaceship.particle_object_ids
and spaceship.particle_object_ids[particle.graphic.unit_number]
then
for _, id in pairs(spaceship.particle_object_ids[particle.graphic.unit_number]) do
rendering.destroy(id)
end
spaceship.particle_object_ids[particle.graphic.unit_number] = nil
end
particle.graphic.destroy()
particle.graphic = nil
end
if particle.projectile and particle.projectile.valid then
particle.projectile.destroy()
particle.projectile = nil
end
if particle.targetable and particle.targetable.valid then
particle.targetable.destroy()
particle.targetable = nil
end
spaceship.particles[_] = nil
else -- particle is still valid
local position = particle.graphic.position
position.y = position.y + particle.speed * speed_factor
if spaceship.speed > 0.001 then
particle.graphic.teleport(position)
if particle.projectile then
--particle.projectile.teleport(position)
particle.projectile.speed = particle.speed * speed_factor
elseif particle.projectile_name then
particle.projectile = surface.create_entity{
name = particle.projectile_name,
-- particle would overtake the rest if we didn't subtract the current vector
position = util.vectors_add(position, {x = 0, y = -particle.speed * speed_factor}),
speed = particle.speed * speed_factor,
target = {x = position.x, y = position.y + 100},
force="enemy"
}
end
if particle.targetable then
particle.targetable.teleport(position)
elseif particle.targetable_name then
particle.targetable = surface.create_entity{
name = particle.targetable_name,
position = position,
target = {x = position.x, y = position.y + 100},
force="enemy"
}
end
if particle.targetable and particle.destroys_floor then
-- destroy floor
local x = math.floor(position.x)
local y = math.floor(position.y)
if spaceship.known_tiles[x] and spaceship.known_tiles[x][y] then
spaceship.known_tiles[x][y] = nil
local tile = surface.get_tile(x, y)
local tile_name = tile.name
if tile_name ~= name_space_tile then
particle.targetable.damage(50, "neutral", "explosion") -- damage the particle
local entities = surface.find_entities_filtered{ -- destroy entities on the floor
area = util.tile_to_area({x=x,y=y}, margin),
collision_mask = {
global.named_collision_masks.space_collision_layer,
"object-layer",
"resource-layer"
}
}
for _, entity in pairs(entities) do
if entity and entity.valid then entity.die() end
end
surface.set_tiles(
{{name=name_space_tile, position = {x,y}}},
false, -- corect tiles
true, -- remove_colliding_entities
true, -- remove_colliding_decoratives
true -- raise_event
)
if Util.table_contains(Spaceship.names_spaceship_floors, tile_name) then
-- make blueprint for the tile.
surface.create_entity{name = "tile-ghost", inner_name = tile_name, force = spaceship.force_name, position = {x=x,y=y}}
end
Spaceship.start_integrity_check(spaceship)
end
end
end
else
if particle.projectile and particle.projectile.valid then
particle.projectile.destroy()
particle.projectile = nil
end
if particle.targetable and particle.targetable.valid then
particle.targetable.destroy()
particle.targetable = nil
end
end
end
end
end
end
function Spaceship.set_light(spaceship, surface)
-- expect 15 is the max, 10 + 5 planets but reduced start position
local light_percent = Zone.get_solar(spaceship)
surface.freeze_daytime = true
if light_percent >= 0.5 then
surface.daytime = 0.35 -- half light
surface.solar_power_multiplier = Zone.solar_multiplier * light_percent * 2 -- x2 compensate for half light
else
surface.daytime = 0.45 - 0.2 * light_percent
surface.solar_power_multiplier = Zone.solar_multiplier -- x2 compensate for half light max
-- light_percent of 1 would be 0.35 (half-light),
-- light_percent of 0 would be 0.45 (dark)
end
end
function Spaceship.move_to_destination(spaceship)
if not spaceship.destination then return end
--Log.trace(game.tick .. " moving to destination.")
-- move away from current zone
if spaceship.near and not Spaceship.is_near_destination(spaceship) then
--Log.trace(game.tick .. "Leaving zone.")
spaceship.near = nil
-- close any scouting views
for _, player in pairs(game.connected_players) do
local playerdata = get_make_playerdata(player)
if playerdata.anchor_scouting_for_spaceship_index == spaceship.index then
Spaceship.stop_anchor_scouting(player)
player.print("Cannot anchor, spaceship has departed for a different destination.")
end
end
end
local target_zone = Spaceship.get_destination_zone(spaceship)
if not target_zone then
spaceship.destination = nil
spaceship.travel_message = "No destination."
Log.trace("Spaceship destination invalid")
return
end
local ship_surface = Spaceship.get_own_surface(spaceship)
-- mid-flight validation
if not spaceship.tile_count or (game.tick % 60 == 0) then
spaceship.tile_count = ship_surface.count_tiles_filtered{name = Spaceship.names_spaceship_floors}
end
if not spaceship.bulkhead_count or ((game.tick + 30) % 60 == 0) then
spaceship.bulkhead_count = ship_surface.count_entities_filtered{name = Spaceship.names_spaceship_bulkheads}
end
local target_size = spaceship.tile_count - spaceship.bulkhead_count / 2
local mass_estimate = Spaceship.minimum_mass + target_size
if spaceship.integrity_stress then
-- not using container stress was a bug, but now that it is being fixed...
-- mostly use integrity stress, but do allow 25% benefit for a compact container ship
mass_estimate = Util.lerp(mass_estimate, Spaceship.minimum_mass + spaceship.integrity_stress, 0.75)
end
if spaceship.engines then
for _, engine_table in pairs(spaceship.engines) do
local engine = engine_table.entity
if engine and engine.valid then
if engine.active and engine.is_crafting() then
for _, engine_proto in pairs(Spaceship.engines) do
if engine.name == engine_proto.name then
spaceship.speed = spaceship.speed
+ (spaceship.speed_multiplier or 1) * engine_table.efficiency * engine_proto.thrust
* math.min(1, engine.energy / engine_proto.max_energy) / mass_estimate
* (Spaceship.speed_taper / (Spaceship.speed_taper + spaceship.speed))
end
end
end
else
spaceship.engines[_] = nil
end
end
end
-- space_drag from imperfect vacuum
-- streamline 0 = 110.45
-- streamline 1 = 181.25
local drag = Spaceship.space_drag * (2 - (spaceship.streamline or 0))
spaceship.speed = spaceship.speed * (1 - drag) + Spaceship.minimum_impulse
spaceship.max_speed = math.max(spaceship.speed, spaceship.max_speed or 0)
-- floating characters
if remote.interfaces["jetpack"] and remote.interfaces["jetpack"]["get_jetpacks"] then
local jetpacks = remote.call("jetpack", "get_jetpacks", {surface_index = spaceship.own_surface_index})
for _, jetpack in pairs(jetpacks) do
jetpack.velocity.y = jetpack.velocity.y +
0.000005 * math.pow(spaceship.speed / Spaceship.speed_taper, Spaceship.particle_speed_power) * Spaceship.speed_taper
if remote.interfaces["jetpack"]["set_velocity"] then
remote.call("jetpack", "set_velocity", {unit_number = jetpack.unit_number, velocity = jetpack.velocity})
end
end
end
-- spawn particles based on speed.
spaceship.particles = spaceship.particles or {}
local function get_make_travel_anchor_particle( y, particle_template )
local speed = particle_template.speed
if not (spaceship.travel_anchor_particles
and spaceship.travel_anchor_particles[speed]
and spaceship.travel_anchor_particles[speed].valid
and y + 10 > spaceship.travel_anchor_particles[speed].position.y) then
spaceship.travel_anchor_particles = spaceship.travel_anchor_particles or {}
spaceship.travel_anchor_particles[speed] = ship_surface.create_entity({
name = mod_prefix .. "spaceship-travel-anchor",
position = {
x = spaceship.known_bounds.left_top.x - Spaceship.particle_spawn_range,
y = spaceship.known_bounds.left_top.y - Spaceship.particle_spawn_range - 10 - 10
}
})
Spaceship.add_as_particle(spaceship, spaceship.travel_anchor_particles[speed],
{ speed = speed })
end
return spaceship.travel_anchor_particles[speed]
end
local function spawn_particles_speed_size(particle_template)
if spaceship.speed > (particle_template.min_speed or 0)
and spaceship.integrity_stress > (particle_template.min_size or 0) then
local spawn_particles_quota = target_size * spaceship.speed / 60 * particle_template.multiplier
local spawn_particles_whole = math.floor(spawn_particles_quota)
local spawn_particles_chance = spawn_particles_quota - spawn_particles_whole
if math.random() <= spawn_particles_chance then
spawn_particles_whole = spawn_particles_whole + 1
end
if spawn_particles_whole > 0 then
local x_min = spaceship.known_bounds.left_top.x - Spaceship.particle_spawn_range
local x_max = spaceship.known_bounds.right_bottom.x + Spaceship.particle_spawn_range
local spawn_y = spaceship.known_bounds.left_top.y - Spaceship.particle_spawn_range - 10
for i = 1, spawn_particles_whole do
local spawn_x = x_min + math.random() * (x_max - x_min)
if particle_template.sprite_name then
local travel_anchor_particle = get_make_travel_anchor_particle(spawn_y, particle_template)
local object_id = rendering.draw_sprite({
surface = ship_surface,
sprite = particle_template.sprite_name,
target = travel_anchor_particle,
target_offset = {
x = spawn_x - travel_anchor_particle.position.x,
y = spawn_y - travel_anchor_particle.position.y
}
})
spaceship.particle_object_ids = spaceship.particle_object_ids or {}
spaceship.particle_object_ids[travel_anchor_particle.unit_number] = spaceship.particle_object_ids[travel_anchor_particle.unit_number] or {}
table.insert(spaceship.particle_object_ids[travel_anchor_particle.unit_number], object_id)
else
local particle = table.deepcopy(particle_template)
particle.valid = true
particle.graphic = ship_surface.create_entity{
name = particle_template.graphic_name,
position = {x = spawn_x, y = spawn_y},
}
table.insert(spaceship.particles, particle)
end
end
end
end
end
for _, particle_template in pairs(Spaceship.particles) do
spawn_particles_speed_size(particle_template)
end
-- step towards destination
local travel_speed = spaceship.speed * Spaceship.travel_speed_multiplier
local destination_space_distorion = Zone.get_space_distortion(target_zone)
local destination_stellar_position = Zone.get_stellar_position(target_zone)
local destination_star_gravity_well = Zone.get_star_gravity_well(target_zone)
local destination_planet_gravity_well = Zone.get_planet_gravity_well(target_zone)
if destination_space_distorion > 0 then -- target is anomaly (or spaceship)
if spaceship.planet_gravity_well > 0 then
spaceship.planet_gravity_well = math.max(0, spaceship.planet_gravity_well - travel_speed / Zone.travel_cost_planet_gravity)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-exiting-planet-gravity"}
elseif spaceship.star_gravity_well > 0 then
spaceship.star_gravity_well = math.max(0, spaceship.star_gravity_well - travel_speed / Zone.travel_cost_star_gravity)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-exiting-star-gravity"}
else -- can enter distortion
spaceship.near_stellar_object = nil
local delta_space_distortion = destination_space_distorion - spaceship.space_distortion
if delta_space_distortion == 0 then
spaceship.near = table.deepcopy(spaceship.destination)
spaceship.stopped = true
else
local space_distortion_travel = travel_speed / Zone.travel_cost_space_distortion
-- step towards destination
spaceship.space_distortion = spaceship.space_distortion
+ math.min(math.max(delta_space_distortion, -space_distortion_travel), space_distortion_travel)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-spatial-distortions"}
end
end
elseif spaceship.space_distortion == 1 then
spaceship.stellar_position = table.deepcopy(destination_stellar_position)
spaceship.space_distortion = 1 - travel_speed / Zone.travel_cost_space_distortion
spaceship.travel_message = {"space-exploration.spaceship-travel-message-spatial-distortions"}
elseif spaceship.space_distortion > 0 then
spaceship.space_distortion = spaceship.space_distortion - travel_speed / Zone.travel_cost_space_distortion
spaceship.travel_message = {"space-exploration.spaceship-travel-message-spatial-distortions"}
else -- conventional travel
local interstellar_distance = Util.vectors_delta_length(spaceship.stellar_position, destination_stellar_position)
if interstellar_distance == 0 then -- same system
spaceship.near_star = Zone.get_star_from_child(target_zone) or Zone.get_star_from_position(target_zone)
spaceship.near_stellar_object = Zone.get_stellar_object_from_child(target_zone) or Zone.get_stellar_object_from_position(target_zone)
if spaceship.star_gravity_well == destination_star_gravity_well then -- same planet system
if spaceship.planet_gravity_well == destination_planet_gravity_well then -- we're here
spaceship.near = table.deepcopy(spaceship.destination)
spaceship.stopped = true
else
local delta_planet_gravity = destination_planet_gravity_well - spaceship.planet_gravity_well
local planet_gravity_travel = travel_speed / Zone.travel_cost_planet_gravity
spaceship.planet_gravity_well = spaceship.planet_gravity_well
+ math.min(math.max(delta_planet_gravity, -planet_gravity_travel), planet_gravity_travel)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-navigating-planet-gravity"}
end
else
if spaceship.planet_gravity_well > 0 then
spaceship.planet_gravity_well = math.max(0, spaceship.planet_gravity_well - travel_speed / Zone.travel_cost_planet_gravity)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-exiting-planet-gravity"}
else
local delta_star_gravity = destination_star_gravity_well - spaceship.star_gravity_well
local star_gravity_travel = travel_speed / Zone.travel_cost_star_gravity
spaceship.star_gravity_well = spaceship.star_gravity_well
+ math.min(math.max(delta_star_gravity, -star_gravity_travel), star_gravity_travel)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-navigating-star-gravity"}
end
end
else -- different systems
if spaceship.planet_gravity_well > 0 then
spaceship.planet_gravity_well = math.max(0, spaceship.planet_gravity_well - travel_speed / Zone.travel_cost_planet_gravity)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-exiting-planet-gravity"}
elseif spaceship.star_gravity_well > 0 then
spaceship.star_gravity_well = math.max(0, spaceship.star_gravity_well - travel_speed / Zone.travel_cost_star_gravity)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-exiting-star-gravity"}
else
spaceship.near_stellar_object = nil
spaceship.stellar_position = Util.move_to(spaceship.stellar_position, destination_stellar_position,
travel_speed / Zone.travel_cost_interstellar)
spaceship.travel_message = {"space-exploration.spaceship-travel-message-navigating-interstellar"}
end
end
end
Spaceship.set_light(spaceship, ship_surface)
end
function Spaceship.spaceship_tick(spaceship)
if not spaceship.console and spaceship.console.valid then
spaceship.check_message= {"space-exploration.spaceship-check-message-no-console"}
spaceship.integrity_valid = false
end
-- Pause inserters, workaround for https://forums.factorio.com/viewtopic.php?f=58&t=89035
-- Note: production machines should NOT be included as some are supposed to be disabled on specific surfaces.
if spaceship.entities_to_restore and spaceship.entities_to_restore_tick < game.tick then
for _, storedState in pairs(spaceship.entities_to_restore) do
if storedState.entity and storedState.entity.valid then
storedState.entity.active = storedState.active
end
end
spaceship.entities_to_restore = nil
end
if spaceship.own_surface_index or (spaceship.console and spaceship.console.valid) then
-- integrity check
if spaceship.is_doing_check then
if spaceship.own_surface_index and spaceship.known_floor_tiles then
-- need to tick faster on bigger ships
for i = 0, math.ceil(spaceship.known_floor_tiles / 1000) do
-- tick once for each 1000 tiles
if spaceship.is_doing_check then
Spaceship.integrity_check_tick(spaceship)
end
end
else
Spaceship.integrity_check_tick(spaceship)
end
elseif game.tick % (Spaceship.integrity_pulse_interval) == 0 and spaceship.console and spaceship.console.valid and spaceship.console.energy > 0 then
Spaceship.start_integrity_check(spaceship)
end
if spaceship.console and spaceship.console.valid and (game.tick % 60) == 0 then
-- read signals
local red = spaceship.console.get_circuit_network(defines.wire_type.red)
local green = spaceship.console.get_circuit_network(defines.wire_type.green)
-- set speed
spaceship.target_speed = nil
local signal_target_speed = util.signal_from_wires(red, green, Spaceship.signal_for_speed)
if signal_target_speed > 0 then
spaceship.target_speed = signal_target_speed + 0.5
elseif signal_target_speed < 0 then
spaceship.stopped = true
spaceship.target_speed = nil
end
-- 0 means no change
-- set destination
for signal_name, type in pairs(Zone.signal_to_zone_type) do
local value = util.signal_from_wires(red, green, {type = "virtual", name = signal_name})
if value > 0 then
local zone = Zone.from_zone_index(value)
if zone and zone.type == type then
if Zone.is_visible_to_force(zone, spaceship.force_name) or global.debug_view_all_zones then
if (not spaceship.destination) or (spaceship.destination.type == "zone" and spaceship.destination.index ~= value) then
spaceship.destination = { type = "zone", index = value }
Spaceship.update_output_combinator(spaceship)
end
break
end
end
end
end
-- TODO: allow spacwhip as destination
-- launch
if not spaceship.own_surface_index then
if (red and red.get_signal(Spaceship.signal_for_launch) > 0)
or (green and green.get_signal(Spaceship.signal_for_launch) > 0) then
spaceship.is_launching = true
spaceship.is_landing = false
Spaceship.start_integrity_check(spaceship)
end
end
if spaceship.own_surface_index
and spaceship.destination
and spaceship.destination.type == "zone"
and Spaceship.is_near_destination(spaceship) then
local using_left = util.signal_from_wires(red, green, Spaceship.signal_for_anchor_using_left)
local to_right = util.signal_from_wires(red, green, Spaceship.signal_for_anchor_to_right)
local using_right = util.signal_from_wires(red, green, Spaceship.signal_for_anchor_using_right)
local to_left = util.signal_from_wires(red, green, Spaceship.signal_for_anchor_to_left)
if (using_left ~= 0 and to_right ~= 0) or (using_right ~= 0 and to_left ~= 0) then
local spaceship_surface = spaceship.console.surface
local destination_surface = Zone.get_surface(Zone.from_zone_index(spaceship.destination.index))
if destination_surface then
local spaceship_clamps = spaceship_surface.find_entities_filtered{name=Spaceship.name_spaceship_clamp_keep}
local destination_clamps = destination_surface.find_entities_filtered{name=Spaceship.name_spaceship_clamp_keep}
if using_left ~= 0 and to_right ~= 0 then
local spaceship_clamp
for _, s_clamp in pairs(spaceship_clamps) do
if s_clamp.direction == defines.direction.west then
local comb = s_clamp.get_or_create_control_behavior()
local signal = comb.get_signal(1)
if signal and signal.count == using_left then
spaceship_clamp = s_clamp
break
end
end
end
local destination_clamp
if spaceship_clamp then
for _, d_clamp in pairs(destination_clamps) do
if d_clamp.direction == defines.direction.east then
local comb = d_clamp.get_or_create_control_behavior()
local signal = comb.get_signal(1)
if signal and signal.count == to_right then
destination_clamp = d_clamp
break
end
end
end
end
if destination_clamp and spaceship_clamp then
-- try to land based on the relative offsets
local position = {
x = -1 * spaceship_clamp.position.x + 2,
y = -1 * spaceship_clamp.position.y
}
position = util.vectors_add(position, destination_clamp.position)
Spaceship.land_at_position(spaceship, position, true)
return --?
end
end
if using_right ~= 0 and to_left ~= 0 then
local spaceship_clamp
for _, s_clamp in pairs(spaceship_clamps) do
if s_clamp.direction == defines.direction.east then
local comb = s_clamp.get_or_create_control_behavior()
local signal = comb.get_signal(1)
if signal and signal.count == using_right then
spaceship_clamp = s_clamp
break
end
end
end
local destination_clamp
if spaceship_clamp then
for _, d_clamp in pairs(destination_clamps) do
if d_clamp.direction == defines.direction.west then
local comb = d_clamp.get_or_create_control_behavior()
local signal = comb.get_signal(1)
if signal and signal.count == to_left then
destination_clamp = d_clamp
break
end
end
end
end
if destination_clamp and spaceship_clamp then
-- try to land based on the relative offsets
local position = {
x = -1 * spaceship_clamp.position.x - 2,
y = -1 * spaceship_clamp.position.y
}
position = util.vectors_add(position, destination_clamp.position)
Spaceship.land_at_position(spaceship, position, true)
return --?
end
end
end
end
end
end
if spaceship.own_surface_index then
-- space upkeep
Spaceship.surface_tick(spaceship)
if spaceship.target_speed and spaceship.is_moving == false and spaceship.stopped then
spaceship.stopped = false
end
-- navigation
if spaceship.integrity_valid
and spaceship.destination
and not spaceship.stopped
and not Spaceship.is_near_destination(spaceship) then
-- wants to move and can move
if not spaceship.is_moving then
spaceship.is_moving = true
Spaceship.activate_engines(spaceship)
Spaceship.start_integrity_check(spaceship)
end
if (game.tick % 6) == 0 then
if spaceship.target_speed and spaceship.is_moving and spaceship.engines then
if spaceship.speed > spaceship.target_speed then
for _, engine in pairs(spaceship.engines) do
if engine.entity.valid and math.random() < 1/table_size(spaceship.engines) then
Log.trace("slow")
engine.entity.active = false
end
end
else
for _, engine in pairs(spaceship.engines) do
if engine.entity.valid and math.random() < 1/table_size(spaceship.engines) then
engine.entity.active = true
end
end
end
end
end
Spaceship.move_to_destination(spaceship)
else
-- can't move
if spaceship.is_moving then
spaceship.is_moving = false
Spaceship.deactivate_engines(spaceship)
end
if Spaceship.is_near_destination(spaceship) then
spaceship.speed = 0
spaceship.travel_message = {"space-exploration.spaceship-travel-message-at-destination"}
end
if spaceship.speed > 1 then
spaceship.speed = spaceship.speed * (1 - Spaceship.space_drag)
elseif spaceship.speed > 0 then
spaceship.speed = 0
end
end
end
else
Spaceship.destroy(spaceship)
return
end
if game.tick % 60 == 0 then
Spaceship.update_output_combinator(spaceship)
end
end
function Spaceship.update_output_combinator(spaceship)
if not spaceship.console and spaceship.console.valid then return end
if not (spaceship.console_output and spaceship.console_output.valid) then
spaceship.console_output = Spaceship.get_make_console_output(spaceship.console)
end
if spaceship.console_output and spaceship.console_output.valid then
local ctrl = spaceship.console_output.get_or_create_control_behavior()
local slot = 1
-- Spaceship ID
ctrl.set_signal(slot, {signal=Spaceship.signal_for_own_spaceship_id, count=spaceship.index})
slot = slot + 1
-- Speed
if spaceship.is_moving and spaceship.speed > 0 then
ctrl.set_signal(slot, {signal=Spaceship.signal_for_speed, count=math.max(1, spaceship.speed)})
elseif spaceship.own_surface_index then
ctrl.set_signal(slot, {signal=Spaceship.signal_for_speed, count=-1}) -- stopped
else
ctrl.set_signal(slot, {signal=Spaceship.signal_for_speed, count=-2}) -- anchored
end
slot = slot + 1
-- Distance
if (not spaceship.distance_to_destination_tick) or spaceship.distance_to_destination_tick + 60 < game.tick then
spaceship.distance_to_destination = Spaceship.get_distance_to_destination(spaceship)
spaceship.distance_to_destination_tick = game.tick
end
if spaceship.destination and Spaceship.is_near_destination(spaceship) then
ctrl.set_signal(slot, {signal=Spaceship.signal_for_distance, count=-1})
elseif spaceship.destination and Spaceship.is_at_destination(spaceship) then
ctrl.set_signal(slot, {signal=Spaceship.signal_for_distance, count=-2})
elseif spaceship.distance_to_destination and spaceship.distance_to_destination > 0 then
ctrl.set_signal(slot, {signal=Spaceship.signal_for_distance, count=math.max(1, spaceship.distance_to_destination)})
else --no destination
ctrl.set_signal(slot, {signal=Spaceship.signal_for_distance, count=-3})
end
slot = slot + 1
-- Destination
if spaceship.destination then
if spaceship.destination.type == "spaceship" then
ctrl.set_signal(slot, {signal=Spaceship.signal_for_destination_spaceship, count=spaceship.destination.index})
elseif spaceship.destination.type == "zone" then
local zone = Spaceship.get_destination_zone(spaceship)
local signal_name = Zone.get_signal_name(zone)
ctrl.set_signal(slot, {signal={type="virtual", name=signal_name}, count=zone.index})
else
ctrl.set_signal(slot, {signal=Spaceship.signal_for_destination_spaceship, count=0})
end
else
ctrl.set_signal(slot, {signal=Spaceship.signal_for_destination_spaceship, count=0})
end
slot = slot + 1
-- Anchor
if spaceship.zone_index then
ctrl.set_signal(slot, {signal={type = "virtual", name = "signal-A"}, count=spaceship.zone_index})
else
ctrl.set_signal(slot, {signal={type = "virtual", name = "signal-A"}, count=0})
end
slot = slot + 1
end
end
function Spaceship.on_tick(event)
if global.spaceships then
for _, spaceship in pairs(global.spaceships) do
Spaceship.spaceship_tick(spaceship)
end
end
for _, player in pairs(game.connected_players) do
local playerdata = get_make_playerdata(player)
if playerdata and playerdata.anchor_scouting_for_spaceship_index then
Spaceship.anchor_scouting_tick(player, Spaceship.from_index(playerdata.anchor_scouting_for_spaceship_index))
end
Spaceship.gui_update(player)
end
end
Event.addListener(defines.events.on_tick, Spaceship.on_tick)
--[[
function Spaceship.on_chunk_generated(event)
local area = event.area
local surface = event.surface
local spaceship = Spaceship.from_own_surface_index(surface.index)
if spaceship then
area.right_bottom.x = area.right_bottom.x + 0.99
area.right_bottom.y = area.right_bottom.y + 0.99
entities = surface.find_entities_filtered{
area = area
}
for _, entity in pairs(entities) do -- rocks
entity.destroy()
end
local bad_tiles = surface.find_tiles_filtered{name={name_asteroid_tile, "out-of-map"}}
local set_tiles = {}
for _, tile in pairs(bad_tiles) do
table.insert(set_tiles, {position = tile.position, name=name_space_tile})
surface.set_hidden_tile(tile.position, name_space_tile)
end
surface.set_tiles(set_tiles)
end
end
Event.addListener(defines.events.on_chunk_generated, Spaceship.on_chunk_generated)
]]--
function Spaceship.flash_tile(surface, position, color, time)
local a = (color.a or 1)
rendering.draw_rectangle{
color = {r = color.r * a, g = color.g * a, b = color.b * a, a = a},
filled = true,
left_top = position,
right_bottom = {(position.x or position[1])+1, (position.y or position[2])+1},
surface = surface,
time_to_live = time
}
end
function Spaceship.calculate_integrity_stress(spaceship, area)
-- use all tiles
local surface = Spaceship.get_own_surface(spaceship)
if not surface then surface = spaceship.console.surface end
if not area then --- whole surface
spaceship.tile_count = surface.count_tiles_filtered{name = Spaceship.names_spaceship_floors}
spaceship.wall_count = surface.count_entities_filtered{name = Spaceship.names_spaceship_walls}
else
spaceship.tile_count = spaceship.known_floor_tiles + spaceship.known_bulkhead_tiles
spaceship.wall_count = 0
local walls = surface.find_entities_filtered{name = Spaceship.names_spaceship_walls}
for _, wall in pairs(walls) do
local tile_pos = Util.position_to_tile(wall.position)
if spaceship.known_tiles[tile_pos.x] and spaceship.known_tiles[tile_pos.x][tile_pos.y] == Spaceship.tile_status.bulkhead_console_connected then
spaceship.wall_count = spaceship.wall_count + 1
end
end
end
spaceship.container_slot_count = 0
local containers = surface.find_entities_filtered{ type = {"container", "logistic-container", "car", "spider-vehicle"}, area = area}
for _, container in pairs(containers) do
local tile_pos = Util.position_to_tile(container.position)
if area == nil or (spaceship.known_tiles[tile_pos.x] and spaceship.known_tiles[tile_pos.x][tile_pos.y] == Spaceship.tile_status.floor_console_connected) then
if container.type == "car" or container.type == "spider-vehicle" then
spaceship.container_slot_count = spaceship.container_slot_count + container.prototype.get_inventory_size(defines.inventory.car_trunk)
else
spaceship.container_slot_count = spaceship.container_slot_count + container.prototype.get_inventory_size(defines.inventory.chest)
end
end
end
spaceship.container_fluid_capacity = 0
local containers = surface.find_entities_filtered{type = {"storage-tank"}}
for _, container in pairs(containers) do
local mult = string.find(container.name, "booster", 1, true) and 0.5 or 1
local tile_pos = Util.position_to_tile(container.position)
if area == nil or (spaceship.known_tiles[tile_pos.x] and spaceship.known_tiles[tile_pos.x][tile_pos.y] == Spaceship.tile_status.floor_console_connected) then
if container.fluidbox and #container.fluidbox > 0 then
local i = 1
for i = 1, #container.fluidbox do
spaceship.container_fluid_capacity = spaceship.container_fluid_capacity + mult * container.fluidbox.get_capacity(i)
end
end
end
end
-- container slot is 0.5 or 24 for a normal container 4800 ish items. Cost is 24
-- container can caryy 48 * 10 barrels = 24k fluid
-- storage tank is 5, 25k fluids = 250 effective items. Cost is 12.5 (50% discount)
-- booster tanks cost50% less
spaceship.integrity_stress_structure = spaceship.tile_count - spaceship.wall_count / 2
spaceship.integrity_stress_container = spaceship.container_slot_count/2 + spaceship.container_fluid_capacity / 2000
spaceship.speed_multiplier = 1
if game.active_mods["Krastorio2"] then
local reactors = 0
local entities = surface.find_entities_filtered{name = {"kr-antimatter-reactor"}}
for _, entity in pairs(entities) do
local tile_pos = Util.position_to_tile(entity.position)
if area == nil or (spaceship.known_tiles[tile_pos.x] and spaceship.known_tiles[tile_pos.x][tile_pos.y] == Spaceship.tile_status.floor_console_connected) then
reactors = reactors + 1
spaceship.integrity_stress_structure = spaceship.integrity_stress_structure + 100
spaceship.integrity_stress_container = spaceship.integrity_stress_container + 100
end
end
if reactors > 0 then
spaceship.speed_multiplier = 0.5
end
end
-- linked-containers
local entities = surface.find_entities_filtered{type = {"linked-container"}}
for _, entity in pairs(entities) do
local tile_pos = Util.position_to_tile(entity.position)
if area == nil or (spaceship.known_tiles[tile_pos.x] and spaceship.known_tiles[tile_pos.x][tile_pos.y] == Spaceship.tile_status.floor_console_connected) then
spaceship.integrity_stress_container = spaceship.integrity_stress_container + 1000
end
end
spaceship.integrity_stress = math.max(spaceship.integrity_stress_structure, spaceship.integrity_stress_container)
if spaceship.integrity_stress > spaceship.integrity_limit then
spaceship.integrity_valid = false
spaceship.check_message = {"space-exploration.spaceship-check-message-failed-stress"}
end
end
function Spaceship.check_integrity_stress(spaceship)
spaceship.integrity_limit = Spaceship.get_integrity_limit(game.forces[spaceship.force_name])
if spaceship.own_surface_index then
-- use all tiles
Spaceship.calculate_integrity_stress(spaceship, nil) -- whole area
elseif not (spaceship.console and spaceship.console.valid) then
spaceship.integrity_valid = false
spaceship.check_message= {"space-exploration.spaceship-check-message-no-console"}
elseif not spaceship.integrity_valid then
-- already invalid
elseif not spaceship.known_bounds then
spaceship.integrity_valid = false
spaceship.check_message= {"space-exploration.spaceship-check-message-failed-unknown-bounds"}
elseif spaceship.integrity_valid and spaceship.known_bounds and spaceship.known_tiles then
Spaceship.calculate_integrity_stress(spaceship, spaceship.known_bounds) -- limited area
--[[ TODO: use improved know tiles approach
and spaceship.known_tile_count and spaceship.known_wall_count then
spaceship.integrity_stress = spaceship.known_tile_count - spaceship.known_wall_count / 2
if spaceship.integrity_stress > spaceship.integrity_limit then
spaceship.integrity_valid = false
spaceship.check_message = "Fail: Structural integrity stress exceeds technology limit."
end
]]--
end
end
function Spaceship.start_integrity_check(spaceship, alpha)
if alpha then
spaceship.check_flash_alpha = alpha
end
spaceship.is_doing_check = true
end
function Spaceship.stop_integrity_check(spaceship)
spaceship.check_flash_alpha = nil
spaceship.is_doing_check = nil
spaceship.check_stage = nil
spaceship.pending_tiles = nil
spaceship.streamline = nil
if spaceship.integrity_valid and spaceship.check_tiles then
-- success
spaceship.known_tiles = table.deepcopy(spaceship.check_tiles)
spaceship.check_tiles = nil
-- get the average for surface transfer
local min_x = nil
local max_x = nil
local min_y = nil
local max_y = nil
local floor_tiles = 0
local bulkhead_tiles = 0
local front_tiles = {} -- x:y
for x, x_tiles in pairs(spaceship.known_tiles) do
if min_x == nil or x < min_x then min_x = x end
if max_x == nil or x > max_x then max_x = x end
for y, status in pairs(x_tiles) do
if status == Spaceship.tile_status.floor_console_connected
or status == Spaceship.tile_status.bulkhead_console_connected then
if min_y == nil or y < min_y then min_y = y end
if max_y == nil or y > max_y then max_y = y end
if status == Spaceship.tile_status.floor_console_connected then
floor_tiles = floor_tiles + 1
else
bulkhead_tiles = bulkhead_tiles + 1
end
if (not front_tiles[x]) or y < front_tiles[x] then
front_tiles[x] = y
end
end
end
end
max_x = max_x + 1 -- whole tile
max_y = max_y + 1 -- whole tile
spaceship.known_floor_tiles = floor_tiles
spaceship.known_bulkhead_tiles = bulkhead_tiles
spaceship.known_bounds = {left_top = {x = min_x, y = min_y}, right_bottom={x = max_x, y = max_y}}
spaceship.known_tiles_average_x = math.floor((min_x + max_x)/2)
spaceship.known_tiles_average_y = math.floor((min_y + max_y)/2)
local front_tiles_by_y = {} -- y:count
local front_tiles_by_y_left = {} -- y:count
local front_tiles_by_y_right = {} -- y:count
local max_flat = 0
for x, y in pairs(front_tiles) do
front_tiles_by_y[y] = (front_tiles_by_y[y] or 0) + 1
if front_tiles_by_y[y] > max_flat then
max_flat = front_tiles_by_y[y]
end
if x < spaceship.known_tiles_average_x then
front_tiles_by_y_left[y] = (front_tiles_by_y_left[y] or 0) + 1
else
front_tiles_by_y_right[y] = (front_tiles_by_y_right[y] or 0) + 1
end
end
local width = max_x - min_x
-- max_flat == width = 0
-- max_flat == width / 3 = 1
local streamline_flatness = math.min(1, (1 - (max_flat-2) / (width-2)) * 1.5)
local streamline_left = math.min(1, 2 * (table_size(front_tiles_by_y_left)-1) / (math.max(0.5, (width-1) / 2)))
local streamline_right = math.min(1, 2 * (table_size(front_tiles_by_y_right)-1) / (math.max(0.5, (width-1) / 2)))
spaceship.streamline = (streamline_flatness + streamline_left + streamline_right
+ 3 * math.min(streamline_flatness, math.min(streamline_left, streamline_right))) / 6
--spaceship.streamline = math.min(1, 3.5 * (table_size(front_tiles_by_y)-1) / width)
-- if it is symetrical then 1/2 would be max (excluding the -1)
-- use 1/3.5 as max so there can be a few flat areas
Log.trace("streamline flat "..streamline_flatness.." left "..streamline_left.." right "..streamline_right)
--Log.trace("streamline "..spaceship.streamline )
if spaceship.own_surface_index then
Spaceship.find_own_surface_engines(spaceship)
end
else
spaceship.integrity_valid = false
spaceship.check_tiles = nil
spaceship.check_message = {"space-exploration.spaceship-check-message-failed-empty"}
end
--spaceship.check_message = nil
if spaceship.console and spaceship.console.valid then
--spaceship.console.force.print("Spaceship integrity check complete.")
end
Spaceship.check_integrity_stress(spaceship)
if spaceship.is_launching then
Spaceship.launch(spaceship)
end
end
function Spaceship.integrity_check_tick(spaceship)
if not(spaceship.console and spaceship.console.valid) then
spaceship.check_message= {"space-exploration.spaceship-check-message-no-console"}
spaceship.integrity_valid = false
Spaceship.stop_integrity_check(spaceship)
return
end
local surface = spaceship.console.surface
if not (spaceship.check_stage and spaceship.check_tiles and spaceship.pending_tiles) then
local start_tile = surface.get_tile(spaceship.console.position)
if Spaceship.is_floor(start_tile.name) then
Spaceship.check_message = nil
Spaceship.is_doing_check = true
-- floor tiles is a 2d array x then y
spaceship.check_tiles = {}
spaceship.check_tiles[start_tile.position.x] = {}
spaceship.check_tiles[start_tile.position.x][start_tile.position.y] = Spaceship.tile_status.floor
spaceship.pending_tiles = {}
spaceship.pending_tiles[start_tile.position.x] = {}
spaceship.pending_tiles[start_tile.position.x][start_tile.position.y] = true
spaceship.check_stage = "floor-connectivity"
spaceship.check_message= {"space-exploration.spaceship-check-message-checking-console-floor"}
else
spaceship.check_message= {"space-exploration.spaceship-check-message-failed-console-floor"}
spaceship.integrity_valid = false
Spaceship.stop_integrity_check(spaceship)
return
end
end
if not (spaceship.check_tiles and spaceship.pending_tiles) then return end
local alpha = spaceship.check_flash_alpha or 0.05
-- do a round of checking
-- check_tiles. List of tiles to check this tick.
-- pending_tiles should always exists in check_tiles
-- it basically justs keeps a lst of which ones to search
local next_pending_tiles = {}
local changed = false
if spaceship.check_stage == "floor-connectivity" then
for x, x_tiles in pairs(spaceship.pending_tiles) do
for y, yes in pairs(x_tiles) do
if spaceship.check_tiles[x][y] == Spaceship.tile_status.floor
or spaceship.check_tiles[x][y] == Spaceship.tile_status.bulkhead then
for d = 1, 4 do -- 4 way direction
local cx = x + (d == 2 and 1 or (d == 4 and -1 or 0))
local cy = y + (d == 1 and -1 or (d == 3 and 1 or 0))
if not (spaceship.check_tiles[cx] and spaceship.check_tiles[cx][cy]) then -- unknown tile
changed = true
local tile = surface.get_tile({cx, cy})
local wall_count = surface.count_entities_filtered{
area = Util.position_to_area({x = cx + 0.5, y = cy + 0.5}, 0.4),
name = Spaceship.names_spaceship_bulkheads
}
if tile.valid and Spaceship.is_floor(tile.name) then
spaceship.check_tiles[cx] = spaceship.check_tiles[cx] or {}
if wall_count > 0 then
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.bulkhead
Spaceship.flash_tile(surface, {cx, cy}, {r = 0, g = 0, b = 1, a = alpha}, 5)
else
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.floor
Spaceship.flash_tile(surface, {cx, cy}, {r = 0, g = 1, b = 0, a = alpha}, 5)
end
next_pending_tiles[cx] = next_pending_tiles[cx] or {}
next_pending_tiles[cx][cy] = true
else
spaceship.check_tiles[cx] = spaceship.check_tiles[cx] or {}
if wall_count > 0 then
local clamps = surface.count_entities_filtered{
area = Util.position_to_area({x = cx + 0.5, y = cy + 0.5}, 0.4),
name = Spaceship.name_spaceship_clamp_keep
}
if clamps > 0 then
-- if it is a clamp sticking out of the craft treat it as exterior,
-- otherwise it is treated as unstaeble bulkhead and takes damage
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.exterior
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 0, b = 1, a = alpha}, 5)
else
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.bulkhead_exterior
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 0, b = 0, a = alpha}, 5)
end
else
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.exterior
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 0, b = 1, a = alpha}, 5)
end
end
end
end
end
end
end
if changed == false then
-- all connected tiles have been found
-- if in space and if moving detach disconnected tiles
if spaceship.is_moving then
local surface = Spaceship.get_own_surface(spaceship)
if surface then
local set_tiles = {}
local all_tiles = surface.find_tiles_filtered{name=Spaceship.names_spaceship_floors}
for _, tile in pairs(all_tiles) do
if not (spaceship.check_tiles[tile.position.x] and spaceship.check_tiles[tile.position.x][tile.position.y]) then
local stack = tile.prototype.items_to_place_this[1]
table.insert(set_tiles, {name = name_space_tile, position = tile.position, ghost_name = tile.name, stack = stack})
Spaceship.flash_tile(surface, tile.position, {r = 1, g = 0, b = 0, a = alpha}, 120)
end
end
if #set_tiles > 0 then
surface.set_tiles(set_tiles)
for _, tile in pairs(set_tiles) do
if Util.table_contains(Spaceship.names_spaceship_floors, tile.ghost_name) then
surface.create_entity{name = "tile-ghost", inner_name = tile.ghost_name, force = spaceship.force_name, position=tile.position}
end
end
end
end
end
changed = true
spaceship.check_stage = "containment"
spaceship.check_message = {"space-exploration.spaceship-check-message-checking-containment"}
for x, x_tiles in pairs(spaceship.check_tiles) do
for y, status in pairs(x_tiles) do
if status == Spaceship.tile_status.exterior
or status == Spaceship.tile_status.bulkhead_exterior then
next_pending_tiles[x] = next_pending_tiles[x] or {}
next_pending_tiles[x][y] = true
end
end
end
end
elseif spaceship.check_stage == "containment" then
for x, x_tiles in pairs(spaceship.pending_tiles) do
for y, yes in pairs(x_tiles) do
if spaceship.check_tiles[x][y] == Spaceship.tile_status.exterior
or spaceship.check_tiles[x][y] == Spaceship.tile_status.bulkhead_exterior
or spaceship.check_tiles[x][y] == Spaceship.tile_status.floor_exterior then
for cx = x-1, x+1 do
for cy = y-1, y+1 do
if spaceship.check_tiles[cx] and spaceship.check_tiles[cx][cy]
and spaceship.check_tiles[cx][cy] == Spaceship.tile_status.floor then
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.floor_exterior
changed = true
next_pending_tiles[cx] = next_pending_tiles[cx] or {}
next_pending_tiles[cx][cy] = true
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 1, b = 0, a = alpha}, 30)
end
end
end
end
end
end
if changed == false then
changed = true
-- convert non-exterior floor to interior
for x, x_tiles in pairs(spaceship.check_tiles) do
for y, status in pairs(x_tiles) do
if status == Spaceship.tile_status.floor then
spaceship.check_tiles[x][y] = Spaceship.tile_status.floor_interior
Spaceship.flash_tile(surface, {x, y}, {r = 0, g = 0, b = 1, a = alpha}, 5)
end
end
end
local console_tile_x = math.floor(spaceship.console.position.x)
local console_tile_y = math.floor(spaceship.console.position.y)
if spaceship.check_tiles and spaceship.check_tiles[console_tile_x] and spaceship.check_tiles[console_tile_x][console_tile_y] == Spaceship.tile_status.floor_interior then
spaceship.check_tiles[console_tile_x][console_tile_y] = Spaceship.tile_status.floor_console_connected
next_pending_tiles[console_tile_x] = {}
next_pending_tiles[console_tile_x][console_tile_y] = true
spaceship.check_stage = "console-connectivity"
spaceship.check_message = {"space-exploration.spaceship-check-message-checking-connectivity"}
else
for x, x_tiles in pairs(spaceship.check_tiles) do
for y, status in pairs(x_tiles) do
if status == Spaceship.tile_status.exterior
or status == Spaceship.tile_status.bulkhead_exterior then
Spaceship.flash_tile(surface, {x, y}, {r = 1, g = 0, b = 0, a = alpha}, 120)
end
end
end
spaceship.integrity_valid = false
spaceship.check_message = {"space-exploration.spaceship-check-message-failed-containment"}
return Spaceship.stop_integrity_check(spaceship)
end
end
elseif spaceship.check_stage == "console-connectivity" then
for x, x_tiles in pairs(spaceship.pending_tiles) do
for y, yes in pairs(x_tiles) do
if spaceship.check_tiles[x][y] == Spaceship.tile_status.floor_console_connected
or spaceship.check_tiles[x][y] == Spaceship.tile_status.bulkhead_console_connected then
for d = 1, 4 do -- 4 way direction
local cx = x + (d == 2 and 1 or (d == 4 and -1 or 0))
local cy = y + (d == 1 and -1 or (d == 3 and 1 or 0))
if spaceship.check_tiles[cx] and spaceship.check_tiles[cx][cy] and
(spaceship.check_tiles[cx][cy] == Spaceship.tile_status.floor_interior
or spaceship.check_tiles[cx][cy] == Spaceship.tile_status.bulkhead) then
if spaceship.check_tiles[cx][cy] == Spaceship.tile_status.floor_interior then
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.floor_console_connected
else
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.bulkhead_console_connected
end
Spaceship.flash_tile(surface, {cx, cy}, {r = 0, g = 1, b = 1, a = alpha}, 5)
changed = true
next_pending_tiles[cx] = next_pending_tiles[cx] or {}
next_pending_tiles[cx][cy] = true
end
end
end
end
end
if changed == false then
-- completed the check
spaceship.check_message = {"space-exploration.spaceship-check-message-passed"}
spaceship.integrity_valid = true
local set_tiles = {}
local reset = false
for x, x_tiles in pairs(spaceship.check_tiles) do
for y, status in pairs(x_tiles) do
if not (status == Spaceship.tile_status.floor_console_connected
or status == Spaceship.tile_status.bulkhead_console_connected
or status == Spaceship.tile_status.exterior) then
spaceship.check_message = {"space-exploration.spaceship-check-message-unstable"}
Spaceship.flash_tile(surface, {x, y}, {r = 1, g = 0, b = 0, a = alpha}, 120)
-- detatch
if spaceship.own_surface_index and spaceship.is_moving then
local support = 1 -- it will count self
for cx = x-1, x+1 do
for cy = y-1, y+1 do
if spaceship.check_tiles[cx] and spaceship.check_tiles[cx][cy] then
if spaceship.check_tiles[cx][cy] ~= Spaceship.tile_status.exterior then
support = support + 1
if spaceship.check_tiles[cx][cy] == Spaceship.bulkhead_console_connected then
support = support + 2
end
end
end
end
end
if support <= 6 then -- has a chance to be removed
reset = true
if support - math.random(2) <= 4 then
local entities = surface.find_entities({{x,y}, {x+1,y+1}})
local remove = true
for _, entity in pairs(entities) do
if entity and entity.valid and entity.type ~= "character" and entity.health then
entity.damage(150, "neutral", "explosion")
remove = false
end
end
if remove then
local tile = surface.get_tile(x,y)
table.insert(set_tiles, {name = name_space_tile, ghost_name=tile.name, position = {x,y}})
end
end
end
end
end
end
end
if #set_tiles > 0 then
spaceship.check_message = {"space-exploration.spaceship-check-message-valid-but-disconnecting"}
surface.print({"space-exploration.spaceship-warning-sections-disconnecting"})
surface.set_tiles(set_tiles)
for _, tile in pairs(set_tiles) do
if Util.table_contains(Spaceship.names_spaceship_floors, tile.ghost_name) then
surface.create_entity{name = "tile-ghost", inner_name = tile.ghost_name, force = spaceship.force_name, position=tile.position}
end
end
end
Spaceship.stop_integrity_check(spaceship)
if reset then
Spaceship.start_integrity_check(spaceship)
return
else
Spaceship.get_launch_energy(spaceship)
return
end
end
end
if changed then
spaceship.pending_tiles = next_pending_tiles
else
spaceship.integrity_valid = false
spaceship.check_message = {"space-exploration.spaceship-check-message-did-not-complete"}
return Spaceship.stop_integrity_check(spaceship)
end
end
function Spaceship.on_init(event)
global.spaceships = {}
end
Event.addListener("on_init", Spaceship.on_init, true)
return Spaceship