Update structures and implement modifying the job_item item type.

develop
Alexander Gavrilov 2012-01-09 16:20:17 +04:00
parent ea790f1346
commit 50386f66a3
8 changed files with 513 additions and 39 deletions

@ -125,23 +125,25 @@ namespace DFHack
template<class T>
T *ifnull(T *a, T *b) { return a ? a : b; }
// Enums
template<class T, T start, bool (*isvalid)(T)>
inline T next_enum_item_(T v) {
v = T(int(v) + 1);
return isvalid(v) ? v : start;
}
struct bitfield_item_info {
const char *name;
int size;
};
template<class T>
struct enum_list_attr {
int size;
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 int findBitfieldField(const std::string &name, int size, const bitfield_item_info *items);
@ -156,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
{
using DFHack::virtual_ptr;

@ -257,6 +257,11 @@ DFHACK_EXPORT bool split_string(std::vector<std::string> *out,
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.
* Works on both windows and linux.

@ -32,6 +32,15 @@ distribution.
#include "Virtual.h"
#include "modules/Materials.h"
#include "MemAccess.h"
#include "DataDefs.h"
#include "df/item_type.h"
namespace df
{
struct itemdef;
}
/**
* \defgroup grp_items Items module and its types
* @ingroup grp_modules
@ -39,6 +48,42 @@ distribution.
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 DFContextShared;

@ -42,8 +42,338 @@ using namespace std;
#include "ModuleFactory.h"
#include "Core.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 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()
{

@ -37,6 +37,7 @@ using namespace std;
#include "modules/Job.h"
#include "modules/Materials.h"
#include "modules/Items.h"
#include "DataDefs.h"
#include <df/world.h>
@ -140,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)
{
c->con << " Input Item " << (idx+1) << ": " << ENUM_KEY_STR(item_type,item->item_type);
if (item->item_subtype != -1)
c->con << " [" << item->item_subtype << "]";
ItemTypeInfo info(item);
c->con << " Input Item " << (idx+1) << ": " << info.toString();
if (item->quantity != 1)
c->con << "; quantity=" << item->quantity;
if (item->min_dimension >= 0)
@ -179,7 +180,12 @@ void DFHack::printJobDetails(Core *c, df::job *job)
c->con << " (" << bitfieldToString(job->flags) << ")";
c->con << endl;
df::item_type itype = ENUM_ATTR(job_type, item, job->job_type);
MaterialInfo mat(job);
if (itype == item_type::FOOD)
mat.decode(-1);
if (mat.isValid() || job->material_category.whole)
{
c->con << " material: " << mat.toString();
@ -189,8 +195,12 @@ void DFHack::printJobDetails(Core *c, df::job *job)
}
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;
}
if (job->hist_figure_id >= 0)
c->con << " figure: " << job->hist_figure_id << endl;

@ -161,6 +161,8 @@ bool MaterialInfo::find(const std::string &token)
return true;
if (findInorganic(items[0]))
return true;
if (findPlant(items[0], ""))
return true;
}
else if (items.size() == 2)
{
@ -177,6 +179,12 @@ 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;
for (int i = 1; i < NUM_BUILTIN; i++)
if (raws.mat_table.builtin[i]->id == token)
@ -188,6 +196,12 @@ 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;
for (unsigned i = 0; i < raws.inorganics.size(); i++)
{
@ -200,7 +214,7 @@ bool MaterialInfo::findInorganic(const std::string &token)
bool MaterialInfo::findPlant(const std::string &token, const std::string &subtoken)
{
if (token.empty() || subtoken.empty())
if (token.empty())
return decode(-1);
df::world_raws &raws = df::global::world->raws;
for (unsigned i = 0; i < raws.plants.all.size(); i++)
@ -209,6 +223,10 @@ bool MaterialInfo::findPlant(const std::string &token, const std::string &subtok
if (p->id != token)
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++)
if (p->material[j]->id == subtoken)
return decode(PLANT_BASE+j, i);
@ -317,7 +335,7 @@ bool MaterialInfo::matches(const df::job_material_category &cat)
bool MaterialInfo::matches(const df::job_item &item)
{
if (!isValid()) return false;
if (!isValid()) return true;
df::job_item_flags1 ok1, mask1;
getMatchBits(ok1, mask1);
@ -328,9 +346,9 @@ bool MaterialInfo::matches(const df::job_item &item)
df::job_item_flags3 ok3, mask3;
getMatchBits(ok3, mask3);
return ((item.flags1.whole & mask1.whole) == (item.flags1.whole & ok1.whole)) &&
((item.flags2.whole & mask2.whole) == (item.flags2.whole & ok2.whole)) &&
((item.flags3.whole & mask3.whole) == (item.flags3.whole & ok3.whole));
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);
}
void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask)
@ -349,7 +367,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &ma
TEST(sharpenable, MAT_FLAG(IS_STONE));
TEST(distillable, structural && FLAG(plant, plant_raw_flags::DRINK));
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(extract_bearing_plant, structural && FLAG(plant, plant_raw_flags::EXTRACT_STILL_VIAL));
TEST(extract_bearing_fish, false);
@ -363,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_OTHER)));
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);
//04000000 - "milkable" - vtable[107],1,1
}

@ -1 +1 @@
Subproject commit b93b61a988020a4aaa0103cefcee0217c37ce7b0
Subproject commit 85dfa3550fe204c8c75279335cae1457cf03e0ed

@ -5,6 +5,7 @@
#include <MiscUtils.h>
#include <modules/Materials.h>
#include <modules/Items.h>
#include <modules/Gui.h>
#include <modules/Job.h>
@ -60,12 +61,14 @@ DFhackCExport command_result plugin_init (Core *c, std::vector <PluginCommand> &
PluginCommand(
"job", "General job query and manipulation.",
job_cmd, false,
" job query\n"
" job [query]\n"
" Print details of the current job.\n"
" job list\n"
" Print details of all jobs in the workshop.\n"
" job item-material <item-idx> <material[:subtoken]>\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());
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
@ -282,14 +292,25 @@ static command_result job_duplicate(Core * c, vector <string> & parameters)
/* 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)
{
CoreSuspender suspend(c);
if (parameters.empty())
return CR_WRONG_USAGE;
std::string cmd = parameters[0];
std::string cmd = (parameters.empty() ? "query" : parameters[0]);
if (cmd == "query" || cmd == "list")
{
df::job *job = getSelectedWorkshopJob(c);
@ -306,28 +327,23 @@ static command_result job_cmd(Core * c, vector <string> & parameters)
}
else if (cmd == "item-material")
{
if (parameters.size() < 1+1+1)
if (parameters.size() != 3)
return CR_WRONG_USAGE;
df::job *job = getSelectedWorkshopJob(c);
if (!job)
df::job_item *item = getJobItem(c, job, parameters[1]);
if (!item)
return CR_WRONG_USAGE;
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;
}
ItemTypeInfo iinfo(item);
MaterialInfo minfo;
df::job_item *item = job->job_items[v-1];
MaterialInfo info;
if (!info.find(parameters[2])) {
if (!minfo.find(parameters[2])) {
c->con.printerr("Could not find the specified material.\n");
return CR_FAILURE;
}
if (!info.matches(*item)) {
if (!iinfo.matches(*item, &minfo)) {
c->con.printerr("Material does not match the requirements.\n");
printJobDetails(c, job);
return CR_FAILURE;
@ -337,19 +353,52 @@ static command_result job_cmd(Core * c, vector <string> & parameters)
job->mat_type == item->mat_type &&
job->mat_index == item->mat_index)
{
job->mat_type = info.type;
job->mat_index = info.index;
job->mat_type = minfo.type;
job->mat_index = minfo.index;
}
item->mat_type = info.type;
item->mat_index = info.index;
item->mat_type = minfo.type;
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);
return CR_OK;
}
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
return CR_WRONG_USAGE;