Skip to content
Open
75 changes: 70 additions & 5 deletions engine/class_modules/sc_rogue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ class rogue_t : public player_t

// Set Bonuses
damage_buff_t* mid1_outlaw_4pc;
buff_t* mid2_outlaw_4pc;

} buffs;

Expand Down Expand Up @@ -724,6 +725,8 @@ class rogue_t : public player_t
const spell_data_t* rupture; // Assassination + Subtlety
const spell_data_t* shadowstep; // Assassination + Subtlety, baseline charge increase passive

// Set Bonuses
const spell_data_t* mid2_outlaw_4pc_buff;
} spec;

// Talents
Expand Down Expand Up @@ -1103,6 +1106,8 @@ class rogue_t : public player_t
const spell_data_t* mid1_subtlety_2pc;
const spell_data_t* mid1_subtlety_4pc;

const spell_data_t* mid2_outlaw_2pc;
const spell_data_t* mid2_outlaw_4pc;
} set_bonuses;

// Options
Expand Down Expand Up @@ -1626,6 +1631,8 @@ class rogue_action_t : public Base
bool mid1_assassination_4pc = false;
bool mid1_subtlety_2pc = false;

bool mid2_outlaw_4pc = false;

damage_affect_data follow_the_blood;
damage_affect_data mastery_executioner;
damage_affect_data mastery_potent_assassin;
Expand Down Expand Up @@ -1785,6 +1792,11 @@ class rogue_action_t : public Base
{
affected_by.mid1_subtlety_2pc = consumes_combo_points();
}

if ( p->set_bonuses.mid2_outlaw_4pc->ok() )
{
affected_by.mid2_outlaw_4pc = ab::data().affected_by( p->spec.mid2_outlaw_4pc_buff->effectN( 1 ) );
}
}

void init() override
Expand Down Expand Up @@ -1895,6 +1907,7 @@ class rogue_action_t : public Base
register_consume_buff( p()->buffs.symbolic_victory, p()->buffs.symbolic_victory->is_affecting( &ab::data() ),
nullptr, p()->bugs ? 0_ms : 1_ms, false, true ); // 2024-08-12 -- Consumed immediatey, does not work with Shadowy Finishers
register_consume_buff( p()->buffs.the_rotten, p()->buffs.the_rotten->is_affecting_direct( &ab::data() ), nullptr, 1_ms, false, true );
register_consume_buff( p()->buffs.mid2_outlaw_4pc, affected_by.mid2_outlaw_4pc );
}

// Type Wrappers ============================================================
Expand Down Expand Up @@ -1992,6 +2005,11 @@ class rogue_action_t : public Base
int consume_cp = consumes_combo_points() ? as<int>( std::min( p()->current_cp(), p()->consume_cp_max() ) ) : 0;
int effective_cp = consume_cp;

if ( affected_by.mid2_outlaw_4pc && p()->buffs.mid2_outlaw_4pc->check() )
{
effective_cp = as<int>( p()->consume_cp_max() );
}

// Apply and Snapshot Supercharger Buffs
if ( p()->talent.rogue.supercharger->ok() && consumes_supercharger() )
{
Expand Down Expand Up @@ -2196,7 +2214,7 @@ class rogue_action_t : public Base
return 0.0;

auto chance = ab::parry_chance(exp, target);
return std::max(0.0, chance);
return std::max( 0.0, chance );
}

public:
Expand Down Expand Up @@ -2608,6 +2626,11 @@ class rogue_action_t : public Base
c *= 1.0 + p()->buffs.goremaws_bite->check_value();
}

if ( affected_by.mid2_outlaw_4pc )
{
c *= 1.0 + p()->buffs.mid2_outlaw_4pc->check_value();
}

return c;
}

Expand Down Expand Up @@ -3381,6 +3404,12 @@ struct melee_t : public rogue_attack_t

if ( p()->talent.outlaw.zero_in->ok() && state->result == RESULT_CRIT )
{
if ( p()->is_ptr() && state->action->weapon->type == WEAPON_DAGGER )
{
double chance = p()->talent.outlaw.zero_in->effectN( 2 ).percent();
if ( p()->rng().roll( chance ) )
return;
}
p()->buffs.zero_in->trigger();
}
}
Expand Down Expand Up @@ -3543,6 +3572,7 @@ struct ambush_t : public rogue_attack_t
{
rogue_attack_t::execute();
trigger_blindside( execute_state );
p()->buffs.mid2_outlaw_4pc->trigger();
}

void impact( action_state_t* state ) override
Expand Down Expand Up @@ -5798,6 +5828,7 @@ struct sinister_strike_t : public rogue_attack_t
rogue_attack_t::execute();
trigger_unseen_blade( execute_state );
trigger_opportunity( execute_state, extra_attack );
p()->buffs.mid2_outlaw_4pc->trigger();
}

