Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions engine/action/action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,21 +1409,24 @@ double action_t::calculate_weapon_damage( double attack_power ) const
if ( !weapon || weapon_multiplier <= 0 )
return 0;

// TODO Forever: Do we need special handling for ranged weapons here?
// Currently the weapon roll is multiplied by melee AP so probably need ranged handling
// TODO Forever: Verify melee and ranged attack power are handled separately
// TODO Forever: Wands are probably a 3rd branch here that we'll need to deal with
// TODO Forever: Verify that ammo is not affected by RAP multipliers (Classic ammo was not affected)
// TODO Forever: Verify that ammo is/isn't affected by damage % talents (Focused Fire, Ranged Weapon Specialization)

// The weapon damage roll (and its bonus damage) is affected by attak power modifiers just like regular attack power
double capm = player -> composite_attack_power_multiplier();
double capm = weapon->slot != SLOT_RANGED ? player -> composite_attack_power_multiplier() : player -> composite_ranged_attack_power_multiplier();
double dmg = capm * ( sim->averaged_range( weapon->min_dmg, weapon->max_dmg ) + weapon->bonus_dmg );
double ammo = sim->averaged_range( weapon->ammo_min_dmg, weapon->ammo_max_dmg );
timespan_t weapon_speed = normalize_weapon_speed ? weapon->get_normalized_speed() : weapon->swing_time;
double power_damage = weapon_speed.total_seconds() * weapon_power_mod * attack_power;
double total_dmg = dmg + power_damage;
double total_dmg = dmg + power_damage + ammo;

sim->print_debug(
"{} weapon damage for {}: base=({:.7g} to {:.7g}) total={:.6f} weapon_damage={:.7g} bonus_damage={:.7g} "
"{} weapon damage for {}: base=({:.7g} to {:.7g}) ammo=({:.7g} to {:.7g}) total={:.6f} weapon_damage={:.7g} bonus_damage={:.7g} "
"multiplier={:.7g} speed={} power_damage={:.7g} ap={:.7g}",
*player, *this, weapon->min_dmg, weapon->max_dmg, total_dmg, dmg, weapon->bonus_dmg, capm, weapon_speed,
power_damage, attack_power );
*player, *this, weapon->min_dmg, weapon->max_dmg, weapon->ammo_min_dmg, weapon->ammo_max_dmg, total_dmg,
dmg, weapon->bonus_dmg, capm, weapon_speed, power_damage, attack_power );

return total_dmg;
}
Expand Down
4 changes: 4 additions & 0 deletions engine/item/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,8 @@ inventory_type item_t::dbc_inventory_type() const
}
case SLOT_RANGED:
return INVTYPE_RANGED;
case SLOT_AMMO:
return INVTYPE_AMMO;
case SLOT_TABARD: return INVTYPE_TABARD;

default: return INVTYPE_NON_EQUIP;
Expand Down Expand Up @@ -736,6 +738,8 @@ item_class item_t::dbc_item_class() const
case SLOT_OFF_HAND:
case SLOT_RANGED:
return ITEM_CLASS_WEAPON;
case SLOT_AMMO:
return ITEM_CLASS_PROJECTILE;
// No good default value, so just use Miscellaneous
default:
return ITEM_CLASS_MISC;
Expand Down
21 changes: 19 additions & 2 deletions engine/player/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2124,6 +2124,21 @@ void player_t::init_weapon( weapon_t& w )
assert( w.type > WEAPON_NONE && w.type < WEAPON_RANGED );
if ( w.slot == SLOT_OFF_HAND )
assert( w.type > WEAPON_NONE && w.type < WEAPON_2H );
// Store ammo min/max damage on the ranged weapon
// TODO Forever: This all needs to be verified. I was only able to test Light Shot in-game at level 11
if ( w.slot == SLOT_RANGED && items[ SLOT_AMMO ].parsed.data.id )
{
const item_t& ammo = items[ SLOT_AMMO ];
double dps = dbc->item_damage_ammo( ammo.item_level() ).value( ammo.parsed.data.quality );
// TODO: Verify that ammo damage is speed normalized
double avg = dps * w.swing_time.total_seconds() / w.get_normalized_speed().total_seconds();
double r = items[ SLOT_RANGED ].parsed.data.dmg_range;
// 60% range fallback if no weapon data is present. This seems to be the default in game data for these items
if ( r <= 0 )
r = 0.6;
w.ammo_min_dmg = std::floor( avg * ( 1 - r / 2 ) );
w.ammo_max_dmg = std::floor( avg * ( 1 + r / 2 ) + 0.5 );
}
}

