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.
2022 lines
78 KiB
2022 lines
78 KiB
local Zone = {}
|
|
|
|
-- constants
|
|
Zone.solar_multiplier = 1
|
|
|
|
Zone.discovery_scan_radius = 32
|
|
Zone.clear_enemies_radius = 512
|
|
|
|
Zone.travel_cost_interstellar = 400 -- stellar position distance, roughly 50 distance between stars, can be up to 300 apart
|
|
Zone.travel_cost_star_gravity = 500 -- roughly 10-20 base for a star
|
|
Zone.travel_cost_planet_gravity = 100 -- roughly 10-20 base for a planet
|
|
Zone.travel_cost_space_distortion = Zone.travel_cost_interstellar * 25 -- based on 0-1 range
|
|
|
|
Zone.name_tech_discover_random = mod_prefix.."zone-discovery-random"
|
|
Zone.name_tech_discover_targeted = mod_prefix.."zone-discovery-targeted"
|
|
Zone.name_tech_discover_deep = mod_prefix.."zone-discovery-deep"
|
|
|
|
-- based on alien biomes, unsure as to how ti make this more dynamic
|
|
-- biome name = {tilenames}
|
|
Zone.biome_tiles = {
|
|
["out-of-map"] = {"out-of-map"}, -- always allowed
|
|
["water"] = {"water", "deepwater", "water-green", "deepwater-green", "water-shallow", "water-mud"},
|
|
|
|
["dirt-purple"] = {"mineral-purple-dirt-1", "mineral-purple-dirt-2", "mineral-purple-dirt-3", "mineral-purple-dirt-4", "mineral-purple-dirt-5", "mineral-purple-dirt-6"},
|
|
["dirt-violet"] = {"mineral-violet-dirt-1", "mineral-violet-dirt-2", "mineral-violet-dirt-3", "mineral-violet-dirt-4", "mineral-violet-dirt-5", "mineral-violet-dirt-6"},
|
|
["dirt-red"] = {"mineral-red-dirt-1", "mineral-red-dirt-2", "mineral-red-dirt-3", "mineral-red-dirt-4", "mineral-red-dirt-5", "mineral-red-dirt-6"},
|
|
["dirt-brown"] = {"mineral-brown-dirt-1", "mineral-brown-dirt-2", "mineral-brown-dirt-3", "mineral-brown-dirt-4", "mineral-brown-dirt-5", "mineral-brown-dirt-6"},
|
|
["dirt-tan"] = {"mineral-tan-dirt-1", "mineral-tan-dirt-2", "mineral-tan-dirt-3", "mineral-tan-dirt-4", "mineral-tan-dirt-5", "mineral-tan-dirt-6"},
|
|
["dirt-aubergine"] = {"mineral-aubergine-dirt-1", "mineral-aubergine-dirt-2", "mineral-aubergine-dirt-3", "mineral-aubergine-dirt-4", "mineral-aubergine-dirt-5", "mineral-aubergine-dirt-6"},
|
|
["dirt-dustyrose"] = {"mineral-dustyrose-dirt-1", "mineral-dustyrose-dirt-2", "mineral-dustyrose-dirt-3", "mineral-dustyrose-dirt-4", "mineral-dustyrose-dirt-5", "mineral-dustyrose-dirt-6"},
|
|
["dirt-beige"] = {"mineral-beige-dirt-1", "mineral-beige-dirt-2", "mineral-beige-dirt-3", "mineral-beige-dirt-4", "mineral-beige-dirt-5", "mineral-beige-dirt-6"},
|
|
["dirt-cream"] = {"mineral-cream-dirt-1", "mineral-cream-dirt-2", "mineral-cream-dirt-3", "mineral-cream-dirt-4", "mineral-cream-dirt-5", "mineral-cream-dirt-6"},
|
|
["dirt-black"] = {"mineral-black-dirt-1", "mineral-black-dirt-2", "mineral-black-dirt-3", "mineral-black-dirt-4", "mineral-black-dirt-5", "mineral-black-dirt-6"},
|
|
["dirt-grey"] = {"mineral-grey-dirt-1", "mineral-grey-dirt-2", "mineral-grey-dirt-3", "mineral-grey-dirt-4", "mineral-grey-dirt-5", "mineral-grey-dirt-6"},
|
|
["dirt-white"] = {"mineral-white-dirt-1", "mineral-white-dirt-2", "mineral-white-dirt-3", "mineral-white-dirt-4", "mineral-white-dirt-5", "mineral-white-dirt-6"},
|
|
|
|
["sand-purple"] = {"mineral-purple-sand-1", "mineral-purple-sand-2", "mineral-purple-sand-3"},
|
|
["sand-violet"] = {"mineral-violet-sand-1", "mineral-violet-sand-2", "mineral-violet-sand-3"},
|
|
["sand-red"] = {"mineral-red-sand-1", "mineral-red-sand-2", "mineral-red-sand-3"},
|
|
["sand-brown"] = {"mineral-brown-sand-1", "mineral-brown-sand-2", "mineral-brown-sand-3"},
|
|
["sand-tan"] = {"mineral-tan-sand-1", "mineral-tan-sand-2", "mineral-tan-sand-3"},
|
|
["sand-aubergine"] = {"mineral-aubergine-sand-1", "mineral-aubergine-sand-2", "mineral-aubergine-sand-3"},
|
|
["sand-dustyrose"] = {"mineral-dustyrose-sand-1", "mineral-dustyrose-sand-2", "mineral-dustyrose-sand-3"},
|
|
["sand-beige"] = {"mineral-beige-sand-1", "mineral-beige-sand-2", "mineral-beige-sand-3"},
|
|
["sand-cream"] = {"mineral-cream-sand-1", "mineral-cream-sand-2", "mineral-cream-sand-3"},
|
|
["sand-black"] = {"mineral-black-sand-1", "mineral-black-sand-2", "mineral-black-sand-3"},
|
|
["sand-grey"] = {"mineral-grey-sand-1", "mineral-grey-sand-2", "mineral-grey-sand-3"},
|
|
["sand-white"] = {"mineral-white-sand-1", "mineral-white-sand-2", "mineral-white-sand-3"},
|
|
|
|
["vegetation-green"] = {"vegetation-green-grass-1", "vegetation-green-grass-2", "vegetation-green-grass-3", "vegetation-green-grass-4"},
|
|
["vegetation-olive"] = {"vegetation-olive-grass-1", "vegetation-olive-grass-2"},
|
|
["vegetation-yellow"] = {"vegetation-yellow-grass-1", "vegetation-yellow-grass-2"},
|
|
["vegetation-orange"] = {"vegetation-orange-grass-1", "vegetation-orange-grass-2"},
|
|
["vegetation-red"] = {"vegetation-red-grass-1", "vegetation-red-grass-2"},
|
|
["vegetation-violet"] = {"vegetation-violet-grass-1", "vegetation-violet-grass-2"},
|
|
["vegetation-purple"] = {"vegetation-purple-grass-1", "vegetation-purple-grass-2"},
|
|
["vegetation-mauve"] = {"vegetation-mauve-grass-1", "vegetation-mauve-grass-2"},
|
|
["vegetation-blue"] = {"vegetation-blue-grass-1", "vegetation-blue-grass-2"},
|
|
["vegetation-turquoise"] = {"vegetation-turquoise-grass-1", "vegetation-turquoise-grass-2"},
|
|
|
|
["volcanic-orange"] = {"volcanic-orange-heat-1", "volcanic-orange-heat-2", "volcanic-orange-heat-3", "volcanic-orange-heat-4"},
|
|
["volcanic-green"] = {"volcanic-green-heat-1", "volcanic-green-heat-2", "volcanic-green-heat-3", "volcanic-green-heat-4"},
|
|
["volcanic-blue"] = {"volcanic-blue-heat-1", "volcanic-blue-heat-2", "volcanic-blue-heat-3", "volcanic-blue-heat-4"},
|
|
["volcanic-purple"] = {"volcanic-purple-heat-1", "volcanic-purple-heat-2", "volcanic-purple-heat-3", "volcanic-purple-heat-4"},
|
|
|
|
["frozen-snow"] = {"frozen-snow-0", "frozen-snow-1", "frozen-snow-2", "frozen-snow-3", "frozen-snow-4"},
|
|
["frozen-ice"] = {"frozen-snow-5", "frozen-snow-6", "frozen-snow-7", "frozen-snow-8", "frozen-snow-9"},
|
|
}
|
|
Zone.biome_collections = {
|
|
["all-sand"] = {"sand-purple", "sand-violet", "sand-red", "sand-brown", "sand-tan", "sand-aubergine", "sand-dustyrose", "sand-beige", "sand-cream", "sand-black", "sand-grey", "sand-white"},
|
|
["all-dirt"] = {"dirt-purple", "dirt-violet", "dirt-red", "dirt-brown", "dirt-tan", "dirt-aubergine", "dirt-dustyrose", "dirt-beige", "dirt-cream", "dirt-black", "dirt-grey", "dirt-white"},
|
|
["all-vegetation"] = {"vegetation-green", "vegetation-olive", "vegetation-yellow", "vegetation-orange", "vegetation-red",
|
|
"vegetation-violet", "vegetation-purple", "vegetation-mauve", "vegetation-blue", "vegetation-turquoise" },
|
|
["all-volcanic"] = {"volcanic-orange", "volcanic-green", "volcanic-blue", "volcanic-purple"},
|
|
["all-frozen"] = {"frozen-snow", "frozen-ice"},
|
|
}
|
|
Zone.signal_to_zone_type = {
|
|
[mod_prefix.."planet"] = "planet",
|
|
[mod_prefix.."moon"] = "moon",
|
|
[mod_prefix.."planet-orbit"] = "orbit",
|
|
[mod_prefix.."moon-orbit"] = "orbit",
|
|
[mod_prefix.."star"] = "orbit",
|
|
[mod_prefix.."asteroid-belt"] = "asteroid-belt",
|
|
[mod_prefix.."asteroid-field"] = "asteroid-field",
|
|
[mod_prefix.."anomaly"] = "anomaly",
|
|
}
|
|
Zone.controls_without_frequency_multiplier = {
|
|
"trees",
|
|
"enemy-base"
|
|
}
|
|
-- NOTE: cliff and base terrain sliders have special settings,
|
|
|
|
--[[ eg:
|
|
biome_replacements = {
|
|
{replace={"all-dirt", "all-sand", "all-volcanic"}, with="sand-red"},
|
|
{replace={"all-vegetation", "all-frozen"}, with="vegetation-red"}
|
|
} ]]--
|
|
|
|
function Zone.get_default()
|
|
return Zone.from_name("Nauvis")
|
|
end
|
|
|
|
function Zone.get_force_home_zone(force_name)
|
|
if global.forces[force_name] and global.forces[force_name].homeworld_index then
|
|
return Zone.from_zone_index(global.forces[force_name].homeworld_index)
|
|
end
|
|
end
|
|
|
|
function Zone.type_title(zone)
|
|
if zone.type == "planet" then
|
|
return "Planet"
|
|
elseif zone.type == "moon" then
|
|
return "Moon"
|
|
elseif zone.type == "star" then
|
|
return "Star"
|
|
elseif zone.type == "asteroid-field" then
|
|
return "Asteroid Field"
|
|
elseif zone.type == "asteroid-belt" then
|
|
return "Asteroid Belt"
|
|
elseif zone.type == "anomaly" then
|
|
return "Anomaly"
|
|
elseif zone.type == "spaceship" then
|
|
return "Spaceship"
|
|
elseif zone.type == "orbit" then
|
|
return (Zone.type_title(zone.parent) .. " Orbit")
|
|
end
|
|
end
|
|
|
|
function Zone.get_signal_name(zone)
|
|
-- used for rich text
|
|
if zone.type == "orbit" and zone.parent.type == "star" then
|
|
return mod_prefix.."star"
|
|
elseif zone.type == "orbit" and zone.parent.type == "planet" then
|
|
return mod_prefix.."planet-orbit"
|
|
elseif zone.type == "orbit" and zone.parent.type == "moon" then
|
|
return mod_prefix.."moon-orbit"
|
|
else
|
|
return mod_prefix..zone.type
|
|
end
|
|
end
|
|
|
|
function Zone.get_icon(zone)
|
|
-- used for rich text
|
|
return "virtual-signal/" .. Zone.get_signal_name(zone)
|
|
end
|
|
|
|
function Zone.is_solid(zone)
|
|
return zone.type == "planet" or zone.type == "moon"
|
|
end
|
|
|
|
function Zone.is_space(zone)
|
|
return not Zone.is_solid(zone)
|
|
end
|
|
|
|
function Zone.from_zone_index(zone_index)
|
|
return global.zone_index[zone_index]
|
|
end
|
|
|
|
function Zone.from_name(name)
|
|
return global.zones_by_name[name]
|
|
end
|
|
|
|
function Zone.from_surface_index(surface_index)
|
|
return global.zones_by_surface[surface_index]
|
|
end
|
|
|
|
function Zone.from_surface(surface)
|
|
local from_index = Zone.from_surface_index(surface.index)
|
|
if from_index then return from_index end
|
|
-- maybe a spaceship
|
|
return Spaceship.from_own_surface_index(surface.index)
|
|
end
|
|
|
|
function Zone.get_stellar_position(zone)
|
|
if not zone then return nil end
|
|
if zone.type == "anomaly" then return {x = 0, y = 0} end
|
|
-- everything else should have a stellar position
|
|
return zone.stellar_position or Zone.get_stellar_position(zone.parent)
|
|
end
|
|
|
|
function Zone.get_star_gravity_well(zone)
|
|
if zone.type == "orbit" then
|
|
return Zone.get_star_gravity_well(zone.parent)
|
|
end
|
|
return zone.star_gravity_well or 0
|
|
end
|
|
|
|
function Zone.get_planet_gravity_well(zone)
|
|
if zone.type == "orbit" then
|
|
if zone.parent.type == "star" then
|
|
return 0
|
|
elseif zone.parent.type == "planet" then
|
|
return Zone.get_planet_gravity_well(zone.parent) - 1
|
|
else
|
|
return Zone.get_planet_gravity_well(zone.parent) - 0.5
|
|
end
|
|
end
|
|
return zone.planet_gravity_well or 0
|
|
end
|
|
|
|
function Zone.get_space_distortion(zone) -- anomaly
|
|
if zone.space_distortion then
|
|
return zone.space_distortion
|
|
end
|
|
return zone.type == "anomaly" and 1 or 0
|
|
end
|
|
|
|
function Zone.find_parent_star(zone)
|
|
if zone.type == "star" then
|
|
return zone
|
|
elseif zone.parent then
|
|
return Zone.find_parent_star(zone.parent)
|
|
end
|
|
end
|
|
|
|
function Zone.apply_markers(zone)
|
|
for force_name, force_data in pairs(global.forces) do
|
|
local force = game.forces[force_name]
|
|
if force and force_data.zones_discovered[zone.index] then
|
|
if not (force_data.zones_discovered[zone.index].marker and force_data.zones_discovered[zone.index].marker.valid) then
|
|
local surface = Zone.get_surface(zone)
|
|
if surface then
|
|
force_data.zones_discovered[zone.index].marker = force.add_chart_tag(surface, {
|
|
icon = {type = "virtual", name = Zone.get_signal_name(zone)},
|
|
position = {0,0},
|
|
text = zone.name
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.validate_controls_and_error(controls)
|
|
if controls then
|
|
for name, control in pairs(controls) do
|
|
if type(name) ~= "string" then
|
|
error(serpent.block(name))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.validate_controls(controls)
|
|
if controls then
|
|
for name, control in pairs(controls) do
|
|
if type(name) ~= "string" then
|
|
controls[name] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.apply_controls_to_mapgen(zone, controls, mapgen)
|
|
Zone.validate_controls(controls)
|
|
local frequency_multiplier = Zone.get_frequency_multiplier(zone)
|
|
for name, control in pairs(controls) do
|
|
if type(name) == "string" then
|
|
if name == "moisture" then
|
|
mapgen.property_expression_names = mapgen.property_expression_names or {}
|
|
if control.frequency then
|
|
mapgen.property_expression_names["control-setting:moisture:frequency:multiplier"] = control.frequency
|
|
end
|
|
if control.bias then
|
|
mapgen.property_expression_names["control-setting:moisture:bias"] = control.bias
|
|
end
|
|
elseif name == "aux" then
|
|
mapgen.property_expression_names = mapgen.property_expression_names or {}
|
|
if control.frequency then
|
|
mapgen.property_expression_names["control-setting:aux:frequency:multiplier"] = control.frequency
|
|
end
|
|
if control.bias then
|
|
mapgen.property_expression_names["control-setting:aux:bias"] = control.bias
|
|
end
|
|
elseif name == "water" then
|
|
if control.frequency then
|
|
mapgen.terrain_segmentation = control.frequency
|
|
end
|
|
if control.size then
|
|
mapgen.water = control.size
|
|
end
|
|
elseif name == "cliff" then
|
|
mapgen.cliff_settings = mapgen.cliff_settings or {
|
|
name="cliff",
|
|
cliff_elevation_0=10, -- default
|
|
cliff_elevation_interval=400, -- when set from the GUI the value is 40 / frequency.
|
|
richness=0, -- 0.17 to 6.
|
|
}
|
|
if control.frequency then
|
|
mapgen.cliff_settings.cliff_elevation_interval = 40 / control.frequency
|
|
mapgen.cliff_settings.cliff_elevation_0 = mapgen.cliff_settings.cliff_elevation_interval / 4
|
|
end
|
|
if control.richness then
|
|
mapgen.cliff_settings.richness = richness
|
|
end
|
|
else
|
|
if game.autoplace_control_prototypes[name] then
|
|
mapgen.autoplace_controls[name] = table.deepcopy(control)
|
|
if mapgen.autoplace_controls[name].frequency and not Util.table_contains(Zone.controls_without_frequency_multiplier, name) then
|
|
mapgen.autoplace_controls[name].frequency = mapgen.autoplace_controls[name].frequency * frequency_multiplier
|
|
end
|
|
else
|
|
log("Zone.apply_controls_to_mapgen: Attempt to apply invalid control name to mapgen: "..name)
|
|
controls[name] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.get_frequency_multiplier(zone)
|
|
if zone.radius then
|
|
return 5000 / zone.radius
|
|
end
|
|
return 1
|
|
end
|
|
|
|
function Zone.create_surface(zone)
|
|
|
|
if not zone.surface_index then
|
|
|
|
Universe.inflate_climate_controls(zone, false)
|
|
|
|
-- TODO planets should have customised controls
|
|
|
|
local map_gen_settings = table.deepcopy(game.default_map_gen_settings)
|
|
map_gen_settings.width = 0
|
|
map_gen_settings.height = 0
|
|
if not zone.seed then zone.seed = math.random(4294967295) end
|
|
map_gen_settings.seed = zone.seed
|
|
|
|
local autoplace_controls = map_gen_settings.autoplace_controls
|
|
zone.controls = zone.controls or {}
|
|
|
|
local frequency_multiplier = Zone.get_frequency_multiplier(zone) -- increase for small planets and moons
|
|
|
|
-- For all possible controls set values so it can be regenreated consistently.
|
|
for control_name, control_prototype in pairs(game.autoplace_control_prototypes) do
|
|
if control_name ~= "planet-size" and game.autoplace_control_prototypes[control_name] then
|
|
zone.controls[control_name] = zone.controls[control_name] or {}
|
|
zone.controls[control_name].frequency = (zone.controls[control_name].frequency or (0.17 + math.random() * math.random() * 2))
|
|
zone.controls[control_name].size = zone.controls[control_name].size or (0.1 + math.random() * 0.8)
|
|
zone.controls[control_name].richness = zone.controls[control_name].richness or (0.1 + math.random() * 0.8)
|
|
end
|
|
end
|
|
|
|
zone.controls.moisture = zone.controls.moisture or {}
|
|
zone.controls.moisture.frequency = (zone.controls.moisture.frequency or (0.17 + math.random() * math.random() * 2))
|
|
zone.controls.moisture.bias = zone.controls.moisture.bias or (math.random() - 0.5)
|
|
|
|
zone.controls.aux = zone.controls.aux or {}
|
|
zone.controls.aux.frequency = (zone.controls.aux.frequency or (0.17 + math.random() * math.random() * 2))
|
|
zone.controls.aux.bias = zone.controls.aux.bias or (math.random() - 0.5)
|
|
|
|
zone.controls.cliff = zone.controls.cliff or {}
|
|
zone.controls.cliff.frequency = (zone.controls.cliff.frequency or (0.17 + math.random() * math.random() * 10)) --
|
|
zone.controls.cliff.richness = zone.controls.cliff.richness or (math.random() * 1.25)
|
|
|
|
--[[
|
|
map_gen_settings.property_expression_names = {
|
|
["control-setting:moisture:frequency:multiplier"] = zone.controls.moisture.frequency * frequency_multiplier,
|
|
["control-setting:moisture:bias"] = zone.controls.moisture.bias,
|
|
["control-setting:aux:frequency:multiplier"] = zone.controls.aux.frequency * frequency_multiplier,
|
|
["control-setting:aux:bias"] = zone.controls.aux.bias,
|
|
}
|
|
zone.controls.water = zone.controls.water or {}
|
|
zone.controls.water.size = zone.controls.water.size or 0.01 -- high is 6, low is 0
|
|
zone.controls.water.frequency = (zone.controls.water.frequency or 1) -- low is 0.17
|
|
zone.controls.water.frequency = 0.5 + zone.controls.water.frequency / 2
|
|
map_gen_settings.water = zone.controls.water.size
|
|
map_gen_settings.terrain_segmentation = zone.controls.water.frequency
|
|
]]--
|
|
Zone.apply_controls_to_mapgen(zone, zone.controls, map_gen_settings)
|
|
|
|
autoplace_controls["planet-size"] = { frequency = 1, size = 1 } -- default
|
|
-- planet_radius = 10000 / 6 * (6 + log(1/planet_frequency/6, 2))
|
|
-- planet_frequency = 1 / 6 / 2 ^ (planet_radius * 6 / 10000 - 6)
|
|
local planet_size_frequency = 1/6 -- 10000 radius planet
|
|
if Zone.is_solid(zone) then
|
|
map_gen_settings.width = zone.radius*2+32
|
|
map_gen_settings.height = zone.radius*2+32
|
|
-- planet or moon
|
|
--planet_size_frequency = 1 / (zone.radius / 10000)
|
|
planet_size_frequency = 1 / 6 / 2 ^ (zone.radius * 6 / 10000 - 6)
|
|
local penalty = -100000
|
|
if zone.tags and util.table_contains(zone.tags, "water_none") then
|
|
map_gen_settings.property_expression_names["tile:deepwater:probability"] = penalty
|
|
map_gen_settings.property_expression_names["tile:water:probability"] = penalty
|
|
map_gen_settings.property_expression_names["tile:water-shallow:probability"] = penalty
|
|
map_gen_settings.property_expression_names["tile:water-mud:probability"] = penalty
|
|
end
|
|
map_gen_settings.property_expression_names["decorative:se-crater3-huge:probability"] = penalty
|
|
if not zone.is_homeworld then
|
|
map_gen_settings.starting_area = 0.5
|
|
end
|
|
else
|
|
if zone.type == "orbit" then
|
|
autoplace_controls["planet-size"].size = zone.parent.radius and (zone.parent.radius / 200) or 50
|
|
elseif zone.type == "asteroid-belt" then
|
|
autoplace_controls["planet-size"].size = 200
|
|
elseif zone.type == "asteroid-field" then
|
|
autoplace_controls["planet-size"].size = 10000
|
|
end
|
|
|
|
planet_size_frequency = 1/1000
|
|
map_gen_settings.cliff_settings={
|
|
name="cliff",
|
|
cliff_elevation_0=10, -- default
|
|
cliff_elevation_interval=400, -- when set from the GUI the value is 40 / frequency.
|
|
richness=0, -- 0.17 to 6.
|
|
}
|
|
zone.controls.cliff.frequency = 0
|
|
zone.controls.cliff.richness = 0
|
|
|
|
map_gen_settings.property_expression_names = {
|
|
["control-setting:moisture:frequency:multiplier"] = 10,
|
|
["control-setting:moisture:bias"] = -1,
|
|
["control-setting:aux:frequency:multiplier"] = 0,
|
|
["control-setting:aux:bias"] = 0,
|
|
}
|
|
map_gen_settings.starting_area = 0
|
|
end
|
|
autoplace_controls["planet-size"].frequency = planet_size_frequency
|
|
|
|
Log.debug_log("Creating surface " .. zone.name .. " with map_gen_settings:")
|
|
Log.debug_log(util.table_to_string(map_gen_settings))
|
|
if Zone.is_space(zone) then
|
|
-- Speed up terrain generation by excluding everything not specifically allowed to spawn
|
|
--map_gen_settings.default_enable_all_autoplace_controls = false
|
|
map_gen_settings.autoplace_settings={
|
|
["decorative"]={
|
|
treat_missing_as_default=false,
|
|
settings={
|
|
["se-crater3-huge"] ={},
|
|
["se-crater1-large-rare"] ={},
|
|
["se-crater1-large"] ={},
|
|
["se-crater2-medium"] ={},
|
|
["se-crater4-small"] ={},
|
|
["se-sand-decal-space"] ={},
|
|
["se-stone-decal-space"] ={},
|
|
["se-rock-medium-asteroid"] ={},
|
|
["se-rock-small-asteroid"] ={},
|
|
["se-rock-tiny-asteroid"] ={},
|
|
["se-sand-rock-medium-asteroid"] ={},
|
|
["se-sand-rock-small-asteroid"] ={}
|
|
}
|
|
},
|
|
--[[["entity"]={
|
|
treat_missing_as_default=false,
|
|
settings={
|
|
["se-rock-huge-asteroid"] ={},
|
|
["se-rock-big-asteroid"] ={},
|
|
["se-sand-rock-big-asteroid"] ={},
|
|
["se-rock-huge-space"] ={},
|
|
["se-rock-big-space"] ={},
|
|
}
|
|
},]]--
|
|
["tile"]={
|
|
treat_missing_as_default=false,
|
|
settings={
|
|
["se-asteroid"]={},
|
|
["se-space"]={}
|
|
}
|
|
},
|
|
}
|
|
else
|
|
-- speed up terrain generation by specifying specific things not to spawn
|
|
local penalty = -100000
|
|
map_gen_settings.property_expression_names["decorative:se-crater3-huge:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-crater1-large-rare:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-crater1-large:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-crater2-medium:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-crater4-small:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-sand-decal-space:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-stone-decal-space:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-rock-medium-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-rock-small-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-rock-tiny-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-sand-rock-medium-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["decorative:se-sand-rock-small-asteroid:probability"] = penalty
|
|
|
|
map_gen_settings.property_expression_names["entity:se-rock-huge-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["entity:se-rock-big-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["entity:se-sand-rock-big-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["entity:se-rock-huge-space:probability"] = penalty
|
|
map_gen_settings.property_expression_names["entity:se-rock-big-space:probability"] = penalty
|
|
|
|
map_gen_settings.property_expression_names["tile:se-asteroid:probability"] = penalty
|
|
map_gen_settings.property_expression_names["tile:se-space:probability"] = penalty
|
|
end
|
|
|
|
local surface
|
|
if not game.surfaces[zone.name] then
|
|
surface = game.create_surface(zone.name, map_gen_settings)
|
|
surface.force_generate_chunk_requests()
|
|
|
|
else
|
|
-- this happens if the mod was uninstalled and reinstalled. The surface will be invalid and unfaxable.
|
|
-- game.delete_surface(zone.name) -- does not work in time to re make the surface
|
|
surface = game.surfaces[zone.name]
|
|
surface.clear(false)
|
|
|
|
end
|
|
|
|
zone.surface_index = surface.index
|
|
global.zones_by_surface = global.zones_by_surface or {}
|
|
global.zones_by_surface[surface.index] = zone
|
|
|
|
Zone.set_solar_and_daytime(zone)
|
|
|
|
if zone.type == "planet" and not zone.is_homeworld then
|
|
|
|
-- Vault planet
|
|
if (not zone.ruins) and not zone.glyph then
|
|
Ancient.assign_zone_next_glyph(zone)
|
|
end
|
|
Ancient.make_vault_exterior(zone) -- only makes the vault if glyph exists
|
|
|
|
-- Interburbulator planet
|
|
if (not zone.glyph) and (not zone.ruins) and not global.interburbulator then
|
|
Interburbulator.make_interburbulator(zone)
|
|
end
|
|
if zone.interburbulator then
|
|
Interburbulator.build_platform()
|
|
end
|
|
|
|
end
|
|
|
|
if not zone.is_homeworld then
|
|
-- Unique ruins
|
|
Ruin.zone_assign_unique_ruins(zone)
|
|
Ruin.zone_build_ruins(zone, surface)
|
|
end
|
|
|
|
if zone.type == "anomaly" then
|
|
Ancient.make_gate(Ancient.gate_default_position)
|
|
Ruin.build({ruin_name = "galaxy-ship", surface_index = surface.index, position = Ancient.galaxy_ship_default_position})
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
|
|
function Zone.get_star_from_child(zone)
|
|
if zone.type == "star" then
|
|
return zone
|
|
elseif zone.parent then
|
|
return Zone.get_star_from_child(zone.parent)
|
|
end
|
|
end
|
|
|
|
function Zone.get_stellar_object_from_child(zone)
|
|
if zone.type == "asteroid-field" then
|
|
return zone
|
|
else
|
|
return Zone.get_star_from_child(zone)
|
|
end
|
|
end
|
|
|
|
function Zone.get_star_from_position(zone)
|
|
if not zone.stellar_position then return end
|
|
for _, star in pairs(global.universe.stars) do
|
|
if star.stellar_position.x == zone.stellar_position.x and star.stellar_position.y == zone.stellar_position.y then
|
|
return star
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.get_stellar_object_from_position(zone)
|
|
local star = Zone.get_star_from_position(zone)
|
|
if star then return star end
|
|
for _, asteroid_field in pairs(global.universe.space_zones) do
|
|
if asteroid_field.stellar_position.x == zone.stellar_position.x and asteroid_field.stellar_position.y == zone.stellar_position.y then
|
|
return asteroid_field
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.get_solar(zone) -- can be spaceship too
|
|
-- return the actual expected light % including daytime for space zones and spaceships.
|
|
-- return peak expected light % space solid zones.
|
|
|
|
if zone.type == "anomaly" then
|
|
return 0
|
|
end
|
|
|
|
local star
|
|
local star_gravity_well = 0
|
|
|
|
if zone.type == "spaceship" then
|
|
star = zone.near_star
|
|
star_gravity_well = zone.star_gravity_well or 0
|
|
else
|
|
star = Zone.get_star_from_child(zone)
|
|
star_gravity_well = Zone.get_star_gravity_well(zone)
|
|
end
|
|
|
|
local light_percent = 0
|
|
|
|
if star then
|
|
light_percent = 1.6 * star_gravity_well / (star.star_gravity_well + 1)
|
|
end
|
|
|
|
if Zone.is_space(zone) then
|
|
if(zone.type == "orbit" and zone.parent and zone.parent.type == "star") then -- star
|
|
light_percent = light_percent * 10 -- x20
|
|
elseif zone.type == "asteroid-belt" then
|
|
light_percent = light_percent * 2.5 -- x5
|
|
else
|
|
light_percent = light_percent * 5 -- x10
|
|
if zone.parent and zone.parent.radius then
|
|
light_percent = light_percent * (1 - 0.1 * zone.parent.radius / 10000)
|
|
end
|
|
end
|
|
light_percent = light_percent + 0.01
|
|
else
|
|
if zone.radius then
|
|
light_percent = light_percent * (1 - 0.1 * zone.radius / 10000)
|
|
if zone.is_homeworld then
|
|
light_percent = 1
|
|
end
|
|
end
|
|
end
|
|
|
|
if zone.space_distortion and zone.space_distortion > 0 then
|
|
|
|
light_percent = light_percent * (1 - zone.space_distortion)
|
|
|
|
if zone.is_homeworld then
|
|
light_percent = 1
|
|
end
|
|
end
|
|
return light_percent
|
|
end
|
|
|
|
function Zone.get_display_light_percent(zone)
|
|
return Zone.get_solar(zone)
|
|
end
|
|
|
|
function Zone.set_solar_and_daytime(zone)
|
|
local surface = Zone.get_surface(zone)
|
|
if not surface then return end
|
|
|
|
if zone.type == "anomaly" then
|
|
surface.solar_power_multiplier = 0
|
|
surface.daytime = 0.5
|
|
return
|
|
end
|
|
|
|
local light_percent = Zone.get_solar(zone)
|
|
|
|
if Zone.is_space(zone) then
|
|
|
|
-- light_percent is the total output
|
|
-- but we have most space zones daylight ranging from mid evening to night so that lights are active.
|
|
-- so that is the main driving factor.
|
|
-- except stars.
|
|
|
|
surface.freeze_daytime = true
|
|
|
|
if zone.type == "orbit" and zone.parent.type == "star" then
|
|
|
|
surface.daytime = 0 -- very bright
|
|
surface.solar_power_multiplier = Zone.solar_multiplier * light_percent
|
|
|
|
else
|
|
|
|
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
|
|
-- for now all zone surfaces are syneced so that daytime 0 is at game.tick 0
|
|
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
|
|
|
|
else
|
|
-- planet or moon
|
|
-- has daytime
|
|
|
|
surface.daytime = (game.tick / zone.ticks_per_day) % 1
|
|
surface.solar_power_multiplier = Zone.solar_multiplier * light_percent
|
|
|
|
if zone.ticks_per_day then
|
|
surface.ticks_per_day = zone.ticks_per_day
|
|
end
|
|
|
|
end
|
|
|
|
if zone.type == "anomaly" then
|
|
surface.solar_power_multiplier = 0
|
|
surface.daytime = 0.5
|
|
end
|
|
|
|
end
|
|
|
|
function Zone.get_flags_weight(zone, force_name, playerdata)
|
|
local weight = (
|
|
((playerdata.track_glyphs and zone.glyph) and 0.96 or 0)
|
|
+ ((zone.interburbulator or zone.ruins) and 0.95 or 0)
|
|
+ ((playerdata.visited_zone and playerdata.visited_zone[zone.index]) and 0.97 or 0)
|
|
+ ((global.forces[force_name].zone_assets and global.forces[force_name].zone_assets[zone.index] and table_size(global.forces[force_name].zone_assets[zone.index].rocket_landing_pad_names) > 0) and 0.98 or 0)
|
|
+ ((global.forces[force_name].zone_assets and global.forces[force_name].zone_assets[zone.index] and table_size(global.forces[force_name].zone_assets[zone.index].rocket_launch_pad_names) > 0) and 1 or 0)
|
|
)
|
|
Log.debug_log("Zone.get_flags_weight " .. zone.name.." " ..weight, "zone")
|
|
return weight
|
|
end
|
|
|
|
function Zone.get_surface(zone) -- returns surface but does not build
|
|
if zone.type == "spaceship" then
|
|
return Spaceship.get_current_surface(zone)
|
|
end
|
|
if zone.surface_index then
|
|
return game.get_surface(zone.surface_index)
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function Zone.get_surface_name(zone)
|
|
local surface = Zone.get_surface(zone)
|
|
if surface then return surface.name end
|
|
end
|
|
|
|
function Zone.get_make_surface(zone)
|
|
if zone.type == "spaceship" then
|
|
return Spaceship.get_current_surface(zone)
|
|
end
|
|
if not zone.surface_index then Zone.create_surface(zone) end
|
|
return game.get_surface(zone.surface_index)
|
|
end
|
|
|
|
function Zone.discover(force_name, zone, source) -- source could be "Satellite "
|
|
global.forces[force_name] = global.forces[force_name] or {}
|
|
global.forces[force_name].zones_discovered = global.forces[force_name].zones_discovered or {}
|
|
global.forces[force_name].zones_discovered_count = global.forces[force_name].zones_discovered_count or 0
|
|
|
|
if not global.forces[force_name].zones_discovered[zone.index] then
|
|
|
|
Universe.inflate_climate_controls(zone, false)
|
|
|
|
global.forces[force_name].zones_discovered[zone.index] = {
|
|
discovered_at = game.tick,
|
|
marker = nil
|
|
}
|
|
global.forces[force_name].zones_discovered_count = global.forces[force_name].zones_discovered_count + 1
|
|
if zone.type == "planet" and (not zone.is_homeworld) and (not zone.ruins) and (not zone.glyph) then
|
|
Ancient.assign_zone_next_glyph(zone)
|
|
end
|
|
|
|
-- Unique ruins
|
|
Ruin.zone_assign_unique_ruins(zone)
|
|
|
|
local message = nil
|
|
if source then
|
|
message = {"space-exploration.source-discovered-zone", source, Zone.type_title(zone), Zone.get_icon(zone), zone.name}
|
|
--game.forces[force_name].print(source .. " discovered a new " .. Zone.type_title(zone) .. ": " .. zone.name)
|
|
else
|
|
message = {"space-exploration.discovered-zone", source, Zone.type_title(zone), Zone.get_icon(zone), zone.name}
|
|
--game.forces[force_name].print("Discovered a new " .. Zone.type_title(zone) .. ": " .. zone.name)
|
|
end
|
|
local tick_task = new_tick_task("force-message")
|
|
tick_task.force_name = force_name
|
|
tick_task.message = message
|
|
|
|
Zone.apply_markers(zone) -- in case the surface exists
|
|
|
|
for _, player in pairs(game.connected_players) do
|
|
if Zonelist.get_main_window(player.index) then
|
|
Zonelist.gui_update_list(player.index)
|
|
end
|
|
end
|
|
|
|
if zone.type == "anomaly" then
|
|
local tick_task = new_tick_task("force-message")
|
|
tick_task.force_name = force_name
|
|
tick_task.message = {"space-exploration.discovered-anomaly-additional"}
|
|
tick_task.delay_until = game.tick + 300 --5s
|
|
end
|
|
|
|
if zone.glyph then
|
|
local force = game.forces[force_name]
|
|
for _, player in pairs(force.players) do
|
|
if player.connected then
|
|
local playerdata = get_make_playerdata(player)
|
|
if playerdata.track_glyphs then
|
|
player.print({"space-exploration.discovered-glyph-vault", zone.name})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
return false
|
|
end
|
|
|
|
function Zone.discover_next_research(force_name, source, allow_targeted)
|
|
global.forces[force_name] = global.forces[force_name] or {}
|
|
global.forces[force_name].zones_discovered = global.forces[force_name].zones_discovered or {}
|
|
global.forces[force_name].zones_discovered_count = global.forces[force_name].zones_discovered_count or 0
|
|
|
|
local target_resource = "n/a"
|
|
if allow_targeted then
|
|
target_resource = global.forces[force_name].search_for_resource
|
|
end
|
|
|
|
local can_discover = {}
|
|
local can_discover_targeted = {}
|
|
|
|
-- star and deep space discovery should be bias to nearer positions
|
|
local closest_1 = nil
|
|
local closest_stellar_distance = 1000000
|
|
local pos1 = {x = 0, y = 0} -- focus on aeras close to the center of the star map
|
|
|
|
for _, star in pairs(global.universe.stars) do
|
|
if not global.forces[force_name].zones_discovered[star.index] then
|
|
local pos2 = Zone.get_stellar_position(star)
|
|
local distance = util.vectors_delta_length(pos1, pos2)
|
|
if distance < closest_stellar_distance then
|
|
closest_stellar_distance = distance
|
|
closest_1 = star
|
|
end
|
|
end
|
|
end
|
|
|
|
if closest_1 then
|
|
-- x5
|
|
table.insert(can_discover, closest_1)
|
|
table.insert(can_discover, closest_1)
|
|
table.insert(can_discover, closest_1)
|
|
table.insert(can_discover, closest_1)
|
|
table.insert(can_discover, closest_1)
|
|
end
|
|
|
|
for _, star in pairs(global.universe.stars) do
|
|
if global.forces[force_name].zones_discovered[star.index] then
|
|
for _, planet in pairs(star.children) do
|
|
if not global.forces[force_name].zones_discovered[planet.index] then
|
|
if planet.primary_resource == target_resource then
|
|
table.insert(can_discover_targeted, planet)
|
|
if planet.type == "planet" then
|
|
--x5 bias towards planets
|
|
table.insert(can_discover_targeted, planet)
|
|
table.insert(can_discover_targeted, planet)
|
|
table.insert(can_discover_targeted, planet)
|
|
table.insert(can_discover_targeted, planet)
|
|
end
|
|
else
|
|
table.insert(can_discover, planet)
|
|
if planet.type == "planet" then
|
|
--x5 bias towards planets
|
|
table.insert(can_discover, planet)
|
|
table.insert(can_discover, planet)
|
|
table.insert(can_discover, planet)
|
|
table.insert(can_discover, planet)
|
|
end
|
|
end
|
|
else
|
|
if planet.children then
|
|
for _, moon in pairs(planet.children) do
|
|
if not global.forces[force_name].zones_discovered[moon.index] then
|
|
if moon.primary_resource == target_resource then
|
|
table.insert(can_discover_targeted, moon)
|
|
else
|
|
table.insert(can_discover, moon)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #can_discover_targeted > 0 then
|
|
return Zone.discover(force_name, Util.random_from_array(can_discover_targeted), source)
|
|
end
|
|
if #can_discover > 0 then
|
|
return Zone.discover(force_name, Util.random_from_array(can_discover), source)
|
|
end
|
|
|
|
end
|
|
|
|
function Zone.discover_next_research_deep_space(force_name, source, allow_targeted)
|
|
global.forces[force_name] = global.forces[force_name] or {}
|
|
global.forces[force_name].zones_discovered = global.forces[force_name].zones_discovered or {}
|
|
global.forces[force_name].zones_discovered_count = global.forces[force_name].zones_discovered_count or 0
|
|
|
|
if game.forces[force_name].technologies[Zone.name_tech_discover_deep].level > 11 then
|
|
-- should have discovered multiple stars at this point
|
|
if not global.forces[force_name].zones_discovered[global.universe.anomaly.index] then
|
|
return Zone.discover(force_name, global.universe.anomaly, source)
|
|
end
|
|
end
|
|
|
|
local target_resource = "n/a"
|
|
if allow_targeted then
|
|
target_resource = global.forces[force_name].search_for_resource
|
|
end
|
|
|
|
local can_discover = {}
|
|
local can_discover_targeted = {}
|
|
for _, zone in pairs(global.universe.space_zones) do
|
|
if not global.forces[force_name].zones_discovered[zone.index] then
|
|
if zone.primary_resource == target_resource then
|
|
table.insert(can_discover_targeted, zone)
|
|
else
|
|
table.insert(can_discover, zone)
|
|
end
|
|
end
|
|
end
|
|
|
|
if #can_discover_targeted > 0 then
|
|
return Zone.discover(force_name, Util.random_from_array(can_discover_targeted), source)
|
|
end
|
|
if #can_discover > 0 then
|
|
return Zone.discover(force_name, Util.random_from_array(can_discover), source)
|
|
end
|
|
end
|
|
|
|
function Zone.discover_next_satellite(force_name, source, system_restriction)
|
|
global.forces[force_name] = global.forces[force_name] or {}
|
|
global.forces[force_name].zones_discovered = global.forces[force_name].zones_discovered or {}
|
|
global.forces[force_name].zones_discovered_count = global.forces[force_name].zones_discovered_count or 0
|
|
|
|
if system_restriction then
|
|
local star = system_restriction
|
|
while star.parent do
|
|
star = star.parent
|
|
end
|
|
local last_valid
|
|
if star.children then
|
|
for _, planet in pairs(star.children) do
|
|
if not global.forces[force_name].zones_discovered[planet.index] then
|
|
if math.random() < 0.5 then -- skip it
|
|
last_valid = planet
|
|
else
|
|
return Zone.discover(force_name, planet, source)
|
|
end
|
|
elseif planet.children then
|
|
for _, moon in pairs(planet.children) do
|
|
if not global.forces[force_name].zones_discovered[moon.index] then
|
|
if math.random() < 0.5 then -- skip it
|
|
last_valid = moon
|
|
else
|
|
return Zone.discover(force_name, moon, source)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--skipped too much
|
|
if last_valid then
|
|
return Zone.discover(force_name, last_valid, source)
|
|
end
|
|
-- find stars now
|
|
local closest_1 = nil
|
|
local closest_stellar_distance = 1000000
|
|
local pos1 = Zone.get_stellar_position(star)
|
|
|
|
for _, star2 in pairs(global.universe.stars) do
|
|
if not global.forces[force_name].zones_discovered[star2.index] then
|
|
local pos2 = Zone.get_stellar_position(star2)
|
|
local distance = util.vectors_delta_length(pos1, pos2)
|
|
if distance < closest_stellar_distance then
|
|
closest_stellar_distance = distance
|
|
closest_1 = star2
|
|
end
|
|
end
|
|
end
|
|
if closest_1 then
|
|
return Zone.discover(force_name, closest_1, source)
|
|
end
|
|
return false
|
|
end
|
|
|
|
end
|
|
|
|
function Zone.find_nearest_stellar_object(stellar_position)
|
|
local closest_distance = math.huge
|
|
local closest = nil
|
|
|
|
for _, star in pairs(global.universe.stars) do
|
|
local distance = util.vectors_delta_length(star.stellar_position, stellar_position)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest = star
|
|
end
|
|
end
|
|
|
|
for _, space_zone in pairs(global.universe.space_zones) do
|
|
local distance = util.vectors_delta_length(space_zone.stellar_position, stellar_position)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest = space_zone
|
|
end
|
|
end
|
|
return closest
|
|
end
|
|
|
|
function Zone.find_nearest_star(stellar_position)
|
|
local closest_distance = math.huge
|
|
local closest = nil
|
|
|
|
for _, star in pairs(global.universe.stars) do
|
|
local distance = util.vectors_delta_length(star.stellar_position, stellar_position)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest = star
|
|
end
|
|
end
|
|
return closest
|
|
end
|
|
|
|
function Zone.find_nearest_zone(space_distortion, stellar_position, star_gravity_well, planet_gravity_well)
|
|
|
|
if space_distortion > 0.4 then return global.universe.anomaly end -- default from the anomaly
|
|
|
|
local star = Zone.find_nearest_stellar_object(stellar_position) -- can be asteroid field
|
|
if star_gravity_well > 0 then
|
|
local closest_zone = star
|
|
local closest_distance = math.abs((star.star_gravity_well or 0) - star_gravity_well)
|
|
if closest_zone.type == "star" then
|
|
for _, planet in pairs(star.children) do
|
|
local distance = math.abs(planet.star_gravity_well - star_gravity_well)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest_zone = planet
|
|
end
|
|
end
|
|
end
|
|
|
|
if closest_zone.type == "planet" then
|
|
local closest_zone2 = closest_zone
|
|
closest_distance = math.abs(closest_zone.planet_gravity_well - planet_gravity_well)
|
|
for _, moon in pairs(closest_zone.children) do
|
|
if moon.type == "moon" then
|
|
local distance = math.abs(moon.planet_gravity_well - planet_gravity_well)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest_zone2 = moon
|
|
end
|
|
end
|
|
end
|
|
return closest_zone2
|
|
end
|
|
return closest_zone
|
|
else
|
|
if not star then return global.universe.anomaly end
|
|
if not star.children then return star end
|
|
local last_child = star.children[#star.children]
|
|
if last_child.children then
|
|
last_child = last_child.children[#last_child.children]
|
|
end
|
|
return last_child
|
|
end
|
|
return Zone.get_default()
|
|
|
|
end
|
|
|
|
function Zone.find_nearest_solid_zone(space_distortion, stellar_position, star_gravity_well, planet_gravity_well, allow_moon)
|
|
if space_distortion == 1 then return Zone.get_default() end -- default from the anomaly
|
|
|
|
if planet_gravity_well == 0 then -- if no moon
|
|
planet_gravity_well = 100000 -- high to land on planet
|
|
end
|
|
|
|
if star_gravity_well == 0 then -- if no planet
|
|
star_gravity_well = 100000 -- high to land on planet with high solar
|
|
end
|
|
|
|
local star = Zone.find_nearest_star(stellar_position)
|
|
if not star then return Zone.get_default() end
|
|
|
|
local closest_planet = nil
|
|
local closest_distance = math.huge
|
|
for _, planet in pairs(star.children) do
|
|
if planet.type == "planet" then -- not an asteroid belt
|
|
local distance = math.abs(planet.star_gravity_well - star_gravity_well)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest_planet = planet
|
|
end
|
|
end
|
|
end
|
|
|
|
if not closest_planet then return Zone.get_default() end
|
|
|
|
local closest_body = closest_planet -- default to planet
|
|
|
|
if allow_moon then
|
|
closest_distance = math.abs(closest_body.planet_gravity_well - planet_gravity_well)
|
|
-- see if a moon is closer
|
|
for _, moon in pairs(closest_planet.children) do
|
|
if moon.type == "moon" then
|
|
local distance = math.abs(moon.planet_gravity_well - planet_gravity_well)
|
|
if distance < closest_distance then
|
|
closest_distance = distance
|
|
closest_body = moon
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return closest_body
|
|
|
|
end
|
|
|
|
function Zone.find_nearest_solid_zone_from_zone(zone)
|
|
-- typically used for escape pod
|
|
if Zone.is_solid(zone) then return nil end -- already there
|
|
|
|
if zone.type == "orbit" and Zone.is_solid(zone.parent) then
|
|
return zone.parent -- drop to planet / moon
|
|
end
|
|
|
|
return Zone.find_nearest_solid_zone(
|
|
Zone.get_space_distortion(zone),
|
|
Zone.get_stellar_position(zone),
|
|
Zone.get_star_gravity_well(zone),
|
|
Zone.get_planet_gravity_well(zone),
|
|
true
|
|
)
|
|
end
|
|
|
|
function Zone.get_force_assets(force_name, zone_index)
|
|
if not global.forces[force_name] then
|
|
if game.forces[force_name] then
|
|
setup_force(game.forces[force_name])
|
|
else
|
|
return
|
|
end
|
|
end
|
|
if not global.forces[force_name] then
|
|
if game.forces[force_name] then
|
|
game.forces[force_name].print("Error getting force data for invalid player force " .. force_name)
|
|
else
|
|
game.forces[force_name].print("Error getting force data for invalid force " .. force_name)
|
|
end
|
|
return -- invalid force
|
|
end
|
|
if not global.forces[force_name].zone_assets then
|
|
global.forces[force_name].zone_assets = {}
|
|
end
|
|
if not global.forces[force_name].zone_assets[zone_index] then
|
|
global.forces[force_name].zone_assets[zone_index] = {
|
|
rocket_launch_pad_names = {},
|
|
rocket_landing_pad_names = {},
|
|
}
|
|
end
|
|
--Log.debug_log("Zone.get_force_assets: " .. util.table_to_string(global.forces[force_name].zone_assets[zone_index]))
|
|
return global.forces[force_name].zone_assets[zone_index]
|
|
end
|
|
|
|
function Zone.get_travel_delta_v_sub(origin, destination)
|
|
-- expected ranges:
|
|
-- 1500 planetary system
|
|
-- 15000 solar system
|
|
-- 50000 interstellarsystem
|
|
-- 50000 to/from anomaly
|
|
if origin == destination then return 0 end
|
|
|
|
local origin_space_distorion = Zone.get_space_distortion(origin)
|
|
local origin_stellar_position = Zone.get_stellar_position(origin)
|
|
local origin_star_gravity_well = Zone.get_star_gravity_well(origin)
|
|
local origin_planet_gravity_well = Zone.get_planet_gravity_well(origin)
|
|
|
|
local destination_space_distorion = Zone.get_space_distortion(destination)
|
|
local destination_stellar_position = Zone.get_stellar_position(destination)
|
|
local destination_star_gravity_well = Zone.get_star_gravity_well(destination)
|
|
local destination_planet_gravity_well = Zone.get_planet_gravity_well(destination)
|
|
|
|
if origin_space_distorion > 0 then
|
|
return Zone.travel_cost_space_distortion
|
|
+ Zone.travel_cost_star_gravity * destination_star_gravity_well
|
|
+ Zone.travel_cost_planet_gravity * destination_planet_gravity_well
|
|
elseif destination_space_distorion > 0 then
|
|
return Zone.travel_cost_space_distortion
|
|
+ Zone.travel_cost_star_gravity * origin_star_gravity_well
|
|
+ Zone.travel_cost_planet_gravity * origin_planet_gravity_well
|
|
end
|
|
|
|
if origin_stellar_position.x == destination_stellar_position.x and origin_stellar_position.y == destination_stellar_position.y then
|
|
-- same solar system
|
|
if origin_star_gravity_well == destination_star_gravity_well then
|
|
-- same planetary system
|
|
return Zone.travel_cost_planet_gravity * math.abs(origin_planet_gravity_well - destination_planet_gravity_well) -- the planet_gravity_well difference
|
|
else
|
|
-- different planetary systems
|
|
return Zone.travel_cost_star_gravity * math.abs(destination_star_gravity_well - origin_star_gravity_well) -- the star_gravity_well difference
|
|
+ Zone.travel_cost_planet_gravity * origin_planet_gravity_well
|
|
+ Zone.travel_cost_planet_gravity * destination_planet_gravity_well
|
|
end
|
|
else
|
|
-- interstellar
|
|
return Zone.travel_cost_interstellar * util.vectors_delta_length(origin_stellar_position, destination_stellar_position)
|
|
+ Zone.travel_cost_star_gravity * origin_star_gravity_well
|
|
+ Zone.travel_cost_planet_gravity * origin_planet_gravity_well
|
|
+ Zone.travel_cost_star_gravity * destination_star_gravity_well
|
|
+ Zone.travel_cost_planet_gravity * destination_planet_gravity_well
|
|
end
|
|
|
|
end
|
|
|
|
function Zone.get_travel_delta_v(origin, destination)
|
|
if origin and destination then
|
|
if origin.type == "spaceship" or destination.type == "spaceship" then
|
|
return Zone.get_travel_delta_v_sub(origin, destination)
|
|
end
|
|
global.cache_travel_delta_v = global.cache_travel_delta_v or {}
|
|
global.cache_travel_delta_v[origin.index] = global.cache_travel_delta_v[origin.index] or {}
|
|
if not global.cache_travel_delta_v[origin.index][destination.index] then
|
|
global.cache_travel_delta_v[origin.index][destination.index] = Zone.get_travel_delta_v_sub(origin, destination)
|
|
end
|
|
return global.cache_travel_delta_v[origin.index][destination.index]
|
|
end
|
|
end
|
|
|
|
function Zone.get_launch_delta_v(zone)
|
|
-- 10000 to 800 for planets, 0 for in space
|
|
return 500 + (zone.radius or 50)
|
|
end
|
|
|
|
function Zone.get_closest_zone(origin, destination_list)
|
|
if origin and destination_list then
|
|
if Util.table_contains(destination_list, origin) then return origin end
|
|
local destination = nil
|
|
local delta_v_closest = math.huge
|
|
local delta_v_current = nil
|
|
for _, zone in pairs(destination_list) do
|
|
delta_v_current = Zone.get_travel_delta_v(origin, zone)
|
|
if delta_v_current < delta_v_closest then
|
|
delta_v_closest = delta_v_current
|
|
destination = zone
|
|
end
|
|
end
|
|
return destination
|
|
end
|
|
end
|
|
|
|
function Zone.find_zone_landing_position(zone, try_position)
|
|
Log.debug_log("Zone.find_zone_landing_position: " ..zone.name)
|
|
local surface = Zone.get_make_surface(zone)
|
|
|
|
if not try_position then
|
|
if zone.type == "spaceship" then
|
|
try_position = Spaceship.get_boarding_position(zone)
|
|
elseif Zone.is_solid(zone) then
|
|
local try_angle = math.random() * math.pi * 2 -- rad
|
|
local try_distance = math.random() * (zone.radius / 4 or 512)
|
|
try_position = {x = math.cos(try_angle) * try_distance, y = math.sin(try_angle) * try_distance}
|
|
else
|
|
try_position = {x = math.random(-512, 512), y = math.random(-128, 128)}
|
|
end
|
|
end
|
|
surface.request_to_generate_chunks(try_position, 2)
|
|
surface.force_generate_chunk_requests()
|
|
local safe_position
|
|
if Zone.is_solid(zone) then
|
|
safe_position = surface.find_non_colliding_position(collision_rocket_destination_surface, try_position, 64, 1)
|
|
else
|
|
safe_position = surface.find_non_colliding_position(collision_rocket_destination_orbit, try_position, 64, 1)
|
|
end
|
|
if not safe_position then
|
|
local try_position_2 = {x = 64 * (math.random() - 0.5), y = 64 * (math.random() - 0.5)}
|
|
surface.request_to_generate_chunks(try_position_2, 2)
|
|
surface.force_generate_chunk_requests()
|
|
if Zone.is_solid(zone) then
|
|
safe_position = surface.find_non_colliding_position(collision_rocket_destination_surface, try_position_2, 64, 1)
|
|
else
|
|
safe_position = surface.find_non_colliding_position(collision_rocket_destination_orbit, try_position_2, 64, 1)
|
|
end
|
|
end
|
|
if safe_position then
|
|
Log.debug_log("Zone.find_zone_landing_position: safe_position found")
|
|
return safe_position
|
|
else
|
|
Log.debug_log("Zone.find_zone_landing_position: safe_position not found, falling back to try_position")
|
|
return try_position
|
|
end
|
|
end
|
|
|
|
function Zone.dropdown_name_from_zone(zone, no_indent)
|
|
local i1 = " "
|
|
local i2 = " "
|
|
local i3 = " "
|
|
if no_indent then
|
|
i1 = ""
|
|
i2 = ""
|
|
i3 = ""
|
|
end
|
|
if zone.type == "orbit" then
|
|
if zone.parent.type == "star" then
|
|
return "[img=virtual-signal/se-star] " .. zone.name -- star orbit
|
|
elseif zone.parent.type == "planet" then
|
|
return i2.."[img=virtual-signal/se-planet-orbit] " .. zone.name -- planet orbit
|
|
elseif zone.parent.type == "moon" then
|
|
return i3.."[img=virtual-signal/se-moon-orbit] " .. zone.name -- moon orbit
|
|
end
|
|
elseif zone.type == "asteroid-belt" then
|
|
return i1 .. "[img=virtual-signal/se-asteroid-belt] " .. zone.name
|
|
elseif zone.type == "planet" then
|
|
return i1 .. "[img=virtual-signal/se-planet] " .. zone.name
|
|
elseif zone.type == "moon" then
|
|
return i2 .. "[img=virtual-signal/se-moon] " .. zone.name
|
|
elseif zone.type == "asteroid-field" then
|
|
return "[img=virtual-signal/"..mod_prefix..zone.type .. "] " .. zone.name .. " [color=black](Asteroid Field)[/color]"
|
|
elseif zone.type == "anomaly" then
|
|
return "[img=virtual-signal/"..mod_prefix..zone.type .. "] " .. zone.name .. " [color=black](Anomaly)[/color]"
|
|
elseif zone.type == "spaceship" then
|
|
return "[img=virtual-signal/"..mod_prefix..zone.type .. "] " .. zone.name .. " [color=black](Spaceship)[/color]"
|
|
end
|
|
return "[img=virtual-signal/"..mod_prefix..zone.type .. "] " .. zone.name
|
|
end
|
|
|
|
function Zone.get_alphabetised()
|
|
local zones_alphabetised = {}
|
|
for _, zone in pairs(global.zone_index) do
|
|
table.insert(zones_alphabetised, zone)
|
|
end
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
table.insert(zones_alphabetised, spaceship)
|
|
end
|
|
table.sort(zones_alphabetised, function(a,b) return a.name < b.name end)
|
|
return zones_alphabetised
|
|
end
|
|
|
|
function Zone.is_visible_to_force(zone, force_name)
|
|
if global.debug_view_all_zones then return true end
|
|
if zone.type == "spaceship" then return zone.force_name == force_name end
|
|
if not (global.forces[force_name] and global.forces[force_name].zones_discovered) then return false end
|
|
return global.forces[force_name].zones_discovered[zone.index] or (zone.type == "orbit" and global.forces[force_name].zones_discovered[zone.parent.index]) and true or false
|
|
end
|
|
function Zone.insert_if_visible_to_force(target_table, zone, force_name)
|
|
if Zone.is_visible_to_force(zone, force_name) then table.insert(target_table, zone) end
|
|
end
|
|
|
|
function Zone.dropdown_list_zone_destinations(force_name, current, alphabetical, filter, wildcard, star_restriction)
|
|
-- wildcard = {list = "display", value={type = "any"}}
|
|
local selected_index = 1
|
|
local list = {""}
|
|
local values = {{type = "nil", index = nil}} -- zone indexes
|
|
local forcedata = global.forces[force_name]
|
|
if not forcedata then return list, selected_index, values end
|
|
|
|
if wildcard then
|
|
table.insert(list, wildcard.list)
|
|
table.insert(values, wildcard.value)
|
|
end
|
|
|
|
function conditional_add_zone_to_list(forcedata, current, zone)
|
|
if global.debug_view_all_zones
|
|
or (zone.type == "spaceship" and forcedata.force_name == zone.force_name)
|
|
or (zone.type ~= "spaceship" and (forcedata.zones_discovered[zone.index] or (zone.type == "orbit" and forcedata.zones_discovered[zone.parent.index]))) then
|
|
local allowed_system = true
|
|
if star_restriction then
|
|
allowed_system = false
|
|
if zone == star_restriction
|
|
or Zone.get_star_from_child(zone) == star_restriction then
|
|
allowed_system = true
|
|
end
|
|
end
|
|
if allowed_system then
|
|
if zone.type == "star" then
|
|
table.insert(list, Zone.dropdown_name_from_zone(zone.orbit, alphabetical or filter))
|
|
table.insert(values, {type = "zone", index = zone.orbit.index})
|
|
if current and zone.orbit.index == current.index then selected_index = #list end
|
|
elseif zone.type == "spaceship" then
|
|
table.insert(list, Zone.dropdown_name_from_zone(zone, alphabetical or filter))
|
|
table.insert(values, {type = "spaceship", index = zone.index})
|
|
if current and zone.type == current.type and zone.index == current.index then selected_index = #list end
|
|
else
|
|
table.insert(list, Zone.dropdown_name_from_zone(zone, alphabetical or filter))
|
|
table.insert(values, {type = "zone", index = zone.index})
|
|
if current and zone.type == current.type and zone.index == current.index then selected_index = #list end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if alphabetical == true or filter then
|
|
for _, zone in pairs(Zone.get_alphabetised()) do
|
|
if not(zone.type == "orbit" and zone.parent.type == "star") then
|
|
if (not filter) or string.find(string.lower(zone.name), string.lower(filter), 1, true) then
|
|
conditional_add_zone_to_list(forcedata, current, zone)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
conditional_add_zone_to_list(forcedata, current, global.universe.anomaly)
|
|
|
|
for _, star in pairs(global.universe.stars) do
|
|
conditional_add_zone_to_list(forcedata, current, star)
|
|
for _, planet in pairs(star.children) do
|
|
conditional_add_zone_to_list(forcedata, current, planet)
|
|
if planet.children then
|
|
conditional_add_zone_to_list(forcedata, current, planet.orbit)
|
|
for _, moon in pairs(planet.children) do
|
|
conditional_add_zone_to_list(forcedata, current, moon)
|
|
conditional_add_zone_to_list(forcedata, current, moon.orbit)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, zone in pairs(global.universe.space_zones) do
|
|
conditional_add_zone_to_list(forcedata, current, zone)
|
|
end
|
|
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
conditional_add_zone_to_list(forcedata, current, spaceship)
|
|
end
|
|
end
|
|
|
|
if star_restriction then
|
|
if filter then
|
|
list[1] = (#list - 1) .. " matching locations in system"
|
|
else
|
|
list[1] = (#list - 1) .. " known locations in system"
|
|
end
|
|
else
|
|
if filter then
|
|
list[1] = (#list - 1) .. " matching locations"
|
|
else
|
|
list[1] = (#list - 1) .. " known locations"
|
|
end
|
|
end
|
|
|
|
return list, selected_index, values
|
|
end
|
|
|
|
function Zone.build_tile_replacements(zone)
|
|
-- replaces biome_collections in replace specifications with the full biome names
|
|
--[[
|
|
convert :
|
|
biome_replacement = {
|
|
{replace={"all-dirt", "all-sand", "all-volcanic"}, with="sand-red"},
|
|
{replace={"all-vegetation", "all-frozen"}, with="vegetation-red"}
|
|
}
|
|
to
|
|
{
|
|
["tile-from-1"] = "tile-to-1",
|
|
["tile-from-2"] = "tile-to-1",
|
|
}
|
|
]]--
|
|
if zone.biome_replacements then
|
|
|
|
-- expand replacement collections
|
|
local biome_replacements_expanded = {}
|
|
for _, replacement in pairs(zone.biome_replacements) do
|
|
local replace_biomes = {}
|
|
for _, replace in pairs(replacement.replace) do
|
|
if Zone.biome_collections[replace] then -- this is a collection name
|
|
for _, biome_name in pairs(Zone.biome_collections[replace]) do
|
|
if biome_name ~= replacement.with then -- don't replace to iteself
|
|
table.insert(replace_biomes, biome_name)
|
|
end
|
|
end
|
|
elseif replace ~= replacement.with then -- this is a biome name
|
|
table.insert(replace_biomes, replace)
|
|
end
|
|
end
|
|
table.insert(biome_replacements_expanded, {replace = replace_biomes, with = replacement.with})
|
|
end
|
|
-- biome_replacements_expanded now has all of the replace names being all biome names
|
|
-- build tile map
|
|
local tile_replacements = {}
|
|
for _, replacement in pairs(biome_replacements_expanded) do
|
|
local to_tiles = Zone.biome_tiles[replacement.with]
|
|
local i = 0
|
|
for _, replace in pairs(replacement.replace) do
|
|
for _, replace_tile in pairs(Zone.biome_tiles[replace]) do
|
|
tile_replacements[replace_tile] = to_tiles[(i % #to_tiles) + 1]
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
zone.tile_replacements = tile_replacements
|
|
end
|
|
|
|
end
|
|
|
|
function Zone.on_chunk_generated(event)
|
|
local area = event.area
|
|
local surface = event.surface
|
|
local zone = Zone.from_surface(surface)
|
|
if zone and Zone.is_solid(zone) then
|
|
if zone.biome_replacements then
|
|
if not zone.tile_replacements then
|
|
Zone.build_tile_replacements(zone)
|
|
end
|
|
local set_tiles = {} -- by tile name, array of positions, for the surface.set_tiles function
|
|
for x = area.left_top.x, area.right_bottom.x do
|
|
for y = area.left_top.y, area.right_bottom.y do
|
|
local tile = surface.get_tile(x, y)
|
|
if zone.tile_replacements[tile.name] then
|
|
table.insert(set_tiles, {
|
|
name = zone.tile_replacements[tile.name],
|
|
position = tile.position
|
|
})
|
|
end
|
|
end
|
|
end
|
|
if #set_tiles > 0 then
|
|
surface.set_tiles(set_tiles, true)
|
|
surface.destroy_decoratives{area = area}
|
|
surface.regenerate_decorative(nil, {{x = math.floor((area.left_top.x+16)/32), y = math.floor((area.left_top.y+16)/32)}})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_chunk_generated, Zone.on_chunk_generated)
|
|
|
|
function Zone.export_zone(zone)
|
|
if not zone then return end
|
|
-- not safe to deepcopy
|
|
-- make all object table references id references instead.
|
|
local export_zone = Util.shallow_copy(zone)
|
|
if export_zone.orbit then
|
|
export_zone.orbit_index = export_zone.orbit.index
|
|
export_zone.orbit = nil
|
|
end
|
|
if export_zone.parent then
|
|
export_zone.parent_index = export_zone.parent.index
|
|
export_zone.parent = nil
|
|
end
|
|
if export_zone.children then
|
|
export_zone.child_indexes = {}
|
|
for i, child in pairs(export_zone.children) do
|
|
export_zone.child_indexes[i] = child.index
|
|
end
|
|
export_zone.children = nil
|
|
end
|
|
-- should be safe to deepcopy now
|
|
export_zone = Util.deep_copy(export_zone)
|
|
return export_zone
|
|
end
|
|
|
|
function Zone.get_threat(zone)
|
|
if Zone.is_solid(zone) then
|
|
if zone.is_homeworld and zone.surface_index then
|
|
local surface = Zone.get_surface(zone)
|
|
local mapgen = surface.map_gen_settings
|
|
if mapgen.autoplace_controls["enemy-base"] and mapgen.autoplace_controls["enemy-base"].size then
|
|
return math.max(0, math.min(1, mapgen.autoplace_controls["enemy-base"].size / 3)) -- 0-1
|
|
end
|
|
end
|
|
if zone.controls and zone.controls["enemy-base"] and zone.controls["enemy-base"].size then
|
|
return math.max(0, math.min(1, zone.controls["enemy-base"].size / 3)) -- 0-1
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
function Zone.get_priority(zone, force_name)
|
|
if global.forces[force_name] then
|
|
if zone.type ~= "spaceship" and global.forces[force_name].zone_priorities and global.forces[force_name].zone_priorities[zone.index] then
|
|
return global.forces[force_name].zone_priorities[zone.index]
|
|
end
|
|
if zone.type == "spaceship" and global.forces[force_name].spaceship_priorities and global.forces[force_name].spaceship_priorities[zone.index] then
|
|
return global.forces[force_name].spaceship_priorities[zone.index]
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
function Zone.get_attrition(zone, default_rate)
|
|
if default_rate == nil then default_rate = settings.global["robot-attrition-factor"].value end
|
|
if zone then
|
|
if zone.type == "spaceship" then
|
|
return 0 -- no attrition
|
|
elseif zone.type == "anomaly" then -- anomalies are dangerous, give them an absolute increase
|
|
return 10 + default_rate * 2
|
|
elseif Zone.is_solid(zone) then -- planet or moon
|
|
if zone.name == "Nauvis" or zone.is_homeworld then
|
|
return default_rate
|
|
end
|
|
local enemy = 0
|
|
if zone.controls and zone.controls["enemy-base"] and zone.controls["enemy-base"].size then
|
|
enemy = Zone.get_threat(zone) -- 0-1
|
|
end
|
|
local rate = 0.5 * (1 - enemy * 0.9)
|
|
rate = rate + 0.5 * zone.radius / 10000
|
|
if enemy == 0 then -- add a penalty to enemy free zones
|
|
rate = rate + 10
|
|
end
|
|
return rate * (0.5 + 0.5 * default_rate)
|
|
else -- space
|
|
local star_gravity_well = Zone.get_star_gravity_well(zone)
|
|
local planet_gravity_well = Zone.get_planet_gravity_well(zone)
|
|
local base_rate = star_gravity_well / 20 + planet_gravity_well / 200
|
|
local rate = 10 * (0.01 + 0.99 * base_rate) -- 0-1
|
|
return rate * (0.5 + 0.5 * default_rate)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.rebuild_surface_index()
|
|
global.zones_by_surface = {}
|
|
for _, zone in pairs(global.zone_index) do
|
|
if zone.surface_index then
|
|
if game.surfaces[zone.surface_index] then
|
|
global.zones_by_surface[zone.surface_index] = zone
|
|
else
|
|
zone.surface_index = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.trim_surface(zone, player_index)
|
|
local surface = Zone.get_surface(zone)
|
|
if not surface then return end
|
|
|
|
local protected_forces = {
|
|
friendle = "friendly",
|
|
capture = "capture",
|
|
conquest = "conquest"
|
|
}
|
|
|
|
for force_name, forcedata in pairs(global.forces) do
|
|
if game.forces[force_name] and not is_system_force(force_name) then
|
|
protected_forces[force_name] = force_name
|
|
end
|
|
end
|
|
|
|
local protected_entities = table.deepcopy(Ancient.vault_entrance_structures)
|
|
table.insert(protected_entities, Ancient.name_gate_blocker)
|
|
table.insert(protected_entities, Ancient.name_gate_blocker_void)
|
|
for name, stuff in pairs(Ancient.gate_fragments) do
|
|
table.insert(protected_entities, name)
|
|
end
|
|
|
|
local min_x = 1000000
|
|
local max_x = -1000000
|
|
local min_y = 1000000
|
|
local max_y = -1000000
|
|
|
|
local entities = surface.find_entities_filtered{name = protected_entities}
|
|
if #entities > 0 then
|
|
for _, entity in pairs(entities) do
|
|
--local p = entity.position
|
|
local b = entity.bounding_box
|
|
min_x = min_x and math.min(min_x, b.left_top.x) or b.left_top.x
|
|
min_y = min_y and math.min(min_y, b.left_top.y) or b.left_top.y
|
|
max_x = max_x and math.max(max_x, b.right_bottom.x) or b.right_bottom.x
|
|
max_y = max_y and math.max(max_y, b.right_bottom.y) or b.right_bottom.y
|
|
end
|
|
end
|
|
|
|
|
|
for chunk in surface.get_chunks() do
|
|
if surface.count_entities_filtered{force = protected_forces, area = chunk.area} > 0 then
|
|
min_x = min_x and math.min(min_x, chunk.area.left_top.x) or chunk.area.left_top.x
|
|
min_y = min_y and math.min(min_y, chunk.area.left_top.y) or chunk.area.left_top.y
|
|
max_x = max_x and math.max(max_x, chunk.area.right_bottom.x) or chunk.area.right_bottom.x
|
|
max_y = max_y and math.max(max_y, chunk.area.right_bottom.y) or chunk.area.right_bottom.y
|
|
end
|
|
end
|
|
|
|
local chunk_min_x = math.floor(min_x/32) -1
|
|
local chunk_max_x = math.floor(max_x/32) +1
|
|
local chunk_min_y = math.ceil(min_y/32) -1
|
|
local chunk_max_y = math.ceil(max_y/32) +1
|
|
|
|
local chunks_deleted = 0
|
|
for chunk in surface.get_chunks() do
|
|
if chunk.x < chunk_min_x
|
|
or chunk.x > chunk_max_x
|
|
or chunk.y < chunk_min_y
|
|
or chunk.y > chunk_max_y then
|
|
chunks_deleted = chunks_deleted + 1
|
|
surface.delete_chunk(chunk)
|
|
end
|
|
end
|
|
rendering.draw_rectangle{
|
|
color = {0,0,1},
|
|
widht = 1,
|
|
filled = false,
|
|
left_top = {min_x, min_y},
|
|
right_bottom = {max_x, max_y},
|
|
surface = surface,
|
|
time_to_live = 60
|
|
}
|
|
rendering.draw_rectangle{
|
|
color = {1,0,0},
|
|
widht = 1,
|
|
filled = false,
|
|
left_top = {chunk_min_x*32, chunk_min_y*32},
|
|
right_bottom = {chunk_max_x*32, chunk_max_y*32},
|
|
surface = surface,
|
|
time_to_live = 60
|
|
}
|
|
game.print({"space-exploration.trim-zone-results", chunks_deleted, zone.name, chunk_min_x, chunk_max_x, chunk_min_y, chunk_max_y})
|
|
--game.print("Deleted "..chunks_deleted.." from "..zone.name ..". Trimmed X ["..chunk_min_x.." to "..chunk_max_x.."] Y ["..chunk_min_y.." to "..chunk_max_y.."]")
|
|
|
|
end
|
|
|
|
function Zone.delete_surface(zone, player_index)
|
|
local player
|
|
if player_index then
|
|
player = game.players[player_index]
|
|
end
|
|
if zone.surface_index == 1 then
|
|
if player then player.print("Game cannot delete surface 1.") end
|
|
return
|
|
end
|
|
if zone.is_homeworld then
|
|
if player then player.print("Cannot delete homeworlds.") end
|
|
return
|
|
end
|
|
if zone.type == "anomaly" then
|
|
if player then player.print("Cannot delete anomalies.") end
|
|
return
|
|
end
|
|
if zone.type == "spaceship" then
|
|
if player then player.print("Cannot delete spaceships. They must be landed and dismantled.") end
|
|
return
|
|
end
|
|
if not zone.surface_index then
|
|
if player then player.print("Zone has no surface.") end
|
|
return
|
|
end
|
|
local surface = Zone.get_surface(zone)
|
|
if not surface then
|
|
--error(Zone.name.." has a surface but it was missing.")
|
|
zone.surface_index = nil
|
|
Zone.rebuild_surface_index()
|
|
return
|
|
end
|
|
local force_names = {}
|
|
for force_name, force in pairs(global.forces) do
|
|
if force.has_players and game.forces[force_name] then
|
|
table.insert(force_names, force_name)
|
|
end
|
|
end
|
|
local count_entities = surface.count_entities_filtered{force=force_names, type={"character", "electric-pole", "container", "logistic-container"}}
|
|
if count_entities > 0 then
|
|
if player then player.print("Cannot delete a zone with player entities.") end
|
|
else
|
|
log("Deleting surface for zone: " .. zone.name)
|
|
-- find all players viewing that surface
|
|
for _, player in pairs(game.connected_players) do
|
|
if player.surface == surface then
|
|
local force = player.force
|
|
local home_zone
|
|
if global.forces[force.name] and global.forces[force.name].homeworld_index then
|
|
home_zone = Zone.from_zone_index(global.forces[force.name].homeworld_index)
|
|
end
|
|
if not home_zone then home_zone = Zone.from_name("Nauvis") end
|
|
player.teleport({0,0}, Zone.get_make_surface(home_zone))
|
|
end
|
|
end
|
|
game.delete_surface(surface)
|
|
zone.surface_index = nil
|
|
zone.deleted_surface = {tick = game.tick, player_index = player_index}
|
|
Zone.rebuild_surface_index()
|
|
end
|
|
end
|
|
|
|
|
|
function Zone.on_surface_created(surface_index)
|
|
local zone = Zone.from_surface_index(surface_index)
|
|
if not zone then
|
|
game.surfaces[surface_index].solar_power_multiplier = Zone.solar_multiplier * 0.5
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_surface_created, on_surface_created)
|
|
|
|
function Zone.on_research_finished(event)
|
|
|
|
local force = event.research.force
|
|
|
|
if event.research.name == Zone.name_tech_discover_random then
|
|
local dicovered_something = Zone.discover_next_research(force.name, "Telescope data analysis", false)
|
|
if not dicovered_something then
|
|
force.print({"space-exploration.tech-discovered-nothing"})
|
|
end
|
|
elseif event.research.name == Zone.name_tech_discover_targeted then
|
|
local dicovered_something = Zone.discover_next_research(force.name, "Telescope data analysis", true)
|
|
if not dicovered_something then
|
|
force.print({"space-exploration.tech-discovered-nothing"})
|
|
end
|
|
elseif event.research.name == Zone.name_tech_discover_deep then
|
|
local dicovered_something = Zone.discover_next_research_deep_space(force.name, "Telescope data analysis", false)
|
|
if not dicovered_something then
|
|
force.print({"space-exploration.tech-deep-discovered-nothing"})
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_research_finished, Zone.on_research_finished)
|
|
|
|
|
|
function Zone.zone_fix_all_tiles(zone) -- or spaceship
|
|
--/c p = game.player po = p.position for x = -5,5 do for y = -5,5 do p.surface.set_tiles{{name = "dirt-1", position = {x+po.x, y=y+po.y}}} end end
|
|
local surface = Zone.get_surface(zone)
|
|
if surface then
|
|
if Zone.is_space(zone) then
|
|
if zone.type ~= "spaceship" or zone.own_surface_index then
|
|
-- remove all tiles not allowd in space
|
|
local tiles = surface.find_tiles_filtered{
|
|
collision_mask = {"ground-tile", "water-tile"},
|
|
}
|
|
local set_tiles = {}
|
|
for _, tile in pairs(tiles) do
|
|
if not Util.table_contains(tiles_allowed_in_space, tile.name) then
|
|
-- remove the tile
|
|
table.insert(set_tiles, {name = name_space_tile, position = tile.position})
|
|
surface.set_hidden_tile(tile.position, nil)
|
|
end
|
|
end
|
|
|
|
if #set_tiles > 0 then
|
|
surface.set_tiles(
|
|
set_tiles,
|
|
true, -- corect tiles
|
|
true, -- remove_colliding_entities
|
|
true, -- remove_colliding_decoratives
|
|
true -- raise_event
|
|
)
|
|
end
|
|
|
|
log("Zone.zone_fix_all_tiles: " .. zone.type.." " .. zone.name.." is_space "..#set_tiles.." tiles changed surface_index "..surface.index.." surface_name " .. surface.name)
|
|
else
|
|
log("Zone.zone_fix_all_tiles: " .. zone.type.." " .. zone.name.." anchored surface_index "..surface.index.." surface_name " .. surface.name)
|
|
end
|
|
else
|
|
-- remove space tiles
|
|
local tiles = surface.find_tiles_filtered{
|
|
collision_mask = global.named_collision_masks.space_collision_layer
|
|
}
|
|
local set_tiles = {}
|
|
for _, tile in pairs(tiles) do
|
|
if not Util.table_contains(Spaceship.names_spaceship_floors, tile.name) then -- spacehip floor is allowed
|
|
-- remove the tile
|
|
table.insert(set_tiles, {name = "nuclear-ground", position = tile.position})
|
|
surface.set_hidden_tile(tile.position, nil)
|
|
end
|
|
end
|
|
|
|
if #set_tiles > 0 then
|
|
surface.set_tiles(
|
|
set_tiles,
|
|
true, -- corect tiles
|
|
true, -- remove_colliding_entities
|
|
true, -- remove_colliding_decoratives
|
|
true -- raise_event
|
|
)
|
|
end
|
|
|
|
log("Zone.zone_fix_all_tiles: " .. zone.type.." " ..zone.name.." is_land "..#set_tiles.." tiles changed surface_index "..surface.index.." surface_name " .. surface.name)
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
function Zone.zones_fix_all_tiles()
|
|
for _, zone in pairs(global.zone_index) do
|
|
Zone.zone_fix_all_tiles(zone)
|
|
end
|
|
if global.spaceships then
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
Zone.zone_fix_all_tiles(spaceship)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function Zone.set_zone_as_homeworld(data)
|
|
local zone = Zone.from_name(data.zone_name)
|
|
local nauvis = Zone.from_name("Nauvis")
|
|
if not zone then
|
|
game.print("No zone found")
|
|
else
|
|
if zone.type ~= "planet" then
|
|
game.print("Zone type must be planet, selected zone is: " .. zone.type)
|
|
return
|
|
else
|
|
zone.is_homeworld = true
|
|
zone.inflated = true
|
|
zone.resources = {}
|
|
zone.ticks_per_day = 25000
|
|
zone.fragment_name = "se-core-fragment-omni"
|
|
zone.radius = nauvis.radius
|
|
|
|
local nauvis_map_gen = table.deepcopy(game.surfaces[1].map_gen_settings)
|
|
|
|
if not zone.original_seed then
|
|
zone.original_seed = zone.seed
|
|
end
|
|
if data.match_nauvis_seed then
|
|
zone.seed = nauvis_map_gen.seed
|
|
else
|
|
nauvis_map_gen.seed = zone.seed
|
|
end
|
|
|
|
local surface = Zone.get_surface(zone)
|
|
if surface and reset_surface ~= false then
|
|
surface.map_gen_settings = nauvis_map_gen
|
|
surface.clear()
|
|
else
|
|
surface = Zone.get_make_surface(zone)
|
|
surface.map_gen_settings = nauvis_map_gen
|
|
end
|
|
|
|
Universe.make_validate_homesystem(zone)
|
|
global.resources_and_controls_compare_string = nil -- force udpate resources
|
|
Universe.load_resource_data()
|
|
|
|
surface.request_to_generate_chunks({0,0}, 4)
|
|
surface.force_generate_chunk_requests()
|
|
|
|
if settings.startup[mod_prefix.."spawn-small-resources"].value then
|
|
Zone.spawn_small_resources(surface)
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
return Zone.export_zone(Zone.from_name(data.zone_name))
|
|
end
|
|
|
|
function Zone.spawn_small_resources(surface)
|
|
|
|
local seed = surface.map_gen_settings.seed
|
|
local rng = game.create_random_generator(seed)
|
|
-- The starting resourecs of the map generation are inconsistent and spread out.
|
|
-- Add some tiny patches to reduce the amount of running around at the start.
|
|
-- We only care about super-early game, so just iron, copper, stone, and coal.
|
|
-- If there are other resources added to the game then the naturally spawned resources will have to do for now.
|
|
-- These resources are not designed to replace the normal starting resources at all.
|
|
local valid_position_search_range = 256
|
|
local cluster_primary_radius = 50 -- get away from crash site
|
|
local cluster_secondary_radius = 25
|
|
local resources = {}
|
|
if game.entity_prototypes["iron-ore"] then table.insert(resources, { name = "iron-ore", tiles = 200, amount = 100000}) end
|
|
if game.entity_prototypes["copper-ore"] then table.insert(resources, { name = "copper-ore", tiles = 150, amount = 80000}) end
|
|
if game.entity_prototypes["stone"] then table.insert(resources, { name = "stone", tiles = 150, amount = 80000}) end
|
|
if game.entity_prototypes["coal"] then table.insert(resources, { name = "coal", tiles = 150, amount = 80000}) end
|
|
|
|
local cluster_orientation = rng()
|
|
local secondary_orientation = rng()
|
|
local cluster_position = Util.orientation_to_vector(cluster_orientation, cluster_primary_radius)
|
|
surface.request_to_generate_chunks(cluster_position, 4)
|
|
surface.force_generate_chunk_requests()
|
|
|
|
Log.trace("[gps="..math.floor(cluster_position.x)..","..math.floor(cluster_position.y).."]")
|
|
local closed_tiles = {} -- 2d disctionary
|
|
local open_tiles = {} -- 1d array
|
|
local function close_tile(position)
|
|
closed_tiles[position.x] = closed_tiles[position.x] or {}
|
|
closed_tiles[position.x][position.y] = true
|
|
end
|
|
local function open_tile(set, position) -- don't open if closed
|
|
if not (closed_tiles[position.x] and closed_tiles[position.x][position.y]) then
|
|
table.insert(set, position)
|
|
close_tile(position)
|
|
end
|
|
end
|
|
local function open_neighbour_tiles(set, position)
|
|
open_tile(set, Util.vectors_add(position, {x=0,y=-1}))
|
|
open_tile(set, Util.vectors_add(position, {x=1,y=0}))
|
|
open_tile(set, Util.vectors_add(position, {x=0,y=1}))
|
|
open_tile(set, Util.vectors_add(position, {x=-1,y=0}))
|
|
end
|
|
for i, resource in pairs(resources) do
|
|
resource.orientation = secondary_orientation + rng()
|
|
local offset = Util.orientation_to_vector(resource.orientation, rng(cluster_secondary_radius/2, cluster_secondary_radius))
|
|
local position = Util.tile_to_position(Util.vectors_add(offset, cluster_position))
|
|
local valid = surface.find_non_colliding_position(resource.name, position, valid_position_search_range, 1, true)
|
|
if not valid then Log.trace("no valid position found") end
|
|
resource.start_point = surface.find_non_colliding_position(resource.name, position, valid_position_search_range, 1, true) or position
|
|
resource.open_tiles = {resource.start_point}
|
|
resource.entities = {}
|
|
resource.amount_placed = 0
|
|
end
|
|
local continue = true
|
|
local repeats = 0
|
|
while continue and repeats < 1000 do
|
|
repeats = repeats + 1
|
|
continue = false
|
|
for _, resource in pairs(resources) do
|
|
--if #resource.entities < resource.tiles then
|
|
if resource.amount_placed < resource.amount then
|
|
continue = true
|
|
local try_tile
|
|
if #resource.open_tiles > 0 then
|
|
local choose = rng(#resource.open_tiles)
|
|
try_tile = resource.open_tiles[choose]
|
|
close_tile(try_tile)
|
|
end
|
|
if not try_tile then -- handle tiny island case
|
|
try_tile = resource.start_point
|
|
end
|
|
local position = surface.find_non_colliding_position(resource.name, try_tile, valid_position_search_range, 1, true)
|
|
if not position then -- exit
|
|
resource.amount_placed = resource.amount
|
|
log("Space Exploration failed to place starting resource, no valid positions in range. [".. resource.name.."]")
|
|
else
|
|
close_tile(try_tile)
|
|
close_tile(position)
|
|
local remaining = resource.amount - resource.amount_placed
|
|
local amount = math.ceil(math.min( remaining * (0.01 + rng() * 0.005) + 100 + rng() * 100, remaining))
|
|
resource.amount_placed = resource.amount_placed + amount
|
|
table.insert(resource.entities, surface.create_entity{name = resource.name, position=position, amount=amount, enable_tree_removal=true, snap_to_tile_center =true})
|
|
--Log.trace("Starting resource entity created "..resource.name.." ".. position.x.." "..position.y)
|
|
open_neighbour_tiles(resource.open_tiles, position)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
return Zone
|
|
|