Skip to content
Merged
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
54 changes: 29 additions & 25 deletions engine/buff/buff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3303,31 +3303,43 @@ stat_buff_t* stat_buff_t::set_stat_from_effect_type( effect_subtype_t type, doub
return add_stat_from_effect_type( type, a, c );
}

double stat_buff_t::buff_stat_stack_amount( const buff_stat_t& buff_stat, int s ) const
double stat_buff_t::buff_stat_stack_amount( const buff_stat_t& buff_stat, int stacks ) const
{
return buff_stat.stack_amount( s );
return std::max( 1.0, std::fabs( buff_stat.amount ) ) * stacks;
}

void stat_buff_t::bump( int stacks, double /* value */ )
void stat_buff_t::update_player_buff_stat( buff_stat_t& buff_stat, int stacks )
{
buff_t::bump( stacks );
// Blizzard likes to use effect coefficients that give (almost) exact values at the
// intended level. Small floating point conversion errors can add up to give the wrong
// value. We compensate by increasing the absolute value by a tiny bit before truncating.
double _val = buff_stat_stack_amount( buff_stat, stacks );
_val = std::copysign( std::trunc( _val + stat_fp_epsilon ), buff_stat.amount );

double delta = _val - buff_stat.current_value;

if ( delta > 0 )
{
player->stat_gain( buff_stat.stat, delta, stat_gain, nullptr, buff_duration() > 0_ms );
}
else if ( delta < 0 )
{
player->stat_loss( buff_stat.stat, std::fabs( delta ), stat_gain, nullptr, buff_duration() > 0_ms );
}

buff_stat.current_value += delta;
}

void stat_buff_t::bump( int stacks, double value )
{
buff_t::bump( stacks, value );

for ( auto& buff_stat : stats )
{
if ( buff_stat.check_func && !buff_stat.check_func( *this ) )
continue;

double delta = buff_stat_stack_amount( buff_stat, current_stack ) - buff_stat.current_value;
if ( delta > 0 )
{
player->stat_gain( buff_stat.stat, delta, stat_gain, nullptr, buff_duration() > timespan_t::zero() );
}
else if ( delta < 0 )
{
player->stat_loss( buff_stat.stat, std::fabs( delta ), stat_gain, nullptr, buff_duration() > timespan_t::zero() );
}

buff_stat.current_value += delta;
update_player_buff_stat( buff_stat, current_stack );
}
}

Expand All @@ -3347,17 +3359,9 @@ void stat_buff_t::decrement( int stacks, double /* value */ )

for ( auto& buff_stat : stats )
{
double delta = buff_stat.current_value - buff_stat_stack_amount( buff_stat, new_stack );
if ( delta > 0 )
{
player->stat_loss( buff_stat.stat, delta, stat_gain, nullptr, buff_duration() > timespan_t::zero() );
}
else if ( delta < 0 )
{
player->stat_gain( buff_stat.stat, std::fabs( delta ), stat_gain, nullptr, buff_duration() > timespan_t::zero() );
}
buff_stat.current_value -= delta;
update_player_buff_stat( buff_stat, new_stack );
}

current_stack -= stacks;

invalidate_cache();
Expand Down
22 changes: 8 additions & 14 deletions engine/buff/buff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,26 +466,17 @@ struct stat_buff_t : public buff_t
double current_value;
stat_check_fn check_func;

buff_stat_t( stat_e s, double a,
std::function<bool( const stat_buff_t& )> c = std::function<bool( const stat_buff_t& )>() )
buff_stat_t( stat_e s, double a, std::function<bool( const stat_buff_t& )> c = nullptr )
: stat( s ), amount( a ), current_value( 0 ), check_func( std::move( c ) )
{
}

double stack_amount( int stacks ) const
{
// Blizzard likes to use effect coefficients that give (almost) exact values at the
// intended level. Small floating point conversion errors can add up to give the wrong
// value. We compensate by increasing the absolute value by a tiny bit before truncating.
double val = std::max( 1.0, std::fabs( amount ) );
return std::copysign( std::trunc( stacks * val + 1e-3 ), amount );
}
{}
};

std::vector<buff_stat_t> stats;
gain_t* stat_gain;
bool manual_stats_added;

virtual double buff_stat_stack_amount( const buff_stat_t&, int ) const;
virtual double buff_stat_stack_amount( const buff_stat_t&, int stacks ) const;
void update_player_buff_stat( buff_stat_t&, int stacks );

