diff --git a/docs/Lua API.rst b/docs/Lua API.rst index d00acd271..78878beda 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -1172,13 +1172,18 @@ Units module Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged. -* ``dfhack.getUnitsInBox(x1,y1,z1,x2,y2,z2[,filter])`` +* ``dfhack.units.getUnitsInBox(x1,y1,z1,x2,y2,z2[,filter])`` Returns a table of all units within the specified coordinates. If the ``filter`` argument is given, only units where ``filter(unit)`` returns true will be included. Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to the arguments required by this function. +* ``dfhack.units.teleport(unit, pos)`` + + Moves the specified unit and any riders to the target coordinates, setting + tile occupancy flags appropriately. Returns true if successful. + * ``dfhack.units.getGeneralRef(unit, type)`` Searches for a general_ref with the given type. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 1c115471a..11346ebaa 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1572,6 +1572,7 @@ static const luaL_Reg dfhack_job_funcs[] = { /***** Units module *****/ static const LuaWrapper::FunctionReg dfhack_units_module[] = { + WRAPM(Units, teleport), WRAPM(Units, getGeneralRef), WRAPM(Units, getSpecificRef), WRAPM(Units, getContainer), diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 4a8452203..419a7b0db 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -82,6 +82,9 @@ DFHACK_EXPORT int32_t findIndexById(int32_t id); /// Returns the true position of the unit (non-trivial in case of caged). DFHACK_EXPORT df::coord getPosition(df::unit *unit); +// moves unit and any riders to the target coordinates +DFHACK_EXPORT bool teleport(df::unit *unit, df::coord target_pos); + DFHACK_EXPORT df::general_ref *getGeneralRef(df::unit *unit, df::general_ref_type type); DFHACK_EXPORT df::specific_ref *getSpecificRef(df::unit *unit, df::specific_ref_type type); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 7edf90817..58cd24d44 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -71,6 +71,7 @@ using namespace std; #include "df/job.h" #include "df/nemesis_record.h" #include "df/squad.h" +#include "df/tile_occupancy.h" #include "df/ui.h" #include "df/unit_inventory_item.h" #include "df/unit_misc_trait.h" @@ -144,6 +145,49 @@ df::coord Units::getPosition(df::unit *unit) return unit->pos; } +bool Units::teleport(df::unit *unit, df::coord target_pos) +{ + // make sure source and dest map blocks are valid + auto old_occ = Maps::getTileOccupancy(unit->pos); + auto new_occ = Maps::getTileOccupancy(target_pos); + if (!old_occ || !new_occ) + return false; + + // clear appropriate occupancy flags at old tile + if (unit->flags1.bits.on_ground) + // this is potentially wrong, but the game will recompute this as needed + old_occ->bits.unit_grounded = 0; + else + old_occ->bits.unit = 0; + + // if there's already somebody standing at the destination, then force the + // unit to lay down + if (new_occ->bits.unit) + unit->flags1.bits.on_ground = 1; + + // set appropriate occupancy flags at new tile + if (unit->flags1.bits.on_ground) + new_occ->bits.unit_grounded = 1; + else + new_occ->bits.unit = 1; + + // move unit to destination + unit->pos = target_pos; + + // move unit's riders (including babies) to destination + if (unit->flags1.bits.ridden) + { + for (size_t j = 0; j < world->units.other[units_other_id::ANY_RIDER].size(); j++) + { + df::unit *rider = world->units.other[units_other_id::ANY_RIDER][j]; + if (rider->relationship_ids[df::unit_relationship_type::RiderMount] == unit->id) + rider->pos = unit->pos; + } + } + + return true; +} + df::general_ref *Units::getGeneralRef(df::unit *unit, df::general_ref_type type) { CHECK_NULL_POINTER(unit); diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index feddb5546..82e9d39dd 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -54,7 +54,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (!Units::isCitizen(unit)) continue; - if (enable_teledwarf) do + if (enable_teledwarf) { // skip dwarves that are dragging creatures or being dragged if ((unit->relationship_ids[df::unit_relationship_type::Draggee] != -1) || @@ -74,44 +74,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) break; } - // make sure source and dest map blocks are valid - auto old_occ = Maps::getTileOccupancy(unit->pos); - auto new_occ = Maps::getTileOccupancy(unit->path.dest); - if (!old_occ || !new_occ) + if (!Units::teleport(unit, unit->path.dest)) break; - // clear appropriate occupancy flags at old tile - if (unit->flags1.bits.on_ground) - // this is technically wrong, but the game will recompute this as needed - old_occ->bits.unit_grounded = 0; - else - old_occ->bits.unit = 0; - - // if there's already somebody standing at the destination, then force the unit to lay down - if (new_occ->bits.unit) - unit->flags1.bits.on_ground = 1; - - // set appropriate occupancy flags at new tile - if (unit->flags1.bits.on_ground) - new_occ->bits.unit_grounded = 1; - else - new_occ->bits.unit = 1; - - // move unit to destination - unit->pos = unit->path.dest; unit->path.path.clear(); - - //move unit's riders(including babies) to destination - if (unit->flags1.bits.ridden) - { - for (size_t j = 0; j < world->units.other[units_other_id::ANY_RIDER].size(); j++) - { - df::unit *rider = world->units.other[units_other_id::ANY_RIDER][j]; - if (rider->relationship_ids[df::unit_relationship_type::RiderMount] == unit->id) - rider->pos = unit->pos; - } - } - } while (0); + } if (enable_fastdwarf) {