From 3df76c89f775226f31f7d68ad2f80fa45324dae6 Mon Sep 17 00:00:00 2001 From: Nathan Berman Date: Thu, 19 Dec 2024 20:00:28 +0000 Subject: [PATCH 01/14] updated presets --- ui/core/preset_utils.tsx | 1 + ui/hunter/presets.ts | 246 +++++++++++++++++++++++++++++++-------- ui/hunter/sim.ts | 5 +- 3 files changed, 203 insertions(+), 49 deletions(-) diff --git a/ui/core/preset_utils.tsx b/ui/core/preset_utils.tsx index 11d1ba9de4..7faa6d378e 100644 --- a/ui/core/preset_utils.tsx +++ b/ui/core/preset_utils.tsx @@ -16,6 +16,7 @@ import { Spec, UnitReference, } from './proto/common.js'; +import { Hunter_Options } from './proto/hunter'; import { SavedRotation, SavedTalents } from './proto/ui.js'; import { Stats } from './proto_utils/stats.js'; import { SpecRotation, specTypeFunctions } from './proto_utils/utils.js'; diff --git a/ui/hunter/presets.ts b/ui/hunter/presets.ts index 04c3b63d7c..a03533226a 100644 --- a/ui/hunter/presets.ts +++ b/ui/hunter/presets.ts @@ -44,6 +44,11 @@ import Phase5AplMeleeBm from './apls/p5_melee_bm.apl.json'; import Phase5AplMeleeSv from './apls/p5_melee_sv.apl.json'; import Phase5AplRanged from './apls/p5_ranged.apl.json'; import Phase5AplWeave from './apls/p5_weave.apl.json'; +import Phase6AplMeleeBm from './apls/p6_melee_bm.apl.json'; +import Phase6AplMeleeSv from './apls/p6_melee_sv.apl.json'; +import Phase6AplRangedDraconic from './apls/p6_ranged_draconic.apl.json'; +import Phase6AplRangedKillshot from './apls/p6_ranged_killshot.apl.json'; +import Phase6AplWeave from './apls/p6_weave.apl.json'; import Phase2GearMelee from './gear_sets/p2_melee.gear.json'; import Phase2GearRangedBm from './gear_sets/p2_ranged_bm.gear.json'; import Phase2GearRangedMm from './gear_sets/p2_ranged_mm.gear.json'; @@ -55,6 +60,11 @@ import Phase5GearMeleeBm from './gear_sets/p5_melee_bm.gear.json'; import Phase5GearMeleeSv from './gear_sets/p5_melee_sv.gear.json'; import Phase5GearRangedSv from './gear_sets/p5_ranged_sv.gear.json'; import Phase5GearWeave from './gear_sets/p5_weave.gear.json'; +import Phase6GearMeleeBm from './gear_sets/p6_melee_bm.gear.json'; +import Phase6GearMeleeSv from './gear_sets/p6_melee_sv.gear.json'; +import Phase6GearRangedDraconic from './gear_sets/p6_ranged_draconic.gear.json'; +import Phase6GearRangedKillshot from './gear_sets/p6_ranged_killshot.gear.json'; +import Phase6GearWeave from './gear_sets/p6_weave.gear.json'; import Phase1Gear from './gear_sets/phase1.gear.json'; // Preset options for this spec. @@ -84,21 +94,28 @@ export const GearRangedSVPhase5 = PresetUtils.makePresetGear('P5 Ranged SV', Pha export const GearMeleeBMPhase5 = PresetUtils.makePresetGear('P5 Melee BM', Phase5GearMeleeBm, { customCondition: player => player.getLevel() === 60 }); export const GearMeleeSVPhase5 = PresetUtils.makePresetGear('P5 Melee SV', Phase5GearMeleeSv, { customCondition: player => player.getLevel() === 60 }); +export const GearWeavePhase6 = PresetUtils.makePresetGear('P6 Weave', Phase6GearWeave, { customCondition: player => player.getLevel() === 60 }); +export const GearRangedDraconicPhase6 = PresetUtils.makePresetGear('P6 Ranged Draconic', Phase6GearRangedDraconic, { customCondition: player => player.getLevel() === 60 }); +export const GearRangedKillshotPhase6 = PresetUtils.makePresetGear('P6 Ranged Killshot', Phase6GearRangedKillshot, { customCondition: player => player.getLevel() === 60 }); +export const GearMeleeBMPhase6 = PresetUtils.makePresetGear('P6 Melee BM', Phase6GearMeleeBm, { customCondition: player => player.getLevel() === 60 }); +export const GearMeleeSVPhase6 = PresetUtils.makePresetGear('P6 Melee SV', Phase6GearMeleeSv, { customCondition: player => player.getLevel() === 60 }); + export const GearPresets = { [Phase.Phase1]: [GearBeastMasteryPhase1, GearMarksmanPhase1, GearSurvivalPhase1], [Phase.Phase2]: [GearRangedBmPhase2, GearRangedMmPhase2, GearMeleePhase2], [Phase.Phase3]: [GearRangedMmPhase3, GearMeleeBmPhase3], [Phase.Phase4]: [], //[GearWeavePhase4, GearRangedSVPhase4], - [Phase.Phase5]: [GearWeavePhase5, GearRangedMMPhase5, GearRangedSVPhase5, GearMeleeBMPhase5, GearMeleeSVPhase5], + [Phase.Phase5]: [], //[GearWeavePhase5, GearRangedMMPhase5, GearRangedSVPhase5, GearMeleeBMPhase5, GearMeleeSVPhase5], + [Phase.Phase6]: [GearWeavePhase6, GearRangedDraconicPhase6, GearRangedKillshotPhase6, GearMeleeBMPhase6, GearMeleeSVPhase6], }; -export const DefaultGearWeave = GearPresets[Phase.Phase5][0]; -export const DefaultGearRangedMM = GearPresets[Phase.Phase5][1]; -export const DefaultGearRangedSV = GearPresets[Phase.Phase5][2]; -export const DefaultGearMeleeBM = GearPresets[Phase.Phase5][3]; -export const DefaultGearMeleeSV = GearPresets[Phase.Phase5][4]; +export const DefaultGearWeave = GearPresets[Phase.Phase6][0]; +export const DefaultGearRangedDraconic = GearPresets[Phase.Phase6][1]; +export const DefaultGearRangedKillshot = GearPresets[Phase.Phase6][2]; +export const DefaultGearMeleeBM = GearPresets[Phase.Phase6][3]; +export const DefaultGearMeleeSV = GearPresets[Phase.Phase6][4]; -export const DefaultGear = DefaultGearRangedSV; +export const DefaultGear = DefaultGearRangedKillshot; /////////////////////////////////////////////////////////////////////////// // APL Presets @@ -122,18 +139,26 @@ export const APLRanged22Phase5 = PresetUtils.makePresetAPLRotation('P5 Ranged 2- export const APLMeleeBMPhase5 = PresetUtils.makePresetAPLRotation('P5 Melee BM', Phase5AplMeleeBm, { customCondition: player => player.getLevel() === 60 }); export const APLMeleeSVPhase5 = PresetUtils.makePresetAPLRotation('P5 Melee SV', Phase5AplMeleeSv, { customCondition: player => player.getLevel() === 60 }); +export const APLWeavePhase6 = PresetUtils.makePresetAPLRotation('P6 Weave', Phase6AplWeave, { customCondition: player => player.getLevel() === 60 }); +export const APLRangedDraconicPhase6 = PresetUtils.makePresetAPLRotation('P6 Ranged Draconic', Phase6AplRangedDraconic, { customCondition: player => player.getLevel() === 60 }); +export const APLRangedKillshotPhase6 = PresetUtils.makePresetAPLRotation('P6 Ranged Killshot', Phase6AplRangedKillshot, { customCondition: player => player.getLevel() === 60 }); +export const APLMeleeBMPhase6 = PresetUtils.makePresetAPLRotation('P6 Melee BM', Phase6AplMeleeBm, { customCondition: player => player.getLevel() === 60 }); +export const APLMeleeSVPhase6 = PresetUtils.makePresetAPLRotation('P6 Melee SV', Phase6AplMeleeSv, { customCondition: player => player.getLevel() === 60 }); + export const APLPresets = { [Phase.Phase1]: [APLMeleeWeavePhase1], [Phase.Phase2]: [APLRangedBmPhase2, APLRangedMmPhase2, APLMeleePhase2], [Phase.Phase3]: [APLRangedMmPhase3, APLMeleeBmPhase3], [Phase.Phase4]: [], //[APLWeavePhase4, APLRangedPhase4], - [Phase.Phase5]: [APLWeavePhase5, APLRanged31Phase5, APLRanged22Phase5, APLMeleeBMPhase5, APLMeleeSVPhase5], + [Phase.Phase5]: [], //[APLWeavePhase5, APLRanged31Phase5, APLRanged22Phase5, APLMeleeBMPhase5, APLMeleeSVPhase5], + [Phase.Phase6]: [APLWeavePhase6, APLRangedDraconicPhase6, APLRangedKillshotPhase6, APLMeleeBMPhase6, APLMeleeSVPhase6], }; -export const DefaultAPLWeave = APLPresets[Phase.Phase5][0]; -export const DefaultAPLRanged = APLPresets[Phase.Phase5][1]; -export const DefaultAPLMeleeBM = APLPresets[Phase.Phase5][3]; -export const DefaultAPLMeleeSV = APLPresets[Phase.Phase5][4]; +export const DefaultAPLWeave = APLPresets[Phase.Phase6][0]; +export const DefaultAPLRangedDraconic = APLPresets[Phase.Phase6][1]; +export const DefaultAPLRangedKillshot = APLPresets[Phase.Phase6][2]; +export const DefaultAPLMeleeBM = APLPresets[Phase.Phase6][3]; +export const DefaultAPLMeleeSV = APLPresets[Phase.Phase6][4]; /////////////////////////////////////////////////////////////////////////// // Talent Presets @@ -199,53 +224,44 @@ export const TalentsMeleeSVPhase5 = PresetUtils.makePresetTalents('P5 Melee SV', customCondition: player => player.getLevel() === 60, }); +export const TalentsWeavePhase6 = PresetUtils.makePresetTalents('P6 Weave', SavedTalents.create({ talentsString: '-054510005-3305202202303051' }), { + customCondition: player => player.getLevel() === 60, +}); +export const TalentsRangedMMPhase6 = PresetUtils.makePresetTalents('P6 Ranged MM', SavedTalents.create({ talentsString: '5-05451005503051-3320202' }), { + customCondition: player => player.getLevel() === 60, +}); +export const TalentsRangedSVPhase6 = PresetUtils.makePresetTalents('P6 Ranged SV', SavedTalents.create({ talentsString: '-054510015-334000250230305' }), { + customCondition: player => player.getLevel() === 60, +}); +export const TalentsMeleeBMPhase6 = PresetUtils.makePresetTalents('P6 Melee BM', SavedTalents.create({ talentsString: '5500020050501251-0505-33202' }), { + customCondition: player => player.getLevel() === 60, +}); +export const TalentsMeleeSVPhase6 = PresetUtils.makePresetTalents('P6 Melee SV', SavedTalents.create({ talentsString: '-055500005-3320202412303051' }), { + customCondition: player => player.getLevel() === 60, +}); + export const TalentPresets = { [Phase.Phase1]: [TalentsBeastMasteryPhase1, TalentsMarksmanPhase1, TalentsSurvivalPhase1], [Phase.Phase2]: [TalentsBeastMasteryPhase2, TalentsMarksmanPhase2, TalentsSurvivalPhase2], [Phase.Phase3]: [TalentsRangedMMPhase3, TalentsMeleeBMPhase3], [Phase.Phase4]: [], //[TalentsWeavePhase4, TalentsRangedMMPhase4, TalentsRangedSVPhase4], - [Phase.Phase5]: [TalentsWeavePhase5, TalentsRangedMMPhase5, TalentsRangedSVPhase5, TalentsMeleeBMPhase5, TalentsMeleeSVPhase5], + [Phase.Phase5]: [], //[TalentsWeavePhase5, TalentsRangedMMPhase5, TalentsRangedSVPhase5, TalentsMeleeBMPhase5, TalentsMeleeSVPhase5], + [Phase.Phase6]: [TalentsWeavePhase6, TalentsRangedMMPhase6, TalentsRangedSVPhase6, TalentsMeleeBMPhase6, TalentsMeleeSVPhase6], }; -export const DefaultTalentsWeave = TalentPresets[Phase.Phase5][0]; -export const DefaultTalentsRangedMM = TalentPresets[Phase.Phase5][1]; -export const DefaultTalentsRangedSV = TalentPresets[Phase.Phase5][2]; -export const DefaultTalentsMeleeBM = TalentPresets[Phase.Phase5][3]; -export const DefaultTalentsMeleeSV = TalentPresets[Phase.Phase5][4]; +export const DefaultTalentsWeave = TalentPresets[Phase.Phase6][0]; +export const DefaultTalentsRangedDraconic = TalentPresets[Phase.Phase6][2]; +export const DefaultTalentsRangedKillshot = TalentPresets[Phase.Phase6][2]; +export const DefaultTalentsMeleeBM = TalentPresets[Phase.Phase6][3]; +export const DefaultTalentsMeleeSV = TalentPresets[Phase.Phase6][4]; -export const DefaultTalents = DefaultTalentsWeave; - -export const PresetBuildWeave = PresetUtils.makePresetBuild('Weave', { - gear: DefaultGearWeave, - talents: DefaultTalentsWeave, - rotation: DefaultAPLWeave, -}); -export const PresetBuildRangedMM = PresetUtils.makePresetBuild('Ranged MM', { - gear: DefaultGearRangedMM, - talents: DefaultTalentsRangedMM, - rotation: DefaultAPLRanged, -}); -export const PresetBuildRangedSV = PresetUtils.makePresetBuild('Ranged SV', { - gear: DefaultGearRangedSV, - talents: DefaultTalentsRangedSV, - rotation: DefaultAPLRanged, -}); -export const PresetBuildMeleeBM = PresetUtils.makePresetBuild('Melee BM', { - gear: DefaultGearMeleeBM, - talents: DefaultTalentsMeleeBM, - rotation: DefaultAPLMeleeBM, -}); -export const PresetBuildMeleeSV = PresetUtils.makePresetBuild('Melee SV', { - gear: DefaultGearMeleeSV, - talents: DefaultTalentsMeleeSV, - rotation: DefaultAPLMeleeSV, -}); +export const DefaultTalents = DefaultTalentsRangedKillshot; /////////////////////////////////////////////////////////////////////////// // Options /////////////////////////////////////////////////////////////////////////// -export const DefaultOptions = HunterOptions.create({ +export const OptionsRangedLonewolf = HunterOptions.create({ ammo: Ammo.ThoriumHeadedArrow, quiverBonus: Hunter_Options_QuiverBonus.Speed15, petAttackSpeed: 2.0, @@ -255,7 +271,31 @@ export const DefaultOptions = HunterOptions.create({ sniperTrainingUptime: 1.0, }); -export const DefaultConsumes = Consumes.create({ +export const OptionsRangedPet = HunterOptions.create({ + ammo: Ammo.ThoriumHeadedArrow, + quiverBonus: Hunter_Options_QuiverBonus.Speed15, + petAttackSpeed: 2.0, + petTalents: {}, + petType: PetType.WindSerpent, + petUptime: 1, + sniperTrainingUptime: 1.0, +}); + +export const OptionsMeleePet = HunterOptions.create({ + ammo: Ammo.ThoriumHeadedArrow, + quiverBonus: Hunter_Options_QuiverBonus.Speed15, + petAttackSpeed: 2.0, + petTalents: {}, + petType: PetType.Cat, + petUptime: 1, + sniperTrainingUptime: 1.0, +}); + +export const DefaultOptions = OptionsRangedLonewolf; + +// Consumable Presets + +export const MeleeConsumes = Consumes.create({ agilityElixir: AgilityElixir.ElixirOfTheHoneyBadger, alcohol: Alcohol.AlcoholRumseyRumBlackLabel, attackPowerBuff: AttackPowerBuff.JujuMight, @@ -263,7 +303,7 @@ export const DefaultConsumes = Consumes.create({ defaultPotion: Potions.MajorManaPotion, dragonBreathChili: true, enchantedSigil: EnchantedSigil.WrathOfTheStormSigil, - flask: Flask.FlaskOfAncientKnowledge, + flask: Flask.FlaskOfMadness, food: Food.FoodSmokedDesertDumpling, healthElixir: HealthElixir.ElixirOfFortitude, mainHandImbue: WeaponImbue.WildStrikes, @@ -278,6 +318,52 @@ export const DefaultConsumes = Consumes.create({ zanzaBuff: ZanzaBuff.GroundScorpokAssay, }); +export const RangedConsumes = Consumes.create({ + agilityElixir: AgilityElixir.ElixirOfTheHoneyBadger, + alcohol: Alcohol.AlcoholRumseyRumBlackLabel, + attackPowerBuff: AttackPowerBuff.JujuMight, + defaultConjured: Conjured.ConjuredDemonicRune, + defaultPotion: Potions.MajorManaPotion, + dragonBreathChili: false, + enchantedSigil: EnchantedSigil.WrathOfTheStormSigil, + flask: Flask.FlaskOfAncientKnowledge, + food: Food.FoodGrilledSquid, + healthElixir: HealthElixir.ElixirOfFortitude, + mainHandImbue: WeaponImbue.EnchantedRepellent, + manaRegenElixir: ManaRegenElixir.MagebloodPotion, + offHandImbue: WeaponImbue.EnchantedRepellent, + petAttackPowerConsumable: 1, + petAgilityConsumable: 1, + petStrengthConsumable: 1, + sapperExplosive: SapperExplosive.SapperFumigator, + spellPowerBuff: SpellPowerBuff.ElixirOfTheMageLord, + zanzaBuff: ZanzaBuff.GroundScorpokAssay, +}); + +export const WeaveConsumes = Consumes.create({ + agilityElixir: AgilityElixir.ElixirOfTheHoneyBadger, + alcohol: Alcohol.AlcoholRumseyRumBlackLabel, + attackPowerBuff: AttackPowerBuff.JujuMight, + defaultConjured: Conjured.ConjuredDemonicRune, + defaultPotion: Potions.MajorManaPotion, + dragonBreathChili: false, + enchantedSigil: EnchantedSigil.WrathOfTheStormSigil, + flask: Flask.FlaskOfAncientKnowledge, + food: Food.FoodGrilledSquid, + healthElixir: HealthElixir.ElixirOfFortitude, + mainHandImbue: WeaponImbue.WildStrikes, + manaRegenElixir: ManaRegenElixir.MagebloodPotion, + offHandImbue: WeaponImbue.EnchantedRepellent, + petAttackPowerConsumable: 1, + petAgilityConsumable: 1, + petStrengthConsumable: 1, + sapperExplosive: SapperExplosive.SapperFumigator, + spellPowerBuff: SpellPowerBuff.ElixirOfTheMageLord, + zanzaBuff: ZanzaBuff.GroundScorpokAssay, +}); + +export const DefaultConsumes = RangedConsumes; + export const DefaultRaidBuffs = RaidBuffs.create({ arcaneBrilliance: true, aspectOfTheLion: true, @@ -333,3 +419,67 @@ export const OtherDefaults = { profession2: Profession.Engineering, race: Race.RaceTroll, }; + +/////////////////////////////////////////////////////////////////////////// +// Encounters +/////////////////////////////////////////////////////////////////////////// + +export const EncounterWeavePhase6 = PresetUtils.makePresetEncounter( + 'Weave', + 'https://wowsims.github.io/sod/hunter/#eJztV2toHFUU3nN3djM5acbJzWv2Jmm321anYzad3c3GpGm7k+CPEqWGWKG/pIZWaikkuCD6r5YWbbUS8kcaUVOtNgQjJaBgEVIjQqtoW6Ulvgui+ECM/SFVaeOZ12Z2G03FCopelt17zzn3O4/73cdiXGYqizOdmdAOFtsKJ9m2MYDdbA8MMTjGoLtJgzGnZ8EMwAWAXnYMtu2AUWAnAXhIHI1htHfXPQ9tv1+NaOvizBgDrFCnn1W0Dzr1iYkYSuqvwwqWqW+Q6OUa0k2OK9rZqH7uR0GDN0n6haIfIMMK9cmDivZ1p37lJXvwNA321ugfXtFQVg8dVPTROY3EJ1zkn53BM2TzuaL/cLmBbD56QdEf32v3Xjys6I9RT1KfOqzQ96Un7AAujira+RoMUXffYUX7pjPdLJeroEed5GGjNMTYg2WPQmQI2ChIVIcTAO+DpEKcco+s/UrCCyz0f/tbG9/9ny/xtyzUXd0OxD8iJfTCFtgKO2AQehqSZrY1mzJNM5vMZMxs2kzTJ2NSN9UrbYH+0M4zVVoYq1EWUZTkQ2cbE2UYwXAym8cYopAxKo89T4xPlCMpkpmWbF4DrEV5p23/ykQsoMA6D+aTUUgg0txkqqWD5JorV38ZqvTkZkurA+TPeO1dCGoK8lcf6SmS12OZoPDkL+tLgDQv2NNvgV4e1PEGEcMq4wY5zFl/SDjZZbOrhIRMq+RxsRRreXWpQRsZ0I/6Uz9vEg2ONhqYbq7qs4ObmojxlSKBmlEnSxx3FvzboSPL5p0U6Dji60UntvBmrDWq5QiPTDMmMztclFItt+Sx2qgicXgYQq7QJOEwOF1kqTSfBfE94AXgn4JjG3UhIq6fVB77RK8TpAfiizt5B4YPQAjrjVrSlY+DF1DBImbUk6Ji3HYmv0exz2PeJe5EYWhUliXjQIssHx+Ih3XZ1afzji6ykC7jwpbOc7PGykQFlk86gRx5pxGX2Gs1Ce6aDoNTX4QspSyD+A7wM+Afg1O3QtJeidr/VWkXciwpQCHlNt5FGa/HFF+DSmIJ4oitkU8P6HJw/PqAHnaTW6hqkmBrQ0HKrvUom864ZCSPvJ38tKLBdac+Ub8+Fy83FBIJxEhbvDAzSTNXY5wvdQ2e8+saHNowblzOzlMNxYvDwW7N+8pGIRZQeqcEv5n8LONNxfQo8drnG6+gsJZhnVFjJzMN3rkh3LL6/iwyWodpbmK1qApgRt6m9YHShALVd7KnxwNfI5K4gi9feP5CE+4gl7dhF88t6pKKFhwen9OC610AXC1uwibeUMSN4sALpnYBFzEtcJ/HBNWWk3KkCMc+uSpFhZhnM6dDVngB8w1/VM8gXUvY4Ye4icrTgxbfUEK30iPCY0mPxxLa90WsEF7FeD/h3Y2bed+fxrOpIwXPCYc6mQV8zLNocR9S0IeDMEUIt4pubOdtv7P3UvlSJl6dq107S2zAVp6+ZpSrEfz959567oW1SvjXGi268HnDm4Uxv+ndHKeKU6Z3g/9+KJDUPWMDoXn3nXct0hvhGk3Vh48o3BD6YqY+3wZPwn5wH0a6dYnBKe99KFtnvV6HNcNgBELT0MgFveijidBqVzObS/mdjSwxHS53R5usKrez0oqNHLLbqVyHKzmTW36v087n0sNheWZPJZdv3/7A9l3xNlPLxcPGP+wfgL7fui44N078JRxVzF2fOKi5SzZ41Oq4b2B236nN53JdnsbaAr8BPTHkGg==', +); + +export const EncounterRangedDraconicPhase6 = PresetUtils.makePresetEncounter( + 'Ranged Draconic', + 'https://wowsims.github.io/sod/hunter/#eJztVl1oHFUUnnNnZnf2pBknt5vN5DbGcUllHLth9mdCfpSdRh9i1bKKD3nU0EgrhQYDom8lpGiV1BipYH0wwdqEQiQEFOxLa6CQIGojWKKCFkRRn2ofpCi4nvnZ7RpbI1hB0Puwc+79vvvdc86cc2cxqzGDWcxmLvSCz4bgMVhl+xYADrMJmGawxGDwVhMWQsuHDYBLABW2BPv2wyywVQAuiaUWTFQOPv7s6FNG0rzbYs4CYJPxypRuvj9gv7PYjorxy4yOSeNlWno7Tdjyad1cT9jrPwqavEqrX+v2C0RsMl6iyXcD9uLxAJma083JtP3ZryZqxvSUbs9WTVo+90aoPB9uOE4bvtLt89WA8/lbun1scgdZp+Z0+0WyFOPqMZ1+X58LHLgyq5sX0yiReYS0vx8o3KWlDLATYfgwpIwln+l8HtRpYLOgUBrOAXwCigEWha72f6vgJSb9P/7RwQ//51P8A5MGt/cC1R/VJFRgmNpyP4zBHpFzvZKXd928lysWS67rFjy3UHSLrldRhmFEevKLlCljKyaFirL2TVtWwwQquUJh3ATMIAqaaxfOg52Mkbw7jm3x+sKb1AnZFCZRzRW7vXGklhJE0t59bk8WkSg5t7tUCqRixPhyXt+EZGLk5+nmRqSu9d6H8Aet6PiPya1UI8Y7hEA9uw2xUMevMUO0xblFUzgbkQQ5jbLn7QwCNyYPXA/tidGfRnin6EDuGFqCs35JhKkoUMS1aP8MD2Lmt2kgdlyXEeWdW0S48RGUhPAIU2QwtQzxqbEZHNAv8S5SsDDjpEkhtVLjBPoI+brKw8R6CO/lu7HNadVUnjodMs9WzYCCrDj+e+BUA9CcbaqdTrd0KHnygw7uim7s4tkwe5HzYfby3g13JMgJ4vFdwkGLd9Z21t+Ku7O26+xi+yPxk1fEXryPD24SDd2Va+7S1R+5W6AKcjJBKs7UA4zccqOUnqmavEeU0OH2X1UUsVDd7037GqYUpYjTx6laRazEt4mG1qnPPqL6VHmv6MFd3MFWZzvlX11hTFOjOsgHzRUkSeXyDEiRP/mouiZO6twSnZjmHA1Hj+s3ZFD51nL3gLgf7+EDjS7SctQsy0CJ0a4cshMoHPPaqw8iD5s7312iOqzfBOFxQaUCj4srfLghg2rMYlsy5C0ZypYMdUtGYmwVjkJ0Rdr+VQZr8ZdC89djq8/fYHACpBXo4II+7YmsdGeEXC7na8YQy67IqWi212+JjC6//cRrwVgr90UrF8q3PxGOi+XCjKxtTDRz7cHRp0cPWj2uWbZk51/2X8A+6t8UnTsW/5aOIao3xw8a0Ssbm/f7Dhy6fGTt0U/Lu2PEH4bfAEReM3g=', +); + +export const EncounterRangedKillshotPhase6 = PresetUtils.makePresetEncounter( + 'Ranged Killshot', + 'https://wowsims.github.io/sod/hunter/#eJztVE1oVFcUfufOvJn7Tszr82aSvFxjGR8ir1NG3sxkJPGHebqK8W8WLrIQseIUG0QHB0q7k1TxDyUEXKgLE40aAiMSULArHRESS6sRlOiiFYqiXRTbRbEtGM/7yRjrshYK7VnMfPd855x7vnPvu2hxZrAks5kDneCybtgGE2zHKMA+1g8DDC4zWLPQhFEfuTAN8BigyC7Djp0wBGwCQCjyq0aMFXd98mVprxE3VyZZahSwwaid0c0bK+xqtQ2jxp+DOsaNG+S6lCBufEw3p2L21C+SFjfJ+6NuH6HABuPnEd18RlknPObYsG7uT9gPX5nIjVcjuj00Y5L7elD5d39xnxJ+0O1bM17Mo/O6fXz/AkIXhnX7KKGo8fK4Tr+nh70Gfh3SzQcJVAgeoNrPV2Q/5poBdsyXD93RcvyLDw+BOgBsCKI0husA9yBqQJKkq8ufRvExU/63f9TEvv/8iH9iypqmTqD7R3cSitBLn+VOKEPPgkzayXfkM47j5NO5XAf9Z/NONufknHwx2gvblb6qakawBVFyjPG7t8COW4Qwms44FWwN/aPn6M5bGsZRTeeW5iuUwCUFGX8MNFqIFJJ2lnaQ3wwT7lAh7S8M76MMfqXaNtdvgmiXEnVrHmK2nvmmhlgn12JBrMLmVBNXhVpjjKvS7zDjNTI/9QG5I4OgeP0gywSuiGA9iqR2MZJzKn6r/SO62Cq3ID8L5OVPWkURY5vKpd2lvcIXw+ndCBE9FSH6uo6uHuwJkPH9RV0EMr+jFtU52ZZMYksqwWNCq0GYJGk3hLwfc+1bEIulhWaqhVrEvjeC/eaXVeqVemS3L7s11UyR2hiExGygT6jvEB0V1M5CKEGGOhNSIB8PVdf1bZDrcLUovF2I5AaFaLRtqVYiGsagrjSgOut7hNvykW/aRbNsQm0cwmVdB83pHeztPou9+c6Tc6fZIDXv4PidPTYXhtRRvU1nDp4Y47ft5Qk4DMGtt92XDCbDj5+7UyHqcqcZnAKlBu1C0msds5SPAuZFITMLuplVi2jBaqM7PwCL3bZTJz2bLHQFnruFRZ/69qCQHYzw6f5GwdeXPi/tSi5zzEIykvqXPe/2Yfe91FlS/Vt1DDnzfvogC46sfNHt+mzPiwOTm+8XVoeM2wuvAX2b+Tk=', +); + +export const EncounterMeleeBMPhase6 = PresetUtils.makePresetEncounter( + 'Melee BM', + 'https://wowsims.github.io/sod/hunter/#eJztVF1IFFEU3nN3dnf2mNN0XXX2arBtaePCyuzYVGqxmxVJDyHUg48lWZqli0tQPa1m9IckQaT1oEJUSP4QPUQQlhQY9GeQ+RBURFERqC/hSz937q76UD1VENQZmPud73z33HPP3LmYLxOVBIhODFgNMbId4iRJWqGDwBCBCqbBZYFiMAFQRYZgZx30ABkFoA42mIHuqr07DtY2qy5tTYCEBgEz1JFeRbtdrvf3+1FSO08p6FHvcGrAx2P3uxVtzK2PTTPu3OLsa0Xv/KJx52y7or0r179c8XPn2hlFa/PpXZ/zUFaHzij6jKDPt4vME8K5zWe/UPRnST/XJHsV/WSbrX7Yreg3D+fxpc/3Kvx94qhdwHSPon20wYPuNDgxkqm9LzdXyh6V6G6xeaiUOgg54EmCdAxcHUB6QLoMMARwHWAG4BbAEyAq74Sr7K2EL4njv/1Ro8l/vsUfiKNCFWcTqqAatkMdxGFznmUZhmEahsWfiGlFwjYKl5SYhlklVUONY887p+bEHMTT4EEXOhGsoIxulMIRI4FZKDOO5a6x/KAIh60E5iIyLpEf3QXdG/QiD4QjxTyQnVY/74Ef0Z86Z2mjOJKgEiNlDlrAliKjGuaEfLKbekfAVvIfl4laTHuQ3+RSi5VgEV2OmcEM9F4VIvsGsGc5v5sVYWmXRtlaLKERzA1l27obczo+IjETP1l2PgHfqcCTHGcwL+PVy8NNujQXsEUmM7CQLkM1pPBVSI0jld4qEIybks1pZkVCzBnu99PFLA8XhRbaUa4XvbWMgtlm041sPZbSVegP5dr19UG6CCa+jWgrC2myi2IfzNY0HxNpbnzV6AIZUnWqLReU+Cgch9Rp8cVmCJwDxwhk0yxZVt0aBB1FA5teHfINTEYrPcFxpzel3BJblALLYv5zXbbdi5ammMfRJbuEjUfNi055ojWT5m/d39hQ2xjYVrsvvrc2sKF5x+6mxob6xkBFUyKhRQPu0F92GU61xH5LnsL+X8qjBlau+y11cIuId/xSrLS+aerIvW1Po7OpY9XwDYq30s4=', +); + +export const EncounterMeleeSVPhase6 = PresetUtils.makePresetEncounter( + 'Melee SV', + 'https://wowsims.github.io/sod/hunter/#eJztVF1IFFEU3nP3b/aY03j9m5012ra0aWhjZteJzGI36UF8CCEffLTI0jRdWqJ6szL6wxAfKotIX0oiQ3qI8EXbelCIyCLxISqIoiLQ6kUIrTt3d6wwnzII6gzMnPOd73z33HNnBgMCkUiQqESHDRAndZAgbWRTJ4EBAhVFMvRxLw4TANVkAHY1QA+QEYBKoA7lyRL0VDfvOFy/X3LLm4JE6wPMklK9ovykXO3v96NL+tIlole6z6CbeSz34Iooj3nUpx8VFgwz9JWoXpiVWXC+Q5TflquzN/wsuMyC9jy1eyaAgtTdIarTHL7UwZUneHCXVb8Q1UnOaesV1bPtlvfwiqieYZ5Len5NZPdLvVYDfYz8QUQHc0+nsuV35ZGo4JWI6uHbhkpXJyGHvCfB3QmkB1x9AAMAdwCmAYYBHgOR2ATcG9+48CVx/Lc/arTtnx/xe+KokPiLCdVQC3XQAAmoCoR10zR1ZmY4Go3o7Co1IlE9qptGtasWdjr2nnPLTixA7AIvutGJYIYE9KArbOhJzEVBYb7QPVYU4umwmWRkn8IC4XOjKoR8yNywsY7h+Rnysx74FXz7RFUG1tcZSeoRQCEbHXQley7HAi1P8FBfCiwm+1oV3krEegivC2lE0XGtoi1EQ0kTGc7krJWQGDoTNpUorqGrMTuUhb5bvML6VVgSznkSUSUT0piyGaPUwEIt3+INzvG4ciS5QA/GnEC+kmuvd+xeEYc/zQT4Forpyp/LZ+1yE31dTrvEGu5Qq+qiqKRHN8lkbd/SylJ880nW2tYiJXQVn4eTkp2ZeZjF9oSqMkhpktcM9fvpMiWAOdpSK8v4/JRNvdg+dnpQOYBNtBFlrUBw/zgPzmQnzDNsR9ch0+oCGdb494xfK2QZvA72NuZSfOHBr3JiBE5B+t3Oi08TuAiOFOTTXEGQPDKEHGvSualYpTc07vSlo23xHLvEf7HbstFYWRp5FFuxm9t4LHLVKUwczaZF2w+0NNW3BGvq9yWa64Nb9+/Y09rS1NgSrGhNJuVY0KP9Zb/uqSPxRdEp6f8tHSm4fsui9MHM4PfEtXhZY+vU8dGapzFbOl4L3wABv+hb', +); + +/////////////////////////////////////////////////////////////////////////// +// Presets +/////////////////////////////////////////////////////////////////////////// + +export const PresetBuildWeave = PresetUtils.makePresetBuild('Weave', { + gear: DefaultGearWeave, + talents: DefaultTalentsWeave, + rotation: DefaultAPLWeave, + encounter: EncounterWeavePhase6, +}); +export const PresetBuildRangedDraconic = PresetUtils.makePresetBuild('Ranged Draconic', { + gear: DefaultGearRangedDraconic, + talents: DefaultTalentsRangedDraconic, + rotation: DefaultAPLRangedDraconic, + encounter: EncounterRangedDraconicPhase6, +}); +export const PresetBuildRangedKillshot = PresetUtils.makePresetBuild('Ranged Killshot', { + gear: DefaultGearRangedKillshot, + talents: DefaultTalentsRangedKillshot, + rotation: DefaultAPLRangedKillshot, + encounter: EncounterRangedKillshotPhase6, +}); +export const PresetBuildMeleeBM = PresetUtils.makePresetBuild('Melee DW', { + gear: DefaultGearMeleeBM, + talents: DefaultTalentsMeleeBM, + rotation: DefaultAPLMeleeBM, + encounter: EncounterMeleeBMPhase6, +}); +export const PresetBuildMeleeSV = PresetUtils.makePresetBuild('Melee 2H', { + gear: DefaultGearMeleeSV, + talents: DefaultTalentsMeleeSV, + rotation: DefaultAPLMeleeSV, + encounter: EncounterMeleeSVPhase6, +}); \ No newline at end of file diff --git a/ui/hunter/sim.ts b/ui/hunter/sim.ts index 129d65504f..f9564eb189 100644 --- a/ui/hunter/sim.ts +++ b/ui/hunter/sim.ts @@ -143,6 +143,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHunter, { presets: { // Preset talents that the user can quickly select. talents: [ + ...Presets.TalentPresets[Phase.Phase6], ...Presets.TalentPresets[Phase.Phase5], ...Presets.TalentPresets[Phase.Phase4], ...Presets.TalentPresets[Phase.Phase3], @@ -151,6 +152,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHunter, { ], // Preset rotations that the user can quickly select. rotations: [ + ...Presets.APLPresets[Phase.Phase6], ...Presets.APLPresets[Phase.Phase5], ...Presets.APLPresets[Phase.Phase4], ...Presets.APLPresets[Phase.Phase3], @@ -159,13 +161,14 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecHunter, { ], // Preset gear configurations that the user can quickly select. gear: [ + ...Presets.GearPresets[Phase.Phase6], ...Presets.GearPresets[Phase.Phase5], ...Presets.GearPresets[Phase.Phase4], ...Presets.GearPresets[Phase.Phase3], ...Presets.GearPresets[Phase.Phase2], ...Presets.GearPresets[Phase.Phase1], ], - builds: [Presets.PresetBuildRangedMM, Presets.PresetBuildRangedSV, Presets.PresetBuildMeleeBM, Presets.PresetBuildMeleeSV, Presets.PresetBuildWeave], + builds: [Presets.PresetBuildRangedDraconic, Presets.PresetBuildRangedKillshot, Presets.PresetBuildMeleeBM, Presets.PresetBuildMeleeSV, Presets.PresetBuildWeave], }, autoRotation: player => { From 584acd7158c58dae54f8bc890d960fbe24e811ee Mon Sep 17 00:00:00 2001 From: Nathan Berman Date: Thu, 19 Dec 2024 20:00:39 +0000 Subject: [PATCH 02/14] updated presets --- ui/hunter/apls/p6_melee_bm.apl.json | 23 ++++++++ ui/hunter/apls/p6_melee_sv.apl.json | 26 ++++++++++ ui/hunter/apls/p6_ranged_draconic.apl.json | 42 +++++++++++++++ ui/hunter/apls/p6_ranged_killshot.apl.json | 27 ++++++++++ ui/hunter/apls/p6_weave.apl.json | 52 +++++++++++++++++++ ui/hunter/gear_sets/p6_melee_bm.gear.json | 21 ++++++++ ui/hunter/gear_sets/p6_melee_sv.gear.json | 21 ++++++++ .../gear_sets/p6_ranged_draconic.gear.json | 21 ++++++++ .../gear_sets/p6_ranged_killshot.gear.json | 21 ++++++++ ui/hunter/gear_sets/p6_weave.gear.json | 21 ++++++++ 10 files changed, 275 insertions(+) create mode 100644 ui/hunter/apls/p6_melee_bm.apl.json create mode 100644 ui/hunter/apls/p6_melee_sv.apl.json create mode 100644 ui/hunter/apls/p6_ranged_draconic.apl.json create mode 100644 ui/hunter/apls/p6_ranged_killshot.apl.json create mode 100644 ui/hunter/apls/p6_weave.apl.json create mode 100644 ui/hunter/gear_sets/p6_melee_bm.gear.json create mode 100644 ui/hunter/gear_sets/p6_melee_sv.gear.json create mode 100644 ui/hunter/gear_sets/p6_ranged_draconic.gear.json create mode 100644 ui/hunter/gear_sets/p6_ranged_killshot.gear.json create mode 100644 ui/hunter/gear_sets/p6_weave.gear.json diff --git a/ui/hunter/apls/p6_melee_bm.apl.json b/ui/hunter/apls/p6_melee_bm.apl.json new file mode 100644 index 0000000000..1c7041f772 --- /dev/null +++ b/ui/hunter/apls/p6_melee_bm.apl.json @@ -0,0 +1,23 @@ +{ + "type": "TypeAPL", + "prepullActions": [ + {"action":{"move":{"rangeFromTarget":{"const":{"val":"5"}}}},"doAtValue":{"const":{"val":"-10s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":469145}}},"doAtValue":{"const":{"val":"-5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":25295,"rank":9}}},"doAtValue":{"const":{"val":"-1.5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":20572}}},"doAtValue":{"const":{"val":"-1.5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":19574}}},"doAtValue":{"const":{"val":"-0.1s"}}} + ], + "priorityList": [ + {"action":{"autocastOtherCooldowns":{}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"2"}}}}]}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415358}}}}},{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"1"}}}}]}},"castSpell":{"spellId":{"spellId":415320}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraRemainingTime":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"2s"}}}},{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"1"}}}}]}},"castSpell":{"spellId":{"spellId":415320}}}}, + {"action":{"castSpell":{"spellId":{"spellId":415343}}}}, + {"action":{"castSpell":{"spellId":{"spellId":14271,"rank":4}}}}, + {"action":{"castSpell":{"spellId":{"spellId":415320}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"5%"}}}},{"cmp":{"op":"OpGe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"4s"}}}}]}},"castSpell":{"spellId":{"spellId":415423}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"50%"}}}},"castSpell":{"spellId":{"spellId":469145}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":415343}}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14271,"rank":4}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"castSpell":{"spellId":{"spellId":409530}}}}, + {"hide":true,"action":{"castSpell":{"spellId":{"itemId":233985}}}} + ] +} \ No newline at end of file diff --git a/ui/hunter/apls/p6_melee_sv.apl.json b/ui/hunter/apls/p6_melee_sv.apl.json new file mode 100644 index 0000000000..e97a56e01d --- /dev/null +++ b/ui/hunter/apls/p6_melee_sv.apl.json @@ -0,0 +1,26 @@ +{ + "type": "TypeAPL", + "prepullActions": [ + {"action":{"move":{"rangeFromTarget":{"const":{"val":"5"}}}},"doAtValue":{"const":{"val":"-10s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":469145}}},"doAtValue":{"const":{"val":"-5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":13555,"rank":8}}},"doAtValue":{"const":{"val":"-1.5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":20572}}},"doAtValue":{"const":{"val":"-1.5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":1213366}}},"doAtValue":{"const":{"val":"-0.1s"}}} + ], + "priorityList": [ + {"hide":true,"action":{"autocastOtherCooldowns":{}}}, + {"hide":true,"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"2"}}}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"action":{"condition":{"or":{"vals":[{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"2"}}}},{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"10"}}}}]}},"autocastOtherCooldowns":{}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415358}}}}},{"cmp":{"op":"OpLt","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"3"}}}}]}},"castSpell":{"spellId":{"spellId":415320}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"auraRemainingTime":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"2s"}}}},{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"1"}}}}]}},"castSpell":{"spellId":{"spellId":415320}}}}, + {"action":{"condition":{"auraIsActive":{"auraId":{"spellId":467331}}},"castSpell":{"spellId":{"spellId":458482}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":415358}}},"rhs":{"const":{"val":"5"}}}},{"auraIsActiveWithReactionTime":{"auraId":{"spellId":467331}}}]}},"castSpell":{"spellId":{"spellId":14271,"rank":4}}}}, + {"action":{"castSpell":{"spellId":{"spellId":415343}}}}, + {"action":{"castSpell":{"spellId":{"spellId":458482}}}}, + {"action":{"castSpell":{"spellId":{"spellId":14271,"rank":4}}}}, + {"action":{"castSpell":{"spellId":{"spellId":415320}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"5%"}}}},{"cmp":{"op":"OpGe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"4s"}}}}]}},"castSpell":{"spellId":{"spellId":415423}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"50%"}}}},"castSpell":{"spellId":{"spellId":469145}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"auraRemainingTime":{"auraId":{"spellId":415320}}},"rhs":{"const":{"val":"1.5"}}}},{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":415343}}},"rhs":{"const":{"val":"1.5"}}}},{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"1.5"}}}},{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":14271,"rank":4}}},"rhs":{"const":{"val":"1.5"}}}}]}},"castSpell":{"spellId":{"spellId":409530}}}} + ] +} \ No newline at end of file diff --git a/ui/hunter/apls/p6_ranged_draconic.apl.json b/ui/hunter/apls/p6_ranged_draconic.apl.json new file mode 100644 index 0000000000..42b8dc9ec6 --- /dev/null +++ b/ui/hunter/apls/p6_ranged_draconic.apl.json @@ -0,0 +1,42 @@ +{ + "type": "TypeAPL", + "prepullActions": [ + {"action":{"castSpell":{"spellId":{"spellId":3045}}},"doAtValue":{"const":{"val":"-22s"}},"hide":true}, + {"action":{"castSpell":{"spellId":{"spellId":25296,"rank":7}}},"doAtValue":{"const":{"val":"-10s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":20904,"rank":6}}},"doAtValue":{"const":{"val":"-3.5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":1213366}}},"doAtValue":{"const":{"val":"-0.44s"}},"hide":true}, + {"action":{"castSpell":{"spellId":{"itemId":234462}}},"doAtValue":{"const":{"val":"-0.44s"}},"hide":true}, + {"action":{"castSpell":{"spellId":{"itemId":215162}}},"doAtValue":{"const":{"val":"-0.44s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":26297}}},"doAtValue":{"const":{"val":"-0.44s"}},"hide":true}, + {"action":{"castSpell":{"spellId":{"spellId":25295,"rank":9}}},"doAtValue":{"const":{"val":"-0.44s"}}} + ], + "priorityList": [ + {"action":{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":25295,"rank":9}}}}},"castSpell":{"spellId":{"spellId":25295,"rank":9}}}}, + {"action":{"condition":{"cmp":{"op":"OpLe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"55%"}}}},"castSpell":{"spellId":{"itemId":13444}}}}, + {"action":{"condition":{"cmp":{"op":"OpLe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"65%"}}}},"castSpell":{"spellId":{"itemId":12662}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"2.5s"}}}},"castSpell":{"spellId":{"spellId":1213366}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"2.5s"}}}},"castSpell":{"spellId":{"itemId":234462}}}}, + {"hide":true,"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"2.5s"}}}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"hide":true,"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"2.5s"}}}},"castSpell":{"spellId":{"spellId":26297}}}}, + {"action":{"condition":{"or":{"vals":[{"auraIsActive":{"auraId":{"spellId":1213366}}},{"auraIsActive":{"auraId":{"itemId":234462}}}]}},"autocastOtherCooldowns":{}}}, + {"hide":true,"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":1213366}}},"rhs":{"const":{"val":"1"}}}},"castSpell":{"spellId":{"spellId":26297}}}}, + {"hide":true,"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409535}}},"rhs":{"const":{"val":"3s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409510}}},"rhs":{"const":{"val":"3s"}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}}]}},"castSpell":{"spellId":{"spellId":468388}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"15s"}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}}]}},"castSpell":{"spellId":{"spellId":468388}}}}, + {"hide":true,"action":{"autocastOtherCooldowns":{}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"50%"}}}},{"auraIsActive":{"auraId":{"spellId":415423}}}]}},"cancelAura":{"auraId":{"spellId":415423}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"2s"}}}},{"cmp":{"op":"OpGe","lhs":{"auraRemainingTime":{"auraId":{"spellId":409535}}},"rhs":{"const":{"val":"10s"}}}}]}},"castSpell":{"spellId":{"spellId":409530}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"2s"}}}}]}},"castSpell":{"spellId":{"spellId":409535}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":468388}}}}}]}},"castSpell":{"spellId":{"spellId":409510}}}}, + {"action":{"castSpell":{"spellId":{"spellId":409433}}}}, + {"action":{"castSpell":{"spellId":{"spellId":20904,"rank":6}}}}, + {"action":{"castSpell":{"spellId":{"spellId":25294,"rank":5}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"autoTimeToNext":{"autoType":"Ranged"}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"gcdTimeToReady":{}},"rhs":{"const":{"val":"1s"}}}}]}},"castSpell":{"spellId":{"itemId":233986}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"5%"}}}}]}},"castSpell":{"spellId":{"spellId":415423}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415423}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":14322,"rank":6}}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"1.45s"}}}}]}},"castSpell":{"spellId":{"spellId":25296,"rank":7}}}}, + {"action":{"condition":{"cmp":{"op":"OpEq","lhs":{"const":{"val":"1"}},"rhs":{"const":{"val":"0"}}}},"castSpell":{"spellId":{"spellId":26297,"tag":2}}}}, + {"action":{"condition":{"cmp":{"op":"OpEq","lhs":{"const":{"val":"1"}},"rhs":{"const":{"val":"0"}}}},"castSpell":{"spellId":{"spellId":26297,"tag":3}}}}, + {"action":{"condition":{"cmp":{"op":"OpEq","lhs":{"const":{"val":"1"}},"rhs":{"const":{"val":"0"}}}},"castSpell":{"spellId":{"spellId":26297,"tag":4}}}}, + {"action":{"condition":{"cmp":{"op":"OpEq","lhs":{"const":{"val":"1"}},"rhs":{"const":{"val":"0"}}}},"castSpell":{"spellId":{"spellId":26297,"tag":5}}}}, + {"action":{"condition":{"cmp":{"op":"OpEq","lhs":{"const":{"val":"1"}},"rhs":{"const":{"val":"0"}}}},"castSpell":{"spellId":{"spellId":26297,"tag":6}}}} + ] +} \ No newline at end of file diff --git a/ui/hunter/apls/p6_ranged_killshot.apl.json b/ui/hunter/apls/p6_ranged_killshot.apl.json new file mode 100644 index 0000000000..0e12662e15 --- /dev/null +++ b/ui/hunter/apls/p6_ranged_killshot.apl.json @@ -0,0 +1,27 @@ +{ + "type": "TypeAPL", + "prepullActions": [ + {"action":{"castSpell":{"spellId":{"spellId":25296,"rank":7}}},"doAtValue":{"const":{"val":"-10s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":20904,"rank":6}}},"doAtValue":{"const":{"val":"-3.5s"}}}, + {"action":{"castSpell":{"spellId":{"itemId":215162}}},"doAtValue":{"const":{"val":"-0.45s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":25295,"rank":9}}},"doAtValue":{"const":{"val":"-0.45s"}}}, + {"action":{"activateAura":{"auraId":{"spellId":415413}}},"doAtValue":{"const":{"val":"-0.45s"}},"hide":true} + ], + "priorityList": [ + {"action":{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":25295,"rank":9}}}}},"castSpell":{"spellId":{"spellId":25295,"rank":9}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"autoTimeToNext":{"autoType":"Ranged"}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"gcdTimeToReady":{}},"rhs":{"const":{"val":"1s"}}}},{"cmp":{"op":"OpLt","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"30s"}}}}]}},"castSpell":{"spellId":{"itemId":233986}}}}, + {"action":{"condition":{"spellIsReady":{"spellId":{"spellId":3045}}},"sequence":{"name":"Opener","actions":[{"castSpell":{"spellId":{"spellId":409433}}},{"castSpell":{"spellId":{"spellId":409593}}},{"castSpell":{"spellId":{"spellId":409530}}},{"castSpell":{"spellId":{"spellId":1213366}}},{"castSpell":{"spellId":{"itemId":234462}}},{"castSpell":{"spellId":{"spellId":25294,"rank":5}}},{"castSpell":{"spellId":{"spellId":409433}}}]}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":1213366}}},"rhs":{"const":{"val":"5"}}}},"castSpell":{"spellId":{"spellId":26297}}}}, + {"action":{"condition":{"cmp":{"op":"OpLt","lhs":{"dotRemainingTime":{"spellId":{"spellId":25295,"rank":9}}},"rhs":{"const":{"val":"6s"}}}},"castSpell":{"spellId":{"spellId":409433}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"6s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"4s"}}}},{"spellIsReady":{"spellId":{"spellId":409593}}}]}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"action":{"condition":{"auraIsActive":{"auraId":{"spellId":3045}}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409530}}},"rhs":{"const":{"val":"5s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":25294,"rank":5}}},"rhs":{"const":{"val":"8s"}}}},{"spellIsReady":{"spellId":{"spellId":409433}}}]}},"castSpell":{"spellId":{"spellId":468388}}}}, + {"action":{"condition":{"auraIsActive":{"auraId":{"spellId":468388}}},"castSpell":{"spellId":{"spellId":409433}}}}, + {"action":{"castSpell":{"spellId":{"spellId":409433}}}}, + {"action":{"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"castSpell":{"spellId":{"spellId":409530}}}}, + {"action":{"castSpell":{"spellId":{"spellId":25294,"rank":5}}}}, + {"action":{"castSpell":{"spellId":{"spellId":14287,"rank":8}}}}, + {"action":{"condition":{"isExecutePhase":{"threshold":"E20"}},"castSpell":{"spellId":{"itemId":12662}}}} + ] +} \ No newline at end of file diff --git a/ui/hunter/apls/p6_weave.apl.json b/ui/hunter/apls/p6_weave.apl.json new file mode 100644 index 0000000000..377964ee05 --- /dev/null +++ b/ui/hunter/apls/p6_weave.apl.json @@ -0,0 +1,52 @@ +{ + "type": "TypeAPL", + "prepullActions": [ + {"action":{"castSpell":{"spellId":{"spellId":469145}}},"doAtValue":{"const":{"val":"-5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":20904,"rank":6}}},"doAtValue":{"const":{"val":"-3.5s"}},"hide":true}, + {"action":{"activateAura":{"auraId":{"spellId":415413}}},"doAtValue":{"const":{"val":"-3.5s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":20572}}},"doAtValue":{"const":{"val":"-1.95s"}}}, + {"action":{"castSpell":{"spellId":{"itemId":215162}}},"doAtValue":{"const":{"val":"-0.45s"}},"hide":true}, + {"action":{"castSpell":{"spellId":{"spellId":26297}}},"doAtValue":{"const":{"val":"-0.45s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":1213366}}},"doAtValue":{"const":{"val":"-0.45s"}}}, + {"action":{"castSpell":{"spellId":{"spellId":3045}}},"doAtValue":{"const":{"val":"-0.45s"}},"hide":true}, + {"action":{"castSpell":{"spellId":{"spellId":25295,"rank":9}}},"doAtValue":{"const":{"val":"-0.45s"}}} + ], + "priorityList": [ + {"action":{"condition":{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"55%"}}}},"castSpell":{"spellId":{"otherId":"OtherActionPotion"}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"65%"}}}}]}},"castSpell":{"spellId":{"itemId":12662}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"50%"}}}},"cancelAura":{"auraId":{"spellId":415423}}}}, + {"action":{"condition":{"cmp":{"op":"OpLe","lhs":{"dotRemainingTime":{"spellId":{"spellId":25295,"rank":9}}},"rhs":{"const":{"val":"5s"}}}},"castSpell":{"spellId":{"spellId":409433}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"autoTimeToNext":{"autoType":"Melee"}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"gcdTimeToReady":{}},"rhs":{"const":{"val":"0.6s"}}}}]}},"move":{"rangeFromTarget":{"const":{"val":"12"}}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"autoTimeToNext":{"autoType":"Ranged"}},"rhs":{"const":{"val":"1s"}}}},{"or":{"vals":[{"cmp":{"op":"OpGt","lhs":{"gcdTimeToReady":{}},"rhs":{"const":{"val":"1s"}}}},{"and":{"vals":[{"gcdIsReady":{}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"1s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":25294,"rank":5}}},"rhs":{"const":{"val":"1s"}}}}]}}]}},{"or":{"vals":[{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14266,"tag":3,"rank":8}}},"rhs":{"const":{"val":"2s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14266,"tag":3,"rank":8}}},"rhs":{"const":{"val":"3s"}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14266,"tag":3,"rank":8}}},"rhs":{"autoTimeToNext":{"autoType":"Melee"}}}}]}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":468388}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":3045}}}}}]}},"move":{"rangeFromTarget":{"const":{"val":"5"}}}}}, + {"hide":true,"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"autoTimeToNext":{"autoType":"Ranged"}},"rhs":{"const":{"val":"0.8s"}}}},{"or":{"vals":[{"cmp":{"op":"OpGt","lhs":{"gcdTimeToReady":{}},"rhs":{"const":{"val":"1s"}}}},{"and":{"vals":[{"gcdIsReady":{}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"1s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":25294,"rank":5}}},"rhs":{"const":{"val":"1s"}}}}]}}]}},{"or":{"vals":[{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14266,"tag":3,"rank":8}}},"rhs":{"const":{"val":"2s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14266,"tag":3,"rank":8}}},"rhs":{"const":{"val":"3s"}}}},{"cmp":{"op":"OpLt","lhs":{"spellTimeToReady":{"spellId":{"spellId":14266,"tag":3,"rank":8}}},"rhs":{"autoTimeToNext":{"autoType":"Melee"}}}}]}},{"auraIsActive":{"auraId":{"spellId":3045}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":468388}}}}}]}},"move":{"rangeFromTarget":{"const":{"val":"6"}}}}}, + {"hide":true,"action":{"condition":{"and":{"vals":[{"not":{"val":{"spellCanCast":{"spellId":{"spellId":14287,"rank":8}}}}},{"not":{"val":{"spellCanCast":{"spellId":{"spellId":14268,"rank":3}}}}},{"gcdIsReady":{}},{"auraIsActive":{"auraId":{"spellId":3045}}}]}},"move":{"rangeFromTarget":{"const":{"val":"5"}}}}}, + {"action":{"autocastOtherCooldowns":{}}}, + {"hide":true,"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"19s"}}}},"castSpell":{"spellId":{"spellId":468388}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"3s"}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":409530}}},"rhs":{"const":{"val":"3s"}}}}]}},"castSpell":{"spellId":{"spellId":468388}}}}, + {"hide":true,"action":{"condition":{"and":{"vals":[{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":458482}}}}}]}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"4s"}}}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"currentTime":{}},"rhs":{"const":{"val":"4s"}}}},"castSpell":{"spellId":{"itemId":215162}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":3045}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}}]}},"cancelAura":{"auraId":{"itemId":215162}}}}, + {"hide":true,"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"auraNumStacks":{"auraId":{"spellId":1213366}}},"rhs":{"const":{"val":"6"}}}},"castSpell":{"spellId":{"spellId":3045}}}}, + {"hide":true,"action":{"condition":{"and":{"vals":[{"or":{"vals":[{"auraIsActive":{"auraId":{"spellId":3045}}},{"isExecutePhase":{"threshold":"E20"}}]}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}},{"spellCanCast":{"spellId":{"spellId":14287,"rank":8}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"and":{"vals":[{"or":{"vals":[{"auraIsActive":{"auraId":{"spellId":3045}}},{"isExecutePhase":{"threshold":"E20"}}]}},{"spellCanCast":{"spellId":{"spellId":14287,"rank":8}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"hide":true,"action":{"condition":{"and":{"vals":[{"or":{"vals":[{"auraIsActive":{"auraId":{"spellId":3045}}},{"isExecutePhase":{"threshold":"E20"}}]}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":458482}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409530}}}}},{"spellCanCast":{"spellId":{"spellId":14268,"rank":3}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"and":{"vals":[{"or":{"vals":[{"auraIsActive":{"auraId":{"spellId":3045}}},{"isExecutePhase":{"threshold":"E20"}}]}},{"spellCanCast":{"spellId":{"spellId":14268,"rank":3}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":458482}}}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"and":{"vals":[{"spellCanCast":{"spellId":{"spellId":25294,"rank":5}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"and":{"vals":[{"spellCanCast":{"spellId":{"spellId":25294,"rank":5}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}}]}},"castSpell":{"spellId":{"spellId":25294,"rank":5}}}}, + {"action":{"condition":{"and":{"vals":[{"spellCanCast":{"spellId":{"spellId":409433}}}]}},"castSpell":{"spellId":{"spellId":409433}}}}, + {"action":{"castSpell":{"spellId":{"spellId":14266,"tag":3,"rank":8}}}}, + {"action":{"castSpell":{"spellId":{"spellId":458482}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":468388}}}}},{"cmp":{"op":"OpGe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"8s"}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":3045}}}}}]}},"castSpell":{"spellId":{"spellId":409530}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":468388}}}}},{"cmp":{"op":"OpGe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"8s"}}}},{"cmp":{"op":"OpLe","lhs":{"spellTimeToReady":{"spellId":{"spellId":409433}}},"rhs":{"const":{"val":"3"}}}},{"auraIsActive":{"auraId":{"spellId":3045}}}]}},"castSpell":{"spellId":{"spellId":409530}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415413}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":468388}}}}},{"cmp":{"op":"OpLe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"8s"}}}}]}},"castSpell":{"spellId":{"spellId":409535}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"1s"}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409535}}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":3045}}}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGe","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"1s"}}}},{"not":{"val":{"spellIsReady":{"spellId":{"spellId":409433}}}}},{"auraIsActive":{"auraId":{"spellId":3045}}}]}},"castSpell":{"spellId":{"spellId":409593}}}}, + {"action":{"condition":{"cmp":{"op":"OpLt","lhs":{"currentManaPercent":{}},"rhs":{"const":{"val":"5%"}}}},"castSpell":{"spellId":{"spellId":415423}}}}, + {"action":{"castSpell":{"spellId":{"spellId":14287,"rank":8}}}}, + {"action":{"condition":{"and":{"vals":[{"not":{"val":{"auraIsActive":{"auraId":{"spellId":415423}}}}},{"not":{"val":{"auraIsActive":{"auraId":{"spellId":469145}}}}}]}},"castSpell":{"spellId":{"spellId":469145}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409535}}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"castSpell":{"spellId":{"spellId":20572}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409535}}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"castSpell":{"spellId":{"itemId":233985}}}}, + {"action":{"condition":{"and":{"vals":[{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":409535}}},"rhs":{"const":{"val":"1.5s"}}}},{"cmp":{"op":"OpGt","lhs":{"spellTimeToReady":{"spellId":{"spellId":458482}}},"rhs":{"const":{"val":"1.5s"}}}}]}},"castSpell":{"spellId":{"spellId":14268,"rank":3}}}} + ] +} \ No newline at end of file diff --git a/ui/hunter/gear_sets/p6_melee_bm.gear.json b/ui/hunter/gear_sets/p6_melee_bm.gear.json new file mode 100644 index 0000000000..ee28f8e084 --- /dev/null +++ b/ui/hunter/gear_sets/p6_melee_bm.gear.json @@ -0,0 +1,21 @@ +{ + "items": [ + {"id":233666,"enchant":7617,"rune":415405}, + {"id":231320}, + {"id":233668,"enchant":2606}, + {"id":233420,"enchant":849,"rune":440529}, + {"id":233664,"enchant":1891,"rune":409368}, + {"id":231063,"enchant":7656,"rune":415358}, + {"id":232116,"enchant":2564,"rune":458393}, + {"id":232112,"rune":415352}, + {"id":231067,"enchant":7617,"rune":415320}, + {"id":233665,"enchant":1887,"rune":409687}, + {"id":233600,"rune":442891}, + {"id":233422,"rune":442813}, + {"id":233627}, + {"id":230282}, + {"id":233585,"enchant":1900}, + {"id":233421,"enchant":1900}, + {"id":221450,"enchant":7657} + ] +} \ No newline at end of file diff --git a/ui/hunter/gear_sets/p6_melee_sv.gear.json b/ui/hunter/gear_sets/p6_melee_sv.gear.json new file mode 100644 index 0000000000..1606b690c1 --- /dev/null +++ b/ui/hunter/gear_sets/p6_melee_sv.gear.json @@ -0,0 +1,21 @@ +{ + "items": [ + {"id":233666,"enchant":7635,"rune":415405}, + {"id":231803}, + {"id":233668,"enchant":2606}, + {"id":233420,"enchant":849,"rune":440533}, + {"id":233664,"enchant":1891,"rune":409368}, + {"id":231063,"enchant":7656,"rune":415358}, + {"id":231069,"enchant":2564,"rune":458393}, + {"id":231065,"rune":415352}, + {"id":231067,"enchant":7635,"rune":415320}, + {"id":233665,"enchant":1887,"rune":458479}, + {"id":233600,"rune":442894}, + {"id":233422,"rune":442891}, + {"id":234462}, + {"id":233627}, + {"id":233640,"enchant":1900}, + {}, + {"id":221450,"enchant":7657} + ] + } \ No newline at end of file diff --git a/ui/hunter/gear_sets/p6_ranged_draconic.gear.json b/ui/hunter/gear_sets/p6_ranged_draconic.gear.json new file mode 100644 index 0000000000..c848966842 --- /dev/null +++ b/ui/hunter/gear_sets/p6_ranged_draconic.gear.json @@ -0,0 +1,21 @@ +{ + "items": [ + {"id":231059,"enchant":7617,"rune":415413}, + {"id":231803}, + {"id":231057,"enchant":2606}, + {"id":234802,"enchant":849,"rune":440529}, + {"id":231062,"enchant":1891,"rune":415370}, + {"id":231055,"enchant":7656,"rune":428717}, + {"id":233613,"enchant":2564,"rune":409433}, + {"id":231056,"rune":409504}, + {"id":233408,"enchant":7617,"rune":415399}, + {"id":231061,"enchant":1887,"rune":409541}, + {"id":234202,"rune":442894}, + {"id":233638,"rune":442891}, + {"id":231288}, + {"id":233627}, + {"id":233586,"enchant":2646}, + {}, + {"id":233605,"enchant":7657} + ] +} \ No newline at end of file diff --git a/ui/hunter/gear_sets/p6_ranged_killshot.gear.json b/ui/hunter/gear_sets/p6_ranged_killshot.gear.json new file mode 100644 index 0000000000..e52d126040 --- /dev/null +++ b/ui/hunter/gear_sets/p6_ranged_killshot.gear.json @@ -0,0 +1,21 @@ +{ + "items": [ + {"id":233410,"enchant":7617,"rune":415405}, + {"id":231803}, + {"id":233409,"enchant":2606}, + {"id":234802,"enchant":849,"rune":440529}, + {"id":233411,"enchant":1891,"rune":415370}, + {"id":234065,"enchant":7656,"rune":428717}, + {"id":233613,"enchant":2564,"rune":409433}, + {"id":234110,"rune":409504}, + {"id":233408,"enchant":7617,"rune":409593}, + {"id":234069,"enchant":1887,"rune":409541}, + {"id":234202,"rune":442894}, + {"id":233638,"rune":442891}, + {"id":231288}, + {"id":233627}, + {"id":233586,"enchant":2646}, + {}, + {"id":233605,"enchant":7657} + ] +} \ No newline at end of file diff --git a/ui/hunter/gear_sets/p6_weave.gear.json b/ui/hunter/gear_sets/p6_weave.gear.json new file mode 100644 index 0000000000..c169ebea25 --- /dev/null +++ b/ui/hunter/gear_sets/p6_weave.gear.json @@ -0,0 +1,21 @@ +{ + "items": [ + {"id":233410,"enchant":7635,"rune":415405}, + {"id":231803}, + {"id":233409,"enchant":2606}, + {"id":234802,"enchant":849,"rune":440529}, + {"id":231071,"enchant":1891,"rune":415370}, + {"id":231063,"enchant":7656,"rune":428717}, + {"id":231069,"enchant":2564,"rune":409433}, + {"id":231065,"rune":409504}, + {"id":233408,"enchant":7635,"rune":409593}, + {"id":233407,"enchant":1887,"rune":458479}, + {"id":234202,"rune":442892}, + {"id":233638,"rune":442891}, + {"id":233627}, + {"id":231288}, + {"id":233586,"enchant":2646}, + {}, + {"id":233605,"enchant":7657} + ] +} \ No newline at end of file From f283731cb2493d09435fc060613e5f2538e870cf Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 21:05:12 -0500 Subject: [PATCH 03/14] add more thorns effects, display total thorns damage, fix timeworn EP calculation --- proto/common.proto | 11 +- sim/common/sod/enchant_effects.go | 30 +++-- sim/common/sod/item_effects/phase_6.go | 138 +++++++++++++++++++++- sim/common/sod/item_effects/phase_7.go | 48 ++++++++ sim/core/buffs.go | 2 + sim/core/character.go | 10 +- sim/core/stats/stats.go | 2 + ui/core/components/character_stats.tsx | 104 ++++++++-------- ui/core/components/stat_weights_action.ts | 3 +- ui/core/constants/other.ts | 6 +- ui/core/individual_sim_ui.tsx | 4 +- ui/core/player.ts | 6 +- ui/core/proto_utils/names.ts | 5 +- ui/elemental_shaman/sim.ts | 6 +- ui/enhancement_shaman/sim.ts | 26 ++-- ui/feral_druid/sim.ts | 4 +- ui/retribution_paladin/sim.ts | 2 +- ui/tank_warrior/sim.ts | 5 +- 18 files changed, 314 insertions(+), 98 deletions(-) create mode 100644 sim/common/sod/item_effects/phase_7.go diff --git a/proto/common.proto b/proto/common.proto index fe09e49ae0..d5eb57e3c6 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -127,15 +127,15 @@ enum Stat { // between the UI and backend. // // It's also OK to include things here which aren't in the PseudoStats struct. -// NextIndex: 32 +// NextIndex: 33 enum PseudoStat { PseudoStatMainHandDps = 0; PseudoStatOffHandDps = 1; PseudoStatRangedDps = 2; PseudoStatBlockValueMultiplier = 3; - PseudoStatDodge = 4; - PseudoStatParry = 5; - BonusPhysicalDamage = 31; + PseudoStatDodge = 4 [deprecated = true]; + PseudoStatParry = 5 [deprecated = true]; + PseudoStatBonusPhysicalDamage = 31; // Melee Weapon Skill PseudoStatUnarmedSkill = 6; @@ -170,7 +170,8 @@ enum PseudoStat { PseudoStatBlockValuePerStrength = 29; // Special Pseudostats - TimewornBonus = 30; + PseudoStatTimewornBonus = 30; + PseudoStatThornsDamage = 32; } message UnitStats { diff --git a/sim/common/sod/enchant_effects.go b/sim/common/sod/enchant_effects.go index 7bf520de57..9290b7d1cc 100644 --- a/sim/common/sod/enchant_effects.go +++ b/sim/common/sod/enchant_effects.go @@ -88,9 +88,21 @@ func init() { }) // Sharpened Chitin Armor Kit + // Permanently cause an item worn on the chest, legs, hands or feet to cause 20 Nature damage to the attacker when struck in combat. + // Only usable on items level 45 and above. core.NewEnchantEffect(7649, func(agent core.Agent) { character := agent.GetCharacter() - actionID := core.ActionID{SpellID: 1213833} + actionID := core.ActionID{ItemID: 233803} + + damage := 20.0 + numEnchants := 0 + for _, item := range character.Equipment { + if item.Enchant.EffectID == 7649 { + numEnchants++ + } + } + + character.PseudoStats.ThornsDamage += damage * float64(numEnchants) procSpell := character.RegisterSpell(core.SpellConfig{ ActionID: actionID, @@ -102,22 +114,20 @@ func init() { ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 20, spell.OutcomeMagicHit) + spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMagicHit) }, }) - character.GetOrRegisterAura(core.Aura{ - Label: "Thorns +20", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - aura.Activate(sim) - }, + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Thorns +20", OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - procSpell.Cast(sim, spell.Unit) + for i := 0; i < numEnchants; i++ { + procSpell.Cast(sim, spell.Unit) + } } }, - }) + })) }) // Obsidian Scope diff --git a/sim/common/sod/item_effects/phase_6.go b/sim/common/sod/item_effects/phase_6.go index 43e699d3f5..3a4c4307a2 100644 --- a/sim/common/sod/item_effects/phase_6.go +++ b/sim/common/sod/item_effects/phase_6.go @@ -12,9 +12,13 @@ import ( ) const ( - LeggingsOfImmersion = 233505 - RingOfSwarmingThought = 233507 - RobesOfTheBattleguard = 233575 + RazorspikeBattleplate = 233492 + LeggingsOfImmersion = 233505 + RingOfSwarmingThought = 233507 + RobesOfTheBattleguard = 233575 + RazorspikeShoulderplates = 233793 + RazorspikeHeadcage = 233795 + LodestoneOfRetaliation = 233992 // Obsidian Weapons ObsidianChampion = 233490 @@ -167,6 +171,41 @@ func init() { ObsidianEdgedAura(ObsidianStormhammer, agent) }) + /////////////////////////////////////////////////////////////////////////// + // Trinkets + /////////////////////////////////////////////////////////////////////////// + + // https://www.wowhead.com/classic/item=233992/lodestone-of-retaliation + // When struck in combat inflicts 80 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(LodestoneOfRetaliation, func(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 80 + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: LodestoneOfRetaliation}, + SpellSchool: core.SpellSchoolNature, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 2, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 80, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Damage Shield Dmg +80 (Lodestone of Retaliation)", + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) + }) + /////////////////////////////////////////////////////////////////////////// // Rings /////////////////////////////////////////////////////////////////////////// @@ -291,6 +330,99 @@ func init() { }) }) + // https://www.wowhead.com/classic/item=233492/razorspike-battleplate + // When struck in combat inflicts 100 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(RazorspikeBattleplate, func(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 100 + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: RazorspikeBattleplate}, + SpellSchool: core.SpellSchoolNature, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 2, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 100, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Damage Shield Dmg +100 (Razorspike Battleplate)", + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) + }) + + // https://www.wowhead.com/classic/item=233795/razorspike-headcage + // When struck in combat inflicts 100 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(RazorspikeHeadcage, func(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 100 + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: RazorspikeHeadcage}, + SpellSchool: core.SpellSchoolNature, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 2, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 100, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Damage Shield Dmg +100 (Razorspike Headcage)", + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) + }) + + // https://www.wowhead.com/classic/item=233793/razorspike-shoulderplates + // When struck in combat inflicts 80 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(RazorspikeShoulderplates, func(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 80 + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: RazorspikeShoulderplates}, + SpellSchool: core.SpellSchoolNature, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 2, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 80, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Damage Shield Dmg +80 (Razorspike Shoulderplates)", + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) + }) + // https://www.wowhead.com/classic/item=233575/robes-of-the-battleguard // Your damaging non-periodic spells increase your spell damage by 20 for 15 sec. // If the target is player controlled, gain 120 spell penetration for 15 sec instead. diff --git a/sim/common/sod/item_effects/phase_7.go b/sim/common/sod/item_effects/phase_7.go new file mode 100644 index 0000000000..c6844e2953 --- /dev/null +++ b/sim/common/sod/item_effects/phase_7.go @@ -0,0 +1,48 @@ +package item_effects + +import "github.com/wowsims/sod/sim/core" + +const ( + BulwarkOfIre = 235868 +) + +func init() { + core.AddEffectsToTest = false + + /////////////////////////////////////////////////////////////////////////// + // Other + /////////////////////////////////////////////////////////////////////////// + + // https://www.wowhead.com/classic/item=235868/bulwark-of-ire + // Deal 100 Shadow damage to melee attackers. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(BulwarkOfIre, func(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 100 + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: BulwarkOfIre}, + SpellSchool: core.SpellSchoolShadow, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 2, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 100, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Splintered Shieldd", + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) + }) + + core.AddEffectsToTest = true +} diff --git a/sim/core/buffs.go b/sim/core/buffs.go index c70d57e862..3730cbf10e 100644 --- a/sim/core/buffs.go +++ b/sim/core/buffs.go @@ -1179,6 +1179,8 @@ func ThornsAura(character *Character, points int32) *Aura { actionID := ActionID{SpellID: spellID} damage := float64(baseDamage) * (1 + 0.25*float64(points)) + character.PseudoStats.ThornsDamage += damage + procSpell := character.RegisterSpell(SpellConfig{ ActionID: actionID, SpellSchool: SpellSchoolNature, diff --git a/sim/core/character.go b/sim/core/character.go index 7104d13bbf..7eb8828e60 100644 --- a/sim/core/character.go +++ b/sim/core/character.go @@ -189,8 +189,8 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character character.PseudoStats.BowsSkill += ps[proto.PseudoStat_PseudoStatBowsSkill] character.PseudoStats.CrossbowsSkill += ps[proto.PseudoStat_PseudoStatCrossbowsSkill] character.PseudoStats.GunsSkill += ps[proto.PseudoStat_PseudoStatGunsSkill] - character.PseudoStats.BonusPhysicalDamage += ps[proto.PseudoStat_BonusPhysicalDamage] - character.PseudoStats.TimewornBonus += int32(ps[proto.PseudoStat_TimewornBonus]) + character.PseudoStats.BonusPhysicalDamage += ps[proto.PseudoStat_PseudoStatBonusPhysicalDamage] + character.PseudoStats.TimewornBonus += int32(ps[proto.PseudoStat_PseudoStatTimewornBonus]) } } @@ -271,7 +271,6 @@ func (character *Character) applyEquipment() { character.PseudoStats.BonusPhysicalDamage += item.BonusPhysicalDamage } - } func (character *Character) addUniversalStatDependencies() { @@ -612,6 +611,7 @@ func (character *Character) GetPseudoStatsProto() []float64 { proto.PseudoStat_PseudoStatMainHandDps: character.AutoAttacks.MH().DPS(), proto.PseudoStat_PseudoStatOffHandDps: character.AutoAttacks.OH().DPS(), proto.PseudoStat_PseudoStatRangedDps: character.AutoAttacks.Ranged().DPS(), + proto.PseudoStat_PseudoStatBonusPhysicalDamage: float64(character.PseudoStats.BonusPhysicalDamage), proto.PseudoStat_PseudoStatBlockValueMultiplier: character.PseudoStats.BlockValueMultiplier, proto.PseudoStat_PseudoStatAxesSkill: float64(character.PseudoStats.AxesSkill), proto.PseudoStat_PseudoStatSwordsSkill: float64(character.PseudoStats.SwordsSkill), @@ -640,8 +640,8 @@ func (character *Character) GetPseudoStatsProto() []float64 { proto.PseudoStat_PseudoStatRangedSpeedMultiplier: float64(character.PseudoStats.RangedSpeedMultiplier), proto.PseudoStat_PseudoStatBlockValuePerStrength: float64(character.PseudoStats.BlockValuePerStrength), - proto.PseudoStat_TimewornBonus: float64(character.PseudoStats.TimewornBonus), - proto.PseudoStat_BonusPhysicalDamage: float64(character.PseudoStats.BonusPhysicalDamage), + proto.PseudoStat_PseudoStatTimewornBonus: float64(character.PseudoStats.TimewornBonus), + proto.PseudoStat_PseudoStatThornsDamage: character.PseudoStats.ThornsDamage, } } diff --git a/sim/core/stats/stats.go b/sim/core/stats/stats.go index 2ff1a7cdef..8019d84b65 100644 --- a/sim/core/stats/stats.go +++ b/sim/core/stats/stats.go @@ -471,6 +471,8 @@ type PseudoStats struct { // Tracks the number of Timeworn items equipped for Bronze Signet bonuses TimewornBonus int32 + // Tracks the cumulative amount of Thorns damage from various effects purely for UI display + ThornsDamage float64 /////////////////////////////////////////////////// // Effects that apply when this unit is the target. diff --git a/ui/core/components/character_stats.tsx b/ui/core/components/character_stats.tsx index 798abbdaa9..b64affa537 100644 --- a/ui/core/components/character_stats.tsx +++ b/ui/core/components/character_stats.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx'; import tippy from 'tippy.js'; import { ref } from 'tsx-vanilla'; @@ -10,82 +11,87 @@ import { Component } from './component.js'; import { NumberPicker } from './number_picker'; export type StatMods = { talents?: Stats; buffs?: Stats }; +export type DisplayStat = { + stat: UnitStat, + notEditable?: boolean +} -const statGroups = new Map>([ +const statGroups = new Map>([ [ 'Primary', [ - UnitStat.fromStat(Stat.StatHealth), - UnitStat.fromStat(Stat.StatMana), + {stat: UnitStat.fromStat(Stat.StatHealth)}, + {stat: UnitStat.fromStat(Stat.StatMana)}, ], ], [ 'Attributes', [ - UnitStat.fromStat(Stat.StatStrength), - UnitStat.fromStat(Stat.StatAgility), - UnitStat.fromStat(Stat.StatStamina), - UnitStat.fromStat(Stat.StatIntellect), - UnitStat.fromStat(Stat.StatSpirit), + {stat: UnitStat.fromStat(Stat.StatStrength)}, + {stat: UnitStat.fromStat(Stat.StatAgility)}, + {stat: UnitStat.fromStat(Stat.StatStamina)}, + {stat: UnitStat.fromStat(Stat.StatIntellect)}, + {stat: UnitStat.fromStat(Stat.StatSpirit)}, ] ], [ 'Physical', [ - UnitStat.fromStat(Stat.StatAttackPower), - UnitStat.fromStat(Stat.StatFeralAttackPower), - UnitStat.fromStat(Stat.StatRangedAttackPower), - UnitStat.fromStat(Stat.StatMeleeHit), - UnitStat.fromStat(Stat.StatExpertise), - UnitStat.fromStat(Stat.StatMeleeCrit), - UnitStat.fromStat(Stat.StatMeleeHaste), - UnitStat.fromPseudoStat(PseudoStat.BonusPhysicalDamage), + {stat: UnitStat.fromStat(Stat.StatAttackPower)}, + {stat: UnitStat.fromStat(Stat.StatFeralAttackPower)}, + {stat: UnitStat.fromStat(Stat.StatRangedAttackPower)}, + {stat: UnitStat.fromStat(Stat.StatMeleeHit)}, + {stat: UnitStat.fromStat(Stat.StatExpertise)}, + {stat: UnitStat.fromStat(Stat.StatMeleeCrit)}, + {stat: UnitStat.fromStat(Stat.StatMeleeHaste)}, + {stat: UnitStat.fromPseudoStat(PseudoStat.PseudoStatBonusPhysicalDamage)}, ] ], [ 'Spell', [ - UnitStat.fromStat(Stat.StatSpellPower), - UnitStat.fromStat(Stat.StatSpellDamage), - UnitStat.fromStat(Stat.StatArcanePower), - UnitStat.fromStat(Stat.StatFirePower), - UnitStat.fromStat(Stat.StatFrostPower), - UnitStat.fromStat(Stat.StatHolyPower), - UnitStat.fromStat(Stat.StatNaturePower), - UnitStat.fromStat(Stat.StatShadowPower), - UnitStat.fromStat(Stat.StatSpellHit), - UnitStat.fromStat(Stat.StatSpellCrit), - UnitStat.fromStat(Stat.StatSpellHaste), - UnitStat.fromStat(Stat.StatSpellPenetration), - UnitStat.fromStat(Stat.StatMP5), + {stat: UnitStat.fromStat(Stat.StatSpellPower)}, + {stat: UnitStat.fromStat(Stat.StatSpellDamage)}, + {stat: UnitStat.fromStat(Stat.StatArcanePower)}, + {stat: UnitStat.fromStat(Stat.StatFirePower)}, + {stat: UnitStat.fromStat(Stat.StatFrostPower)}, + {stat: UnitStat.fromStat(Stat.StatHolyPower)}, + {stat: UnitStat.fromStat(Stat.StatNaturePower)}, + {stat: UnitStat.fromStat(Stat.StatShadowPower)}, + {stat: UnitStat.fromStat(Stat.StatSpellHit)}, + {stat: UnitStat.fromStat(Stat.StatSpellCrit)}, + {stat: UnitStat.fromStat(Stat.StatSpellHaste)}, + {stat: UnitStat.fromStat(Stat.StatSpellPenetration)}, + {stat: UnitStat.fromStat(Stat.StatMP5)}, ] ], [ 'Defense', [ - UnitStat.fromStat(Stat.StatArmor), - UnitStat.fromStat(Stat.StatBonusArmor), - UnitStat.fromStat(Stat.StatDefense), - UnitStat.fromStat(Stat.StatDodge), - UnitStat.fromStat(Stat.StatParry), - UnitStat.fromStat(Stat.StatBlock), - UnitStat.fromStat(Stat.StatBlockValue), + {stat: UnitStat.fromStat(Stat.StatArmor)}, + {stat: UnitStat.fromStat(Stat.StatBonusArmor)}, + {stat: UnitStat.fromStat(Stat.StatDefense)}, + {stat: UnitStat.fromStat(Stat.StatDodge)}, + {stat: UnitStat.fromStat(Stat.StatParry)}, + {stat: UnitStat.fromStat(Stat.StatBlock)}, + {stat: UnitStat.fromStat(Stat.StatBlockValue)}, ] ], [ 'Resistance', [ - UnitStat.fromStat(Stat.StatArcaneResistance), - UnitStat.fromStat(Stat.StatFireResistance), - UnitStat.fromStat(Stat.StatFrostResistance), - UnitStat.fromStat(Stat.StatNatureResistance), - UnitStat.fromStat(Stat.StatShadowResistance), + {stat: UnitStat.fromStat(Stat.StatArcaneResistance)}, + {stat: UnitStat.fromStat(Stat.StatFireResistance)}, + {stat: UnitStat.fromStat(Stat.StatFrostResistance)}, + {stat: UnitStat.fromStat(Stat.StatNatureResistance)}, + {stat: UnitStat.fromStat(Stat.StatShadowResistance)}, ] ], [ 'Misc', [ - UnitStat.fromPseudoStat(PseudoStat.TimewornBonus), + {stat: UnitStat.fromPseudoStat(PseudoStat.PseudoStatThornsDamage), notEditable: true}, + {stat: UnitStat.fromPseudoStat(PseudoStat.PseudoStatTimewornBonus), notEditable: true}, ] ], ]) @@ -121,12 +127,13 @@ export class CharacterStats extends Component { this.valueElems = []; statGroups.forEach((groupedStats, _) => { - const filteredStats = groupedStats.filter(stat => displayStats.find(displayStat => displayStat.equals(stat))); + const filteredStats = groupedStats.filter(stat => displayStats.find(displayStat => displayStat.equals(stat.stat))); if (!filteredStats.length) return; const body = ; - filteredStats.forEach(stat => { + filteredStats.forEach(displayStat => { + const { stat } = displayStat this.stats.push(stat); const statName = stat.getName(player.getClass()); @@ -134,7 +141,7 @@ export class CharacterStats extends Component { const row = ( {statName} - {this.bonusStatsLink(stat)} + {this.bonusStatsLink(displayStat)} ); body.appendChild(row); @@ -487,8 +494,9 @@ export class CharacterStats extends Component { return debuffStats; } - private bonusStatsLink(stat: UnitStat): HTMLElement { - const statName = stat.getName(this.player.getClass()); + private bonusStatsLink(displayStat: DisplayStat): HTMLElement { + const { stat, notEditable } = displayStat; + const statName = displayStat.stat.getName(this.player.getClass()); const linkRef = ref(); const iconRef = ref(); @@ -496,7 +504,7 @@ export class CharacterStats extends Component { diff --git a/ui/core/components/stat_weights_action.ts b/ui/core/components/stat_weights_action.ts index 41d9162640..3f28a4746f 100644 --- a/ui/core/components/stat_weights_action.ts +++ b/ui/core/components/stat_weights_action.ts @@ -753,7 +753,8 @@ class EpWeightsMenu extends BaseModal { PseudoStat.PseudoStatMainHandDps, PseudoStat.PseudoStatOffHandDps, PseudoStat.PseudoStatRangedDps, - PseudoStat.BonusPhysicalDamage + PseudoStat.PseudoStatBonusPhysicalDamage, + PseudoStat.PseudoStatTimewornBonus, ].includes(stat.getPseudoStat()); } }); diff --git a/ui/core/constants/other.ts b/ui/core/constants/other.ts index 2c0343f992..e5058b29f3 100644 --- a/ui/core/constants/other.ts +++ b/ui/core/constants/other.ts @@ -40,7 +40,7 @@ export const GLOBAL_DISPLAY_STATS = [ ]; export const GLOBAL_DISPLAY_PSEUDO_STATS = [ - PseudoStat.TimewornBonus, + PseudoStat.PseudoStatTimewornBonus, ]; export const GLOBAL_EP_STATS = [ @@ -49,6 +49,10 @@ export const GLOBAL_EP_STATS = [ Stat.StatNatureResistance, ]; +export const GLOBAL_EP_PSEUDOSTATS = [ + PseudoStat.PseudoStatTimewornBonus, +]; + export enum SortDirection { ASC, DESC, diff --git a/ui/core/individual_sim_ui.tsx b/ui/core/individual_sim_ui.tsx index 708207a80e..217566c731 100644 --- a/ui/core/individual_sim_ui.tsx +++ b/ui/core/individual_sim_ui.tsx @@ -15,7 +15,7 @@ import * as InputHelpers from './components/input_helpers'; import { addRaidSimAction, RaidSimResultsManager } from './components/raid_sim_action'; import { SavedDataConfig } from './components/saved_data_manager'; import { addStatWeightsAction } from './components/stat_weights_action'; -import { GLOBAL_DISPLAY_PSEUDO_STATS, GLOBAL_DISPLAY_STATS, GLOBAL_EP_STATS, LEVEL_THRESHOLDS } from './constants/other'; +import { GLOBAL_DISPLAY_PSEUDO_STATS, GLOBAL_DISPLAY_STATS, GLOBAL_EP_PSEUDOSTATS, GLOBAL_EP_STATS, LEVEL_THRESHOLDS } from './constants/other'; import * as Tooltips from './constants/tooltips'; import { simLaunchStatuses } from './launched_sims'; import { Player, PlayerConfig, registerSpecConfig as registerPlayerConfig } from './player'; @@ -330,7 +330,7 @@ export abstract class IndividualSimUI extends SimUI { private addSidebarComponents() { this.raidSimResultsManager = addRaidSimAction(this); - addStatWeightsAction(this, this.individualConfig.epStats.concat(GLOBAL_EP_STATS), this.individualConfig.epPseudoStats, this.individualConfig.epReferenceStat); + addStatWeightsAction(this, this.individualConfig.epStats.concat(GLOBAL_EP_STATS), GLOBAL_EP_PSEUDOSTATS.concat(this.individualConfig.epPseudoStats ?? []), this.individualConfig.epReferenceStat); const displayStats: UnitStat[] = []; diff --git a/ui/core/player.ts b/ui/core/player.ts index 3e44c3eead..43779fb882 100644 --- a/ui/core/player.ts +++ b/ui/core/player.ts @@ -1077,7 +1077,7 @@ export class Player { } // Add pseudo stats that should be included in item EP. - itemStats = itemStats.addPseudoStat(PseudoStat.BonusPhysicalDamage, item.bonusPhysicalDamage); + itemStats = itemStats.addPseudoStat(PseudoStat.PseudoStatBonusPhysicalDamage, item.bonusPhysicalDamage); // For random suffix items, use the suffix option with the highest EP for the purposes of ranking items in the picker. let maxSuffixEP = 0; @@ -1094,6 +1094,10 @@ export class Player { ep -= 0.01; } + if (item.timeworn) { + ep += this.epWeights.getPseudoStat(PseudoStat.PseudoStatTimewornBonus) + } + this.itemEPCache[slot].set(item.id, ep); return ep; } diff --git a/ui/core/proto_utils/names.ts b/ui/core/proto_utils/names.ts index 1f5e5fd31b..c5e35110ae 100644 --- a/ui/core/proto_utils/names.ts +++ b/ui/core/proto_utils/names.ts @@ -207,8 +207,9 @@ export const pseudoStatNames: Map = new Map([ [PseudoStat.PseudoStatOffHandDps, 'Off Hand DPS'], [PseudoStat.PseudoStatRangedDps, 'Ranged DPS'], [PseudoStat.PseudoStatBlockValueMultiplier, 'Block Value Multiplier'], - [PseudoStat.TimewornBonus, 'Timeworn Pieces'], - [PseudoStat.BonusPhysicalDamage, 'Bonus Weapon Damage'], + [PseudoStat.PseudoStatTimewornBonus, 'Timeworn Pieces'], + [PseudoStat.PseudoStatBonusPhysicalDamage, 'Bonus Weapon Damage'], + [PseudoStat.PseudoStatThornsDamage, 'Total Thorns Damage'], ]); export function getClassStatName(stat: Stat, playerClass: Class): string { diff --git a/ui/elemental_shaman/sim.ts b/ui/elemental_shaman/sim.ts index bedf2402d9..247068438d 100644 --- a/ui/elemental_shaman/sim.ts +++ b/ui/elemental_shaman/sim.ts @@ -30,9 +30,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { Stat.StatSpellHaste, Stat.StatMP5, ], - epPseudoStats: [ - PseudoStat.TimewornBonus, - ], + epPseudoStats: [], // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. epReferenceStat: Stat.StatSpellPower, // Which stats to display in the Character Stats section, at the bottom of the left-hand sidebar. @@ -72,6 +70,8 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { [Stat.StatStrength]: 0.01, [Stat.StatAttackPower]: 0.01, [Stat.StatFireResistance]: 0.5, + }, { + [PseudoStat.PseudoStatTimewornBonus]: 35.74, }), // Default consumes settings. consumes: Presets.DefaultConsumes, diff --git a/ui/enhancement_shaman/sim.ts b/ui/enhancement_shaman/sim.ts index 3e96971f2f..52bd59d147 100644 --- a/ui/enhancement_shaman/sim.ts +++ b/ui/enhancement_shaman/sim.ts @@ -75,26 +75,28 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecEnhancementShaman, { // Default EP weights for sorting gear in the gear picker. epWeights: Stats.fromMap( { - [Stat.StatIntellect]: 0.02, - [Stat.StatAgility]: 1.12, - [Stat.StatStrength]: 2.29, - [Stat.StatSpellPower]: 1.15, - [Stat.StatSpellDamage]: 1.15, - [Stat.StatFirePower]: 0.63, - [Stat.StatNaturePower]: 0.48, + [Stat.StatIntellect]: 2.01, + [Stat.StatAgility]: 1.88, + [Stat.StatStrength]: 2.66, + [Stat.StatSpellPower]: 0.68, + [Stat.StatSpellDamage]: 0.68, + [Stat.StatFirePower]: 0.31, + [Stat.StatNaturePower]: 0.37, [Stat.StatSpellHit]: 0.03, //default EP assumes cap - [Stat.StatSpellCrit]: 1.94, + [Stat.StatSpellCrit]: 7.54, [Stat.StatSpellHaste]: 2.97, [Stat.StatMP5]: 0.01, [Stat.StatAttackPower]: 1.0, - [Stat.StatMeleeHit]: 9.62, - [Stat.StatMeleeCrit]: 14.8, + [Stat.StatMeleeHit]: 22.53, + [Stat.StatMeleeCrit]: 25.21, + [Stat.StatExpertise]: 50.82, [Stat.StatFireResistance]: 0.5, }, { - [PseudoStat.PseudoStatMainHandDps]: 8.15, - [PseudoStat.PseudoStatOffHandDps]: 5.81, + [PseudoStat.PseudoStatMainHandDps]: 4.86, + [PseudoStat.PseudoStatOffHandDps]: 5.55, [PseudoStat.PseudoStatMeleeSpeedMultiplier]: 5.81, + [PseudoStat.PseudoStatTimewornBonus]: 9.00, }, ), // Default consumes settings. diff --git a/ui/feral_druid/sim.ts b/ui/feral_druid/sim.ts index 1323f299de..5b611c27f6 100644 --- a/ui/feral_druid/sim.ts +++ b/ui/feral_druid/sim.ts @@ -39,7 +39,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralDruid, { Stat.StatMP5, ], epPseudoStats: [ - PseudoStat.BonusPhysicalDamage, + PseudoStat.PseudoStatBonusPhysicalDamage, ], // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. epReferenceStat: Stat.StatAttackPower, @@ -62,7 +62,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecFeralDruid, { Stat.StatMP5, ], displayPseudoStats: [ - PseudoStat.BonusPhysicalDamage, + PseudoStat.PseudoStatBonusPhysicalDamage, ], defaults: { diff --git a/ui/retribution_paladin/sim.ts b/ui/retribution_paladin/sim.ts index d56c533c02..25bd1f3ac3 100644 --- a/ui/retribution_paladin/sim.ts +++ b/ui/retribution_paladin/sim.ts @@ -57,7 +57,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecRetributionPaladin, { epPseudoStats: [ PseudoStat.PseudoStatMainHandDps, PseudoStat.PseudoStatMeleeSpeedMultiplier, - PseudoStat.TimewornBonus + PseudoStat.PseudoStatTimewornBonus, ], // Reference stat against which to calculate EP. I think all classes use either spell power or attack power. epReferenceStat: Stat.StatAttackPower, diff --git a/ui/tank_warrior/sim.ts b/ui/tank_warrior/sim.ts index 35a670d52f..f5b90ce0bf 100644 --- a/ui/tank_warrior/sim.ts +++ b/ui/tank_warrior/sim.ts @@ -64,8 +64,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankWarrior, { // Resistances Stat.StatShadowResistance, ], - displayPseudoStats: [], - + displayPseudoStats: [ + PseudoStat.PseudoStatThornsDamage, + ], defaults: { // Default equipped gear. gear: Presets.DefaultGear.gear, From edcf066e0806e03e5c298627721c3d4417959d50 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 21:07:16 -0500 Subject: [PATCH 04/14] add thorns display pseudo to all tanks --- ui/protection_paladin/sim.ts | 4 +++- ui/tank_rogue/sim.ts | 4 +++- ui/tank_warlock/sim.ts | 6 ++++-- ui/tank_warrior/sim.ts | 1 + ui/warden_shaman/sim.ts | 4 +++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ui/protection_paladin/sim.ts b/ui/protection_paladin/sim.ts index 5b290bc34f..df814d5f82 100644 --- a/ui/protection_paladin/sim.ts +++ b/ui/protection_paladin/sim.ts @@ -103,7 +103,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecProtectionPaladin, { Stat.StatShadowResistance, Stat.StatArcaneResistance, ], - displayPseudoStats: [], + displayPseudoStats: [ + PseudoStat.PseudoStatThornsDamage, + ], defaults: { // Default equipped gear. diff --git a/ui/tank_rogue/sim.ts b/ui/tank_rogue/sim.ts index c5bf49f82f..7c22e08296 100644 --- a/ui/tank_rogue/sim.ts +++ b/ui/tank_rogue/sim.ts @@ -63,7 +63,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankRogue, { // Resistances Stat.StatShadowResistance, ], - displayPseudoStats: [], + displayPseudoStats: [ + PseudoStat.PseudoStatThornsDamage, + ], defaults: { // Default equipped gear. diff --git a/ui/tank_warlock/sim.ts b/ui/tank_warlock/sim.ts index d5ee5eb5fb..925a053d4e 100644 --- a/ui/tank_warlock/sim.ts +++ b/ui/tank_warlock/sim.ts @@ -5,7 +5,7 @@ import * as OtherInputs from '../core/components/other_inputs.js'; import { Phase } from '../core/constants/other.js'; import { IndividualSimUI, registerSpecConfig } from '../core/individual_sim_ui.js'; import { Player } from '../core/player.js'; -import { Class, Faction, ItemSlot, PartyBuffs, Race, Spec, Stat } from '../core/proto/common.js'; +import { Class, Faction, ItemSlot, PartyBuffs, PseudoStat, Race, Spec, Stat } from '../core/proto/common.js'; import { WarlockRune } from '../core/proto/warlock.js'; import { Stats } from '../core/proto_utils/stats.js'; import { getSpecIcon } from '../core/proto_utils/utils.js'; @@ -82,7 +82,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankWarlock, { // Resistances Stat.StatShadowResistance, ], - displayPseudoStats: [], + displayPseudoStats: [ + PseudoStat.PseudoStatThornsDamage, + ], defaults: { // Default equipped gear. diff --git a/ui/tank_warrior/sim.ts b/ui/tank_warrior/sim.ts index f5b90ce0bf..e2cee35fee 100644 --- a/ui/tank_warrior/sim.ts +++ b/ui/tank_warrior/sim.ts @@ -67,6 +67,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecTankWarrior, { displayPseudoStats: [ PseudoStat.PseudoStatThornsDamage, ], + defaults: { // Default equipped gear. gear: Presets.DefaultGear.gear, diff --git a/ui/warden_shaman/sim.ts b/ui/warden_shaman/sim.ts index 8328459f0d..2ff7dddcee 100644 --- a/ui/warden_shaman/sim.ts +++ b/ui/warden_shaman/sim.ts @@ -83,7 +83,9 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecWardenShaman, { // Resistances Stat.StatShadowResistance, ], - displayPseudoStats: [], + displayPseudoStats: [ + PseudoStat.PseudoStatThornsDamage, + ], defaults: { race: Race.RaceTroll, From 3a750eec34174ac1cce41050b863ad9f0b394c1e Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 21:18:13 -0500 Subject: [PATCH 05/14] add naglering effect --- sim/common/sod/item_effects/phase_7.go | 2 +- sim/common/vanilla/item_effects.go | 31 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/sim/common/sod/item_effects/phase_7.go b/sim/common/sod/item_effects/phase_7.go index c6844e2953..2cb7ed9038 100644 --- a/sim/common/sod/item_effects/phase_7.go +++ b/sim/common/sod/item_effects/phase_7.go @@ -35,7 +35,7 @@ func init() { }) core.MakePermanent(character.GetOrRegisterAura(core.Aura{ - Label: "Splintered Shieldd", + Label: "Splintered Shield", OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { procSpell.Cast(sim, spell.Unit) diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index 3b47f51a02..ccc3bc7f09 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -34,6 +34,7 @@ const ( Firebreather = 10797 VilerendSlicer = 11603 HookfangShanker = 11635 + Naglering = 11669 LinkensSwordOfMastery = 11902 SearingNeedle = 12531 PipsSkinner = 12709 @@ -2898,6 +2899,36 @@ func init() { BlazefuryTriggerAura(character, 7712, core.SpellSchoolFire, 2) }) + // https://www.wowhead.com/classic/item=11669/naglering + // When struck in combat inflicts 3 Arcane damage to the attacker. + core.NewItemEffect(Naglering, func(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 3 + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: Naglering}, + SpellSchool: core.SpellSchoolArcane, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 3, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Thorns (Naglering)", + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) + }) + // https://www.wowhead.com/classic/item=1168/skullflame-shield // Equip: When struck in combat has a 3% chance of stealing 35 life from target enemy. (Proc chance: 3%) // Equip: When struck in combat has a 1% chance of dealing 75 to 125 Fire damage to all targets around you. (Proc chance: 1%) From b56c84ca2b3412e9999544b7c04f86f58de352f7 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 21:38:37 -0500 Subject: [PATCH 06/14] add naglering, drillborer disk, razorbramble items --- sim/common/sod/item_effects/phase_6.go | 155 +++++++++---------------- sim/common/vanilla/item_effects.go | 65 +++++++---- 2 files changed, 96 insertions(+), 124 deletions(-) diff --git a/sim/common/sod/item_effects/phase_6.go b/sim/common/sod/item_effects/phase_6.go index 3a4c4307a2..cf5251214f 100644 --- a/sim/common/sod/item_effects/phase_6.go +++ b/sim/common/sod/item_effects/phase_6.go @@ -1,6 +1,7 @@ package item_effects import ( + "fmt" "math" "time" @@ -18,6 +19,9 @@ const ( RobesOfTheBattleguard = 233575 RazorspikeShoulderplates = 233793 RazorspikeHeadcage = 233795 + RazorbrambleShoulderpads = 233804 + RazorbrambleCowl = 233808 + RazorbrambleLeathers = 233813 LodestoneOfRetaliation = 233992 // Obsidian Weapons @@ -179,31 +183,7 @@ func init() { // When struck in combat inflicts 80 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(LodestoneOfRetaliation, func(agent core.Agent) { - character := agent.GetCharacter() - character.PseudoStats.ThornsDamage += 80 - - procSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{ItemID: LodestoneOfRetaliation}, - SpellSchool: core.SpellSchoolNature, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, - - DamageMultiplier: 1, - ThreatMultiplier: 2, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 80, spell.OutcomeMagicHit) - }, - }) - - core.MakePermanent(character.GetOrRegisterAura(core.Aura{ - Label: "Damage Shield Dmg +80 (Lodestone of Retaliation)", - OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - procSpell.Cast(sim, spell.Unit) - } - }, - })) + thornsNatureDamageEffect(agent, LodestoneOfRetaliation, "Lodestone of Retaliation", 80) }) /////////////////////////////////////////////////////////////////////////// @@ -330,97 +310,46 @@ func init() { }) }) - // https://www.wowhead.com/classic/item=233492/razorspike-battleplate + // https://www.wowhead.com/classic/item=233808/razorbramble-cowl // When struck in combat inflicts 100 Nature damage to the attacker. // Causes twice as much threat as damage dealt. - core.NewItemEffect(RazorspikeBattleplate, func(agent core.Agent) { - character := agent.GetCharacter() - character.PseudoStats.ThornsDamage += 100 - - procSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{ItemID: RazorspikeBattleplate}, - SpellSchool: core.SpellSchoolNature, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + core.NewItemEffect(RazorbrambleCowl, func(agent core.Agent) { + thornsNatureDamageEffect(agent, RazorbrambleCowl, "Razorbramble Cowl", 100) + }) - DamageMultiplier: 1, - ThreatMultiplier: 2, + // https://www.wowhead.com/classic/item=233813/razorbramble-leathers + // When struck in combat inflicts 100 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(RazorbrambleLeathers, func(agent core.Agent) { + thornsNatureDamageEffect(agent, RazorbrambleLeathers, "Razorbramble Leathers", 100) + }) - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 100, spell.OutcomeMagicHit) - }, - }) + // https://www.wowhead.com/classic/item=233804/razorbramble-shoulderpads + // When struck in combat inflicts 80 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(RazorbrambleShoulderpads, func(agent core.Agent) { + thornsNatureDamageEffect(agent, RazorbrambleShoulderpads, "Razorbramble Shoulderpads", 80) + }) - core.MakePermanent(character.GetOrRegisterAura(core.Aura{ - Label: "Damage Shield Dmg +100 (Razorspike Battleplate)", - OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - procSpell.Cast(sim, spell.Unit) - } - }, - })) + // https://www.wowhead.com/classic/item=233492/razorspike-battleplate + // When struck in combat inflicts 100 Nature damage to the attacker. + // Causes twice as much threat as damage dealt. + core.NewItemEffect(RazorspikeBattleplate, func(agent core.Agent) { + thornsNatureDamageEffect(agent, RazorspikeBattleplate, "Razorspike Battleplate", 100) }) // https://www.wowhead.com/classic/item=233795/razorspike-headcage // When struck in combat inflicts 100 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorspikeHeadcage, func(agent core.Agent) { - character := agent.GetCharacter() - character.PseudoStats.ThornsDamage += 100 - - procSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{ItemID: RazorspikeHeadcage}, - SpellSchool: core.SpellSchoolNature, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, - - DamageMultiplier: 1, - ThreatMultiplier: 2, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 100, spell.OutcomeMagicHit) - }, - }) - - core.MakePermanent(character.GetOrRegisterAura(core.Aura{ - Label: "Damage Shield Dmg +100 (Razorspike Headcage)", - OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - procSpell.Cast(sim, spell.Unit) - } - }, - })) + thornsNatureDamageEffect(agent, RazorspikeHeadcage, "Razorspike Headcage", 100) }) // https://www.wowhead.com/classic/item=233793/razorspike-shoulderplates // When struck in combat inflicts 80 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorspikeShoulderplates, func(agent core.Agent) { - character := agent.GetCharacter() - character.PseudoStats.ThornsDamage += 80 - - procSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{ItemID: RazorspikeShoulderplates}, - SpellSchool: core.SpellSchoolNature, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, - - DamageMultiplier: 1, - ThreatMultiplier: 2, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 80, spell.OutcomeMagicHit) - }, - }) - - core.MakePermanent(character.GetOrRegisterAura(core.Aura{ - Label: "Damage Shield Dmg +80 (Razorspike Shoulderplates)", - OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - procSpell.Cast(sim, spell.Unit) - } - }, - })) + thornsNatureDamageEffect(agent, RazorspikeShoulderplates, "Razorspike Shoulderplates", 80) }) // https://www.wowhead.com/classic/item=233575/robes-of-the-battleguard @@ -668,3 +597,31 @@ func TimewornStrikeAura(agent core.Agent) { }, }) } + +func thornsNatureDamageEffect(agent core.Agent, itemID int32, itemName string, damage float64) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += damage + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: itemID}, + SpellSchool: core.SpellSchoolNature, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 2, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: fmt.Sprintf("Damage Shield Dmg +%f (%s)", damage, itemName), + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) +} diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index ccc3bc7f09..d778a0f1cd 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -80,6 +80,7 @@ const ( FiendishMachete = 228056 // 18310 RefinedArcaniteChampion = 228125 TalismanOfEphemeralPower = 228255 // 18820 + DrillborerDisk = 228266 // 17066 GutgoreRipper = 228267 // 17071 Shadowstrike = 228272 // 17074 Thunderstrike = 228273 // 17223 @@ -106,6 +107,7 @@ const ( SeepingWillow = 228666 // 12969 DraconicInfusedEmblem = 228678 // 22268 QuelSerrar = 228679 // 18348 + DrillborerDiskMolten = 228702 HandOfJustice = 228722 // 11815 Felstriker = 228757 // 12590 GutgoreRipperMolten = 229372 @@ -2899,34 +2901,19 @@ func init() { BlazefuryTriggerAura(character, 7712, core.SpellSchoolFire, 2) }) + // https://www.wowhead.com/classic/item=228266/drillborer-disk + // When struck in combat inflicts 3 Arcane damage to the attacker. + core.NewItemEffect(DrillborerDisk, func(agent core.Agent) { + thornsArcaneDamageEffect(agent, DrillborerDisk, "Drillborer Disk", 3) + }) + core.NewItemEffect(DrillborerDiskMolten, func(agent core.Agent) { + thornsArcaneDamageEffect(agent, DrillborerDiskMolten, "Drillborer Disk (Molten)", 3) + }) + // https://www.wowhead.com/classic/item=11669/naglering // When struck in combat inflicts 3 Arcane damage to the attacker. core.NewItemEffect(Naglering, func(agent core.Agent) { - character := agent.GetCharacter() - character.PseudoStats.ThornsDamage += 3 - - procSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{ItemID: Naglering}, - SpellSchool: core.SpellSchoolArcane, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, - - DamageMultiplier: 1, - ThreatMultiplier: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 3, spell.OutcomeMagicHit) - }, - }) - - core.MakePermanent(character.GetOrRegisterAura(core.Aura{ - Label: "Thorns (Naglering)", - OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - procSpell.Cast(sim, spell.Unit) - } - }, - })) + thornsArcaneDamageEffect(agent, Naglering, "Naglering", 3) }) // https://www.wowhead.com/classic/item=1168/skullflame-shield @@ -3148,6 +3135,34 @@ func dreadbladeOfTheDestructorEffect(character *core.Character) *core.Spell { }) } +func thornsArcaneDamageEffect(agent core.Agent, itemID int32, itemName string, damage float64) { + character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += damage + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: itemID}, + SpellSchool: core.SpellSchoolArcane, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMagicHit) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: fmt.Sprintf("Thorns (%s)", itemName), + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + procSpell.Cast(sim, spell.Unit) + } + }, + })) +} + func eskhandarsRightClawAura(character *core.Character) *core.Aura { return character.GetOrRegisterAura(core.Aura{ Label: "Eskhandar's Rage", From 409153e045d87c79a649f5210308d404ab94c90e Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 21:42:30 -0500 Subject: [PATCH 07/14] add thorns damage from essence of the pure flame --- sim/common/sod/item_effects/phase_6.go | 14 +++++++------- sim/common/vanilla/item_effects.go | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/sim/common/sod/item_effects/phase_6.go b/sim/common/sod/item_effects/phase_6.go index cf5251214f..8f834e79c4 100644 --- a/sim/common/sod/item_effects/phase_6.go +++ b/sim/common/sod/item_effects/phase_6.go @@ -180,7 +180,7 @@ func init() { /////////////////////////////////////////////////////////////////////////// // https://www.wowhead.com/classic/item=233992/lodestone-of-retaliation - // When struck in combat inflicts 80 Nature damage to the attacker. + // Equip: When struck in combat inflicts 80 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(LodestoneOfRetaliation, func(agent core.Agent) { thornsNatureDamageEffect(agent, LodestoneOfRetaliation, "Lodestone of Retaliation", 80) @@ -311,42 +311,42 @@ func init() { }) // https://www.wowhead.com/classic/item=233808/razorbramble-cowl - // When struck in combat inflicts 100 Nature damage to the attacker. + // Equip: When struck in combat inflicts 100 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorbrambleCowl, func(agent core.Agent) { thornsNatureDamageEffect(agent, RazorbrambleCowl, "Razorbramble Cowl", 100) }) // https://www.wowhead.com/classic/item=233813/razorbramble-leathers - // When struck in combat inflicts 100 Nature damage to the attacker. + // Equip: When struck in combat inflicts 100 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorbrambleLeathers, func(agent core.Agent) { thornsNatureDamageEffect(agent, RazorbrambleLeathers, "Razorbramble Leathers", 100) }) // https://www.wowhead.com/classic/item=233804/razorbramble-shoulderpads - // When struck in combat inflicts 80 Nature damage to the attacker. + // Equip: When struck in combat inflicts 80 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorbrambleShoulderpads, func(agent core.Agent) { thornsNatureDamageEffect(agent, RazorbrambleShoulderpads, "Razorbramble Shoulderpads", 80) }) // https://www.wowhead.com/classic/item=233492/razorspike-battleplate - // When struck in combat inflicts 100 Nature damage to the attacker. + // Equip: When struck in combat inflicts 100 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorspikeBattleplate, func(agent core.Agent) { thornsNatureDamageEffect(agent, RazorspikeBattleplate, "Razorspike Battleplate", 100) }) // https://www.wowhead.com/classic/item=233795/razorspike-headcage - // When struck in combat inflicts 100 Nature damage to the attacker. + // Equip: When struck in combat inflicts 100 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorspikeHeadcage, func(agent core.Agent) { thornsNatureDamageEffect(agent, RazorspikeHeadcage, "Razorspike Headcage", 100) }) // https://www.wowhead.com/classic/item=233793/razorspike-shoulderplates - // When struck in combat inflicts 80 Nature damage to the attacker. + // Equip: When struck in combat inflicts 80 Nature damage to the attacker. // Causes twice as much threat as damage dealt. core.NewItemEffect(RazorspikeShoulderplates, func(agent core.Agent) { thornsNatureDamageEffect(agent, RazorspikeShoulderplates, "Razorspike Shoulderplates", 80) diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index d778a0f1cd..1230d32bb7 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -2347,13 +2347,14 @@ func init() { // Equip: When struck in combat inflicts 50 Fire damage to the attacker. core.NewItemEffect(EssenceOfThePureFlame, func(agent core.Agent) { character := agent.GetCharacter() + character.PseudoStats.ThornsDamage += 50 procSpell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 461694}, SpellSchool: core.SpellSchoolFire, DefenseType: core.DefenseTypeMagic, ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + Flags: core.SpellFlagBinary | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, DamageMultiplier: 1, ThreatMultiplier: 1, @@ -2902,7 +2903,7 @@ func init() { }) // https://www.wowhead.com/classic/item=228266/drillborer-disk - // When struck in combat inflicts 3 Arcane damage to the attacker. + // Equip: When struck in combat inflicts 3 Arcane damage to the attacker. core.NewItemEffect(DrillborerDisk, func(agent core.Agent) { thornsArcaneDamageEffect(agent, DrillborerDisk, "Drillborer Disk", 3) }) @@ -2911,7 +2912,7 @@ func init() { }) // https://www.wowhead.com/classic/item=11669/naglering - // When struck in combat inflicts 3 Arcane damage to the attacker. + // Equip: When struck in combat inflicts 3 Arcane damage to the attacker. core.NewItemEffect(Naglering, func(agent core.Agent) { thornsArcaneDamageEffect(agent, Naglering, "Naglering", 3) }) From 43088313c906c4d5c8b07b036efbb71ee5716298 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 21:46:38 -0500 Subject: [PATCH 08/14] add razor gauntlets --- sim/common/vanilla/item_effects.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index 1230d32bb7..7379aaed59 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -50,6 +50,7 @@ const ( ThrashBlade = 17705 SatyrsLash = 17752 MarkOfTheChosen = 17774 + RazorGauntlets = 18326 Nightfall = 19169 EbonHand = 19170 RuneOfTheDawn = 19812 @@ -2917,6 +2918,12 @@ func init() { thornsArcaneDamageEffect(agent, Naglering, "Naglering", 3) }) + // https://www.wowhead.com/classic/item=18326/razor-gauntlets + // Equip: When struck in combat inflicts 3 Arcane damage to the attacker. + core.NewItemEffect(RazorGauntlets, func(agent core.Agent) { + thornsArcaneDamageEffect(agent, RazorGauntlets, "Razor Gauntlets", 3) + }) + // https://www.wowhead.com/classic/item=1168/skullflame-shield // Equip: When struck in combat has a 3% chance of stealing 35 life from target enemy. (Proc chance: 3%) // Equip: When struck in combat has a 1% chance of dealing 75 to 125 Fire damage to all targets around you. (Proc chance: 1%) From 92ac0e0827517fece10953154295480aa8d7ab37 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 11 Jan 2025 23:05:13 -0500 Subject: [PATCH 09/14] add force reactive disk items --- sim/common/sod/item_effects/phase_6.go | 80 ++++++++++++++++++++++++++ sim/common/vanilla/item_effects.go | 35 +++++++++++ 2 files changed, 115 insertions(+) diff --git a/sim/common/sod/item_effects/phase_6.go b/sim/common/sod/item_effects/phase_6.go index 8f834e79c4..d46bb7d01e 100644 --- a/sim/common/sod/item_effects/phase_6.go +++ b/sim/common/sod/item_effects/phase_6.go @@ -22,6 +22,7 @@ const ( RazorbrambleShoulderpads = 233804 RazorbrambleCowl = 233808 RazorbrambleLeathers = 233813 + TunedForceReactiveDisk = 233988 LodestoneOfRetaliation = 233992 // Obsidian Weapons @@ -384,6 +385,85 @@ func init() { }) }) + // https://www.wowhead.com/classic/item=233988/tuned-force-reactive-disk + // Equip: When the shield blocks it releases an electrical charge that damages all nearby enemies. (1s cooldown) + // Use: Charge up the energy within the shield for 3 sec to deal 450 to 750 Nature damage to all nearby enemies. After use, the shield needs 10 sec to recharge. (2 Min Cooldown) + core.NewItemEffect(TunedForceReactiveDisk, func(agent core.Agent) { + character := agent.GetCharacter() + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: TunedForceReactiveDisk}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for _, aoeTarget := range sim.Encounter.TargetUnits { + spell.CalcAndDealDamage(sim, aoeTarget, 35, spell.OutcomeMagicHitAndCrit) + } + }, + }) + + procTriggerAura := core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Force Reactive Disk", + Callback: core.CallbackOnSpellHitTaken, + ProcMask: core.ProcMaskMelee, + Outcome: core.OutcomeBlock, + ICD: time.Second, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + procSpell.Cast(sim, spell.Unit) + }, + }) + + spell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 1213967}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + Cast: core.CastConfig{ + CD: core.Cooldown{ + Timer: character.NewTimer(), + Duration: time.Minute * 2, + }, + }, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + sim.AddPendingAction(&core.PendingAction{ + NextActionAt: sim.CurrentTime + time.Second*3, + OnAction: func(sim *core.Simulation) { + for _, aoeTarget := range sim.Encounter.TargetUnits { + spell.CalcAndDealDamage(sim, aoeTarget, sim.Roll(450, 750), spell.OutcomeMagicHitAndCrit) + } + + spell.CD.Use(sim) + + procTriggerAura.Deactivate(sim) + sim.AddPendingAction(&core.PendingAction{ + NextActionAt: sim.CurrentTime + time.Second*10, + OnAction: func(sim *core.Simulation) { + procTriggerAura.Activate(sim) + }, + }) + }, + }) + }, + }) + + character.AddMajorCooldown(core.MajorCooldown{ + Type: core.CooldownTypeDPS, + Spell: spell, + }) + }) + core.AddEffectsToTest = true } diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index 7379aaed59..6199978bf1 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -50,6 +50,7 @@ const ( ThrashBlade = 17705 SatyrsLash = 17752 MarkOfTheChosen = 17774 + ForceReactiveDisk = 18168 RazorGauntlets = 18326 Nightfall = 19169 EbonHand = 19170 @@ -2912,6 +2913,40 @@ func init() { thornsArcaneDamageEffect(agent, DrillborerDiskMolten, "Drillborer Disk (Molten)", 3) }) + // https://www.wowhead.com/classic/item=18168/force-reactive-disk + // Equip: When the shield blocks it releases an electrical charge that damages all nearby enemies. (1s cooldown) + core.NewItemEffect(ForceReactiveDisk, func(agent core.Agent) { + character := agent.GetCharacter() + + procSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: ForceReactiveDisk}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for _, aoeTarget := range sim.Encounter.TargetUnits { + spell.CalcAndDealDamage(sim, aoeTarget, 25, spell.OutcomeMagicHitAndCrit) + } + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Force Reactive Disk", + Callback: core.CallbackOnSpellHitTaken, + ProcMask: core.ProcMaskMelee, + Outcome: core.OutcomeBlock, + ICD: time.Second, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + procSpell.Cast(sim, spell.Unit) + }, + }) + }) + // https://www.wowhead.com/classic/item=11669/naglering // Equip: When struck in combat inflicts 3 Arcane damage to the attacker. core.NewItemEffect(Naglering, func(agent core.Agent) { From b3a08cb64280f4b28397338b9429fc31ebd3cfee Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sun, 12 Jan 2025 00:47:10 -0500 Subject: [PATCH 10/14] add in sanctified bonuses --- proto/common.proto | 8 ++- proto/ui.proto | 3 +- sim/common/sod/item_effects/phase_7.go | 88 +++++++++++++++++++++++++- sim/common/vanilla/item_effects.go | 5 ++ sim/core/character.go | 9 ++- sim/core/database.go | 4 +- sim/core/stats/stats.go | 3 + tools/database/wowhead_tooltips.go | 7 ++ ui/core/components/character_stats.tsx | 1 + ui/core/constants/other.ts | 2 + ui/core/player.ts | 4 ++ ui/core/proto_utils/names.ts | 1 + 12 files changed, 127 insertions(+), 8 deletions(-) diff --git a/proto/common.proto b/proto/common.proto index d5eb57e3c6..5f1f8e9912 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -127,7 +127,7 @@ enum Stat { // between the UI and backend. // // It's also OK to include things here which aren't in the PseudoStats struct. -// NextIndex: 33 +// NextIndex: 34 enum PseudoStat { PseudoStatMainHandDps = 0; PseudoStatOffHandDps = 1; @@ -136,6 +136,7 @@ enum PseudoStat { PseudoStatDodge = 4 [deprecated = true]; PseudoStatParry = 5 [deprecated = true]; PseudoStatBonusPhysicalDamage = 31; + PseudoStatThornsDamage = 32; // Melee Weapon Skill PseudoStatUnarmedSkill = 6; @@ -171,7 +172,7 @@ enum PseudoStat { // Special Pseudostats PseudoStatTimewornBonus = 30; - PseudoStatThornsDamage = 32; + PseudoStatSanctifiedBonus = 33; } message UnitStats { @@ -942,7 +943,7 @@ message SimDatabase { } // Contains only the Item info needed by the sim. -// NextIndex: 21 +// NextIndex: 22 message SimItem { int32 id = 1; int32 requires_level = 16; @@ -967,6 +968,7 @@ message SimItem { repeated double weapon_skills = 15; bool timeworn = 19; + bool sanctified = 21; } // Extra enum for describing which items are eligible for an enchant, when diff --git a/proto/ui.proto b/proto/ui.proto index da16891b81..14ac07dd6e 100644 --- a/proto/ui.proto +++ b/proto/ui.proto @@ -44,7 +44,7 @@ message UIFaction { // Contains all information about an Item needed by the UI. // Generally this will include everything needed by the sim, plus some // additional data for displaying / filtering. -// NextIndex: 32 +// NextIndex: 33 message UIItem { int32 id = 1; string name = 2; @@ -73,6 +73,7 @@ message UIItem { bool unique = 19; bool heroic = 20; bool timeworn = 30; + bool sanctified = 32; // Classes that are allowed to use the item. Empty indicates no special class restrictions. repeated Class class_allowlist = 21; diff --git a/sim/common/sod/item_effects/phase_7.go b/sim/common/sod/item_effects/phase_7.go index 2cb7ed9038..909388dbc0 100644 --- a/sim/common/sod/item_effects/phase_7.go +++ b/sim/common/sod/item_effects/phase_7.go @@ -1,14 +1,78 @@ package item_effects -import "github.com/wowsims/sod/sim/core" +import ( + "github.com/wowsims/sod/sim/core" + "github.com/wowsims/sod/sim/core/stats" +) const ( BulwarkOfIre = 235868 + + // Seals of the Dawn + SquiresSealOfTheDawnDamage = 236356 + KnightsSealOfTheDawnDamage = 236357 + TemplarsSealOfTheDawnDamage = 236358 + ChampionsSealOfTheDawnDamage = 236360 + VanguardsSealOfTheDawnDamage = 236361 + CrusadersSealOfTheDawnDamage = 236362 + CommandersSealOfTheDawnDamage = 236363 + HighlordsSSealOfTheDawnDamage = 236364 + + SquiresSealOfTheDawnHealing = 236383 + KnightsSealOfTheDawnHealing = 236382 + TemplarsSealOfTheDawnHealing = 236380 + ChampionsSealOfTheDawnHealing = 236379 + VanguardsSealOfTheDawnHealing = 236378 + CrusadersSealOfTheDawnHealing = 236376 + CommandersSealOfTheDawnHealing = 236375 + HighlordsSSealOfTheDawnHealing = 236374 + + SquiresSealOfTheDawnTanking = 236394 + KnightsSealOfTheDawnTanking = 236393 + TemplarsSealOfTheDawnTanking = 236392 + ChampionsSealOfTheDawnTanking = 236391 + VanguardsSealOfTheDawnTanking = 236390 + CrusadersSealOfTheDawnTanking = 236389 + CommandersSealOfTheDawnTanking = 236388 + HighlordsSSealOfTheDawnTanking = 236386 ) func init() { core.AddEffectsToTest = false + /////////////////////////////////////////////////////////////////////////// + // Trinkets + /////////////////////////////////////////////////////////////////////////// + // https://www.wowhead.com/classic/item=236356/squires-seal-of-the-dawn + core.NewItemEffect(SquiresSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(KnightsSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(TemplarsSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(ChampionsSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(VanguardsSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(CrusadersSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(CommandersSealOfTheDawnDamage, sanctifiedDamageEffect) + core.NewItemEffect(HighlordsSSealOfTheDawnDamage, sanctifiedDamageEffect) + + // https://www.wowhead.com/classic/item=236383/squires-seal-of-the-dawn + core.NewItemEffect(SquiresSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(KnightsSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(TemplarsSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(ChampionsSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(VanguardsSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(CrusadersSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(CommandersSealOfTheDawnHealing, sanctifiedHealingEffect) + core.NewItemEffect(HighlordsSSealOfTheDawnHealing, sanctifiedHealingEffect) + + // https://www.wowhead.com/classic/item=236394/squires-seal-of-the-dawn + core.NewItemEffect(SquiresSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(KnightsSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(TemplarsSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(ChampionsSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(VanguardsSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(CrusadersSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(CommandersSealOfTheDawnTanking, sanctifiedTankingEffect) + core.NewItemEffect(HighlordsSSealOfTheDawnTanking, sanctifiedTankingEffect) + /////////////////////////////////////////////////////////////////////////// // Other /////////////////////////////////////////////////////////////////////////// @@ -46,3 +110,25 @@ func init() { core.AddEffectsToTest = true } + +// Equip: Unlocks your potential while inside Naxxramas. +// Increasing your damage by 2% for each piece of Sanctified armor equipped. +func sanctifiedDamageEffect(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.DamageDealtMultiplier *= 1 + 0.02*float64(character.PseudoStats.SanctifiedBonus) +} + +// Equip: Unlocks your potential while inside Naxxramas. +// Increasing your healing and shielding by 2% for each piece of Sanctified armor equipped. +func sanctifiedHealingEffect(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.HealingDealtMultiplier *= 1 + 0.02*float64(character.PseudoStats.SanctifiedBonus) +} + +// Equip: Unlocks your potential while inside Naxxramas. +// Increasing your threat caused by 1% and health by 1 for each piece of Sanctified armor equipped. +func sanctifiedTankingEffect(agent core.Agent) { + character := agent.GetCharacter() + character.PseudoStats.ThreatMultiplier *= 1 + 0.01*float64(character.PseudoStats.SanctifiedBonus) + character.AddStat(stats.Health, 1*float64(character.PseudoStats.SanctifiedBonus)) +} diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index 6199978bf1..a2b6d0bb67 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -44,6 +44,7 @@ const ( SerpentSlicer = 13035 TheNeedler = 13060 SealOfTheDawn = 13209 + CloudkeeperLegplates = 14554 JoonhosMercy = 17054 Deathbringer = 17068 ViskagTheBloodletter = 17075 @@ -2904,6 +2905,10 @@ func init() { BlazefuryTriggerAura(character, 7712, core.SpellSchoolFire, 2) }) + // https://www.wowhead.com/classic/item=14554/cloudkeeper-legplates + // Use: Increases Attack Power by 100 for 30 sec. (15 Min Cooldown) + core.NewSimpleStatOffensiveTrinketEffect(CloudkeeperLegplates, stats.Stats{stats.AttackPower: 100, stats.RangedAttackPower: 100}, time.Second*30, time.Minute*15) + // https://www.wowhead.com/classic/item=228266/drillborer-disk // Equip: When struck in combat inflicts 3 Arcane damage to the attacker. core.NewItemEffect(DrillborerDisk, func(agent core.Agent) { diff --git a/sim/core/character.go b/sim/core/character.go index 7eb8828e60..cd6d94b67b 100644 --- a/sim/core/character.go +++ b/sim/core/character.go @@ -191,6 +191,7 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character character.PseudoStats.GunsSkill += ps[proto.PseudoStat_PseudoStatGunsSkill] character.PseudoStats.BonusPhysicalDamage += ps[proto.PseudoStat_PseudoStatBonusPhysicalDamage] character.PseudoStats.TimewornBonus += int32(ps[proto.PseudoStat_PseudoStatTimewornBonus]) + character.PseudoStats.SanctifiedBonus += int32(ps[proto.PseudoStat_PseudoStatSanctifiedBonus]) } } @@ -267,6 +268,8 @@ func (character *Character) applyEquipment() { for _, item := range character.Equipment { if item.Timeworn { character.PseudoStats.TimewornBonus += 1 + } else if item.Sanctified { + character.PseudoStats.SanctifiedBonus += 1 } character.PseudoStats.BonusPhysicalDamage += item.BonusPhysicalDamage @@ -613,6 +616,8 @@ func (character *Character) GetPseudoStatsProto() []float64 { proto.PseudoStat_PseudoStatRangedDps: character.AutoAttacks.Ranged().DPS(), proto.PseudoStat_PseudoStatBonusPhysicalDamage: float64(character.PseudoStats.BonusPhysicalDamage), proto.PseudoStat_PseudoStatBlockValueMultiplier: character.PseudoStats.BlockValueMultiplier, + proto.PseudoStat_PseudoStatThornsDamage: character.PseudoStats.ThornsDamage, + proto.PseudoStat_PseudoStatAxesSkill: float64(character.PseudoStats.AxesSkill), proto.PseudoStat_PseudoStatSwordsSkill: float64(character.PseudoStats.SwordsSkill), proto.PseudoStat_PseudoStatMacesSkill: float64(character.PseudoStats.MacesSkill), @@ -640,8 +645,8 @@ func (character *Character) GetPseudoStatsProto() []float64 { proto.PseudoStat_PseudoStatRangedSpeedMultiplier: float64(character.PseudoStats.RangedSpeedMultiplier), proto.PseudoStat_PseudoStatBlockValuePerStrength: float64(character.PseudoStats.BlockValuePerStrength), - proto.PseudoStat_PseudoStatTimewornBonus: float64(character.PseudoStats.TimewornBonus), - proto.PseudoStat_PseudoStatThornsDamage: character.PseudoStats.ThornsDamage, + proto.PseudoStat_PseudoStatTimewornBonus: float64(character.PseudoStats.TimewornBonus), + proto.PseudoStat_PseudoStatSanctifiedBonus: float64(character.PseudoStats.SanctifiedBonus), } } diff --git a/sim/core/database.go b/sim/core/database.go index 319af4020c..18f482baf7 100644 --- a/sim/core/database.go +++ b/sim/core/database.go @@ -66,7 +66,8 @@ type Item struct { SetID int32 // 0 if not part of a set. WeaponSkills stats.WeaponSkills - Timeworn bool + Timeworn bool + Sanctified bool // Modified for each instance of the item. RandomSuffix RandomSuffix @@ -97,6 +98,7 @@ func ItemFromProto(pData *proto.SimItem) Item { SetID: pData.SetId, WeaponSkills: stats.WeaponSkillsFloatArray(pData.WeaponSkills), Timeworn: pData.Timeworn, + Sanctified: pData.Sanctified, } } diff --git a/sim/core/stats/stats.go b/sim/core/stats/stats.go index 8019d84b65..d470a58947 100644 --- a/sim/core/stats/stats.go +++ b/sim/core/stats/stats.go @@ -471,6 +471,9 @@ type PseudoStats struct { // Tracks the number of Timeworn items equipped for Bronze Signet bonuses TimewornBonus int32 + // Tracks the number of Sanctified items equipped for Seal of the Dawn bonuses + SanctifiedBonus int32 + // Tracks the cumulative amount of Thorns damage from various effects purely for UI display ThornsDamage float64 diff --git a/tools/database/wowhead_tooltips.go b/tools/database/wowhead_tooltips.go index 5f677b3c9a..20ff9dc4f6 100644 --- a/tools/database/wowhead_tooltips.go +++ b/tools/database/wowhead_tooltips.go @@ -706,6 +706,7 @@ func (item WowheadItemResponse) ToItemProto() *proto.UIItem { Unique: item.GetUnique(), Heroic: item.IsHeroic(), Timeworn: item.IsTimeworn(), + Sanctified: item.IsSanctified(), RequiredProfession: item.GetRequiredProfession(), SetName: item.GetItemSetName(), @@ -766,6 +767,12 @@ func (item WowheadItemResponse) IsTimeworn() bool { return timewornRegexp.MatchString(item.Tooltip) } +var sanctifiedRegexp = regexp.MustCompile(`Sanctified<\/span>`) + +func (item WowheadItemResponse) IsSanctified() bool { + return sanctifiedRegexp.MatchString(item.Tooltip) +} + func (item WowheadItemResponse) GetRequiredProfession() proto.Profession { if alchemyRegex.MatchString(item.Tooltip) { return proto.Profession_Alchemy diff --git a/ui/core/components/character_stats.tsx b/ui/core/components/character_stats.tsx index b64affa537..65e2da1813 100644 --- a/ui/core/components/character_stats.tsx +++ b/ui/core/components/character_stats.tsx @@ -92,6 +92,7 @@ const statGroups = new Map>([ [ {stat: UnitStat.fromPseudoStat(PseudoStat.PseudoStatThornsDamage), notEditable: true}, {stat: UnitStat.fromPseudoStat(PseudoStat.PseudoStatTimewornBonus), notEditable: true}, + {stat: UnitStat.fromPseudoStat(PseudoStat.PseudoStatSanctifiedBonus), notEditable: true}, ] ], ]) diff --git a/ui/core/constants/other.ts b/ui/core/constants/other.ts index e5058b29f3..35ef280a58 100644 --- a/ui/core/constants/other.ts +++ b/ui/core/constants/other.ts @@ -41,6 +41,7 @@ export const GLOBAL_DISPLAY_STATS = [ export const GLOBAL_DISPLAY_PSEUDO_STATS = [ PseudoStat.PseudoStatTimewornBonus, + PseudoStat.PseudoStatSanctifiedBonus, ]; export const GLOBAL_EP_STATS = [ @@ -51,6 +52,7 @@ export const GLOBAL_EP_STATS = [ export const GLOBAL_EP_PSEUDOSTATS = [ PseudoStat.PseudoStatTimewornBonus, + PseudoStat.PseudoStatSanctifiedBonus, ]; export enum SortDirection { diff --git a/ui/core/player.ts b/ui/core/player.ts index 43779fb882..627dfa763a 100644 --- a/ui/core/player.ts +++ b/ui/core/player.ts @@ -1098,6 +1098,10 @@ export class Player { ep += this.epWeights.getPseudoStat(PseudoStat.PseudoStatTimewornBonus) } + if (item.sanctified) { + ep += this.epWeights.getPseudoStat(PseudoStat.PseudoStatSanctifiedBonus) + } + this.itemEPCache[slot].set(item.id, ep); return ep; } diff --git a/ui/core/proto_utils/names.ts b/ui/core/proto_utils/names.ts index c5e35110ae..20069e2c17 100644 --- a/ui/core/proto_utils/names.ts +++ b/ui/core/proto_utils/names.ts @@ -208,6 +208,7 @@ export const pseudoStatNames: Map = new Map([ [PseudoStat.PseudoStatRangedDps, 'Ranged DPS'], [PseudoStat.PseudoStatBlockValueMultiplier, 'Block Value Multiplier'], [PseudoStat.PseudoStatTimewornBonus, 'Timeworn Pieces'], + [PseudoStat.PseudoStatSanctifiedBonus, 'Sanctified Pieces'], [PseudoStat.PseudoStatBonusPhysicalDamage, 'Bonus Weapon Damage'], [PseudoStat.PseudoStatThornsDamage, 'Total Thorns Damage'], ]); From eb6adb31861b87032060ea60d2435171b84fe9ce Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sun, 12 Jan 2025 11:36:18 -0500 Subject: [PATCH 11/14] skullflame shield scales with spell power --- sim/common/vanilla/item_effects.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index a2b6d0bb67..c187084f58 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -2973,11 +2973,16 @@ func init() { drainLifeActionID := core.ActionID{SpellID: 18817} healthMetrics := character.NewHealthMetrics(drainLifeActionID) drainLifeSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: drainLifeActionID, - SpellSchool: core.SpellSchoolShadow, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskEmpty, + ActionID: drainLifeActionID, + SpellSchool: core.SpellSchoolShadow, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + DamageMultiplier: 1, + ThreatMultiplier: 1, + BonusCoefficient: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { result := spell.CalcAndDealDamage(sim, target, 35, spell.OutcomeAlwaysHit) character.GainHealth(sim, result.Damage, healthMetrics) @@ -2985,12 +2990,15 @@ func init() { }) flamestrikeSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 18818}, - SpellSchool: core.SpellSchoolFire, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskEmpty, + ActionID: core.ActionID{SpellID: 18818}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + DamageMultiplier: 1, ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { for _, aoeTarget := range sim.Encounter.TargetUnits { spell.CalcAndDealDamage(sim, aoeTarget, sim.Roll(75, 125), spell.OutcomeMagicHit) From a7448d6bb221466819184c874f39d4989f456a38 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sun, 12 Jan 2025 11:54:21 -0500 Subject: [PATCH 12/14] add vampiric phase 6 gear --- sim/common/sod/item_effects/phase_6.go | 54 ++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/sim/common/sod/item_effects/phase_6.go b/sim/common/sod/item_effects/phase_6.go index d46bb7d01e..36bc1dcf24 100644 --- a/sim/common/sod/item_effects/phase_6.go +++ b/sim/common/sod/item_effects/phase_6.go @@ -22,6 +22,9 @@ const ( RazorbrambleShoulderpads = 233804 RazorbrambleCowl = 233808 RazorbrambleLeathers = 233813 + VampiricCowl = 233826 + VampiricShawl = 233833 + VampiricRobe = 233837 TunedForceReactiveDisk = 233988 LodestoneOfRetaliation = 233992 @@ -464,6 +467,24 @@ func init() { }) }) + // https://www.wowhead.com/classic/item=233826/vampiric-cowl + // Equip: When struck in combat has a 20% chance of stealing 50 life from target enemy. (Proc chance: 20%) + core.NewItemEffect(VampiricCowl, func(agent core.Agent) { + vampiricDrainLifeEffect(agent, VampiricCowl, "Vampiric Cowl", 50) + }) + + // https://www.wowhead.com/classic/item=233833/vampiric-shawl + // Equip: When struck in combat has a 20% chance of stealing 40 life from target enemy. (Proc chance: 20%) + core.NewItemEffect(VampiricShawl, func(agent core.Agent) { + vampiricDrainLifeEffect(agent, VampiricShawl, "Vampiric Shawl", 40) + }) + + // https://www.wowhead.com/classic/item=233837/vampiric-robe + // Equip: When struck in combat has a 20% chance of stealing 50 life from target enemy. (Proc chance: 20%) + core.NewItemEffect(VampiricRobe, func(agent core.Agent) { + vampiricDrainLifeEffect(agent, VampiricRobe, "Vampiric Robe", 50) + }) + core.AddEffectsToTest = true } @@ -705,3 +726,36 @@ func thornsNatureDamageEffect(agent core.Agent, itemID int32, itemName string, d }, })) } + +func vampiricDrainLifeEffect(agent core.Agent, itemID int32, itemName string, damage float64) { + character := agent.GetCharacter() + + actionID := core.ActionID{ItemID: itemID} + healthMetrics := character.NewHealthMetrics(actionID) + drainLifeSpell := character.RegisterSpell(core.SpellConfig{ + ActionID: actionID, + SpellSchool: core.SpellSchoolShadow, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + result := spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeAlwaysHit) + character.GainHealth(sim, result.Damage, healthMetrics) + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: fmt.Sprintf("Drain Life Trigger (%s)", itemName), + Callback: core.CallbackOnSpellHitTaken, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + ProcChance: 0.20, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + drainLifeSpell.Cast(sim, spell.Unit) + }, + }) +} From 44f0c45793c2296bd0abeb60b3136d4432739044 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sun, 12 Jan 2025 12:36:36 -0500 Subject: [PATCH 13/14] run make items --- assets/database/db.bin | Bin 6448409 -> 6448412 bytes assets/database/db.json | 6 +++--- assets/database/leftover_db.bin | Bin 927580 -> 927580 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/database/db.bin b/assets/database/db.bin index 34e7a7ff1e3bf424741470cbde68720b3b9be031..67ff833567d33852bd2f08d77739a092349a9851 100644 GIT binary patch delta 285 zcmWN_H%`M~7=U5@Hob;WLWdAz(|ahUg-$|AFb31xz?M5?c%=v-Au%u@v9hrsA^msY z5}Y8)75Ii{dES4Y%ny-6VPH~1DP@#XK_yjGQ$sCv)DxnCFpV_PObZcOX``JEI_aXD z9(sw=M+}R8Y#as{WQbu#7-fucCYWT3X=aEs%N+A8;IhaP%dD_Uf;HCJV3RGj*#M}K94 i7o{U23vnr~#I?8)x8hFRYu9`D3;zS6_Gf+o delta 281 zcmWm2M@|A!9Khi`uy?UzL1knVv5S4|4J_a|_!PSf*BrscFCoUnl`c$Nx$*!c?+#wV z6A-T8-^I82K7XG5AL0px4?ks;Q$ZzFR8vDOb=1>9BTY0DpoLc2Xs3ftx(L!u554pe zqMrc<31cxt1e;+-h%!oyF~*r-k}0N{VU{>^%(K8EODwa3!zyd6v%w}?Y_r2Id+d`S z$pMEPaZHLdC%BvzyhKrbnPkxm`|`PxCo(2iIyjl81nS!k!ZS$S}pI}PA ds=VWBaV{=IR$PiJaV>7dt+*5S#&I8h1AlEIXCMFo diff --git a/assets/database/db.json b/assets/database/db.json index b450b79a23..ec569dde34 100644 --- a/assets/database/db.json +++ b/assets/database/db.json @@ -9536,7 +9536,7 @@ {"id":236219,"name":"Harbinger of Doom","icon":"inv_knife_1h_stratholme_d_03","type":13,"weaponType":2,"handType":2,"requiresLevel":60,"stats":[0,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponDamageMin":83,"weaponDamageMax":126,"weaponSpeed":1.6,"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":83,"phase":7,"quality":4,"unique":true}, {"id":236220,"name":"Necro-Knight's Garb","icon":"inv_chest_cloth_46","type":5,"armorType":1,"requiresLevel":60,"stats":[0,0,32,0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,448,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":85,"phase":7,"quality":4,"classAllowlist":[5,3,8]}, {"id":236221,"name":"Misplaced Servo Arm","icon":"inv_mace_28","type":13,"weaponType":4,"handType":2,"requiresLevel":60,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponDamageMin":128,"weaponDamageMax":238,"weaponSpeed":2.8,"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":83,"phase":7,"quality":4}, -{"id":236222,"name":"Ghoul Skin Tunic","icon":"inv_chest_leather_02","type":5,"armorType":2,"requiresLevel":60,"stats":[40,0,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,411,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":83,"phase":7,"quality":4}, +{"id":236222,"name":"Ghoul Skin Tunic","icon":"inv_chest_leather_02","type":5,"armorType":2,"requiresLevel":60,"stats":[40,0,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,411,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":83,"phase":7,"quality":4,"sanctified":true}, {"id":236223,"name":"Ring of the Eternal Flame","icon":"inv_jewelry_ring_48naxxramas","type":11,"requiresLevel":60,"stats":[0,0,0,10,0,0,0,34,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":83,"phase":7,"quality":4,"unique":true}, {"id":236224,"name":"Stygian Buckler","icon":"inv_armor_shield_naxxramas_d_02","type":13,"weaponType":7,"handType":3,"requiresLevel":60,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":83,"phase":7,"quality":4}, {"id":236225,"name":"Girdle of Elemental Fury","icon":"inv_belt_32","type":8,"armorType":3,"requiresLevel":60,"stats":[0,0,20,21,0,29,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,301,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"weaponSkills":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ilvl":85,"phase":7,"quality":4}, @@ -10933,8 +10933,8 @@ {"effectId":63,"spellId":13538,"name":"Enchant Chest - Lesser Absorption","type":5,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":66,"spellId":7457,"name":"Enchant Bracer - Minor Stamina","type":6,"stats":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":66,"spellId":7863,"name":"Enchant Boots - Minor Stamina","type":10,"stats":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, -{"effectId":241,"spellId":13503,"name":"Enchant Weapon - Lesser Striking","type":13,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":241,"spellId":7745,"name":"Enchant 2H Weapon - Minor Impact","type":13,"enchantType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, +{"effectId":241,"spellId":13503,"name":"Enchant Weapon - Lesser Striking","type":13,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":242,"spellId":7748,"name":"Enchant Chest - Lesser Health","type":5,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":243,"spellId":7766,"name":"Enchant Bracer - Minor Spirit","type":6,"stats":[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2}, {"effectId":246,"spellId":7776,"name":"Enchant Chest - Lesser Mana","type":5,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2}, @@ -11009,8 +11009,8 @@ {"effectId":929,"itemId":16217,"spellId":20017,"name":"Enchant Shield - Greater Stamina","type":13,"enchantType":2,"stats":[0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":930,"spellId":13947,"name":"Enchant Gloves - Riding Skill","type":7,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2}, {"effectId":931,"spellId":13948,"name":"Enchant Gloves - Minor Haste","type":7,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, -{"effectId":943,"spellId":13529,"name":"Enchant 2H Weapon - Lesser Impact","type":13,"enchantType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":943,"spellId":13693,"name":"Enchant Weapon - Striking","type":13,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, +{"effectId":943,"spellId":13529,"name":"Enchant 2H Weapon - Lesser Impact","type":13,"enchantType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":963,"spellId":13937,"name":"Enchant 2H Weapon - Greater Impact","type":13,"enchantType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1}, {"effectId":1483,"itemId":11622,"spellId":15340,"name":"Lesser Arcanum of Rumination","type":1,"extraTypes":[9],"enchantType":3,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2}, {"effectId":1503,"itemId":11642,"spellId":15389,"name":"Lesser Arcanum of Constitution","type":1,"extraTypes":[9],"enchantType":3,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0],"quality":2}, diff --git a/assets/database/leftover_db.bin b/assets/database/leftover_db.bin index a1c042cac97c4a76b075b16682324401f107250a..aa3fd0b1bbdcbfbba3227af71c54d51cf0ec029b 100644 GIT binary patch delta 73 zcmV-P0Ji_!&N$r8IDmu!gaU*Egam{Iga(8Mgb0KQgbIWUvvvy4Gp)*4GxqU?rItk delta 73 zcmV-P0Ji_!&N$r8IDmu!gaU*Egam{Iga(8Mgb0KQgbIWUv Date: Sun, 12 Jan 2025 12:40:17 -0500 Subject: [PATCH 14/14] run atlasloot scraper --- assets/database/db.bin | Bin 6448412 -> 6449312 bytes assets/database/db.json | 120 ++++++++++++++--------------- assets/database/leftover_db.bin | Bin 927580 -> 927580 bytes assets/database/leftover_db.json | 4 +- assets/db_inputs/atlasloot_db.json | 120 ++++++++++++++++++++++------- 5 files changed, 154 insertions(+), 90 deletions(-) diff --git a/assets/database/db.bin b/assets/database/db.bin index 67ff833567d33852bd2f08d77739a092349a9851..d725a4994c0063f90a6aa00069aadd7d16168f9b 100644 GIT binary patch delta 2466 zcmb7Fdr(wm6rT_7wig%jiYE8~m+(PA@~~i4kh_~ax2$zzpLwjy7he zMWz{sjpU<-WF=5Q5q!{Tk_2hee1tG*r8A2EIT_7pPGeD1eRmhQ%R)~3$M4Ra-=5$3 zo$s8x=c~V18R#0U4BP_*0w53sFu-GA1QCK@8kis$Lf~u1y4dWEQCd{7?wZ`JO!~32l4PUEQJI}gd|9YW$+9<3n`Ea7FZ4| zU?rqMI;?^WkiiNzcn(%WCS*Z28WSUfZ87Gr4{F zU8gKExh~zn?(L#Rwl-b~6E6~DtU;_YvUh&BRIt+OHG(F|XGbq(N$g&G-1`3|)VvOP zguBZMxJgnNe>BMhHJ$f&KGKq0$5jc+Wiqi4&Q>Q)V97PPLUU==TsAR#wJm(V%VuHH z9^(kRnW{wV3C2R(JO!oA%$1n(hsDl%_u7WpP^uC&Iraz}wDPo_C$lY1xsDBNRUK^Q z3h(}mWj(4Fz+9JYdShK@GYzaalSb;<%SJp!x9b|c=tsmKb61X+GL+q3jnT64#`Ie! zG{(*Lp?S5^Uq#w)fE1!}j+#}Q=5~-zp?5%bDt!@16FgxgqagZDM z@dKsVFVTDKw!OeaF5s2llmmV#9po!~@KCAqQ#61xM*OVNLB7Sbzm!Hlo$lb)zm;x3 zML%^NqG$Zmu+ol50e=ag{%8?6%Um_(n%9ijA)PPce`Kf}gZwAGs)WJQ(u-Ry~^`|~`B z0duBE(T7Xt(Jt>4Jh~F!h@eM&NC)Y_`USL6r`xnDD)C&DcdVOqkS=@}O9|7m^rXS{dKqx5M^N${S6hi2xt^90SIbyk)^i&$?+vdHqC1^Sx#ua(Fb*$t0IYPN~2DoB-TGx82Q z<(jE?86L=?C*sIANXzMIpRH&Z62z|$Gjlp!$IR`r#1elDmaz2alw`u({_otT|U9AVJD5yMcP3%(fLu4Ylk4Qgm+UV?Z)6`zdwE@dO2wqFn+X~ z2Ha$>tQ-LwHmF|T&**^8;=m^LluKy3l>k+`@yesDoY|)lSOH z4dDXO^VOBqo650{qln{qEZkQ7MJCpF6n{WaJXZW6!KKHGhd;p< srDQ+BxobeN2^Q6yI}s>ArLcE+2`CBH$Ma2=bLC>DyJfq$2Ng<3kYzolQ3ifiO2??arN> zm(~{%oHdJ~fk1%Zg?3C9AI);ZCLiI%Rtu`Ft^5O`?17v<)fyv>N^&z!XR1XwotV<&t5G?w8W*G;(q&2R(dbnan4xZE*m^J5fy!5lBoc=%J=RDrkLG!+ zXwKvwZ~M*Js=xb{#_H8s-<{rK#&>(HgSAQ!_Pl63g$pd|$W~uyW)~>vclBe2Z=AC^ zn2QeIQ(N2t;B-m0iJhatKh#G3I{2(4x3mNVn|D+mI(oe9?pw ztLEQzQ2QF*#_+Dm=A@ru_z=U5Yki%R8Y|q9hXy<8m3Tht5x$k63A;;KmLy6+{*8~5 z3R6S{TBd|){Dw(e%3By^<++$xTqNUcZJdlsiKV+%se9zQOu5PR@5nNa?o06wgm)bx ztz6$i_n3XFiH$IM)hP)tETN8je%X<70U*w;w0~9*xQ}xkZwxyml##etEl|9 zv?PY^p)Y%tM{0}VU^gkEnoqblT?_25%?C7N=Z5duCHnb*O|Oy>MVHw67|uv0r(v=K zXEN3OIGw@V+0;!3UiMC^?q0kQc|lN ztLwRYYN1>1pN8YiM5#^Sd4XJkJenxYzmh=_z4`Amdh=5LdyHIt`FB2|e-p$yaIu}0{a9C1@;RR2^F7M2#)7Pc1l7LFFq7OocV7M?AF7M2#)7Pc1l7LFFq7OocV7M?A