diff --git a/assets/database/db.bin b/assets/database/db.bin index cb97d6085a..06c46cf9b1 100644 Binary files a/assets/database/db.bin and b/assets/database/db.bin differ diff --git a/assets/database/db.json b/assets/database/db.json index 535b219d05..a991a4683f 100644 --- a/assets/database/db.json +++ b/assets/database/db.json @@ -10131,8 +10131,8 @@ {"path":"Default/Movement","targets":[{"path":"Default/Movement","target":{"id":31147,"name":"Movement","level":88,"mobType":7,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,120016403,0,0,0,0,0,0,0],"minBaseDamage":210000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Movement Interval","tooltip":"How often the player will move in seconds","numberValue":10},{"inputType":1,"label":"Reaction Time","tooltip":"How long the player can wait for casts to finish before moving in seconds","numberValue":1.5},{"inputType":1,"label":"Yards","tooltip":"How many yards the player moves","numberValue":5}]}}]}, {"path":"Blackwing Descent/Magmaw 10","targets":[{"path":"Blackwing Descent/Magmaw 10","target":{"id":41570,"name":"Magmaw 10","level":88,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,26798304,0,0,0,0,0,0,0],"minBaseDamage":110000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Impale Reaction Time","tooltip":"How long will the Raid take to Impale Head in Seconds. (After the initial 10s)","numberValue":5}]}}]}, {"path":"Blackwing Descent/Magmaw 25","targets":[{"path":"Blackwing Descent/Magmaw 25","target":{"id":41571,"name":"Magmaw 25","level":88,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,81082048,0,0,0,0,0,0,0],"minBaseDamage":150000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Impale Reaction Time","tooltip":"How long will the Raid take to Impale Head in Seconds. (After the initial 10s)","numberValue":5}]}}]}, -{"path":"Blackwing Descent/Magmaw 10 H","targets":[{"path":"Blackwing Descent/Magmaw 10 H","target":{"id":41572,"name":"Magmaw 10 H","level":88,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,39200000,0,0,0,0,0,0,0],"minBaseDamage":150000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Impale Reaction Time","tooltip":"How long will the Raid take to Impale Head in Seconds. (After the initial 10s)","numberValue":5}]}},{"path":"Blackwing Descent/Magmaw 10 H Blazing Construct","target":{"id":41582,"name":"Magmaw 10 H Blazing Construct","level":87,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,1410000,0,0,0,0,0,0,0],"minBaseDamage":44000,"damageSpread":0.5,"swingSpeed":2,"tankIndex":1}}]}, -{"path":"Blackwing Descent/Magmaw 25 H","targets":[{"path":"Blackwing Descent/Magmaw 25 H","target":{"id":41573,"name":"Magmaw 25 H","level":88,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,120016403,0,0,0,0,0,0,0],"minBaseDamage":210000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Impale Reaction Time","tooltip":"How long will the Raid take to Impale Head in Seconds. (After the initial 10s)","numberValue":5}]}},{"path":"Blackwing Descent/Magmaw 25 H Blazing Construct","target":{"id":41583,"name":"Magmaw 25 H Blazing Construct","level":87,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,4500000,0,0,0,0,0,0,0],"minBaseDamage":80000,"damageSpread":0.5,"swingSpeed":2,"tankIndex":1}}]} +{"path":"Blackwing Descent/Magmaw 10 H","targets":[{"path":"Blackwing Descent/Magmaw 10 H","target":{"id":41572,"name":"Magmaw 10 H","level":88,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,39200000,0,0,0,0,0,0,0],"minBaseDamage":150000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Impale Reaction Time","tooltip":"How long will the Raid take to Impale Head in Seconds. (After the initial 10s)","numberValue":5}]}},{"path":"Blackwing Descent/Blazing Construct 10 H","target":{"id":41582,"name":"Blazing Construct 10 H","level":87,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,1410000,0,0,0,0,0,0,0],"minBaseDamage":44000,"damageSpread":0.5,"swingSpeed":2,"tankIndex":1}}]}, +{"path":"Blackwing Descent/Magmaw 25 H","targets":[{"path":"Blackwing Descent/Magmaw 25 H","target":{"id":41573,"name":"Magmaw 25 H","level":88,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,120016403,0,0,0,0,0,0,0],"minBaseDamage":210000,"damageSpread":0.4,"swingSpeed":2.5,"targetInputs":[{"inputType":1,"label":"Impale Reaction Time","tooltip":"How long will the Raid take to Impale Head in Seconds. (After the initial 10s)","numberValue":5}]}},{"path":"Blackwing Descent/Blazing Construct 25 H","target":{"id":41583,"name":"Blazing Construct 25 H","level":87,"mobType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,650,0,0,0,0,0,0,11977,0,0,0,0,0,0,4500000,0,0,0,0,0,0,0],"minBaseDamage":80000,"damageSpread":0.5,"swingSpeed":2,"tankIndex":1}}]} ], "glyphIds":[ {"itemId":40919,"spellId":54830}, diff --git a/sim/core/environment.go b/sim/core/environment.go index b525a9e956..a26e8d354c 100644 --- a/sim/core/environment.go +++ b/sim/core/environment.go @@ -102,6 +102,7 @@ func (env *Environment) construct(raidProto *proto.Raid, encounterProto *proto.E raidTarget := env.GetUnit(raidTargetProto, nil) if raidTarget != nil { target.CurrentTarget = raidTarget + raidTarget.CurrentTarget = &target.Unit } } } diff --git a/sim/encounters/bwd/magmaw_ai.go b/sim/encounters/bwd/magmaw_ai.go index 09ba6855bc..c11a4ec9fd 100644 --- a/sim/encounters/bwd/magmaw_ai.go +++ b/sim/encounters/bwd/magmaw_ai.go @@ -7,14 +7,16 @@ import ( "github.com/wowsims/cata/sim/core" "github.com/wowsims/cata/sim/core/proto" "github.com/wowsims/cata/sim/core/stats" + "github.com/wowsims/cata/sim/encounters/default_ai" ) func createMagmawPreset(bossPrefix string, raidSize int, isHeroic bool, npcId int32, health float64, minBaseDamage float64, addHealth float64, addMinBaseDamage float64) { targetName := fmt.Sprintf("Magmaw %d", raidSize) + targetNameAdd := fmt.Sprintf("Blazing Construct %d", raidSize) if isHeroic { targetName = targetName + " H" + targetNameAdd = targetNameAdd + " H" } - targetNameAdd := targetName + " Blazing Construct" core.AddPresetTarget(&core.PresetTarget{ PathPrefix: bossPrefix, Config: &proto.Target{ @@ -92,7 +94,42 @@ func createMagmawPreset(bossPrefix string, raidSize int, isHeroic bool, npcId in DamageSpread: 0.5, TargetInputs: []*proto.TargetInput{}, }, - AI: nil, + AI: default_ai.NewDefaultAI([]default_ai.TargetAbility{ + { + InitialCD: time.Second * 5, + ChanceToUse: 0, + MakeSpell: func(target *core.Target) *core.Spell { + // Fiery Slash Next melee Spell + nextMeleeSpell := target.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 92144}, + SpellSchool: core.SpellSchoolFire, + ProcMask: core.ProcMaskSpellDamage, + + Cast: core.CastConfig{ + CD: core.Cooldown{ + Timer: target.NewTimer(), + Duration: time.Second * 7, + }, + }, + + DamageMultiplier: 0.75, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, spell.Unit.AutoAttacks.MH().EnemyWeaponDamage(sim, spell.MeleeAttackPower(), 0.5), spell.OutcomeEnemyMeleeWhite) + }, + }) + + target.AutoAttacks.SetReplaceMHSwing(func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { + if nextMeleeSpell.CanCast(sim, target.CurrentTarget) && sim.Proc(0.75, "Fiery Slash Cast") { + return nextMeleeSpell + } + return mhSwingSpell + }) + + return nextMeleeSpell + }, + }, + }), }) core.AddPresetEncounter(targetName, []string{ bossPrefix + "/" + targetName, @@ -221,6 +258,7 @@ func (ai *MagmawAI) registerSpells() { OnExpire: func(aura *core.Aura, sim *core.Simulation) { aura.Unit.PseudoStats.DamageTakenMultiplier /= 2 + // TODO: Move this to an APL Action if !isIndividualSim && ai.Target.Env.GetNumTargets() > 1 { addTarget := ai.Target.Env.NextTargetUnit(&ai.Target.Unit) if addTarget.CurrentTarget != nil { @@ -457,21 +495,25 @@ func (ai *MagmawAI) registerSpells() { } else { // Individual sim fake tank swaps tankUnit := &ai.Target.Env.Raid.Parties[0].Players[0].GetCharacter().Unit - if tankUnit.Metrics.IsTanking() && ai.Target.Env.GetNumTargets() > 1 { + if tankUnit.Metrics.IsTanking() { if !ai.individualTankSwap { ai.swelteringArmor.Get(ai.lastMangleTarget).Activate(sim) + } - // Remove boss target - ai.individualTankSwap = true - ai.Target.CurrentTarget = nil - - // Set add target - addTarget := ai.Target.Env.NextTargetUnit(&ai.Target.Unit) - tankUnit.CurrentTarget = addTarget - addTarget.CurrentTarget = tankUnit - addTarget.AutoAttacks.EnableAutoSwing(sim) - } else { - ai.individualTankSwap = false + if ai.Target.Env.GetNumTargets() > 1 { + if !ai.individualTankSwap { + // Remove boss target + ai.individualTankSwap = true + ai.Target.CurrentTarget = nil + + // Set add target + addTarget := ai.Target.Env.NextTargetUnit(&ai.Target.Unit) + tankUnit.CurrentTarget = addTarget + addTarget.CurrentTarget = tankUnit + addTarget.AutoAttacks.EnableAutoSwing(sim) + } else { + ai.individualTankSwap = false + } } } } diff --git a/sim/encounters/default_ai.go b/sim/encounters/default_ai/default_ai.go similarity index 98% rename from sim/encounters/default_ai.go rename to sim/encounters/default_ai/default_ai.go index 72c3c33b47..b4e1339db1 100644 --- a/sim/encounters/default_ai.go +++ b/sim/encounters/default_ai/default_ai.go @@ -1,4 +1,4 @@ -package encounters +package default_ai import ( "time" diff --git a/sim/encounters/movement_ai.go b/sim/encounters/movement_ai.go index f973af7863..4d8aeb58ab 100644 --- a/sim/encounters/movement_ai.go +++ b/sim/encounters/movement_ai.go @@ -62,7 +62,7 @@ func addMovementAI() { type MovementAI struct { Target *core.Target - LastMoveTime time.Duration + NextMoveTime time.Duration MoveInterval time.Duration // How often moves happen ReactionTime time.Duration // Time available to react before area should be cleared MoveYards float64 // Duration of the move @@ -97,10 +97,16 @@ func (ai *MovementAI) Initialize(target *core.Target, config *proto.Target) { } func (ai *MovementAI) Reset(sim *core.Simulation) { - ai.LastMoveTime = 0 + ai.NextMoveTime = 0 } func (ai *MovementAI) ExecuteCustomRotation(sim *core.Simulation) { + // This can be called from auto attacks and not only gcd + // so we return if thats the case + if sim.CurrentTime < ai.NextMoveTime { + return + } + players := sim.Raid.AllPlayerUnits for i := 0; i < len(players); i++ { @@ -126,7 +132,8 @@ func (ai *MovementAI) ExecuteCustomRotation(sim *core.Simulation) { } } - ai.Target.WaitUntil(sim, sim.CurrentTime+ai.MoveInterval) + ai.NextMoveTime = sim.CurrentTime + ai.MoveInterval + ai.Target.WaitUntil(sim, ai.NextMoveTime) } func (ai *MovementAI) TimeToMove(distance float64, unit *core.Unit) time.Duration { return core.DurationFromSeconds(distance / unit.GetMovementSpeed()) diff --git a/sim/mage/talents_fire.go b/sim/mage/talents_fire.go index 2caf264d4e..74a949cbb4 100644 --- a/sim/mage/talents_fire.go +++ b/sim/mage/talents_fire.go @@ -46,6 +46,14 @@ func (mage *Mage) ApplyFireTalents() { }) } + // Firestarter + if mage.Talents.Firestarter { + mage.AddStaticMod(core.SpellModConfig{ + ClassMask: MageSpellScorch, + Kind: core.SpellMod_AllowCastWhileMoving, + }) + } + // Improved Flamestrike if mage.Talents.ImprovedFlamestrike > 0 { mage.AddStaticMod(core.SpellModConfig{ diff --git a/ui/core/components/detailed_results/timeline.tsx b/ui/core/components/detailed_results/timeline.tsx index df58f11db4..c19e862a01 100644 --- a/ui/core/components/detailed_results/timeline.tsx +++ b/ui/core/components/detailed_results/timeline.tsx @@ -1142,7 +1142,8 @@ const DEFAULT_ACTION_CATEGORY = 3; // Hard-coded spell categories for controlling rotation ordering. const idToCategoryMap: Record = { - [OtherAction.OtherActionAttack]: 0, + [OtherAction.OtherActionMove]: 0, + [OtherAction.OtherActionAttack]: 0.01, [OtherAction.OtherActionShoot]: 0.5, // Druid diff --git a/ui/core/components/encounter_picker.ts b/ui/core/components/encounter_picker.ts index 1423b2490d..a3de5f350f 100644 --- a/ui/core/components/encounter_picker.ts +++ b/ui/core/components/encounter_picker.ts @@ -29,12 +29,34 @@ export class EncounterPicker extends Component { modEncounter.sim.waitForInit().then(() => { const presetTargets = modEncounter.sim.db.getAllPresetTargets(); + // new EnumPicker(this.rootElem, modEncounter, { + // extraCssClasses: ['damage-metrics', 'npc-picker'], + // label: 'NPC', + // labelTooltip: 'Selects a preset NPC configuration.', + // values: [{ name: 'Custom', value: -1 }].concat( + // presetTargets.map((pe, i) => { + // return { + // name: pe.path, + // value: i, + // }; + // }), + // ), + // changedEvent: (encounter: Encounter) => encounter.changeEmitter, + // getValue: (encounter: Encounter) => presetTargets.findIndex(pe => equalTargetsIgnoreInputs(encounter.primaryTarget, pe.target)), + // setValue: (eventID: EventID, encounter: Encounter, newValue: number) => { + // if (newValue != -1) { + // encounter.applyPresetTarget(eventID, presetTargets[newValue], 0); + // } + // }, + // }); + + const presetEncounters = modEncounter.sim.db.getAllPresetEncounters(); new EnumPicker(this.rootElem, modEncounter, { + label: 'Encounter', + //extraCssClasses: ['encounter-picker', 'mb-0', 'pe-2', 'order-first'], extraCssClasses: ['damage-metrics', 'npc-picker'], - label: 'NPC', - labelTooltip: 'Selects a preset NPC configuration.', values: [{ name: 'Custom', value: -1 }].concat( - presetTargets.map((pe, i) => { + presetEncounters.map((pe, i) => { return { name: pe.path, value: i, @@ -42,10 +64,10 @@ export class EncounterPicker extends Component { }), ), changedEvent: (encounter: Encounter) => encounter.changeEmitter, - getValue: (encounter: Encounter) => presetTargets.findIndex(pe => equalTargetsIgnoreInputs(encounter.primaryTarget, pe.target)), + getValue: (encounter: Encounter) => presetEncounters.findIndex(pe => encounter.matchesPreset(pe)), setValue: (eventID: EventID, encounter: Encounter, newValue: number) => { if (newValue != -1) { - encounter.applyPresetTarget(eventID, presetTargets[newValue], 0); + encounter.applyPreset(eventID, presetEncounters[newValue]); } }, });