Skip to content

Add raid_events=absorb#11435

Open
mat-simon wants to merge 1 commit intosimulationcraft:midnightfrom
mat-simon:feature/raid-event-shield
Open

Add raid_events=absorb#11435
mat-simon wants to merge 1 commit intosimulationcraft:midnightfrom
mat-simon:feature/raid-event-shield

Conversation

@mat-simon
Copy link
Copy Markdown

@mat-simon mat-simon commented May 3, 2026

What

  • raid_events=absorb — new event that puts an absorb_buff_t on a target enemy. Options: target=, amount=, school= plus standard timing controls. No amount= means unbreakable for the window.
  • target.has_absorb APL expression and player_t::has_absorb() / current_absorb_amount() helpers (scoped to enemy actors via enemy_t overrides).
  • Attacker DPS attribution: when an action damages an enemy with an active absorb, the attacker's stats credit the rolled mitigated damage instead of just the post-absorb HP impact. Implemented as a virtual credited_damage_amount on player_t, overridden on enemy_t to add the absorbed delta. result_amount semantics are preserved.

Use case

Encounter modeling — boss / add mechanics with absorb shields (Aleria-style fights). Analogous in shape to raid_event=vulnerable; class APLs and class modules can react to shielded targets via the helpers and the target.has_absorb expression.

@mat-simon mat-simon force-pushed the feature/raid-event-shield branch from 4803c2a to 58a8482 Compare May 3, 2026 11:00
@mat-simon
Copy link
Copy Markdown
Author

Pushed an update: added a target.has_absorb expression to the player expression dispatch (player_t::create_expression in engine/player/player.cpp). It returns 1.0 when the actor has any active absorb_buff_t, 0.0 otherwise.

This was originally scoped as a follow-up but I'm pulling it into this PR because a planned class-module follow-up (Voidweaver's Devour Matter) needs to gate an APL line on shield presence, and the expression is the cleanest way to expose that. Three lines added next to the is_add / is_boss / is_enemy predicates.

No diff impact on the rest of the PR.

Comment thread engine/player/player.cpp Outdated
Comment thread engine/player/player.cpp Outdated
if ( incoming_state->result_absorbed > 0.0 )
{
actual_amount = resource_loss( RESOURCE_HEALTH, incoming_state->result_amount, nullptr, incoming_action );
actual_amount = resource_loss( RESOURCE_HEALTH, incoming_state->result_absorbed, nullptr, incoming_action );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related specifically to this change, but the PR in general...

Historically, there were issues where some effects did not proc when the action attempting to trigger the proc was fully absorbed. The most notable case I immediately recall is Slimy Expulsion Boots during Dragonflight, most notably in Underrot (as it was available in M+ and the final boss has an absorb over its entire HP for the entire fight).

More investigation is definitely required before making this change.

Comment thread engine/player/player.cpp
return expr_t::create_constant( "is_enemy", is_enemy() );

if ( expression_str == "has_absorb" )
return make_fn_expr( expression_str, [ this ] { return has_absorb() ? 1.0 : 0.0; } );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An expression that yields the total amount of absorbs could also be useful, but it will be hard to avoid the trap that UnitGetTotalAbsorbs falls for given that there are multiple types of absorbs.

@mat-simon mat-simon force-pushed the feature/raid-event-shield branch from 58a8482 to 54b9ffb Compare May 4, 2026 00:53
Comment thread engine/player/player.cpp Outdated
@mat-simon mat-simon force-pushed the feature/raid-event-shield branch from 54b9ffb to 5d04f68 Compare May 4, 2026 03:10
Comment thread engine/action/action.cpp Outdated
if ( absorbed > 0 )
credited_amount += absorbed;
}

Copy link
Copy Markdown
Member

@renanthera renanthera May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and the following changes at 2171-2179) could be an assessor that enemy_t registers potentially?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a method on enemy ok?

double enemy_t::credited_damage_amount( const action_state_t* s ) const override {
double absorbed = s->result_mitigated - s->result_absorbed;
return s->result_amount + std::max( 0.0, absorbed );
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could make the assessor but I think the functionality would be the same?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to inject additional code & calculations into the main codepath that every damage goes through for something that only applies to a niche case. You can contain all the code entirely within raid_events.cpp by utilizing assessors and only have it apply to the player should the profile actually create a shield event.

Since you can't really search for existing assessors, something like raid_event_t::init( sim ) would be a good place to search for the absorb event and add the assessor if necessary.

Comment thread engine/sim/raid_event.cpp Outdated
@mat-simon mat-simon changed the title Add raid_events=shield + non-mutating absorb pipeline for enemies Add raid_events=shield May 4, 2026
@mat-simon mat-simon force-pushed the feature/raid-event-shield branch from 5d04f68 to c8e31ab Compare May 4, 2026 05:32
@mat-simon mat-simon changed the title Add raid_events=shield Add raid_events=absorb May 4, 2026
A new `absorb` raid event puts an absorb buff on a target enemy
(target=, amount=, school= options plus standard timing controls).
The raid event creates a real absorb_buff_t so it correctly absorbs
damage and influences fight termination — the encounter-modeling use
case, analogous to raid_event=vulnerable.

When a player damages an enemy with an absorb, the attacker's stats
include the absorbed portion as damage dealt (action.cpp), so DPS
attribution reflects the rolled damage rather than only the post-
absorb HP impact.

Adds has_absorb() and current_absorb_amount() (virtual on player_t,
defined for enemy_t) plus a target.has_absorb expression for APL
queries.
@mat-simon mat-simon force-pushed the feature/raid-event-shield branch from c8e31ab to 47268b2 Compare May 5, 2026 03:22
{
TARGET_MITIGATION = 100U, // Target assessing (mitigation etc)
TARGET_DAMAGE = 200U, // Do damage to target (and related functionality)
ABSORB_CREDIT = 250U, // Credit absorbed-on-enemy damage to attacker stats
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be more common to set the assessor priority to TARGET_DAMAGE+1 if you want the assessor to occur after TARGET_DAMAGE assessors, but beyond that don't mind the exact ordering. That is likely sufficient in this context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants