-
Notifications
You must be signed in to change notification settings - Fork 0
Exploration
ItemId. The item can then be looked up in item_data.inc.
sc_player.cpp:1700:player_t.init_items() calls 1777:item.init() to get item stats.
item.cpp:1260:item_t::init()
This contains a bunch of methods to do various things, one of which is to use item data from the lookup to calculate through decode_stats() then stat_value().
for ( size_t i = 0; i < range::size( parsed.data.stat_type_e ); i++ )
{
stat_e s = stat( i );
if ( s == STAT_NONE )
continue;
base_stats.add_stat( s, stat_value( i ) );
stats.add_stat( s, stat_value( i ) );
}First stat_type_e is checked, then a stat_e is cast against the stat stat_type_e value as item_mod:
stat_e util::translate_item_mod( int item_mod )
{
switch ( item_mod )
{
case ITEM_MOD_AGILITY: return STAT_AGILITY;
case ITEM_MOD_STRENGTH: return STAT_STRENGTH;
case ITEM_MOD_INTELLECT: return STAT_INTELLECT;
case ITEM_MOD_SPIRIT: return STAT_SPIRIT;
case ITEM_MOD_STAMINA: return STAT_STAMINA;
case ITEM_MOD_DODGE_RATING: return STAT_DODGE_RATING;
case ITEM_MOD_PARRY_RATING: return STAT_PARRY_RATING;
case ITEM_MOD_BLOCK_RATING: return STAT_BLOCK_RATING;
case ITEM_MOD_HIT_RATING: return STAT_HIT_RATING;
case ITEM_MOD_CRIT_RATING: return STAT_CRIT_RATING;
case ITEM_MOD_HASTE_RATING: return STAT_HASTE_RATING;
case ITEM_MOD_EXPERTISE_RATING: return STAT_EXPERTISE_RATING;
case ITEM_MOD_ATTACK_POWER: return STAT_ATTACK_POWER;
case ITEM_MOD_RANGED_ATTACK_POWER: return STAT_ATTACK_POWER;
case ITEM_MOD_SPELL_POWER: return STAT_SPELL_POWER;
case ITEM_MOD_MASTERY_RATING: return STAT_MASTERY_RATING;
case ITEM_MOD_EXTRA_ARMOR: return STAT_BONUS_ARMOR;
case ITEM_MOD_RESILIENCE_RATING: return STAT_RESILIENCE_RATING;
case ITEM_MOD_PVP_POWER: return STAT_PVP_POWER;
case ITEM_MOD_STRENGTH_AGILITY_INTELLECT: return STAT_STR_AGI_INT;
case ITEM_MOD_AGILITY_INTELLECT: return STAT_AGI_INT;
case ITEM_MOD_STRENGTH_AGILITY: return STAT_STR_AGI;
case ITEM_MOD_STRENGTH_INTELLECT: return STAT_STR_INT;
case ITEM_MOD_VERSATILITY_RATING: return STAT_VERSATILITY_RATING;
case ITEM_MOD_LEECH_RATING: return STAT_LEECH_RATING;
case ITEM_MOD_SPEED_RATING: return STAT_SPEED_RATING;
case ITEM_MOD_AVOIDANCE_RATING: return STAT_AVOIDANCE_RATING;
case ITEM_MOD_CORRUPTION: return STAT_CORRUPTION;
case ITEM_MOD_CORRUPTION_RESISTANCE: return STAT_CORRUPTION_RESISTANCE;
default: return STAT_NONE;
}
}The first enum is item_mod_type and it is translated to stat_e in sc_enums.hpp:926.
This is used to determine what the stat is that it needs to calculate. Then it's added to the item based on the scaled stat calculations from stat_value(). item.cpp:521:item_t::stat_value then gets its value from item_database::scaled_stat.
sc_item_data.cpp:458:item_database::scaled_stat runs through calculating stat scaling.
slot_type is calculated at 479
int slot_type = random_suffix_type( item.parsed.data );
item_database::random_suffix_type is a giant switch, returning the item type.
int item_database::random_suffix_type( const dbc_item_data_t& item )
{
switch ( item.item_class )
{
case ITEM_CLASS_WEAPON:
switch ( item.item_subclass )
{
case ITEM_SUBCLASS_WEAPON_AXE2:
case ITEM_SUBCLASS_WEAPON_MACE2:
case ITEM_SUBCLASS_WEAPON_POLEARM:
case ITEM_SUBCLASS_WEAPON_SWORD2:
case ITEM_SUBCLASS_WEAPON_STAFF:
case ITEM_SUBCLASS_WEAPON_GUN:
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
case ITEM_SUBCLASS_WEAPON_THROWN:
return 0;
default:
return 3;
}
case ITEM_CLASS_ARMOR:
switch ( item.inventory_type )
{
case INVTYPE_HEAD:
case INVTYPE_CHEST:
case INVTYPE_LEGS:
case INVTYPE_ROBE:
return 0;
case INVTYPE_SHOULDERS:
case INVTYPE_WAIST:
case INVTYPE_FEET:
case INVTYPE_HANDS:
case INVTYPE_TRINKET:
return 1;
case INVTYPE_NECK:
case INVTYPE_FINGER:
case INVTYPE_CLOAK:
case INVTYPE_WRISTS:
return 2;
case INVTYPE_WEAPONOFFHAND:
case INVTYPE_HOLDABLE:
case INVTYPE_SHIELD:
return 3;
default:
return -1;
}
default:
return -1;
}
return -1;
}Item budget is used as part of the stat scaling. The budget is calculated based on slot type and item quality.
The end result is a lookup against the rand_prop_data_t based on item level, slot type (which of the 5 values) and which set of propdata to use depends on the item quality.
First the prop data is obtained for the desired item level, then the item_budget is calculated from there. random property data is stored in the rand_prop_points.inc generated file.
https://github.com/simulationcraft/simc/blob/shadowlands/engine/dbc/generated/rand_prop_points.inc
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 ];
}sc_item_data.cpp:509 starts the block that has the meat of the calculations. It calculates the socket penalty, then the raw value, gets the stat type then apply the rating multipliers.
We can mostly ignore socket penalty as it always seems to be 0 in bfa/sl items.
The raw stat value is calculated as:
int v_raw = static_cast<int>(item.parsed.data.stat_alloc[ idx ] * item_budget * 0.0001 - v_socket_penalty + 0.5);Using this specific rounding, and using the items stat allocation value, the item_budget from earlier and the socket penalty of 0 we end up with a raw value of how much stat there should be, as an integer.
Then the stat_type is determined, this is the associated stat_type_e that relates to the same idx we're using for stat_alloc as part of the item.parsed.data properties. Cast this to a item_mod_type (found in data_enums.hh:456)
auto stat_type = static_cast<item_mod_type>( item.parsed.data.stat_type_e[ idx ] );Now we have the stat type and raw stat values, we can apply rating multipliers.
if ( util::is_combat_rating( stat_type ) )
{
v_raw = static_cast<int>(apply_combat_rating_multiplier( item, as<double>( v_raw ) ));
}
else if ( stat_type == ITEM_MOD_STAMINA )
{
v_raw = static_cast<int>(apply_stamina_multiplier( item, as<double>( v_raw ) ));
}Combat ratings are applied based on the type of stat. util::is_combat_rating is in `util.cpp:2846:is_combat_rating: and is a big switch statement against item_mod_type. This type comes from the same enum as the previous item_mod_type as previous.
If it matches one of these types, then the apply_combat_rating_multiplier is run, if its stamina then apply_stamina_multiplier otherwise the value is already correct.
This is a busy set of nested methods that do a bunch of checks to properly apply the right rating.
First, if the sc_item_data.cpp:1504:item_combat_rating_type method returns rating type. This checks with a giant switch the data_enums.hh:337:inventory_type enum casted data.inventory_type item property and returns a data_enums.hh:14:combat_rating_multiplier_type.
combat_rating_multiplier_type item_database::item_combat_rating_type( const dbc_item_data_t& data )
{
switch ( data.inventory_type )
{
case INVTYPE_NECK:
case INVTYPE_FINGER:
return CR_MULTIPLIER_JEWLERY;
case INVTYPE_TRINKET:
return CR_MULTIPLIER_TRINKET;
case INVTYPE_WEAPON:
case INVTYPE_2HWEAPON:
case INVTYPE_WEAPONMAINHAND:
case INVTYPE_WEAPONOFFHAND:
case INVTYPE_RANGED:
case INVTYPE_RANGEDRIGHT:
case INVTYPE_THROWN:
return CR_MULTIPLIER_WEAPON;
case INVTYPE_ROBE:
case INVTYPE_HEAD:
case INVTYPE_SHOULDERS:
case INVTYPE_CHEST:
case INVTYPE_CLOAK:
case INVTYPE_BODY:
case INVTYPE_WRISTS:
case INVTYPE_WAIST:
case INVTYPE_LEGS:
case INVTYPE_FEET:
case INVTYPE_SHIELD:
case INVTYPE_HOLDABLE:
case INVTYPE_HANDS:
return CR_MULTIPLIER_ARMOR;
default:
return CR_MULTIPLIER_INVALID;
}
}This then feeds into another overload of apply_combat_rating_multiplier.
Now that we have type, a lookup is made on ilvl/type against the __combat_ratings_mult_by_ilvl data structure in sc_scale_data.inc.
__combat_ratings_mult_by_ilvl[type][ item_level - 1 ]
This is then multiplied against our earlier raw value to get a new value (referred to in the code still as raw value).
FILE.simc html=output.html output=output.txt debug=1 as the project
command line parameter.
Then place the FILE.simc file in the .\vs\ folder.
sc_player.cpp:1700:player_t.init_items() calls 1777:item.init() to get item stats.
item.cpp:1260:item_t::init()
item mods are translated in util.cpp line 2026 @ translate_item_mod
slot type comes from sc_item_data.cpp line 598
case INVTYPE_SHOULDERS:
case INVTYPE_WAIST:
case INVTYPE_FEET:
case INVTYPE_HANDS:
case INVTYPE_TRINKET:
return 1;
Get random prop data for the ilvl:
random_prop_data_t based on ilvl_ - this comes from ???
Then depending on the quality type we modify it again from random_prop_data_t
4/5 use p_epic 3/7 use p_rare rest use p_uncommon
For ilvl 158, slot type 1 (trinket), the item budget is 62. v_socket_socket_penalty is 0
stat allocation is 6666 from items stat_alloc.
stat_alloc * item_budget * 0.001 - socket_penalty + 0.5
6666 * 62 * .0001 - 0 + 0.5
Item Budget
First you need the slot type. Trinket is 1 Then you need the quality. This trinket is quality 3 or rare. Now you use that to look at ilvldata.p_rare in the random prop data for that ilvl
Get the ilvl row from rand_prop_points.inc in the __rand_prop_points_data collection. This item is ilvl 158:
{ 158,20,28, {82,62,46,41,41 }, {82,62,46,41,41 }, {82,62,46,41,41 } },
^
Format of this object is from random_prop_points.hpp
struct random_prop_data_t
{
unsigned ilevel;
unsigned damage_replace_stat;
unsigned damage_secondary;
float p_epic[5];
float p_rare[5];
float p_uncommon[5];
}
So now to get the 62, we dive into the p_rare which is the 2nd subarray and grab the trinket value (element 1).
{ 158,20,28, {82,62,46,41,41 }, {82,62,46,41,41 }, {82,62,46,41,41 } },
^
Stat Allocation
The 6666 is from this entry on line 39905 in __item_stats_data (item_data.inc)
{ 71, 6666, 0.000000 },
This line number comes from the item entry. Find itemId 184053 ("Sinful Aspirant's Relentless Brooch") in the same file in the __item_data list.
which references &__item_stats_data[39903] for item stats data (entry 39903 in __item_stats_data).
spelleffect_data_t::average in spell_data.cpp
auto budget = item_database::item_budget( item, _spell -> max_scaling_level() );
if ( _spell -> scaling_class() == PLAYER_SPECIAL_SCALE7 )
{
budget = item_database::apply_combat_rating_multiplier( *item, budget );
}
else if ( _spell -> scaling_class() == PLAYER_SPECIAL_SCALE8 )
{
const auto& props = item -> player -> dbc->random_property( item -> item_level() );
budget = props.damage_replace_stat;
}
else if ( _spell->scaling_class() == PLAYER_NONE &&
_spell->flags( spell_attribute::SX_SCALE_ILEVEL ) )
{
const auto& props = item -> player -> dbc->random_property( item -> item_level() );
budget = props.damage_secondary;
}
return _m_coeff * budget;base_budget = 52 combat_rating_multiplier type = TRINKET (2)
ilvl = 110
cr multi = (using type & ilvl): return __combat_ratings_mult_by_ilvl[type][ item_level - 1 ];
cr multi = 1.2892817710000000
cr multi (scaled budget) = 1.2892817710000000 * 52 = 67.042652092
For ilvl 183 trinket Crimson Chorus (id=344806) with coeff of 0.424566
trinket penalty is 1.528306865 from sc_scale_data.inc's trinket array (2)
in __combat_ratings_mult_by_ilvl at 183 ilvl.
base_budget is 104 from rand_prop_points.inc @ ilvl 183. The first array is Epic quality,
and the first value is the base scale value.
104 * 1.528306865 = 158.94391396
158.94391396 * 0.424566 = 67.48218177434136
Tooltip value @ ilvl 183 = 201. int(67.48218177434136) * 3 = 201
Add item bonuses and stats:
sc_item_data.cpp:577
item_database::apply_item_bonus
Switches based on entry.type. entry is item_bonus_entry_t
the definition is found at data_definitions.hh
struct item_bonus_entry_t
{
unsigned id;
unsigned bonus_id;
unsigned type;
int value_1;
int value_2;
unsigned index;
};
type is the enum item_bonus_type from data_enums.hh_
enum item_bonus_type
{
ITEM_BONUS_ILEVEL = 1,
ITEM_BONUS_MOD = 2,
ITEM_BONUS_QUALITY = 3,
ITEM_BONUS_DESC = 4,
ITEM_BONUS_SUFFIX = 5,
ITEM_BONUS_SOCKET = 6,
ITEM_BONUS_REQ_LEVEL = 8,
ITEM_BONUS_SCALING = 11, // Scaling based on ScalingStatDistribution.db2
ITEM_BONUS_SCALING_2 = 13, // Scaling based on ScalingStatDistribution.db2
ITEM_BONUS_SET_ILEVEL= 14,
ITEM_BONUS_ADD_RANK = 17, // Add artifact power rank to a specific trait
ITEM_BONUS_ADD_ITEM_EFFECT = 23,
};
For adding bonuses:
- ITEM_BONUS_LEVEL the value is in the
entry.value_1field, gets added to the items ilvl - ITEM_BONUS_MOD the
value_1is the item modifier and thevalue_2is the allocation percent
the item modifier is one of these from data_enums.hh:
enum item_mod_type {
ITEM_MOD_NONE = -1,
ITEM_MOD_MANA = 0,
ITEM_MOD_HEALTH = 1,
ITEM_MOD_AGILITY = 3,
ITEM_MOD_STRENGTH = 4,
ITEM_MOD_INTELLECT = 5,
ITEM_MOD_SPIRIT = 6,
ITEM_MOD_STAMINA = 7,
ITEM_MOD_DEFENSE_SKILL_RATING = 12,
ITEM_MOD_DODGE_RATING = 13,
ITEM_MOD_PARRY_RATING = 14,
ITEM_MOD_BLOCK_RATING = 15,
ITEM_MOD_HIT_MELEE_RATING = 16,
ITEM_MOD_HIT_RANGED_RATING = 17,
ITEM_MOD_HIT_SPELL_RATING = 18,
ITEM_MOD_CRIT_MELEE_RATING = 19,
ITEM_MOD_CRIT_RANGED_RATING = 20,
ITEM_MOD_CRIT_SPELL_RATING = 21,
ITEM_MOD_CORRUPTION = 22,
ITEM_MOD_CORRUPTION_RESISTANCE = 23, // TODO: Guess
ITEM_MOD_UNSUSED = 24,
ITEM_MOD_CRIT_TAKEN_MELEE_RATING = 25,
ITEM_MOD_CRIT_TAKEN_RANGED_RATING = 26,
ITEM_MOD_CRIT_TAKEN_SPELL_RATING = 27,
ITEM_MOD_HASTE_MELEE_RATING = 28,
ITEM_MOD_HASTE_RANGED_RATING = 29,
ITEM_MOD_HASTE_SPELL_RATING = 30,
ITEM_MOD_HIT_RATING = 31,
ITEM_MOD_CRIT_RATING = 32,
ITEM_MOD_HIT_TAKEN_RATING = 33,
ITEM_MOD_CRIT_TAKEN_RATING = 34,
ITEM_MOD_RESILIENCE_RATING = 35,
ITEM_MOD_HASTE_RATING = 36,
ITEM_MOD_EXPERTISE_RATING = 37,
ITEM_MOD_ATTACK_POWER = 38,
ITEM_MOD_RANGED_ATTACK_POWER = 39,
ITEM_MOD_VERSATILITY_RATING = 40,
ITEM_MOD_SPELL_HEALING_DONE = 41, // deprecated
ITEM_MOD_SPELL_DAMAGE_DONE = 42, // deprecated
ITEM_MOD_MANA_REGENERATION = 43,
ITEM_MOD_ARMOR_PENETRATION_RATING = 44,
ITEM_MOD_SPELL_POWER = 45,
ITEM_MOD_HEALTH_REGEN = 46,
ITEM_MOD_SPELL_PENETRATION = 47,
ITEM_MOD_BLOCK_VALUE = 48,
ITEM_MOD_MASTERY_RATING = 49,
ITEM_MOD_EXTRA_ARMOR = 50,
ITEM_MOD_FIRE_RESISTANCE = 51,
ITEM_MOD_FROST_RESISTANCE = 52,
ITEM_MOD_HOLY_RESISTANCE = 53,
ITEM_MOD_SHADOW_RESISTANCE = 54,
ITEM_MOD_NATURE_RESISTANCE = 55,
ITEM_MOD_ARCANE_RESISTANCE = 56,
ITEM_MOD_PVP_POWER = 57,
ITEM_MOD_MULTISTRIKE_RATING = 59,
ITEM_MOD_READINESS_RATING = 60,
ITEM_MOD_SPEED_RATING = 61,
ITEM_MOD_LEECH_RATING = 62,
ITEM_MOD_AVOIDANCE_RATING = 63,
ITEM_MOD_INDESTRUCTIBLE = 64,
ITEM_MOD_WOD_5 = 65,
ITEM_MOD_WOD_6 = 66,
ITEM_MOD_STRENGTH_AGILITY_INTELLECT = 71,
ITEM_MOD_STRENGTH_AGILITY = 72,
ITEM_MOD_AGILITY_INTELLECT = 73,
ITEM_MOD_STRENGTH_INTELLECT = 74,
};
-
ITEM_BONUS_SOCKET has number of sockets in
value_1and the type (colour) of socket invalue_2 -
ITEM_BONUS_ADD_ITEM_EFFECT- ???