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.
2635 lines
112 KiB
2635 lines
112 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.engine_efficiency_blocked = 0.60
|
|
Spaceship.engine_efficiency_unblocked = 1
|
|
Spaceship.engine_efficiency_unblocked_taper = 20
|
|
Spaceship.engine_efficiency_side = 0.01
|
|
Spaceship.engines = {
|
|
[mod_prefix .. "spaceship-rocket-engine"] = { name = mod_prefix .. "spaceship-rocket-engine", thrust = 100 / 5, max_energy = 1837, smoke_trigger = mod_prefix .. "spaceship-engine-smoke" },
|
|
[mod_prefix .. "spaceship-ion-engine"] = { name = mod_prefix .. "spaceship-ion-engine", thrust = 250 / 5, max_energy = 183700, smoke_trigger = mod_prefix .. "spaceship-engine-smoke" },
|
|
[mod_prefix .. "spaceship-antimatter-engine"]= { name = mod_prefix .. "spaceship-antimatter-engine", thrust = 500 / 5, max_energy = 18370, smoke_trigger = mod_prefix .. "spaceship-engine-smoke" }
|
|
}
|
|
Spaceship.names_engines = {}
|
|
Spaceship.names_smoke_trigger = {}
|
|
|
|
Spaceship.names_booster_tanks = {
|
|
mod_prefix .. "spaceship-rocket-booster-tank",
|
|
mod_prefix .. "spaceship-ion-booster-tank",
|
|
mod_prefix .. "spaceship-antimatter-booster-tank"
|
|
}
|
|
Spaceship.ion_stream_energy = 4000000 -- 2x rocket fuel
|
|
|
|
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",
|
|
SpaceshipClamp.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)
|
|
if engine.smoke_trigger then
|
|
table.insert(Spaceship.names_smoke_trigger, engine.smoke_trigger)
|
|
end
|
|
end
|
|
|
|
Spaceship.integrity_affecting_types = {
|
|
{type = "linked-container", integrity_stress_container = 1000},
|
|
}
|
|
Spaceship.integrity_affecting_names = {
|
|
{name = mod_prefix.."nexus", integrity_stress_container = 2000},
|
|
{mod = "Krastorio2", name = "kr-antimatter-reactor", integrity_stress_structure = 100, integrity_stress_container = 100, max_speed_multiplier = 0.5},
|
|
}
|
|
|
|
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 = 135 * 1000
|
|
Spaceship.tick_interval_density = 60 -- must coincide with %60
|
|
Spaceship.tick_interval_move = 20 -- must coincide with %60
|
|
Spaceship.tick_interval_anchor = 5 -- must coincide with %60
|
|
Spaceship.tick_interval_gui = 5 -- must coincide with %60
|
|
|
|
Spaceship.tick_max_await = 60 * 10 -- 10 seconds
|
|
|
|
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
|
|
|
|
---@class Spaceship All data necessary to maintain the state of a spaceship
|
|
|
|
Spaceship.particle_speed_power = 0.75 -- 0.5 would be sqrt, 0 is static, 1 is linear with speed.
|
|
Spaceship.space_drag = 0.00135
|
|
Spaceship.minimum_impulse = 1/100
|
|
Spaceship.minimum_mass = 100
|
|
Spaceship.speed_taper = 250
|
|
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"
|
|
}
|
|
|
|
--[[========================================================================================
|
|
Helper functions for identifying if a tile or entity is a certain type of spaceship thing.
|
|
]]--
|
|
|
|
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
|
|
|
|
--[[========================================================================================
|
|
Helper functions for getting spaceship references.
|
|
]]--
|
|
|
|
function Spaceship.from_index(spaceship_index)
|
|
if global.spaceships then return global.spaceships[tonumber(spaceship_index)] end
|
|
end
|
|
|
|
function Spaceship.from_entity(entity)
|
|
if not entity then return end
|
|
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_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.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
|
|
|
|
--[[========================================================================================
|
|
Helper functions for spaceship states
|
|
]]
|
|
function Spaceship.is_on_own_surface(spaceship)
|
|
return spaceship.own_surface_index and not spaceship.awaiting_requests
|
|
end
|
|
|
|
--[[========================================================================================
|
|
Helper functions for getting the surfaces a spaceship cares about.
|
|
]]
|
|
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
|
|
|
|
--[[
|
|
Computes the spaceship integrity limit for a force.
|
|
]]
|
|
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
|
|
|
|
--[[
|
|
Computes the cost (in fuel) of launching a spaceship from its current surface.
|
|
]]
|
|
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
|
|
if Zone.is_space(zone) then
|
|
return 250 * spaceship.integrity_stress * Spaceship.energy_per_launch_integrity_delta_v
|
|
end
|
|
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
|
|
|
|
--- Computes and returns the launch energy needed for a spaceship. Also stores the value in spaceship.launch_energy
|
|
--- Returns reference to all the tanks used to make the calculation
|
|
---@param spaceship Spaceship spaceship data
|
|
function Spaceship.get_compute_launch_energy(spaceship)
|
|
spaceship.launch_energy = nil
|
|
local tanks = nil
|
|
local zone
|
|
if spaceship.zone_index then
|
|
zone = Zone.from_zone_index(spaceship.zone_index)
|
|
end
|
|
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
|
|
tanks = surface.find_entities_filtered{name = Spaceship.names_booster_tanks, area = spaceship.known_bounds}
|
|
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
|
|
if zone and Zone.is_space(zone) and fluidbox.name == "se-ion-stream" then
|
|
spaceship.launch_energy = spaceship.launch_energy + fluidbox.amount * Spaceship.ion_stream_energy
|
|
else
|
|
spaceship.launch_energy = spaceship.launch_energy + fluidbox.amount * game.fluid_prototypes[fluidbox.name].fuel_value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return spaceship.launch_energy, tanks
|
|
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 = spaceship.known_tiles_average_x, y = spaceship.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.destroy(spaceship)
|
|
if spaceship.zone_index or not Spaceship.is_on_own_surface(spaceship) 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 = SpaceshipGUI.name_spaceship_gui_root
|
|
for _, player in pairs(game.connected_players) do
|
|
local root = player.gui.left[gui_name]
|
|
if root and root.tags and root.tags.index == spaceship.index then
|
|
root.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
|
|
|
|
--[[========================================================================================
|
|
Helper functions for getting information about a spaceship's target destination
|
|
]]
|
|
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
|
|
|
|
--- Gets or makes the spaceship's own surface
|
|
---@param spaceship Spaceship spaceship data
|
|
function Spaceship.get_make_spaceship_surface(spaceship)
|
|
local current_surface = spaceship.console.surface
|
|
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")
|
|
elseif current_surface == ship_surface then
|
|
Log.trace("Same surface")
|
|
end
|
|
return ship_surface
|
|
end
|
|
|
|
--- Launch a spaceship
|
|
---@param spaceship Spaceship the spaceship data
|
|
function Spaceship.launch(spaceship)
|
|
if not spaceship.is_launching then Log.trace("Abort launch not is_launching") return end
|
|
|
|
local automated_launch = spaceship.is_launching_automatically
|
|
spaceship.is_launching = false
|
|
spaceship.is_launching_automatically = 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)
|
|
local _, tanks = Spaceship.get_compute_launch_energy(spaceship)
|
|
if not (required_energy and spaceship.launch_energy and spaceship.launch_energy >= required_energy) then return end
|
|
|
|
-- point of no return
|
|
log("spaceship launch start")
|
|
local ship_surface = Spaceship.get_make_spaceship_surface(spaceship)
|
|
|
|
local linked_containers = current_surface.find_entities_filtered{
|
|
area=spaceship.known_bounds,
|
|
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 total_energy = 0
|
|
local zone = Zone.from_zone_index(spaceship.zone_index)
|
|
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 = (Zone.is_space(zone) and fluidbox.name == "se-ion-stream") and Spaceship.ion_stream_energy or game.fluid_prototypes[fluidbox.name].fuel_value
|
|
table.insert(valid_tanks, tank)
|
|
table.insert(valid_tank_fuel, fluidbox.amount)
|
|
total_energy = total_energy + amount * energy_per_fuel
|
|
end
|
|
end
|
|
end
|
|
|
|
for i, tank in pairs(valid_tanks) do
|
|
local percent_required = required_energy / total_energy -- Remove x% from all booster tanks
|
|
local consume = math.min(valid_tank_fuel[i], math.ceil(percent_required * valid_tank_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
|
|
|
|
-- set the ship's location to the new statuses
|
|
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)
|
|
|
|
-- start generating chunks at the destination
|
|
spaceship.requests_made = SpaceshipClone.enqueue_generate_clone_to_area(spaceship, current_surface, ship_surface, {dx=0,dy=0})
|
|
|
|
if automated_launch then
|
|
-- await chunk generation before completing the launch
|
|
spaceship.await_start_tick = game.tick
|
|
spaceship.awaiting_requests = true
|
|
spaceship.clone_params = {
|
|
clone_from = current_surface,
|
|
clone_to = ship_surface,
|
|
clone_delta = {dx=0,dy=0}
|
|
}
|
|
else
|
|
-- immediate launch
|
|
SpaceshipClone.clone(spaceship, current_surface, ship_surface, {dx=0,dy=0})
|
|
end
|
|
log("spaceship launch end")
|
|
end
|
|
|
|
--- Land a spaceship
|
|
---@param spaceship Spaceship spaceship data
|
|
---@param position any the position at which to land
|
|
---@param ignore_average any if we are cloning based on the corner of the ship or its center
|
|
function Spaceship.land_at_position(spaceship, position, ignore_average)
|
|
if not (Spaceship.is_on_own_surface(spaceship) and spaceship.near and spaceship.near.type == "zone" and spaceship.known_tiles) then return end
|
|
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 destination_area = {
|
|
left_top = {
|
|
x = spaceship.known_bounds.left_top.x + offset_x,
|
|
y = spaceship.known_bounds.left_top.y + offset_y
|
|
},
|
|
right_bottom = {
|
|
x = spaceship.known_bounds.right_bottom.x + offset_x,
|
|
y = spaceship.known_bounds.right_bottom.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
|
|
ship_surface.print({"cant-build-reason.entity-in-the-way", {"space-exploration.ruin"}})
|
|
return
|
|
end
|
|
|
|
local landing_area_entities = {}
|
|
for x = spaceship.known_bounds.left_top.x, spaceship.known_bounds.right_bottom.x do
|
|
for y = spaceship.known_bounds.left_top.y, spaceship.known_bounds.right_bottom.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
|
|
log('spaceship land start')
|
|
Spaceship.deactivate_engines(spaceship)
|
|
|
|
local linked_containers = ship_surface.find_entities_filtered{
|
|
area=spaceship.known_bounds,
|
|
type="linked-container"
|
|
}
|
|
for _, linked_container in pairs(linked_containers) do
|
|
linked_container.link_id = 0
|
|
end
|
|
|
|
Spaceship.destroy_all_smoke_triggers(ship_surface)
|
|
SpaceshipObstacles.destroy(spaceship, ship_surface) -- destroy all the obstacles on the ship's surface
|
|
Zone.apply_markers(destination_zone) -- in case the surface exists
|
|
|
|
-- 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
|
|
|
|
-- request chunk generation at destination and then immediately land there
|
|
local clone_delta = {dx=offset_x,dy=offset_y}
|
|
SpaceshipClone.enqueue_generate_clone_to_area(spaceship, ship_surface, target_surface, clone_delta)
|
|
SpaceshipClone.clone(spaceship, ship_surface, target_surface, clone_delta, Spaceship.post_land_at_position)
|
|
log('spaceship land end')
|
|
end
|
|
|
|
--- Finishes landing the spaceship after the cloning procedure finishes
|
|
---@param spaceship any
|
|
---@param clone_from any
|
|
---@param clone_to any
|
|
---@param clone_delta any
|
|
function Spaceship.post_land_at_position(spaceship, clone_from, clone_to, clone_delta)
|
|
-- move non-character players before deleting the surface the ship was on
|
|
for _, player in pairs(game.connected_players) do
|
|
if player.surface == clone_from then
|
|
player.teleport({
|
|
x = player.position.x + clone_delta.dx,
|
|
y = player.position.y + clone_delta.dy
|
|
}, clone_to)
|
|
RemoteView.gui_update(player)
|
|
end
|
|
end
|
|
game.delete_surface(clone_from)
|
|
|
|
-- set the ship's location to the new statuses
|
|
spaceship.own_surface_index = nil
|
|
spaceship.particles = {}
|
|
spaceship.mineables = {}
|
|
spaceship.zone_index = spaceship.near.index
|
|
spaceship.near = nil
|
|
spaceship.stopped = true
|
|
spaceship.is_moving = false
|
|
spaceship.speed = 0
|
|
end
|
|
|
|
--- Decrements the number of requests being waited upon for a spaceship surface transfer whenever a chunk is generated
|
|
---@param event any
|
|
function Spaceship.on_chunk_generated(event)
|
|
if event.surface and string.find(event.surface.name, "spaceship-") then
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
if spaceship.clone_params and spaceship.requests_made and spaceship.clone_params.clone_to == event.surface then
|
|
spaceship.requests_made = spaceship.requests_made - 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_chunk_generated, Spaceship.on_chunk_generated)
|
|
|
|
--- Determines the rectangles necessary to somewhat quickly draw an image of the spaceship
|
|
--- that this player is anchor scouting. Necessary because drawing each individual tile
|
|
--- as a separate rectangle lags the game.
|
|
---@param player any
|
|
---@param spaceship Spaceship spaceship data
|
|
function Spaceship.get_make_anchor_scouting_cache(player, spaceship)
|
|
local playerdata = get_make_playerdata(player)
|
|
if not playerdata.anchor_scouting_cache then
|
|
playerdata.anchor_scouting_cache = {}
|
|
|
|
if spaceship.known_tiles and spaceship.known_bounds then
|
|
local aabb
|
|
for x = spaceship.known_bounds.left_top.x, spaceship.known_bounds.right_bottom.x do
|
|
for y = spaceship.known_bounds.left_top.y, spaceship.known_bounds.right_bottom.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
|
|
return playerdata.anchor_scouting_cache
|
|
end
|
|
|
|
--- While anchoring scouting we need to draw rectangles at the position the ship would land
|
|
--- if the player were to confirm the landing location
|
|
---@param player any
|
|
---@param spaceship Spaceship spaceship data
|
|
function Spaceship.anchor_scouting_tick(player, spaceship)
|
|
local anchor_scouting_cache = Spaceship.get_make_anchor_scouting_cache(player, spaceship)
|
|
if 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(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 = Spaceship.tick_interval_anchor + 1, -- tll must be 1 greater than the interval at which we draw to not flicker
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Starts anchor scouting at the location the spaceship is currently at
|
|
---@param spaceship Spaceship spaceship data
|
|
---@param player any
|
|
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
|
|
|
|
--- Stops any in progress anchor scouting for the given player
|
|
---@param player any
|
|
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
|
|
|
|
--- When an equipment grid is changed, we have to recalculate integrity costs
|
|
---@param event any
|
|
function Spaceship.on_equipment_grid_changed(event)
|
|
local player = game.players[event.player_index]
|
|
local spaceship = Spaceship.from_own_surface_index(player.surface.index)
|
|
if spaceship then
|
|
Spaceship.start_integrity_check(spaceship)
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_player_placed_equipment, Spaceship.on_equipment_grid_changed)
|
|
Event.addListener(defines.events.on_player_removed_equipment, Spaceship.on_equipment_grid_changed)
|
|
|
|
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)
|
|
if spaceship and not spaceship.is_cloning then
|
|
Spaceship.start_integrity_check(spaceship)
|
|
end
|
|
|
|
for _, name in pairs(Spaceship.names_engines) do
|
|
if entity.name == name then
|
|
if spaceship and spaceship.is_moving then
|
|
-- sets smoke
|
|
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
|
|
SpaceshipGUI.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
|
|
SpaceshipGUI.gui_open(game.players[event.player_index], spaceship)
|
|
end
|
|
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.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)
|
|
if not spaceship.is_cloning then
|
|
Spaceship.check_integrity_stress(spaceship)
|
|
Spaceship.start_integrity_check(spaceship)
|
|
end
|
|
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)
|
|
if not spaceship.is_cloning then
|
|
Spaceship.check_integrity_stress(spaceship)
|
|
Spaceship.start_integrity_check(spaceship)
|
|
end
|
|
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
|
|
-- this check is *not* appropriate if we can have multiple spaceships on a spaceship surface
|
|
-- when implementing multiple ships per spaceship surface, the way of handling not responding to events raised by cloning must be changed to work with that
|
|
if string.find(event.entity.surface.name, "spaceship-") then
|
|
local spaceship = Spaceship.from_own_surface_index(event.entity.surface.index)
|
|
if not spaceship.is_cloning then
|
|
spaceship.speed = spaceship.speed * 0.9
|
|
Spaceship.check_integrity_stress(spaceship)
|
|
Spaceship.start_integrity_check(spaceship)
|
|
end
|
|
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
|
|
|
|
if target_zone.type == "anomaly" and star_gravity_distance == 0 and planet_gravity_distance == 0 and distortion_distance > 0 then
|
|
return math.random(Zone.travel_cost_space_distortion - 1000) * 4 + 1000
|
|
-- actual distance calculation: return distortion_distance * Zone.travel_cost_space_distortion
|
|
else
|
|
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
|
|
end
|
|
|
|
--- Finds the engines for this spaceship
|
|
---@param spaceship Spaceship
|
|
---@param start_position {x=int, y=int}
|
|
function Spaceship.get_space_behind(spaceship, start_position)
|
|
-- spaceship must be on own surface
|
|
local space_behind = math.huge
|
|
if spaceship.known_tiles and spaceship.known_tiles[start_position.x] then
|
|
-- there's no way this is efficient but it needs to be profiled to see if this actually
|
|
-- meaningfully contributes to the UPS cost of ships (probably contributes more to integrity
|
|
-- checks than anything else)
|
|
for y = start_position.y, spaceship.known_bounds.right_bottom.y, 1 do
|
|
if spaceship.known_tiles[start_position.x][y]
|
|
and (spaceship.known_tiles[start_position.x][y] == Spaceship.tile_status.floor_console_connected
|
|
or spaceship.known_tiles[start_position.x][y] == Spaceship.tile_status.bulkhead_console_connected) then
|
|
space_behind = y - start_position.y
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return space_behind
|
|
end
|
|
|
|
---@param spaceship Spaceship the spaceship data
|
|
---@param engine Spaceship engine
|
|
function Spaceship.update_smoke(spaceship, engine)
|
|
if engine.entity.active then
|
|
if (not engine.smoke_trigger) and Spaceship.engines[engine.entity.name].smoke_trigger then
|
|
engine.smoke_trigger = engine.entity.surface.create_entity{
|
|
name = Spaceship.engines[engine.entity.name].smoke_trigger,
|
|
position = {x = engine.entity.position.x, y = engine.entity.bounding_box.right_bottom.y}
|
|
}
|
|
end
|
|
elseif engine.smoke_trigger and engine.smoke_trigger.valid then
|
|
engine.smoke_trigger.destroy()
|
|
engine.smoke_trigger = nil
|
|
end
|
|
end
|
|
|
|
function Spaceship.destroy_all_smoke_triggers(surface)
|
|
local smoke_triggers = surface.find_entities_filtered{
|
|
type = "smoke-with-trigger",
|
|
name = Spaceship.names_smoke_trigger
|
|
-- do not restrict area,
|
|
}
|
|
for _, smoke_trigger in pairs(smoke_triggers) do
|
|
smoke_trigger.destroy()
|
|
end
|
|
end
|
|
|
|
--- Finds the engines for this spaceship
|
|
---@param spaceship Spaceship the spaceship data
|
|
function Spaceship.find_own_surface_engines(spaceship)
|
|
spaceship.engines = nil
|
|
local surface = Spaceship.get_own_surface(spaceship)
|
|
if surface then
|
|
Spaceship.destroy_all_smoke_triggers(surface)
|
|
end
|
|
if surface and spaceship.known_tiles and spaceship.known_bounds then
|
|
spaceship.engines = {}
|
|
local engines = surface.find_entities_filtered{
|
|
name = Spaceship.names_engines,
|
|
area = spaceship.known_bounds
|
|
}
|
|
local y_engines = {} -- thrust harmonics
|
|
for _, entity in pairs(engines) do
|
|
local efficiency = Spaceship.engine_efficiency_blocked
|
|
local box = entity.bounding_box
|
|
local engine_y_behind = math.floor(box.right_bottom.y) + 1
|
|
local engine_x = math.floor((box.left_top.x + box.right_bottom.x)/2)
|
|
local space_behind
|
|
if entity.position.x % 1 < 0.25 or entity.position.x % 1 > 0.75 then
|
|
-- 2-wide trail
|
|
space_behind = math.min(
|
|
Spaceship.get_space_behind(spaceship, {x = engine_x - 1, y = engine_y_behind}),
|
|
Spaceship.get_space_behind(spaceship, {x = engine_x, y = engine_y_behind})
|
|
)
|
|
else
|
|
-- 1-wide trail
|
|
space_behind = Spaceship.get_space_behind(spaceship, {x = engine_x, y = engine_y_behind})
|
|
end
|
|
if space_behind < 0 then
|
|
efficiency = Spaceship.engine_efficiency_unblocked
|
|
else
|
|
efficiency = 1-(1-Spaceship.engine_efficiency_blocked) / (space_behind + Spaceship.engine_efficiency_unblocked_taper) * Spaceship.engine_efficiency_unblocked_taper
|
|
end
|
|
efficiency = efficiency - Spaceship.engine_efficiency_side
|
|
local engine = {entity = entity, efficiency = efficiency}
|
|
if not y_engines[engine_y_behind] then
|
|
y_engines[engine_y_behind] = {left = engine, right = engine}
|
|
else
|
|
if entity.position.x < y_engines[engine_y_behind].left.entity.position.x then
|
|
y_engines[engine_y_behind].left = engine
|
|
end
|
|
if entity.position.x > y_engines[engine_y_behind].right.entity.position.x then
|
|
y_engines[engine_y_behind].right = engine
|
|
end
|
|
end
|
|
table.insert(spaceship.engines, engine)
|
|
end
|
|
|
|
-- thrust harmonics
|
|
-- the left-most and right-most engines get a bonus
|
|
-- there is a 1% incentive to have engines on different Y values.
|
|
-- You waste integrity building this way, but it means more interesting designs are penalised less by the forced grid.
|
|
for y, left_right in pairs(y_engines) do
|
|
left_right.left.efficiency = left_right.left.efficiency + Spaceship.engine_efficiency_side
|
|
if left_right.left ~= left_right.right then
|
|
left_right.right.efficiency = left_right.right.efficiency + Spaceship.engine_efficiency_side
|
|
end
|
|
end
|
|
|
|
-- Show the result
|
|
for _, engine in pairs(spaceship.engines) do
|
|
engine.entity.surface.create_entity{
|
|
name = "flying-text",
|
|
position = util.vectors_add({x=0, y=1}, engine.entity.position),
|
|
text = math.ceil(engine.efficiency*100).."%"
|
|
}
|
|
Spaceship.update_smoke(spaceship, engine)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Activates the engines on this spaceship
|
|
---@param spaceship Spaceship the spaceship data
|
|
function Spaceship.activate_engines(spaceship)
|
|
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
|
|
Spaceship.update_smoke(spaceship, engine)
|
|
else
|
|
spaceship.engines = nil
|
|
Spaceship.activate_engines(spaceship)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Deactivates the engines on this spaceship
|
|
---@param spaceship Spaceship the spaceship data
|
|
function Spaceship.deactivate_engines(spaceship)
|
|
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.smoke_trigger and engine.smoke_trigger.valid then
|
|
engine.smoke_trigger.destroy()
|
|
engine.smoke_trigger = nil
|
|
end
|
|
if engine.entity and engine.entity.valid then
|
|
engine.entity.active = false
|
|
Spaceship.update_smoke(spaceship, engine)
|
|
else
|
|
spaceship.engines = nil
|
|
Spaceship.deactivate_engines(spaceship)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Spaceship.surface_tick(spaceship, time_passed)
|
|
-- 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 = SpaceshipObstacles.particle_speed_factor(spaceship.speed)
|
|
surface.wind_speed = 0.01 + 0.005 * speed_factor
|
|
|
|
-- 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 * time_passed * 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
|
|
|
|
-- obstacles
|
|
SpaceshipObstacles.tick_obstacles(spaceship, surface, time_passed)
|
|
end
|
|
|
|
--- Sets the lighting of a spaceship surface. This dictates both how bright the player's screen is and solar power
|
|
---@param spaceship Spaceship spaceship data
|
|
---@param surface LuaSurface the spaceship's surface
|
|
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.apply_engine_thust(spaceship, time_passed)
|
|
-- Apply engines to speed.
|
|
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
|
|
* (engine.energy / engine_proto.max_energy) / (Spaceship.minimum_mass + spaceship.integrity_stress)
|
|
* (Spaceship.speed_taper / (Spaceship.speed_taper + spaceship.speed))
|
|
* time_passed
|
|
end
|
|
end
|
|
end
|
|
else
|
|
spaceship.engines[_] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function Spaceship.apply_drag(spaceship, time_passed)
|
|
-- space_drag from imperfect vacuum
|
|
-- streamline 0 = 110.45
|
|
-- streamline 1 = 181.25
|
|
local drag = Spaceship.space_drag * (2 - (spaceship.streamline or 0)) * time_passed
|
|
spaceship.speed = spaceship.speed * (1 - drag) + Spaceship.minimum_impulse
|
|
|
|
-- brake
|
|
if spaceship.target_speed and spaceship.speed > spaceship.target_speed then
|
|
spaceship.speed = math.min(spaceship.speed, math.max(spaceship.target_speed + 1, spaceship.speed - 0.001 * time_passed))
|
|
spaceship.speed = math.min(spaceship.speed, math.max(spaceship.target_speed + 0.5, spaceship.speed - (spaceship.speed + 0.5 - spaceship.target_speed) * (1 - math.pow(0.999, time_passed))))
|
|
end
|
|
spaceship.max_speed = math.max(spaceship.speed, spaceship.max_speed or 0)
|
|
end
|
|
|
|
--- Move away from the current planet
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
function Spaceship.move_from_planet(spaceship, travel_speed)
|
|
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"}
|
|
end
|
|
|
|
--- Move away from the current star
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
function Spaceship.move_from_star(spaceship, travel_speed)
|
|
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"}
|
|
end
|
|
|
|
--- Move towards a particular position in intestellar space
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
---@param destination_stellar_position Position the target location
|
|
function Spaceship.move_towards_interstellar_position(spaceship, travel_speed, destination_stellar_position)
|
|
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
|
|
|
|
--- Move towards a particular position in intestellar space instantly
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
---@param destination_stellar_position Position the target location
|
|
function Spaceship.move_instant_interstellar_position(spaceship, travel_speed, destination_stellar_position)
|
|
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"}
|
|
end
|
|
|
|
--- Move towards the anomaly
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
---@param destination_space_distorion number the distortion of the target (does not have to be 1 / at the anomaly)
|
|
function Spaceship.move_to_anomaly(spaceship, travel_speed, destination_space_distorion)
|
|
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
|
|
|
|
--- Move away from the anomaly
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
function Spaceship.move_from_anomaly(spaceship, travel_speed)
|
|
spaceship.space_distortion = spaceship.space_distortion - travel_speed / Zone.travel_cost_space_distortion
|
|
spaceship.travel_message = {"space-exploration.spaceship-travel-message-spatial-distortions"}
|
|
end
|
|
|
|
--- Move the spaceship through space conventionally (i.e. no teleportation/spatial distortion)
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param travel_speed number speed at which to move
|
|
---@param target_zone any
|
|
---@param destination_stellar_position any
|
|
---@param destination_star_gravity_well any
|
|
---@param destination_planet_gravity_well any
|
|
function Spaceship.move_conventional(spaceship, travel_speed, target_zone, destination_stellar_position, destination_star_gravity_well, destination_planet_gravity_well)
|
|
local interstellar_distance = Util.vectors_delta_length(spaceship.stellar_position, destination_stellar_position)
|
|
if interstellar_distance == 0 then -- same system
|
|
if spaceship.type == "spaceship" then -- not needed on spaceship-lookahead
|
|
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)
|
|
end
|
|
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 -- leave the planet gravity well
|
|
Spaceship.move_from_planet(spaceship, travel_speed)
|
|
elseif spaceship.star_gravity_well > 0 then -- leave the star gravity well
|
|
Spaceship.move_from_star(spaceship, travel_speed)
|
|
else -- match interstellar position
|
|
Spaceship.move_towards_interstellar_position(spaceship, travel_speed, destination_stellar_position)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Moves the spaceship from its current position towards its destination
|
|
---@param spaceship Spaceship the spaceship
|
|
---@param time_passed number amount of time passed since the position of the spaceship was last updated
|
|
function Spaceship.move_to_destination(spaceship, time_passed)
|
|
if not spaceship.destination then return 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
|
|
|
|
--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
|
|
|
|
Spaceship.move_to_destination_basic(spaceship, target_zone, time_passed)
|
|
|
|
local ship_surface = Spaceship.get_own_surface(spaceship)
|
|
Spaceship.set_light(spaceship, ship_surface)
|
|
end
|
|
|
|
--- Moves a spaceship or spaceship
|
|
---@param spaceship Spaceship the spaceship or spaceship-lookahead
|
|
---@param target_zone Zone or Spaceship
|
|
---@param time_passed number amount of time passed since the position of the spaceship was last updated
|
|
function Spaceship.move_to_destination_basic(spaceship, target_zone, time_passed)
|
|
|
|
-- step towards destination
|
|
local travel_speed = spaceship.speed * Spaceship.travel_speed_multiplier * time_passed
|
|
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 == 1 then -- target is anomaly (or spaceship at anomaly)
|
|
if spaceship.planet_gravity_well > 0 then -- leave the planet gravity well
|
|
Spaceship.move_from_planet(spaceship, travel_speed)
|
|
elseif spaceship.star_gravity_well > 0 then -- leave the star gravity well
|
|
Spaceship.move_from_star(spaceship, travel_speed)
|
|
else -- move towards the anomaly
|
|
Spaceship.move_to_anomaly(spaceship, travel_speed, destination_space_distorion)
|
|
end
|
|
elseif spaceship.space_distortion == 1 then -- at the anomaly so the stellar position can be anywhere instantly
|
|
Spaceship.move_instant_interstellar_position(spaceship, travel_speed, destination_stellar_position)
|
|
elseif destination_space_distorion > 0 then -- target is spaceship on way to/from anomaly
|
|
local interstellar_distance = Util.vectors_delta_length(spaceship.stellar_position, destination_stellar_position)
|
|
if spaceship.planet_gravity_well > 0 then -- leave the planet gravity well
|
|
Spaceship.move_from_planet(spaceship, travel_speed)
|
|
elseif spaceship.star_gravity_well > 0 then -- leave the star gravity well
|
|
Spaceship.move_from_star(spaceship, travel_speed)
|
|
elseif interstellar_distance > 0 then -- match interstellar position
|
|
Spaceship.move_towards_interstellar_position(spaceship, travel_speed, destination_stellar_position)
|
|
else -- move towards the anomaly
|
|
Spaceship.move_to_anomaly(spaceship, travel_speed, destination_space_distorion)
|
|
end
|
|
elseif spaceship.space_distortion > 0 then -- leaving the anomaly
|
|
Spaceship.move_from_anomaly(spaceship, travel_speed)
|
|
else -- conventional travel to planet/star/spaceship
|
|
Spaceship.move_conventional(spaceship, travel_speed, target_zone, destination_stellar_position, destination_star_gravity_well, destination_planet_gravity_well)
|
|
end
|
|
|
|
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.is_on_own_surface(spaceship) or (spaceship.console and spaceship.console.valid) then
|
|
-- integrity check
|
|
if spaceship.is_doing_check then
|
|
if Spaceship.is_on_own_surface(spaceship) and spaceship.known_floor_tiles and not spaceship.is_doing_check_slowly 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 spaceship.console and spaceship.console.valid and (game.tick + spaceship.console.unit_number) % (Spaceship.integrity_pulse_interval) == 0 and spaceship.console.energy > 0 then
|
|
Spaceship.start_slow_integrity_check(spaceship)
|
|
end
|
|
|
|
if spaceship.console and spaceship.console.valid and (game.tick + spaceship.console.unit_number) % 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
|
|
if spaceship.target_speed_source ~= "manual-override" then
|
|
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
|
|
spaceship.target_speed_source = "circuit"
|
|
elseif signal_target_speed < 0 then
|
|
spaceship.stopped = true
|
|
spaceship.target_speed = nil
|
|
spaceship.target_speed_source = "circuit"
|
|
elseif not spaceship.stopped then
|
|
-- 0 means use set targets
|
|
local last_target_speed = spaceship.target_speed
|
|
local asteroid_density = spaceship.asteroid_density or SpaceshipObstacles.default_asteroid_density
|
|
if spaceship.future_asteroid_density and spaceship.future_asteroid_density > asteroid_density then
|
|
asteroid_density = spaceship.future_asteroid_density
|
|
end
|
|
if asteroid_density == SpaceshipObstacles.default_asteroid_density then
|
|
spaceship.target_speed = spaceship.target_speed_normal
|
|
spaceship.target_speed_source = "normal"
|
|
elseif asteroid_density == SpaceshipObstacles.asteroid_density_by_zone_type['asteroid-belt'] then
|
|
spaceship.target_speed = spaceship.target_speed_belt or spaceship.target_speed_normal
|
|
spaceship.target_speed_source = "asteroid-belt"
|
|
elseif asteroid_density == SpaceshipObstacles.asteroid_density_by_zone_type['asteroid-field'] then
|
|
spaceship.target_speed = spaceship.target_speed_field or spaceship.target_speed_belt or spaceship.target_speed_normal
|
|
spaceship.target_speed_source = "asteroid-field"
|
|
end
|
|
-- target speed was set to nil which means the fields are empty and target speed should be unlimited
|
|
if not spaceship.target_speed then
|
|
if last_target_speed and spaceship.is_moving then Spaceship.activate_engines(spaceship) end -- if spaceship was being speed throttled then need to fire all engines now that it's unlimited (but only once)
|
|
spaceship.target_speed_source = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
-- 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 and not spaceship.entities_to_restore 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_launching_automatically = true
|
|
spaceship.is_landing = false
|
|
Spaceship.start_integrity_check(spaceship)
|
|
end
|
|
end
|
|
|
|
-- land
|
|
if Spaceship.is_on_own_surface(spaceship)
|
|
and not spaceship.entities_to_restore
|
|
and spaceship.destination
|
|
and spaceship.destination.type == "zone"
|
|
and Spaceship.is_near_destination(spaceship) then
|
|
SpaceshipClamp.attempt_anchor_spaceship(spaceship, red, green)
|
|
end
|
|
|
|
end
|
|
|
|
-- delayed launch (either until the number of waiting chunk requests is finished or a maximum delay is reached)
|
|
if spaceship.awaiting_requests and (spaceship.requests_made <= 0 or (game.tick - spaceship.await_start_tick) >= Spaceship.tick_max_await) then
|
|
local params = spaceship.clone_params
|
|
SpaceshipClone.clone(spaceship, params.clone_from, params.clone_to, params.clone_delta)
|
|
spaceship.awaiting_requests = false
|
|
spaceship.clone_params = nil
|
|
end
|
|
|
|
-- don't upkeep the ship as if it is in-transit until it has actually cloned to the own_surface_index surface
|
|
-- maybe use Spaceship.is_on_own_surface(spaceship) instead
|
|
if spaceship.own_surface_index and not spaceship.awaiting_requests then
|
|
-- space upkeep
|
|
if game.tick % Spaceship.tick_interval_move == 0 then
|
|
Spaceship.surface_tick(spaceship, Spaceship.tick_interval_move)
|
|
end
|
|
|
|
if spaceship.target_speed and spaceship.is_moving == false and spaceship.stopped then
|
|
spaceship.stopped = false
|
|
end
|
|
|
|
-- this has to be done every tick for seamless movement
|
|
local surface = Spaceship.get_own_surface(spaceship)
|
|
SpaceshipObstacles.tick_entity_obstacles(spaceship, surface)
|
|
|
|
-- 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
|
|
engine.entity.active = false
|
|
Spaceship.update_smoke(spaceship, engine)
|
|
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
|
|
Spaceship.update_smoke(spaceship, engine)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if game.tick % Spaceship.tick_interval_move == 0 then
|
|
Spaceship.apply_engine_thust(spaceship, Spaceship.tick_interval_move)
|
|
Spaceship.apply_drag(spaceship, Spaceship.tick_interval_move)
|
|
Spaceship.move_to_destination(spaceship, Spaceship.tick_interval_move)
|
|
end
|
|
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 > 0 then
|
|
local drag = Spaceship.space_drag * (2 - (spaceship.streamline or 0))
|
|
spaceship.speed = math.max(0, spaceship.speed * (1 - Spaceship.space_drag) - (spaceship.stopped and 0.1 or 0.02))
|
|
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
|
|
-- spawn the console output
|
|
local console = spaceship.console
|
|
local output_position = util.vectors_add(console.position, Spaceship.console_output_offset)
|
|
local output = util.find_entity_or_revive_ghost(console.surface, Spaceship.name_spaceship_console_output, output_position)
|
|
if not output then
|
|
output = console.surface.create_entity{
|
|
name = Spaceship.name_spaceship_console_output,
|
|
position = util.vectors_add(console.position, Spaceship.console_output_offset),
|
|
force = console.force
|
|
}
|
|
end
|
|
spaceship.console_output = output
|
|
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.is_on_own_surface(spaceship) 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
|
|
|
|
-- Asteroid Density
|
|
if spaceship.asteroid_density then
|
|
ctrl.set_signal(slot, {signal={type = "virtual", name = "signal-D"}, count=spaceship.asteroid_density * 100})
|
|
else
|
|
ctrl.set_signal(slot, {signal={type = "virtual", name = "signal-D"}, count=SpaceshipObstacles.default_asteroid_density * 100})
|
|
end
|
|
slot = slot + 1
|
|
|
|
-- Anchor (only change if the ship is not in the middle of launching)
|
|
if not spaceship.awaiting_requests then
|
|
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
|
|
end
|
|
|
|
--- Updates all spaceships, potentially launching/landing them or spawning/destroying/changing speed of obstacles
|
|
--- or updating their guis or updating anchor scoutin
|
|
---@param event any
|
|
function Spaceship.on_tick(event)
|
|
-- update asteroid density
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
if spaceship.console and spaceship.console.valid and Spaceship.is_on_own_surface(spaceship) then
|
|
if (game.tick + spaceship.console.unit_number) % Spaceship.tick_interval_density == 0 then
|
|
spaceship.asteroid_density = 0
|
|
if spaceship.speed > 5 then
|
|
local target_zone = Spaceship.get_destination_zone(spaceship)
|
|
if target_zone then
|
|
local spaceship_lookahead = {
|
|
type = "spaceship-lookahead",
|
|
speed = spaceship.speed,
|
|
stellar_position = spaceship.stellar_position,
|
|
space_distortion = spaceship.space_distortion,
|
|
star_gravity_well = spaceship.star_gravity_well,
|
|
planet_gravity_well = spaceship.planet_gravity_well,
|
|
}
|
|
Spaceship.move_to_destination_basic(spaceship_lookahead, target_zone, 5 * 60) -- 5 seconds
|
|
spaceship.future_asteroid_density = SpaceshipObstacles.get_asteroid_density(spaceship_lookahead)
|
|
end
|
|
end
|
|
spaceship.asteroid_density = SpaceshipObstacles.get_asteroid_density(spaceship)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- update spaceships
|
|
if global.spaceships then
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
Spaceship.spaceship_tick(spaceship)
|
|
end
|
|
end
|
|
|
|
-- update guis
|
|
if game.tick % Spaceship.tick_interval_gui == 0 then
|
|
for _, player in pairs(game.connected_players) do
|
|
SpaceshipGUI.gui_update(player)
|
|
end
|
|
end
|
|
|
|
-- update obstacles
|
|
SpaceshipObstacles.tick_projectile_speeds()
|
|
|
|
-- update anchoring
|
|
if game.tick % Spaceship.tick_interval_anchor == 0 then
|
|
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
|
|
end
|
|
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)
|
|
|
|
spaceship.integrity_stress_structure = 0
|
|
spaceship.integrity_stress_container = 0
|
|
|
|
-- use all tiles for the cost even if they are not connected
|
|
-- get walls for an integrity discount
|
|
local surface = Spaceship.get_current_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, area = area}
|
|
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
|
|
|
|
-- Find weakpoints in the design
|
|
-- If very narrow sections are used in the middle those are not strong,
|
|
-- so we need to increase the cost not decrease it if it gets too narrow.
|
|
-- Scan from front and back so decorative pointy bits are not a problem
|
|
-- don't increase width in jump, follow the edge more loosely.
|
|
-- phantom tiles start at the 50% reduced with mark.
|
|
-- They should not have full effect immediatly.
|
|
local widths = {}
|
|
for x, x_tiles in pairs(spaceship.known_tiles) do
|
|
for y, state in pairs(x_tiles) do
|
|
if state == Spaceship.tile_status.floor_console_connected or state == Spaceship.tile_status.bulkhead_console_connected then
|
|
widths[y] = (widths[y] or 0) + 1
|
|
end
|
|
end
|
|
end
|
|
local front_y = spaceship.known_bounds.left_top.y
|
|
local back_y = spaceship.known_bounds.right_bottom.y
|
|
|
|
local front_max_width = 0
|
|
local back_max_width = 0
|
|
|
|
local phantom_tiles = 0
|
|
|
|
local widths_total = 0
|
|
for _, width in pairs(widths) do
|
|
widths_total = widths_total + width
|
|
end
|
|
local width_average = widths_total / table_size(widths)
|
|
|
|
while front_y <= back_y do
|
|
if front_max_width <= back_max_width then
|
|
if widths[front_y] then
|
|
local width = widths[front_y]
|
|
if front_max_width < width then
|
|
front_max_width = math.min(width, front_max_width + 2)
|
|
end
|
|
if width < front_max_width / 2 then
|
|
-- tiles in more extreme hollows count more
|
|
local hollow = (front_max_width / 2 - width)
|
|
phantom_tiles = phantom_tiles + hollow * hollow / (front_max_width / 2)
|
|
end
|
|
end
|
|
front_y = front_y + 1
|
|
else
|
|
if widths[back_y] then
|
|
local width = widths[back_y]
|
|
if back_max_width < width then
|
|
back_max_width = math.min(width, back_max_width + 2)
|
|
end
|
|
if width < back_max_width / 2 then
|
|
local hollow = (back_max_width / 2 - width)
|
|
phantom_tiles = phantom_tiles + hollow * hollow / (front_max_width / 2)
|
|
end
|
|
end
|
|
back_y = back_y - 1
|
|
end
|
|
end
|
|
if phantom_tiles > 0 then
|
|
Log.trace("phantom_tiles " .. phantom_tiles)
|
|
end
|
|
|
|
spaceship.container_slot_count = 0
|
|
|
|
local containers = surface.find_entities_filtered{ type = {"container", "logistic-container", "car", "spider-vehicle", "locomotive", "cargo-wagon"}, 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
|
|
local container_size = container.prototype.get_inventory_size(defines.inventory.car_trunk) or (container.prototype.get_inventory_size(defines.inventory.chest) or 0)
|
|
if container.type == "car" or container.type == "spider-vehicle" then
|
|
spaceship.container_slot_count = spaceship.container_slot_count + container_size
|
|
elseif container.type == "locomotive" or container.type == "cargo-wagon" then
|
|
spaceship.container_slot_count = spaceship.container_slot_count + container_size / 10
|
|
else
|
|
spaceship.container_slot_count = spaceship.container_slot_count + container_size
|
|
end
|
|
if container.type == "car" or container.type == "spider-vehicle" or container.type == "locomotive" or container.type == "cargo-wagon" then
|
|
if container.grid then
|
|
local grid_usage = 0
|
|
for _, equipment in pairs(container.grid.equipment) do
|
|
if equipment and equipment.shape and equipment.shape.width and equipment.shape.height then
|
|
grid_usage = grid_usage + equipment.shape.width * equipment.shape.height
|
|
end
|
|
end
|
|
spaceship.integrity_stress_structure = spaceship.integrity_stress_structure + grid_usage
|
|
spaceship.integrity_stress_container = spaceship.integrity_stress_container + grid_usage
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
spaceship.container_fluid_capacity = 0
|
|
|
|
local containers = surface.find_entities_filtered{type = {"storage-tank", "fluid-wagon"}, area = area}
|
|
for _, container in pairs(containers) do
|
|
if container.name ~= BigTurbine.name_big_turbine_tank and container.name ~= CondenserTurbine.name_condenser_turbine_tank and (not string.starts(container.name, "se-space-pipe")) then
|
|
local mult = 2
|
|
if container.type == "fluid-wagon" then
|
|
mult = 0.1
|
|
else
|
|
if string.find(container.name, "booster", 1, true) then mult = 0.5 end
|
|
if container.name == "storage-tank" then mult = 1 end
|
|
end
|
|
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
|
|
if container.fluidbox[1] and container.fluidbox[1].name == "steam" then
|
|
spaceship.container_fluid_capacity = spaceship.container_fluid_capacity + 2 * mult * container.fluidbox.get_capacity(i)
|
|
else
|
|
spaceship.container_fluid_capacity = spaceship.container_fluid_capacity + mult * container.fluidbox.get_capacity(i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
spaceship.speed_multiplier = 1
|
|
|
|
-- name-based entity modifiers
|
|
local names = {}
|
|
local name_effects = {}
|
|
for _, ia_name in pairs(Spaceship.integrity_affecting_names) do
|
|
if ia_name.mod == nil or game.active_mods[ia_name.mod] then
|
|
table.insert(names, ia_name.name)
|
|
name_effects[ia_name.name] = ia_name
|
|
end
|
|
end
|
|
local entities = surface.find_entities_filtered{name = names, area = area}
|
|
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
|
|
local name_effect_set = name_effects[entity.name]
|
|
if name_effect_set.integrity_stress_container then
|
|
spaceship.integrity_stress_container = spaceship.integrity_stress_container + name_effect_set.integrity_stress_container
|
|
end
|
|
if name_effect_set.integrity_stress_structure then
|
|
spaceship.integrity_stress_structure = spaceship.integrity_stress_structure + name_effect_set.integrity_stress_structure
|
|
end
|
|
if name_effect_set.max_speed_multiplier then
|
|
spaceship.speed_multiplier = math.min(spaceship.speed_multiplier, name_effect_set.max_speed_multiplier)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- type-based entity modifiers
|
|
local types = {}
|
|
local type_effects = {}
|
|
for _, ia_type in pairs(Spaceship.integrity_affecting_types) do
|
|
if ia_type.mod == nil or game.active_mods[ia_type.mod] then
|
|
table.insert(types, ia_type.type)
|
|
type_effects[ia_type.type] = ia_type
|
|
end
|
|
end
|
|
local entities = surface.find_entities_filtered{type = types, area = area}
|
|
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
|
|
local type_effect_set = type_effects[entity.type]
|
|
if type_effect_set.integrity_stress_container then
|
|
spaceship.integrity_stress_container = spaceship.integrity_stress_container + type_effect_set.integrity_stress_container
|
|
end
|
|
if type_effect_set.integrity_stress_structure then
|
|
spaceship.integrity_stress_structure = spaceship.integrity_stress_structure + type_effect_set.integrity_stress_structure
|
|
end
|
|
if type_effect_set.max_speed_multiplier then
|
|
spaceship.speed_multiplier = math.min(spaceship.speed_multiplier, type_effect_set.max_speed_multiplier)
|
|
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
|
|
--Log.trace((spaceship.count_empty_tiles or 0) .." / "..(spaceship.tile_count - spaceship.wall_count))
|
|
local empty_tiles = spaceship.count_empty_tiles or 0
|
|
spaceship.integrity_stress_structure =
|
|
spaceship.integrity_stress_structure
|
|
+ spaceship.tile_count
|
|
+ phantom_tiles * 0.25
|
|
- spaceship.wall_count * 0.75
|
|
|
|
spaceship.integrity_stress_container =
|
|
spaceship.integrity_stress_container
|
|
+ spaceship.container_slot_count/2
|
|
+ spaceship.container_fluid_capacity / 2000
|
|
|
|
-- if the ship is very long and thin start taking integrity penalties.
|
|
local length = spaceship.known_bounds.right_bottom.y - spaceship.known_bounds.left_top.y
|
|
-- over 4:1 length gets a penalty of 5% per additional length
|
|
local integrity_multiplier = math.max(1, 1 + 0.05 * (length / width_average - 4))
|
|
spaceship.integrity_stress_structure = spaceship.integrity_stress_structure * integrity_multiplier
|
|
spaceship.integrity_stress_container = spaceship.integrity_stress_container
|
|
|
|
-- corridor allowance
|
|
spaceship.integrity_stress_structure_max = spaceship.integrity_stress_structure
|
|
spaceship.integrity_stress_structure = spaceship.integrity_stress_structure_max
|
|
-- this encourages keeping 10% of the tital size empty.
|
|
- math.min(spaceship.tile_count * 0.1, empty_tiles)
|
|
-- this encourages keeping 20% of the internal space empty.
|
|
-- math.min((spaceship.tile_count - spaceship.wall_count) * 0.2, empty_tiles)
|
|
-- this has the first empty tile discounted by 1, the last by 0, if 20% of the ship is empty they are discouted by 80%
|
|
-- empty_tiles * math.max(0, 1 - empty_tiles / (spaceship.tile_count - spaceship.wall_count))
|
|
|
|
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.is_on_own_surface(spaceship) 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_slow_integrity_check(spaceship, alpha)
|
|
spaceship.is_doing_check_slowly = true
|
|
Spaceship.start_integrity_check(spaceship, alpha)
|
|
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.is_doing_check_slowly = nil
|
|
spaceship.check_stage = nil
|
|
spaceship.pending_tiles = nil
|
|
spaceship.streamline = nil
|
|
if spaceship.integrity_valid and spaceship.check_tiles then
|
|
-- success
|
|
spaceship.count_empty_tiles = spaceship.check_count_empty_tiles
|
|
spaceship.check_count_empty_tiles = nil
|
|
|
|
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.5)))
|
|
local streamline_right = math.min(1, 2 * (table_size(front_tiles_by_y_right)-1) / (math.max(0.5, (width-1) / 2.5)))
|
|
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.is_on_own_surface(spaceship) 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
|
|
-- check if the player is around
|
|
local player_is_here = not SpaceshipObstacles.surface_has_no_players(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_count_empty_tiles = 0
|
|
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
|
|
local position_table = {}
|
|
for x, x_tiles in pairs(spaceship.pending_tiles) do
|
|
for y, yes in pairs(x_tiles) do
|
|
local value = spaceship.check_tiles[x][y]
|
|
if value == Spaceship.tile_status.floor or value == 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})
|
|
position_table.x = cx + 0.5
|
|
position_table.y = cy + 0.5
|
|
local wall_count = surface.count_entities_filtered{
|
|
position = position_table,
|
|
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
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 0, g = 0, b = 1, a = alpha}, 5)
|
|
end
|
|
else
|
|
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.floor
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 0, g = 1, b = 0, a = alpha}, 5)
|
|
end
|
|
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
|
|
position_table.x = cx + 0.5
|
|
position_table.y = cy + 0.5
|
|
local clamps = surface.count_entities_filtered{
|
|
position = position_table,
|
|
name = SpaceshipClamp.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
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 0, b = 1, a = alpha}, 5)
|
|
end
|
|
else
|
|
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.bulkhead_exterior
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 0, b = 0, a = alpha}, 5)
|
|
end
|
|
end
|
|
else
|
|
spaceship.check_tiles[cx][cy] = Spaceship.tile_status.exterior
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 0, b = 1, a = alpha}, 5)
|
|
end
|
|
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})
|
|
if player_is_here then
|
|
Spaceship.flash_tile(surface, tile.position, {r = 1, g = 0, b = 0, a = alpha}, 120)
|
|
end
|
|
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
|
|
local value = spaceship.check_tiles[x][y]
|
|
if value == Spaceship.tile_status.exterior
|
|
or value == Spaceship.tile_status.bulkhead_exterior
|
|
or value == 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
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 1, g = 1, b = 0, a = alpha}, 30)
|
|
end
|
|
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
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {x, y}, {r = 0, g = 0, b = 1, a = alpha}, 40)
|
|
end
|
|
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
|
|
if player_is_here then
|
|
Spaceship.flash_tile(surface, {x, y}, {r = 1, g = 0, b = 0, a = alpha}, 120)
|
|
end
|
|
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
|
|
local value = spaceship.check_tiles[x][y]
|
|
if value == Spaceship.tile_status.floor_console_connected then
|
|
local blockers = surface.count_entities_filtered{
|
|
position = {x + 0.5, y + 0.5},
|
|
collision_mask = {"object-layer"}
|
|
}
|
|
if blockers == 0 then -- this tile is clear, add to corridor allowance.
|
|
spaceship.check_count_empty_tiles = (spaceship.check_count_empty_tiles or 0) + 1
|
|
end
|
|
end
|
|
if value == Spaceship.tile_status.floor_console_connected
|
|
or value == 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
|
|
if player_is_here and not spaceship.is_doing_check_slowly then
|
|
Spaceship.flash_tile(surface, {cx, cy}, {r = 0, g = 1, b = 1, a = alpha}, 5)
|
|
end
|
|
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"}
|
|
if player_is_here then
|
|
Spaceship.flash_tile(surface, {x, y}, {r = 1, g = 0, b = 0, a = alpha}, 120)
|
|
end
|
|
-- detatch
|
|
if Spaceship.is_on_own_surface(spaceship) 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 Spaceship.is_floor(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_compute_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
|
|
|