Conflicts:
	library/xml
develop
Petr Mrázek 2012-01-09 20:43:31 +01:00
commit 263bed7d95
14 changed files with 715 additions and 86 deletions

@ -197,6 +197,16 @@ std::string DFHack::bitfieldToString(const void *p, int size, const bitfield_ite
return res; return res;
} }
int DFHack::findBitfieldField(const std::string &name, int size, const bitfield_item_info *items)
{
for (int i = 0; i < size*8; i++) {
if (items[i].name && items[i].name == name)
return i;
}
return -1;
}
#define SIMPLE_GLOBAL(name,tname) \ #define SIMPLE_GLOBAL(name,tname) \
tname *df::global::name = NULL; tname *df::global::name = NULL;
#define GLOBAL(name,tname) SIMPLE_GLOBAL(name,df::tname) #define GLOBAL(name,tname) SIMPLE_GLOBAL(name,df::tname)

@ -34,6 +34,7 @@ distribution.
#include <ctime> #include <ctime>
#endif #endif
#include <ctype.h>
#include <stdarg.h> #include <stdarg.h>
std::string stl_sprintf(const char *fmt, ...) { std::string stl_sprintf(const char *fmt, ...) {
@ -59,6 +60,45 @@ std::string stl_vsprintf(const char *fmt, va_list args) {
} }
} }
bool split_string(std::vector<std::string> *out,
const std::string &str, const std::string &separator, bool squash_empty)
{
out->clear();
size_t start = 0, pos;
if (!separator.empty())
{
while ((pos = str.find(separator,start)) != std::string::npos)
{
if (pos > start || !squash_empty)
out->push_back(str.substr(start, pos-start));
start = pos + separator.size();
}
}
if (start < str.size() || !squash_empty)
out->push_back(str.substr(start));
return out->size() > 1;
}
std::string toUpper(const std::string &str)
{
std::string rv(str.size(),' ');
for (unsigned i = 0; i < str.size(); ++i)
rv[i] = toupper(str[i]);
return rv;
}
std::string toLower(const std::string &str)
{
std::string rv(str.size(),' ');
for (unsigned i = 0; i < str.size(); ++i)
rv[i] = tolower(str[i]);
return rv;
}
#ifdef LINUX_BUILD // Linux #ifdef LINUX_BUILD // Linux
uint64_t GetTimeMs64() uint64_t GetTimeMs64()
{ {

@ -125,24 +125,32 @@ namespace DFHack
template<class T> template<class T>
T *ifnull(T *a, T *b) { return a ? a : b; } T *ifnull(T *a, T *b) { return a ? a : b; }
// Enums
template<class T, T start, bool (*isvalid)(T)> template<class T, T start, bool (*isvalid)(T)>
inline T next_enum_item_(T v) { inline T next_enum_item_(T v) {
v = T(int(v) + 1); v = T(int(v) + 1);
return isvalid(v) ? v : start; return isvalid(v) ? v : start;
} }
struct bitfield_item_info {
const char *name;
int size;
};
template<class T> template<class T>
struct enum_list_attr { struct enum_list_attr {
int size; int size;
const T *items; const T *items;
}; };
// Bitfields
struct bitfield_item_info {
const char *name;
int size;
};
DFHACK_EXPORT std::string bitfieldToString(const void *p, int size, const bitfield_item_info *items); DFHACK_EXPORT std::string bitfieldToString(const void *p, int size, const bitfield_item_info *items);
DFHACK_EXPORT int findBitfieldField(const std::string &name, int size, const bitfield_item_info *items);
template<class T>
inline int findBitfieldField(const T &val, const std::string &name) {
return findBitfieldField(name, sizeof(val.whole), val.get_items());
}
template<class T> template<class T>
inline std::string bitfieldToString(const T &val) { inline std::string bitfieldToString(const T &val) {
@ -150,6 +158,21 @@ namespace DFHack
} }
} }
template<class T>
int linear_index(const DFHack::enum_list_attr<T> &lst, T val) {
for (int i = 0; i < lst.size; i++)
if (lst.items[i] == val)
return i;
return -1;
}
inline int linear_index(const DFHack::enum_list_attr<const char*> &lst, const std::string &val) {
for (int i = 0; i < lst.size; i++)
if (lst.items[i] == val)
return i;
return -1;
}
namespace df namespace df
{ {
using DFHack::virtual_ptr; using DFHack::virtual_ptr;

@ -163,6 +163,28 @@ inline bool vector_contains(const std::vector<CT*> &vec, FT CT::*field, FT key)
return binsearch_index(vec, field, key) >= 0; return binsearch_index(vec, field, key) >= 0;
} }
template<typename T>
inline T vector_get(const std::vector<T> &vec, unsigned idx, const T &defval = T())
{
if (idx < vec.size())
return vec[idx];
else
return defval;
}
template<typename T>
inline void vector_insert_at(std::vector<T> &vec, unsigned idx, const T &val)
{
vec.insert(vec.begin()+idx, val);
}
template<typename T>
inline void vector_erase_at(std::vector<T> &vec, unsigned idx)
{
if (idx < vec.size())
vec.erase(vec.begin()+idx);
}
template<typename FT> template<typename FT>
unsigned insert_into_vector(std::vector<FT> &vec, FT key, bool *inserted = NULL) unsigned insert_into_vector(std::vector<FT> &vec, FT key, bool *inserted = NULL)
{ {
@ -170,7 +192,7 @@ unsigned insert_into_vector(std::vector<FT> &vec, FT key, bool *inserted = NULL)
bool to_ins = (pos >= vec.size() || vec[pos] != key); bool to_ins = (pos >= vec.size() || vec[pos] != key);
if (inserted) *inserted = to_ins; if (inserted) *inserted = to_ins;
if (to_ins) if (to_ins)
vec.insert(vec.begin()+pos,key); vector_insert_at(vec, pos, key);
return pos; return pos;
} }
@ -181,7 +203,7 @@ unsigned insert_into_vector(std::vector<CT*> &vec, FT CT::*field, CT *obj, bool
bool to_ins = (pos >= vec.size() || vec[pos] != obj); bool to_ins = (pos >= vec.size() || vec[pos] != obj);
if (inserted) *inserted = to_ins; if (inserted) *inserted = to_ins;
if (to_ins) if (to_ins)
vec.insert(vec.begin()+pos,obj); vector_insert_at(vec, pos, obj);
return pos; return pos;
} }
@ -213,10 +235,33 @@ Link *linked_list_append(Link *head, Link *tail)
return tail; return tail;
} }
template<typename Link>
Link *linked_list_insert_after(Link *pos, Link *link)
{
link->next = pos->next;
if (pos->next)
pos->next->prev = link;
link->prev = pos;
pos->next = link;
return link;
}
/* /*
* MISC * MISC
*/ */
DFHACK_EXPORT bool split_string(std::vector<std::string> *out,
const std::string &str, const std::string &separator,
bool squash_empty = false);
DFHACK_EXPORT std::string toUpper(const std::string &str);
DFHACK_EXPORT std::string toLower(const std::string &str);
inline bool bits_match(unsigned required, unsigned ok, unsigned mask)
{
return (required & mask) == (required & mask & ok);
}
/** /**
* Returns the amount of milliseconds elapsed since the UNIX epoch. * Returns the amount of milliseconds elapsed since the UNIX epoch.
* Works on both windows and linux. * Works on both windows and linux.

@ -32,6 +32,15 @@ distribution.
#include "Virtual.h" #include "Virtual.h"
#include "modules/Materials.h" #include "modules/Materials.h"
#include "MemAccess.h" #include "MemAccess.h"
#include "DataDefs.h"
#include "df/item_type.h"
namespace df
{
struct itemdef;
}
/** /**
* \defgroup grp_items Items module and its types * \defgroup grp_items Items module and its types
* @ingroup grp_modules * @ingroup grp_modules
@ -39,6 +48,42 @@ distribution.
namespace DFHack namespace DFHack
{ {
struct DFHACK_EXPORT ItemTypeInfo {
df::item_type type;
int16_t subtype;
df::itemdef *custom;
public:
ItemTypeInfo(df::item_type type_ = df::enums::item_type::NONE, int16_t subtype_ = -1) {
decode(type_, subtype_);
}
template<class T> ItemTypeInfo(T *ptr) { decode(ptr); }
bool isValid() const {
return (type != df::enums::item_type::NONE) && (subtype == -1 || custom);
}
bool decode(df::item_type type_, int16_t subtype_ = -1);
bool decode(df::item *ptr);
template<class T> bool decode(T *ptr) {
return ptr ? decode(ptr->item_type, ptr->item_subtype) : decode(df::enums::item_type::NONE);
}
std::string toString();
bool find(const std::string &token);
bool matches(const df::job_item &item, MaterialInfo *mat = NULL);
};
inline bool operator== (const ItemTypeInfo &a, const ItemTypeInfo &b) {
return a.type == b.type && a.subtype == b.subtype;
}
inline bool operator!= (const ItemTypeInfo &a, const ItemTypeInfo &b) {
return a.type != b.type || a.subtype != b.subtype;
}
class Context; class Context;
class DFContextShared; class DFContextShared;

@ -35,6 +35,7 @@ namespace df
struct job; struct job;
struct job_item; struct job_item;
struct job_item_filter; struct job_item_filter;
struct building;
} }
namespace DFHack namespace DFHack
@ -49,6 +50,10 @@ namespace DFHack
DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b); DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b);
DFHACK_EXPORT void printJobDetails(Core *c, df::job *job); DFHACK_EXPORT void printJobDetails(Core *c, df::job *job);
DFHACK_EXPORT df::building *getJobHolder(df::job *job);
DFHACK_EXPORT bool linkJobIntoWorld(df::job *job, bool new_id = true);
} }
#endif #endif

@ -101,7 +101,8 @@ namespace DFHack
return ptr ? decode(ptr->mat_type, ptr->mat_index) : decode(-1); return ptr ? decode(ptr->mat_type, ptr->mat_index) : decode(-1);
} }
bool find(const std::string &token, const std::string &subtoken = std::string()); bool find(const std::string &token);
bool findBuiltin(const std::string &token); bool findBuiltin(const std::string &token);
bool findInorganic(const std::string &token); bool findInorganic(const std::string &token);
bool findPlant(const std::string &token, const std::string &subtoken); bool findPlant(const std::string &token, const std::string &subtoken);
@ -121,6 +122,8 @@ namespace DFHack
bool matches(const df::job_item &item); bool matches(const df::job_item &item);
}; };
DFHACK_EXPORT bool parseJobMaterialCategory(df::job_material_category *cat, const std::string &token);
inline bool operator== (const MaterialInfo &a, const MaterialInfo &b) { inline bool operator== (const MaterialInfo &a, const MaterialInfo &b) {
return a.type == b.type && a.index == b.index; return a.type == b.type && a.index == b.index;
} }

@ -42,8 +42,338 @@ using namespace std;
#include "ModuleFactory.h" #include "ModuleFactory.h"
#include "Core.h" #include "Core.h"
#include "Virtual.h" #include "Virtual.h"
#include "MiscUtils.h"
#include "df/world.h"
#include "df/item.h"
#include "df/tool_uses.h"
#include "df/itemdef_weaponst.h"
#include "df/itemdef_trapcompst.h"
#include "df/itemdef_toyst.h"
#include "df/itemdef_toolst.h"
#include "df/itemdef_instrumentst.h"
#include "df/itemdef_armorst.h"
#include "df/itemdef_ammost.h"
#include "df/itemdef_siegeammost.h"
#include "df/itemdef_glovesst.h"
#include "df/itemdef_shoesst.h"
#include "df/itemdef_shieldst.h"
#include "df/itemdef_helmst.h"
#include "df/itemdef_pantsst.h"
#include "df/itemdef_foodst.h"
#include "df/trapcomp_flags.h"
#include "df/job_item.h"
using namespace DFHack; using namespace DFHack;
using namespace df::enums;
#define ITEMDEF_VECTORS \
ITEM(WEAPON, weapons) \
ITEM(TRAPCOMP, trapcomps) \
ITEM(TOY, toys) \
ITEM(TOOL, tools) \
ITEM(INSTRUMENT, instruments) \
ITEM(ARMOR, armor) \
ITEM(AMMO, ammo) \
ITEM(SIEGEAMMO, siege_ammo) \
ITEM(GLOVES, gloves) \
ITEM(SHOES, shoes) \
ITEM(SHIELD, shields) \
ITEM(HELM, helms) \
ITEM(PANTS, pants) \
ITEM(FOOD, food)
bool ItemTypeInfo::decode(df::item_type type_, int16_t subtype_)
{
using namespace df::enums::item_type;
type = type_;
subtype = subtype_;
custom = NULL;
df::world_raws::T_itemdefs &defs = df::global::world->raws.itemdefs;
switch (type_) {
case NONE:
return false;
#define ITEM(type,vec) \
case type: \
custom = vector_get(defs.vec, subtype); \
break;
ITEMDEF_VECTORS
#undef ITEM
default:
break;
}
return isValid();
}
bool ItemTypeInfo::decode(df::item *ptr)
{
if (!ptr)
return decode(item_type::NONE);
else
return decode(ptr->getType(), ptr->getSubtype());
}
std::string ItemTypeInfo::toString()
{
std::string rv = ENUM_KEY_STR(item_type, type);
if (custom)
rv += ":" + custom->id;
else if (subtype != -1)
rv += stl_sprintf(":%d", subtype);
return rv;
}
bool ItemTypeInfo::find(const std::string &token)
{
using namespace df::enums::item_type;
std::vector<std::string> items;
split_string(&items, token, ":");
type = NONE;
subtype = -1;
custom = NULL;
if (items.size() < 1 || items.size() > 2)
return false;
if (items[0] == "NONE")
return true;
FOR_ENUM_ITEMS(item_type, i)
{
const char *key = ENUM_ATTR(item_type, key, i);
if (key && key == items[0])
{
type = i;
break;
}
}
if (type == NONE)
return false;
if (items.size() == 1)
return true;
df::world_raws::T_itemdefs &defs = df::global::world->raws.itemdefs;
switch (type) {
#define ITEM(type,vec) \
case type: \
for (int i = 0; i < defs.vec.size(); i++) { \
if (defs.vec[i]->id == items[1]) { \
subtype = i; custom = defs.vec[i]; return true; \
} \
} \
break;
ITEMDEF_VECTORS
#undef ITEM
default:
break;
}
return (subtype >= 0);
}
bool ItemTypeInfo::matches(const df::job_item &item, MaterialInfo *mat)
{
using namespace df::enums::item_type;
if (!isValid())
return mat ? mat->matches(item) : true;
df::job_item_flags1 ok1, mask1, item_ok1, item_mask1;
df::job_item_flags2 ok2, mask2, item_ok2, item_mask2;
df::job_item_flags3 ok3, mask3, item_ok3, item_mask3;
ok1.whole = mask1.whole = item_ok1.whole = item_mask1.whole = 0;
ok2.whole = mask2.whole = item_ok2.whole = item_mask2.whole = 0;
ok3.whole = mask3.whole = item_ok3.whole = item_mask3.whole = 0;
if (mat) {
mat->getMatchBits(ok1, mask1);
mat->getMatchBits(ok2, mask2);
mat->getMatchBits(ok3, mask3);
}
#define OK(id,name) item_ok##id.bits.name = true
#define RQ(id,name) item_mask##id.bits.name = true
// Set up the flag mask
RQ(1,millable); RQ(1,sharpenable); RQ(1,distillable); RQ(1,processable); RQ(1,bag);
RQ(1,extract_bearing_plant); RQ(1,extract_bearing_fish); RQ(1,extract_bearing_vermin);
RQ(1,processable_to_vial); RQ(1,processable_to_bag); RQ(1,processable_to_barrel);
RQ(1,solid); RQ(1,tameable_vermin); RQ(1,sand_bearing); RQ(1,milk); RQ(1,milkable);
RQ(1,not_bin); RQ(1,lye_bearing);
RQ(2,dye); RQ(2,dyeable); RQ(2,dyed); RQ(2,glass_making); RQ(2,screw);
RQ(2,building_material); RQ(2,fire_safe); RQ(2,magma_safe); RQ(2,non_economic);
RQ(2,totemable); RQ(2,plaster_containing); RQ(2,body_part); RQ(2,lye_milk_free);
RQ(2,blunt); RQ(2,unengraved); RQ(2,hair_wool);
RQ(3,any_raw_material); RQ(3,non_pressed); RQ(3,food_storage);
// Compute the ok mask
OK(1,solid);
OK(1,not_bin);
// TODO: furniture, ammo, finished good, craft
switch (type) {
case PLANT:
OK(1,millable); OK(1,processable);
OK(1,distillable);
OK(1,extract_bearing_plant);
OK(1,processable_to_vial);
OK(1,processable_to_bag);
OK(1,processable_to_barrel);
break;
case BOULDER:
OK(1,sharpenable);
OK(2,non_economic);
case BAR:
OK(3,any_raw_material);
case BLOCKS:
case WOOD:
OK(2,building_material);
OK(2,fire_safe); OK(2,magma_safe);
break;
case VERMIN:
OK(1,extract_bearing_fish);
OK(1,extract_bearing_vermin);
OK(1,tameable_vermin);
OK(1,milkable);
break;
case DRINK:
item_ok1.bits.solid = false;
break;
case ROUGH:
OK(2,glass_making);
break;
case ANIMALTRAP:
case CAGE:
OK(1,milk);
OK(1,milkable);
break;
case BUCKET:
case FLASK:
OK(1,milk);
break;
case TOOL:
OK(1,lye_bearing);
OK(1,milk);
OK(2,lye_milk_free);
OK(2,blunt);
if (VIRTUAL_CAST_VAR(def, df::itemdef_toolst, custom)) {
df::enum_field<df::tool_uses,int16_t> key(tool_uses::FOOD_STORAGE);
if (linear_index(def->tool_use, key) >= 0)
OK(3,food_storage);
} else {
OK(3,food_storage);
}
break;
case BARREL:
OK(1,lye_bearing);
OK(1,milk);
OK(2,lye_milk_free);
OK(3,food_storage);
break;
case BOX:
OK(1,bag); OK(1,sand_bearing); OK(1,milk);
OK(2,dye); OK(2,plaster_containing);
break;
case BIN:
item_ok1.bits.not_bin = false;
break;
case LIQUID_MISC:
item_ok1.bits.solid = false;
OK(1,milk);
break;
case POWDER_MISC:
OK(2,dye);
case GLOB:
OK(3,any_raw_material);
OK(3,non_pressed);
break;
case THREAD:
case CLOTH:
OK(2,dyeable); OK(2,dyed);
break;
case WEAPON:
case AMMO:
case ROCK:
OK(2,blunt);
break;
case TRAPCOMP:
if (VIRTUAL_CAST_VAR(def, df::itemdef_trapcompst, custom)) {
if (def->flags.is_set(trapcomp_flags::IS_SCREW))
OK(2,screw);
} else {
OK(2,screw);
}
OK(2,blunt);
break;
case CORPSE:
case CORPSEPIECE:
OK(2,totemable);
OK(2,body_part);
OK(2,hair_wool);
break;
case SLAB:
OK(2,unengraved);
break;
case ANVIL:
OK(2,fire_safe); OK(2,magma_safe);
break;
default:
break;
}
if ((item_ok1.whole & ~item_mask1.whole) ||
(item_ok2.whole & ~item_mask2.whole) ||
(item_ok3.whole & ~item_mask3.whole))
Core::getInstance().con.printerr("ItemTypeInfo.matches inconsistent\n");
#undef OK
#undef RQ
return bits_match(item.flags1.whole, ok1.whole, mask1.whole) &&
bits_match(item.flags2.whole, ok2.whole, mask2.whole) &&
bits_match(item.flags3.whole, ok3.whole, mask3.whole) &&
bits_match(item.flags1.whole, item_ok1.whole, item_mask1.whole) &&
bits_match(item.flags2.whole, item_ok2.whole, item_mask2.whole) &&
bits_match(item.flags3.whole, item_ok3.whole, item_mask3.whole);
}
Module* DFHack::createItems() Module* DFHack::createItems()
{ {

@ -33,9 +33,11 @@ using namespace std;
#include "Core.h" #include "Core.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "MiscUtils.h"
#include "modules/Job.h" #include "modules/Job.h"
#include "modules/Materials.h" #include "modules/Materials.h"
#include "modules/Items.h"
#include "DataDefs.h" #include "DataDefs.h"
#include <df/world.h> #include <df/world.h>
@ -45,6 +47,7 @@ using namespace std;
#include <df/job_list_link.h> #include <df/job_list_link.h>
#include <df/general_ref.h> #include <df/general_ref.h>
#include <df/general_ref_unit_workerst.h> #include <df/general_ref_unit_workerst.h>
#include <df/general_ref_building_holderst.h>
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
@ -69,7 +72,7 @@ df::job *DFHack::cloneJobStruct(df::job *job)
df::general_ref *ref = pnew->references[i]; df::general_ref *ref = pnew->references[i];
if (virtual_cast<df::general_ref_unit_workerst>(ref)) if (virtual_cast<df::general_ref_unit_workerst>(ref))
pnew->references.erase(pnew->references.begin()+i); vector_erase_at(pnew->references, i);
else else
pnew->references[i] = ref->clone(); pnew->references[i] = ref->clone();
} }
@ -138,9 +141,9 @@ bool DFHack::operator== (const df::job &a, const df::job &b)
static void print_job_item_details(Core *c, df::job *job, unsigned idx, df::job_item *item) static void print_job_item_details(Core *c, df::job *job, unsigned idx, df::job_item *item)
{ {
c->con << " Input Item " << (idx+1) << ": " << ENUM_KEY_STR(item_type,item->item_type); ItemTypeInfo info(item);
if (item->item_subtype != -1) c->con << " Input Item " << (idx+1) << ": " << info.toString();
c->con << " [" << item->item_subtype << "]";
if (item->quantity != 1) if (item->quantity != 1)
c->con << "; quantity=" << item->quantity; c->con << "; quantity=" << item->quantity;
if (item->min_dimension >= 0) if (item->min_dimension >= 0)
@ -177,7 +180,12 @@ void DFHack::printJobDetails(Core *c, df::job *job)
c->con << " (" << bitfieldToString(job->flags) << ")"; c->con << " (" << bitfieldToString(job->flags) << ")";
c->con << endl; c->con << endl;
df::item_type itype = ENUM_ATTR(job_type, item, job->job_type);
MaterialInfo mat(job); MaterialInfo mat(job);
if (itype == item_type::FOOD)
mat.decode(-1);
if (mat.isValid() || job->material_category.whole) if (mat.isValid() || job->material_category.whole)
{ {
c->con << " material: " << mat.toString(); c->con << " material: " << mat.toString();
@ -187,8 +195,12 @@ void DFHack::printJobDetails(Core *c, df::job *job)
} }
if (job->item_subtype >= 0 || job->item_category.whole) if (job->item_subtype >= 0 || job->item_category.whole)
c->con << " item: " << job->item_subtype {
ItemTypeInfo iinfo(itype, job->item_subtype);
c->con << " item: " << iinfo.toString()
<< " (" << bitfieldToString(job->item_category) << ")" << endl; << " (" << bitfieldToString(job->item_category) << ")" << endl;
}
if (job->hist_figure_id >= 0) if (job->hist_figure_id >= 0)
c->con << " figure: " << job->hist_figure_id << endl; c->con << " figure: " << job->hist_figure_id << endl;
@ -199,3 +211,44 @@ void DFHack::printJobDetails(Core *c, df::job *job)
for (unsigned i = 0; i < job->job_items.size(); i++) for (unsigned i = 0; i < job->job_items.size(); i++)
print_job_item_details(c, job, i, job->job_items[i]); print_job_item_details(c, job, i, job->job_items[i]);
} }
df::building *DFHack::getJobHolder(df::job *job)
{
for (unsigned i = 0; i < job->references.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]);
if (ref)
return ref->getBuilding();
}
return NULL;
}
bool DFHack::linkJobIntoWorld(df::job *job, bool new_id)
{
using df::global::world;
using df::global::job_next_id;
assert(!job->list_link);
if (new_id) {
job->id = (*job_next_id)++;
job->list_link = new df::job_list_link();
job->list_link->item = job;
linked_list_append(&world->job_list, job->list_link);
return true;
} else {
df::job_list_link *ins_pos = &world->job_list;
while (ins_pos->next && ins_pos->next->item->id < job->id)
ins_pos = ins_pos->next;
if (ins_pos->next && ins_pos->next->item->id == job->id)
return false;
job->list_link = new df::job_list_link();
job->list_link->item = job;
linked_list_insert_after(ins_pos, job->list_link);
return true;
}
}

@ -143,27 +143,48 @@ bool MaterialInfo::decode(int16_t type, int32_t index)
return (material != NULL); return (material != NULL);
} }
bool MaterialInfo::find(const std::string &token, const std::string &subtoken) bool MaterialInfo::find(const std::string &token)
{ {
if (findBuiltin(token)) std::vector<std::string> items;
return true; split_string(&items, token, ":");
if (subtoken.empty())
if (items[0] == "INORGANIC")
return findInorganic(vector_get(items,1));
if (items[0] == "CREATURE_MAT" || items[0] == "CREATURE")
return findCreature(vector_get(items,1), vector_get(items,2));
if (items[0] == "PLANT_MAT" || items[0] == "PLANT")
return findPlant(vector_get(items,1), vector_get(items,2));
if (items.size() == 1)
{ {
if (findInorganic(token)) if (findBuiltin(items[0]))
return true;
if (findInorganic(items[0]))
return true;
if (findPlant(items[0], ""))
return true; return true;
} }
else else if (items.size() == 2)
{ {
if (findPlant(token, subtoken)) if (findPlant(items[0], items[1]))
return true; return true;
if (findCreature(token, subtoken)) if (findCreature(items[0], items[1]))
return true; return true;
} }
return false; return false;
} }
bool MaterialInfo::findBuiltin(const std::string &token) bool MaterialInfo::findBuiltin(const std::string &token)
{ {
if (token.empty())
return decode(-1);
if (token == "NONE") {
decode(-1);
return true;
}
df::world_raws &raws = df::global::world->raws; df::world_raws &raws = df::global::world->raws;
for (int i = 1; i < NUM_BUILTIN; i++) for (int i = 1; i < NUM_BUILTIN; i++)
if (raws.mat_table.builtin[i]->id == token) if (raws.mat_table.builtin[i]->id == token)
@ -173,6 +194,14 @@ bool MaterialInfo::findBuiltin(const std::string &token)
bool MaterialInfo::findInorganic(const std::string &token) bool MaterialInfo::findInorganic(const std::string &token)
{ {
if (token.empty())
return decode(-1);
if (token == "NONE") {
decode(0, -1);
return true;
}
df::world_raws &raws = df::global::world->raws; df::world_raws &raws = df::global::world->raws;
for (unsigned i = 0; i < raws.inorganics.size(); i++) for (unsigned i = 0; i < raws.inorganics.size(); i++)
{ {
@ -185,6 +214,8 @@ bool MaterialInfo::findInorganic(const std::string &token)
bool MaterialInfo::findPlant(const std::string &token, const std::string &subtoken) bool MaterialInfo::findPlant(const std::string &token, const std::string &subtoken)
{ {
if (token.empty())
return decode(-1);
df::world_raws &raws = df::global::world->raws; df::world_raws &raws = df::global::world->raws;
for (unsigned i = 0; i < raws.plants.all.size(); i++) for (unsigned i = 0; i < raws.plants.all.size(); i++)
{ {
@ -192,6 +223,10 @@ bool MaterialInfo::findPlant(const std::string &token, const std::string &subtok
if (p->id != token) if (p->id != token)
continue; continue;
// As a special exception, return the structural material with empty subtoken
if (subtoken.empty())
return decode(p->material_defs.type_basic_mat, p->material_defs.idx_basic_mat);
for (unsigned j = 0; j < p->material.size(); j++) for (unsigned j = 0; j < p->material.size(); j++)
if (p->material[j]->id == subtoken) if (p->material[j]->id == subtoken)
return decode(PLANT_BASE+j, i); return decode(PLANT_BASE+j, i);
@ -203,6 +238,8 @@ bool MaterialInfo::findPlant(const std::string &token, const std::string &subtok
bool MaterialInfo::findCreature(const std::string &token, const std::string &subtoken) bool MaterialInfo::findCreature(const std::string &token, const std::string &subtoken)
{ {
if (token.empty() || subtoken.empty())
return decode(-1);
df::world_raws &raws = df::global::world->raws; df::world_raws &raws = df::global::world->raws;
for (unsigned i = 0; i < raws.creatures.all.size(); i++) for (unsigned i = 0; i < raws.creatures.all.size(); i++)
{ {
@ -298,7 +335,7 @@ bool MaterialInfo::matches(const df::job_material_category &cat)
bool MaterialInfo::matches(const df::job_item &item) bool MaterialInfo::matches(const df::job_item &item)
{ {
if (!isValid()) return false; if (!isValid()) return true;
df::job_item_flags1 ok1, mask1; df::job_item_flags1 ok1, mask1;
getMatchBits(ok1, mask1); getMatchBits(ok1, mask1);
@ -309,9 +346,9 @@ bool MaterialInfo::matches(const df::job_item &item)
df::job_item_flags3 ok3, mask3; df::job_item_flags3 ok3, mask3;
getMatchBits(ok3, mask3); getMatchBits(ok3, mask3);
return ((item.flags1.whole & mask1.whole) == (item.flags1.whole & ok1.whole)) && return bits_match(item.flags1.whole, ok1.whole, mask1.whole) &&
((item.flags2.whole & mask2.whole) == (item.flags2.whole & ok2.whole)) && bits_match(item.flags2.whole, ok2.whole, mask2.whole) &&
((item.flags3.whole & mask3.whole) == (item.flags3.whole & ok3.whole)); bits_match(item.flags3.whole, ok3.whole, mask3.whole);
} }
void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask) void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask)
@ -330,7 +367,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &ma
TEST(sharpenable, MAT_FLAG(IS_STONE)); TEST(sharpenable, MAT_FLAG(IS_STONE));
TEST(distillable, structural && FLAG(plant, plant_raw_flags::DRINK)); TEST(distillable, structural && FLAG(plant, plant_raw_flags::DRINK));
TEST(processable, structural && FLAG(plant, plant_raw_flags::THREAD)); TEST(processable, structural && FLAG(plant, plant_raw_flags::THREAD));
TEST(bag, isAnyCloth()); TEST(bag, isAnyCloth() || MAT_FLAG(LEATHER));
TEST(cookable, MAT_FLAG(EDIBLE_COOKED)); TEST(cookable, MAT_FLAG(EDIBLE_COOKED));
TEST(extract_bearing_plant, structural && FLAG(plant, plant_raw_flags::EXTRACT_STILL_VIAL)); TEST(extract_bearing_plant, structural && FLAG(plant, plant_raw_flags::EXTRACT_STILL_VIAL));
TEST(extract_bearing_fish, false); TEST(extract_bearing_fish, false);
@ -344,7 +381,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &ma
MAT_FLAG(LIQUID_MISC_CREATURE) || MAT_FLAG(LIQUID_MISC_CREATURE) ||
MAT_FLAG(LIQUID_MISC_OTHER))); MAT_FLAG(LIQUID_MISC_OTHER)));
TEST(tameable_vermin, false); TEST(tameable_vermin, false);
TEST(sharpenable, MAT_FLAG(IS_GLASS)); TEST(sharpenable, MAT_FLAG(IS_STONE));
TEST(milk, linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0); TEST(milk, linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0);
//04000000 - "milkable" - vtable[107],1,1 //04000000 - "milkable" - vtable[107],1,1
} }
@ -393,6 +430,25 @@ void MaterialInfo::getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &ma
#undef FLAG #undef FLAG
#undef TEST #undef TEST
bool DFHack::parseJobMaterialCategory(df::job_material_category *cat, const std::string &token)
{
cat->whole = 0;
std::vector<std::string> items;
split_string(&items, toLower(token), ",", true);
for (unsigned i = 0; i < items.size(); i++)
{
int id = findBitfieldField(*cat, items[i]);
if (id < 0)
return false;
cat->whole |= (1 << id);
}
return true;
}
Module* DFHack::createMaterials() Module* DFHack::createMaterials()
{ {
return new Materials(); return new Materials();

@ -265,7 +265,7 @@ PersistentDataItem World::AddPersistentData(const std::string &key)
hfig->id = new_id; hfig->id = new_id;
hfig->name.has_name = true; hfig->name.has_name = true;
hfig->name.first_name = key; hfig->name.first_name = key;
memset(hfig->name.words, 0, sizeof(hfig->name.words)); memset(hfig->name.words, 0xFF, sizeof(hfig->name.words));
hfvec.insert(hfvec.begin(), hfig); hfvec.insert(hfvec.begin(), hfig);
return dataFromHFig(hfig); return dataFromHFig(hfig);

@ -1 +1 @@
Subproject commit 73a02e7c4cbb06eb439c8d489196cebbab64f1f8 Subproject commit 85dfa3550fe204c8c75279335cae1457cf03e0ed

@ -5,6 +5,7 @@
#include <MiscUtils.h> #include <MiscUtils.h>
#include <modules/Materials.h> #include <modules/Materials.h>
#include <modules/Items.h>
#include <modules/Gui.h> #include <modules/Gui.h>
#include <modules/Job.h> #include <modules/Job.h>
@ -60,12 +61,14 @@ DFhackCExport command_result plugin_init (Core *c, std::vector <PluginCommand> &
PluginCommand( PluginCommand(
"job", "General job query and manipulation.", "job", "General job query and manipulation.",
job_cmd, false, job_cmd, false,
" job query\n" " job [query]\n"
" Print details of the current job.\n" " Print details of the current job.\n"
" job list\n" " job list\n"
" Print details of all jobs in the workshop.\n" " Print details of all jobs in the workshop.\n"
" job item-material <item-idx> <material> [submaterial]\n" " job item-material <item-idx> <material[:subtoken]>\n"
" Replace the exact material id in the job item.\n" " Replace the exact material id in the job item.\n"
" job item-type <item-idx> <type[:subtype]>\n"
" Replace the exact item type id in the job item.\n"
) )
); );
@ -154,6 +157,13 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat)
i, item_mat.toString().c_str()); i, item_mat.toString().c_str());
return CR_FAILURE; return CR_FAILURE;
} }
if (!new_mat.matches(*item))
{
c->con.printerr("Job item %d requirements not satisfied by %s.\n",
i, new_mat.toString().c_str());
return CR_FAILURE;
}
} }
// Apply the substitution // Apply the substitution
@ -249,21 +259,6 @@ static command_result job_material(Core * c, vector <string> & parameters)
/* job-duplicate implementation */ /* job-duplicate implementation */
static df::job *clone_job(df::job *job)
{
df::job *pnew = cloneJobStruct(job);
pnew->id = (*job_next_id)++;
// Link the job into the global list
pnew->list_link = new df::job_list_link();
pnew->list_link->item = pnew;
linked_list_append(&world->job_list, pnew->list_link);
return pnew;
}
static command_result job_duplicate(Core * c, vector <string> & parameters) static command_result job_duplicate(Core * c, vector <string> & parameters)
{ {
if (!parameters.empty()) if (!parameters.empty())
@ -287,24 +282,35 @@ static command_result job_duplicate(Core * c, vector <string> & parameters)
} }
// Actually clone // Actually clone
df::job *pnew = clone_job(job); df::job *pnew = cloneJobStruct(job);
int pos = ++*ui_workshop_job_cursor; linkJobIntoWorld(pnew);
building->jobs.insert(building->jobs.begin()+pos, pnew); vector_insert_at(building->jobs, ++*ui_workshop_job_cursor, pnew);
return CR_OK; return CR_OK;
} }
/* Main job command implementation */ /* Main job command implementation */
static df::job_item *getJobItem(Core *c, df::job *job, std::string idx)
{
if (!job)
return NULL;
int v = atoi(idx.c_str());
if (v < 1 || v > job->job_items.size()) {
c->con.printerr("Invalid item index.\n");
return NULL;
}
return job->job_items[v-1];
}
static command_result job_cmd(Core * c, vector <string> & parameters) static command_result job_cmd(Core * c, vector <string> & parameters)
{ {
CoreSuspender suspend(c); CoreSuspender suspend(c);
if (parameters.empty()) std::string cmd = (parameters.empty() ? "query" : parameters[0]);
return CR_WRONG_USAGE;
std::string cmd = parameters[0];
if (cmd == "query" || cmd == "list") if (cmd == "query" || cmd == "list")
{ {
df::job *job = getSelectedWorkshopJob(c); df::job *job = getSelectedWorkshopJob(c);
@ -321,29 +327,23 @@ static command_result job_cmd(Core * c, vector <string> & parameters)
} }
else if (cmd == "item-material") else if (cmd == "item-material")
{ {
if (parameters.size() < 1+1+1) if (parameters.size() != 3)
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
df::job *job = getSelectedWorkshopJob(c); df::job *job = getSelectedWorkshopJob(c);
if (!job) df::job_item *item = getJobItem(c, job, parameters[1]);
return CR_WRONG_USAGE; if (!item)
int v = atoi(parameters[1].c_str());
if (v < 1 || v > job->job_items.size()) {
c->con.printerr("Invalid item index.\n");
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
}
df::job_item *item = job->job_items[v-1]; ItemTypeInfo iinfo(item);
MaterialInfo minfo;
std::string subtoken = (parameters.size()>3 ? parameters[3] : ""); if (!minfo.find(parameters[2])) {
MaterialInfo info;
if (!info.find(parameters[2], subtoken)) {
c->con.printerr("Could not find the specified material.\n"); c->con.printerr("Could not find the specified material.\n");
return CR_FAILURE; return CR_FAILURE;
} }
if (!info.matches(*item)) { if (!iinfo.matches(*item, &minfo)) {
c->con.printerr("Material does not match the requirements.\n"); c->con.printerr("Material does not match the requirements.\n");
printJobDetails(c, job); printJobDetails(c, job);
return CR_FAILURE; return CR_FAILURE;
@ -353,19 +353,52 @@ static command_result job_cmd(Core * c, vector <string> & parameters)
job->mat_type == item->mat_type && job->mat_type == item->mat_type &&
job->mat_index == item->mat_index) job->mat_index == item->mat_index)
{ {
job->mat_type = info.type; job->mat_type = minfo.type;
job->mat_index = info.index; job->mat_index = minfo.index;
} }
item->mat_type = info.type; item->mat_type = minfo.type;
item->mat_index = info.index; item->mat_index = minfo.index;
c->con << "Job item updated." << endl;
if (item->item_type < 0 && minfo.isValid())
c->con.printerr("WARNING: Due to a probable bug, creature & plant material subtype\n"
" is ignored unless the item type is also specified.\n");
c->con << "Job item " << v << " updated." << endl;
printJobDetails(c, job); printJobDetails(c, job);
return CR_OK; return CR_OK;
} }
else if (cmd == "item-type") else if (cmd == "item-type")
{ {
if (parameters.size() != 3)
return CR_WRONG_USAGE;
df::job *job = getSelectedWorkshopJob(c);
df::job_item *item = getJobItem(c, job, parameters[1]);
if (!item)
return CR_WRONG_USAGE;
ItemTypeInfo iinfo;
MaterialInfo minfo(item);
if (!iinfo.find(parameters[2])) {
c->con.printerr("Could not find the specified item type.\n");
return CR_FAILURE;
}
if (!iinfo.matches(*item, &minfo)) {
c->con.printerr("Item type does not match the requirements.\n");
printJobDetails(c, job);
return CR_FAILURE;
}
item->item_type = iinfo.type;
item->item_subtype = iinfo.subtype;
c->con << "Job item updated." << endl;
printJobDetails(c, job);
return CR_OK;
} }
else else
return CR_WRONG_USAGE; return CR_WRONG_USAGE;

@ -98,18 +98,6 @@ DFhackCExport command_result plugin_onstatechange(Core* c, state_change_event ev
/*******************************/ /*******************************/
static df::building *getJobHolder(df::job *job)
{
for (unsigned i = 0; i < job->references.size(); i++)
{
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]);
if (ref)
return ref->getBuilding();
}
return NULL;
};
struct ProtectedJob { struct ProtectedJob {
int id; int id;
int building_id; int building_id;
@ -253,8 +241,6 @@ static int *find_protected_id_slot(Core *c, int key)
if (key == -1) { if (key == -1) {
protected_cfg.push_back(c->getWorld()->AddPersistentData("workflow/protected-jobs")); protected_cfg.push_back(c->getWorld()->AddPersistentData("workflow/protected-jobs"));
PersistentDataItem &item = protected_cfg.back(); PersistentDataItem &item = protected_cfg.back();
for (int j = 0; j < PersistentDataItem::NumInts; j++)
item.ival(j) = -1;
return &item.ival(0); return &item.ival(0);
} }
@ -408,7 +394,7 @@ DFhackCExport command_result plugin_onupdate(Core* c)
{ {
for (int i = pending_recover.size()-1; i >= 0; i--) for (int i = pending_recover.size()-1; i >= 0; i--)
if (recover_job(c, pending_recover[i])) if (recover_job(c, pending_recover[i]))
pending_recover.erase(pending_recover.begin()+i); vector_erase_at(pending_recover, i);
check_lost_jobs(c); check_lost_jobs(c);
} }