From 9256a556b879b90a16417a7c39934fa66648f89b Mon Sep 17 00:00:00 2001 From: Josh DM Date: Fri, 20 Oct 2023 16:22:16 -0400 Subject: [PATCH 1/2] add nibelung survivability configuration --- proto/api.proto | 2 ++ proto/ui.proto | 1 + sim/common/wotlk/nibelung.go | 9 ++++++- sim/core/character.go | 9 ++++--- sim/core/unit.go | 3 +++ ui/balance_druid/presets.ts | 1 + ui/balance_druid/sim.ts | 1 + .../individual_sim_ui/settings_tab.ts | 2 ++ ui/core/components/other_inputs.ts | 27 +++++++++++++------ ui/core/individual_sim_ui.ts | 2 ++ ui/core/player.ts | 16 +++++++++++ ui/core/proto_utils/equipped_item.ts | 4 +++ ui/elemental_shaman/presets.ts | 1 + ui/elemental_shaman/sim.ts | 1 + ui/mage/presets.ts | 1 + ui/mage/sim.ts | 1 + ui/shadow_priest/presets.ts | 1 + ui/shadow_priest/sim.ts | 1 + ui/warlock/presets.ts | 1 + ui/warlock/sim.ts | 1 + 20 files changed, 72 insertions(+), 13 deletions(-) diff --git a/proto/api.proto b/proto/api.proto index bed7bafd2f..2e25b41fb6 100644 --- a/proto/api.proto +++ b/proto/api.proto @@ -71,6 +71,8 @@ message Player { // Items/enchants/gems/etc to include in the database. SimDatabase database = 35; + + double nibelung_average_casts = 43; } message Party { diff --git a/proto/ui.proto b/proto/ui.proto index 1fb71be1c8..8f97299c41 100644 --- a/proto/ui.proto +++ b/proto/ui.proto @@ -299,6 +299,7 @@ message SavedSettings { bool in_front_of_target = 11; double distance_from_target = 12; HealingModel healing_model = 13; + double nibelung_average_casts = 15; } message SavedTalents { diff --git a/sim/common/wotlk/nibelung.go b/sim/common/wotlk/nibelung.go index 463f99e568..73207bc6cb 100644 --- a/sim/common/wotlk/nibelung.go +++ b/sim/common/wotlk/nibelung.go @@ -114,7 +114,14 @@ func MakeNibelungTriggerAura(agent core.Agent, isHeroic bool) { for _, petAgent := range character.PetAgents { if valkyr, ok := petAgent.(*ValkyrPet); ok && !valkyr.IsEnabled() { valkyr.registerSmite(isHeroic) - valkyr.EnableWithTimeout(sim, petAgent, valkyrAura.Duration) + + averageCasts := character.NibelungAverageCasts + duration := min(time.Duration(averageCasts/16*30)*time.Second, time.Second*30) + valkyrAura.Duration = duration + + if averageCasts != 0 { + valkyr.EnableWithTimeout(sim, petAgent, duration) + } break } } diff --git a/sim/core/character.go b/sim/core/character.go index d4b3ab15b7..b60b834d84 100644 --- a/sim/core/character.go +++ b/sim/core/character.go @@ -102,10 +102,11 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character StatDependencyManager: stats.NewStatDependencyManager(), - ReactionTime: max(0, time.Duration(player.ReactionTimeMs)*time.Millisecond), - ChannelClipDelay: max(0, time.Duration(player.ChannelClipDelayMs)*time.Millisecond), - DistanceFromTarget: player.DistanceFromTarget, - IsUsingAPL: player.Rotation != nil && player.Rotation.Type == proto.APLRotation_TypeAPL, + ReactionTime: max(0, time.Duration(player.ReactionTimeMs)*time.Millisecond), + ChannelClipDelay: max(0, time.Duration(player.ChannelClipDelayMs)*time.Millisecond), + DistanceFromTarget: player.DistanceFromTarget, + NibelungAverageCasts: player.NibelungAverageCasts, + IsUsingAPL: player.Rotation != nil && player.Rotation.Type == proto.APLRotation_TypeAPL, }, Name: player.Name, diff --git a/sim/core/unit.go b/sim/core/unit.go index bbec1ecd68..54f327445b 100644 --- a/sim/core/unit.go +++ b/sim/core/unit.go @@ -60,6 +60,9 @@ type Unit struct { // for calculating spell travel time for certain spells. DistanceFromTarget float64 + // How many casts on average a Valkyr will get off during its lifetime. + NibelungAverageCasts float64 + // Environment in which this Unit exists. This will be nil until after the // construction phase. Env *Environment diff --git a/ui/balance_druid/presets.ts b/ui/balance_druid/presets.ts index b60d557c2d..fac932a70e 100644 --- a/ui/balance_druid/presets.ts +++ b/ui/balance_druid/presets.ts @@ -183,4 +183,5 @@ export const OtherDefaults = { distanceFromTarget: 18, profession1: Profession.Engineering, profession2: Profession.Tailoring, + nibelungAverageCasts: 11, }; diff --git a/ui/balance_druid/sim.ts b/ui/balance_druid/sim.ts index 047b357a83..ad76e3c1f7 100644 --- a/ui/balance_druid/sim.ts +++ b/ui/balance_druid/sim.ts @@ -103,6 +103,7 @@ export class BalanceDruidSimUI extends IndividualSimUI { OtherInputs.TankAssignment, OtherInputs.ReactionTime, OtherInputs.DistanceFromTarget, + OtherInputs.nibelungAverageCasts, ], }, encounterPicker: { diff --git a/ui/core/components/individual_sim_ui/settings_tab.ts b/ui/core/components/individual_sim_ui/settings_tab.ts index 3a5f55dca1..73140508e1 100644 --- a/ui/core/components/individual_sim_ui/settings_tab.ts +++ b/ui/core/components/individual_sim_ui/settings_tab.ts @@ -399,6 +399,7 @@ export class SettingsTab extends SimTab { inFrontOfTarget: player.getInFrontOfTarget(), distanceFromTarget: player.getDistanceFromTarget(), healingModel: player.getHealingModel(), + nibelungAverageCasts: player.getNibelungAverageCasts(), cooldowns: aplLaunchStatuses[simUI.player.spec] == LaunchStatus.Unlaunched ? player.getCooldowns() : undefined, rotationJson: aplLaunchStatuses[simUI.player.spec] == LaunchStatus.Unlaunched ? JSON.stringify(player.specTypeFunctions.rotationToJson(player.getRotation())) : undefined, }); @@ -420,6 +421,7 @@ export class SettingsTab extends SimTab { simUI.player.setInFrontOfTarget(eventID, newSettings.inFrontOfTarget); simUI.player.setDistanceFromTarget(eventID, newSettings.distanceFromTarget); simUI.player.setHealingModel(eventID, newSettings.healingModel || HealingModel.create()); + simUI.player.setNibelungAverageCasts(eventID, newSettings.nibelungAverageCasts) if (aplLaunchStatuses[simUI.player.spec] == LaunchStatus.Unlaunched) { simUI.player.setCooldowns(eventID, newSettings.cooldowns || Cooldowns.create()); if (newSettings.rotationJson) { diff --git a/ui/core/components/other_inputs.ts b/ui/core/components/other_inputs.ts index 4dd2ea1ac8..4a7ef8687e 100644 --- a/ui/core/components/other_inputs.ts +++ b/ui/core/components/other_inputs.ts @@ -1,11 +1,10 @@ -import { BooleanPicker } from '../components/boolean_picker.js'; -import { EnumPicker } from '../components/enum_picker.js'; -import { UnitReference } from '../proto/common.js'; -import { Player } from '../player.js'; -import { Sim } from '../sim.js'; -import { EventID, TypedEvent } from '../typed_event.js'; -import { emptyUnitReference } from '../proto_utils/utils.js'; -import { APLRotation_Type } from '../proto/apl.js'; +import {BooleanPicker} from '../components/boolean_picker.js'; +import {EnumPicker} from '../components/enum_picker.js'; +import {ItemSlot, UnitReference} from '../proto/common.js'; +import {Player} from '../player.js'; +import {Sim} from '../sim.js'; +import {EventID} from '../typed_event.js'; +import {emptyUnitReference} from '../proto_utils/utils.js'; export function makeShow1hWeaponsSelector(parent: HTMLElement, sim: Sim): BooleanPicker { return new BooleanPicker(parent, sim, { @@ -127,6 +126,18 @@ export const DistanceFromTarget = { }, }; +export const nibelungAverageCasts = { + type: 'number' as const, + label: "Nibelung's Valkyr Survival (in # of casts)", + labelTooltip: 'Number of casts of Nibelung\'s summoned Valkyrs get out before they die (max 16)', + changedEvent: (player: Player) => player.changeEmitter, + getValue: (player: Player) => player.getNibelungAverageCasts(), + setValue: (eventID: EventID, player: Player, newValue: number) => { + player.setNibelungAverageCasts(eventID, newValue); + }, + showWhen: (player: Player) => [49992, 50648].includes(player.getEquippedItem(ItemSlot.ItemSlotMainHand)?.id || 0) +} + export const TankAssignment = { type: 'enum' as const, extraCssClasses: [ diff --git a/ui/core/individual_sim_ui.ts b/ui/core/individual_sim_ui.ts index 7c2543be3e..ae2871a28d 100644 --- a/ui/core/individual_sim_ui.ts +++ b/ui/core/individual_sim_ui.ts @@ -85,6 +85,7 @@ export interface OtherDefaults { profession2?: Profession, distanceFromTarget?: number, channelClipDelay?: number, + nibelungAverageCasts?: number, } export interface IndividualSimUIConfig { @@ -428,6 +429,7 @@ export abstract class IndividualSimUI extends SimUI { this.player.setProfession2(eventID, this.individualConfig.defaults.other?.profession2 || Profession.Jewelcrafting); this.player.setDistanceFromTarget(eventID, this.individualConfig.defaults.other?.distanceFromTarget || 0); this.player.setChannelClipDelay(eventID, this.individualConfig.defaults.other?.channelClipDelay || 0); + this.player.setNibelungAverageCasts(eventID, this.individualConfig.defaults.other?.nibelungAverageCasts || 11); if (this.isWithinRaidSim) { this.sim.raid.setTargetDummies(eventID, 0); diff --git a/ui/core/player.ts b/ui/core/player.ts index b0fcf992bd..9630b94820 100644 --- a/ui/core/player.ts +++ b/ui/core/player.ts @@ -236,6 +236,7 @@ export class Player { private channelClipDelay: number = 0; private inFrontOfTarget: boolean = false; private distanceFromTarget: number = 0; + private nibelungAverageCasts: number = 11; private healingModel: HealingModel = HealingModel.create(); private healingEnabled: boolean = false; @@ -935,6 +936,19 @@ export class Player { this.distanceFromTarget = newDistanceFromTarget; this.distanceFromTargetChangeEmitter.emit(eventID); } + + getNibelungAverageCasts(): number { + return this.nibelungAverageCasts; + } + + setNibelungAverageCasts(eventID: EventID, newnibelungAverageCasts: number) { + if (newnibelungAverageCasts == this.nibelungAverageCasts) + return; + + this.nibelungAverageCasts = Math.min(newnibelungAverageCasts, 16); + this.miscOptionsChangeEmitter.emit(eventID); + } + setDefaultHealingParams(hm: HealingModel) { var boss = this.sim.encounter.primaryTarget; var dualWield = boss.dualWield; @@ -1324,6 +1338,7 @@ export class Player { distanceFromTarget: this.getDistanceFromTarget(), healingModel: this.getHealingModel(), database: forExport ? SimDatabase.create() : this.toDatabase(), + nibelungAverageCasts: this.getNibelungAverageCasts(), }), (aplIsLaunched || (forSimming && aplRotation.type == APLRotationType.TypeAPL)) ? this.specTypeFunctions.rotationCreate() @@ -1384,6 +1399,7 @@ export class Player { this.setChannelClipDelay(eventID, proto.channelClipDelayMs); this.setInFrontOfTarget(eventID, proto.inFrontOfTarget); this.setDistanceFromTarget(eventID, proto.distanceFromTarget); + this.setNibelungAverageCasts(eventID, proto.nibelungAverageCasts); this.setHealingModel(eventID, proto.healingModel || HealingModel.create()); this.setSpecOptions(eventID, this.specTypeFunctions.optionsFromPlayer(proto)); diff --git a/ui/core/proto_utils/equipped_item.ts b/ui/core/proto_utils/equipped_item.ts index 12f08aae2f..427fc3ae74 100644 --- a/ui/core/proto_utils/equipped_item.ts +++ b/ui/core/proto_utils/equipped_item.ts @@ -50,6 +50,10 @@ export class EquippedItem { return Item.clone(this._item); } + get id(): number { + return this._item.id; + } + get enchant(): Enchant | null { // Make a defensive copy return this._enchant ? Enchant.clone(this._enchant) : null; diff --git a/ui/elemental_shaman/presets.ts b/ui/elemental_shaman/presets.ts index 7718807d5f..febf62e60f 100644 --- a/ui/elemental_shaman/presets.ts +++ b/ui/elemental_shaman/presets.ts @@ -115,6 +115,7 @@ export const OtherDefaults = { distanceFromTarget: 20, profession1: Profession.Engineering, profession2: Profession.Tailoring, + nibelungAverageCasts: 11, } export const DefaultConsumes = Consumes.create({ diff --git a/ui/elemental_shaman/sim.ts b/ui/elemental_shaman/sim.ts index 614fb11ba1..2982c4acff 100644 --- a/ui/elemental_shaman/sim.ts +++ b/ui/elemental_shaman/sim.ts @@ -143,6 +143,7 @@ export class ElementalShamanSimUI extends IndividualSimUI { OtherInputs.ReactionTime, OtherInputs.DistanceFromTarget, OtherInputs.TankAssignment, + OtherInputs.nibelungAverageCasts, ], }, encounterPicker: { diff --git a/ui/shadow_priest/presets.ts b/ui/shadow_priest/presets.ts index 9e68b9992a..e16ac8ef07 100644 --- a/ui/shadow_priest/presets.ts +++ b/ui/shadow_priest/presets.ts @@ -131,4 +131,5 @@ export const OtherDefaults = { channelClipDelay: 100, profession1: Profession.Engineering, profession2: Profession.Tailoring, + nibelungAverageCasts: 11, }; diff --git a/ui/shadow_priest/sim.ts b/ui/shadow_priest/sim.ts index 81939e6af3..9cc17de203 100644 --- a/ui/shadow_priest/sim.ts +++ b/ui/shadow_priest/sim.ts @@ -116,6 +116,7 @@ export class ShadowPriestSimUI extends IndividualSimUI { inputs: [ OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay, + OtherInputs.nibelungAverageCasts, ], }, encounterPicker: { diff --git a/ui/warlock/presets.ts b/ui/warlock/presets.ts index 1f8f03c272..5eb69f5f6d 100644 --- a/ui/warlock/presets.ts +++ b/ui/warlock/presets.ts @@ -226,4 +226,5 @@ export const OtherDefaults = { profession1: Profession.Engineering, profession2: Profession.Tailoring, channelClipDelay: 150, + nibelungAverageCasts: 11, }; diff --git a/ui/warlock/sim.ts b/ui/warlock/sim.ts index 69291f82c6..1334314938 100644 --- a/ui/warlock/sim.ts +++ b/ui/warlock/sim.ts @@ -123,6 +123,7 @@ export class WarlockSimUI extends IndividualSimUI { OtherInputs.DistanceFromTarget, OtherInputs.TankAssignment, OtherInputs.ChannelClipDelay, + OtherInputs.nibelungAverageCasts, WarlockInputs.NewDPBehaviour, ], }, From 193f37772021c819cce4b331abe5fd2f82a98e96 Mon Sep 17 00:00:00 2001 From: Josh DM Date: Fri, 20 Oct 2023 16:26:29 -0400 Subject: [PATCH 2/2] better --- sim/common/wotlk/nibelung.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim/common/wotlk/nibelung.go b/sim/common/wotlk/nibelung.go index 73207bc6cb..43cd2cd8d6 100644 --- a/sim/common/wotlk/nibelung.go +++ b/sim/common/wotlk/nibelung.go @@ -117,10 +117,10 @@ func MakeNibelungTriggerAura(agent core.Agent, isHeroic bool) { averageCasts := character.NibelungAverageCasts duration := min(time.Duration(averageCasts/16*30)*time.Second, time.Second*30) - valkyrAura.Duration = duration + valkyrAura.Duration = max(duration, time.Millisecond*250) - if averageCasts != 0 { - valkyr.EnableWithTimeout(sim, petAgent, duration) + if averageCasts > 0 { + valkyr.EnableWithTimeout(sim, petAgent, valkyrAura.Duration) } break }