I hacked together a script that tries to select only merchants and seems to use the same basic logic as lethosor does (which I didn't see until posting). Since I don't know how to call other scripts I brute forced it by including the teleport script into it...
It's been marginally tested on a normal caravan, where it only teleported the units that had entered the map, but if the stuck units in the bug sort of have entered it ought to work.
The differences from lethosor's logic is that it's a script, so it might be more convenient to call than copy/pasting a command, and I've also added an additional check to exclude dead units (trying to avoid homicidal players' previous exploits from being teleported around: not tested, as I'm not intentionally killing merchants).
Anyway, always make a backup before trying.
-- Teleports merchants to the selected position. Incorporates the teleport script.
--[====[
teleportmerchants
=================
teleport
========
-- teleports a unit to a location
-- author Putnam
-- edited by expwnent
]====]
function teleport(unit,pos)
local unitoccupancy = dfhack.maps.getTileBlock(unit.pos).occupancy[unit.pos.x%16][unit.pos.y%16]
local newoccupancy = dfhack.maps.getTileBlock(pos).occupancy[pos.x%16][pos.y%16]
if newoccupancy.unit then
unit.flags1.on_ground=true
end
unit.pos.x = pos.x
unit.pos.y = pos.y
unit.pos.z = pos.z
if not unit.flags1.on_ground then unitoccupancy.unit = false else unitoccupancy.unit_grounded = false end
end
utils = require('utils')
validArgs = validArgs or utils.invert({
'unit',
'x',
'y',
'z',
'showunitid',
'showpos'
})
if moduleMode then
return
end
--local args = utils.processArgs({...}, validArgs)
--if args.showunitid or args.showpos then
-- if args.showunitid then
-- print(dfhack.gui.getSelectedUnit(true).id)
-- else
-- printall(df.global.cursor)
-- end
--else
-- local unit = args.unit and df.unit.find(args.unit) or dfhack.gui.getSelectedUnit(true)
-- local pos = not(not args.x or not args.y or not args.z) and {x=args.x,y=args.y,z=args.z} or {x=df.global.cursor.x,y=df.global.cursor.y,z=df.global.cursor.z}
-- if not unit then qerror('A unit needs to be selected or specified. Use teleport -showunitid to get a unit\'s ID.') end
-- if not pos.x or pos.x==-30000 then qerror('A position needs to be highlighted or specified. Use teleport -showpos to get a position\'s exact xyz values.') end
-- teleport(unit,pos)
--end
function teleportmerchants ()
local pos
if not df.global.cursor.x or df.global.cursor.x == -30000 then
qerror("A position needs to be highlighted")
else
pos = {x=df.global.cursor.x,y=df.global.cursor.y,z=df.global.cursor.z}
end
for i = 0, #df.global.world.units.active - 1 do
if df.global.world.units.active [i].flags1.merchant and
not df.global.world.units.active [i].flags1.dead then
dfhack.println ("Teleporting unit: " .. tostring (df.global.world.units.active [i].id) .. " index: " .. tostring (i))
teleport (df.unit.find (df.global.world.units.active [i].id), pos)
end
end
end
teleportmerchants ()
Edit: By the way, my unit id:s for the merchants were over 30000, so a low range brute force approach can easily miss the merchants.