Merge branch 'master' of https://github.com/angavrilov/dfhack
commit
a56f1549a9
@ -1,26 +1,21 @@
|
||||
inline bool getassignment( const df::coord2d &xy )
|
||||
{
|
||||
return getassignment(xy.x,xy.y);
|
||||
return tile_bitmask.getassignment(xy);
|
||||
}
|
||||
inline bool getassignment( int x, int y )
|
||||
{
|
||||
return (tile_bitmask[y] & (1 << x));
|
||||
return tile_bitmask.getassignment(x,y);
|
||||
}
|
||||
inline void setassignment( const df::coord2d &xy, bool bit )
|
||||
{
|
||||
return setassignment(xy.x,xy.y, bit);
|
||||
return tile_bitmask.setassignment(xy, bit);
|
||||
}
|
||||
inline void setassignment( int x, int y, bool bit )
|
||||
{
|
||||
if(bit)
|
||||
tile_bitmask[y] |= (1 << x);
|
||||
else
|
||||
tile_bitmask[y] &= ~(1 << x);
|
||||
return tile_bitmask.setassignment(x, y, bit);
|
||||
}
|
||||
bool has_assignments()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (tile_bitmask[i])
|
||||
return true;
|
||||
return false;
|
||||
return tile_bitmask.has_assignments();
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,20 @@
|
||||
inline bool getassignment( const df::coord2d &xy )
|
||||
{
|
||||
return getassignment(xy.x,xy.y);
|
||||
return tile_bitmask.getassignment(xy);
|
||||
}
|
||||
inline bool getassignment( int x, int y )
|
||||
{
|
||||
return (tile_bitmask[y] & (1 << x));
|
||||
return tile_bitmask.getassignment(x,y);
|
||||
}
|
||||
inline void setassignment( const df::coord2d &xy, bool bit )
|
||||
{
|
||||
return setassignment(xy.x,xy.y, bit);
|
||||
return tile_bitmask.setassignment(xy, bit);
|
||||
}
|
||||
inline void setassignment( int x, int y, bool bit )
|
||||
{
|
||||
if(bit)
|
||||
tile_bitmask[y] |= (1 << x);
|
||||
else
|
||||
tile_bitmask[y] &= ~(1 << x);
|
||||
return tile_bitmask.setassignment(x, y, bit);
|
||||
}
|
||||
bool has_assignments()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (tile_bitmask[i])
|
||||
return true;
|
||||
return false;
|
||||
return tile_bitmask.has_assignments();
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
inline uint16_t &operator[] (int y)
|
||||
{
|
||||
return bits[y];
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
memset(bits,0,sizeof(bits));
|
||||
}
|
||||
void set_all()
|
||||
{
|
||||
memset(bits,0xFF,sizeof(bits));
|
||||
}
|
||||
inline bool getassignment( const df::coord2d &xy )
|
||||
{
|
||||
return getassignment(xy.x,xy.y);
|
||||
}
|
||||
inline bool getassignment( int x, int y )
|
||||
{
|
||||
return (bits[y] & (1 << x));
|
||||
}
|
||||
inline void setassignment( const df::coord2d &xy, bool bit )
|
||||
{
|
||||
return setassignment(xy.x,xy.y, bit);
|
||||
}
|
||||
inline void setassignment( int x, int y, bool bit )
|
||||
{
|
||||
if(bit)
|
||||
bits[y] |= (1 << x);
|
||||
else
|
||||
bits[y] &= ~(1 << x);
|
||||
}
|
||||
bool has_assignments()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (bits[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "Export.h"
|
||||
#include "DataDefs.h"
|
||||
#include "modules/Maps.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* \defgroup grp_burrows Burrows module and its types
|
||||
* @ingroup grp_modules
|
||||
*/
|
||||
|
||||
namespace df
|
||||
{
|
||||
struct unit;
|
||||
struct burrow;
|
||||
struct block_burrow;
|
||||
}
|
||||
|
||||
namespace DFHack
|
||||
{
|
||||
namespace Burrows
|
||||
{
|
||||
DFHACK_EXPORT df::burrow *findByName(std::string name);
|
||||
|
||||
// Units
|
||||
DFHACK_EXPORT void clearUnits(df::burrow *burrow);
|
||||
|
||||
DFHACK_EXPORT bool isAssignedUnit(df::burrow *burrow, df::unit *unit);
|
||||
DFHACK_EXPORT void setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable);
|
||||
|
||||
// Tiles
|
||||
DFHACK_EXPORT void clearTiles(df::burrow *burrow);
|
||||
|
||||
DFHACK_EXPORT void listBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow);
|
||||
|
||||
DFHACK_EXPORT bool isAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile);
|
||||
DFHACK_EXPORT bool setAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable);
|
||||
|
||||
inline bool isAssignedTile(df::burrow *burrow, df::coord tile) {
|
||||
return isAssignedBlockTile(burrow, Maps::getTileBlock(tile), tile);
|
||||
}
|
||||
inline bool setAssignedTile(df::burrow *burrow, df::coord tile, bool enable) {
|
||||
return setAssignedBlockTile(burrow, Maps::getTileBlock(tile), tile, enable);
|
||||
}
|
||||
|
||||
DFHACK_EXPORT df::block_burrow *getBlockMask(df::burrow *burrow, df::map_block *block, bool create = false);
|
||||
DFHACK_EXPORT bool deleteBlockMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask);
|
||||
|
||||
inline bool deleteBlockMask(df::burrow *burrow, df::map_block *block) {
|
||||
return deleteBlockMask(burrow, block, getBlockMask(burrow, block));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
local _ENV = mkmodule('utils')
|
||||
|
||||
-- Comparator function
|
||||
function compare(a,b)
|
||||
if a < b then
|
||||
return -1
|
||||
elseif a > b then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort strings; compare empty last
|
||||
function compare_name(a,b)
|
||||
if a == '' then
|
||||
if b == '' then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
elseif b == '' then
|
||||
return -1
|
||||
else
|
||||
return compare(a,b)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Sort items in data according to ordering.
|
||||
|
||||
Each ordering spec is a table with possible fields:
|
||||
|
||||
* key = function(value)
|
||||
Computes comparison key from a data value. Not called on nil.
|
||||
* key_table = function(data)
|
||||
Computes a key table from the data table in one go.
|
||||
* compare = function(a,b)
|
||||
Comparison function. Defaults to compare above.
|
||||
Called on non-nil keys; nil sorts last.
|
||||
* nil_first
|
||||
If true, nil keys are sorted first instead of last.
|
||||
* reverse
|
||||
If true, sort non-nil keys in descending order.
|
||||
|
||||
Returns a table of integer indices into data.
|
||||
--]]
|
||||
function make_sort_order(data,ordering)
|
||||
-- Compute sort keys and comparators
|
||||
local keys = {}
|
||||
local cmps = {}
|
||||
local size = data.n or #data
|
||||
|
||||
for i=1,#ordering do
|
||||
local order = ordering[i]
|
||||
|
||||
if order.key_table then
|
||||
keys[i] = order.key_table(data)
|
||||
elseif order.key then
|
||||
local kt = {}
|
||||
local kf = order.key
|
||||
for j=1,size do
|
||||
if data[j] == nil then
|
||||
kt[j] = nil
|
||||
else
|
||||
kt[j] = kf(data[j])
|
||||
end
|
||||
end
|
||||
keys[i] = kt
|
||||
else
|
||||
keys[i] = data
|
||||
end
|
||||
|
||||
cmps[i] = order.compare or compare
|
||||
end
|
||||
|
||||
-- Make an order table
|
||||
local index = {}
|
||||
for i=1,size do
|
||||
index[i] = i
|
||||
end
|
||||
|
||||
-- Sort the ordering table
|
||||
table.sort(index, function(ia,ib)
|
||||
for i=1,#keys do
|
||||
local ka = keys[i][ia]
|
||||
local kb = keys[i][ib]
|
||||
|
||||
-- Sort nil keys to the end
|
||||
if ka == nil then
|
||||
if kb ~= nil then
|
||||
return ordering[i].nil_first
|
||||
end
|
||||
elseif kb == nil then
|
||||
return not ordering[i].nil_first
|
||||
else
|
||||
local cmpv = cmps[i](ka,kb)
|
||||
if ordering[i].reverse then
|
||||
cmpv = -cmpv
|
||||
end
|
||||
if cmpv < 0 then
|
||||
return true
|
||||
elseif cmpv > 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return ia < ib -- this should ensure stable sort
|
||||
end)
|
||||
|
||||
return index
|
||||
end
|
||||
|
||||
return _ENV
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
https://github.com/peterix/dfhack
|
||||
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
|
||||
#include "Internal.h"
|
||||
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
using namespace std;
|
||||
|
||||
#include "Error.h"
|
||||
#include "Core.h"
|
||||
|
||||
#include "modules/Burrows.h"
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/Units.h"
|
||||
|
||||
#include "MiscUtils.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/burrow.h"
|
||||
#include "df/block_burrow.h"
|
||||
#include "df/block_burrow_link.h"
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
|
||||
df::burrow *Burrows::findByName(std::string name)
|
||||
{
|
||||
auto &vec = df::burrow::get_vector();
|
||||
|
||||
for (size_t i = 0; i < vec.size(); i++)
|
||||
if (vec[i]->name == name)
|
||||
return vec[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Burrows::clearUnits(df::burrow *burrow)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
for (size_t i = 0; i < burrow->units.size(); i++)
|
||||
{
|
||||
auto unit = df::unit::find(burrow->units[i]);
|
||||
|
||||
if (unit)
|
||||
erase_from_vector(unit->burrows, burrow->id);
|
||||
}
|
||||
|
||||
burrow->units.clear();
|
||||
|
||||
// Sync ui if active
|
||||
if (ui && ui->main.mode == ui_sidebar_mode::Burrows &&
|
||||
ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id)
|
||||
{
|
||||
auto &sel = ui->burrows.sel_units;
|
||||
|
||||
for (size_t i = 0; i < sel.size(); i++)
|
||||
sel[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Burrows::isAssignedUnit(df::burrow *burrow, df::unit *unit)
|
||||
{
|
||||
CHECK_NULL_POINTER(unit);
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
return binsearch_index(unit->burrows, burrow->id) >= 0;
|
||||
}
|
||||
|
||||
void Burrows::setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable)
|
||||
{
|
||||
using df::global::ui;
|
||||
|
||||
CHECK_NULL_POINTER(unit);
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
if (enable)
|
||||
{
|
||||
insert_into_vector(unit->burrows, burrow->id);
|
||||
insert_into_vector(burrow->units, unit->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
erase_from_vector(unit->burrows, burrow->id);
|
||||
erase_from_vector(burrow->units, unit->id);
|
||||
}
|
||||
|
||||
// Sync ui if active
|
||||
if (ui && ui->main.mode == ui_sidebar_mode::Burrows &&
|
||||
ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id)
|
||||
{
|
||||
int idx = linear_index(ui->burrows.list_units, unit);
|
||||
if (idx >= 0)
|
||||
ui->burrows.sel_units[idx] = enable;
|
||||
}
|
||||
}
|
||||
|
||||
void Burrows::listBlocks(std::vector<df::map_block*> *pvec, df::burrow *burrow)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
pvec->clear();
|
||||
pvec->reserve(burrow->block_x.size());
|
||||
|
||||
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
|
||||
|
||||
for (size_t i = 0; i < burrow->block_x.size(); i++)
|
||||
{
|
||||
df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
|
||||
|
||||
auto block = Maps::getBlock(pos - base);
|
||||
if (block)
|
||||
pvec->push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroyBurrowMask(df::block_burrow *mask)
|
||||
{
|
||||
if (!mask) return;
|
||||
|
||||
auto link = mask->link;
|
||||
|
||||
link->prev->next = link->next;
|
||||
if (link->next)
|
||||
link->next->prev = link->prev;
|
||||
delete link;
|
||||
|
||||
delete mask;
|
||||
}
|
||||
|
||||
void Burrows::clearTiles(df::burrow *burrow)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
|
||||
|
||||
for (size_t i = 0; i < burrow->block_x.size(); i++)
|
||||
{
|
||||
df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
|
||||
|
||||
auto block = Maps::getBlock(pos - base);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
destroyBurrowMask(getBlockMask(burrow, block));
|
||||
}
|
||||
|
||||
burrow->block_x.clear();
|
||||
burrow->block_y.clear();
|
||||
burrow->block_z.clear();
|
||||
}
|
||||
|
||||
df::block_burrow *Burrows::getBlockMask(df::burrow *burrow, df::map_block *block, bool create)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
CHECK_NULL_POINTER(block);
|
||||
|
||||
int32_t id = burrow->id;
|
||||
df::block_burrow_link *prev = &block->block_burrows;
|
||||
df::block_burrow_link *link = prev->next;
|
||||
|
||||
for (; link; prev = link, link = link->next)
|
||||
if (link->item->id == id)
|
||||
return link->item;
|
||||
|
||||
if (create)
|
||||
{
|
||||
link = new df::block_burrow_link;
|
||||
link->item = new df::block_burrow;
|
||||
|
||||
link->item->id = burrow->id;
|
||||
link->item->tile_bitmask.clear();
|
||||
link->item->link = link;
|
||||
|
||||
link->next = NULL;
|
||||
link->prev = prev;
|
||||
prev->next = link;
|
||||
|
||||
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
|
||||
df::coord pos = base + block->map_pos/16;
|
||||
|
||||
burrow->block_x.push_back(pos.x);
|
||||
burrow->block_y.push_back(pos.y);
|
||||
burrow->block_z.push_back(pos.z);
|
||||
|
||||
return link->item;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Burrows::deleteBlockMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
CHECK_NULL_POINTER(block);
|
||||
|
||||
if (!mask)
|
||||
return false;
|
||||
|
||||
df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
|
||||
df::coord pos = base + block->map_pos/16;
|
||||
|
||||
destroyBurrowMask(mask);
|
||||
|
||||
for (size_t i = 0; i < burrow->block_x.size(); i++)
|
||||
{
|
||||
df::coord cur(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
|
||||
|
||||
if (cur == pos)
|
||||
{
|
||||
vector_erase_at(burrow->block_x, i);
|
||||
vector_erase_at(burrow->block_y, i);
|
||||
vector_erase_at(burrow->block_z, i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Burrows::isAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
if (!block) return false;
|
||||
|
||||
auto mask = getBlockMask(burrow, block);
|
||||
|
||||
return mask ? mask->getassignment(tile & 15) : false;
|
||||
}
|
||||
|
||||
bool Burrows::setAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable)
|
||||
{
|
||||
CHECK_NULL_POINTER(burrow);
|
||||
|
||||
if (!block) return false;
|
||||
|
||||
auto mask = getBlockMask(burrow, block, enable);
|
||||
|
||||
if (mask)
|
||||
{
|
||||
mask->setassignment(tile & 15, enable);
|
||||
|
||||
if (!enable && !mask->has_assignments())
|
||||
deleteBlockMask(burrow, block, mask);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e8036d3f13c6be0141899baae90f605ad11d5385
|
||||
Subproject commit f649d31001e6023a9df5fe83c7971c17afe0d87d
|
@ -0,0 +1,53 @@
|
||||
local _ENV = mkmodule('plugins.sort')
|
||||
|
||||
local utils = require('utils')
|
||||
local units = require('plugins.sort.units')
|
||||
|
||||
orders = orders or {}
|
||||
orders.units = units.orders
|
||||
|
||||
function parse_ordering_spec(type,...)
|
||||
local group = orders[type]
|
||||
if group == nil then
|
||||
dfhack.printerr('Invalid ordering class: '..tostring(type))
|
||||
return nil
|
||||
end
|
||||
|
||||
local specs = table.pack(...)
|
||||
local rv = { }
|
||||
|
||||
for _,spec in ipairs(specs) do
|
||||
local nil_first = false
|
||||
if string.sub(spec,1,1) == '<' then
|
||||
nil_first = true
|
||||
spec = string.sub(spec,2)
|
||||
end
|
||||
|
||||
local reverse = false
|
||||
if string.sub(spec,1,1) == '>' then
|
||||
reverse = true
|
||||
spec = string.sub(spec,2)
|
||||
end
|
||||
|
||||
local cm = group[spec]
|
||||
|
||||
if cm == nil then
|
||||
dfhack.printerr('Unknown order for '..type..': '..tostring(spec))
|
||||
return nil
|
||||
end
|
||||
|
||||
if nil_first or reverse then
|
||||
cm = copyall(cm)
|
||||
cm.nil_first = nil_first
|
||||
cm.reverse = reverse
|
||||
end
|
||||
|
||||
rv[#rv+1] = cm
|
||||
end
|
||||
|
||||
return rv
|
||||
end
|
||||
|
||||
make_sort_order = utils.make_sort_order
|
||||
|
||||
return _ENV
|
@ -0,0 +1,112 @@
|
||||
local _ENV = mkmodule('plugins.sort.units')
|
||||
|
||||
local utils = require('utils')
|
||||
|
||||
orders = orders or {}
|
||||
|
||||
-- Relies on NULL being auto-translated to NULL, and then sorted
|
||||
orders.exists = {
|
||||
key = function(unit)
|
||||
return 1
|
||||
end
|
||||
}
|
||||
|
||||
orders.name = {
|
||||
key = function(unit)
|
||||
local name = dfhack.units.getVisibleName(unit)
|
||||
if name and name.has_name then
|
||||
return dfhack.TranslateName(name)
|
||||
end
|
||||
end,
|
||||
compare = utils.compare_name
|
||||
}
|
||||
|
||||
orders.age = {
|
||||
key = function(unit)
|
||||
return dfhack.units.getAge(unit)
|
||||
end
|
||||
}
|
||||
|
||||
-- This assumes that units are added to active in arrival order
|
||||
orders.arrival = {
|
||||
key_table = function(units)
|
||||
local size = units.n or #units
|
||||
local lookup={}
|
||||
for i=1,size do
|
||||
if units[i] then
|
||||
lookup[units[i].id] = i
|
||||
end
|
||||
end
|
||||
local idx={}
|
||||
for i,v in ipairs(df.global.world.units.active) do
|
||||
if lookup[v.id] then
|
||||
idx[lookup[v.id]] = i
|
||||
end
|
||||
end
|
||||
return idx
|
||||
end
|
||||
}
|
||||
|
||||
local function findRaceCaste(unit)
|
||||
local rraw = df.creature_raw.find(unit.race)
|
||||
return rraw, safe_index(rraw, 'caste', unit.caste)
|
||||
end
|
||||
|
||||
orders.noble = {
|
||||
key = function(unit)
|
||||
local info = dfhack.units.getNoblePositions(unit)
|
||||
if info then
|
||||
return info[1].position.precedence
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
orders.profession = {
|
||||
key = function(unit)
|
||||
local cp = dfhack.units.getProfessionName(unit)
|
||||
if cp and cp ~= '' then
|
||||
return string.lower(cp)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
orders.profession_class = {
|
||||
key = function(unit)
|
||||
local pid = unit.profession
|
||||
local parent = df.profession.attrs[pid].parent
|
||||
if parent >= 0 then
|
||||
return parent
|
||||
else
|
||||
return pid
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
orders.race = {
|
||||
key = function(unit)
|
||||
local rraw = findRaceCaste(unit)
|
||||
if rraw then
|
||||
return rraw.name[0]
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
orders.squad = {
|
||||
key = function(unit)
|
||||
local sidx = unit.military.squad_index
|
||||
if sidx >= 0 then
|
||||
return sidx
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
orders.squad_position = {
|
||||
key = function(unit)
|
||||
local sidx = unit.military.squad_index
|
||||
if sidx >= 0 then
|
||||
return sidx * 1000 + unit.military.squad_position
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
return _ENV
|
@ -0,0 +1,528 @@
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/Translation.h"
|
||||
#include "modules/Units.h"
|
||||
|
||||
#include "LuaTools.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/world.h"
|
||||
#include "df/viewscreen_joblistst.h"
|
||||
#include "df/viewscreen_unitlistst.h"
|
||||
#include "df/viewscreen_layer_militaryst.h"
|
||||
#include "df/viewscreen_layer_workshop_profilest.h"
|
||||
#include "df/viewscreen_layer_noblelistst.h"
|
||||
#include "df/viewscreen_layer_overall_healthst.h"
|
||||
#include "df/viewscreen_dwarfmodest.h"
|
||||
#include "df/viewscreen_petst.h"
|
||||
#include "df/layer_object_listst.h"
|
||||
|
||||
#include "MiscUtils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::endl;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::ui;
|
||||
using df::global::world;
|
||||
using df::global::ui_building_in_assign;
|
||||
using df::global::ui_building_item_cursor;
|
||||
using df::global::ui_building_assign_type;
|
||||
using df::global::ui_building_assign_is_marked;
|
||||
using df::global::ui_building_assign_units;
|
||||
using df::global::ui_building_assign_items;
|
||||
|
||||
static bool unit_list_hotkey(df::viewscreen *top);
|
||||
|
||||
static command_result sort_units(color_ostream &out, vector <string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("sort");
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"sort-units", "Sort the visible unit list.", sort_units, unit_list_hotkey,
|
||||
" sort-units order [order...]\n"
|
||||
" Sort the unit list using the given sequence of comparisons.\n"
|
||||
" The '<' prefix for an order makes undefined values sort first.\n"
|
||||
" The '>' prefix reverses the sort order for defined values.\n"
|
||||
" Unit order examples:\n"
|
||||
" name, age, arrival, squad, squad_position, profession\n"
|
||||
"The orderings are defined in hack/lua/plugins/sort/*.lua\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void reorder_vector(std::vector<T> *pvec, const std::vector<unsigned> &order)
|
||||
{
|
||||
assert(pvec->size() == order.size());
|
||||
|
||||
std::vector<T> tmp(*pvec);
|
||||
for (size_t i = 0; i < order.size(); i++)
|
||||
(*pvec)[i] = tmp[order[i]];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void reorder_cursor(T *cursor, const std::vector<unsigned> &order)
|
||||
{
|
||||
if (*cursor == 0)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < order.size(); i++)
|
||||
{
|
||||
if (unsigned(*cursor) == order[i])
|
||||
{
|
||||
*cursor = T(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_ordering_spec(color_ostream &out, lua_State *L, std::string type, const std::vector<std::string> ¶ms)
|
||||
{
|
||||
if (!lua_checkstack(L, params.size() + 2))
|
||||
return false;
|
||||
|
||||
if (!Lua::PushModulePublic(out, L, "plugins.sort", "parse_ordering_spec"))
|
||||
return false;
|
||||
|
||||
Lua::Push(L, type);
|
||||
for (size_t i = 0; i < params.size(); i++)
|
||||
Lua::Push(L, params[i]);
|
||||
|
||||
if (!Lua::SafeCall(out, L, params.size()+1, 1))
|
||||
return false;
|
||||
|
||||
if (!lua_istable(L, -1))
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_order(color_ostream &out, lua_State *L, std::vector<unsigned> *order, size_t size)
|
||||
{
|
||||
std::vector<char> found;
|
||||
|
||||
Lua::StackUnwinder frame(L, 1);
|
||||
|
||||
if (!lua_istable(L, -1))
|
||||
{
|
||||
out.printerr("Not a table returned as ordering.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lua_rawlen(L, -1) != size)
|
||||
{
|
||||
out.printerr("Invalid ordering size: expected %d, actual %d\n", size, lua_rawlen(L, -1));
|
||||
return false;
|
||||
}
|
||||
|
||||
order->clear();
|
||||
order->resize(size);
|
||||
found.resize(size);
|
||||
|
||||
for (size_t i = 1; i <= size; i++)
|
||||
{
|
||||
lua_rawgeti(L, frame[1], i);
|
||||
int v = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (v < 1 || size_t(v) > size)
|
||||
{
|
||||
out.printerr("Order value out of range: %d\n", v);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (found[v-1])
|
||||
{
|
||||
out.printerr("Duplicate order value: %d\n", v);
|
||||
return false;
|
||||
}
|
||||
|
||||
found[v-1] = 1;
|
||||
(*order)[i-1] = v-1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool compute_order(color_ostream &out, lua_State *L, int base, std::vector<unsigned> *order, const std::vector<T> &key)
|
||||
{
|
||||
lua_pushvalue(L, base+1);
|
||||
Lua::PushVector(L, key, true);
|
||||
lua_pushvalue(L, base+2);
|
||||
|
||||
if (!Lua::SafeCall(out, L, 2, 1))
|
||||
return false;
|
||||
|
||||
return read_order(out, L, order, key.size());
|
||||
}
|
||||
|
||||
static bool ParseSpec(color_ostream &out, lua_State *L, const char *type, vector<string> ¶ms)
|
||||
{
|
||||
if (!parse_ordering_spec(out, L, "units", params))
|
||||
{
|
||||
out.printerr("Invalid ordering specification for %s.\n", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define PARSE_SPEC(type, params) \
|
||||
if (!ParseSpec(*pout, L, type, params)) return false;
|
||||
|
||||
static void sort_null_first(vector<string> ¶meters)
|
||||
{
|
||||
vector_insert_at(parameters, 0, std::string("<exists"));
|
||||
}
|
||||
|
||||
static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx)
|
||||
{
|
||||
return virtual_cast<df::layer_object_listst>(vector_get(layer->layer_objects,idx));
|
||||
}
|
||||
|
||||
static bool maybe_sort_units(color_ostream *pout, lua_State *L,
|
||||
df::viewscreen *screen, vector<string> ¶meters)
|
||||
{
|
||||
Lua::StackUnwinder top(L);
|
||||
|
||||
if (L)
|
||||
{
|
||||
if (!Lua::PushModulePublic(*pout, L, "plugins.sort", "make_sort_order"))
|
||||
{
|
||||
pout->printerr("Cannot access the sorter function.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned> order;
|
||||
|
||||
if (auto units = strict_virtual_cast<df::viewscreen_unitlistst>(screen))
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort units in the 'u'nit list screen.
|
||||
*/
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
int page = units->page;
|
||||
|
||||
if (compute_order(*pout, L, top, &order, units->units[page]))
|
||||
{
|
||||
reorder_cursor(&units->cursor_pos[page], order);
|
||||
reorder_vector(&units->units[page], order);
|
||||
reorder_vector(&units->jobs[page], order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (auto jobs = strict_virtual_cast<df::viewscreen_joblistst>(screen))
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort units in the 'j'ob list screen.
|
||||
*/
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, jobs->units))
|
||||
{
|
||||
reorder_cursor(&jobs->cursor_pos, order);
|
||||
reorder_vector(&jobs->units, order);
|
||||
reorder_vector(&jobs->jobs, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (auto military = strict_virtual_cast<df::viewscreen_layer_militaryst>(screen))
|
||||
{
|
||||
switch (military->page)
|
||||
{
|
||||
case df::viewscreen_layer_militaryst::Positions:
|
||||
{
|
||||
auto &candidates = military->positions.candidates;
|
||||
auto list3 = getLayerList(military, 2);
|
||||
|
||||
/*
|
||||
* Sort candidate units in the 'p'osition page of the 'm'ilitary screen.
|
||||
*/
|
||||
|
||||
if (list3 && !candidates.empty() && list3->bright)
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, candidates))
|
||||
{
|
||||
reorder_cursor(&list3->cursor, order);
|
||||
reorder_vector(&candidates, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (auto profile = strict_virtual_cast<df::viewscreen_layer_workshop_profilest>(screen))
|
||||
{
|
||||
auto list1 = getLayerList(profile, 0);
|
||||
|
||||
if (!list1) return false;
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort units in the workshop 'q'uery 'P'rofile modification screen.
|
||||
*/
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, profile->workers))
|
||||
{
|
||||
reorder_cursor(&list1->cursor, order);
|
||||
reorder_vector(&profile->workers, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (auto nobles = strict_virtual_cast<df::viewscreen_layer_noblelistst>(screen))
|
||||
{
|
||||
switch (nobles->mode)
|
||||
{
|
||||
case df::viewscreen_layer_noblelistst::Appoint:
|
||||
{
|
||||
auto list2 = getLayerList(nobles, 1);
|
||||
|
||||
/*
|
||||
* Sort units in the appointment candidate list of the 'n'obles screen.
|
||||
*/
|
||||
|
||||
if (list2)
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
sort_null_first(parameters);
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
std::vector<df::unit*> units;
|
||||
for (size_t i = 0; i < nobles->candidates.size(); i++)
|
||||
units.push_back(nobles->candidates[i]->unit);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, units))
|
||||
{
|
||||
reorder_cursor(&list2->cursor, order);
|
||||
reorder_vector(&nobles->candidates, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (auto animals = strict_virtual_cast<df::viewscreen_petst>(screen))
|
||||
{
|
||||
switch (animals->mode)
|
||||
{
|
||||
case df::viewscreen_petst::List:
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort animal units in the Animal page of the 'z' status screen.
|
||||
*/
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
std::vector<df::unit*> units;
|
||||
for (size_t i = 0; i < animals->animal.size(); i++)
|
||||
units.push_back(animals->is_vermin[i] ? NULL : (df::unit*)animals->animal[i]);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, units))
|
||||
{
|
||||
reorder_cursor(&animals->cursor, order);
|
||||
reorder_vector(&animals->animal, order);
|
||||
reorder_vector(&animals->is_vermin, order);
|
||||
reorder_vector(&animals->pet_info, order);
|
||||
reorder_vector(&animals->is_tame, order);
|
||||
reorder_vector(&animals->is_adopting, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case df::viewscreen_petst::SelectTrainer:
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort candidate trainers in the Animal page of the 'z' status screen.
|
||||
*/
|
||||
|
||||
sort_null_first(parameters);
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, animals->trainer_unit))
|
||||
{
|
||||
reorder_cursor(&animals->trainer_cursor, order);
|
||||
reorder_vector(&animals->trainer_unit, order);
|
||||
reorder_vector(&animals->trainer_mode, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (auto health = strict_virtual_cast<df::viewscreen_layer_overall_healthst>(screen))
|
||||
{
|
||||
auto list1 = getLayerList(health, 0);
|
||||
|
||||
if (!list1) return false;
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort units in the Health page of the 'z' status screen.
|
||||
*/
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, health->unit))
|
||||
{
|
||||
reorder_cursor(&list1->cursor, order);
|
||||
reorder_vector(&health->unit, order);
|
||||
reorder_vector(&health->bits1, order);
|
||||
reorder_vector(&health->bits2, order);
|
||||
reorder_vector(&health->bits3, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (strict_virtual_cast<df::viewscreen_dwarfmodest>(screen))
|
||||
{
|
||||
switch (ui->main.mode)
|
||||
{
|
||||
case ui_sidebar_mode::Burrows:
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort burrow member candidate units in the 'w' sidebar mode.
|
||||
*/
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, ui->burrows.list_units))
|
||||
{
|
||||
reorder_cursor(&ui->burrows.unit_cursor_pos, order);
|
||||
reorder_vector(&ui->burrows.list_units, order);
|
||||
reorder_vector(&ui->burrows.sel_units, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case ui_sidebar_mode::QueryBuilding:
|
||||
if (!ui_building_in_assign || !*ui_building_in_assign)
|
||||
return false;
|
||||
// fall through for building owner / chain assign animal
|
||||
|
||||
case ui_sidebar_mode::ZonesPenInfo:
|
||||
if (ui_building_item_cursor &&
|
||||
ui_building_assign_type &&
|
||||
ui_building_assign_is_marked &&
|
||||
ui_building_assign_units &&
|
||||
ui_building_assign_items &&
|
||||
ui_building_assign_type->size() == ui_building_assign_units->size() &&
|
||||
!ui_building_assign_type->empty())
|
||||
{
|
||||
if (!L) return true;
|
||||
|
||||
/*
|
||||
* Sort building owner candidate units in the 'q' sidebar mode,
|
||||
* or pen assignment candidate units in 'z'->'N', or cage assignment.
|
||||
*/
|
||||
|
||||
// TODO: better way
|
||||
bool is_assign_owner = ((*ui_building_assign_type)[0] == -1);
|
||||
|
||||
if (is_assign_owner)
|
||||
sort_null_first(parameters);
|
||||
|
||||
PARSE_SPEC("units", parameters);
|
||||
|
||||
if (compute_order(*pout, L, top, &order, *ui_building_assign_units))
|
||||
{
|
||||
reorder_cursor(ui_building_item_cursor, order);
|
||||
reorder_vector(ui_building_assign_type, order);
|
||||
reorder_vector(ui_building_assign_units, order);
|
||||
|
||||
if (ui_building_assign_units->size() == ui_building_assign_items->size())
|
||||
reorder_vector(ui_building_assign_items, order);
|
||||
if (ui_building_assign_units->size() == ui_building_assign_is_marked->size())
|
||||
reorder_vector(ui_building_assign_is_marked, order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool unit_list_hotkey(df::viewscreen *screen)
|
||||
{
|
||||
vector<string> dummy;
|
||||
return maybe_sort_units(NULL, NULL, screen, dummy);
|
||||
}
|
||||
|
||||
static command_result sort_units(color_ostream &out, vector <string> ¶meters)
|
||||
{
|
||||
if (parameters.empty())
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
auto L = Lua::Core::State;
|
||||
auto screen = Core::getInstance().getTopViewscreen();
|
||||
|
||||
if (!maybe_sort_units(&out, L, screen, parameters))
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
return CR_OK;
|
||||
}
|
Loading…
Reference in New Issue