Skip to content
This repository has been archived by the owner on Aug 19, 2022. It is now read-only.

Commit

Permalink
Combine ericsium and more recent work to determine categories from ic…
Browse files Browse the repository at this point in the history
…on paths.
  • Loading branch information
DaneelTrevize committed Sep 16, 2019
1 parent 505486a commit 53aa28e
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 105 deletions.
182 changes: 84 additions & 98 deletions src/item.cpp
Expand Up @@ -32,6 +32,26 @@
#include "porting.h"
#include "itemlocation.h"

const std::array<Item::CategoryReplaceMap, Item::k_CategoryLevels> Item::replace_map_ = {
// Category hierarchy 0 replacement map
Item::CategoryReplaceMap({{"Divination", "Divination Cards"},
{"QuestItems", "Quest Items"}}),
// Category hierarchy 1 replacement map
Item::CategoryReplaceMap({{"BodyArmours", "Body"},
{"VaalGems", "Vaal"},
{"AtlasMaps", "2.4"},
{"act4maps", "2.0"},
{"OneHandWeapons", "1Hand"},
{"TwoHandWeapons", "2Hand"}}),
// Category hierarchy 2 replacement map
Item::CategoryReplaceMap({{"OneHandAxes", "Axes"},
{"OneHandMaces", "Maces"},
{"OneHandSwords", "Swords"},
{"TwoHandAxes", "Axes"},
{"TwoHandMaces", "Maces"},
{"TwoHandSwords", "Swords"}})
};

const std::vector<std::string> ITEM_MOD_TYPES = {
"implicitMods", "enchantMods", "explicitMods", "craftedMods"
};
Expand Down Expand Up @@ -267,128 +287,94 @@ std::string Item::PrettyName() const {
}

