Исправлен мод heroturrets
Добавлен мод blueprint-sandboxes Добавляет метку версии в zzzparanoidal (#72)
This commit is contained in:
7
blueprint-sandboxes/scripts/bpsb.lua
Normal file
7
blueprint-sandboxes/scripts/bpsb.lua
Normal file
@@ -0,0 +1,7 @@
|
||||
local BPSB = {}
|
||||
|
||||
BPSB.name = "blueprint-sandboxes"
|
||||
BPSB.path = "__" .. BPSB.name .. "__"
|
||||
BPSB.pfx = "bpsb-"
|
||||
|
||||
return BPSB
|
||||
22
blueprint-sandboxes/scripts/chat.lua
Normal file
22
blueprint-sandboxes/scripts/chat.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
-- Chat helpers to proxy messages between Sandboxes and the normal Surfaces
|
||||
local Chat = {}
|
||||
|
||||
-- Proxy Chats between Sandbox Force <-> Original Force
|
||||
function Chat.OnChat(event)
|
||||
if event.player_index == nil then
|
||||
return
|
||||
end
|
||||
local player = game.players[event.player_index]
|
||||
local playerData = global.players[event.player_index]
|
||||
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
game.forces[playerData.forceName].print(player.name .. ": " .. event.message, player.chat_color)
|
||||
else
|
||||
local sandboxForce = game.forces[playerData.sandboxForceName]
|
||||
if sandboxForce ~= nil then
|
||||
sandboxForce.print(player.name .. ": " .. event.message, player.chat_color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Chat
|
||||
34
blueprint-sandboxes/scripts/debug.lua
Normal file
34
blueprint-sandboxes/scripts/debug.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
local Debug = {}
|
||||
|
||||
function Debug.ItemStack(value)
|
||||
data = {}
|
||||
if (value == nil) then
|
||||
data["nil"] = true
|
||||
return data
|
||||
end
|
||||
|
||||
data["nil"] = false
|
||||
data["valid"] = value.valid
|
||||
data["valid_for_read"] = value.valid_for_read
|
||||
if not value.valid_for_read then
|
||||
return data
|
||||
end
|
||||
|
||||
data["is_blueprint"] = value.is_blueprint
|
||||
if value.is_blueprint then
|
||||
data["is_blueprint_setup"] = value.is_blueprint_setup()
|
||||
end
|
||||
|
||||
if value.is_blueprint then
|
||||
local entities = value.get_blueprint_entities()
|
||||
if entities then
|
||||
data["entities"] = #entities
|
||||
else
|
||||
data["entities"] = "nil"
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
return Debug
|
||||
15
blueprint-sandboxes/scripts/editor-extensions-cheats.lua
Normal file
15
blueprint-sandboxes/scripts/editor-extensions-cheats.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
-- EditorExtensionsCheats related functionality
|
||||
local EditorExtensionsCheats = {}
|
||||
|
||||
EditorExtensionsCheats.name = "ee_cheat_mode"
|
||||
EditorExtensionsCheats.enabled = not not remote.interfaces[EditorExtensionsCheats.name]
|
||||
|
||||
-- Enables EE's Recipes for a Force
|
||||
function EditorExtensionsCheats.EnableTestingRecipes(force)
|
||||
if not EditorExtensionsCheats.enabled then
|
||||
return false
|
||||
end
|
||||
return remote.call(EditorExtensionsCheats.name, "enable_testing_recipes", force)
|
||||
end
|
||||
|
||||
return EditorExtensionsCheats
|
||||
143
blueprint-sandboxes/scripts/equipment.lua
Normal file
143
blueprint-sandboxes/scripts/equipment.lua
Normal file
@@ -0,0 +1,143 @@
|
||||
-- Equipment-related methods
|
||||
local Equipment = {}
|
||||
|
||||
-- Initializes an Inventory for the default equipment Blueprint(s)
|
||||
function Equipment.Init(default)
|
||||
local equipment = game.create_inventory(1)
|
||||
Equipment.Set(equipment, default)
|
||||
return equipment
|
||||
end
|
||||
|
||||
-- Updates the default equipment Blueprint(s)
|
||||
function Equipment.Set(equipment, default)
|
||||
equipment[1].import_stack(default)
|
||||
end
|
||||
|
||||
--[[
|
||||
Before 1.1.87, there was a bug that did not correctly forcefully generate
|
||||
chunks for surfaces with the Lab Tiles setting, which required us to
|
||||
fix those tiles after generation but before placing the Blueprint.
|
||||
This but was fixed in 1.1.87, however, it introduced another bug where
|
||||
building a blueprint then did not immediately work on those tiles,
|
||||
but building entities seemed to. So, the workaround is to delay the blueprint
|
||||
building, so we do some work when the surface is generated, then the rest
|
||||
as soon as possible (aligning to generated chunks seems faster (in ticks)
|
||||
than waiting any number of specific ticks).
|
||||
]]
|
||||
function Equipment.Place(stack, surface, forceName)
|
||||
if stack.is_blueprint then
|
||||
log("Beginning Equipment Placement")
|
||||
Equipment.Prepare(stack, surface)
|
||||
|
||||
global.equipmentInProgress[surface.name] = {
|
||||
stack = stack,
|
||||
surface = surface,
|
||||
forceName = forceName,
|
||||
retries = 100,
|
||||
}
|
||||
Equipment.BuildBlueprint(stack, surface, forceName)
|
||||
else
|
||||
global.equipmentInProgress[surface.name] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Prepares a Surface for an Equipment Blueprint
|
||||
function Equipment.Prepare(stack, surface)
|
||||
-- We need to know how many Chunks must be generated to fit this Blueprint
|
||||
local radius = 0
|
||||
local function updateRadius(thing)
|
||||
local x = math.abs(thing.position.x)
|
||||
local y = math.abs(thing.position.y)
|
||||
radius = math.max(radius, x, y)
|
||||
end
|
||||
local entities = stack.get_blueprint_entities()
|
||||
local tiles = stack.get_blueprint_tiles()
|
||||
if entities then
|
||||
for _, thing in pairs(entities) do
|
||||
updateRadius(thing)
|
||||
end
|
||||
end
|
||||
if tiles then
|
||||
for _, thing in pairs(tiles) do
|
||||
updateRadius(thing)
|
||||
end
|
||||
end
|
||||
|
||||
-- Then, we can forcefully generate the necessary Chunks
|
||||
local chunkRadius = 1 + math.ceil(radius / 32)
|
||||
log("Requesting Chunks for Blueprint Placement: " .. chunkRadius)
|
||||
surface.request_to_generate_chunks({ x = 0, y = 0 }, chunkRadius)
|
||||
surface.force_generate_chunk_requests()
|
||||
log("Chunks allegedly generated")
|
||||
end
|
||||
|
||||
-- Applies an Equipment Blueprint to a Surface
|
||||
function Equipment.IsReadyForBlueprint(stack, surface)
|
||||
local entities = stack.get_blueprint_entities()
|
||||
local tiles = stack.get_blueprint_tiles()
|
||||
local function is_chunk_generated(thing)
|
||||
return surface.is_chunk_generated({
|
||||
thing.position.x / 32,
|
||||
thing.position.y / 32,
|
||||
})
|
||||
end
|
||||
if entities then
|
||||
for _, thing in pairs(entities) do
|
||||
if not is_chunk_generated(thing) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
if tiles then
|
||||
for _, thing in pairs(tiles) do
|
||||
if not is_chunk_generated(thing) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Applies an Equipment Blueprint to a Surface
|
||||
function Equipment.BuildBlueprint(stack, surface, forceName)
|
||||
local equipmentData = global.equipmentInProgress[surface.name]
|
||||
|
||||
-- First, let's check if the Chunks are ready for us
|
||||
if not Equipment.IsReadyForBlueprint(stack, surface) then
|
||||
equipmentData.retries = equipmentData.retries - 1
|
||||
return false
|
||||
end
|
||||
|
||||
-- Then, place the Tiles ourselves since it might prevent placing the Blueprint
|
||||
local tiles = stack.get_blueprint_tiles()
|
||||
if tiles then
|
||||
surface.set_tiles(tiles, true, true, true, true)
|
||||
end
|
||||
|
||||
-- Finally, we can place the Blueprint
|
||||
local ghosts = stack.build_blueprint({
|
||||
surface = surface.name,
|
||||
force = forceName,
|
||||
position = { 0, 0 },
|
||||
skip_fog_of_war = true,
|
||||
raise_built = true,
|
||||
})
|
||||
|
||||
-- But that may have not been successful, despite our attempts to ensure it!
|
||||
if #ghosts > 0 then
|
||||
log("Some ghosts created, ending repeated attempts; assuming Blueprint is placed")
|
||||
global.equipmentInProgress[surface.name] = nil
|
||||
return true
|
||||
elseif equipmentData.retries <= 0 then
|
||||
log("No ghosts created, but we've exceeded retry limit, ending repeated attempts")
|
||||
surface.print("Failed to place Equipment Blueprint after too many retries")
|
||||
global.equipmentInProgress[surface.name] = nil
|
||||
return false
|
||||
else
|
||||
equipmentData.retries = equipmentData.retries - 1
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return Equipment
|
||||
13
blueprint-sandboxes/scripts/events.lua
Normal file
13
blueprint-sandboxes/scripts/events.lua
Normal file
@@ -0,0 +1,13 @@
|
||||
local Events = {}
|
||||
|
||||
Events.on_daylight_changed_event = script.generate_event_name()
|
||||
|
||||
function Events.SendDaylightChangedEvent(player_index, surface_name, daytime)
|
||||
script.raise_event(Events.on_daylight_changed_event, {
|
||||
surface_name = surface_name,
|
||||
player_index = player_index,
|
||||
daytime = daytime,
|
||||
})
|
||||
end
|
||||
|
||||
return Events
|
||||
68
blueprint-sandboxes/scripts/factorissimo.lua
Normal file
68
blueprint-sandboxes/scripts/factorissimo.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
-- Factorissimo related functionality
|
||||
local Factorissimo = {}
|
||||
|
||||
Factorissimo.name = "factorissimo"
|
||||
Factorissimo.enabled = not not remote.interfaces[Factorissimo.name]
|
||||
|
||||
Factorissimo.surfacePfx = "factory-floor-"
|
||||
local surfacePfxLength = string.len(Factorissimo.surfacePfx)
|
||||
|
||||
function Factorissimo.GetAllFactories()
|
||||
if Factorissimo.enabled then
|
||||
return remote.call(Factorissimo.name, "get_global", { "factories" })
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Whether the Surface is a Factory
|
||||
function Factorissimo.IsFactory(thingWithName)
|
||||
if not Factorissimo.enabled then
|
||||
return false
|
||||
end
|
||||
|
||||
return string.sub(thingWithName.name, 1, surfacePfxLength) == Factorissimo.surfacePfx
|
||||
end
|
||||
|
||||
-- Whether the Surface is a Factory inside of a Sandbox
|
||||
function Factorissimo.IsFactoryInsideSandbox(surface, position)
|
||||
if not Factorissimo.enabled then
|
||||
return false
|
||||
end
|
||||
|
||||
local factory = Factorissimo.GetFactory(surface, position)
|
||||
if not factory then
|
||||
return false
|
||||
end
|
||||
|
||||
return Sandbox.IsSandboxForce(factory.force)
|
||||
end
|
||||
|
||||
-- Find a Factory given a Surface and Position (if possible)
|
||||
function Factorissimo.GetFactory(surface, position)
|
||||
return remote.call(Factorissimo.name, "find_surrounding_factory", surface, position)
|
||||
end
|
||||
|
||||
-- Find a Factory's Outside Surface recursively
|
||||
function Factorissimo.GetOutsideSurfaceForFactory(surface, position)
|
||||
if not Factorissimo.IsFactory(surface) then
|
||||
return nil
|
||||
end
|
||||
|
||||
local factory = Factorissimo.GetFactory(surface, position)
|
||||
if not factory then
|
||||
return nil
|
||||
end
|
||||
|
||||
if Factorissimo.IsFactory(factory.outside_surface) then
|
||||
return Factorissimo.GetOutsideSurfaceForFactory(factory.outside_surface, {
|
||||
x = factory.outside_door_x,
|
||||
y = factory.outside_door_y,
|
||||
})
|
||||
else
|
||||
return factory.outside_surface
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return Factorissimo
|
||||
149
blueprint-sandboxes/scripts/force.lua
Normal file
149
blueprint-sandboxes/scripts/force.lua
Normal file
@@ -0,0 +1,149 @@
|
||||
-- Managing Forces and their Sandbox Forces
|
||||
local Force = {}
|
||||
|
||||
-- Properties from the original Force that are synced to the Sandbox Force (in not-all-tech mode)
|
||||
Force.syncedProperties = {
|
||||
-- "manual_mining_speed_modifier", Forcibly set
|
||||
"manual_crafting_speed_modifier",
|
||||
-- "laboratory_speed_modifier", Forcibly set
|
||||
"laboratory_productivity_bonus",
|
||||
"worker_robots_speed_modifier",
|
||||
"worker_robots_battery_modifier",
|
||||
"worker_robots_storage_bonus",
|
||||
"inserter_stack_size_bonus",
|
||||
"stack_inserter_capacity_bonus",
|
||||
"character_trash_slot_count",
|
||||
"maximum_following_robot_count",
|
||||
"following_robots_lifetime_modifier",
|
||||
"character_running_speed_modifier",
|
||||
"artillery_range_modifier",
|
||||
"character_build_distance_bonus",
|
||||
"character_item_drop_distance_bonus",
|
||||
"character_reach_distance_bonus",
|
||||
"character_resource_reach_distance_bonus",
|
||||
"character_item_pickup_distance_bonus",
|
||||
"character_loot_pickup_distance_bonus",
|
||||
-- "character_inventory_slots_bonus", Set with a bonus
|
||||
"character_health_bonus",
|
||||
"mining_drill_productivity_bonus",
|
||||
"train_braking_force_bonus",
|
||||
}
|
||||
|
||||
-- Setup Force, if necessary
|
||||
function Force.Init(force)
|
||||
if global.forces[force.name]
|
||||
or Sandbox.IsSandboxForce(force)
|
||||
or #force.players < 1
|
||||
then
|
||||
log("Skip Force.Init: " .. force.name)
|
||||
return
|
||||
end
|
||||
|
||||
log("Force.Init: " .. force.name)
|
||||
local forceLabName = Lab.NameFromForce(force)
|
||||
local sandboxForceName = Sandbox.NameFromForce(force)
|
||||
global.forces[force.name] = {
|
||||
sandboxForceName = sandboxForceName,
|
||||
}
|
||||
global.sandboxForces[sandboxForceName] = {
|
||||
forceName = force.name,
|
||||
hiddenItemsUnlocked = false,
|
||||
labName = forceLabName,
|
||||
sePlanetaryLabZoneName = nil,
|
||||
seOrbitalSandboxZoneName = nil,
|
||||
}
|
||||
end
|
||||
|
||||
-- Delete Force's information, if necessary
|
||||
function Force.Merge(oldForceName, newForce)
|
||||
-- Double-check we know about this Force
|
||||
local oldForceData = global.forces[oldForceName]
|
||||
local newForceData = global.forces[newForce.name]
|
||||
if not oldForceData or not newForceData then
|
||||
log("Skip Force.Merge: " .. oldForceName .. " -> " .. newForce.name)
|
||||
return
|
||||
end
|
||||
local sandboxForceName = oldForceData.sandboxForceName
|
||||
local oldSandboxForceData = global.sandboxForces[sandboxForceName]
|
||||
local oldSandboxForce = game.forces[sandboxForceName]
|
||||
|
||||
-- Bounce any Players currently using the older Sandboxes
|
||||
if oldSandboxForce then
|
||||
for _, player in pairs(oldSandboxForce.players) do
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
log("Force.Merge must manually change Sandbox Player's Force: " .. player.name .. " -> " .. newForce.name)
|
||||
player.force = newForce
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete the old Force-related Surfaces/Forces
|
||||
Lab.DeleteLab(oldSandboxForceData.labName)
|
||||
SpaceExploration.DeleteSandbox(oldSandboxForceData, oldSandboxForceData.sePlanetaryLabZoneName)
|
||||
SpaceExploration.DeleteSandbox(oldSandboxForceData, oldSandboxForceData.seOrbitalSandboxZoneName)
|
||||
if oldSandboxForce then
|
||||
log("Force.Merge must merge Sandbox Forces: " .. oldSandboxForce.name .. " -> " .. newForceData.sandboxForceName)
|
||||
game.merge_forces(oldSandboxForce, newForceData.sandboxForceName)
|
||||
end
|
||||
|
||||
-- Delete the old Force's data
|
||||
global.forces[oldForceName] = nil
|
||||
global.sandboxForces[sandboxForceName] = nil
|
||||
end
|
||||
|
||||
-- Configure Sandbox Force
|
||||
function Force.ConfigureSandboxForce(force, sandboxForce)
|
||||
-- Ensure the two Forces don't attack each other
|
||||
force.set_cease_fire(sandboxForce, true)
|
||||
sandboxForce.set_cease_fire(force, true)
|
||||
|
||||
-- Sync a few properties just in case, but only if they should be linked
|
||||
if not settings.global[Settings.allowAllTech].value then
|
||||
for _, property in pairs(Force.syncedProperties) do
|
||||
sandboxForce[property] = force[property]
|
||||
end
|
||||
end
|
||||
|
||||
-- Counteract Space Exploration's slow Mining Speed for Gods
|
||||
sandboxForce.manual_mining_speed_modifier = settings.global[Settings.extraMiningSpeed].value
|
||||
|
||||
-- Make research faster/slower based on play-style
|
||||
sandboxForce.laboratory_speed_modifier = settings.global[Settings.extraLabSpeed].value
|
||||
|
||||
-- You should have a little more space too
|
||||
sandboxForce.character_inventory_slots_bonus =
|
||||
force.character_inventory_slots_bonus
|
||||
+ settings.global[Settings.bonusInventorySlots].value
|
||||
|
||||
return sandboxForce
|
||||
end
|
||||
|
||||
-- Create Sandbox Force, if necessary
|
||||
function Force.GetOrCreateSandboxForce(force)
|
||||
local sandboxForceName = global.forces[force.name].sandboxForceName
|
||||
local sandboxForce = game.forces[sandboxForceName]
|
||||
if sandboxForce then
|
||||
Force.ConfigureSandboxForce(force, sandboxForce)
|
||||
return sandboxForce
|
||||
end
|
||||
|
||||
log("Creating Sandbox Force: " .. sandboxForceName)
|
||||
sandboxForce = game.create_force(sandboxForceName)
|
||||
Force.ConfigureSandboxForce(force, sandboxForce)
|
||||
Research.Sync(force, sandboxForce)
|
||||
return sandboxForce
|
||||
end
|
||||
|
||||
-- For all Forces with Sandboxes, Configure them again
|
||||
function Force.SyncAllForces()
|
||||
for _, force in pairs(game.forces) do
|
||||
if not Sandbox.IsSandboxForce(force) then
|
||||
local sandboxForce = game.forces[Sandbox.NameFromForce(force)]
|
||||
if sandboxForce then
|
||||
Force.ConfigureSandboxForce(force, sandboxForce)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Force
|
||||
301
blueprint-sandboxes/scripts/god.lua
Normal file
301
blueprint-sandboxes/scripts/god.lua
Normal file
@@ -0,0 +1,301 @@
|
||||
-- Custom Extensions to the God-Controller
|
||||
local God = {}
|
||||
|
||||
God.onBuiltEntityFilters = {
|
||||
{ filter = "type", type = "tile-ghost" },
|
||||
{ filter = "type", type = "entity-ghost" },
|
||||
{ filter = "type", type = "item-request-proxy" },
|
||||
}
|
||||
|
||||
for realEntityName, illusionName in pairs(Illusion.realToIllusionMap) do
|
||||
table.insert(
|
||||
God.onBuiltEntityFilters,
|
||||
{ filter = "name", name = realEntityName }
|
||||
)
|
||||
end
|
||||
|
||||
-- TODO: Perhaps this can be determined by flags?
|
||||
God.skipHandlingEntities = {
|
||||
["logistic-train-stop-input"] = true,
|
||||
["logistic-train-stop-output"] = true,
|
||||
["tl-dummy-entity"] = true,
|
||||
["si-in-world-drop-entity"] = true,
|
||||
["si-in-world-pickup-entity"] = true,
|
||||
}
|
||||
|
||||
-- Immediately destroy an Entity (and perhaps related Entities)
|
||||
function God.Destroy(entity)
|
||||
if entity.valid
|
||||
and entity.can_be_destroyed()
|
||||
and entity.to_be_deconstructed()
|
||||
then
|
||||
|
||||
-- If the Entity has Transport Lines, also delete any Items on it
|
||||
if entity.prototype.belt_speed ~= nil then
|
||||
for i = 1, entity.get_max_transport_line_index() do
|
||||
entity.get_transport_line(i).clear()
|
||||
end
|
||||
end
|
||||
|
||||
-- If the Entity represents a Hidden Tile underneath
|
||||
if entity.type == "deconstructible-tile-proxy" then
|
||||
local hiddenTile = entity.surface.get_hidden_tile(entity.position)
|
||||
entity.surface.set_tiles {
|
||||
{
|
||||
name = hiddenTile,
|
||||
position = entity.position,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
entity.destroy({ raise_destroy = true })
|
||||
end
|
||||
end
|
||||
|
||||
-- Immediately Insert an Entity's Requests
|
||||
function God.InsertRequests(entity)
|
||||
if entity.valid
|
||||
and entity.type == "item-request-proxy"
|
||||
and entity.proxy_target then
|
||||
-- Insert any Requested Items (like Modules, Fuel)
|
||||
for name, count in pairs(entity.item_requests) do
|
||||
entity.proxy_target.insert({
|
||||
name = name,
|
||||
count = count,
|
||||
})
|
||||
end
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- Immediately Revive a Ghost Entity
|
||||
function God.Create(entity)
|
||||
Illusion.ReplaceIfNecessary(entity)
|
||||
if entity.valid then
|
||||
if entity.type == "tile-ghost" then
|
||||
-- Tiles are simple Revives
|
||||
entity.silent_revive({ raise_revive = true })
|
||||
elseif entity.type == "item-request-proxy" then
|
||||
-- Requests are simple
|
||||
God.InsertRequests(entity)
|
||||
elseif entity.type == "entity-ghost" then
|
||||
-- Entities might also want Items after Reviving
|
||||
local _, revived, request = entity.silent_revive({
|
||||
return_item_request_proxy = true,
|
||||
raise_revive = true
|
||||
})
|
||||
|
||||
if revived and request then
|
||||
God.InsertRequests(request)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Immediately turn one Entity into another
|
||||
function God.Upgrade(entity)
|
||||
if entity.valid
|
||||
and entity.to_be_upgraded()
|
||||
then
|
||||
local target = entity.get_upgrade_target()
|
||||
local direction = entity.get_upgrade_direction()
|
||||
|
||||
if Illusion.IsIllusion(entity.name) and
|
||||
Illusion.GetActualName(entity.name) == target.name
|
||||
then
|
||||
log("Cancelling an Upgrade from an Illusion to its Real Entity: " .. entity.name)
|
||||
entity.cancel_upgrade(entity.force)
|
||||
return
|
||||
end
|
||||
|
||||
local options = {
|
||||
name = target.name,
|
||||
position = entity.position,
|
||||
direction = direction or entity.direction,
|
||||
force = entity.force,
|
||||
fast_replace = true,
|
||||
spill = false,
|
||||
raise_built = true,
|
||||
}
|
||||
|
||||
-- Otherwise it fails to place "output" sides (it defaults to "input")
|
||||
if entity.type == "underground-belt" then
|
||||
options.type = entity.belt_to_ground_type
|
||||
end
|
||||
|
||||
local result = entity.surface.create_entity(options)
|
||||
|
||||
if result == nil and entity.valid then
|
||||
log("Upgrade Failed, Cancelling: " .. entity.name)
|
||||
entity.cancel_upgrade(entity.force)
|
||||
else
|
||||
log("Upgrade Failed, Old Entity Gone too!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure the God's Inventory is kept in-sync
|
||||
function God.OnInventoryChanged(event)
|
||||
local player = game.players[event.player_index]
|
||||
local playerData = global.players[event.player_index]
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
Inventory.Prune(player)
|
||||
playerData.sandboxInventory = Inventory.Persist(
|
||||
player.get_main_inventory(),
|
||||
playerData.sandboxInventory
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure newly-crafted Items are put into the Cursor for use
|
||||
function God.OnPlayerCraftedItem(event)
|
||||
local player = game.players[event.player_index]
|
||||
if Sandbox.IsPlayerInsideSandbox(player)
|
||||
and player.cursor_stack
|
||||
and player.cursor_stack.valid
|
||||
and event.item_stack.valid
|
||||
and event.item_stack.valid_for_read
|
||||
and event.recipe.valid
|
||||
and (
|
||||
#event.recipe.products == 1
|
||||
or (
|
||||
event.recipe.prototype.main_product
|
||||
and event.recipe.prototype.main_product.name == event.item_stack.name
|
||||
)
|
||||
)
|
||||
and player.mod_settings[Settings.craftToCursor].value
|
||||
then
|
||||
event.item_stack.count = event.item_stack.prototype.stack_size
|
||||
player.cursor_stack.clear()
|
||||
player.cursor_stack.transfer_stack(event.item_stack)
|
||||
end
|
||||
end
|
||||
|
||||
function God.AsyncWrapper(setting, queue, handler, entity)
|
||||
if settings.global[setting].value == 0 then
|
||||
handler(entity)
|
||||
else
|
||||
Queue.Push(queue, entity)
|
||||
end
|
||||
end
|
||||
|
||||
function God.ShouldHandleEntity(entity)
|
||||
if entity.force.name ~= "neutral"
|
||||
and not Sandbox.IsSandboxForce(entity.force) then
|
||||
return false
|
||||
end
|
||||
|
||||
local name = Illusion.GhostOrRealName(entity)
|
||||
if God.skipHandlingEntities[name] then
|
||||
return false
|
||||
end
|
||||
|
||||
return Lab.IsLab(entity.surface)
|
||||
or SpaceExploration.IsSandbox(entity.surface)
|
||||
or (Factorissimo.IsFactory(entity.surface)
|
||||
and Factorissimo.IsFactoryInsideSandbox(entity.surface, entity.position))
|
||||
end
|
||||
|
||||
-- Ensure new Orders are handled
|
||||
function God.OnMarkedForDeconstruct(event)
|
||||
-- log("Entity Deconstructing: " .. event.entity.unit_number .. " " .. event.entity.type)
|
||||
if God.ShouldHandleEntity(event.entity) then
|
||||
God.AsyncWrapper(
|
||||
Settings.godAsyncDeleteRequestsPerTick,
|
||||
global.asyncDestroyQueue,
|
||||
God.Destroy,
|
||||
event.entity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure new Orders are handled
|
||||
function God.OnMarkedForUpgrade(event)
|
||||
-- log("Entity Upgrading: " .. event.entity.unit_number .. " " .. event.entity.type)
|
||||
if God.ShouldHandleEntity(event.entity) then
|
||||
God.AsyncWrapper(
|
||||
Settings.godAsyncUpgradeRequestsPerTick,
|
||||
global.asyncUpgradeQueue,
|
||||
God.Upgrade,
|
||||
event.entity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure new Ghosts are handled
|
||||
function God.OnBuiltEntity(entity)
|
||||
-- log("Entity Creating: " .. entity.unit_number .. " " .. entity.type)
|
||||
if God.ShouldHandleEntity(entity) then
|
||||
God.AsyncWrapper(
|
||||
Settings.godAsyncCreateRequestsPerTick,
|
||||
global.asyncCreateQueue,
|
||||
God.Create,
|
||||
entity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- For each known Sandbox Surface, handle any async God functionality
|
||||
function God.HandleAllSandboxRequests(event)
|
||||
local createRequestsPerTick = settings.global[Settings.godAsyncCreateRequestsPerTick].value
|
||||
local upgradeRequestsPerTick = settings.global[Settings.godAsyncUpgradeRequestsPerTick].value
|
||||
local deleteRequestsPerTick = settings.global[Settings.godAsyncDeleteRequestsPerTick].value
|
||||
|
||||
local destroyRequestsHandled = 0
|
||||
while Queue.Size(global.asyncDestroyQueue) > 0
|
||||
and deleteRequestsPerTick > 0
|
||||
do
|
||||
God.Destroy(Queue.Pop(global.asyncDestroyQueue))
|
||||
destroyRequestsHandled = destroyRequestsHandled + 1
|
||||
deleteRequestsPerTick = deleteRequestsPerTick - 1
|
||||
end
|
||||
if Queue.Size(global.asyncDestroyQueue) == 0
|
||||
and destroyRequestsHandled > 0
|
||||
then
|
||||
global.asyncDestroyQueue = Queue.New()
|
||||
end
|
||||
|
||||
local upgradeRequestsHandled = 0
|
||||
while Queue.Size(global.asyncUpgradeQueue) > 0
|
||||
and upgradeRequestsPerTick > 0
|
||||
do
|
||||
God.Upgrade(Queue.Pop(global.asyncUpgradeQueue))
|
||||
upgradeRequestsHandled = upgradeRequestsHandled + 1
|
||||
upgradeRequestsPerTick = upgradeRequestsPerTick - 1
|
||||
end
|
||||
if Queue.Size(global.asyncUpgradeQueue) == 0
|
||||
and upgradeRequestsHandled > 0
|
||||
then
|
||||
global.asyncUpgradeQueue = Queue.New()
|
||||
end
|
||||
|
||||
local createRequestsHandled = 0
|
||||
while Queue.Size(global.asyncCreateQueue) > 0
|
||||
and createRequestsPerTick > 0
|
||||
do
|
||||
God.Create(Queue.Pop(global.asyncCreateQueue))
|
||||
createRequestsHandled = createRequestsHandled + 1
|
||||
createRequestsPerTick = createRequestsPerTick - 1
|
||||
end
|
||||
if Queue.Size(global.asyncCreateQueue) == 0
|
||||
and createRequestsHandled > 0
|
||||
then
|
||||
global.asyncCreateQueue = Queue.New()
|
||||
end
|
||||
end
|
||||
|
||||
-- Charts each Sandbox that a Player is currently inside of
|
||||
function God.ChartAllOccupiedSandboxes()
|
||||
if settings.global[Settings.scanSandboxes].value then
|
||||
local charted = {}
|
||||
for _, player in pairs(game.players) do
|
||||
local hash = player.force.name .. player.surface.name
|
||||
if Sandbox.IsSandbox(player.surface) and not charted[hash] then
|
||||
player.force.chart_all(player.surface)
|
||||
charted[hash] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return God
|
||||
209
blueprint-sandboxes/scripts/illusion.lua
Normal file
209
blueprint-sandboxes/scripts/illusion.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
-- Illusion magic for swapping real/complex entities with fake/simple variants
|
||||
local Illusion = {}
|
||||
|
||||
Illusion.pfx = BPSB.pfx .. "ils-"
|
||||
local pfxLength = string.len(Illusion.pfx)
|
||||
|
||||
-- Full list of Entities that require Illusions
|
||||
Illusion.mappings = {
|
||||
-- { "type", "entity-name", "item-name" },
|
||||
{ "ammo-turret", "se-meteor-defence-container", "se-meteor-defence" },
|
||||
{ "ammo-turret", "se-meteor-point-defence-container", "se-meteor-point-defence" },
|
||||
{ "assembling-machine", "se-delivery-cannon", "se-delivery-cannon" },
|
||||
{ "assembling-machine", "se-delivery-cannon-weapon", "se-delivery-cannon-weapon" },
|
||||
{ "assembling-machine", "se-energy-transmitter-injector", "se-energy-transmitter-injector" },
|
||||
{ "assembling-machine", "se-energy-transmitter-emitter", "se-energy-transmitter-emitter" },
|
||||
{ "assembling-machine", "se-space-elevator", "se-space-elevator" },
|
||||
{ "boiler", "se-energy-transmitter-chamber", "se-energy-transmitter-chamber" },
|
||||
{ "container", "se-rocket-launch-pad", "se-rocket-launch-pad" },
|
||||
{ "container", "se-rocket-landing-pad", "se-rocket-landing-pad" },
|
||||
{ "electric-energy-interface", "se-energy-beam-defence", "se-energy-beam-defence" },
|
||||
{ "mining-drill", "se-core-miner-drill", "se-core-miner" },
|
||||
}
|
||||
|
||||
Illusion.realToIllusionMap = {}
|
||||
for _, mapping in ipairs(Illusion.mappings) do
|
||||
Illusion.realToIllusionMap[mapping[2]] = Illusion.pfx .. mapping[2]
|
||||
end
|
||||
|
||||
Illusion.realNameFilters = {}
|
||||
for realEntityName, illusionName in pairs(Illusion.realToIllusionMap) do
|
||||
table.insert(Illusion.realNameFilters, realEntityName)
|
||||
end
|
||||
|
||||
-- Whether the Thing is an Illusion
|
||||
function Illusion.IsIllusion(name)
|
||||
return string.sub(name, 1, pfxLength) == Illusion.pfx
|
||||
end
|
||||
|
||||
-- Extract the Name from an Illusion
|
||||
function Illusion.GetActualName(name)
|
||||
return string.sub(name, pfxLength + 1)
|
||||
end
|
||||
|
||||
-- Extract the Name from an Entity
|
||||
function Illusion.GhostOrRealName(entity)
|
||||
local realName = entity.name
|
||||
if entity.type == "entity-ghost" then
|
||||
realName = entity.ghost_name
|
||||
end
|
||||
return realName
|
||||
end
|
||||
|
||||
-- Convert a built Entity into an Illusion (if possible)
|
||||
function Illusion.ReplaceIfNecessary(entity)
|
||||
if not entity.valid then
|
||||
return
|
||||
end
|
||||
|
||||
local realName = Illusion.GhostOrRealName(entity)
|
||||
local illusionName = Illusion.realToIllusionMap[realName]
|
||||
if illusionName == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local options = {
|
||||
name = illusionName,
|
||||
position = entity.position,
|
||||
direction = entity.direction,
|
||||
force = entity.force,
|
||||
fast_replace = true,
|
||||
spill = false,
|
||||
raise_built = true,
|
||||
}
|
||||
|
||||
local result = entity.surface.create_entity(options)
|
||||
|
||||
if result == nil then
|
||||
log("Could not replace " .. realName .. " with " .. illusionName)
|
||||
else
|
||||
log("Replaced " .. realName .. " with " .. illusionName)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Holy shit, this is perhaps __the__ most gross portion of the Modding API.
|
||||
on_player_setup_blueprint _should_ be the primary and only method for handling
|
||||
Blueprints, but it is not. It is only _correctly_ called when the Blueprint
|
||||
is first created. If a Blueprint has its contents reselected, then it is still
|
||||
called, but the event data is entirely useless. Allegedly, you'd have to find
|
||||
the Blueprint in the Player's inventory - but it might not always be there either!
|
||||
It seems most in the community went for on_gui_closed which _probably_ has the
|
||||
Blueprint, however it will occur after selection so I imagine there's potential
|
||||
for it to _appear_ wrong before saving. on_gui_opened would work the same,
|
||||
but of course it would not catch any updates, so it's useless in this case.
|
||||
Worse still, this _only_ works for Blueprints in your Inventory - not from
|
||||
the Library! For that situation, we'll warn the Player.
|
||||
|
||||
See:
|
||||
|
||||
[kovarex] [1.0.0] Updated blueprint has no entities during on_player_setup_blueprint
|
||||
https://forums.factorio.com/viewtopic.php?f=48&t=88100
|
||||
|
||||
[kovarex] [1.1.36] Blueprints missing entity list when reused
|
||||
https://forums.factorio.com/viewtopic.php?f=7&t=99323
|
||||
|
||||
[kovarex] [1.0.0] New contents for blueprint broken vs. new blueprint
|
||||
https://forums.factorio.com/viewtopic.php?f=29&t=88793
|
||||
|
||||
Copying (Ctrl+C):
|
||||
- on_blueprint_setup is called
|
||||
- blueprint_to_setup NOT valid_for_read
|
||||
- cursor_stack valid_for_read
|
||||
- cursor_stack is setup, has entity mapping, and entities
|
||||
|
||||
Copying into new Blueprint (Shift+Ctrl+C):
|
||||
Empty Blueprint in cursor, from Inventory or Book in Inventory, selecting new area (Alt+B):
|
||||
- on_blueprint_setup is called
|
||||
- blueprint_to_setup valid_for_read
|
||||
- blueprint_to_setup is setup, has entity mapping, and entities
|
||||
- cursor_stack NOT valid_for_read
|
||||
|
||||
Selecting new contents, from Inventory or Book in Inventory:
|
||||
Selecting new contents, from Library or Book in Library:
|
||||
- on_blueprint_setup is called
|
||||
- blueprint_to_setup NOT valid_for_read
|
||||
- cursor_stack valid_for_read
|
||||
- cursor_stack NOT setup, NO entity mapping, NO entities
|
||||
|
||||
Closing/Confirming Blueprint GUI, from Inventory or Book in Inventory:
|
||||
- on_gui_closed is called
|
||||
- item valid_for_read
|
||||
- item NOT setup, NO entity mapping, but has entities
|
||||
|
||||
Closing/Confirming Blueprint GUI, from Library or Book in Library:
|
||||
- on_gui_closed is called
|
||||
- item valid_for_read
|
||||
- item NOT setup, NO entity mapping, NO entities
|
||||
|
||||
]]
|
||||
|
||||
-- Convert an entire Blueprint's contents from Illusions (if possible)
|
||||
function Illusion.HandleBlueprintEvent(player, potentialItemStacks)
|
||||
if not Sandbox.IsPlayerInsideSandbox(player) then
|
||||
return
|
||||
end
|
||||
|
||||
local blueprint = nil
|
||||
for _, itemStack in pairs(potentialItemStacks) do
|
||||
if itemStack and itemStack.valid_for_read and itemStack.is_blueprint then
|
||||
blueprint = itemStack
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- We must catch more events than we need, so this is expected
|
||||
if not blueprint then
|
||||
return
|
||||
end
|
||||
|
||||
-- Some events won't have a functional Blueprint, so we're screwed!
|
||||
local entities = blueprint.get_blueprint_entities()
|
||||
if not entities then
|
||||
log("Cannot handle Blueprint update: no entities in Blueprint (caused by selecting new contents)")
|
||||
local playerData = global.players[player.index]
|
||||
local lastWarningForNewContents = playerData.lastWarningForNewContents or 0
|
||||
if game.tick - lastWarningForNewContents > (216000) then -- 1 hour
|
||||
player.print("WARNING: Known issues in Factorio prevent mods from seeing or updating Blueprints when using 'Select new contents'.")
|
||||
player.print("This mod requires that ability to swap the Fake Illusions for their Real Entities in your Blueprints.")
|
||||
player.print("If you are including any Fake Illusions in this Blueprint, they likely will NOT be replaced, especially if the source Blueprint is within the Library instead of your Inventory.")
|
||||
player.print("This message will only appear periodically. See the mod's page for more details.")
|
||||
playerData.lastWarningForNewContents = game.tick
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local replaced = 0
|
||||
for _, entity in pairs(entities) do
|
||||
if Illusion.IsIllusion(entity.name) then
|
||||
entity.name = Illusion.GetActualName(entity.name)
|
||||
replaced = replaced + 1
|
||||
end
|
||||
end
|
||||
if replaced > 0 then
|
||||
blueprint.set_blueprint_entities(entities)
|
||||
end
|
||||
log("Replaced " .. replaced .. " entities in Sandbox Blueprint")
|
||||
end
|
||||
|
||||
-- A Player is creating a new Blueprint from a selection
|
||||
function Illusion.OnBlueprintSetup(event)
|
||||
local player = game.players[event.player_index]
|
||||
Illusion.HandleBlueprintEvent(player, {
|
||||
player.blueprint_to_setup,
|
||||
player.cursor_stack,
|
||||
})
|
||||
end
|
||||
|
||||
-- A Player might be updating an existing Blueprint (only works in the Inventory!)
|
||||
function Illusion.OnBlueprintGUIClosed(event)
|
||||
if not event.item then
|
||||
return
|
||||
end
|
||||
Illusion.HandleBlueprintEvent(game.players[event.player_index], {
|
||||
event.item,
|
||||
})
|
||||
end
|
||||
|
||||
return Illusion
|
||||
50
blueprint-sandboxes/scripts/init.lua
Normal file
50
blueprint-sandboxes/scripts/init.lua
Normal file
@@ -0,0 +1,50 @@
|
||||
-- First-time Setup for new Games and new Players
|
||||
local Init = {}
|
||||
|
||||
-- Setup Player, if necessary
|
||||
function Init.Player(player)
|
||||
if global.players[player.index] then
|
||||
log("Skip Init.Player: " .. player.name)
|
||||
return
|
||||
end
|
||||
|
||||
log("Init.Player: " .. player.name)
|
||||
local playerLabName = Lab.NameFromPlayer(player)
|
||||
local sandboxForceName = Sandbox.NameFromForce(player.force)
|
||||
global.players[player.index] = {
|
||||
forceName = player.force.name,
|
||||
labName = playerLabName,
|
||||
sandboxForceName = sandboxForceName,
|
||||
selectedSandbox = Sandbox.player,
|
||||
sandboxInventory = nil,
|
||||
insideSandbox = nil,
|
||||
lastSandboxPositions = {},
|
||||
}
|
||||
ToggleGUI.Init(player)
|
||||
end
|
||||
|
||||
-- Reset all Mod data
|
||||
function Init.FirstTimeInit()
|
||||
log("Init.FirstTimeInit")
|
||||
global.version = Migrate.version
|
||||
global.forces = {}
|
||||
global.players = {}
|
||||
global.labSurfaces = {}
|
||||
global.sandboxForces = {}
|
||||
global.seSurfaces = {}
|
||||
global.equipmentInProgress = {}
|
||||
global.asyncCreateQueue = Queue.New()
|
||||
global.asyncUpgradeQueue = Queue.New()
|
||||
global.asyncDestroyQueue = Queue.New()
|
||||
global.lastSettingForAsyncGodTick = settings.global[Settings.godAsyncTick].value
|
||||
|
||||
-- Warning: do not rely on this alone; new Saves have no Players/Forces yet
|
||||
for _, force in pairs(game.forces) do
|
||||
Force.Init(force)
|
||||
end
|
||||
for _, player in pairs(game.players) do
|
||||
Init.Player(player)
|
||||
end
|
||||
end
|
||||
|
||||
return Init
|
||||
100
blueprint-sandboxes/scripts/inventory.lua
Normal file
100
blueprint-sandboxes/scripts/inventory.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
-- Inventory-related methods
|
||||
local Inventory = {}
|
||||
|
||||
-- TODO: Consider the Cursor Inventory too (otherwise items can be lost during transition)
|
||||
-- TODO: Consider Filters (otherwise they are lost during transition)
|
||||
|
||||
-- Extracts a Player Cursor's Blueprint as a string (if present)
|
||||
function Inventory.GetCursorBlueprintString(player)
|
||||
local blueprint = nil
|
||||
if player.is_cursor_blueprint() then
|
||||
if player.character
|
||||
and player.character.cursor_stack
|
||||
and player.character.cursor_stack.valid
|
||||
and player.character.cursor_stack.valid_for_read
|
||||
then
|
||||
blueprint = player.character.cursor_stack.export_stack()
|
||||
elseif player.cursor_stack
|
||||
and player.cursor_stack.valid
|
||||
and player.cursor_stack.valid_for_read
|
||||
then
|
||||
blueprint = player.cursor_stack.export_stack()
|
||||
else
|
||||
player.print("There was a Blueprint in your cursor, but Factorio incorrectly describes it as invalid. This is most likely because it's currently in the Blueprint Library (a known bug in Factorio).")
|
||||
end
|
||||
end
|
||||
return blueprint
|
||||
end
|
||||
|
||||
-- Whether a Player's Cursor can non-destructively be replaced
|
||||
function Inventory.WasCursorSafelyCleared(player)
|
||||
if not player or not player.cursor_stack.valid then
|
||||
return false
|
||||
end
|
||||
if player.is_cursor_empty() then return true end
|
||||
if player.is_cursor_blueprint() then return true end
|
||||
|
||||
--[[ TODO:
|
||||
This doesn't usually happen, since the "source location" of the item
|
||||
seems to be lost after swapping the character.
|
||||
]]
|
||||
if not player.cursor_stack_temporary then
|
||||
player.clear_cursor()
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Whether a Player's Inventory is vulnerable to going missing due to lack of a body
|
||||
function Inventory.ShouldPersist(controller)
|
||||
return controller ~= defines.controllers.character
|
||||
end
|
||||
|
||||
-- Ensure a Player's Inventory isn't full
|
||||
function Inventory.Prune(player)
|
||||
local inventory = player.get_main_inventory()
|
||||
if not inventory then
|
||||
return
|
||||
end
|
||||
|
||||
if inventory.count_empty_stacks() == 0 then
|
||||
player.print("Your inventory is almost full. Please throw some items away.")
|
||||
player.surface.spill_item_stack(player.position, inventory[#inventory])
|
||||
inventory[#inventory].clear()
|
||||
end
|
||||
end
|
||||
|
||||
-- Persist one Inventory into another mod-created one
|
||||
function Inventory.Persist(from, to)
|
||||
if not from then
|
||||
return nil
|
||||
end
|
||||
if not to then
|
||||
to = game.create_inventory(#from)
|
||||
else
|
||||
to.resize(#from)
|
||||
end
|
||||
for i = 1, #from do
|
||||
to[i].set_stack(from[i])
|
||||
end
|
||||
return to
|
||||
end
|
||||
|
||||
-- Restore one Inventory into another
|
||||
function Inventory.Restore(from, to)
|
||||
if not from or not to then
|
||||
return
|
||||
end
|
||||
local transition = math.min(#from, #to)
|
||||
for i = 1, transition do
|
||||
to[i].set_stack(from[i])
|
||||
end
|
||||
if transition < #to then
|
||||
for i = transition + 1, #to do
|
||||
to[i].set_stack()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Inventory
|
||||
203
blueprint-sandboxes/scripts/lab.lua
Normal file
203
blueprint-sandboxes/scripts/lab.lua
Normal file
@@ -0,0 +1,203 @@
|
||||
-- Manage the Lab-like Surfaces
|
||||
local Lab = {}
|
||||
|
||||
Lab.pfx = BPSB.pfx .. "lab-"
|
||||
local pfxLength = string.len(Lab.pfx)
|
||||
|
||||
Lab.chartAllLabsTick = 300
|
||||
|
||||
Lab.equipmentString = "0eNqNkd1ugzAMhd/F10kF6bq2eZVqQpAaZgkMSkI1hnj3OVRC1dT95M6x/Z2TkxmqdsTBE0ewM5DrOYC9zBCo4bJNd3EaECxQxA4UcNmlClt00ZPTyOibScs++rp0CIsC4it+gM0X9SenokZvrKFvH/fN8qYAOVIkvJtai6ngsavQi8AvGAVDH2Sz56QttEzBBFabJbn6BjL/eNcPwEz8VmNdoy8CfQoiz7bzRGm/KRHXxNLS7h1DfILfHVYBszuskdyni4AxEjchTXns+hsWo/RasYnXIoUrrehHXFJ6a9j24Y8V3NCHVcWc8pfj2RxfzyY77SWXL6PBsLw="
|
||||
|
||||
-- A unique per-Player Lab Name
|
||||
function Lab.NameFromPlayer(player)
|
||||
return Lab.pfx .. "p-" .. player.name
|
||||
end
|
||||
|
||||
-- A unique per-Force Lab Name
|
||||
function Lab.NameFromForce(force)
|
||||
return Lab.pfx .. "f-" .. force.name
|
||||
end
|
||||
|
||||
-- Whether the Surface (or Force) is specific to a Lab
|
||||
function Lab.IsLab(thingWithName)
|
||||
return string.sub(thingWithName.name, 1, pfxLength) == Lab.pfx
|
||||
-- return not not global.labSurfaces[thingWithName.name]
|
||||
end
|
||||
|
||||
-- Create a new Lab Surface, if necessary
|
||||
function Lab.GetOrCreateSurface(labName, sandboxForce)
|
||||
local surface = game.surfaces[labName]
|
||||
|
||||
if not Lab.IsLab({ name = labName }) then
|
||||
log("Not a Lab, won't Create: " .. labName)
|
||||
return
|
||||
end
|
||||
|
||||
if surface then
|
||||
if global.labSurfaces[labName] then
|
||||
return surface
|
||||
end
|
||||
log("Found a Lab Surface, but not the Data; recreating it for safety")
|
||||
end
|
||||
|
||||
log("Creating Lab: " .. labName)
|
||||
global.labSurfaces[labName] = {
|
||||
sandboxForceName = sandboxForce.name,
|
||||
equipmentBlueprints = Equipment.Init(Lab.equipmentString),
|
||||
daytime = 0.95,
|
||||
}
|
||||
if not surface then
|
||||
surface = game.create_surface(labName, {
|
||||
default_enable_all_autoplace_controls = false,
|
||||
cliff_settings = { cliff_elevation_0 = 1024 },
|
||||
})
|
||||
end
|
||||
|
||||
return surface
|
||||
end
|
||||
|
||||
-- Set a Lab's Daytime to a specific value
|
||||
function Lab.SetDayTime(player, surface, daytime)
|
||||
if Lab.IsLab(surface) then
|
||||
surface.freeze_daytime = true
|
||||
surface.daytime = daytime
|
||||
global.labSurfaces[surface.name].daytime = daytime
|
||||
Events.SendDaylightChangedEvent(player.index, surface.name, daytime)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete a Lab Surface, if present
|
||||
function Lab.DeleteLab(surfaceName)
|
||||
if game.surfaces[surfaceName] and global.labSurfaces[surfaceName] then
|
||||
log("Deleting Lab: " .. surfaceName)
|
||||
local equipmentBlueprints = global.labSurfaces.equipmentBlueprints
|
||||
if equipmentBlueprints and equipmentBlueprints.valid() then
|
||||
equipmentBlueprints.destroy()
|
||||
end
|
||||
global.labSurfaces[surfaceName] = nil
|
||||
game.delete_surface(surfaceName)
|
||||
return true
|
||||
else
|
||||
log("Not a Lab, won't Delete: " .. surfaceName)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the Lab's equipment Blueprint for a Surface
|
||||
function Lab.SetEquipmentBlueprint(surface, equipmentString)
|
||||
if Lab.IsLab(surface) then
|
||||
log("Setting Lab equipment: " .. surface.name)
|
||||
Equipment.Set(
|
||||
global.labSurfaces[surface.name].equipmentBlueprints,
|
||||
equipmentString
|
||||
)
|
||||
surface.print("The equipment Blueprint for this Lab has been changed")
|
||||
return true
|
||||
else
|
||||
log("Not a Lab, won't Set equipment: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset the Lab's equipment Blueprint for a Surface
|
||||
function Lab.ResetEquipmentBlueprint(surface)
|
||||
if Lab.IsLab(surface) then
|
||||
log("Resetting Lab equipment: " .. surface.name)
|
||||
Equipment.Set(
|
||||
global.labSurfaces[surface.name].equipmentBlueprints,
|
||||
Lab.equipmentString
|
||||
)
|
||||
surface.print("The equipment Blueprint for this Lab has been reset")
|
||||
return true
|
||||
else
|
||||
log("Not a Lab, won't Reset equipment: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset the Lab a Player is currently in
|
||||
function Lab.Reset(player)
|
||||
if Lab.IsLab(player.surface) then
|
||||
log("Resetting Lab: " .. player.surface.name)
|
||||
player.teleport({ 0, 0 }, player.surface.name)
|
||||
player.surface.clear(false)
|
||||
return true
|
||||
else
|
||||
log("Not a Lab, won't Reset: " .. player.surface.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Set some important Surface settings for a Lab
|
||||
function Lab.AfterCreate(surface)
|
||||
local surfaceData = global.labSurfaces[surface.name]
|
||||
if not surfaceData then
|
||||
log("Not a Lab, won't handle Creation: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
|
||||
log("Handling Creation of Lab: " .. surface.name)
|
||||
|
||||
if remote.interfaces["RSO"] then
|
||||
pcall(remote.call, "RSO", "ignoreSurface", surface.name)
|
||||
end
|
||||
|
||||
if remote.interfaces["dangOreus"] then
|
||||
pcall(remote.call, "dangOreus", "toggle", surface.name)
|
||||
end
|
||||
|
||||
if remote.interfaces["AbandonedRuins"] then
|
||||
pcall(remote.call, "AbandonedRuins", "exclude_surface", surface.name)
|
||||
end
|
||||
|
||||
surface.freeze_daytime = true
|
||||
surface.daytime = 0.95
|
||||
surface.show_clouds = false
|
||||
surface.generate_with_lab_tiles = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Add some helpful initial Entities to a Lab
|
||||
function Lab.Equip(surface)
|
||||
local surfaceData = global.labSurfaces[surface.name]
|
||||
if not surfaceData then
|
||||
log("Not a Lab, won't Equip: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
|
||||
log("Equipping Lab: " .. surface.name)
|
||||
|
||||
Equipment.Place(
|
||||
surfaceData.equipmentBlueprints[1],
|
||||
surface,
|
||||
surfaceData.sandboxForceName
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Update all Entities in Lab with a new Force
|
||||
function Lab.AssignEntitiesToForce(surface, force)
|
||||
local surfaceData = global.labSurfaces[surface.name]
|
||||
if not surfaceData then
|
||||
log("Not a Lab, won't Reassign: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
|
||||
log("Reassigning Lab to: " .. surface.name .. " -> " .. force.name)
|
||||
|
||||
for _, entity in pairs(surface.find_entities_filtered {
|
||||
force = surfaceData.sandboxForceName,
|
||||
invert = true,
|
||||
}) do
|
||||
entity.force = force
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return Lab
|
||||
404
blueprint-sandboxes/scripts/migrate.lua
Normal file
404
blueprint-sandboxes/scripts/migrate.lua
Normal file
@@ -0,0 +1,404 @@
|
||||
local Migrate = {}
|
||||
|
||||
Migrate.version = 011701
|
||||
|
||||
function Migrate.Run()
|
||||
if not global.version then
|
||||
global.version = 0
|
||||
end
|
||||
|
||||
if global.version < Migrate.version then
|
||||
if global.version < 010003 then Migrate.v1_0_3() end
|
||||
if global.version < 010100 then Migrate.v1_1_0() end
|
||||
if global.version < 010401 then Migrate.v1_4_1() end
|
||||
if global.version < 010500 then Migrate.v1_5_0() end
|
||||
if global.version < 010600 then Migrate.v1_6_0() end
|
||||
if global.version < 010700 then Migrate.v1_7_0() end
|
||||
if global.version < 010703 then Migrate.v1_7_3() end
|
||||
if global.version < 010704 then Migrate.v1_7_4() end
|
||||
if global.version < 011000 then Migrate.v1_10_0() end
|
||||
if global.version < 011001 then Migrate.v1_10_1() end
|
||||
if global.version < 011101 then Migrate.v1_11_1() end
|
||||
if global.version < 011103 then Migrate.v1_11_3() end
|
||||
if global.version < 011500 then Migrate.v1_15_0() end
|
||||
if global.version < 011604 then Migrate.v1_16_4() end
|
||||
if global.version < 011606 then Migrate.v1_16_6() end
|
||||
end
|
||||
|
||||
global.version = Migrate.version
|
||||
end
|
||||
|
||||
function Migrate.RecreateGuis()
|
||||
for _, player in pairs(game.players) do
|
||||
ToggleGUI.Destroy(player)
|
||||
ToggleGUI.Init(player)
|
||||
end
|
||||
end
|
||||
|
||||
function Migrate.v1_0_3()
|
||||
--[[
|
||||
It was discovered that in on_configuration_changed Space Exploration would
|
||||
"fix" all Tiles for all Zones that it knows of, which causes problems
|
||||
specifically for the Planetary Sandbox, which initially used Stars.
|
||||
At this point, we unfortunately have to completely remove those Sandboxes,
|
||||
which is unavoidable because by the nature of this update we would have
|
||||
triggered the complete-reset of that Surface anyway.
|
||||
]]
|
||||
|
||||
log("Migration 1.0.3 Starting")
|
||||
|
||||
if SpaceExploration.enabled then
|
||||
local planetaryLabId = 3
|
||||
local planetaryLabsOnStars = {}
|
||||
local playersToKickFromPlanetaryLabs = {}
|
||||
|
||||
for name, surfaceData in pairs(global.seSurfaces) do
|
||||
if (not surfaceData.orbital) and SpaceExploration.IsStar(name) then
|
||||
table.insert(planetaryLabsOnStars, {
|
||||
zoneName = name,
|
||||
sandboxForceName = surfaceData.sandboxForceName,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for index, player in pairs(game.players) do
|
||||
local playerData = global.players[index]
|
||||
if playerData.insideSandbox == planetaryLabId
|
||||
and SpaceExploration.IsStar(player.surface.name)
|
||||
then
|
||||
table.insert(playersToKickFromPlanetaryLabs, player)
|
||||
end
|
||||
end
|
||||
|
||||
for _, player in pairs(playersToKickFromPlanetaryLabs) do
|
||||
log("Kicking Player out of Planetary Lab: " .. player.name)
|
||||
Sandbox.Exit(player)
|
||||
end
|
||||
|
||||
for _, surfaceData in pairs(planetaryLabsOnStars) do
|
||||
log("Destroying Planetary Lab inside Star: " .. surfaceData.zoneName)
|
||||
SpaceExploration.DeleteSandbox(
|
||||
global.sandboxForces[surfaceData.sandboxForceName],
|
||||
surfaceData.zoneName
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
log("Migration 1.0.3 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_1_0()
|
||||
--[[
|
||||
A "persistent" Sandbox Inventory was created for each Player.
|
||||
]]
|
||||
|
||||
log("Migration 1.1.0 Starting")
|
||||
|
||||
for index, player in pairs(game.players) do
|
||||
local playerData = global.players[index]
|
||||
playerData.sandboxInventory = game.create_inventory(#player.get_main_inventory())
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
log("Player inside Sandbox, fully-syncing the inventory.")
|
||||
Inventory.Persist(
|
||||
player.get_main_inventory(),
|
||||
playerData.sandboxInventory
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
log("Migration 1.1.0 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_4_1()
|
||||
--[[
|
||||
The levels for level-based Research wasn't being synchronized.
|
||||
]]
|
||||
|
||||
log("Migration 1.4.1 Starting")
|
||||
|
||||
Research.SyncAllForces()
|
||||
|
||||
log("Migration 1.4.1 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_5_0()
|
||||
--[[
|
||||
Bonus Slots for Sandbox Force Inventories were added.
|
||||
]]
|
||||
|
||||
log("Migration 1.5.0 Starting")
|
||||
|
||||
Force.SyncAllForces()
|
||||
|
||||
log("Migration 1.5.0 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_6_0()
|
||||
--[[
|
||||
Last-known positions inside Sandboxes were added.
|
||||
]]
|
||||
|
||||
log("Migration 1.6.0 Starting")
|
||||
|
||||
for index, _ in pairs(game.players) do
|
||||
local playerData = global.players[index]
|
||||
playerData.lastSandboxPositions = {}
|
||||
end
|
||||
|
||||
log("Migration 1.6.0 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_7_0()
|
||||
--[[
|
||||
Configurable-per-Sandbox daytime was added.
|
||||
]]
|
||||
|
||||
log("Migration 1.7.0 Starting")
|
||||
|
||||
for surfaceName, _ in pairs(global.labSurfaces) do
|
||||
local surface = game.surfaces[surfaceName]
|
||||
if surface then
|
||||
surface.always_day = false
|
||||
surface.freeze_daytime = true
|
||||
surface.daytime = 0.95
|
||||
global.labSurfaces[surfaceName].daytime = 0.95
|
||||
end
|
||||
end
|
||||
|
||||
for surfaceName, _ in pairs(global.seSurfaces) do
|
||||
local surface = game.surfaces[surfaceName]
|
||||
if surface then
|
||||
surface.always_day = false
|
||||
surface.freeze_daytime = true
|
||||
surface.daytime = 0.95
|
||||
global.seSurfaces[surfaceName].daytime = 0.95
|
||||
end
|
||||
end
|
||||
|
||||
Migrate.RecreateGuis()
|
||||
|
||||
log("Migration 1.7.0 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_7_3()
|
||||
--[[
|
||||
The daylight portrait icon had the same name as the Reset Button.
|
||||
]]
|
||||
|
||||
log("Migration 1.7.3 Starting")
|
||||
|
||||
Migrate.RecreateGuis()
|
||||
|
||||
log("Migration 1.7.3 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_7_4()
|
||||
--[[
|
||||
The 1.7.3 migration wasn't correctly applied to 1.7.x
|
||||
Allow-all-Tech was incorrectly applying the existing Force's bonuses
|
||||
]]
|
||||
|
||||
Migrate.v1_7_3()
|
||||
|
||||
log("Migration 1.7.4 Starting")
|
||||
|
||||
if settings.global[Settings.allowAllTech].value then
|
||||
game.print("Blueprint Sandboxes Notice: You had the Unlock-all-Technologies " ..
|
||||
"Setting enabled, but there was a bug pre-1.7.4 that was incorrectly " ..
|
||||
"overriding some of the bonuses from leveled-research. You should " ..
|
||||
"disable, then re-enable this setting in order to fix that.")
|
||||
end
|
||||
|
||||
log("Migration 1.7.4 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_10_0()
|
||||
--[[
|
||||
Internal Queues for Asynchronous Sandbox requests
|
||||
replace the old find_entities_filtered
|
||||
]]
|
||||
|
||||
log("Migration 1.10.0 Starting")
|
||||
|
||||
global.asyncCreateQueue = Queue.New()
|
||||
global.asyncUpgradeQueue = Queue.New()
|
||||
global.asyncDestroyQueue = Queue.New()
|
||||
|
||||
for _, surfaceData in pairs(global.labSurfaces) do
|
||||
surfaceData.hasRequests = nil
|
||||
end
|
||||
|
||||
for _, surfaceData in pairs(global.seSurfaces) do
|
||||
surfaceData.hasRequests = nil
|
||||
end
|
||||
|
||||
log("Migration 1.10.0 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_10_1()
|
||||
--[[
|
||||
Planetary Labs were possibly created within a Player's Home System
|
||||
and on Planets that could be dangerous.
|
||||
]]
|
||||
|
||||
log("Migration 1.10.1 Starting")
|
||||
|
||||
if SpaceExploration.enabled then
|
||||
local planetaryLabId = 3
|
||||
local badPlanetaryLabs = {}
|
||||
local badPlanetaryLabNames = {}
|
||||
local playersToKickFromPlanetaryLabs = {}
|
||||
local zoneIndex = remote.call(SpaceExploration.name, "get_zone_index", {})
|
||||
|
||||
for name, surfaceData in pairs(global.seSurfaces) do
|
||||
if not surfaceData.orbital then
|
||||
local zone = remote.call(SpaceExploration.name, "get_zone_from_name", {
|
||||
zone_name = name,
|
||||
})
|
||||
local rootZone = SpaceExploration.GetRootZone(zoneIndex, zone)
|
||||
if SpaceExploration.IsZoneThreatening(zone)
|
||||
or rootZone.special_type == "homesystem" then
|
||||
table.insert(badPlanetaryLabs, {
|
||||
zoneName = name,
|
||||
sandboxForceName = surfaceData.sandboxForceName,
|
||||
})
|
||||
badPlanetaryLabNames[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for index, player in pairs(game.players) do
|
||||
local playerData = global.players[index]
|
||||
if playerData.insideSandbox == planetaryLabId
|
||||
and badPlanetaryLabNames[player.surface.name]
|
||||
then
|
||||
table.insert(playersToKickFromPlanetaryLabs, player)
|
||||
end
|
||||
end
|
||||
|
||||
for _, player in pairs(playersToKickFromPlanetaryLabs) do
|
||||
log("Kicking Player out of Planetary Lab: " .. player.name)
|
||||
Sandbox.Exit(player)
|
||||
end
|
||||
|
||||
for _, surfaceData in pairs(badPlanetaryLabs) do
|
||||
log("Destroying Planetary Lab: " .. surfaceData.zoneName)
|
||||
SpaceExploration.DeleteSandbox(
|
||||
global.sandboxForces[surfaceData.sandboxForceName],
|
||||
surfaceData.zoneName
|
||||
)
|
||||
local message = "Unfortunately, your Planetary Sandbox was generated in a " ..
|
||||
"non-ideal or dangerous location, so it was destroyed. Accessing " ..
|
||||
"the Sandbox again will create a new one in a safer location."
|
||||
game.forces[surfaceData.sandboxForceName].print(message)
|
||||
game.forces[global.sandboxForces[surfaceData.sandboxForceName].forceName].print(message)
|
||||
end
|
||||
end
|
||||
|
||||
log("Migration 1.10.1 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_11_1()
|
||||
--[[
|
||||
dangOreus was applying to Labs and causing significant lag
|
||||
]]
|
||||
|
||||
log("Migration 1.11.1 Starting")
|
||||
|
||||
if remote.interfaces["dangOreus"] then
|
||||
for labName, _ in pairs(global.labSurfaces) do
|
||||
pcall(remote.call, "dangOreus", "toggle", labName)
|
||||
end
|
||||
end
|
||||
|
||||
log("Migration 1.11.1 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_11_3_surface(surfaceName)
|
||||
local surface = game.surfaces[surfaceName]
|
||||
if not surface then
|
||||
return
|
||||
end
|
||||
|
||||
local entitiesToSwap = surface.find_entities_filtered({ name = Illusion.realNameFilters, })
|
||||
for _, entity in pairs(entitiesToSwap) do
|
||||
Illusion.ReplaceIfNecessary(entity)
|
||||
end
|
||||
|
||||
local ghostsToSwap = surface.find_entities_filtered({ ghost_name = Illusion.realNameFilters, })
|
||||
for _, entity in pairs(ghostsToSwap) do
|
||||
Illusion.ReplaceIfNecessary(entity)
|
||||
end
|
||||
end
|
||||
|
||||
function Migrate.v1_11_3()
|
||||
--[[
|
||||
1.11.0 did not include a migration of real-to-illusion Entities,
|
||||
but it was found that some older Entities combined with Space Exploration 0.6
|
||||
could cause a crash.
|
||||
]]
|
||||
|
||||
log("Migration 1.11.3 Starting")
|
||||
|
||||
for surfaceName, _ in pairs(global.labSurfaces) do
|
||||
Migrate.v1_11_3_surface(surfaceName)
|
||||
end
|
||||
|
||||
for surfaceName, _ in pairs(global.seSurfaces) do
|
||||
Migrate.v1_11_3_surface(surfaceName)
|
||||
end
|
||||
|
||||
log("Migration 1.11.3 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_15_0()
|
||||
--[[
|
||||
1.15.0 introduced a default Equipment Inventory for each Sandbox
|
||||
]]
|
||||
|
||||
log("Migration 1.15.0 Starting")
|
||||
|
||||
for surfaceName, surfaceData in pairs(global.labSurfaces) do
|
||||
surfaceData.equipmentBlueprints = Equipment.Init(Lab.equipmentString)
|
||||
end
|
||||
|
||||
for surfaceName, surfaceData in pairs(global.seSurfaces) do
|
||||
if (surfaceData.orbital) then
|
||||
surfaceData.equipmentBlueprints = Equipment.Init(SpaceExploration.orbitalEquipmentString)
|
||||
else
|
||||
surfaceData.equipmentBlueprints = Equipment.Init(Lab.equipmentString)
|
||||
end
|
||||
end
|
||||
|
||||
log("Migration 1.15.0 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_16_4()
|
||||
--[[
|
||||
1.16.4 introduced an alternative Equipment placement technique
|
||||
]]
|
||||
|
||||
log("Migration 1.16.4 Starting")
|
||||
|
||||
global.equipmentInProgress = {}
|
||||
|
||||
log("Migration 1.16.4 Finished")
|
||||
end
|
||||
|
||||
function Migrate.v1_16_6()
|
||||
--[[
|
||||
1.16.6 added Remote Interface support for Editor Extensions
|
||||
]]
|
||||
|
||||
log("Migration 1.16.6 Starting")
|
||||
|
||||
for _, force in pairs(game.forces) do
|
||||
if Sandbox.IsSandboxForce(force) then
|
||||
EditorExtensionsCheats.EnableTestingRecipes(force)
|
||||
end
|
||||
end
|
||||
|
||||
log("Migration 1.16.6 Finished")
|
||||
end
|
||||
|
||||
return Migrate
|
||||
70
blueprint-sandboxes/scripts/planner-icons.lua
Normal file
70
blueprint-sandboxes/scripts/planner-icons.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
local PlannerIcons = {}
|
||||
|
||||
function PlannerIcons.CreateLayeredIcon(prototype)
|
||||
local backgroundIconSize = 64
|
||||
local overallLayeredIconScale = 0.4
|
||||
|
||||
local layeredIcons = {
|
||||
{
|
||||
icon = BPSB.path .. "/graphics/icon-x64.png",
|
||||
icon_size = backgroundIconSize,
|
||||
icon_mipmaps = 3,
|
||||
tint = { r = 0.75, g = 0.75, b = 0.75, a = 1 },
|
||||
},
|
||||
}
|
||||
|
||||
local foundIcon = false
|
||||
if prototype.icons then
|
||||
foundIcon = true
|
||||
-- Complex Icons approach (layer but re-scale each)
|
||||
for _, icon in pairs(prototype.icons) do
|
||||
local thisIconScale = 1.0
|
||||
if icon.scale then
|
||||
thisIconScale = icon.scale
|
||||
end
|
||||
table.insert(layeredIcons, {
|
||||
icon = icon.icon,
|
||||
icon_size = icon.icon_size or prototype.icon_size,
|
||||
tint = icon.tint,
|
||||
shift = icon.shift,
|
||||
scale = thisIconScale * overallLayeredIconScale * (backgroundIconSize / (icon.icon_size or prototype.icon_size)),
|
||||
icon_mipmaps = icon.icon_mipmaps,
|
||||
})
|
||||
end
|
||||
elseif prototype.icon then
|
||||
foundIcon = true
|
||||
-- The simplest Icon approach
|
||||
table.insert(layeredIcons, {
|
||||
icon = prototype.icon,
|
||||
icon_size = prototype.icon_size,
|
||||
icon_mipmaps = prototype.icon_mipmaps,
|
||||
scale = overallLayeredIconScale * (backgroundIconSize / prototype.icon_size),
|
||||
})
|
||||
elseif prototype.variants then
|
||||
foundIcon = true
|
||||
-- Slightly complex Tile approach
|
||||
local image = prototype.variants.main[1]
|
||||
if prototype.variants.material_background then
|
||||
image = prototype.variants.material_background
|
||||
end
|
||||
local thisImageScale = 1.0
|
||||
if image.scale then
|
||||
thisImageScale = image.scale
|
||||
end
|
||||
local thisImageSize = (image.size or 1.0) * 32 / thisImageScale
|
||||
table.insert(layeredIcons, {
|
||||
icon = image.picture,
|
||||
icon_size = thisImageSize,
|
||||
tint = prototype.tint,
|
||||
scale = thisImageScale * overallLayeredIconScale * (backgroundIconSize / thisImageSize),
|
||||
})
|
||||
end
|
||||
|
||||
if not foundIcon then
|
||||
log("No icon found for prototype: " .. serpent.block(prototype))
|
||||
end
|
||||
|
||||
return layeredIcons
|
||||
end
|
||||
|
||||
return PlannerIcons
|
||||
30
blueprint-sandboxes/scripts/queue.lua
Normal file
30
blueprint-sandboxes/scripts/queue.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
-- https://www.lua.org/pil/11.4.html
|
||||
|
||||
local Queue = {}
|
||||
|
||||
function Queue.New()
|
||||
return { first = 0, last = -1 }
|
||||
end
|
||||
|
||||
function Queue.Push(list, value)
|
||||
local last = list.last + 1
|
||||
list.last = last
|
||||
list[last] = value
|
||||
end
|
||||
|
||||
function Queue.Pop(list)
|
||||
local first = list.first
|
||||
if first > list.last then
|
||||
return nil
|
||||
end
|
||||
local value = list[first]
|
||||
list[first] = nil
|
||||
list.first = first + 1
|
||||
return value
|
||||
end
|
||||
|
||||
function Queue.Size(list)
|
||||
return list.last - list.first + 1
|
||||
end
|
||||
|
||||
return Queue
|
||||
14
blueprint-sandboxes/scripts/remote-interface.lua
Normal file
14
blueprint-sandboxes/scripts/remote-interface.lua
Normal file
@@ -0,0 +1,14 @@
|
||||
remote.add_interface(
|
||||
"blueprint-sandboxes",
|
||||
{
|
||||
space_exploration_delete_surface = function(event)
|
||||
local surface = game.surfaces[event.surface_index]
|
||||
if SpaceExploration.IsSandbox(surface) then
|
||||
return {
|
||||
allow_delete = false,
|
||||
message = {"bpsb-messages.space-exploration-delete-sandbox"},
|
||||
}
|
||||
end
|
||||
end,
|
||||
}
|
||||
)
|
||||
105
blueprint-sandboxes/scripts/research.lua
Normal file
105
blueprint-sandboxes/scripts/research.lua
Normal file
@@ -0,0 +1,105 @@
|
||||
-- Managing the Research of each Force's Sandboxes
|
||||
local Research = {}
|
||||
|
||||
-- Set a Force's Sandboxes Research equal to that of the Force's (or all research)
|
||||
function Research.Sync(originalForce, sandboxForce)
|
||||
if settings.global[Settings.allowAllTech].value then
|
||||
sandboxForce.research_all_technologies()
|
||||
log("Researching everything for: " .. sandboxForce.name)
|
||||
else
|
||||
for tech, _ in pairs(game.technology_prototypes) do
|
||||
sandboxForce.technologies[tech].researched = originalForce.technologies[tech].researched
|
||||
sandboxForce.technologies[tech].level = originalForce.technologies[tech].level
|
||||
end
|
||||
log("Copied all Research from: " .. originalForce.name .. " -> " .. sandboxForce.name)
|
||||
end
|
||||
end
|
||||
|
||||
-- Set a Force's Sandboxes Research Queue equal to that of the Force's
|
||||
function Research.SyncQueue(originalForce, sandboxForce)
|
||||
if settings.global[Settings.allowAllTech].value then
|
||||
sandboxForce.research_queue = nil
|
||||
log("Emptying Research Queue for: " .. sandboxForce.name)
|
||||
else
|
||||
local newQueue = {}
|
||||
for _, tech in pairs(originalForce.research_queue) do
|
||||
table.insert(newQueue, tech.name)
|
||||
end
|
||||
sandboxForce.research_queue = newQueue
|
||||
log("Copied Research Queue from: " .. originalForce.name .. " -> " .. sandboxForce.name)
|
||||
end
|
||||
end
|
||||
|
||||
-- Enable the Infinity Input/Output Recipes
|
||||
function Research.EnableSandboxSpecificResearch(force)
|
||||
if global.sandboxForces[force.name].hiddenItemsUnlocked == true then
|
||||
return
|
||||
end
|
||||
log("Unlocking hidden Recipes for: " .. force.name)
|
||||
|
||||
if force.recipes[BPSB.pfx .. "loader"] then
|
||||
force.recipes[BPSB.pfx .. "loader"].enabled = true
|
||||
force.recipes[BPSB.pfx .. "fast-loader"].enabled = true
|
||||
force.recipes[BPSB.pfx .. "express-loader"].enabled = true
|
||||
end
|
||||
|
||||
force.recipes[BPSB.pfx .. "electric-energy-interface"].enabled = true
|
||||
force.recipes[BPSB.pfx .. "infinity-chest"].enabled = true
|
||||
force.recipes[BPSB.pfx .. "infinity-pipe"].enabled = true
|
||||
|
||||
for name, recipe in pairs(force.recipes) do
|
||||
if Resources.IsResourcePlanner(name) or Tiles.IsTilePlanner(name) then
|
||||
recipe.enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
EditorExtensionsCheats.EnableTestingRecipes(force)
|
||||
|
||||
global.sandboxForces[force.name].hiddenItemsUnlocked = true
|
||||
end
|
||||
|
||||
-- For all Forces with Sandboxes, Sync their Research
|
||||
function Research.SyncAllForces()
|
||||
for _, force in pairs(game.forces) do
|
||||
if not Sandbox.IsSandboxForce(force) then
|
||||
local sandboxForce = game.forces[Sandbox.NameFromForce(force)]
|
||||
if sandboxForce then
|
||||
Research.Sync(force, sandboxForce)
|
||||
Research.SyncQueue(force, sandboxForce)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- As a Force's Research changes, keep the Force's Sandboxes in-sync
|
||||
function Research.OnResearched(event)
|
||||
if not settings.global[Settings.allowAllTech].value then
|
||||
local force = event.research.force
|
||||
if not Sandbox.IsSandboxForce(force) then
|
||||
local sandboxForce = game.forces[Sandbox.NameFromForce(force)]
|
||||
if sandboxForce then
|
||||
log("New Research: " .. event.research.name .. " from " .. force.name .. " -> " .. sandboxForce.name)
|
||||
sandboxForce.technologies[event.research.name].researched = force.technologies[event.research.name].researched
|
||||
sandboxForce.technologies[event.research.name].level = force.technologies[event.research.name].level
|
||||
sandboxForce.play_sound { path = "utility/research_completed" }
|
||||
Research.SyncQueue(force, sandboxForce)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- As a Force's Research Queue changes, show it in the Force's Sandboxes
|
||||
function Research.OnResearchStarted(event)
|
||||
if not settings.global[Settings.allowAllTech].value then
|
||||
local force = event.research.force
|
||||
if not Sandbox.IsSandboxForce(force) then
|
||||
local sandboxForce = game.forces[Sandbox.NameFromForce(force)]
|
||||
if sandboxForce then
|
||||
log("New Research Queued: " .. event.research.name .. " from " .. force.name .. " -> " .. sandboxForce.name)
|
||||
Research.SyncQueue(force, sandboxForce)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Research
|
||||
97
blueprint-sandboxes/scripts/resources.lua
Normal file
97
blueprint-sandboxes/scripts/resources.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
-- Custom Planners to add/remove Resources
|
||||
local Resources = {}
|
||||
|
||||
Resources.name = BPSB.pfx .. "sandbox-resources"
|
||||
Resources.pfx = BPSB.pfx .. "sbr-"
|
||||
local pfxLength = string.len(Resources.pfx)
|
||||
|
||||
Resources.nameScalar = { default = 1 }
|
||||
Resources.nameScalar["crude-oil"] = 1
|
||||
Resources.nameScalar["mineral-water"] = 5
|
||||
|
||||
Resources.categoryScalar = { default = 10000 }
|
||||
Resources.categoryScalar["basic-fluid"] = 1
|
||||
Resources.categoryScalar["basic-solid"] = 10000
|
||||
Resources.categoryScalar["oil"] = 1
|
||||
Resources.categoryScalar["hard-resource"] = 8000
|
||||
Resources.categoryScalar["kr-quarry"] = 1500
|
||||
|
||||
-- Whether the Thing is a Resource Planner
|
||||
function Resources.IsResourcePlanner(name)
|
||||
return string.sub(name, 1, pfxLength) == Resources.pfx
|
||||
end
|
||||
|
||||
-- Extract the Resource Name from a Resource Planner
|
||||
function Resources.GetResourceName(name)
|
||||
return string.sub(name, pfxLength + 1)
|
||||
end
|
||||
|
||||
-- Determine the amount to spawn for a Resource Planner
|
||||
function Resources.GetResourceAmount(resourceName)
|
||||
local resourcePrototype = game.entity_prototypes[resourceName]
|
||||
|
||||
local nameScalar = Resources.nameScalar[resourceName] or Resources.nameScalar["default"]
|
||||
local categoryScalar = Resources.categoryScalar[resourcePrototype.resource_category] or Resources.categoryScalar["default"]
|
||||
|
||||
local richness = 1
|
||||
local autoplace_controls = game.surfaces["nauvis"].map_gen_settings.autoplace_controls[resourceName]
|
||||
if autoplace_controls then
|
||||
richness = autoplace_controls.richness
|
||||
if richness < 0 then richness = 1
|
||||
else richness = math.max(0.5, richness)
|
||||
end
|
||||
end
|
||||
|
||||
local normal = resourcePrototype.normal_resource_amount
|
||||
local minimum = resourcePrototype.minimum_resource_amount
|
||||
|
||||
return nameScalar * categoryScalar * richness * math.max(normal, minimum)
|
||||
end
|
||||
|
||||
-- Determine how often to spawn for a Resource Planner
|
||||
function Resources.GetResourceSpacing(resourceName)
|
||||
local box = game.entity_prototypes[resourceName].map_generator_bounding_box
|
||||
return {
|
||||
x = math.max(1, math.ceil(box.right_bottom.x - box.left_top.x)),
|
||||
y = math.max(1, math.ceil(box.right_bottom.y - box.left_top.y)),
|
||||
}
|
||||
end
|
||||
|
||||
-- Add Resources when a Resource Planner is used
|
||||
function Resources.OnAreaSelectedForAdd(event)
|
||||
local resourceName = Resources.GetResourceName(event.item)
|
||||
local density = Resources.GetResourceAmount(resourceName)
|
||||
local spacing = Resources.GetResourceSpacing(resourceName)
|
||||
for x = event.area.left_top.x, event.area.right_bottom.x, spacing.x do
|
||||
for y = event.area.left_top.y, event.area.right_bottom.y, spacing.y do
|
||||
event.surface.create_entity({
|
||||
name = resourceName,
|
||||
position = { x = x, y = y },
|
||||
amount = density,
|
||||
raise_built = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Removed Resources when a Resource Planner is used
|
||||
function Resources.OnAreaSelectedForRemove(event)
|
||||
for _, entity in pairs(event.entities) do
|
||||
entity.destroy({ raise_destroy = true })
|
||||
end
|
||||
end
|
||||
|
||||
-- Add/Remove Resources when a Resource Planner is used
|
||||
function Resources.OnAreaSelected(event, add)
|
||||
if (Lab.IsLab(event.surface) or SpaceExploration.IsSandbox(event.surface))
|
||||
and Resources.IsResourcePlanner(event.item)
|
||||
then
|
||||
if add then
|
||||
Resources.OnAreaSelectedForAdd(event)
|
||||
else
|
||||
Resources.OnAreaSelectedForRemove(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Resources
|
||||
402
blueprint-sandboxes/scripts/sandbox.lua
Normal file
402
blueprint-sandboxes/scripts/sandbox.lua
Normal file
@@ -0,0 +1,402 @@
|
||||
-- Managing multiple Sandboxes for each Player/Force
|
||||
local Sandbox = {}
|
||||
|
||||
Sandbox.pfx = BPSB.pfx .. "sb-"
|
||||
|
||||
-- GUI Dropdown items for Sandboxes
|
||||
Sandbox.choices = {
|
||||
{ "sandbox." .. Sandbox.pfx .. "player-lab" },
|
||||
{ "sandbox." .. Sandbox.pfx .. "force-lab" },
|
||||
{ "sandbox." .. Sandbox.pfx .. "force-lab-space-exploration" },
|
||||
{ "sandbox." .. Sandbox.pfx .. "force-orbit-space-exploration" },
|
||||
}
|
||||
if not SpaceExploration.enabled then
|
||||
Sandbox.choices[3] = { "sandbox." .. Sandbox.pfx .. "space-exploration-disabled" }
|
||||
Sandbox.choices[4] = { "sandbox." .. Sandbox.pfx .. "space-exploration-disabled" }
|
||||
end
|
||||
|
||||
-- Constants to represent indexes for Sandbox.choices
|
||||
Sandbox.player = 1
|
||||
Sandbox.force = 2
|
||||
Sandbox.forcePlanetaryLab = 3
|
||||
Sandbox.forceOrbitalSandbox = 4
|
||||
|
||||
-- A unique per-Force Sandbox Name
|
||||
function Sandbox.NameFromForce(force)
|
||||
return Sandbox.pfx .. "f-" .. force.name
|
||||
end
|
||||
|
||||
-- Whether the Force is specific to Blueprint Sandboxes
|
||||
function Sandbox.IsSandboxForce(force)
|
||||
-- return string.sub(force.name, 1, pfxLength) == Sandbox.pfx
|
||||
return not not global.sandboxForces[force.name]
|
||||
end
|
||||
|
||||
-- Whether something is any type of Sandbox
|
||||
function Sandbox.IsSandbox(thingWithName)
|
||||
return Lab.IsLab(thingWithName)
|
||||
or SpaceExploration.IsSandbox(thingWithName)
|
||||
end
|
||||
|
||||
-- Whether something is any type of Sandbox
|
||||
function Sandbox.IsPlayerInsideSandbox(player)
|
||||
return global.players[player.index].preSandboxPosition ~= nil
|
||||
and Sandbox.IsSandbox(player.surface)
|
||||
end
|
||||
|
||||
-- Whether a Sandbox choice is allowed
|
||||
function Sandbox.IsEnabled(selectedSandbox)
|
||||
if selectedSandbox == Sandbox.player then
|
||||
return true
|
||||
elseif selectedSandbox == Sandbox.force then
|
||||
return true
|
||||
elseif selectedSandbox == Sandbox.forceOrbitalSandbox then
|
||||
return SpaceExploration.enabled
|
||||
elseif selectedSandbox == Sandbox.forcePlanetaryLab then
|
||||
return SpaceExploration.enabled
|
||||
else
|
||||
log("Impossible Choice for Sandbox: " .. selectedSandbox)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Which Surface Name to use for this Player based on their Selected Sandbox
|
||||
function Sandbox.GetOrCreateSandboxSurface(player, sandboxForce)
|
||||
local playerData = global.players[player.index]
|
||||
|
||||
if playerData.selectedSandbox == Sandbox.player
|
||||
then
|
||||
return Lab.GetOrCreateSurface(playerData.labName, sandboxForce)
|
||||
elseif playerData.selectedSandbox == Sandbox.force
|
||||
then
|
||||
return Lab.GetOrCreateSurface(global.sandboxForces[sandboxForce.name].labName, sandboxForce)
|
||||
elseif SpaceExploration.enabled
|
||||
and playerData.selectedSandbox == Sandbox.forceOrbitalSandbox
|
||||
then
|
||||
return SpaceExploration.GetOrCreateOrbitalSurfaceForForce(player, sandboxForce)
|
||||
elseif SpaceExploration.enabled
|
||||
and playerData.selectedSandbox == Sandbox.forcePlanetaryLab
|
||||
then
|
||||
return SpaceExploration.GetOrCreatePlanetarySurfaceForForce(player, sandboxForce)
|
||||
else
|
||||
log("Impossible Choice for Sandbox: " .. playerData.selectedSandbox)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Convert the Player to God-mode, save their previous State, and enter Selected Sandbox
|
||||
function Sandbox.Enter(player)
|
||||
local playerData = global.players[player.index]
|
||||
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
log("Already inside Sandbox: " .. playerData.insideSandbox)
|
||||
return
|
||||
end
|
||||
|
||||
if player.stashed_controller_type
|
||||
and player.stashed_controller_type ~= defines.controllers.editor
|
||||
then
|
||||
player.print("You are already detached from your Character, so you cannot enter a Sandbox. Return to your Character first.")
|
||||
return
|
||||
end
|
||||
|
||||
local sandboxForce = Force.GetOrCreateSandboxForce(game.forces[playerData.forceName])
|
||||
local surface = Sandbox.GetOrCreateSandboxSurface(player, sandboxForce)
|
||||
if surface == nil then
|
||||
log("Completely Unknown Sandbox Surface, cannot use")
|
||||
return
|
||||
end
|
||||
log("Entering Sandbox: " .. surface.name)
|
||||
|
||||
-- Store some temporary State to use once inside the Sandbox
|
||||
local inputBlueprint = Inventory.GetCursorBlueprintString(player)
|
||||
|
||||
--[[
|
||||
Otherwise, there is a Factorio "bug" that can destroy what was in the Cursor.
|
||||
It seems to happen with something from the Inventory being in the Stack, then
|
||||
entering the Sandbox, then copying something from the Sandbox, then exiting the
|
||||
Sandbox. At this point, the Cursor Stack is still fine and valid, but it seems
|
||||
to have lost its original location, so "clearing" it out will destroy it.
|
||||
]]
|
||||
player.clear_cursor()
|
||||
|
||||
-- Store the Player's previous State (that must be referenced to Exit)
|
||||
playerData.insideSandbox = playerData.selectedSandbox
|
||||
playerData.preSandboxForceName = player.force.name
|
||||
playerData.preSandboxCharacter = player.character
|
||||
playerData.preSandboxController = player.controller_type
|
||||
playerData.preSandboxPosition = player.position
|
||||
playerData.preSandboxSurfaceName = player.surface.name
|
||||
playerData.preSandboxCheatMode = player.cheat_mode
|
||||
|
||||
-- Sometimes a Player has a volatile Inventory that needs restoring later
|
||||
if Inventory.ShouldPersist(playerData.preSandboxController) then
|
||||
playerData.preSandboxInventory = Inventory.Persist(
|
||||
player.get_main_inventory(),
|
||||
playerData.preSandboxInventory
|
||||
)
|
||||
else
|
||||
if playerData.preSandboxInventory then
|
||||
playerData.preSandboxInventory.destroy()
|
||||
playerData.preSandboxInventory = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Harmlessly detach the Player from their Character
|
||||
player.set_controller({ type = defines.controllers.god })
|
||||
|
||||
-- Harmlessly teleport their God-body to the Sandbox
|
||||
player.teleport(playerData.lastSandboxPositions[surface.name] or { 0, 0 }, surface)
|
||||
|
||||
-- Swap to the new Force; it has different bonuses!
|
||||
player.force = sandboxForce
|
||||
|
||||
-- Since the Sandbox might have Cheat Mode enabled, EditorExtensions won't receive an Event for this otherwise
|
||||
if player.cheat_mode then
|
||||
player.cheat_mode = false
|
||||
end
|
||||
|
||||
-- Enable Cheat mode _afterwards_, since EditorExtensions will alter the Force (now the Sandbox Force) based on this
|
||||
player.cheat_mode = true
|
||||
|
||||
-- Harmlessly ensure our own Recipes are enabled
|
||||
-- TODO: It's unclear why this must happen _after_ the above code
|
||||
Research.EnableSandboxSpecificResearch(sandboxForce)
|
||||
|
||||
-- Now that everything has taken effect, restoring the Inventory is safe
|
||||
Inventory.Restore(
|
||||
playerData.sandboxInventory,
|
||||
player.get_main_inventory()
|
||||
)
|
||||
|
||||
-- Then, restore the Blueprint in the Cursor
|
||||
if inputBlueprint then
|
||||
player.cursor_stack.import_stack(inputBlueprint)
|
||||
player.cursor_stack_temporary = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Convert the Player to their previous State, and leave Selected Sandbox
|
||||
function Sandbox.Exit(player)
|
||||
local playerData = global.players[player.index]
|
||||
|
||||
if not Sandbox.IsPlayerInsideSandbox(player) then
|
||||
log("Already outside Sandbox")
|
||||
return
|
||||
end
|
||||
log("Exiting Sandbox: " .. player.surface.name)
|
||||
|
||||
-- Store some temporary State to use once outside the Sandbox
|
||||
local outputBlueprint = Inventory.GetCursorBlueprintString(player)
|
||||
|
||||
-- Remember where they left off
|
||||
playerData.lastSandboxPositions[player.surface.name] = player.position
|
||||
|
||||
-- Attach the Player back to their original Character (also changes force)
|
||||
Sandbox.RecoverPlayerCharacter(player, playerData)
|
||||
|
||||
-- Swap to their original Force (in case they're not sent back to a Character)
|
||||
player.force = playerData.preSandboxForceName
|
||||
|
||||
-- Toggle Cheat mode _afterwards_, just in case EditorExtensions ever listens to this Event
|
||||
player.cheat_mode = playerData.preSandboxCheatMode or false
|
||||
|
||||
-- Sometimes a Player is already a God (like in Sandbox), and their Inventory wasn't on a body
|
||||
if Inventory.ShouldPersist(playerData.preSandboxController) then
|
||||
Inventory.Restore(
|
||||
playerData.preSandboxInventory,
|
||||
player.get_main_inventory()
|
||||
)
|
||||
end
|
||||
|
||||
-- Reset the Player's previous State
|
||||
playerData.insideSandbox = nil
|
||||
playerData.preSandboxForceName = nil
|
||||
playerData.preSandboxCharacter = nil
|
||||
playerData.preSandboxController = nil
|
||||
playerData.preSandboxPosition = nil
|
||||
playerData.preSandboxSurfaceName = nil
|
||||
playerData.preSandboxCheatMode = nil
|
||||
if playerData.preSandboxInventory then
|
||||
playerData.preSandboxInventory.destroy()
|
||||
playerData.preSandboxInventory = nil
|
||||
end
|
||||
|
||||
-- Potentially, restore the Blueprint in the Cursor
|
||||
if outputBlueprint and Inventory.WasCursorSafelyCleared(player) then
|
||||
player.cursor_stack.import_stack(outputBlueprint)
|
||||
player.cursor_stack_temporary = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure the Player has a Character to go back to
|
||||
function Sandbox.RecoverPlayerCharacter(player, playerData)
|
||||
-- Typical situation, there wasn't a Character, or there was a valid one
|
||||
if (not playerData.preSandboxCharacter) or playerData.preSandboxCharacter.valid then
|
||||
player.teleport(playerData.preSandboxPosition, playerData.preSandboxSurfaceName)
|
||||
player.set_controller({
|
||||
type = playerData.preSandboxController,
|
||||
character = playerData.preSandboxCharacter
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Space Exploration deletes and recreates Characters; check that out next
|
||||
local fromSpaceExploration = SpaceExploration.GetPlayerCharacter(player)
|
||||
if fromSpaceExploration and fromSpaceExploration.valid then
|
||||
player.teleport(fromSpaceExploration.position, fromSpaceExploration.surface.name)
|
||||
player.set_controller({
|
||||
type = defines.controllers.character,
|
||||
character = fromSpaceExploration
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- We might at-least have a Surface to go back to
|
||||
if playerData.preSandboxSurfaceName and game.surfaces[playerData.preSandboxSurfaceName] then
|
||||
player.print("Unfortunately, your previous Character was lost, so it had to be recreated.")
|
||||
player.teleport(playerData.preSandboxPosition, playerData.preSandboxSurfaceName)
|
||||
local recreated = game.surfaces[playerData.preSandboxSurfaceName].create_entity {
|
||||
name = "character",
|
||||
position = playerData.preSandboxPosition,
|
||||
force = playerData.preSandboxForceName,
|
||||
raise_built = true,
|
||||
}
|
||||
player.set_controller({
|
||||
type = playerData.preSandboxController,
|
||||
character = recreated
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Otherwise, we need a completely clean slate :(
|
||||
player.print("Unfortunately, your previous Character was completely lost, so you must start anew.")
|
||||
player.teleport({ 0, 0 }, "nauvis")
|
||||
local recreated = game.surfaces["nauvis"].create_entity {
|
||||
name = "character",
|
||||
position = { 0, 0 },
|
||||
force = playerData.preSandboxForceName,
|
||||
raise_built = true,
|
||||
}
|
||||
player.set_controller({
|
||||
type = playerData.preSandboxController,
|
||||
character = recreated
|
||||
})
|
||||
end
|
||||
|
||||
-- Keep a Player's God-state, but change between Selected Sandboxes
|
||||
function Sandbox.Transfer(player)
|
||||
local playerData = global.players[player.index]
|
||||
|
||||
if not Sandbox.IsPlayerInsideSandbox(player) then
|
||||
log("Outside Sandbox, cannot transfer")
|
||||
return
|
||||
end
|
||||
|
||||
local sandboxForce = Force.GetOrCreateSandboxForce(game.forces[playerData.forceName])
|
||||
local surface = Sandbox.GetOrCreateSandboxSurface(player, sandboxForce)
|
||||
if surface == nil then
|
||||
log("Completely Unknown Sandbox Surface, cannot use")
|
||||
return
|
||||
end
|
||||
|
||||
log("Transferring to Sandbox: " .. surface.name)
|
||||
playerData.lastSandboxPositions[player.surface.name] = player.position
|
||||
player.teleport(playerData.lastSandboxPositions[surface.name] or { 0, 0 }, surface)
|
||||
|
||||
playerData.insideSandbox = playerData.selectedSandbox
|
||||
end
|
||||
|
||||
-- Update Sandboxes Player if a Player actually changes Forces (outside of this mod)
|
||||
function Sandbox.OnPlayerForceChanged(player)
|
||||
local playerData = global.players[player.index]
|
||||
local force = player.force
|
||||
if not Sandbox.IsSandboxForce(force)
|
||||
and playerData.forceName ~= force.name
|
||||
then
|
||||
log("Storing changed Player's Force: " .. player.name .. " -> " .. force.name)
|
||||
playerData.forceName = force.name
|
||||
|
||||
local sandboxForceName = Sandbox.NameFromForce(force)
|
||||
|
||||
playerData.sandboxForceName = sandboxForceName
|
||||
local labData = global.labSurfaces[playerData.labName]
|
||||
if labData then
|
||||
labData.sandboxForceName = sandboxForceName
|
||||
end
|
||||
|
||||
local labForce = Force.GetOrCreateSandboxForce(force)
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
if Sandbox.GetSandboxChoiceFor(player, player.surface) ~= Sandbox.player then
|
||||
player.print("Your Force changed, so you have been removed from a Sandbox that you are no longer allowed in")
|
||||
playerData.preSandboxForceName = force.name
|
||||
Sandbox.Exit(player)
|
||||
else
|
||||
player.force = labForce
|
||||
end
|
||||
end
|
||||
|
||||
local labSurface = game.surfaces[Lab.NameFromPlayer(player)]
|
||||
if labSurface then
|
||||
Lab.AssignEntitiesToForce(labSurface, labForce)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Determine whether the Player is inside a known Sandbox
|
||||
function Sandbox.GetSandboxChoiceFor(player, surface)
|
||||
local playerData = global.players[player.index]
|
||||
if surface.name == playerData.labName then
|
||||
return Sandbox.player
|
||||
elseif surface.name == global.sandboxForces[playerData.sandboxForceName].labName then
|
||||
return Sandbox.force
|
||||
elseif surface.name == global.sandboxForces[playerData.sandboxForceName].seOrbitalSandboxZoneName then
|
||||
return Sandbox.forceOrbitalSandbox
|
||||
elseif surface.name == global.sandboxForces[playerData.sandboxForceName].sePlanetaryLabZoneName then
|
||||
return Sandbox.forcePlanetaryLab
|
||||
elseif Factorissimo.IsFactory(surface) then
|
||||
local outsideSurface = Factorissimo.GetOutsideSurfaceForFactory(
|
||||
surface,
|
||||
player.position
|
||||
)
|
||||
if outsideSurface then
|
||||
return Sandbox.GetSandboxChoiceFor(player, outsideSurface)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Update whether the Player is inside a known Sandbox
|
||||
function Sandbox.OnPlayerSurfaceChanged(player)
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
global.players[player.index].insideSandbox = Sandbox.GetSandboxChoiceFor(player, player.surface)
|
||||
end
|
||||
end
|
||||
|
||||
-- Enter, Exit, or Transfer a Player across Sandboxes
|
||||
function Sandbox.Toggle(player_index)
|
||||
local player = game.players[player_index]
|
||||
local playerData = global.players[player.index]
|
||||
|
||||
if Factorissimo.IsFactoryInsideSandbox(player.surface, player.position) then
|
||||
player.print("You are inside of a Factory, so you cannot change Sandboxes")
|
||||
return
|
||||
end
|
||||
|
||||
if not Sandbox.IsEnabled(playerData.selectedSandbox) then
|
||||
playerData.selectedSandbox = Sandbox.player
|
||||
end
|
||||
|
||||
if Sandbox.IsPlayerInsideSandbox(player)
|
||||
and playerData.insideSandbox ~= playerData.selectedSandbox
|
||||
then
|
||||
Sandbox.Transfer(player)
|
||||
elseif Sandbox.IsPlayerInsideSandbox(player) then
|
||||
Sandbox.Exit(player)
|
||||
else
|
||||
SpaceExploration.ExitRemoteView(player)
|
||||
Sandbox.Enter(player)
|
||||
end
|
||||
end
|
||||
|
||||
return Sandbox
|
||||
51
blueprint-sandboxes/scripts/settings.lua
Normal file
51
blueprint-sandboxes/scripts/settings.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
local Settings = {}
|
||||
|
||||
Settings.scanSandboxes = BPSB.pfx .. "scan-all-chunks"
|
||||
Settings.allowAllTech = BPSB.pfx .. "allow-all-technology"
|
||||
Settings.onlyAdminsForceReset = BPSB.pfx .. "only-admins-force-reset"
|
||||
Settings.craftToCursor = BPSB.pfx .. "craft-to-cursor"
|
||||
Settings.bonusInventorySlots = BPSB.pfx .. "bonus-inventory-slots"
|
||||
Settings.extraMiningSpeed = BPSB.pfx .. "extra-mining-speed"
|
||||
Settings.extraLabSpeed = BPSB.pfx .. "extra-lab-speed"
|
||||
Settings.godAsyncTick = BPSB.pfx .. "god-async-tick"
|
||||
Settings.godAsyncCreateRequestsPerTick = BPSB.pfx .. "god-async-create-per-tick"
|
||||
Settings.godAsyncUpgradeRequestsPerTick = BPSB.pfx .. "god-async-upgrade-per-tick"
|
||||
Settings.godAsyncDeleteRequestsPerTick = BPSB.pfx .. "god-async-delete-per-tick"
|
||||
|
||||
function Settings.SetupScanSandboxes()
|
||||
if settings.global[Settings.scanSandboxes].value then
|
||||
script.on_nth_tick(Lab.chartAllLabsTick, God.ChartAllOccupiedSandboxes)
|
||||
else
|
||||
script.on_nth_tick(Lab.chartAllLabsTick, nil)
|
||||
end
|
||||
end
|
||||
|
||||
function Settings.SetupConditionalHandlers()
|
||||
Settings.SetupScanSandboxes()
|
||||
script.on_nth_tick(settings.global[Settings.godAsyncTick].value, God.HandleAllSandboxRequests)
|
||||
end
|
||||
|
||||
function Settings.OnRuntimeSettingChanged(event)
|
||||
if event.setting == Settings.scanSandboxes then
|
||||
Settings.SetupScanSandboxes()
|
||||
elseif event.setting == Settings.allowAllTech then
|
||||
Research.SyncAllForces()
|
||||
elseif event.setting == Settings.onlyAdminsForceReset then
|
||||
for _, player in pairs(game.players) do
|
||||
ToggleGUI.Update(player)
|
||||
end
|
||||
elseif event.setting == Settings.bonusInventorySlots then
|
||||
Force.SyncAllForces()
|
||||
elseif event.setting == Settings.extraMiningSpeed then
|
||||
Force.SyncAllForces()
|
||||
elseif event.setting == Settings.extraLabSpeed then
|
||||
Force.SyncAllForces()
|
||||
elseif event.setting == Settings.godAsyncTick then
|
||||
local newValue = settings.global[Settings.godAsyncTick].value
|
||||
script.on_nth_tick(global.lastSettingForAsyncGodTick, nil)
|
||||
script.on_nth_tick(newValue, God.HandleAllSandboxRequests)
|
||||
global.lastSettingForAsyncGodTick = newValue
|
||||
end
|
||||
end
|
||||
|
||||
return Settings
|
||||
328
blueprint-sandboxes/scripts/space-exploration.lua
Normal file
328
blueprint-sandboxes/scripts/space-exploration.lua
Normal file
@@ -0,0 +1,328 @@
|
||||
-- Space Exploration related functionality
|
||||
local SpaceExploration = {}
|
||||
|
||||
SpaceExploration.name = "space-exploration"
|
||||
SpaceExploration.enabled = not not remote.interfaces[SpaceExploration.name]
|
||||
|
||||
SpaceExploration.orbitalEquipmentString = "0eNqllt2OgyAQhd9lrqEB2m5bX6XZGLWjS4JoAJt1G999wW5M07T7N94hzDfDzDkJFyjNgL3TNkB2AV111kN2vIDXjS1M+hfGHiEDHbAFBrZo0woNVsHpiqNF14w8xqOriwphYqDtCd8hkxP7kVPqhi+svjO38Wp6ZYA26KDxWtS8GHM7tCW6mOAbDIO+8zGysyl3pAkGI2RcTamqO5D6xb2eAEWstxzqGl3u9UdESLF8DzKtl0za1trGLV69oQ8P8KvtnECttnNLrqdzjyFo2/h0ymHbnTEf4p6JZeIpT82NW8ENOKXuBW2+WndH5+rajs20FOSR+z7elPemCHXnWu6roq47c4J0kSeANRWgqABJBAhiPDU/tQH/n4CkakBSNSCpGpBUDUiiBiRRA5KoAUnTgCBKQBAVIIgCEMT5C9r4BW36gjZ8QZs91f5U91PNT/U+0fpE5xON/yffx5fA/OzKbl57DM7o/AxUe7nZHdTu5aDEfh1fSJ/ZLl9g"
|
||||
|
||||
-- Whether the Surface has been taken as a Space Sandbox
|
||||
function SpaceExploration.IsSandbox(surface)
|
||||
return SpaceExploration.enabled
|
||||
and global.seSurfaces[surface.name]
|
||||
end
|
||||
|
||||
-- Whether the Surface has been taken as a Planetary Lab Sandbox
|
||||
function SpaceExploration.IsPlanetarySandbox(surface)
|
||||
return SpaceExploration.enabled
|
||||
and global.seSurfaces[surface.name]
|
||||
and not global.seSurfaces[surface.name].orbital
|
||||
end
|
||||
|
||||
-- Whether the Zone is Star
|
||||
function SpaceExploration.IsStar(zoneName)
|
||||
if not SpaceExploration.enabled then
|
||||
return false
|
||||
end
|
||||
return remote.call(SpaceExploration.name, "get_zone_from_name", {
|
||||
zone_name = zoneName,
|
||||
}).type == "star"
|
||||
end
|
||||
|
||||
-- Ask Space Exploration for the Player's current Character
|
||||
function SpaceExploration.GetPlayerCharacter(player)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
return remote.call(SpaceExploration.name, "get_player_character", {
|
||||
player = player,
|
||||
})
|
||||
end
|
||||
|
||||
-- Whether the Sandbox might have Biters falling
|
||||
function SpaceExploration.IsZoneThreatening(zone)
|
||||
return (zone.type == "planet" or zone.type == "moon")
|
||||
and zone.controls
|
||||
and zone.controls["se-vitamelange"]
|
||||
and zone.controls["se-vitamelange"].richness > 0
|
||||
end
|
||||
|
||||
-- Walk Parent Indexes to find the Root Zone (Star)
|
||||
function SpaceExploration.GetRootZone(zoneIndex, zone)
|
||||
local rootZone = zone
|
||||
while rootZone.parent_index do
|
||||
rootZone = zoneIndex[rootZone.parent_index]
|
||||
end
|
||||
return rootZone
|
||||
end
|
||||
|
||||
-- Chooses a non-home-system Star or Moon for a Force's Space Sandbox, if necessary
|
||||
-- Notably, Star _Orbits_ are "usable" Zones, but not Stars themselves
|
||||
-- In other words, these should be completely safe and invisible outside of this mod!
|
||||
-- Moons, on the other hand, will take a valuable resource away from the player
|
||||
-- We also carefully choose Moons in order to not take away too much from them,
|
||||
-- and to not be too dangerous.
|
||||
function SpaceExploration.ChooseZoneForForce(player, sandboxForce, type)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local zoneIndex = remote.call(SpaceExploration.name, "get_zone_index", {})
|
||||
for _, zone in pairs(zoneIndex) do
|
||||
if zone.type == type
|
||||
and not zone.is_homeworld
|
||||
and not zone.ruins
|
||||
and not zone.glyph
|
||||
and zone.special_type ~= "homesystem"
|
||||
and not global.seSurfaces[zone.name]
|
||||
then
|
||||
local rootZone = SpaceExploration.GetRootZone(zoneIndex, zone)
|
||||
if not SpaceExploration.IsZoneThreatening(zone)
|
||||
and rootZone.special_type ~= "homesystem"
|
||||
then
|
||||
log("Choosing SE Zone " .. zone.name .. " as Sandbox for " .. sandboxForce.name)
|
||||
return zone.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SpaceExploration.GetOrCreateSurface(zoneName)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
return remote.call(SpaceExploration.name, "zone_get_make_surface", {
|
||||
zone_index = remote.call(SpaceExploration.name, "get_zone_from_name", {
|
||||
zone_name = zoneName,
|
||||
}).index,
|
||||
})
|
||||
end
|
||||
|
||||
-- Chooses a non-home-system Star for a Force's Space Sandbox, if necessary
|
||||
function SpaceExploration.GetOrCreatePlanetarySurfaceForForce(player, sandboxForce)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local zoneName = global.sandboxForces[sandboxForce.name].sePlanetaryLabZoneName
|
||||
if zoneName == nil then
|
||||
zoneName = SpaceExploration.ChooseZoneForForce(player, sandboxForce, "moon")
|
||||
global.sandboxForces[sandboxForce.name].sePlanetaryLabZoneName = zoneName
|
||||
global.seSurfaces[zoneName] = {
|
||||
sandboxForceName = sandboxForce.name,
|
||||
equipmentBlueprints = Equipment.Init(Lab.equipmentString),
|
||||
daytime = 0.95,
|
||||
orbital = false,
|
||||
}
|
||||
end
|
||||
|
||||
return SpaceExploration.GetOrCreateSurface(zoneName)
|
||||
end
|
||||
|
||||
-- Chooses a non-home-system Star for a Force's Planetary Sandbox, if necessary
|
||||
function SpaceExploration.GetOrCreateOrbitalSurfaceForForce(player, sandboxForce)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local zoneName = global.sandboxForces[sandboxForce.name].seOrbitalSandboxZoneName
|
||||
if zoneName == nil then
|
||||
zoneName = SpaceExploration.ChooseZoneForForce(player, sandboxForce, "star")
|
||||
global.sandboxForces[sandboxForce.name].seOrbitalSandboxZoneName = zoneName
|
||||
global.seSurfaces[zoneName] = {
|
||||
sandboxForceName = sandboxForce.name,
|
||||
equipmentBlueprints = Equipment.Init(SpaceExploration.orbitalEquipmentString),
|
||||
daytime = 0.95,
|
||||
orbital = true,
|
||||
}
|
||||
end
|
||||
|
||||
return SpaceExploration.GetOrCreateSurface(zoneName)
|
||||
end
|
||||
|
||||
-- Set a Sandbox's Daytime to a specific value
|
||||
function SpaceExploration.SetDayTime(player, surface, daytime)
|
||||
if SpaceExploration.IsSandbox(surface) then
|
||||
surface.freeze_daytime = true
|
||||
surface.daytime = daytime
|
||||
global.seSurfaces[surface.name].daytime = daytime
|
||||
Events.SendDaylightChangedEvent(player.index, surface.name, daytime)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset the Sandbox's equipment Blueprint for a Surface
|
||||
function SpaceExploration.ResetEquipmentBlueprint(surface)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if SpaceExploration.IsSandbox(surface) then
|
||||
log("Resetting SE equipment: " .. surface.name)
|
||||
if global.seSurfaces[surface.name].orbital then
|
||||
Equipment.Set(
|
||||
global.seSurfaces[surface.name].equipmentBlueprints,
|
||||
SpaceExploration.orbitalEquipmentString
|
||||
)
|
||||
else
|
||||
Equipment.Set(
|
||||
global.seSurfaces[surface.name].equipmentBlueprints,
|
||||
Lab.equipmentString
|
||||
)
|
||||
end
|
||||
surface.print("The equipment Blueprint for this Sandbox has been reset")
|
||||
return true
|
||||
else
|
||||
log("Not a SE Sandbox, won't Reset equipment: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the Sandbox's equipment Blueprint for a Surface
|
||||
function SpaceExploration.SetEquipmentBlueprint(surface, equipmentString)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if SpaceExploration.IsSandbox(surface) then
|
||||
log("Setting SE equipment: " .. surface.name)
|
||||
Equipment.Set(
|
||||
global.seSurfaces[surface.name].equipmentBlueprints,
|
||||
equipmentString
|
||||
)
|
||||
surface.print("The equipment Blueprint for this Sandbox has been changed")
|
||||
return true
|
||||
else
|
||||
log("Not a SE Sandbox, won't Set equipment: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset the Space Sandbox a Player is currently in
|
||||
function SpaceExploration.Reset(player)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if SpaceExploration.IsSandbox(player.surface) then
|
||||
log("Resetting SE Sandbox: " .. player.surface.name)
|
||||
player.teleport({ 0, 0 }, player.surface.name)
|
||||
player.surface.clear(false)
|
||||
return true
|
||||
else
|
||||
log("Not a SE Sandbox, won't Reset: " .. player.surface.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Return a Sandbox to the available Zones
|
||||
function SpaceExploration.PreDeleteSandbox(sandboxForceData, zoneName)
|
||||
if not SpaceExploration.enabled or not zoneName then
|
||||
return
|
||||
end
|
||||
|
||||
if global.seSurfaces[zoneName] then
|
||||
log("Pre-Deleting SE Sandbox: " .. zoneName)
|
||||
local equipmentBlueprints = global.seSurfaces[zoneName].equipmentBlueprints
|
||||
if equipmentBlueprints and equipmentBlueprints.valid() then
|
||||
equipmentBlueprints.destroy()
|
||||
end
|
||||
global.seSurfaces[zoneName] = nil
|
||||
if sandboxForceData.sePlanetaryLabZoneName == zoneName then
|
||||
sandboxForceData.sePlanetaryLabZoneName = nil
|
||||
end
|
||||
if sandboxForceData.seOrbitalSandboxZoneName == zoneName then
|
||||
sandboxForceData.seOrbitalSandboxZoneName = nil
|
||||
end
|
||||
else
|
||||
log("Not a SE Sandbox, won't Pre-Delete: " .. zoneName)
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete a Space Sandbox and return it to the available Zones
|
||||
function SpaceExploration.DeleteSandbox(sandboxForceData, zoneName)
|
||||
if not SpaceExploration.enabled or not zoneName then
|
||||
return
|
||||
end
|
||||
|
||||
if global.seSurfaces[zoneName] then
|
||||
SpaceExploration.PreDeleteSandbox(sandboxForceData, zoneName)
|
||||
log("Deleting SE Sandbox: " .. zoneName)
|
||||
game.delete_surface(zoneName)
|
||||
return true
|
||||
else
|
||||
log("Not a SE Sandbox, won't Delete: " .. zoneName)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Set some important Surface settings for Space Sandbox
|
||||
function SpaceExploration.AfterCreate(surface)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local surfaceData = global.seSurfaces[surface.name]
|
||||
if not surfaceData then
|
||||
log("Not a SE Sandbox, won't handle Creation: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
|
||||
log("Handling Creation of SE Sandbox: " .. surface.name)
|
||||
|
||||
surface.freeze_daytime = true
|
||||
surface.daytime = surfaceData.daytime
|
||||
surface.show_clouds = false
|
||||
|
||||
if (surfaceData.orbital) then
|
||||
surface.generate_with_lab_tiles = false
|
||||
else
|
||||
surface.generate_with_lab_tiles = true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Add some helpful initial Entities to a Space Sandbox
|
||||
function SpaceExploration.Equip(surface)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local surfaceData = global.seSurfaces[surface.name]
|
||||
if not surfaceData then
|
||||
log("Not a SE Sandbox, won't Equip: " .. surface.name)
|
||||
return false
|
||||
end
|
||||
|
||||
log("Equipping SE Sandbox: " .. surface.name)
|
||||
|
||||
if not surfaceData.orbital then
|
||||
surface.generate_with_lab_tiles = true
|
||||
end
|
||||
Equipment.Place(
|
||||
surfaceData.equipmentBlueprints[1],
|
||||
surface,
|
||||
surfaceData.sandboxForceName
|
||||
)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[ Ensure that NavSat is not active
|
||||
NOTE: This was not necessary in SE < 0.5.109 (the NavSat QoL Update)
|
||||
Now, without this, the Inventory-differences after entering a Sandbox while
|
||||
in the Navigation Satellite would be persisted, and without any good way
|
||||
to undo that override.
|
||||
--]]
|
||||
function SpaceExploration.ExitRemoteView(player)
|
||||
if not SpaceExploration.enabled then
|
||||
return
|
||||
end
|
||||
remote.call(SpaceExploration.name, "remote_view_stop", { player = player })
|
||||
end
|
||||
|
||||
return SpaceExploration
|
||||
18
blueprint-sandboxes/scripts/tiles.lua
Normal file
18
blueprint-sandboxes/scripts/tiles.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
-- Custom Planners to add/remove Resources
|
||||
local Tiles = {}
|
||||
|
||||
Tiles.name = BPSB.pfx .. "sandbox-tiles"
|
||||
Tiles.pfx = BPSB.pfx .. "sbt-"
|
||||
local pfxLength = string.len(Tiles.pfx)
|
||||
|
||||
-- Whether the Thing is a Tile Planner
|
||||
function Tiles.IsTilePlanner(name)
|
||||
return string.sub(name, 1, pfxLength) == Tiles.pfx
|
||||
end
|
||||
|
||||
-- Extract the Resource Name from a Tile Planner
|
||||
function Tiles.GetResourceName(name)
|
||||
return string.sub(name, pfxLength + 1)
|
||||
end
|
||||
|
||||
return Tiles
|
||||
186
blueprint-sandboxes/scripts/toggle-gui.lua
Normal file
186
blueprint-sandboxes/scripts/toggle-gui.lua
Normal file
@@ -0,0 +1,186 @@
|
||||
local ToggleGUI = {}
|
||||
|
||||
ToggleGUI.name = BPSB.pfx .. "toggle-gui"
|
||||
ToggleGUI.pfx = ToggleGUI.name .. "-"
|
||||
ToggleGUI.toggleShortcut = ToggleGUI.pfx .. "sb-toggle-shortcut"
|
||||
ToggleGUI.selectedSandboxDropdown = ToggleGUI.pfx .. "selected-sandbox-dropdown"
|
||||
ToggleGUI.resetButton = ToggleGUI.pfx .. "reset-button"
|
||||
ToggleGUI.daytimeSlider = ToggleGUI.pfx .. "daytime-slider"
|
||||
|
||||
function ToggleGUI.Init(player)
|
||||
if player.gui.left[ToggleGUI.name] then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = player.gui.left.add {
|
||||
type = "frame",
|
||||
name = ToggleGUI.name,
|
||||
caption = { "gui." .. ToggleGUI.name },
|
||||
visible = false,
|
||||
}
|
||||
|
||||
local innerFrame = frame.add {
|
||||
type = "frame",
|
||||
name = "innerFrame",
|
||||
direction = "vertical",
|
||||
style = "inside_shallow_frame_with_padding",
|
||||
}
|
||||
|
||||
local topLineFlow = innerFrame.add {
|
||||
type = "flow",
|
||||
name = "topLineFlow",
|
||||
direction = "horizontal",
|
||||
style = BPSB.pfx .. "centered-horizontal-flow",
|
||||
}
|
||||
|
||||
topLineFlow.add {
|
||||
type = "sprite-button",
|
||||
name = ToggleGUI.resetButton,
|
||||
tooltip = { "gui-description." .. ToggleGUI.resetButton },
|
||||
style = "tool_button",
|
||||
sprite = "utility/reset_white",
|
||||
}
|
||||
|
||||
topLineFlow.add {
|
||||
type = "drop-down",
|
||||
name = ToggleGUI.selectedSandboxDropdown,
|
||||
tooltip = { "gui-description." .. ToggleGUI.selectedSandboxDropdown },
|
||||
items = Sandbox.choices,
|
||||
selected_index = global.players[player.index].selectedSandbox,
|
||||
}.style.horizontally_stretchable = true
|
||||
|
||||
local daylightFlow = innerFrame.add {
|
||||
type = "flow",
|
||||
name = "daylightFlow",
|
||||
direction = "horizontal",
|
||||
style = BPSB.pfx .. "centered-horizontal-flow",
|
||||
}
|
||||
|
||||
daylightFlow.add {
|
||||
type = "sprite",
|
||||
tooltip = { "gui-description." .. ToggleGUI.daytimeSlider },
|
||||
sprite = "utility/select_icon_white",
|
||||
resize_to_sprite = false,
|
||||
style = BPSB.pfx .. "sprite-like-tool-button",
|
||||
}
|
||||
|
||||
daylightFlow.add {
|
||||
type = "slider",
|
||||
name = ToggleGUI.daytimeSlider,
|
||||
value = 0.0,
|
||||
minimum_value = 0.5,
|
||||
maximum_value = 0.975,
|
||||
value_step = 0.025,
|
||||
style = "notched_slider",
|
||||
}.style.horizontally_stretchable = true
|
||||
|
||||
ToggleGUI.Update(player)
|
||||
end
|
||||
|
||||
function ToggleGUI.Destroy(player)
|
||||
if not player.gui.left[ToggleGUI.name] then
|
||||
return
|
||||
end
|
||||
player.gui.left[ToggleGUI.name].destroy()
|
||||
end
|
||||
|
||||
function ToggleGUI.FindDescendantByName(instance, name)
|
||||
for _, child in pairs(instance.children) do
|
||||
if child.name == name then
|
||||
return child
|
||||
end
|
||||
local found = ToggleGUI.FindDescendantByName(child, name)
|
||||
if found then return found end
|
||||
end
|
||||
end
|
||||
|
||||
function ToggleGUI.FindByName(player, name)
|
||||
return ToggleGUI.FindDescendantByName(player.gui.left[ToggleGUI.name], name)
|
||||
end
|
||||
|
||||
function ToggleGUI.Update(player)
|
||||
if not player.gui.left[ToggleGUI.name] then
|
||||
return
|
||||
end
|
||||
|
||||
ToggleGUI.FindByName(player, ToggleGUI.selectedSandboxDropdown).selected_index = global.players[player.index].selectedSandbox
|
||||
|
||||
if Sandbox.IsPlayerInsideSandbox(player) then
|
||||
local playerData = global.players[player.index]
|
||||
|
||||
player.set_shortcut_toggled(ToggleGUI.toggleShortcut, true)
|
||||
player.gui.left[ToggleGUI.name].visible = true
|
||||
|
||||
local resetButton = ToggleGUI.FindByName(player, ToggleGUI.resetButton)
|
||||
if game.is_multiplayer
|
||||
and not player.admin
|
||||
and playerData.selectedSandbox ~= Sandbox.player
|
||||
and settings.global[Settings.onlyAdminsForceReset].value
|
||||
then
|
||||
resetButton.enabled = false
|
||||
resetButton.tooltip = { "gui-description." .. ToggleGUI.resetButton .. "-only-admins" }
|
||||
else
|
||||
resetButton.enabled = true
|
||||
resetButton.tooltip = { "gui-description." .. ToggleGUI.resetButton }
|
||||
end
|
||||
|
||||
ToggleGUI.FindByName(player, ToggleGUI.daytimeSlider).slider_value = player.surface.daytime
|
||||
else
|
||||
player.set_shortcut_toggled(ToggleGUI.toggleShortcut, false)
|
||||
player.gui.left[ToggleGUI.name].visible = false
|
||||
ToggleGUI.FindByName(player, ToggleGUI.resetButton).enabled = false
|
||||
end
|
||||
end
|
||||
|
||||
function ToggleGUI.OnGuiValueChanged(event)
|
||||
local player = game.players[event.player_index]
|
||||
if event.element.name == ToggleGUI.daytimeSlider then
|
||||
local daytime = event.element.slider_value
|
||||
return Lab.SetDayTime(player, player.surface, daytime)
|
||||
or SpaceExploration.SetDayTime(player, player.surface, daytime)
|
||||
end
|
||||
end
|
||||
|
||||
function ToggleGUI.OnGuiDropdown(event)
|
||||
local player = game.players[event.player_index]
|
||||
if event.element.name == ToggleGUI.selectedSandboxDropdown then
|
||||
local choice = event.element.selected_index
|
||||
if Sandbox.IsEnabled(choice) then
|
||||
global.players[player.index].selectedSandbox = event.element.selected_index
|
||||
Sandbox.Toggle(event.player_index)
|
||||
else
|
||||
player.print("That Sandbox is not possible.")
|
||||
event.element.selected_index = global.players[player.index].selectedSandbox
|
||||
ToggleGUI.Update(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ToggleGUI.OnGuiClick(event)
|
||||
local player = game.players[event.player_index]
|
||||
if event.element.name == ToggleGUI.toggleShortcut then
|
||||
Sandbox.Toggle(event.player_index)
|
||||
elseif event.element.name == ToggleGUI.resetButton then
|
||||
if event.shift then
|
||||
return Lab.ResetEquipmentBlueprint(player.surface)
|
||||
or SpaceExploration.ResetEquipmentBlueprint(player.surface)
|
||||
else
|
||||
local blueprintString = Inventory.GetCursorBlueprintString(player)
|
||||
if blueprintString then
|
||||
return Lab.SetEquipmentBlueprint(player.surface, blueprintString)
|
||||
or SpaceExploration.SetEquipmentBlueprint(player.surface, blueprintString)
|
||||
else
|
||||
return Lab.Reset(player)
|
||||
or SpaceExploration.Reset(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ToggleGUI.OnToggleShortcut(event)
|
||||
if (event.input_name or event.prototype_name) == ToggleGUI.toggleShortcut then
|
||||
Sandbox.Toggle(event.player_index)
|
||||
end
|
||||
end
|
||||
|
||||
return ToggleGUI
|
||||
Reference in New Issue
Block a user