int wowhead_t::parse_gem( item_t& item, const std::string& gem_id, bool ptr, cache::behavior_t caching ) { if ( gem_id.empty() || gem_id == "0" ) return GEM_NONE; xml_node_t* node = download_id( item.sim, gem_id, caching, ptr ); if ( ! node ) { if ( caching != cache::ONLY ) item.sim -> errorf( "Player %s unable to download gem id %s from wowhead\n", item.player -> name(), gem_id.c_str() ); return GEM_NONE; } int gem_type = GEM_NONE; std::string color_str; if ( xml_t::get_value( color_str, node, "subclass/cdata" ) ) { std::string::size_type pos = color_str.find( ' ' ); if ( pos != std::string::npos ) color_str.erase( pos ); armory_t::format( color_str ); gem_type = util_t::parse_gem_type( color_str ); if ( gem_type == GEM_META ) { std::string name_str; if ( xml_t::get_value( name_str, node, "name/cdata" ) ) { std::string::size_type pos = name_str.find( " Diamond" ); if ( pos != std::string::npos ) name_str.erase( pos ); armory_t::format( name_str ); item.armory_gems_str += "_"; item.armory_gems_str += name_str; } } else { std::string stats_str; if ( xml_t::get_value( stats_str, node, "jsonEquip/cdata" ) ) { parse_stats( item.armory_gems_str, stats_str ); } } } return gem_type; }
bool wowhead::download_glyph( player_t* player, std::string& glyph_name, const std::string& glyph_id, wowhead_e source, cache::behavior_e caching ) { unsigned glyphid = strtoul( glyph_id.c_str(), nullptr, 10 ); std::shared_ptr<xml_node_t> node = download_id( player -> sim, glyphid, caching, source ); if ( ! node || ! node -> get_value( glyph_name, "name/cdata" ) ) { if ( caching != cache::ONLY ) player -> sim -> errorf( "Unable to download glyph id %s from wowhead\n", glyph_id.c_str() ); return false; } return true; }
bool wowhead_t::download_glyph( player_t* player, std::string& glyph_name, const std::string& glyph_id, bool ptr, cache::behavior_t caching ) { xml_node_t* node = download_id( player -> sim, glyph_id, caching, ptr ); if ( ! node || ! xml_t::get_value( glyph_name, node, "name/cdata" ) ) { if ( caching != cache::ONLY ) player -> sim -> errorf( "Unable to download glyph id %s from wowhead\n", glyph_id.c_str() ); return false; } if ( glyph_name.substr( 0, 9 ) == "Glyph of " ) glyph_name.erase( 0, 9 ); else if ( glyph_name.substr( 0, 8 ) == "Glyph - " ) glyph_name.erase( 0, 8 ); armory_t::format( glyph_name ); return true; }
bool wowhead::download_item_data( item_t& item, cache::behavior_e caching, wowhead_e source ) { std::shared_ptr<xml_node_t> xml = item.xml = download_id( item.sim, item.parsed.data.id, caching, source ); if ( ! xml ) { if ( caching != cache::ONLY ) item.sim -> errorf( "Player %s unable to download item id '%u' from wowhead at slot %s.\n", item.player -> name(), item.parsed.data.id, item.slot_name() ); return false; } try { int id; if ( ! xml -> get_value( id, "item/id" ) ) throw( "id" ); item.parsed.data.id = id; if ( ! xml -> get_value( item.name_str, "name/cdata" ) ) throw( "name" ); util::tokenize( item.name_str ); xml -> get_value( item.icon_str, "icon/cdata" ); if ( ! xml -> get_value( item.parsed.data.level, "level/." ) ) throw( "level" ); if ( ! xml -> get_value( item.parsed.data.quality, "quality/id" ) ) throw( "quality" ); std::string jsonequipdata, jsondata; xml -> get_value( jsonequipdata, "jsonEquip/cdata" ); jsonequipdata = "{" + jsonequipdata + "}"; xml -> get_value( jsondata, "json/cdata" ); jsondata = "{" + jsondata + "}"; rapidjson::Document json, jsonequip; json.Parse< 0 >( jsondata.c_str() ); jsonequip.Parse< 0 >( jsonequipdata.c_str() ); if ( json.HasParseError() ) { item.sim -> errorf( "Unable to parse JSON data for item id '%u': %s", id, json.GetParseError() ); return false; } if ( jsonequip.HasParseError() ) { item.sim -> errorf( "Unable to parse JSON data for item id '%u': %s", id, jsonequip.GetParseError() ); return false; } if ( item.sim -> debug ) { rapidjson::StringBuffer b; rapidjson::PrettyWriter< rapidjson::StringBuffer > writer( b ); json.Accept( writer ); item.sim -> out_debug.raw() << b.GetString(); jsonequip.Accept( writer ); item.sim -> out_debug.raw() << b.GetString(); } if ( ! json.HasMember( "slot" ) ) throw( "inventory type" ); if ( ! json.HasMember( "classs" ) ) throw( "item class" ); if ( ! json.HasMember( "subclass" ) ) throw( "item subclass" ); item.parsed.data.inventory_type = json[ "slot" ].GetInt(); item.parsed.data.item_class = json[ "classs" ].GetInt(); item.parsed.data.item_subclass = json[ "subclass" ].GetInt(); if ( item.parsed.data.item_subclass < 0 ) item.parsed.data.item_subclass = 0; if ( json.HasMember( "reqlevel" ) ) item.parsed.data.req_level = json[ "reqlevel" ].GetInt(); if ( json.HasMember( "raidfinder" ) ) item.parsed.data.type_flags |= RAID_TYPE_LFR; if ( json.HasMember( "heroic" ) ) item.parsed.data.type_flags |= RAID_TYPE_HEROIC; if ( json.HasMember( "mythic" ) ) item.parsed.data.type_flags |= RAID_TYPE_MYTHIC; if ( json.HasMember( "warforged" ) ) item.parsed.data.type_flags |= RAID_TYPE_WARFORGED; if ( item.parsed.data.item_class == ITEM_CLASS_WEAPON ) { if ( ! jsonequip.HasMember( "dmgrange" ) ) throw( "weapon damage range" ); if ( ! jsonequip.HasMember( "speed" ) ) throw( "weapon speed" ); } if ( jsonequip.HasMember( "reqskill" ) ) item.parsed.data.req_skill = jsonequip[ "reqskill" ].GetInt(); if ( jsonequip.HasMember( "reqskillrank" ) ) item.parsed.data.req_skill_level = jsonequip[ "reqskillrank" ].GetInt(); // Todo binding type, needs htmlTooltip parsing if ( item.parsed.data.item_class == ITEM_CLASS_WEAPON ) { item.parsed.data.delay = jsonequip[ "speed" ].GetDouble() * 1000.0; item.parsed.data.dmg_range = jsonequip[ "dmgrange" ].GetDouble(); } int races = -1; if ( jsonequip.HasMember( "races" ) ) races = jsonequip[ "races" ].GetInt(); item.parsed.data.race_mask = races; int classes = -1; if ( jsonequip.HasMember( "classes" ) ) classes = jsonequip[ "classes" ].GetInt(); item.parsed.data.class_mask = classes; size_t n = 0; stat_e hybrid_stat = STAT_NONE; for ( rapidjson::Value::ConstMemberIterator i = jsonequip.MemberBegin(); i != jsonequip.MemberEnd() && n < sizeof_array( item.parsed.data.stat_type_e ); i++ ) { stat_e type = util::parse_stat_type( i -> name.GetString() ); // wowhead josnEquip contains redundant entries for queries, take note so we can purge if ( type == STAT_STR_AGI || type == STAT_STR_INT || type == STAT_AGI_INT ) hybrid_stat = type; } for ( rapidjson::Value::ConstMemberIterator i = jsonequip.MemberBegin(); i != jsonequip.MemberEnd() && n < sizeof_array( item.parsed.data.stat_type_e ); i++ ) { stat_e type = util::parse_stat_type( i -> name.GetString() ); if ( type == STAT_NONE || type == STAT_ARMOR || util::translate_stat( type ) == ITEM_MOD_NONE ) continue; // If we have a hybrid stat, don't record the excess STR/INT/AGI entries if ( hybrid_stat != STAT_NONE && ( type == STAT_STRENGTH || type == STAT_INTELLECT || type == STAT_AGILITY ) ) continue; item.parsed.data.stat_type_e[ n ] = util::translate_stat( type ); item.parsed.data.stat_val[ n ] = i -> value.GetInt(); n++; // Soo, weapons need a flag to indicate caster weapon for correct DPS calculation. if ( item.parsed.data.delay > 0 && ( item.parsed.data.stat_type_e[ n - 1 ] == ITEM_MOD_INTELLECT || item.parsed.data.stat_type_e[ n - 1 ] == ITEM_MOD_SPIRIT || item.parsed.data.stat_type_e[ n - 1 ] == ITEM_MOD_SPELL_POWER ) ) item.parsed.data.flags_2 |= ITEM_FLAG2_CASTER_WEAPON; } int n_sockets = 0; if ( jsonequip.HasMember( "nsockets" ) ) n_sockets = jsonequip[ "nsockets" ].GetUint(); assert( n_sockets <= static_cast< int >( sizeof_array( item.parsed.data.socket_color ) ) ); for ( int i = 0; i < n_sockets; i++ ) { std::string socket_str = str::format( "socket%d", i + 1 ); if ( jsonequip.HasMember( socket_str.c_str() ) ) item.parsed.data.socket_color[ i ] = jsonequip[ socket_str.c_str() ].GetUint(); } if ( jsonequip.HasMember( "socketbonus" ) ) item.parsed.data.id_socket_bonus = jsonequip[ "socketbonus" ].GetUint(); if ( jsonequip.HasMember( "itemset" ) ) item.parsed.data.id_set = std::abs( jsonequip[ "itemset" ].GetInt() ); // Sad to the face std::string htmltooltip; xml -> get_value( htmltooltip, "htmlTooltip/cdata" ); // Parse out Equip: and On use: strings int spell_idx = 0; std::shared_ptr<xml_node_t> htmltooltip_xml = xml_node_t::create( item.sim, htmltooltip ); //htmltooltip_xml -> print( item.sim -> output_file, 2 ); std::vector<xml_node_t*> spell_links = htmltooltip_xml -> get_nodes( "span" ); for ( size_t i = 0; i < spell_links.size(); i++ ) { int trigger_type = -1; unsigned spell_id = 0; std::string v; if ( spell_links[ i ] -> get_value( v, "." ) && v != "Equip: " && v != "Use: " ) continue; if ( v == "Use: " ) trigger_type = ITEM_SPELLTRIGGER_ON_USE; else if ( v == "Equip: " ) trigger_type = ITEM_SPELLTRIGGER_ON_EQUIP; std::string url; if ( ! spell_links[ i ] -> get_value( url, "a/href" ) ) continue; size_t begin = url.rfind( "=" ); if ( begin == std::string::npos ) continue; else begin++; spell_id = util::to_unsigned( url.substr( begin ) ); if ( spell_id > 0 && trigger_type != -1 ) { item.parsed.data.id_spell[ spell_idx ] = spell_id; item.parsed.data.trigger_spell[ spell_idx ] = trigger_type; spell_idx++; } } } catch ( const char* fieldname ) { std::string error_str; xml -> get_value( error_str, "error/." ); if ( caching != cache::ONLY ) item.sim -> errorf( "Wowhead (%s): Player %s unable to parse item '%u' %s in slot '%s': %s\n", source_desc_str( source ).c_str(), item.player -> name(), item.parsed.data.id, fieldname, item.slot_name(), error_str.c_str() ); return false; } return true; }
bool wowhead_t::download_slot( item_t& item, const std::string& item_id, const std::string& enchant_id, const std::string& addon_id, const std::string& reforge_id, const std::string& rsuffix_id, const std::string gem_ids[ 3 ], bool ptr, cache::behavior_t caching ) { player_t* p = item.player; xml_node_t* node = download_id( item.sim, item_id, caching, ptr ); if ( ! node ) { if ( caching != cache::ONLY ) item.sim -> errorf( "Player %s unable to download item id '%s' from wowhead at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_name( item, node ) ) { item.sim -> errorf( "Player %s unable to determine item name for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_quality( item, node ) ) { item.sim -> errorf( "Player %s unable to determine item quality for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_level( item, node ) ) { item.sim -> errorf( "Player %s unable to determine item level for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_heroic( item, node ) ) { item.sim -> errorf( "Player %s unable to determine heroic flag for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_lfr( item, node ) ) { item.sim -> errorf( "Player %s unable to determine LFR flag for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_armor_type( item, node ) ) { item.sim -> errorf( "Player %s unable to determine armor type for id %s at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_stats( item, node ) ) { item.sim -> errorf( "Player %s unable to determine stats for item '%s' at slot %s.\n", p -> name(), item.name(), item.slot_name() ); return false; } if ( ! parse_weapon( item, node ) ) { item.sim -> errorf( "Player %s unable to determine weapon info for item '%s' at slot %s.\n", p -> name(), item.name(), item.slot_name() ); return false; } if ( ! parse_item_reforge( item, node ) ) { item.sim -> errorf( "Player %s unable to determine reforge for item '%s' at slot %s.\n", p -> name(), item.name(), item.slot_name() ); return false; } if ( ! parse_gems( item, node, gem_ids ) ) { item.sim -> errorf( "Player %s unable to determine gems for item '%s' at slot %s.\n", p -> name(), item.name(), item.slot_name() ); return false; } if ( ! enchant_t::download( item, enchant_id ) ) { item.sim -> errorf( "Player %s unable to parse enchant id %s for item \"%s\" at slot %s.\n", p -> name(), enchant_id.c_str(), item.name(), item.slot_name() ); //return false; } if ( ! enchant_t::download_addon( item, addon_id ) ) { item.sim -> errorf( "Player %s unable to parse addon id %s for item \"%s\" at slot %s.\n", p -> name(), addon_id.c_str(), item.name(), item.slot_name() ); //return false; } if ( ! enchant_t::download_reforge( item, reforge_id ) ) { item.sim -> errorf( "Player %s unable to parse reforge id %s for item \"%s\" at slot %s.\n", p -> name(), reforge_id.c_str(), item.name(), item.slot_name() ); //return false; } if ( ! enchant_t::download_rsuffix( item, rsuffix_id ) ) { item.sim -> errorf( "Player %s unable to determine random suffix '%s' for item '%s' at slot %s.\n", p -> name(), rsuffix_id.c_str(), item.name(), item.slot_name() ); return false; } return true; }
bool wowhead_t::download_item( item_t& item, const std::string& item_id, bool ptr, cache::behavior_t caching ) { player_t* p = item.player; xml_node_t* node = download_id( item.sim, item_id, caching, ptr ); if ( ! node ) { if ( caching != cache::ONLY ) item.sim -> errorf( "Player %s unable to download item id '%s'' from wowhead at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_name( item, node ) ) { item.sim -> errorf( "Player %s unable to determine item name for id '%s'' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_quality( item, node ) ) { item.sim -> errorf( "Player %s unable to determine item quality for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_level( item, node ) ) { item.sim -> errorf( "Player %s unable to determine item level for id '%s' at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_heroic( item, node ) ) { item.sim -> errorf( "Player %s unable to determine heroic flag for id %s at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_lfr( item, node ) ) { item.sim -> errorf( "Player %s unable to determine LFR flag for id %s at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_armor_type( item, node ) ) { item.sim -> errorf( "Player %s unable to determine armor type for id %s at slot %s.\n", p -> name(), item_id.c_str(), item.slot_name() ); return false; } if ( ! parse_item_stats( item, node ) ) { item.sim -> errorf( "Player %s unable to determine stats for item '%s' at slot %s.\n", p -> name(), item.name(), item.slot_name() ); return false; } if ( ! parse_weapon( item, node ) ) { item.sim -> errorf( "Player %s unable to determine weapon info for item '%s' at slot %s.\n", p -> name(), item.name(), item.slot_name() ); return false; } return true; }