int item_database::scaled_stat( const item_t& item, const dbc_t& dbc, size_t idx, unsigned new_ilevel ) { // Safeguard against array overflow, should never happen in any case if ( idx >= sizeof_array( item.parsed.data.stat_val ) - 1 ) return -1; if ( item.parsed.data.level == 0 ) return item.parsed.data.stat_val[ idx ]; //if ( item.level == ( int ) new_ilevel ) // return item.stat_val[ idx ]; int slot_type = random_suffix_type( &item.parsed.data ); double item_budget = 0/*, orig_budget = 0*/; if ( slot_type != -1 && item.parsed.data.quality > 0 ) { const random_prop_data_t& ilevel_data = dbc.random_property( new_ilevel ); //const random_prop_data_t& orig_data = dbc.random_property( item.level ); // Epic/Legendary if ( item.parsed.data.quality == 4 || item.parsed.data.quality == 5 ) { item_budget = ilevel_data.p_epic[ slot_type ]; //orig_budget = orig_data.p_epic[ slot_type ]; } // Rare/Heirloom else if ( item.parsed.data.quality == 3 || item.parsed.data.quality == 7 ) { item_budget = ilevel_data.p_rare[ slot_type ]; //orig_budget = orig_data.p_rare[ slot_type ]; } // Rest else { item_budget = ilevel_data.p_uncommon[ slot_type ]; //orig_budget = orig_data.p_uncommon[ slot_type ]; } } // Precise stat scaling formula for ilevel increase, stats should be // spot on. if ( item.parsed.data.stat_alloc[ idx ] > 0 /* && orig_budget > 0 */ && item_budget > 0 ) { double v_raw = util::round( item.parsed.data.stat_alloc[ idx ] * item_budget / 10000.0 ); // Socket penalty is supposedly gone in Warlords of Draenor, but it really does not seem so in the latest alpha. // NOTENOTENOTENOTE: Item socket cost penalty multiplier _seems_ to be based on _BASE_ itemlevel, not the upgraded one double v_socket_penalty = util::round( item.parsed.data.stat_socket_mul[ idx ] * dbc.item_socket_cost( item.base_item_level() ) ); return static_cast<int>( v_raw - v_socket_penalty ); } // TODO(?): Should we warn the user that we are using an approximation of // the upgraded stats, and that certain stats may be off by one? else return static_cast<int>( floor( item.parsed.data.stat_val[ idx ] * approx_scale_coefficient( item.parsed.data.level, new_ilevel ) ) ); }
static std::pair<std::pair<int, double>, std::pair<int, double> > get_bonus_id_scaling( dbc_t& dbc, const std::vector<const item_bonus_entry_t*>& entries ) { for ( size_t i = 0; i < entries.size(); ++i ) { if ( entries[ i ] -> type == ITEM_BONUS_SCALING ) { const scaling_stat_distribution_t* data = dbc.scaling_stat_distribution( entries[ i ] -> value_1 ); std::pair<const curve_point_t*, const curve_point_t*> curve_data_min = dbc.curve_point( data -> curve_id, data -> min_level ); std::pair<const curve_point_t*, const curve_point_t*> curve_data_max = dbc.curve_point( data -> curve_id, data -> max_level ); return std::pair<std::pair<int, double>, std::pair<int, double> >( std::pair<int, double>( data -> min_level, curve_data_min.first -> val2 ), std::pair<int, double>( data -> max_level, curve_data_max.first -> val2 ) ); } } return std::pair<std::pair<int, double>, std::pair<int, double> >( std::pair<int, double>( -1, 0 ), std::pair<int, double>( -1, 0 ) ); }
uint32_t item_database::armor_value( const item_data_t* item, const dbc_t& dbc, unsigned item_level ) { if ( ! item || item -> quality > 5 ) return 0; unsigned ilevel = item_level ? item_level : item -> level; // Shield have separate armor table, bypass normal calculation if ( item -> item_class == ITEM_CLASS_ARMOR && item -> item_subclass == ITEM_SUBCLASS_ARMOR_SHIELD ) return ( uint32_t ) floor( dbc.item_armor_shield( ilevel ).values[ item -> quality ] + 0.5 ); // Only Cloth, Leather, Mail and Plate armor has innate armor values if ( item -> item_subclass == ITEM_SUBCLASS_ARMOR_MISC || item -> item_subclass > ITEM_SUBCLASS_ARMOR_PLATE ) return 0; double m_invtype = 0, m_quality = 0, total_armor = 0; switch ( item -> inventory_type ) { case INVTYPE_HEAD: case INVTYPE_SHOULDERS: case INVTYPE_CHEST: case INVTYPE_WAIST: case INVTYPE_LEGS: case INVTYPE_FEET: case INVTYPE_WRISTS: case INVTYPE_HANDS: case INVTYPE_CLOAK: case INVTYPE_ROBE: { total_armor = dbc.item_armor_total( ilevel ).armor_type[ item -> item_subclass - 1 ]; m_quality = dbc.item_armor_quality( ilevel ).values[ item -> quality ]; unsigned invtype = item -> inventory_type; if ( invtype == INVTYPE_ROBE ) invtype = INVTYPE_CHEST; m_invtype = dbc.item_armor_inv_type( invtype ).armor_type[ item -> item_subclass - 1 ]; break; } default: return 0; } return ( uint32_t ) floor( total_armor * m_quality * m_invtype + 0.5 ); }
bool dbc_override::register_effect( dbc_t& dbc, unsigned effect_id, const std::string& field, double v ) { spelleffect_data_t* effect = override_db_.get_mutable_effect( effect_id, dbc.ptr ); if ( ! effect ) { const spelleffect_data_t* dbc_effect = dbc.effect( effect_id ); override_db_.clone_spell( dbc_effect -> spell() -> id(), dbc.ptr ); effect = override_db_.get_mutable_effect( effect_id, dbc.ptr ); } assert( effect ); effect -> override_field( field, v ); override_entries_.push_back( dbc_override_entry_t( DBC_OVERRIDE_EFFECT, field, effect_id, v ) ); return true; }
void rating_t::init( sim_t* sim, dbc_t& dbc, int level, int type ) { if ( sim -> debug ) log_t::output( sim, "rating_t::init: level=%d type=%s", level, util_t::player_type_string( type ) ); if ( type == ENEMY || type == ENEMY_ADD ) { double max = +1.0E+50; spell_haste = max; spell_hit = max; spell_crit = max; attack_haste = max; attack_hit = max; attack_crit = max; ranged_haste = max; ranged_hit = max; ranged_crit = max; expertise = max; dodge = max; parry = max; block = max; mastery = max; } else { spell_haste = dbc.combat_rating( RATING_SPELL_HASTE, level ); spell_hit = dbc.combat_rating( RATING_SPELL_HIT, level ); spell_crit = dbc.combat_rating( RATING_SPELL_CRIT, level ); attack_haste = dbc.combat_rating( RATING_MELEE_HASTE, level ); attack_hit = dbc.combat_rating( RATING_MELEE_HIT, level ); attack_crit = dbc.combat_rating( RATING_MELEE_CRIT, level ); ranged_haste = dbc.combat_rating( RATING_RANGED_HASTE, level ); ranged_hit = dbc.combat_rating( RATING_RANGED_HIT, level ); ranged_crit = dbc.combat_rating( RATING_RANGED_CRIT, level ); expertise = dbc.combat_rating( RATING_EXPERTISE, level ); dodge = dbc.combat_rating( RATING_DODGE, level ); parry = dbc.combat_rating( RATING_PARRY, level ); block = dbc.combat_rating( RATING_BLOCK, level ); mastery = dbc.combat_rating( RATING_MASTERY, level ) / 100; } }
double rating_t::get_attribute_base( sim_t* /* sim */, dbc_t& dbc, int level, player_type class_type, race_type race, base_stat_type stat_type ) { double res = 0.0; switch ( stat_type ) { case BASE_STAT_STRENGTH: res = dbc.race_base( race ).strength + dbc.attribute_base( class_type, level ).strength; break; case BASE_STAT_AGILITY: res = dbc.race_base( race ).agility + dbc.attribute_base( class_type, level ).agility; break; case BASE_STAT_STAMINA: res = dbc.race_base( race ).stamina + dbc.attribute_base( class_type, level ).stamina; break; case BASE_STAT_INTELLECT: res = dbc.race_base( race ).intellect + dbc.attribute_base( class_type, level ).intellect; break; case BASE_STAT_SPIRIT: res = dbc.race_base( race ).spirit + dbc.attribute_base( class_type, level ).spirit; if ( race == RACE_HUMAN ) res *= 1.03; break; case BASE_STAT_HEALTH: res = dbc.attribute_base( class_type, level ).base_health; break; case BASE_STAT_MANA: res = dbc.attribute_base( class_type, level ).base_resource; break; case BASE_STAT_MELEE_CRIT_PER_AGI: res = dbc.melee_crit_scaling( class_type, level ); break; case BASE_STAT_SPELL_CRIT_PER_INT: res = dbc.spell_crit_scaling( class_type, level ); break; case BASE_STAT_DODGE_PER_AGI: res = dbc.dodge_scaling( class_type, level ); break; case BASE_STAT_MELEE_CRIT: res = dbc.melee_crit_base( class_type ); break; case BASE_STAT_SPELL_CRIT: res = dbc.spell_crit_base( class_type ); break; case BASE_STAT_MP5: res = dbc.regen_base( class_type, level ); break; case BASE_STAT_SPI_REGEN: res = dbc.regen_spirit( class_type, level ); break; default: break; } return res; }
std::string dbc::bonus_ids_str( dbc_t& dbc) { std::vector<unsigned> bonus_ids; std::stringstream s; const item_bonus_entry_t* e = dbc::item_bonus_entries( dbc.ptr ); while ( e -> id != 0 ) { if ( std::find( bonus_ids.begin(), bonus_ids.end(), e -> bonus_id ) != bonus_ids.end() ) { e++; continue; } // Need at least one "relevant" type for us if ( e -> type != ITEM_BONUS_ILEVEL && e -> type != ITEM_BONUS_MOD && e -> type != ITEM_BONUS_SOCKET && e -> type != ITEM_BONUS_SCALING ) { e++; continue; } if ( e -> type == ITEM_BONUS_ILEVEL && e -> value_1 == 0 ) { e++; continue; } bonus_ids.push_back( e -> bonus_id ); e++; } std::sort( bonus_ids.begin(), bonus_ids.end() ); for ( size_t i = 0; i < bonus_ids.size(); ++i ) { std::vector<const item_bonus_entry_t*> entries = dbc.item_bonus( bonus_ids[ i ] ); std::string desc = get_bonus_id_desc( dbc.ptr, entries ); std::string suffix = get_bonus_id_suffix( dbc.ptr, entries ); int ilevel = get_bonus_id_ilevel( entries ); int sockets = get_bonus_id_sockets( entries ); std::vector<std::pair<item_mod_type, double> > stats = get_bonus_id_stats( entries ); std::pair< std::pair<int, double>, std::pair<int, double> > scaling = get_bonus_id_scaling( dbc, entries ); std::vector<std::string> fields; fields.push_back( "bonus_id={ " + util::to_string( bonus_ids[ i ] ) + " }" ); if ( ! desc.empty() ) { fields.push_back( "desc={ " + desc + " }" ); } if ( ! suffix.empty() ) { fields.push_back( "suffix={ " + suffix + " }" ); } if ( ilevel != 0 ) { fields.push_back( "ilevel_adjust={ " + util::to_string( ilevel ) + " }" ); } if ( sockets > 0 ) { fields.push_back( "socket={ " + util::to_string( sockets ) + " }" ); } if ( stats.size() > 0 ) { std::string stats_str = "stats={ "; for ( size_t j = 0; j < stats.size(); ++j ) { stats_str += util::to_string( util::round( stats[ j ].second * 100.0, 0 ) ) + "% "; stats_str += util::stat_type_abbrev( util::translate_item_mod( stats[ j ].first ) ); if ( j < stats.size() - 1 ) { stats_str += ", "; } } fields.push_back( stats_str + " }" ); } if ( scaling.first.first >= 0 ) { std::string str = "ilevel={ "; str += util::to_string( scaling.first.second ) + " @plvl " + util::to_string( scaling.first.first ); str += " - "; str += util::to_string( scaling.second.second ) + " @plvl " + util::to_string( scaling.second.first ); str += " }"; fields.push_back( str ); } for ( size_t j = 0; j < fields.size(); ++j ) { s << fields[ j ]; if ( j < fields.size() - 1 ) { s << ", "; } } s << std::endl; } return s.str(); }
uint32_t item_database::weapon_dmg_max( const item_data_t* item, const dbc_t& dbc, unsigned item_level ) { return ( uint32_t ) ceil( dbc.weapon_dps( item, item_level ) * item -> delay / 1000.0 * ( 1 + item -> dmg_range / 2 ) + 0.5 ); }
uint32_t item_database::weapon_dmg_min( const item_data_t* item, const dbc_t& dbc, unsigned item_level ) { return ( uint32_t ) floor( dbc.weapon_dps( item, item_level ) * item -> delay / 1000.0 * ( 1 - item -> dmg_range / 2 ) ); }
uint32_t item_database_t::weapon_dmg_max( const item_data_t* item, const dbc_t& dbc ) { return ( uint32_t ) floor( dbc.weapon_dps( item -> id ) * dbc.item( item -> id ) -> delay / 1000.0 * ( 1 + dbc.item( item -> id ) -> dmg_range / 2 ) + 0.5 ); }