void bump ( int stacks = 1, double value = -1.0 ) override;
void decrement( int stacks = 1, double value = -1.0 ) override;
Expand All @@ -500,6 +491,9 @@ struct stat_buff_t : public buff_t

stat_buff_t( actor_pair_t q, util::string_view name );
stat_buff_t( actor_pair_t q, util::string_view name, const spell_data_t*, const item_t* item = nullptr );

// floating point compensation before truncating for final amount to apply to player stats
static constexpr double stat_fp_epsilon = 1e-3;
};

struct absorb_buff_t : public buff_t
Expand Down
26 changes: 2 additions & 24 deletions engine/class_modules/sc_evoker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4583,18 +4583,7 @@ struct ebon_might_t : public evoker_augment_t
if ( buff_stat.check_func && !buff_stat.check_func( *b ) )
continue;

double delta = buff_stat.stack_amount( b->current_stack ) - buff_stat.current_value;
if ( delta > 0 )
{
b->player->stat_gain( buff_stat.stat, delta, b->stat_gain, nullptr, b->buff_duration() > timespan_t::zero() );
}
else if ( delta < 0 )
{
b->player->stat_loss( buff_stat.stat, std::fabs( delta ), b->stat_gain, nullptr,
b->buff_duration() > timespan_t::zero() );
}

buff_stat.current_value += delta;
b->update_player_buff_stat( buff_stat, b->current_stack );
}
}

Expand Down Expand Up @@ -8132,18 +8121,7 @@ struct blistering_scales_buff_t : public evoker_buff_t<buff_t>
if ( buff_stat.check_func && !buff_stat.check_func( *b ) )
continue;

double delta = buff_stat.stack_amount( b->current_stack ) - buff_stat.current_value;
if ( delta > 0 )
{
b->player->stat_gain( buff_stat.stat, delta, b->stat_gain, nullptr, b->buff_duration() > timespan_t::zero() );
}
else if ( delta < 0 )
{
b->player->stat_loss( buff_stat.stat, std::fabs( delta ), b->stat_gain, nullptr,
b->buff_duration() > timespan_t::zero() );
}

buff_stat.current_value += delta;
b->update_player_buff_stat( buff_stat, b->current_stack );
}
}

Expand Down
5 changes: 4 additions & 1 deletion engine/player/azerite_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5208,7 +5208,10 @@ struct worldvein_resonance_buff_t : public buff_t

if ( lifeblood->check() )
{
double delta = stat_entry.stack_amount( lifeblood->current_stack ) - stat_entry.current_value;
double _val = lifeblood->buff_stat_stack_amount( stat_entry, lifeblood->current_stack );
_val = std::copysign( std::trunc( _val + stat_buff_t::stat_fp_epsilon ), stat_entry.amount );

double delta = _val - stat_entry.current_value;
sim->print_debug( "{} worldvein_resonance {}creases lifeblood stats by {}%,"
" stacks={}, old={}, new={} ({}{})",
player->name(),
Expand Down
13 changes: 1 addition & 12 deletions engine/player/unique_gear_bfa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4560,18 +4560,7 @@ void items::ingenious_mana_battery( special_effect_t& effect )

buff_stat.amount = value;

double delta = buff_stat_stack_amount( buff_stat, current_stack ) - buff_stat.current_value;
if ( delta > 0 )
{
player->stat_gain( buff_stat.stat, delta, stat_gain, nullptr, buff_duration() > timespan_t::zero() );
}
else if ( delta < 0 )
{
player->stat_loss( buff_stat.stat, std::fabs( delta ), stat_gain, nullptr,
buff_duration() > timespan_t::zero() );
}

buff_stat.current_value += delta;
update_player_buff_stat( buff_stat, current_stack );
}
}

