Добавлен мод blueprint-sandboxes Добавляет метку версии в zzzparanoidal (#72)
210 lines
7.8 KiB
Lua
210 lines
7.8 KiB
Lua
-- 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
|