Merge upstream
commit
987cf697db
@ -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 )
|
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 )
|
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 )
|
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 )
|
inline void setassignment( int x, int y, bool bit )
|
||||||
{
|
{
|
||||||
if(bit)
|
return tile_bitmask.setassignment(x, y, bit);
|
||||||
tile_bitmask[y] |= (1 << x);
|
|
||||||
else
|
|
||||||
tile_bitmask[y] &= ~(1 << x);
|
|
||||||
}
|
}
|
||||||
bool has_assignments()
|
bool has_assignments()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 16; i++)
|
return tile_bitmask.has_assignments();
|
||||||
if (tile_bitmask[i])
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
inline bool getassignment( const df::coord2d &xy )
|
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 )
|
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 )
|
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 )
|
inline void setassignment( int x, int y, bool bit )
|
||||||
{
|
{
|
||||||
if(bit)
|
return tile_bitmask.setassignment(x, y, bit);
|
||||||
tile_bitmask[y] |= (1 << x);
|
|
||||||
else
|
|
||||||
tile_bitmask[y] &= ~(1 << x);
|
|
||||||
}
|
}
|
||||||
bool has_assignments()
|
bool has_assignments()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 16; i++)
|
return tile_bitmask.has_assignments();
|
||||||
if (tile_bitmask[i])
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
@ -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