void player_t::create_special_effects()
Expand Down Expand Up @@ -10485,7 +10500,8 @@ double player_t::avg_item_level() const
int num_ilvl_items = 0;
for ( const auto& item : items )
{
if ( item.slot != SLOT_SHIRT && item.slot != SLOT_TABARD && item.slot != SLOT_RANGED && item.active() )
// TODO Forever: Removed SLOT_RANGED and added SLOT_AMMO - verify how SLOT_RANGED affects avg item level in-game
if ( item.slot != SLOT_SHIRT && item.slot != SLOT_TABARD && item.slot != SLOT_AMMO && item.active() )
{
avg_ilvl += item.item_level();
num_ilvl_items++;
Expand Down Expand Up @@ -12162,7 +12178,7 @@ std::string player_t::create_profile( save_e stype )
const slot_e SLOT_OUT_ORDER[] = {
SLOT_HEAD, SLOT_NECK, SLOT_SHOULDERS, SLOT_BACK, SLOT_CHEST, SLOT_SHIRT, SLOT_TABARD,
SLOT_WRISTS, SLOT_HANDS, SLOT_WAIST, SLOT_LEGS, SLOT_FEET, SLOT_FINGER_1, SLOT_FINGER_2,
SLOT_TRINKET_1, SLOT_TRINKET_2, SLOT_MAIN_HAND, SLOT_OFF_HAND, SLOT_RANGED,
SLOT_TRINKET_1, SLOT_TRINKET_2, SLOT_MAIN_HAND, SLOT_OFF_HAND, SLOT_RANGED, SLOT_AMMO,
};

for ( auto& slot : SLOT_OUT_ORDER )
Expand Down Expand Up @@ -12436,6 +12452,7 @@ void player_t::create_options()
add_option( opt_string( "main_hand", items[ SLOT_MAIN_HAND ].options_str ) );
add_option( opt_string( "off_hand", items[ SLOT_OFF_HAND ].options_str ) );
add_option( opt_string( "ranged", items[ SLOT_RANGED ].options_str ) );
add_option( opt_string( "ammo", items[ SLOT_AMMO ].options_str ) );
add_option( opt_string( "tabard", items[ SLOT_TABARD ].options_str ) );

// Set Bonus
Expand Down
5 changes: 4 additions & 1 deletion engine/player/weapon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct weapon_t
school_e school;
double damage, dps;
double min_dmg, max_dmg;
double ammo_min_dmg, ammo_max_dmg;
timespan_t swing_time;
slot_e slot;
int buff_type;
Expand All @@ -31,6 +32,8 @@ struct weapon_t
dps( d / st.total_seconds() ),
min_dmg( d ),
max_dmg( d ),
ammo_min_dmg( 0 ),
ammo_max_dmg( 0 ),
swing_time( st ),
slot( SLOT_INVALID ),
buff_type( 0 ),
Expand Down Expand Up @@ -85,4 +88,4 @@ struct weapon_t

return proc_chance;
}
};
};
4 changes: 3 additions & 1 deletion engine/sc_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,9 @@ enum slot_e // these enum values match armory settings
SLOT_OFF_HAND = 16,
SLOT_RANGED = 17,
SLOT_TABARD = 18,
SLOT_MAX = 19,
// TODO Forever: No Armory for Forever yet so don't know if/how they will represent ammo slot
SLOT_AMMO = 19,
SLOT_MAX = 20,
SLOT_MIN = 0
};

Expand Down
3 changes: 3 additions & 0 deletions engine/util/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@ const char* util::slot_type_string( slot_e slot )
case SLOT_OFF_HAND: return "off_hand";
case SLOT_RANGED: return "ranged";
case SLOT_TABARD: return "tabard";
case SLOT_AMMO: return "ammo";
default: return "unknown";
}
}
Expand Down Expand Up @@ -2360,6 +2361,8 @@ slot_e util::translate_invtype( inventory_type inv_type )
return SLOT_WRISTS;
case INVTYPE_BODY:
return SLOT_SHIRT;
case INVTYPE_AMMO:
return SLOT_AMMO;
default:
return SLOT_INVALID;
}
Expand Down
Loading