diff --git a/sim/core/apl_actions_misc.go b/sim/core/apl_actions_misc.go index e2dfc79d23..906ed9ba08 100644 --- a/sim/core/apl_actions_misc.go +++ b/sim/core/apl_actions_misc.go @@ -155,7 +155,7 @@ func (action *APLActionItemSwap) Execute(sim *Simulation) { if action.swapSet == proto.APLActionItemSwap_Main { action.character.ItemSwap.reset(sim) } else { - action.character.ItemSwap.SwapItems(sim, action.character.ItemSwap.slots, true) + action.character.ItemSwap.SwapItems(sim, action.character.ItemSwap.slots) } } func (action *APLActionItemSwap) String() string { diff --git a/sim/core/character.go b/sim/core/character.go index aa0e0996e8..892d2ccb59 100644 --- a/sim/core/character.go +++ b/sim/core/character.go @@ -481,7 +481,6 @@ func (character *Character) Finalize() { } character.majorCooldownManager.finalize() - character.ItemSwap.finalize() } func (character *Character) FillPlayerStats(playerStats *proto.PlayerStats) { diff --git a/sim/core/item_swaps.go b/sim/core/item_swaps.go index 673b2f4ffa..0677fb1fad 100644 --- a/sim/core/item_swaps.go +++ b/sim/core/item_swaps.go @@ -22,10 +22,6 @@ type ItemSwap struct { // Which slots to actually swap. slots []proto.ItemSlot - // Used for resetting - initialEquippedItems [3]Item - initialUnequippedItems [3]Item - // Holds items that are currently not equipped unEquippedItems [3]Item swapped bool @@ -164,7 +160,7 @@ func (swap *ItemSwap) CalcStatChanges(slots []proto.ItemSlot) stats.Stats { return newStats } -func (swap *ItemSwap) SwapItems(sim *Simulation, slots []proto.ItemSlot, useGCD bool) { +func (swap *ItemSwap) SwapItems(sim *Simulation, slots []proto.ItemSlot) { if !swap.IsEnabled() { return } @@ -201,12 +197,12 @@ func (swap *ItemSwap) SwapItems(sim *Simulation, slots []proto.ItemSlot, useGCD character.AutoAttacks.StopMeleeUntil(sim, sim.CurrentTime, false) } - if useGCD { + // If GCD is ready then use the GCD, otherwise we assume it's being used along side a spell. + if character.GCD.IsReady(sim) { newGCD := sim.CurrentTime + 1500*time.Millisecond - if newGCD > character.GCD.ReadyAt() { - character.SetGCDTimer(sim, newGCD) - } + character.SetGCDTimer(sim, newGCD) } + swap.swapped = !swap.swapped } @@ -269,42 +265,12 @@ func (swap *ItemSwap) swapWeapon(slot proto.ItemSlot) { } } -func (swap *ItemSwap) finalize() { - if !swap.IsEnabled() { - return - } - - swap.initialEquippedItems = getInitialEquippedItems(swap.character) - swap.initialUnequippedItems = swap.unEquippedItems -} - func (swap *ItemSwap) reset(sim *Simulation) { - if !swap.IsEnabled() { + if !swap.IsEnabled() || !swap.IsSwapped() { return } - slots := [3]proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged} - for i, slot := range slots { - swap.character.Equipment[slot] = swap.initialEquippedItems[i] - swap.swapWeapon(slot) - } - - swap.unEquippedItems = swap.initialUnequippedItems - swap.swapped = false - - for _, onSwap := range swap.onSwapCallbacks { - onSwap(sim) - } -} - -func getInitialEquippedItems(character *Character) [3]Item { - var items [3]Item - - for i := range items { - items[i] = character.Equipment[i+int(offset)] - } - - return items + swap.SwapItems(sim, swap.slots) } func toItem(itemSpec *proto.ItemSpec) Item { diff --git a/sim/deathknight/dps/rotation_unholy_helper.go b/sim/deathknight/dps/rotation_unholy_helper.go index d22f84fec5..e02f8015b1 100644 --- a/sim/deathknight/dps/rotation_unholy_helper.go +++ b/sim/deathknight/dps/rotation_unholy_helper.go @@ -169,26 +169,26 @@ func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool { if dk.ur.mhSwap == WeaponSwap_BlackMagic { if !dk.ur.mhSwapped && shouldSwapBm { // Swap to BM - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}) dk.ur.mhSwapped = true } else if dk.ur.mhSwapped && shouldSwapBackFromBm { // Swap to Normal set and set BM Icd tracker dk.ur.bmIcd = dk.ur.blackMagicProc.ExpiresAt() + 35*time.Second dk.ur.mhSwapped = false - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}) } } if dk.ur.ohSwap == WeaponSwap_BlackMagic { if !dk.ur.ohSwapped && shouldSwapBm { // Swap to BM - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}) dk.ur.ohSwapped = true } else if dk.ur.ohSwapped && shouldSwapBackFromBm { // Swap to Normal set and set BM Icd tracker dk.ur.bmIcd = dk.ur.blackMagicProc.ExpiresAt() + 35*time.Second dk.ur.ohSwapped = false - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}) } } @@ -200,24 +200,24 @@ func (dk *DpsDeathknight) weaponSwapCheck(sim *core.Simulation) bool { if dk.ur.mhSwap == WeaponSwap_Berserking { if !dk.ur.mhSwapped && !dk.ur.berserkingMh.IsActive() && shouldSwapBerserking { // Swap to Berserking - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}) dk.ur.mhSwapped = true } else if dk.ur.mhSwapped && (dk.ur.berserkingMh.IsActive() || shouldSwapBackfromBerserking) { // Swap to Normal set dk.ur.mhSwapped = false - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand}) } } if dk.ur.ohSwap == WeaponSwap_Berserking { if !dk.ur.ohSwapped && !dk.ur.berserkingOh.IsActive() && shouldSwapBerserking { // Swap to Berserking - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}) dk.ur.ohSwapped = true } else if dk.ur.ohSwapped && (dk.ur.berserkingOh.IsActive() || shouldSwapBackfromBerserking) { // Swap to Normal set dk.ur.ohSwapped = false - dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}, true) + dk.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotOffHand}) } } diff --git a/sim/shaman/enhancement/enhancement.go b/sim/shaman/enhancement/enhancement.go index a2068b9957..00d3ef34e8 100644 --- a/sim/shaman/enhancement/enhancement.go +++ b/sim/shaman/enhancement/enhancement.go @@ -136,7 +136,7 @@ func (enh *EnhancementShaman) Initialize() { if !enh.IsUsingAPL { enh.RegisterPrepullAction(-time.Second, func(sim *core.Simulation) { - enh.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand}, false) + enh.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand}) }) } } diff --git a/sim/shaman/fire_elemental_totem.go b/sim/shaman/fire_elemental_totem.go index 8402a2c2ac..fe82987ad5 100644 --- a/sim/shaman/fire_elemental_totem.go +++ b/sim/shaman/fire_elemental_totem.go @@ -57,7 +57,7 @@ func (shaman *Shaman) registerFireElementalTotem() { //TODO handle more then one swap if the fight is greater then 5 mins, for now will just do the one. if !shaman.IsUsingAPL && shaman.FireElementalTotem.SpellMetrics[target.Index].Casts == 1 { - shaman.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand}, true) + shaman.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand}) } // Add a dummy aura to show in metrics diff --git a/sim/warlock/rotation.go b/sim/warlock/rotation.go index b30cef6718..601dab5b37 100644 --- a/sim/warlock/rotation.go +++ b/sim/warlock/rotation.go @@ -672,7 +672,7 @@ func (warlock *Warlock) OnGCDReady(sim *core.Simulation) { // after-GCD actions if ac.Spell == warlock.Corruption && warlock.ItemSwap.IsEnabled() && warlock.ItemSwap.IsSwapped() { warlock.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, - proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged}, true) + proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged}) } return diff --git a/sim/warlock/warlock.go b/sim/warlock/warlock.go index 8666c6f8b9..f25546e27d 100644 --- a/sim/warlock/warlock.go +++ b/sim/warlock/warlock.go @@ -205,7 +205,7 @@ func (warlock *Warlock) Reset(sim *core.Simulation) { if !warlock.IsUsingAPL { warlock.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, - proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged}, false) + proto.ItemSlot_ItemSlotOffHand, proto.ItemSlot_ItemSlotRanged}) } warlock.corrRefreshList = make([]time.Duration, len(warlock.Env.Encounter.TargetUnits)) warlock.setupCooldowns(sim) diff --git a/ui/core/components/gear_picker.tsx b/ui/core/components/gear_picker.tsx index 346a6103e4..42c3134701 100644 --- a/ui/core/components/gear_picker.tsx +++ b/ui/core/components/gear_picker.tsx @@ -301,22 +301,21 @@ export class ItemPicker extends Component { } } -export class IconItemSwapPicker extends Input, ValueType> { - private readonly config: InputConfig, ValueType>; +export class IconItemSwapPicker extends Component { + private readonly iconAnchor: HTMLAnchorElement; private readonly socketsContainerElem: HTMLElement; - private readonly player: Player; + private readonly player: Player; private readonly slot: ItemSlot; // All items and enchants that are eligible for this slot private _items: Array = []; private _enchants: Array = []; - constructor(parent: HTMLElement, simUI: SimUI, player: Player, slot: ItemSlot, config: InputConfig, ValueType>) { - super(parent, 'icon-picker-root', player, config) + constructor(parent: HTMLElement, simUI: SimUI, player: Player, slot: ItemSlot) { + super(parent, 'icon-picker-root') this.rootElem.classList.add('icon-picker'); this.player = player; - this.config = config; this.slot = slot; this.iconAnchor = document.createElement('a'); @@ -333,12 +332,12 @@ export class IconItemSwapPicker extends Input< this._enchants = this.player.getEnchants(slot); const gearData = { equipItem: (eventID: EventID, equippedItem: EquippedItem | null) => { - let isg = this.player.getItemSwapGear(); - this.player.setItemSwapGear(eventID, isg.withEquippedItem(this.slot, equippedItem, player.canDualWield2H())); - this.inputChanged(eventID); + let curIsg = player.getItemSwapGear(); + curIsg = curIsg.withEquippedItem(slot, equippedItem, player.canDualWield2H()) + player.setItemSwapGear(eventID, curIsg); }, getEquippedItem: () => this.player.getItemSwapGear().getEquippedItem(this.slot), - changeEvent: config.changedEvent(player), + changeEvent: player.itemSwapChangeEmitter, } this.iconAnchor.addEventListener('click', (event: Event) => { @@ -352,31 +351,27 @@ export class IconItemSwapPicker extends Input< gearData: gearData, }); }); - }).finally(() => this.init()); - } + }); - getInputElem(): HTMLElement { - return this.iconAnchor; - } - getInputValue(): ValueType { - return this.player.getItemSwapGear().toProto() as unknown as ValueType + player.itemSwapChangeEmitter.on(() => { + this.update(player.getItemSwapGear().getEquippedItem(slot)); + }); } - setInputValue(newValue: ValueType): void { + update(newItem: EquippedItem | null) { this.iconAnchor.style.backgroundImage = `url('${getEmptySlotIconUrl(this.slot)}')`; this.iconAnchor.removeAttribute('data-wowhead'); this.iconAnchor.href = "#"; this.socketsContainerElem.innerText = ''; - const equippedItem = this.player.getItemSwapGear().getEquippedItem(this.slot); - if (equippedItem) { + if (newItem) { this.iconAnchor.classList.add("active") - equippedItem.asActionId().fillAndSet(this.iconAnchor, true, true); - this.player.setWowheadData(equippedItem, this.iconAnchor); + newItem.asActionId().fillAndSet(this.iconAnchor, true, true); + this.player.setWowheadData(newItem, this.iconAnchor); - equippedItem.allSocketColors().forEach((socketColor, gemIdx) => { - this.socketsContainerElem.appendChild(createGemContainer(socketColor, equippedItem.gems[gemIdx])); + newItem.allSocketColors().forEach((socketColor, gemIdx) => { + this.socketsContainerElem.appendChild(createGemContainer(socketColor, newItem.gems[gemIdx])); }); } else { this.iconAnchor.classList.remove("active") diff --git a/ui/core/components/item_swap_picker.ts b/ui/core/components/item_swap_picker.ts index 57fba0c075..8d3fd9f6e0 100644 --- a/ui/core/components/item_swap_picker.ts +++ b/ui/core/components/item_swap_picker.ts @@ -1,4 +1,4 @@ -import { Spec, ItemSlot, ItemSpec } from '../proto/common.js'; +import { Spec, ItemSlot } from '../proto/common.js'; import { Player } from '../player.js'; import { Component } from './component.js'; import { IconItemSwapPicker } from './gear_picker.js' @@ -70,15 +70,7 @@ export class ItemSwapPicker extends Component { swapButton.addEventListener('click', _event => { this.swapWithGear(TypedEvent.nextEventID(), player) }); this.itemSlots.forEach(itemSlot => { - new IconItemSwapPicker(itemSwapContainer, simUI, player, itemSlot, { - getValue: (player: Player) => player.getItemSwapGear().getEquippedItem(itemSlot)?.asSpec() || ItemSpec.create(), - setValue: (eventID: EventID, player: Player, newValue: ItemSpec) => { - let curIsg = player.getItemSwapGear(); - curIsg = curIsg.withEquippedItem(itemSlot, player.sim.db.lookupItemSpec(newValue), player.canDualWield2H()) - player.setItemSwapGear(eventID, curIsg); - }, - changedEvent: (player: Player) => player.itemSwapChangeEmitter, - }); + new IconItemSwapPicker(itemSwapContainer, simUI, player, itemSlot) }); }