Skip to content

Commit 4803c2a

Browse files
committed
Add raid_events=shield + non-mutating absorb pipeline for enemies
A new `shield` raid event puts an absorb buff on a target enemy (target=, amount=, school= options plus standard timing controls). To support this without breaking class on-hit triggers, account_absorb_buffs no longer mutates s->result_amount when the target is an enemy. The absorbed amount is tracked via s->result_absorbed (already the HP-impact field) and applied at do_damage time. Player-side behavior is unchanged. Also adds player_t::has_absorb() and current_absorb_amount() helpers.
1 parent d7e65df commit 4803c2a

3 files changed

Lines changed: 125 additions & 2 deletions

File tree

engine/player/player.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5888,6 +5888,23 @@ double player_t::composite_player_absorb_received_multiplier() const
58885888
return current.absorb_received_multiplier;
58895889
}
58905890

5891+
bool player_t::has_absorb() const
5892+
{
5893+
for ( const absorb_buff_t* ab : absorb_buff_list )
5894+
if ( ab->check() )
5895+
return true;
5896+
return false;
5897+
}
5898+
5899+
double player_t::current_absorb_amount() const
5900+
{
5901+
double total = 0;
5902+
for ( const absorb_buff_t* ab : absorb_buff_list )
5903+
if ( ab->check() )
5904+
total += ab->current_value;
5905+
return total;
5906+
}
5907+
58915908
double player_t::composite_player_target_crit_chance( player_t* t ) const
58925909
{
58935910
double c = 0.0;
@@ -8481,6 +8498,12 @@ void account_absorb_buffs( player_t& p, action_state_t* s, school_e school )
84818498

84828499
*/
84838500

8501+
// Captured before the absorb iteration mutates s->result_amount. For enemy
8502+
// targets we restore this value at the end so attacker stats and on-hit gates
8503+
// (e.g., "if result_amount > 0") see pre-absorb damage. HP impact is tracked
8504+
// via s->result_absorbed and applied in do_damage().
8505+
double pre_absorb_amount = s->result_amount;
8506+
84848507
if ( !( p.absorb_buff_list.empty() && p.instant_absorb_list.empty() ) )
84858508
{
84868509
/* First, handle high priority absorbs and instant absorbs. These absorbs should
@@ -8607,6 +8630,9 @@ void account_absorb_buffs( player_t& p, action_state_t* s, school_e school )
86078630
p.iteration_absorb_taken += s->self_absorb_amount;
86088631

86098632
s->result_absorbed = s->result_amount;
8633+
8634+
if ( p.is_enemy() )
8635+
s->result_amount = pre_absorb_amount;
86108636
}
86118637

86128638
/**
@@ -8694,9 +8720,9 @@ void player_t::do_damage( action_state_t* incoming_state )
86948720
collect_dmg_taken_data( *this, incoming_state,
86958721
incoming_state->result_mitigated - incoming_state->self_absorb_amount );
86968722

8697-
if ( incoming_state->result_amount > 0.0 )
8723+
if ( incoming_state->result_absorbed > 0.0 )
86988724
{
8699-
actual_amount = resource_loss( RESOURCE_HEALTH, incoming_state->result_amount, nullptr, incoming_action );
8725+
actual_amount = resource_loss( RESOURCE_HEALTH, incoming_state->result_absorbed, nullptr, incoming_action );
87008726
}
87018727

87028728
// New callback system; proc abilities on incoming events.

engine/player/player.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,10 @@ struct player_t : public actor_t
14131413
virtual void assess_damage_imminent_pre_absorb( school_e, result_amount_type, action_state_t* );
14141414
virtual void assess_damage_imminent( school_e, result_amount_type, action_state_t* );
14151415
virtual void do_damage( action_state_t* );
1416+
1417+
bool has_absorb() const;
1418+
double current_absorb_amount() const;
1419+
14161420
virtual void assess_heal( school_e, result_amount_type, action_state_t* );
14171421
virtual void trigger_callbacks( proc_types, proc_types2, action_t* action, action_state_t* state,
14181422
proc_trigger_type_e pt_type = TRIGGER_ACTION );

engine/sim/raid_event.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "action/action.hpp"
88
#include "action/spell.hpp"
99
#include "buff/buff.hpp"
10+
11+
#include <atomic>
1012
#include "dbc/dbc.hpp"
1113
#include "player/pet.hpp"
1214
#include "player/player.hpp"
@@ -1667,6 +1669,95 @@ struct vulnerable_event_t final : public raid_event_t
16671669
}
16681670
};
16691671

1672+
// Shield ===================================================================
1673+
1674+
struct shield_event_t final : public raid_event_t
1675+
{
1676+
double amount;
1677+
std::string school_str;
1678+
player_t* target = nullptr;
1679+
std::string target_str;
1680+
school_e school;
1681+
absorb_buff_t* buff = nullptr;
1682+
// Each shield event owns its own absorb_buff_t so overlapping shields stack;
1683+
// instance_id seeds a unique buff name for buff_t::find lookups.
1684+
int instance_id;
1685+
1686+
shield_event_t( sim_t* s, util::string_view options_str )
1687+
: raid_event_t( s, "shield" ), amount( 0.0 ), school( SCHOOL_NONE )
1688+
{
1689+
static std::atomic<int> next_id { 0 };
1690+
instance_id = ++next_id;
1691+
1692+
add_option( opt_float( "amount", amount ) );
1693+
add_option( opt_string( "school", school_str ) );
1694+
1695+
if ( sim->fight_style == FIGHT_STYLE_DUNGEON_ROUTE )
1696+
add_option( opt_string( "target", target_str ) );
1697+
else
1698+
add_option( opt_func( "target", [ this ]( auto, auto, util::string_view v ) { return parse_target( v ); } ) );
1699+
1700+
parse_options( options_str );
1701+
1702+
if ( sim->fight_style == FIGHT_STYLE_DUNGEON_ROUTE )
1703+
target_str = "Pull_" + util::to_string( pull ) + "_" + target_str;
1704+
1705+
if ( !school_str.empty() )
1706+
{
1707+
school = util::parse_school_type( school_str );
1708+
if ( school == SCHOOL_NONE )
1709+
throw std::invalid_argument( fmt::format( "Unknown shield raid event school '{}'", school_str ) );
1710+
}
1711+
}
1712+
1713+
bool parse_target( std::string_view value )
1714+
{
1715+
auto it = range::find_if( sim->target_list, [ &value ]( const player_t* t ) {
1716+
return util::str_compare_ci( value, t->name() );
1717+
} );
1718+
1719+
if ( it != sim->target_list.end() )
1720+
{
1721+
target = *it;
1722+
return true;
1723+
}
1724+
sim->error( "Unknown shield raid event target '{}'", value );
1725+
return true;
1726+
}
1727+
1728+
void _start() override
1729+
{
1730+
if ( !buff )
1731+
{
1732+
if ( sim->fight_style == FIGHT_STYLE_DUNGEON_ROUTE )
1733+
{
1734+
target = sim->find_player( target_str );
1735+
if ( !target )
1736+
throw std::invalid_argument( fmt::format( "Unknown shield raid event target '{}'", target_str ) );
1737+
}
1738+
else if ( !target )
1739+
{
1740+
target = sim->target;
1741+
}
1742+
1743+
buff = make_buff<absorb_buff_t>( target, fmt::format( "raid_event_shield_{}", instance_id ) );
1744+
if ( school != SCHOOL_NONE )
1745+
buff->set_absorb_school( school );
1746+
}
1747+
1748+
// amount unspecified => unbreakable for the window. infinity sentinel
1749+
// avoids consume()-driven expiry; _finish() drops it on window close.
1750+
double value = amount > 0 ? amount : std::numeric_limits<double>::infinity();
1751+
buff->trigger( 1, value, -1.0, timespan_t::max() );
1752+
}
1753+
1754+
void _finish() override
1755+
{
1756+
if ( buff )
1757+
buff->expire();
1758+
}
1759+
};
1760+
16701761
// Position Switch ==========================================================
16711762

16721763
struct position_event_t : public raid_event_t
@@ -2305,6 +2396,8 @@ std::unique_ptr<raid_event_t> raid_event_t::create( sim_t* sim, util::string_vie
23052396
return std::unique_ptr<raid_event_t>( new stun_event_t( sim, options_str ) );
23062397
if ( name == "vulnerable" )
23072398
return std::unique_ptr<raid_event_t>( new vulnerable_event_t( sim, options_str ) );
2399+
if ( name == "shield" )
2400+
return std::unique_ptr<raid_event_t>( new shield_event_t( sim, options_str ) );
23082401
if ( name == "position_switch" )
23092402
return std::unique_ptr<raid_event_t>( new position_event_t( sim, options_str ) );
23102403
if ( name == "flying" )

0 commit comments

Comments
 (0)