void impact( action_state_t* state ) override
Expand Down Expand Up @@ -7547,10 +7578,15 @@ void actions::rogue_action_t<Base>::spend_combo_points( const action_state_t* st

const auto rs = cast_state( state );
double max_spend = std::min( p()->current_cp(), p()->consume_cp_max() );
double cp_loss = max_spend;

if ( affected_by.mid2_outlaw_4pc && p()->buffs.mid2_outlaw_4pc->check() )
cp_loss *= 1.0 + p()->spec.mid2_outlaw_4pc_buff->effectN( 2 ).percent();

ab::stats->consume_resource( RESOURCE_COMBO_POINT, max_spend );
p()->resource_loss( RESOURCE_COMBO_POINT, max_spend );
p()->resource_loss( RESOURCE_COMBO_POINT, cp_loss );

p()->sim->print_log( "{} consumes {} {} for {} ({})", *p(), max_spend, util::resource_type_string( RESOURCE_COMBO_POINT ),
p()->sim->print_log( "{} consumes {} {} for {} ({})", *p(), cp_loss, util::resource_type_string( RESOURCE_COMBO_POINT ),
*this, p()->current_cp() );
// Remove Supercharger Buffs
consume_supercharger( state );
Expand All @@ -7569,7 +7605,8 @@ void actions::rogue_action_t<Base>::spend_combo_points( const action_state_t* st
else if ( p()->buffs.deadly_pursuit->check() )
p()->buffs.deadly_pursuit->refresh();
else
p()->buffs.deadly_pursuit_tracker->trigger( as<int>( max_spend ) );
// 2026-06-30 -- PTR TOCHECK: MID2 Outlaw 4pc does not contribute towards Deadly Pursuit
p()->buffs.deadly_pursuit_tracker->trigger( as<int>( cp_loss ) );
}
}

Expand Down Expand Up @@ -7810,6 +7847,12 @@ void actions::rogue_action_t<Base>::trigger_ruthlessness_cp( const action_state_
return;

int cp = cast_state( state )->get_combo_points();

// 2026-06-29 -- PTR TOCHECK: MID2 Outlaw 4pc can only trigger Ruthlessness if its effective CPs are
// boosted by Supercharger or Coup de Grace
if ( p()->bugs && affected_by.mid2_outlaw_4pc && p()->buffs.mid2_outlaw_4pc->check() )
cp = std::max( 0, cp - as<int>( p()->consume_cp_max() ) );

if ( cp == 0 )
return;

Expand Down Expand Up @@ -8012,6 +8055,8 @@ void actions::rogue_action_t<Base>::trigger_restless_blades( const action_state_
p()->cooldowns.killing_spree->adjust( v, false );
p()->cooldowns.roll_the_bones->adjust( v, false );
p()->cooldowns.sprint->adjust( v, false );

p()->sim->print_log( "{} triggered Restless Blades with {}s total cooldown reduction", *p(), v );
}

template <typename Base>
Expand Down Expand Up @@ -10125,6 +10170,11 @@ void rogue_t::init_spells()
set_bonuses.mid1_outlaw_4pc = sets->set( ROGUE_OUTLAW, MID1, B4 );
set_bonuses.mid1_subtlety_2pc = sets->set( ROGUE_SUBTLETY, MID1, B2 );
set_bonuses.mid1_subtlety_4pc = sets->set( ROGUE_SUBTLETY, MID1, B4 );

set_bonuses.mid2_outlaw_2pc = sets->set( ROGUE_OUTLAW, MID2, B2 );
set_bonuses.mid2_outlaw_4pc = sets->set( ROGUE_OUTLAW, MID2, B4 );

spec.mid2_outlaw_4pc_buff = set_bonuses.mid2_outlaw_4pc->effectN( 1 ).trigger();

// Register passives ======================================================

Expand Down Expand Up @@ -10922,7 +10972,15 @@ void rogue_t::create_buffs()

// Set Bonus Items ========================================================

buffs.mid1_outlaw_4pc = make_buff<damage_buff_t>( this, "whirl_of_blades", set_bonuses.mid1_outlaw_4pc->effectN(2).trigger() );
buffs.mid1_outlaw_4pc = make_buff<damage_buff_t>( this, "whirl_of_blades", set_bonuses.mid1_outlaw_4pc->effectN( 2 ).trigger() );

buffs.mid2_outlaw_4pc = make_buff_fallback( set_bonuses.mid2_outlaw_4pc->ok(), this, "fang_strike", spec.mid2_outlaw_4pc_buff )
->set_default_value_from_effect_type( A_ADD_PCT_MODIFIER, P_RESOURCE_COST_1 );

if ( set_bonuses.mid2_outlaw_4pc->ok() )
{
buffs.mid2_outlaw_4pc->set_chance( set_bonuses.mid2_outlaw_4pc->effectN( 1 ).percent() );
}
}

// rogue_t::invalidate_cache =========================================
Expand Down Expand Up @@ -11635,6 +11693,13 @@ class rogue_module_t : public module_t
.operation( hotfix::HOTFIX_SET )
.modifier( 15 )
.verification_value( 10 );

// 2026-07-10 -- PTR TOCHECK: MID2 Outlaw 4pc has a scripted 20% proc rate while spell data is 12%
hotfix::register_effect( "Rogue", "2026-07-10", "MID2 Outlaw 4pc Proc Chance", 1319250 )
.field( "base_value" )
.operation( hotfix::HOTFIX_SET )
.modifier( 20 )
.verification_value( 12 );
}

void register_actor_initializers( sim_t* ) const override {}
Expand Down
Loading