From 06aa752c29a2c39fb178b3cd94d8307b9441305c Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:25:00 -0600 Subject: [PATCH 1/2] [util] Generalize implementation for highest and lowest stat[s]. Rewrite each in terms of generalization. --- engine/util/util.cpp | 52 ++++---------------------------------------- engine/util/util.hpp | 35 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/engine/util/util.cpp b/engine/util/util.cpp index f022f4f0eb3..8fb46a52719 100644 --- a/engine/util/util.cpp +++ b/engine/util/util.cpp @@ -315,66 +315,22 @@ double util::stat_value( const player_t* p, stat_e stat ) // "normal" player-driven stuff. stat_e util::highest_stat( const player_t* p, util::span stats ) { - assert( !stats.empty() ); - - stat_e stat = stats.front(); - double value = stat_value( p, stat ); - for ( stat_e s : stats.subspan( 1 ) ) - { - const double v = stat_value( p, s ); - if ( value < v ) - { - stat = s; - value = v; - } - } - - return stat; + return util::find_stat( p, stats, std::greater() ); } std::vector util::highest_stats( const player_t* p, util::span stats ) { - double value = stat_value( p, highest_stat( p, stats ) ); - - std::vector vec; - vec.reserve( stats.size() ); - for ( const stat_e stat : stats ) - if ( value == stat_value( p, stat ) ) - vec.emplace_back( stat ); - - return vec; + return util::find_stats( p, stats, std::greater() ); } stat_e util::lowest_stat( const player_t* p, util::span stats ) { - assert( !stats.empty() ); - - stat_e stat = stats.front(); - double value = stat_value( p, stat ); - for ( stat_e s : stats.subspan( 1 ) ) - { - const double v = stat_value( p, s ); - if ( value > v ) - { - stat = s; - value = v; - } - } - - return stat; + return util::find_stat( p, stats, std::less() ); } std::vector util::lowest_stats( const player_t* p, util::span stats ) { - double value = stat_value( p, lowest_stat( p, stats ) ); - - std::vector vec; - vec.reserve( stats.size() ); - for ( const stat_e stat : stats ) - if ( value == stat_value( p, stat ) ) - vec.emplace_back( stat ); - - return vec; + return util::find_stats( p, stats, std::less() ); } /// case-insensitive string comparison diff --git a/engine/util/util.hpp b/engine/util/util.hpp index 3c5f544e89f..f468ccd5c3c 100644 --- a/engine/util/util.hpp +++ b/engine/util/util.hpp @@ -79,6 +79,41 @@ SC_EXCEPTION( sc_invalid_item_string, 82, "Invalid item string" ); namespace util { double stat_value( const player_t* p, stat_e stat ); + +template +stat_e find_stat( const player_t* p, util::span stats, Cmp cmp ) +{ + assert( !stats.empty() ); + + stat_e stat = stats.front(); + double value = stat_value( p, stat ); + for ( const stat_e s : stats.subspan( 1 ) ) + { + const double v = stat_value( p, s ); + if ( cmp( v, value ) ) + { + stat = s; + value = v; + } + } + + return stat; +} + +template +std::vector find_stats( const player_t* p, util::span stats, Cmp cmp ) +{ + double value = stat_value( p, find_stat( p, stats, cmp ) ); + + std::vector vec; + vec.reserve( stats.size() ); + for ( const stat_e stat : stats ) + if ( value == stat_value( p, stat ) ) + vec.emplace_back( stat ); + + return vec; +} + stat_e highest_stat( const player_t* p, util::span stat ); std::vector highest_stats( const player_t* p, util::span stat ); stat_e lowest_stat( const player_t* p, util::span stat ); From 8236efe53caa25d844d8ba0dbebb8cd094726c35 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:25:33 -0600 Subject: [PATCH 2/2] [gear] Update Darkmoon Blood implementation for bug fix. --- engine/player/unique_gear_midnight.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index b04e1e7a1f0..37d94b8039c 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -1039,10 +1039,10 @@ void blood( special_effect_t& effect ) void execute( const spell_data_t*, player_t*, action_state_t* ) override { - auto stats = util::lowest_stats( listener, secondary_ratings ); + auto stat = util::find_stat( listener, secondary_ratings, std::less_equal() ); for ( auto [ s, b ] : buffs ) { - if ( std::find( stats.begin(), stats.end(), s ) != stats.end() ) + if ( stat == s ) b->trigger(); else b->expire();