2023-02-21 19:05:15 -07:00
|
|
|
#include "itemfilter.h"
|
|
|
|
|
|
|
|
#include "Debug.h"
|
|
|
|
|
|
|
|
#include "df/item.h"
|
|
|
|
|
|
|
|
namespace DFHack {
|
|
|
|
DBG_EXTERN(buildingplan, status);
|
2023-03-13 14:35:12 -06:00
|
|
|
DBG_EXTERN(buildingplan, cycle);
|
2023-02-21 19:05:15 -07:00
|
|
|
}
|
|
|
|
|
2023-03-02 18:50:12 -07:00
|
|
|
using std::set;
|
2023-02-22 16:08:11 -07:00
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
|
2023-03-06 00:04:03 -07:00
|
|
|
ItemFilter::ItemFilter() : default_max_quality(df::item_quality::Masterful) {
|
2023-02-21 19:05:15 -07:00
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemFilter::clear() {
|
|
|
|
min_quality = df::item_quality::Ordinary;
|
2023-03-06 00:04:03 -07:00
|
|
|
max_quality = default_max_quality;
|
2023-02-21 19:05:15 -07:00
|
|
|
decorated_only = false;
|
|
|
|
mat_mask.whole = 0;
|
|
|
|
materials.clear();
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:08:11 -07:00
|
|
|
bool ItemFilter::isEmpty() const {
|
2023-02-21 19:05:15 -07:00
|
|
|
return min_quality == df::item_quality::Ordinary
|
2023-03-06 00:04:03 -07:00
|
|
|
&& max_quality == default_max_quality
|
2023-02-21 19:05:15 -07:00
|
|
|
&& !decorated_only
|
|
|
|
&& !mat_mask.whole
|
|
|
|
&& materials.empty();
|
|
|
|
}
|
|
|
|
|
2023-03-23 11:48:33 -06:00
|
|
|
static bool deserializeMaterialMask(const string& ser, df::dfhack_material_category& mat_mask) {
|
2023-02-21 19:05:15 -07:00
|
|
|
if (ser.empty())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!parseJobMaterialCategory(&mat_mask, ser)) {
|
|
|
|
DEBUG(status).print("invalid job material category serialization: '%s'", ser.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-23 11:48:33 -06:00
|
|
|
static bool deserializeMaterials(const string& ser, set<DFHack::MaterialInfo> &materials) {
|
2023-02-21 19:05:15 -07:00
|
|
|
if (ser.empty())
|
|
|
|
return true;
|
|
|
|
|
2023-02-22 16:08:11 -07:00
|
|
|
vector<string> mat_names;
|
2023-02-21 19:05:15 -07:00
|
|
|
split_string(&mat_names, ser, ",");
|
|
|
|
for (auto m = mat_names.begin(); m != mat_names.end(); m++) {
|
|
|
|
DFHack::MaterialInfo material;
|
|
|
|
if (!material.find(*m) || !material.isValid()) {
|
|
|
|
DEBUG(status).print("invalid material name serialization: '%s'", ser.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-02 18:50:12 -07:00
|
|
|
materials.emplace(material);
|
2023-02-21 19:05:15 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-23 11:48:33 -06:00
|
|
|
ItemFilter::ItemFilter(color_ostream &out, const string& serialized) : ItemFilter() {
|
2023-02-22 16:08:11 -07:00
|
|
|
vector<string> tokens;
|
2023-02-21 19:05:15 -07:00
|
|
|
split_string(&tokens, serialized, "/");
|
2023-03-06 00:04:03 -07:00
|
|
|
if (tokens.size() < 5) {
|
2023-02-22 16:08:11 -07:00
|
|
|
DEBUG(status,out).print("invalid ItemFilter serialization: '%s'", serialized.c_str());
|
2023-02-21 19:05:15 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!deserializeMaterialMask(tokens[0], mat_mask) || !deserializeMaterials(tokens[1], materials))
|
|
|
|
return;
|
|
|
|
|
|
|
|
setMinQuality(atoi(tokens[2].c_str()));
|
|
|
|
setMaxQuality(atoi(tokens[3].c_str()));
|
|
|
|
decorated_only = static_cast<bool>(atoi(tokens[4].c_str()));
|
2023-03-06 00:04:03 -07:00
|
|
|
|
|
|
|
if (tokens.size() >= 6)
|
|
|
|
default_max_quality = static_cast<df::item_quality>(atoi(tokens[5].c_str()));
|
2023-02-21 19:05:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// format: mat,mask,elements/materials,list/minq/maxq/decorated
|
2023-02-22 16:08:11 -07:00
|
|
|
string ItemFilter::serialize() const {
|
2023-02-21 19:05:15 -07:00
|
|
|
std::ostringstream ser;
|
|
|
|
ser << bitfield_to_string(mat_mask, ",") << "/";
|
2023-03-02 18:50:12 -07:00
|
|
|
vector<string> matstrs;
|
2023-03-23 11:48:33 -06:00
|
|
|
for (auto &mat : materials)
|
|
|
|
matstrs.emplace_back(mat.getToken());
|
|
|
|
ser << join_strings(",", matstrs);
|
2023-02-21 19:05:15 -07:00
|
|
|
ser << "/" << static_cast<int>(min_quality);
|
|
|
|
ser << "/" << static_cast<int>(max_quality);
|
|
|
|
ser << "/" << static_cast<int>(decorated_only);
|
2023-03-06 00:04:03 -07:00
|
|
|
ser << "/" << static_cast<int>(default_max_quality);
|
2023-02-21 19:05:15 -07:00
|
|
|
return ser.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clampItemQuality(df::item_quality *quality) {
|
|
|
|
if (*quality > df::item_quality::Artifact) {
|
|
|
|
DEBUG(status).print("clamping quality to Artifact");
|
|
|
|
*quality = df::item_quality::Artifact;
|
|
|
|
}
|
|
|
|
if (*quality < df::item_quality::Ordinary) {
|
|
|
|
DEBUG(status).print("clamping quality to Ordinary");
|
|
|
|
*quality = df::item_quality::Ordinary;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemFilter::setMinQuality(int quality) {
|
|
|
|
min_quality = static_cast<df::item_quality>(quality);
|
|
|
|
clampItemQuality(&min_quality);
|
|
|
|
if (max_quality < min_quality)
|
|
|
|
max_quality = min_quality;
|
|
|
|
}
|
|
|
|
|
2023-03-06 00:04:03 -07:00
|
|
|
void ItemFilter::setMaxQuality(int quality, bool is_default) {
|
2023-02-21 19:05:15 -07:00
|
|
|
max_quality = static_cast<df::item_quality>(quality);
|
|
|
|
clampItemQuality(&max_quality);
|
|
|
|
if (max_quality < min_quality)
|
|
|
|
min_quality = max_quality;
|
2023-03-06 00:04:03 -07:00
|
|
|
if (is_default)
|
|
|
|
default_max_quality = max_quality;
|
2023-02-21 19:05:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void ItemFilter::setDecoratedOnly(bool decorated) {
|
|
|
|
decorated_only = decorated;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemFilter::setMaterialMask(uint32_t mask) {
|
|
|
|
mat_mask.whole = mask;
|
|
|
|
}
|
|
|
|
|
2023-03-02 18:50:12 -07:00
|
|
|
void ItemFilter::setMaterials(const set<DFHack::MaterialInfo> &materials) {
|
2023-02-21 19:05:15 -07:00
|
|
|
this->materials = materials;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool matchesMask(DFHack::MaterialInfo &mat, df::dfhack_material_category mat_mask) {
|
|
|
|
return mat_mask.whole ? mat.matches(mat_mask) : true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ItemFilter::matches(df::dfhack_material_category mask) const {
|
|
|
|
return mask.whole & mat_mask.whole;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ItemFilter::matches(DFHack::MaterialInfo &material) const {
|
2023-03-02 18:50:12 -07:00
|
|
|
for (auto &mat : materials)
|
|
|
|
if (material.matches(mat))
|
2023-02-21 19:05:15 -07:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ItemFilter::matches(df::item *item) const {
|
2023-06-16 17:05:01 -06:00
|
|
|
int16_t quality = (item->flags.bits.artifact ? df::item_quality::Artifact : item->getQuality());
|
|
|
|
if (quality < min_quality || quality > max_quality) {
|
2023-03-13 14:35:12 -06:00
|
|
|
TRACE(cycle).print("item outside of quality range (%d not between %d and %d)\n",
|
2023-06-16 17:05:01 -06:00
|
|
|
quality, min_quality, max_quality);
|
2023-02-21 19:05:15 -07:00
|
|
|
return false;
|
2023-03-13 14:35:12 -06:00
|
|
|
}
|
2023-02-21 19:05:15 -07:00
|
|
|
|
2023-03-13 14:35:12 -06:00
|
|
|
if (decorated_only && !item->hasImprovements()) {
|
|
|
|
TRACE(cycle).print("item needs improvements and doesn't have any\n");
|
2023-02-21 19:05:15 -07:00
|
|
|
return false;
|
2023-03-13 14:35:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!mat_mask.whole)
|
|
|
|
return true;
|
2023-02-21 19:05:15 -07:00
|
|
|
|
|
|
|
auto imattype = item->getActualMaterial();
|
|
|
|
auto imatindex = item->getActualMaterialIndex();
|
|
|
|
auto item_mat = DFHack::MaterialInfo(imattype, imatindex);
|
|
|
|
|
|
|
|
return (materials.size() == 0) ? matchesMask(item_mat, mat_mask) : matches(item_mat);
|
|
|
|
}
|
2023-02-22 16:08:11 -07:00
|
|
|
|
|
|
|
vector<ItemFilter> deserialize_item_filters(color_ostream &out, const string &serialized) {
|
2023-03-02 18:50:12 -07:00
|
|
|
vector<ItemFilter> filters;
|
2023-02-22 16:08:11 -07:00
|
|
|
|
|
|
|
vector<string> filter_strs;
|
|
|
|
split_string(&filter_strs, serialized, ";");
|
|
|
|
for (auto &str : filter_strs) {
|
|
|
|
filters.emplace_back(out, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return filters;
|
|
|
|
}
|
|
|
|
|
|
|
|
string serialize_item_filters(const vector<ItemFilter> &filters) {
|
|
|
|
vector<string> strs;
|
|
|
|
for (auto &filter : filters) {
|
|
|
|
strs.emplace_back(filter.serialize());
|
|
|
|
}
|
|
|
|
return join_strings(";", strs);
|
|
|
|
}
|