Expand Down
60 changes: 44 additions & 16 deletions engine/player/unique_gear_midnight.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1415,41 +1415,33 @@ void solarflare_prism( special_effect_t& effect )
{
double hp_inc;
double max_val;
double hp_mult;

solarflare_prism_buff_t( std::string_view n, const special_effect_t& e )
: stat_buff_t( e.player, n, e.player->find_spell( 1255504 ) ), hp_inc( 0.0 ), max_val( 0.0 ), hp_mult( 0.0 )
: stat_buff_t( e.player, n, e.player->find_spell( 1255504 ) ),
hp_inc( e.driver()->effectN( 2 ).average( e ) ),
max_val( e.driver()->effectN( 4 ).average( e ) )
{
set_default_value( e.driver()->effectN( 1 ).average( e ) );

hp_inc = e.driver()->effectN( 2 ).average( e );
max_val = e.driver()->effectN( 4 ).average( e );
add_stat_from_effect_type( A_MOD_RATING, e.driver()->effectN( 1 ).average( e ) );
}

void bump( int s, double v ) override
double buff_stat_stack_amount( const buff_stat_t& stat, int stack ) const override
{
for ( auto& stat : stats )
{
stat.amount = std::min( default_value + hp_inc * hp_mult, max_val );
}
stat_buff_t::bump( s, v );
return std::min( stat.amount + hp_inc * check_value(), max_val );
}
};

struct solarflare_prism_cb_t final : public dbc_proc_callback_t
{
buff_t* buff;

solarflare_prism_cb_t( const special_effect_t& e ) : dbc_proc_callback_t( e.player, e ), buff( nullptr )
solarflare_prism_cb_t( const special_effect_t& e ) : dbc_proc_callback_t( e.player, e )
{
buff = make_buff<solarflare_prism_buff_t>( "solarflare_prism", e );
}

void execute( const spell_data_t*, player_t* t, action_state_t* ) override
{
solarflare_prism_buff_t* sf_buff = debug_cast<solarflare_prism_buff_t*>( buff );
sf_buff->hp_mult = 100 - t->health_percentage();
sf_buff->trigger();
buff->trigger( -1, 100.0 - t->health_percentage() );
}
};

Expand Down Expand Up @@ -3058,6 +3050,39 @@ void gloomspattered_dreadscale( special_effect_t& effect )

effect.execute_action = create_proc_action<gloomspattered_dreadscale_t>( "gloomspattered_dreadscale", effect );
}

// 1284696 driver
// 1284698 buff
void sporelords_mycelium( special_effect_t& effect )
{
std::unordered_map<stat_e, buff_t*> buffs;
auto value = effect.driver()->effectN( 1 ).average( effect );

create_all_stat_buffs( effect, effect.trigger(), value, [ &buffs, value ]( stat_e s, buff_t* b ) {
// don't need a separate leech buff
if ( s == STAT_LEECH_RATING )
return;

// add leech stat
debug_cast<stat_buff_t*>( b )->add_stat_from_effect( 5, value );

buffs[ s ] = b;
} );

new dbc_proc_callback_t( effect.player, effect );

effect.player->callbacks.register_callback_execute_function(
effect.spell_id, [ buffs, p = effect.player ]( auto, auto, auto, auto ) {
auto stat = p->rng().range( secondary_ratings );
for ( auto [ s, b ] : buffs )
{
if ( s == stat )
b->trigger();
else
b->expire();
}
} );
}
} // namespace trinkets

namespace weapons
Expand Down Expand Up @@ -3889,6 +3914,9 @@ void register_special_effects()
register_special_effect( 1253112, trinkets::sylvan_wakrapuku );
register_special_effect( 1260633, trinkets::gloomspattered_dreadscale );
register_special_effect( 1260627, DISABLED_EFFECT ); // Gloom-Spattered Dreadscale Passive Driver
set_min_version( wowv_t( 12, 0, 7 ) );
register_special_effect( 1284696, trinkets::sporelords_mycelium );
reset_version_check();
// Weapons
register_special_effect( { 1253357, 1253359 }, weapons::torments_duality ); // umbral sabre & radiant foil
register_special_effect( 1266257, weapons::lightless_lament );
Expand Down
6 changes: 3 additions & 3 deletions engine/player/unique_gear_thewarwithin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1872,12 +1872,12 @@ void ovinaxs_mercurial_egg( special_effect_t& effect )
{}

// values can be off by a +/-2 due to unknown rounding being performed by the in-game script
// TODO: confirm truncation happens on final amount, and not per stack amount
double buff_stat_stack_amount( const buff_stat_t& stat, int s ) const override
{
double val = std::max( 1.0, std::fabs( stat.amount ) );
double stack = s <= cap ? s : cap + ( s - cap ) * cap_mul;
// TODO: confirm truncation happens on final amount, and not per stack amount
return std::copysign( std::trunc( stack * val + 1e-3 ), stat.amount );

return buff_stat_stack_amount( stat, stack );
}
};

Expand Down
Loading