From 86fee7e2b08badc32f3dfee93beff82dcfd59b54 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Mon, 22 Jun 2026 02:05:53 +0200 Subject: [PATCH 1/7] [Gear] Trinket - Gebbo's Bottomless Bag --- engine/player/unique_gear_midnight.cpp | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index 37d94b8039c..b35be5d2612 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -3079,6 +3079,96 @@ void sporelords_mycelium( special_effect_t& effect ) } } ); } + + +// Gebbo's Bottomless Bag +// 1292291 driver +// e1 seashell: crit, ramps up over duration (also a self-dot via e5, ignored) +// e2 scroll: mastery, peak decaying to 0 over duration +// e3 totem: vers, starting bank +// e4 gralstone: haste +// e6 salmon: all 4 secondaries +// e7 voidfin: all 4 secondaries, debuff +// e8 totem: vers, per-cast decrement +void gebbos_bottomless_bag( special_effect_t& effect ) +{ + effect.player->sim->error( UNVERIFIED_VALUE, + "Gebbo's Bottomless Bag: Implementation based on ingame testing, not spell data" ); + + constexpr timespan_t default_duration = 12_s; + constexpr int default_ticks = 12; + + auto salmon = create_buff( effect.player, "fifty_lb_midnight_salmon" ); + for ( auto s : secondary_ratings ) + salmon->add_stat( s, effect.driver()->effectN( 6 ).average( effect ) ); + salmon->set_duration( default_duration ); + + auto gralstone = create_buff( effect.player, "slick_and_slimy_gralstone" ) + ->add_stat( STAT_HASTE_RATING, effect.driver()->effectN( 4 ).average( effect ) ) + ->set_duration( default_duration ); + + auto voidfin = create_buff( effect.player, "rotting_voidfin" ); + for ( auto s : secondary_ratings ) + voidfin->add_stat( s, -effect.driver()->effectN( 7 ).average( effect ) ); + voidfin->set_duration( default_duration ); + + // reverse + period ticks the stacks down 1/sec, draining to 0 by the time it expires + auto scroll = create_buff( effect.player, "tattered_tortollan_scroll" ) + ->add_stat( STAT_MASTERY_RATING, effect.driver()->effectN( 2 ).average( effect ) / default_ticks ) + ->set_max_stack( default_ticks ) + ->set_reverse( true ) + ->set_period( 1_s ) + ->set_duration( default_duration ); + + // non-reverse + period ramps the stacks up 1/sec instead + auto seashell = create_buff( effect.player, "seriously_shard_seashell" ) + ->add_stat( STAT_CRIT_RATING, effect.driver()->effectN( 1 ).average( effect ) ) + ->set_max_stack( default_ticks ) + ->set_period( 1_s ) + ->set_duration( default_duration ); + + // drains per cast rather than over time, until the bank is empty + auto totem_decrement = effect.driver()->effectN( 8 ).average( effect ); + auto totem_stacks = std::max( + 1, as( std::lround( effect.driver()->effectN( 3 ).average( effect ) / totem_decrement ) ) ); + auto totem = create_buff( effect.player, "brittle_torga_totem" ) + ->add_stat( STAT_VERSATILITY_RATING, totem_decrement ) + ->set_max_stack( totem_stacks ) + ->set_reverse( true ); + + struct totem_drain_cb_t : public dbc_proc_callback_t + { + buff_t* totem; + + totem_drain_cb_t( const special_effect_t& e, buff_t* t ) : dbc_proc_callback_t( e.player, e ), totem( t ) + {} + + void execute( const spell_data_t*, player_t*, action_state_t* s ) override + { + if ( s && s->action && !s->action->background ) + totem->decrement(); + } + }; + + auto totem_drain = new special_effect_t( effect.player ); + totem_drain->name_str = "brittle_torga_totem_drain"; + totem_drain->proc_flags_ = effect.driver()->proc_flags(); + totem_drain->proc_chance_ = 1.0; + totem_drain->set_can_proc_from_procs( false ); + effect.player->special_effects.push_back( totem_drain ); + + auto totem_drain_cb = new totem_drain_cb_t( *totem_drain, totem ); + totem_drain_cb->activate_with_buff( totem, true ); + + std::array artifacts{ { salmon, gralstone, voidfin, scroll, seashell, totem } }; + + effect.player->callbacks.register_callback_execute_function( + effect.spell_id, [ artifacts ]( dbc_proc_callback_t*, const spell_data_t*, player_t* p, action_state_t* ) { + p->rng().range( artifacts )->trigger(); + } ); + + new dbc_proc_callback_t( effect.player, effect ); +} } // namespace trinkets namespace weapons @@ -4116,6 +4206,9 @@ void register_special_effects() set_min_version( wowv_t( 12, 0, 7 ) ); register_special_effect( 1284696, trinkets::sporelords_mycelium ); reset_version_check(); + set_min_version( wowv_t( 12, 1, 0 ) ); + register_special_effect( 1292291, trinkets::gebbos_bottomless_bag ); + reset_version_check(); // Weapons register_special_effect( { 1253357, 1253359 }, weapons::torments_duality ); // umbral sabre & radiant foil register_special_effect( 1266257, weapons::lightless_lament ); From 33e7118af1ef285ce8dc9492c36c9b4fdd52f3e1 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 24 Jun 2026 19:32:05 +0200 Subject: [PATCH 2/7] Set unified prefix name 'Gebbo' on buffs, to more easily identify them --- engine/player/unique_gear_midnight.cpp | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index b35be5d2612..aa757b223d9 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -3083,8 +3083,8 @@ void sporelords_mycelium( special_effect_t& effect ) // Gebbo's Bottomless Bag // 1292291 driver -// e1 seashell: crit, ramps up over duration (also a self-dot via e5, ignored) -// e2 scroll: mastery, peak decaying to 0 over duration +// e1 seashell: crit, ramps up over duration (also a small self-dot via e5, ignored) +// e2 scroll: mastery, decaying to 0 over duration // e3 totem: vers, starting bank // e4 gralstone: haste // e6 salmon: all 4 secondaries @@ -3092,25 +3092,25 @@ void sporelords_mycelium( special_effect_t& effect ) // e8 totem: vers, per-cast decrement void gebbos_bottomless_bag( special_effect_t& effect ) { - effect.player->sim->error( UNVERIFIED_VALUE, - "Gebbo's Bottomless Bag: Implementation based on ingame testing, not spell data" ); - constexpr timespan_t default_duration = 12_s; constexpr int default_ticks = 12; auto salmon = create_buff( effect.player, "fifty_lb_midnight_salmon" ); for ( auto s : secondary_ratings ) salmon->add_stat( s, effect.driver()->effectN( 6 ).average( effect ) ); - salmon->set_duration( default_duration ); + salmon->set_duration( default_duration ) + ->set_name_reporting( "Gebbo - 50-lb Midnight Salmon" ); auto gralstone = create_buff( effect.player, "slick_and_slimy_gralstone" ) ->add_stat( STAT_HASTE_RATING, effect.driver()->effectN( 4 ).average( effect ) ) - ->set_duration( default_duration ); + ->set_duration( default_duration ) + ->set_name_reporting( "Gebbo - Slick and Slimy Gralstone" ); auto voidfin = create_buff( effect.player, "rotting_voidfin" ); for ( auto s : secondary_ratings ) voidfin->add_stat( s, -effect.driver()->effectN( 7 ).average( effect ) ); - voidfin->set_duration( default_duration ); + voidfin->set_duration( default_duration ) + ->set_name_reporting( "Gebbo - Rotting Voidfin" ); // reverse + period ticks the stacks down 1/sec, draining to 0 by the time it expires auto scroll = create_buff( effect.player, "tattered_tortollan_scroll" ) @@ -3118,23 +3118,26 @@ void gebbos_bottomless_bag( special_effect_t& effect ) ->set_max_stack( default_ticks ) ->set_reverse( true ) ->set_period( 1_s ) - ->set_duration( default_duration ); + ->set_duration( default_duration ) + ->set_name_reporting( "Gebbo - Tattered Tortollan Scroll" ); // non-reverse + period ramps the stacks up 1/sec instead auto seashell = create_buff( effect.player, "seriously_shard_seashell" ) ->add_stat( STAT_CRIT_RATING, effect.driver()->effectN( 1 ).average( effect ) ) ->set_max_stack( default_ticks ) ->set_period( 1_s ) - ->set_duration( default_duration ); + ->set_duration( default_duration ) + ->set_name_reporting( "Gebbo - Seriously Shard Seashell" ); - // drains per cast rather than over time, until the bank is empty + // drains per cast, until the bank is empty auto totem_decrement = effect.driver()->effectN( 8 ).average( effect ); auto totem_stacks = std::max( 1, as( std::lround( effect.driver()->effectN( 3 ).average( effect ) / totem_decrement ) ) ); auto totem = create_buff( effect.player, "brittle_torga_totem" ) ->add_stat( STAT_VERSATILITY_RATING, totem_decrement ) ->set_max_stack( totem_stacks ) - ->set_reverse( true ); + ->set_reverse( true ) + ->set_name_reporting( "Gebbo - Brittle Torga Totem" ); struct totem_drain_cb_t : public dbc_proc_callback_t { From 1cd226b84fd428cff9e6faa9ce96fc3f65c91b2c Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 24 Jun 2026 20:06:20 +0200 Subject: [PATCH 3/7] add effect spellid's to generator --- dbc_extract3/dbc/generator.py | 1 + engine/player/unique_gear_midnight.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dbc_extract3/dbc/generator.py b/dbc_extract3/dbc/generator.py index cca8833a452..ada3195208b 100644 --- a/dbc_extract3/dbc/generator.py +++ b/dbc_extract3/dbc/generator.py @@ -1538,6 +1538,7 @@ class SpellDataGenerator(DataGenerator): 1263768, # Lightspire Core 1263614, # Wraps of Cosmic Madness 1255685, 1255687, 1255688, # crucible of erratic energies + 1292299, 1292300, 1306870, 1308012, 1308013, 1308014, # Gebbo's Bottomless Bag artifact buffs ), # Warrior: diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index c9958a87ba2..2e041002881 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -3165,7 +3165,7 @@ void gebbos_bottomless_bag( special_effect_t& effect ) ->set_max_stack( default_ticks ) ->set_period( 1_s ) ->set_duration( default_duration ) - ->set_name_reporting( "Gebbo - Seriously Shard Seashell" ); + ->set_name_reporting( "Gebbo - Seriously Sharp Seashell" ); // drains per cast, until the bank is empty auto totem_decrement = effect.driver()->effectN( 8 ).average( effect ); From 5d4d6fc7e7c191881ccb570f7dd02c994c3580eb Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 24 Jun 2026 20:13:27 +0200 Subject: [PATCH 4/7] removed again --- dbc_extract3/dbc/generator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dbc_extract3/dbc/generator.py b/dbc_extract3/dbc/generator.py index ada3195208b..cca8833a452 100644 --- a/dbc_extract3/dbc/generator.py +++ b/dbc_extract3/dbc/generator.py @@ -1538,7 +1538,6 @@ class SpellDataGenerator(DataGenerator): 1263768, # Lightspire Core 1263614, # Wraps of Cosmic Madness 1255685, 1255687, 1255688, # crucible of erratic energies - 1292299, 1292300, 1306870, 1308012, 1308013, 1308014, # Gebbo's Bottomless Bag artifact buffs ), # Warrior: From 041e4cbb65f2545bff631c1c658737cbae9408f5 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 24 Jun 2026 20:47:42 +0200 Subject: [PATCH 5/7] Update effects so they use their own spell data now --- engine/player/unique_gear_midnight.cpp | 52 ++++++++++++-------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index 2e041002881..3f122e0fc4c 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -3121,61 +3121,55 @@ void vile_vial_of_volatile_venom( special_effect_t& effect ) // Gebbo's Bottomless Bag // 1292291 driver -// e1 seashell: crit, ramps up over duration (also a small self-dot via e5, ignored) -// e2 scroll: mastery, decaying to 0 over duration -// e3 totem: vers, starting bank -// e4 gralstone: haste -// e6 salmon: all 4 secondaries -// e7 voidfin: all 4 secondaries, debuff -// e8 totem: vers, per-cast decrement +// e1 seashell (1292299) : crit, ramps up over duration (also a self-dot via e5, ignored) +// e2 scroll (1306870) : mastery, decaying to 0 over duration +// e3 totem (1292300) : vers, starting bank +// e4 gralstone (1308012) : haste +// e6 salmon (1308013) : all 4 secondaries +// e7 voidfin (1308014) : all 4 secondaries, debuff +// e8 totem (1292300) : vers, per-cast decrement void gebbos_bottomless_bag( special_effect_t& effect ) { - constexpr timespan_t default_duration = 12_s; constexpr int default_ticks = 12; - auto salmon = create_buff( effect.player, "fifty_lb_midnight_salmon" ); + auto salmon = create_buff( effect.player, "50lb_midnight_salmon", + effect.player->find_spell( 1308013 ) ); for ( auto s : secondary_ratings ) salmon->add_stat( s, effect.driver()->effectN( 6 ).average( effect ) ); - salmon->set_duration( default_duration ) - ->set_name_reporting( "Gebbo - 50-lb Midnight Salmon" ); - auto gralstone = create_buff( effect.player, "slick_and_slimy_gralstone" ) - ->add_stat( STAT_HASTE_RATING, effect.driver()->effectN( 4 ).average( effect ) ) - ->set_duration( default_duration ) - ->set_name_reporting( "Gebbo - Slick and Slimy Gralstone" ); + auto gralstone = create_buff( effect.player, "slick_and_slimy_gralstone", + effect.player->find_spell( 1308012 ) ) + ->add_stat( STAT_HASTE_RATING, effect.driver()->effectN( 4 ).average( effect ) ); - auto voidfin = create_buff( effect.player, "rotting_voidfin" ); + auto voidfin = create_buff( effect.player, "rotting_voidfin", + effect.player->find_spell( 1308014 ) ); for ( auto s : secondary_ratings ) voidfin->add_stat( s, -effect.driver()->effectN( 7 ).average( effect ) ); - voidfin->set_duration( default_duration ) - ->set_name_reporting( "Gebbo - Rotting Voidfin" ); // reverse + period ticks the stacks down 1/sec, draining to 0 by the time it expires - auto scroll = create_buff( effect.player, "tattered_tortollan_scroll" ) + auto scroll = create_buff( effect.player, "tattered_tortollan_scroll", + effect.player->find_spell( 1306870 ) ) ->add_stat( STAT_MASTERY_RATING, effect.driver()->effectN( 2 ).average( effect ) / default_ticks ) ->set_max_stack( default_ticks ) ->set_reverse( true ) - ->set_period( 1_s ) - ->set_duration( default_duration ) - ->set_name_reporting( "Gebbo - Tattered Tortollan Scroll" ); + ->set_period( 1_s ); // non-reverse + period ramps the stacks up 1/sec instead - auto seashell = create_buff( effect.player, "seriously_shard_seashell" ) + auto seashell = create_buff( effect.player, "seriously_sharp_seashell", + effect.player->find_spell( 1292299 ) ) ->add_stat( STAT_CRIT_RATING, effect.driver()->effectN( 1 ).average( effect ) ) ->set_max_stack( default_ticks ) - ->set_period( 1_s ) - ->set_duration( default_duration ) - ->set_name_reporting( "Gebbo - Seriously Sharp Seashell" ); + ->set_period( 1_s ); // drains per cast, until the bank is empty auto totem_decrement = effect.driver()->effectN( 8 ).average( effect ); auto totem_stacks = std::max( 1, as( std::lround( effect.driver()->effectN( 3 ).average( effect ) / totem_decrement ) ) ); - auto totem = create_buff( effect.player, "brittle_torga_totem" ) + auto totem = create_buff( effect.player, "brittle_torga_totem", + effect.player->find_spell( 1292300 ) ) ->add_stat( STAT_VERSATILITY_RATING, totem_decrement ) ->set_max_stack( totem_stacks ) - ->set_reverse( true ) - ->set_name_reporting( "Gebbo - Brittle Torga Totem" ); + ->set_reverse( true ); struct totem_drain_cb_t : public dbc_proc_callback_t { From 923d043ec720a39071422975df2d4ee73167268d Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 24 Jun 2026 23:29:58 +0200 Subject: [PATCH 6/7] get stat type from effect instead of hardcode + get flags from totem's own spell --- engine/player/unique_gear_midnight.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index 3f122e0fc4c..f97789f142a 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -3139,7 +3139,7 @@ void gebbos_bottomless_bag( special_effect_t& effect ) auto gralstone = create_buff( effect.player, "slick_and_slimy_gralstone", effect.player->find_spell( 1308012 ) ) - ->add_stat( STAT_HASTE_RATING, effect.driver()->effectN( 4 ).average( effect ) ); + ->add_stat_from_effect_type( A_MOD_RATING, effect.driver()->effectN( 4 ).average( effect ) ); auto voidfin = create_buff( effect.player, "rotting_voidfin", effect.player->find_spell( 1308014 ) ); @@ -3149,7 +3149,7 @@ void gebbos_bottomless_bag( special_effect_t& effect ) // reverse + period ticks the stacks down 1/sec, draining to 0 by the time it expires auto scroll = create_buff( effect.player, "tattered_tortollan_scroll", effect.player->find_spell( 1306870 ) ) - ->add_stat( STAT_MASTERY_RATING, effect.driver()->effectN( 2 ).average( effect ) / default_ticks ) + ->add_stat_from_effect_type( A_MOD_RATING, effect.driver()->effectN( 2 ).average( effect ) / default_ticks ) ->set_max_stack( default_ticks ) ->set_reverse( true ) ->set_period( 1_s ); @@ -3157,7 +3157,7 @@ void gebbos_bottomless_bag( special_effect_t& effect ) // non-reverse + period ramps the stacks up 1/sec instead auto seashell = create_buff( effect.player, "seriously_sharp_seashell", effect.player->find_spell( 1292299 ) ) - ->add_stat( STAT_CRIT_RATING, effect.driver()->effectN( 1 ).average( effect ) ) + ->add_stat_from_effect_type( A_MOD_RATING, effect.driver()->effectN( 1 ).average( effect ) ) ->set_max_stack( default_ticks ) ->set_period( 1_s ); @@ -3187,8 +3187,7 @@ void gebbos_bottomless_bag( special_effect_t& effect ) auto totem_drain = new special_effect_t( effect.player ); totem_drain->name_str = "brittle_torga_totem_drain"; - totem_drain->proc_flags_ = effect.driver()->proc_flags(); - totem_drain->proc_chance_ = 1.0; + totem_drain->spell_id = 1292300; totem_drain->set_can_proc_from_procs( false ); effect.player->special_effects.push_back( totem_drain ); From a7786e883b963d5e3a622848f8d68ad5f7d6f067 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 24 Jun 2026 23:40:02 +0200 Subject: [PATCH 7/7] forgot totem --- engine/player/unique_gear_midnight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index f97789f142a..345da2beee3 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -3167,7 +3167,7 @@ void gebbos_bottomless_bag( special_effect_t& effect ) 1, as( std::lround( effect.driver()->effectN( 3 ).average( effect ) / totem_decrement ) ) ); auto totem = create_buff( effect.player, "brittle_torga_totem", effect.player->find_spell( 1292300 ) ) - ->add_stat( STAT_VERSATILITY_RATING, totem_decrement ) + ->add_stat_from_effect_type( A_MOD_RATING, totem_decrement ) ->set_max_stack( totem_stacks ) ->set_reverse( true );