My mod adds a custom entity with its extra data stored in blueprint tags (saved via on_player_setup_blueprint, restored in on_built_entity). This works great except for one case: if a player places a blueprint ghost, then places a different ghost on top of that ghost, the tags given when the ghost is "made real" are the tags from the first ghost. Further, on_built_entity is called for the first ghost placement (and for the subsequent event when the entity itself is built from the ghost) - but *not* when the player places a different ghost on top of the first one. I can't find a way to even be notified that a "ghost replacement" has occurred.
In case that was confusing, here's the specific use case I have:
1. Player copies my custom entity - a chest with an "iron-plate" filter which is then stored in blueprint tags.
2. Player pastes blueprint ghost somewhere.
3. Player copies another entity, say chest with "copper-plate" filter.
4. Player pastes blueprint2 on top of the first.
5. Player builds entity.
What I want to happen in step 5 is the entity is built with the "copper-plate" filter. Instead, it's built with the "iron-plate" filter because the tags are from the first blueprint.
Extra entity data in blueprint tags not overridden on ghost replacement
Re: Extra entity data in blueprint tags not overridden on ghost replacement
I have found a solution to my problem. It would be nice if there were a simpler way (hoping I'm missing something), or if the engine simply replaced the ghost tags automatically, but at least there I have a solution. The method I've found is to use the on_pre_build event to know when a blueprint is about to be placed on top of existing ghosts. Then a bunch of math is necessary to find the actual in-game positions of the entities I want to override, and destroy those entities before the game places the new ones. Here's what the code looks like (with liberal use of Factorio-Stdlib mod):
Code: Select all
function rotateAndFlip(pos, dir, flipH, flipV)
if flipH then pos.x = -pos.x end
if flipV then pos.y = -pos.y end
if dir == defines.direction.north then return pos end
if dir == defines.direction.west then return Position.construct(pos.y, -pos.x) end
if dir == defines.direction.south then return Position.construct(-pos.x, -pos.y) end
if dir == defines.direction.east then return Position.construct(-pos.y, pos.x) end
game.print("Warning: unexpected blueprint rotation " .. dir ". Chest filters will be incorrect.")
return pos
end
script.on_event(defines.events.on_pre_build, function(event)
local player = game.players[event.player_index]
if not player.is_cursor_blueprint() or global.lastPreBuildTick == event.tick then return end
global.lastPreBuildTick = event.tick
-- Find the blueprint's bounding box.
local positions = {}
table.each(player.get_blueprint_entities(), function(v) table.insert(positions, Position.new(v.position)) end)
local leftTop = Position.min_xy(positions)
local rightBottom = Position.max_xy(positions)
local bbox = Area.new{leftTop, rightBottom}
local negCenter = bbox:center():flip()
local bboxSize = bbox:offset(negCenter) -- `bbox - bbox.center`
-- Maybe rotate the bbox.
local area = bboxSize:offset(event.position):ceil()
if event.direction == defines.direction.east or event.direction == defines.direction.west then
area = area:flip()
end
-- Find the positions where the blueprint *would* place the relevant entities.
local bpEntityPositions = {}
table.each(player.get_blueprint_entities(), function(v)
if v.name == entityName then
local bpPos = rotateAndFlip(Position.new(v.position):add(negCenter), event.direction, event.flip_horizontal, event.flip_vertical)
local pos = bpPos:add(event.position):center()
table.insert(bpEntityPositions, pos)
end
end)