Merge remote-tracking branch 'upstream/master'
commit
bd2b55c490
@ -0,0 +1,129 @@
|
||||
/*
|
||||
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 "Export.h"
|
||||
#include "MiscUtils.h"
|
||||
#include "Error.h"
|
||||
#include "Types.h"
|
||||
|
||||
#ifndef LINUX_BUILD
|
||||
#include <Windows.h>
|
||||
#include "wdirent.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <ctime>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
|
||||
int DFHack::getdir(std::string dir, std::vector<std::string> &files)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *dirp;
|
||||
if((dp = opendir(dir.c_str())) == NULL)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
while ((dirp = readdir(dp)) != NULL) {
|
||||
files.push_back(std::string(dirp->d_name));
|
||||
}
|
||||
closedir(dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DFHack::hasEnding (std::string const &fullString, std::string const &ending)
|
||||
{
|
||||
if (fullString.length() > ending.length())
|
||||
{
|
||||
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
df::general_ref *DFHack::findRef(std::vector<df::general_ref*> &vec, df::general_ref_type type)
|
||||
{
|
||||
for (int i = vec.size()-1; i >= 0; i--)
|
||||
{
|
||||
df::general_ref *ref = vec[i];
|
||||
if (ref->getType() == type)
|
||||
return ref;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool DFHack::removeRef(std::vector<df::general_ref*> &vec, df::general_ref_type type, int id)
|
||||
{
|
||||
for (int i = vec.size()-1; i >= 0; i--)
|
||||
{
|
||||
df::general_ref *ref = vec[i];
|
||||
if (ref->getType() != type || ref->getID() != id)
|
||||
continue;
|
||||
|
||||
vector_erase_at(vec, i);
|
||||
delete ref;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
df::specific_ref *DFHack::findRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type)
|
||||
{
|
||||
for (int i = vec.size()-1; i >= 0; i--)
|
||||
{
|
||||
df::specific_ref *ref = vec[i];
|
||||
if (ref->type == type)
|
||||
return ref;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool DFHack::removeRef(std::vector<df::specific_ref*> &vec, df::specific_ref_type type, void *ptr)
|
||||
{
|
||||
for (int i = vec.size()-1; i >= 0; i--)
|
||||
{
|
||||
df::specific_ref *ref = vec[i];
|
||||
if (ref->type != type || ref->object != ptr)
|
||||
continue;
|
||||
|
||||
vector_erase_at(vec, i);
|
||||
delete ref;
|
||||
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,437 @@
|
||||
local dfhack = dfhack
|
||||
local _ENV = dfhack.BASE_G
|
||||
local buildings = dfhack.buildings
|
||||
|
||||
local utils = require 'utils'
|
||||
|
||||
--[[ Building input material tables. ]]
|
||||
|
||||
local building_inputs = {
|
||||
[df.building_type.Chair] = { { item_type=df.item_type.CHAIR, vector_id=df.job_item_vector_id.CHAIR } },
|
||||
[df.building_type.Bed] = { { item_type=df.item_type.BED, vector_id=df.job_item_vector_id.BED } },
|
||||
[df.building_type.Table] = { { item_type=df.item_type.TABLE, vector_id=df.job_item_vector_id.TABLE } },
|
||||
[df.building_type.Coffin] = { { item_type=df.item_type.COFFIN, vector_id=df.job_item_vector_id.COFFIN } },
|
||||
[df.building_type.FarmPlot] = { },
|
||||
[df.building_type.Furnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
|
||||
[df.building_type.TradeDepot] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } },
|
||||
[df.building_type.Door] = { { item_type=df.item_type.DOOR, vector_id=df.job_item_vector_id.DOOR } },
|
||||
[df.building_type.Floodgate] = {
|
||||
{
|
||||
item_type=df.item_type.FLOODGATE,
|
||||
vector_id=df.job_item_vector_id.FLOODGATE
|
||||
}
|
||||
},
|
||||
[df.building_type.Box] = {
|
||||
{
|
||||
flags1={ empty=true },
|
||||
item_type=df.item_type.BOX,
|
||||
vector_id=df.job_item_vector_id.BOX
|
||||
}
|
||||
},
|
||||
[df.building_type.Weaponrack] = {
|
||||
{
|
||||
item_type=df.item_type.WEAPONRACK,
|
||||
vector_id=df.job_item_vector_id.WEAPONRACK
|
||||
}
|
||||
},
|
||||
[df.building_type.Armorstand] = {
|
||||
{
|
||||
item_type=df.item_type.ARMORSTAND,
|
||||
vector_id=df.job_item_vector_id.ARMORSTAND
|
||||
}
|
||||
},
|
||||
[df.building_type.Workshop] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.building_type.Cabinet] = {
|
||||
{ item_type=df.item_type.CABINET, vector_id=df.job_item_vector_id.CABINET }
|
||||
},
|
||||
[df.building_type.Statue] = { { item_type=df.item_type.STATUE, vector_id=df.job_item_vector_id.STATUE } },
|
||||
[df.building_type.WindowGlass] = { { item_type=df.item_type.WINDOW, vector_id=df.job_item_vector_id.WINDOW } },
|
||||
[df.building_type.WindowGem] = {
|
||||
{
|
||||
item_type=df.item_type.SMALLGEM,
|
||||
quantity=3,
|
||||
vector_id=df.job_item_vector_id.ANY_GENERIC35
|
||||
}
|
||||
},
|
||||
[df.building_type.Well] = {
|
||||
{
|
||||
item_type=df.item_type.BLOCKS,
|
||||
vector_id=df.job_item_vector_id.ANY_GENERIC35
|
||||
},
|
||||
{
|
||||
name='bucket',
|
||||
flags2={ lye_milk_free=true },
|
||||
item_type=df.item_type.BUCKET,
|
||||
vector_id=df.job_item_vector_id.BUCKET
|
||||
},
|
||||
{
|
||||
name='chain',
|
||||
item_type=df.item_type.CHAIN,
|
||||
vector_id=df.job_item_vector_id.CHAIN
|
||||
},
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
},
|
||||
[df.building_type.Bridge] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } },
|
||||
[df.building_type.RoadDirt] = { },
|
||||
[df.building_type.RoadPaved] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } },
|
||||
[df.building_type.AnimalTrap] = {
|
||||
{
|
||||
flags1={ empty=true },
|
||||
item_type=df.item_type.ANIMALTRAP,
|
||||
vector_id=df.job_item_vector_id.ANIMALTRAP
|
||||
}
|
||||
},
|
||||
[df.building_type.Support] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.building_type.ArcheryTarget] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.building_type.Chain] = { { item_type=df.item_type.CHAIN, vector_id=df.job_item_vector_id.CHAIN } },
|
||||
[df.building_type.Cage] = { { item_type=df.item_type.CAGE, vector_id=df.job_item_vector_id.CAGE } },
|
||||
[df.building_type.Weapon] = { { vector_id=df.job_item_vector_id.ANY_SPIKE } },
|
||||
[df.building_type.ScrewPump] = {
|
||||
{
|
||||
item_type=df.item_type.BLOCKS,
|
||||
vector_id=df.job_item_vector_id.ANY_GENERIC35
|
||||
},
|
||||
{
|
||||
name='screw',
|
||||
flags2={ screw=true },
|
||||
item_type=df.item_type.TRAPCOMP,
|
||||
vector_id=df.job_item_vector_id.ANY_WEAPON
|
||||
},
|
||||
{
|
||||
name='pipe',
|
||||
item_type=df.item_type.PIPE_SECTION,
|
||||
vector_id=df.job_item_vector_id.PIPE_SECTION
|
||||
}
|
||||
},
|
||||
[df.building_type.Construction] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.building_type.Hatch] = {
|
||||
{
|
||||
item_type=df.item_type.HATCH_COVER,
|
||||
vector_id=df.job_item_vector_id.HATCH_COVER
|
||||
}
|
||||
},
|
||||
[df.building_type.GrateWall] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } },
|
||||
[df.building_type.GrateFloor] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } },
|
||||
[df.building_type.BarsVertical] = {
|
||||
{ item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 }
|
||||
},
|
||||
[df.building_type.BarsFloor] = {
|
||||
{ item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 }
|
||||
},
|
||||
[df.building_type.GearAssembly] = {
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
},
|
||||
[df.building_type.AxleHorizontal] = {
|
||||
{ item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD, quantity=-1 }
|
||||
},
|
||||
[df.building_type.AxleVertical] = { { item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD } },
|
||||
[df.building_type.WaterWheel] = {
|
||||
{
|
||||
item_type=df.item_type.WOOD,
|
||||
quantity=3,
|
||||
vector_id=df.job_item_vector_id.WOOD
|
||||
}
|
||||
},
|
||||
[df.building_type.Windmill] = {
|
||||
{
|
||||
item_type=df.item_type.WOOD,
|
||||
quantity=4,
|
||||
vector_id=df.job_item_vector_id.WOOD
|
||||
}
|
||||
},
|
||||
[df.building_type.TractionBench] = {
|
||||
{
|
||||
item_type=df.item_type.TRACTION_BENCH,
|
||||
vector_id=df.job_item_vector_id.TRACTION_BENCH
|
||||
}
|
||||
},
|
||||
[df.building_type.Slab] = { { item_type=df.item_type.SLAB } },
|
||||
[df.building_type.NestBox] = { { has_tool_use=df.tool_uses.NEST_BOX, item_type=df.item_type.TOOL } },
|
||||
[df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } }
|
||||
}
|
||||
|
||||
local furnace_inputs = {
|
||||
[df.furnace_type.WoodFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
|
||||
[df.furnace_type.Smelter] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
|
||||
[df.furnace_type.GlassFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
|
||||
[df.furnace_type.Kiln] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } },
|
||||
[df.furnace_type.MagmaSmelter] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } },
|
||||
[df.furnace_type.MagmaGlassFurnace] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } },
|
||||
[df.furnace_type.MagmaKiln] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } }
|
||||
}
|
||||
|
||||
local workshop_inputs = {
|
||||
[df.workshop_type.Carpenters] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Farmers] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Masons] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Craftsdwarfs] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Jewelers] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.MetalsmithsForge] = {
|
||||
{
|
||||
name='anvil',
|
||||
flags2={ fire_safe=true },
|
||||
item_type=df.item_type.ANVIL,
|
||||
vector_id=df.job_item_vector_id.ANVIL
|
||||
},
|
||||
{ flags2={ building_material=true, fire_safe=true, non_economic=true } }
|
||||
},
|
||||
[df.workshop_type.MagmaForge] = {
|
||||
{
|
||||
name='anvil',
|
||||
flags2={ magma_safe=true },
|
||||
item_type=df.item_type.ANVIL,
|
||||
vector_id=df.job_item_vector_id.ANVIL
|
||||
},
|
||||
{ flags2={ building_material=true, magma_safe=true, non_economic=true } }
|
||||
},
|
||||
[df.workshop_type.Bowyers] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Mechanics] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Siege] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } },
|
||||
[df.workshop_type.Butchers] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Leatherworks] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Tanners] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Clothiers] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Fishery] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Still] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Loom] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Quern] = { { item_type=df.item_type.QUERN, vector_id=df.job_item_vector_id.QUERN } },
|
||||
[df.workshop_type.Kennels] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Kitchen] = { { flags2={ building_material=true, non_economic=true } } },
|
||||
[df.workshop_type.Ashery] = {
|
||||
{
|
||||
item_type=df.item_type.BLOCKS,
|
||||
vector_id=df.job_item_vector_id.ANY_GENERIC35
|
||||
},
|
||||
{
|
||||
name='barrel',
|
||||
flags1={ empty=true },
|
||||
item_type=df.item_type.BARREL,
|
||||
vector_id=df.job_item_vector_id.BARREL
|
||||
},
|
||||
{
|
||||
name='bucket',
|
||||
flags2={ lye_milk_free=true },
|
||||
item_type=df.item_type.BUCKET,
|
||||
vector_id=df.job_item_vector_id.BUCKET
|
||||
}
|
||||
},
|
||||
[df.workshop_type.Dyers] = {
|
||||
{
|
||||
name='barrel',
|
||||
flags1={ empty=true },
|
||||
item_type=df.item_type.BARREL,
|
||||
vector_id=df.job_item_vector_id.BARREL
|
||||
},
|
||||
{
|
||||
name='bucket',
|
||||
flags2={ lye_milk_free=true },
|
||||
item_type=df.item_type.BUCKET,
|
||||
vector_id=df.job_item_vector_id.BUCKET
|
||||
}
|
||||
},
|
||||
[df.workshop_type.Millstone] = {
|
||||
{
|
||||
item_type=df.item_type.MILLSTONE,
|
||||
vector_id=df.job_item_vector_id.MILLSTONE
|
||||
},
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local trap_inputs = {
|
||||
[df.trap_type.StoneFallTrap] = {
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
},
|
||||
[df.trap_type.WeaponTrap] = {
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
},
|
||||
{ vector_id=df.job_item_vector_id.ANY_WEAPON }
|
||||
},
|
||||
[df.trap_type.Lever] = {
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
},
|
||||
[df.trap_type.PressurePlate] = {
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
},
|
||||
[df.trap_type.CageTrap] = {
|
||||
{
|
||||
name='mechanism',
|
||||
item_type=df.item_type.TRAPPARTS,
|
||||
vector_id=df.job_item_vector_id.TRAPPARTS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local function get_inputs_by_type(type,subtype,custom)
|
||||
if type == df.building_type.Workshop then
|
||||
return workshop_inputs[subtype]
|
||||
elseif type == df.building_type.Furnace then
|
||||
return furnace_inputs[subtype]
|
||||
elseif type == df.building_type.Trap then
|
||||
return trap_inputs[subtype]
|
||||
else
|
||||
return building_inputs[type]
|
||||
end
|
||||
end
|
||||
|
||||
local function augment_input(input, argtable)
|
||||
local rv = {}
|
||||
local arg = argtable[input.name or 'material']
|
||||
|
||||
if arg then
|
||||
utils.assign(rv, arg)
|
||||
end
|
||||
|
||||
utils.assign(rv, input)
|
||||
|
||||
if rv.mat_index and safe_index(rv, 'flags2', 'non_economic') then
|
||||
rv.flags2.non_economic = false
|
||||
end
|
||||
|
||||
rv.new = true
|
||||
rv.name = nil
|
||||
return rv
|
||||
end
|
||||
|
||||
function buildings.getFiltersByType(argtable,type,subtype,custom)
|
||||
local inputs = get_inputs_by_type(type,subtype,custom)
|
||||
if not inputs then
|
||||
return nil
|
||||
end
|
||||
local rv = {}
|
||||
for i,v in ipairs(inputs) do
|
||||
rv[i] = augment_input(v, argtable)
|
||||
end
|
||||
return rv
|
||||
end
|
||||
|
||||
--[[
|
||||
Wraps all steps necessary to create a building with
|
||||
a construct job into one function.
|
||||
|
||||
dfhack.buildings.constructBuilding{
|
||||
-- Position:
|
||||
pos = { x = ..., y = ..., z = ... },
|
||||
-- OR
|
||||
x = ..., y = ..., z = ...,
|
||||
|
||||
-- Type:
|
||||
type = df.building_type.FOO, subtype = ..., custom = ...,
|
||||
|
||||
-- Field initialization:
|
||||
fields = { ... },
|
||||
|
||||
-- Size and orientation:
|
||||
width = ..., height = ..., direction = ...,
|
||||
|
||||
-- Abort if not all tiles in the rectangle are available:
|
||||
full_rectangle = true,
|
||||
|
||||
-- Materials:
|
||||
items = { item, item ... },
|
||||
-- OR
|
||||
filter = { { ... }, { ... }... }
|
||||
-- OR
|
||||
abstract = true
|
||||
-- OR
|
||||
material = { filter_properties... }
|
||||
mechanism = { filter_properties... }
|
||||
barrel, bucket, chain, anvil, screw, pipe
|
||||
}
|
||||
|
||||
Returns: the created building, or 'nil, error'
|
||||
--]]
|
||||
|
||||
function buildings.constructBuilding(info)
|
||||
local btype = info.type
|
||||
local subtype = info.subtype or -1
|
||||
local custom = info.custom or -1
|
||||
local filters = info.filters
|
||||
|
||||
if not (info.pos or info.x) then
|
||||
error('position is required')
|
||||
end
|
||||
if not (info.abstract or info.items or filters) then
|
||||
filters = buildings.getFiltersByType(info,btype,subtype,custom)
|
||||
if not filters then
|
||||
error('one of items, filters or abstract is required')
|
||||
end
|
||||
elseif filters then
|
||||
for _,v in ipairs(filters) do
|
||||
v.new = true
|
||||
end
|
||||
end
|
||||
if type(btype) ~= 'number' or not df.building_type[btype] then
|
||||
error('Invalid building type: '..tostring(btype))
|
||||
end
|
||||
|
||||
local pos = info.pos or xyz2pos(info.x, info.y, info.z)
|
||||
|
||||
local instance = buildings.allocInstance(pos, btype, subtype, custom)
|
||||
if not instance then
|
||||
error('Could not create building of type '..df.building_type[btype])
|
||||
end
|
||||
|
||||
local to_delete = instance
|
||||
return dfhack.with_finalize(
|
||||
function()
|
||||
df.delete(to_delete)
|
||||
end,
|
||||
function()
|
||||
if info.fields then
|
||||
instance:assign(info.fields)
|
||||
end
|
||||
local ok,w,h,area,r_area = buildings.setSize(
|
||||
instance,info.width,info.height,info.direction
|
||||
)
|
||||
if not ok then
|
||||
return nil, "cannot place at this position"
|
||||
end
|
||||
if info.full_rectangle and area ~= r_area then
|
||||
return nil, "not all tiles can be used"
|
||||
end
|
||||
if info.abstract then
|
||||
ok = buildings.constructAbstract(instance)
|
||||
elseif info.items then
|
||||
ok = buildings.constructWithItems(instance, info.items)
|
||||
else
|
||||
ok = buildings.constructWithFilters(instance, filters)
|
||||
end
|
||||
if not ok then
|
||||
return nil, "could not construct the building"
|
||||
end
|
||||
-- Success
|
||||
to_delete = nil
|
||||
return instance
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
return buildings
|
@ -0,0 +1,139 @@
|
||||
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
|
||||
|
||||
--[[
|
||||
Recursively assign data into a table.
|
||||
--]]
|
||||
function assign(tgt,src)
|
||||
if df.isvalid(tgt) == 'ref' then
|
||||
df.assign(tgt, src)
|
||||
elseif type(tgt) == 'table' then
|
||||
for k,v in pairs(src) do
|
||||
if type(v) == 'table' or df.isvalid(v) == 'ref' then
|
||||
local cv = tgt[k]
|
||||
if cv == nil then
|
||||
cv = {}
|
||||
tgt[k] = cv
|
||||
end
|
||||
assign(cv, v)
|
||||
else
|
||||
tgt[k] = v
|
||||
end
|
||||
end
|
||||
else
|
||||
error('Invalid assign target type: '..tostring(tgt))
|
||||
end
|
||||
return tgt
|
||||
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 25c2a3dad964abbcceb5abd41558b71fb113e83b
|
||||
Subproject commit 970bb0ae9530e9c0522cb0ca8fe357815d72fab1
|
@ -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;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
-- Example of a lua script.
|
||||
|
||||
run_count = (run_count or 0) + 1
|
||||
|
||||
print('Arguments: ',...)
|
||||
print('Command called '..run_count..' times.')
|
@ -0,0 +1,28 @@
|
||||
-- Makes the game immediately save the state.
|
||||
|
||||
if not dfhack.isMapLoaded() then
|
||||
dfhack.printerr("World and map aren't loaded.")
|
||||
return
|
||||
end
|
||||
|
||||
local ui_main = df.global.ui.main
|
||||
local flags4 = df.global.d_init.flags4
|
||||
|
||||
local function restore_autobackup()
|
||||
if ui_main.autosave_request and dfhack.isMapLoaded() then
|
||||
dfhack.timeout(10, 'frames', restore_autobackup)
|
||||
else
|
||||
flags4.AUTOBACKUP = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Request auto-save
|
||||
ui_main.autosave_request = true
|
||||
|
||||
-- And since it will overwrite the backup, disable it temporarily
|
||||
if flags4.AUTOBACKUP then
|
||||
flags4.AUTOBACKUP = false
|
||||
restore_autobackup()
|
||||
end
|
||||
|
||||
print 'The game should save the state now.'
|
Loading…
Reference in New Issue