Merge remote-tracking branch 'upstream/master'
commit
dce7f17fe7
@ -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;
|
||||
}
|
@ -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,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 e8036d3f13c6be0141899baae90f605ad11d5385
|
||||
Subproject commit 5707a6aa0c035348c8769058ed77b320f9b1436c
|
@ -0,0 +1,116 @@
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/world.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/building_nest_boxst.h"
|
||||
#include "df/building_type.h"
|
||||
#include "df/global_objects.h"
|
||||
#include "df/item.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/building.h"
|
||||
#include "df/items_other_id.h"
|
||||
#include "df/creature_raw.h"
|
||||
#include "modules/MapCache.h"
|
||||
#include "modules/Items.h"
|
||||
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::endl;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
|
||||
static command_result nestboxes(color_ostream &out, vector <string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("nestboxes");
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
if (world && ui) {
|
||||
commands.push_back(
|
||||
PluginCommand("nestboxes", "Derp.",
|
||||
nestboxes, false,
|
||||
"Derp.\n"
|
||||
)
|
||||
);
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
|
||||
static command_result nestboxes(color_ostream &out, vector <string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
bool clean = false;
|
||||
int dump_count = 0;
|
||||
int good_egg = 0;
|
||||
|
||||
if (parameters.size() == 1 && parameters[0] == "clean")
|
||||
{
|
||||
clean = true;
|
||||
}
|
||||
for (int i = 0; i < world->buildings.all.size(); ++i)
|
||||
{
|
||||
df::building *build = world->buildings.all[i];
|
||||
auto type = build->getType();
|
||||
if (df::enums::building_type::NestBox == type)
|
||||
{
|
||||
bool needs_clean = false;
|
||||
df::building_nest_boxst *nb = virtual_cast<df::building_nest_boxst>(build);
|
||||
out << "Nestbox at (" << nb->x1 << "," << nb->y1 << ","<< nb->z << "): claimed-by " << nb->claimed_by << ", contained item count " << nb->contained_items.size() << " (" << nb->anon_1 << ")" << endl;
|
||||
if (nb->contained_items.size() > 1)
|
||||
needs_clean = true;
|
||||
if (nb->claimed_by != -1)
|
||||
{
|
||||
df::unit* u = df::unit::find(nb->claimed_by);
|
||||
if (u)
|
||||
{
|
||||
out << " Claimed by ";
|
||||
if (u->name.has_name)
|
||||
out << u->name.first_name << ", ";
|
||||
df::creature_raw *raw = df::global::world->raws.creatures.all[u->race];
|
||||
out << raw->creature_id
|
||||
<< ", pregnancy timer " << u->relations.pregnancy_timer << endl;
|
||||
if (u->relations.pregnancy_timer > 0)
|
||||
needs_clean = false;
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < nb->contained_items.size(); j++)
|
||||
{
|
||||
df::item* item = nb->contained_items[j]->item;
|
||||
if (needs_clean) {
|
||||
if (clean && !item->flags.bits.dump)
|
||||
{
|
||||
item->flags.bits.dump = 1;
|
||||
dump_count += item->getStackSize();
|
||||
|
||||
}
|
||||
} else {
|
||||
good_egg += item->getStackSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clean)
|
||||
{
|
||||
out << dump_count << " eggs dumped." << endl;
|
||||
}
|
||||
out << good_egg << " fertile eggs found." << endl;
|
||||
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
@ -0,0 +1,163 @@
|
||||
// Produces a list of materials available on the map.
|
||||
// Options:
|
||||
// -a : show unrevealed tiles
|
||||
// -p : don't show plants
|
||||
// -s : don't show slade
|
||||
// -t : don't show demon temple
|
||||
|
||||
//#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "modules/MapCache.h"
|
||||
|
||||
#include "MiscUtils.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/world.h"
|
||||
#include "df/world_data.h"
|
||||
#include "df/world_region_details.h"
|
||||
#include "df/world_geo_biome.h"
|
||||
#include "df/world_geo_layer.h"
|
||||
#include "df/inclusion_type.h"
|
||||
#include "df/viewscreen_choose_start_sitest.h"
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
using df::global::world;
|
||||
using df::coord2d;
|
||||
|
||||
|
||||
|
||||
command_result rprobe (color_ostream &out, vector <string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("rprobe");
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"rprobe", "Display assorted region information from embark screen",
|
||||
rprobe, false,
|
||||
"Display assorted region information from embark screen\n"
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
|
||||
command_result rprobe (color_ostream &out, vector <string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
|
||||
bool set = false;
|
||||
int to_set, set_field, set_val;
|
||||
|
||||
// Embark screen active: estimate using world geology data
|
||||
VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen());
|
||||
|
||||
if (!screen)
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
if (!world || !world->world_data)
|
||||
{
|
||||
out.printerr("World data is not available.\n");
|
||||
return CR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
if (parameters.size() == 2)
|
||||
{
|
||||
if (parameters[0] == "wet")
|
||||
set_field = 0;
|
||||
else if (parameters[0] == "veg")
|
||||
set_field = 1;
|
||||
else if (parameters[0] == "tem")
|
||||
set_field = 2;
|
||||
else if (parameters[0] == "evi")
|
||||
set_field = 3;
|
||||
else if (parameters[0] == "hil")
|
||||
set_field = 4;
|
||||
else if (parameters[0] == "sav")
|
||||
set_field = 5;
|
||||
else if (parameters[0] == "sal")
|
||||
set_field = 6;
|
||||
else
|
||||
return CR_WRONG_USAGE;
|
||||
|
||||
if (screen->biome_highlighted)
|
||||
to_set = screen->biome_idx;
|
||||
else
|
||||
to_set = 0;
|
||||
|
||||
set = true;
|
||||
set_val = atoi(parameters[1].c_str());
|
||||
}
|
||||
|
||||
df::world_data *data = world->world_data;
|
||||
coord2d cur_region = screen->region_pos;
|
||||
|
||||
// Compute biomes
|
||||
for (int i = 0; i < screen->biome_rgn.size(); i++)
|
||||
{
|
||||
coord2d rg = screen->biome_rgn[i];
|
||||
|
||||
df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y];
|
||||
|
||||
if (set && i == to_set) {
|
||||
if (set_field == 0)
|
||||
rd->wetness = set_val;
|
||||
else if (set_field == 1)
|
||||
rd->vegetation = set_val;
|
||||
else if (set_field == 2)
|
||||
rd->temperature = set_val;
|
||||
else if (set_field == 3)
|
||||
rd->evilness = set_val;
|
||||
else if (set_field == 4)
|
||||
rd->hilliness = set_val;
|
||||
else if (set_field == 5)
|
||||
rd->savagery = set_val;
|
||||
else if (set_field == 6)
|
||||
rd->saltiness = set_val;
|
||||
}
|
||||
|
||||
out << i << ": x = " << rg.x << ", y = " << rg.y;
|
||||
|
||||
out <<
|
||||
" region_id: " << rd->region_id <<
|
||||
" geo_index: " << rd->geo_index <<
|
||||
" landmass_id: " << rd->landmass_id <<
|
||||
" flags: " << hex << rd->flags.as_int() << dec << endl;
|
||||
out <<
|
||||
"wet: " << rd->wetness << " " <<
|
||||
"veg: " << rd->vegetation << " " <<
|
||||
"tem: " << rd->temperature << " " <<
|
||||
"evi: " << rd->evilness << " " <<
|
||||
"hil: " << rd->hilliness << " " <<
|
||||
"sav: " << rd->savagery << " " <<
|
||||
"sal: " << rd->saltiness;
|
||||
|
||||
int32_t *p = (int32_t *)rd;
|
||||
int c = sizeof(*rd) / sizeof(int32_t);
|
||||
for (int j = 0; j < c; j++) {
|
||||
if (j % 8 == 0)
|
||||
out << endl << setfill('0') << setw(8) << hex << (int)(rd+j) << ": ";
|
||||
out << " " << setfill('0') << setw(8) << hex << p[j];
|
||||
}
|
||||
out << setfill(' ') << setw(0) << dec << endl;
|
||||
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
#include "DataDefs.h"
|
||||
#include "df/world.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/building_stockpilest.h"
|
||||
#include "df/global_objects.h"
|
||||
#include "df/item.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/building.h"
|
||||
#include "df/items_other_id.h"
|
||||
#include "df/item_stockpile_ref.h"
|
||||
#include "modules/MapCache.h"
|
||||
#include "modules/Items.h"
|
||||
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::endl;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
using df::global::selection_rect;
|
||||
|
||||
using df::building_stockpilest;
|
||||
|
||||
static command_result copystock(color_ostream &out, vector <string> & parameters);
|
||||
static command_result stockcheck(color_ostream &out, vector <string> & parameters);
|
||||
static bool copystock_guard(df::viewscreen *top);
|
||||
|
||||
DFHACK_PLUGIN("stockcheck");
|
||||
|
||||
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
if (world && ui) {
|
||||
commands.push_back(
|
||||
PluginCommand("stockcheck", "Check for unprotected rottable items.",
|
||||
stockcheck, false,
|
||||
"Scan world for items that are susceptible to rot. Currently just lists the items.\n"
|
||||
)
|
||||
);
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
struct StockpileInfo {
|
||||
building_stockpilest* sp;
|
||||
int size;
|
||||
int free;
|
||||
int x1, x2, y1, y2, z;
|
||||
|
||||
public:
|
||||
StockpileInfo(building_stockpilest *sp_) : sp(sp_)
|
||||
{
|
||||
MapExtras::MapCache mc;
|
||||
|
||||
z = sp_->z;
|
||||
x1 = sp_->room.x;
|
||||
x2 = sp_->room.x + sp_->room.width;
|
||||
y1 = sp_->room.y;
|
||||
y2 = sp_->room.y + sp_->room.height;
|
||||
int e = 0;
|
||||
size = 0;
|
||||
free = 0;
|
||||
for (int y = y1; y < y2; y++)
|
||||
for (int x = x1; x < x2; x++)
|
||||
if (sp_->room.extents[e++] == 1)
|
||||
{
|
||||
size++;
|
||||
DFCoord cursor (x,y,z);
|
||||
uint32_t blockX = x / 16;
|
||||
uint32_t tileX = x % 16;
|
||||
uint32_t blockY = y / 16;
|
||||
uint32_t tileY = y % 16;
|
||||
MapExtras::Block * b = mc.BlockAt(cursor/16);
|
||||
if(b && b->is_valid())
|
||||
{
|
||||
auto &block = *b->getRaw();
|
||||
df::tile_occupancy &occ = block.occupancy[tileX][tileY];
|
||||
if (!occ.bits.item)
|
||||
free++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isFull() { return free == 0; }
|
||||
|
||||
bool canHold(df::item *i)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool inStockpile(df::item *i)
|
||||
{
|
||||
df::item *container = Items::getContainer(i);
|
||||
if (container)
|
||||
return inStockpile(container);
|
||||
|
||||
if (i->pos.z != z) return false;
|
||||
if (i->pos.x < x1 || i->pos.x >= x2 ||
|
||||
i->pos.y < y1 || i->pos.y >= y2) return false;
|
||||
int e = (i->pos.x - x1) + (i->pos.y - y1) * sp->room.width;
|
||||
return sp->room.extents[e] == 1;
|
||||
}
|
||||
|
||||
int getId() { return sp->id; }
|
||||
};
|
||||
|
||||
static command_result stockcheck(color_ostream &out, vector <string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
|
||||
std::vector<StockpileInfo*> stockpiles;
|
||||
|
||||
for (int i = 0; i < world->buildings.all.size(); ++i)
|
||||
{
|
||||
df::building *build = world->buildings.all[i];
|
||||
auto type = build->getType();
|
||||
if (df::enums::building_type::Stockpile == type)
|
||||
{
|
||||
building_stockpilest *sp = virtual_cast<building_stockpilest>(build);
|
||||
StockpileInfo *spi = new StockpileInfo(sp);
|
||||
stockpiles.push_back(spi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<df::item*> &items = world->items.other[items_other_id::ANY_FREE];
|
||||
|
||||
// Precompute a bitmask with the bad flags
|
||||
df::item_flags bad_flags;
|
||||
bad_flags.whole = 0;
|
||||
|
||||
#define F(x) bad_flags.bits.x = true;
|
||||
F(dump); F(forbid); F(garbage_collect);
|
||||
F(hostile); F(on_fire); F(rotten); F(trader);
|
||||
F(in_building); F(construction); F(artifact1);
|
||||
F(spider_web); F(owned); F(in_job);
|
||||
#undef F
|
||||
|
||||
for (size_t i = 0; i < items.size(); i++)
|
||||
{
|
||||
df::item *item = items[i];
|
||||
if (item->flags.whole & bad_flags.whole)
|
||||
continue;
|
||||
|
||||
// we really only care about MEAT, FISH, FISH_RAW, PLANT, CHEESE, FOOD, and EGG
|
||||
|
||||
df::item_type typ = item->getType();
|
||||
if (typ != df::enums::item_type::MEAT &&
|
||||
typ != df::enums::item_type::FISH &&
|
||||
typ != df::enums::item_type::FISH_RAW &&
|
||||
typ != df::enums::item_type::PLANT &&
|
||||
typ != df::enums::item_type::CHEESE &&
|
||||
typ != df::enums::item_type::FOOD &&
|
||||
typ != df::enums::item_type::EGG)
|
||||
continue;
|
||||
|
||||
df::item *container = 0;
|
||||
df::unit *holder = 0;
|
||||
df::building *building = 0;
|
||||
|
||||
for (size_t i = 0; i < item->itemrefs.size(); i++)
|
||||
{
|
||||
df::general_ref *ref = item->itemrefs[i];
|
||||
|
||||
switch (ref->getType())
|
||||
{
|
||||
case general_ref_type::CONTAINED_IN_ITEM:
|
||||
container = ref->getItem();
|
||||
break;
|
||||
|
||||
case general_ref_type::UNIT_HOLDER:
|
||||
holder = ref->getUnit();
|
||||
break;
|
||||
|
||||
case general_ref_type::BUILDING_HOLDER:
|
||||
building = ref->getBuilding();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
df::item *nextcontainer = container;
|
||||
df::item *lastcontainer = 0;
|
||||
|
||||
while(nextcontainer) {
|
||||
df::item *thiscontainer = nextcontainer;
|
||||
nextcontainer = 0;
|
||||
for (size_t i = 0; i < thiscontainer->itemrefs.size(); i++)
|
||||
{
|
||||
df::general_ref *ref = thiscontainer->itemrefs[i];
|
||||
|
||||
switch (ref->getType())
|
||||
{
|
||||
case general_ref_type::CONTAINED_IN_ITEM:
|
||||
lastcontainer = nextcontainer = ref->getItem();
|
||||
break;
|
||||
|
||||
case general_ref_type::UNIT_HOLDER:
|
||||
holder = ref->getUnit();
|
||||
break;
|
||||
|
||||
case general_ref_type::BUILDING_HOLDER:
|
||||
building = ref->getBuilding();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder)
|
||||
continue; // carried items do not rot as far as i know
|
||||
|
||||
if (building) {
|
||||
df::building_type btype = building->getType();
|
||||
if (btype == df::enums::building_type::TradeDepot ||
|
||||
btype == df::enums::building_type::Wagon)
|
||||
continue; // items in trade depot or the embark wagon do not rot
|
||||
|
||||
if (typ == df::enums::item_type::EGG && btype ==df::enums::building_type::NestBox)
|
||||
continue; // eggs in nest box do not rot
|
||||
}
|
||||
|
||||
int canHoldCount = 0;
|
||||
StockpileInfo *current = 0;
|
||||
|
||||
for (int idx = 0; idx < stockpiles.size(); idx++)
|
||||
{
|
||||
StockpileInfo *spi = stockpiles[idx];
|
||||
if (spi->canHold(item)) canHoldCount++;
|
||||
if (spi->inStockpile(item)) current=spi;
|
||||
}
|
||||
|
||||
if (current)
|
||||
continue;
|
||||
|
||||
std::string description;
|
||||
item->getItemDescription(&description, 0);
|
||||
out << " * " << description;
|
||||
|
||||
if (container) {
|
||||
std::string containerDescription;
|
||||
container->getItemDescription(&containerDescription, 0);
|
||||
out << ", in container " << containerDescription;
|
||||
if (lastcontainer) {
|
||||
std::string lastcontainerDescription;
|
||||
lastcontainer->getItemDescription(&lastcontainerDescription, 0);
|
||||
out << ", in container " << lastcontainerDescription;
|
||||
}
|
||||
}
|
||||
|
||||
if (holder) {
|
||||
out << ", carried";
|
||||
}
|
||||
|
||||
if (building) {
|
||||
out << ", in building " << building->id << " (type=" << building->getType() << ")";
|
||||
}
|
||||
|
||||
out << ", flags=" << std::hex << item->flags.whole << std::dec;
|
||||
out << endl;
|
||||
|
||||
}
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
@ -0,0 +1,103 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <climits>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <cstdio>
|
||||
using namespace std;
|
||||
|
||||
#include "Core.h"
|
||||
#include "Console.h"
|
||||
#include "Export.h"
|
||||
#include "PluginManager.h"
|
||||
#include "modules/Units.h"
|
||||
#include "modules/Maps.h"
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/World.h"
|
||||
#include "MiscUtils.h"
|
||||
|
||||
#include <df/ui.h>
|
||||
#include "df/world.h"
|
||||
#include "df/world_raws.h"
|
||||
#include "df/building_def.h"
|
||||
#include "df/unit_inventory_item.h"
|
||||
#include <df/creature_raw.h>
|
||||
#include <df/caste_raw.h>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
using df::global::world;
|
||||
using df::global::cursor;
|
||||
using df::global::ui;
|
||||
|
||||
using namespace DFHack::Gui;
|
||||
|
||||
command_result df_stripcaged(color_ostream &out, vector <string> & parameters);
|
||||
|
||||
DFHACK_PLUGIN("stripcaged");
|
||||
|
||||
// check if contained in item (e.g. animals in cages)
|
||||
bool isContainedInItem(df::unit* unit)
|
||||
{
|
||||
bool contained = false;
|
||||
for (size_t r=0; r < unit->refs.size(); r++)
|
||||
{
|
||||
df::general_ref * ref = unit->refs[r];
|
||||
auto rtype = ref->getType();
|
||||
if(rtype == df::general_ref_type::CONTAINED_IN_ITEM)
|
||||
{
|
||||
contained = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return contained;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
commands.push_back(PluginCommand(
|
||||
"stripcaged", "strip caged units of all items",
|
||||
df_stripcaged, false,
|
||||
"Clears forbid and sets dump for the inventories of all caged units."
|
||||
));
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
|
||||
{
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
command_result df_stripcaged(color_ostream &out, vector <string> & parameters)
|
||||
{
|
||||
CoreSuspender suspend;
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i=0; i < world->units.all.size(); i++)
|
||||
{
|
||||
df::unit* unit = world->units.all[i];
|
||||
if (isContainedInItem(unit))
|
||||
{
|
||||
for (size_t j=0; j < unit->inventory.size(); j++)
|
||||
{
|
||||
df::unit_inventory_item* uii = unit->inventory[j];
|
||||
if (uii->item)
|
||||
{
|
||||
uii->item->flags.bits.forbid = 0;
|
||||
uii->item->flags.bits.dump = 1;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out << count << " items marked for dumping" << endl;
|
||||
|
||||
return CR_OK;
|
||||
}
|
@ -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