void Item::CalculateCategories(const rapidjson::Value &json) {
category_ = "";
if (json.HasMember("category") && json["category"].IsObject()) {
// This object contains a single array who's name is the item's category. The array may contain the item's sub-category
rapidjson::Value::ConstMemberIterator itr = json["category"].MemberBegin();
category_ = itr->name.GetString();
category_ = Util::Capitalise(category_);

if (category_ == "Cards") {
category_ = "Divination cards";
// Derive item type 'category' hierarchy from icon path.
std::smatch sm;
if (std::regex_search(icon_, sm, std::regex("Art/.*?/(.*)/"))) {
std::string match = sm.str(1);
boost::split(category_vector_,match,boost::is_any_of("/"));
//Compress terms with redundant identifiers
//Weapons.OneHandWeapons.OneHandMaces -> Weapons.OneHand.Maces
size_t min = std::min(category_vector_.size(), replace_map_.size());
for (size_t i = 0; i < min; i++) {
auto it = replace_map_[i].find(category_vector_[i]);
if (it != replace_map_[i].end())
category_vector_[i] = it->second;
}
} else if (std::regex_search(icon_, sm, std::regex("/gen/image/.*?/Item.png"))) {
// Flask images are dynamically generated by GGG to reflect current charge status, rather than live under /Art/2DItems/
category_vector_.push_back("Flasks");
} else {
category_vector_.push_back("Unknown");
}

category_vector_.push_back(category_);
if (itr->value.IsArray() && !itr->value.Empty()) {
// Handle sub-categories

if (category_ == "Accessories") { // Elevate accessories sub-categories to their own top level categories
category_vector_.pop_back();
// If amulet and .HasMember("talismanTier") (which is also checked after CalculateCategories()), add Talisman sub-category?
}

/*
If Armour.Chests, rename Armour.BodyArmour?
If Flasks, use Base64 encoded JSON within icon path to determine sub-category?
*/

category_ = itr->value[0].GetString();
category_ = Util::Capitalise(category_);

if (category_ == "Activegem") {
// Rename these sub-categories
category_ = "Skill";
if (icon_.find("/Art/2DItems/Gems/VaalGems/") != std::string::npos) {
category_vector_.push_back(category_);
category_ = "Vaal";
}
} else if (category_ == "Supportgem") {
category_ = "Support";
} else if (category_ == "Bow" || category_ == "Staff") {
// Sub-categories these categories under handedness
category_vector_.push_back("TwoHand");
} else if (category_ == "Claw" || category_ == "Dagger" || category_ == "Sceptre" || category_ == "Wand") {
category_vector_.push_back("OneHand");
} else if (category_ == "Oneaxe") {
// Sub-categories these categories under handedness and rename them
category_vector_.push_back("OneHand");
category_ = "Axe";
} else if (category_ == "Onemace") {
category_vector_.push_back("OneHand");
category_ = "Mace";
} else if (category_ == "Onesword") {
category_vector_.push_back("OneHand");
category_ = "Sword";
} else if (category_ == "Twoaxe") {
category_vector_.push_back("TwoHand");
category_ = "Axe";
} else if (category_ == "Twomace") {
category_vector_.push_back("TwoHand");
category_ = "Mace";
} else if (category_ == "Twosword") {
category_vector_.push_back("TwoHand");
category_ = "Sword";
}

category_vector_.push_back(category_);
} else if (category_ == "Maps") {
// Use icon path to determine possible expansion sub-category
std::smatch sm;
if (std::regex_search(icon_, sm, std::regex("/Art/2DItems/Maps/(.*)/"))) {
std::string match = sm.str(1);
std::vector<std::string> iconsubs;
boost::split(iconsubs, match, boost::is_any_of("/"));
std::string sub = iconsubs[0];
if (sub == "Atlas2Maps") {
category_vector_.push_back("3.1");
if (iconsubs.size() > 1) {
sub = iconsubs[1]; // Typically "New"
sub = Util::Capitalise(sub);
category_vector_.push_back(sub);
}
} else if (sub == "AtlasMaps") {
category_vector_.push_back("2.4");
} else if (sub == "act4maps") {
category_vector_.push_back("2.0");
} else {
Util::Capitalise(sub);
category_vector_.push_back(sub);
if (category_vector_[0] == "Jewels") {
if (std::regex_search(icon_, sm, std::regex("/Jewels/.+?Eye.png"))) {
category_vector_.push_back("Abyss");
}
} else if (json.HasMember("prophecyText") && json["prophecyText"].IsString()) {
// Relocate Prophecies out from Currencies
category_vector_[0] = "Prophecies";
} else if (category_vector_[0] == "Maps") {
if (std::regex_search(icon_, sm, std::regex("Maps/(?:Uber)?Vaal(?:[[:digit:]]){2}.png"))) {
// Check for digits immediately after Vaal, to avoid matching on VaalCity (AKA Ancient City), VaalTemple
category_vector_.push_back("Atziri Fragments");
// What about Prophecy key framents, atlas guardian drop fragments, scarabs, divine vessels? For now most end up under Misc (GGG classed scarabs as currency in their icon path)
} else if (std::regex_search(icon_, sm, std::regex("Maps/(?:.*)Shard.png"))) {
// Check for Legion splinters, recategorise as currency. What about Emblems, need an example to test.
category_vector_[0] = "Currency";
category_vector_.push_back("Legion");
} else {
if (category_vector_.size() > 1) {
if (category_vector_[1] == "Atlas2Maps") {
// Try to give newer maps a more useful category name
category_vector_[1] = "3";
if (category_vector_.size() > 2 ) {
// More accurately 3.1+
if (category_vector_[2] == "New") {
category_vector_[2] = "1";
// Calculate release version from ?mn= parameter
if (std::regex_search(icon_, sm, std::regex("&mn=([[:digit:]])+"))) {
int mn = std::stoi(sm[1], nullptr, 10);
if (mn > 1) {
// mn became 2 for 3.5.0
category_vector_[2] = std::to_string(mn + 3);
}
}
}
} else {
category_vector_.push_back("0"); // Were any added to Atlas2Maps but not New?
}
}
} else {
if (icon_.find("/Art/2DItems/Maps/Map") != std::string::npos) {
// Doesn't find all because of some maps like FairgravesMap01.png, olmec.png, etc. They'll end up in Maps.Misc, then moved to Maps.Older Uniques
// Doesn't find all because of some maps like FairgravesMap01.png, olmec.png, etc. We'll pick them out of Misc later
category_vector_.push_back("Original");
} else if (icon_.find("/Art/2DItems/Currency/Breach/") != std::string::npos) {
// Isn't really a map, has no property named Map Tier
category_vector_.pop_back();
category_vector_.push_back("Currency");
category_vector_.push_back("Breach");
} else {
category_vector_.push_back("Misc");

// Check that these aren't just badly named actual maps
if (json.HasMember("properties") && json["properties"].IsArray()) {
for (auto &prop : json["properties"]) {
if (!prop.HasMember("name") || !prop["name"].IsString() || !prop.HasMember("values") || !prop["values"].IsArray())
continue;
std::string name = prop["name"].GetString();
if (name == "Map Tier") {
category_vector_.pop_back();
category_vector_.push_back("Older Uniques"); // Future ones might fall under a new /Maps/ icon path
category_vector_.push_back("Older Uniques");
break;
}
// Else determine if Sacrifice/Mortal/Offering/etc fragment?
}
}
}
}
} else if (category_ == "Currency") {
if (icon_.find("/Art/2DItems/Currency/Breach/") != std::string::npos) {
category_vector_.push_back("Breach");
} else if (icon_.find("/Art/2DItems/Currency/Essence/") != std::string::npos) {
category_vector_.push_back("Essence");
}
}
}

if (category_vector_.size() > 1 && category_vector_[1] == "Scarabs") {
category_vector_[0] = "Maps";
// Technically scarabs are fragments. Better to leave Fragments as just Atziri ones, give scarabs their own subcategory?
}

category_ = boost::join(category_vector_, ".");
boost::to_lower(category_);

Expand Down
2 changes: 2 additions & 0 deletions src/item.h
Expand Up @@ -115,6 +115,8 @@ class Item {
bool operator<(const Item &other) const;
bool Wearable() const;
std::string POBformat() const;
static const size_t k_CategoryLevels = 3;
static const std::array<CategoryReplaceMap, k_CategoryLevels> replace_map_;

private:
void CalculateCategories(const rapidjson::Value &json);
Expand Down
2 changes: 1 addition & 1 deletion src/itemsmanager.h
Expand Up @@ -55,7 +55,7 @@ class ItemsManager : public QObject {
void ApplyAutoItemBuyouts();
void PropagateTabBuyouts();
void UpdateCategories();
const QSet<QString>& categories() const { return categories_; };
const QSet<QString>& categories() const { return categories_; }
public slots:
// called by auto_update_timer_
void OnAutoRefreshTimer();
Expand Down
12 changes: 6 additions & 6 deletions test/testitem.cpp
Expand Up @@ -51,35 +51,35 @@ void TestItem::ParseCategories() {

doc.Parse(kCategoriesItemBelt.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "belt");
QCOMPARE(item.category().c_str(), "belts");

doc.Parse(kCategoriesItemEssence.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "currency.essence");

doc.Parse(kCategoriesItemVaalGem.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "gems.skill.vaal");
QCOMPARE(item.category().c_str(), "gems.vaal");

doc.Parse(kCategoriesItemSupportGem.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "gems.support");

doc.Parse(kCategoriesItemBow.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "weapons.twohand.bow");
QCOMPARE(item.category().c_str(), "weapons.2hand.bows");

doc.Parse(kCategoriesItemClaw.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "weapons.onehand.claw");
QCOMPARE(item.category().c_str(), "weapons.1hand.claws");

doc.Parse(kCategoriesItemFragment.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "maps.misc");
QCOMPARE(item.category().c_str(), "maps.atziri fragments");

doc.Parse(kCategoriesItemWarMap.c_str());
item = Item(doc);
QCOMPARE(item.category().c_str(), "maps.3.1.new");
QCOMPARE(item.category().c_str(), "maps.3.1");

doc.Parse(kCategoriesItemUniqueMap.c_str());
item = Item(doc);
Expand Down

0 comments on commit 53aa28e

Please sign in to comment.