Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/wowsims/wotlk
Browse files Browse the repository at this point in the history
  • Loading branch information
NerdEgghead committed Jan 8, 2024
2 parents 181424f + 64801da commit 80c3c43
Show file tree
Hide file tree
Showing 87 changed files with 6,858 additions and 9,908 deletions.
12 changes: 9 additions & 3 deletions proto/apl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ message APLListItem {
APLAction action = 3; // The action to be performed.
}

// NextIndex: 19
// NextIndex: 20
message APLAction {
APLValue condition = 1; // If set, action will only execute if value is true or != 0.

Expand Down Expand Up @@ -76,8 +76,11 @@ message APLAction {
APLActionTriggerICD trigger_icd = 11;
APLActionItemSwap item_swap = 17;

// Class or Spec-specific actions
APLActionCatOptimalRotationAction cat_optimal_rotation_action = 18;
// Class or Spec-specific actions
APLActionCatOptimalRotationAction cat_optimal_rotation_action = 18;

// Internal use only, not exposed in UI.
APLActionCustomRotation custom_rotation = 19;
}
}

Expand Down Expand Up @@ -277,6 +280,9 @@ message APLActionCatOptimalRotationAction {
bool flower_weave = 9;
}

message APLActionCustomRotation {
}

///////////////////////////////////////////////////////////////////////////
// VALUES
///////////////////////////////////////////////////////////////////////////
Expand Down
10 changes: 7 additions & 3 deletions proto/deathknight.proto
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ message Deathknight {
Presence starting_presence = 10;
BloodTap blood_tap = 11;

bool use_ams = 13;
double avg_ams_success_rate = 14;
double avg_ams_hit = 15;
bool use_ams = 13 [deprecated = true];
double avg_ams_success_rate = 14 [deprecated = true];
double avg_ams_hit = 15 [deprecated = true];

bool auto_rotation = 16;

Expand Down Expand Up @@ -288,6 +288,10 @@ message Deathknight {
bool new_drw = 7;

double disease_downtime = 8;

bool use_ams = 9;
double avg_ams_success_rate = 10;
double avg_ams_hit = 11;
}
Options options = 3;
}
Expand Down
11 changes: 4 additions & 7 deletions sim/common/wotlk/nibelung.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package wotlk

import (
"time"

"github.com/wowsims/wotlk/sim/core"
"github.com/wowsims/wotlk/sim/core/stats"
"time"
)

var valkyrStats = stats.Stats{
Expand Down Expand Up @@ -71,12 +72,8 @@ func (valkyr *ValkyrPet) Initialize() {}

func (valkyr *ValkyrPet) Reset(_ *core.Simulation) {}

func (valkyr *ValkyrPet) OnGCDReady(sim *core.Simulation) {
target := valkyr.CurrentTarget

if valkyr.smite.CanCast(sim, target) {
valkyr.smite.Cast(sim, target)
}
func (valkyr *ValkyrPet) ExecuteCustomRotation(sim *core.Simulation) {
valkyr.smite.Cast(sim, valkyr.CurrentTarget)
}

func (valkyr *ValkyrPet) GetPet() *core.Pet {
Expand Down
4 changes: 4 additions & 0 deletions sim/core/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ type Agent interface {
// Should return nil when the config doesn't match any custom behaviors.
NewAPLValue(rot *APLRotation, config *proto.APLValue) APLValue
NewAPLAction(rot *APLRotation, config *proto.APLAction) APLActionImpl

// Implements custom rotation behavior. Usually for pets and targets but can be used
// for players too.
ExecuteCustomRotation(sim *Simulation)
}

type ActionID struct {
Expand Down
24 changes: 20 additions & 4 deletions sim/core/apl.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ func (rot *APLRotation) doAndRecordWarnings(warningsList *[]string, isPrepull bo
rot.parsingPrepull = false
}

func (unit *Unit) newCustomRotation() *APLRotation {
return unit.newAPLRotation(&proto.APLRotation{
Type: proto.APLRotation_TypeAPL,
PriorityList: []*proto.APLListItem{
{
Action: &proto.APLAction{
Action: &proto.APLAction_CustomRotation{},
},
},
},
})
}

func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {
if config == nil || !unit.IsUsingAPL {
return nil
Expand Down Expand Up @@ -120,10 +133,13 @@ func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {

// Remove MCDs that are referenced by APL actions, so that the Autocast Other Cooldowns
// action does not include them.
character := unit.Env.Raid.GetPlayerFromUnit(unit).GetCharacter()
for _, action := range rotation.allAPLActions() {
if castSpellAction, ok := action.impl.(*APLActionCastSpell); ok {
character.removeInitialMajorCooldown(castSpellAction.spell.ActionID)
agent := unit.Env.GetAgentFromUnit(unit)
if agent != nil {
character := agent.GetCharacter()
for _, action := range rotation.allAPLActions() {
if castSpellAction, ok := action.impl.(*APLActionCastSpell); ok {
character.removeInitialMajorCooldown(castSpellAction.spell.ActionID)
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion sim/core/apl_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (rot *APLRotation) newAPLActionImpl(config *proto.APLAction) APLActionImpl
return nil
}

customAction := rot.unit.Env.Raid.GetPlayerFromUnit(rot.unit).NewAPLAction(rot, config)
customAction := rot.unit.Env.GetAgentFromUnit(rot.unit).NewAPLAction(rot, config)
if customAction != nil {
return customAction
}
Expand Down Expand Up @@ -177,6 +177,10 @@ func (rot *APLRotation) newAPLActionImpl(config *proto.APLAction) APLActionImpl
return rot.newActionTriggerICD(config.GetTriggerIcd())
case *proto.APLAction_ItemSwap:
return rot.newActionItemSwap(config.GetItemSwap())

case *proto.APLAction_CustomRotation:
return rot.newActionCustomRotation(config.GetCustomRotation())

default:
return nil
}
Expand Down
35 changes: 35 additions & 0 deletions sim/core/apl_actions_misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"fmt"
"time"

"github.com/wowsims/wotlk/sim/core/proto"
)
Expand Down Expand Up @@ -161,3 +162,37 @@ func (action *APLActionItemSwap) Execute(sim *Simulation) {
func (action *APLActionItemSwap) String() string {
return fmt.Sprintf("Item Swap(%s)", action.swapSet)
}

type APLActionCustomRotation struct {
defaultAPLActionImpl
unit *Unit
agent Agent

lastExecutedAt time.Duration
}

func (rot *APLRotation) newActionCustomRotation(config *proto.APLActionCustomRotation) APLActionImpl {
agent := rot.unit.Env.GetAgentFromUnit(rot.unit)
if agent == nil {
panic("Agent not found for custom rotation")
}

return &APLActionCustomRotation{
unit: rot.unit,
agent: agent,
}
}
func (action *APLActionCustomRotation) Reset(sim *Simulation) {
action.lastExecutedAt = -1
}
func (action *APLActionCustomRotation) IsReady(sim *Simulation) bool {
// Prevent infinite loops by only allowing this action to be performed once at each timestamp.
return action.lastExecutedAt != sim.CurrentTime
}
func (action *APLActionCustomRotation) Execute(sim *Simulation) {
action.lastExecutedAt = sim.CurrentTime
action.agent.ExecuteCustomRotation(sim)
}
func (action *APLActionCustomRotation) String() string {
return "Custom Rotation()"
}
14 changes: 13 additions & 1 deletion sim/core/apl_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,19 @@ func (rot *APLRotation) GetAPLSpell(spellId *proto.ActionID) *Spell {
}
}
} else {
spell = rot.unit.GetSpell(actionID)
// Prefer spells marked with APL, but fallback to unmarked spells.
var aplSpell *Spell
for _, s := range rot.unit.Spellbook {
if s.ActionID.SameAction(actionID) && s.Flags.Matches(SpellFlagAPL) {
aplSpell = s
break
}
}
if aplSpell == nil {
spell = rot.unit.GetSpell(actionID)
} else {
spell = aplSpell
}
}

if spell == nil {
Expand Down
2 changes: 1 addition & 1 deletion sim/core/apl_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (rot *APLRotation) newAPLValue(config *proto.APLValue) APLValue {
return nil
}

customValue := rot.unit.Env.Raid.GetPlayerFromUnit(rot.unit).NewAPLValue(rot, config)
customValue := rot.unit.Env.GetAgentFromUnit(rot.unit).NewAPLValue(rot, config)
if customValue != nil {
return customValue
}
Expand Down
24 changes: 11 additions & 13 deletions sim/core/cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,13 @@ func (cast *Cast) EffectiveTime() time.Duration {
type CastFunc func(*Simulation, *Unit)
type CastSuccessFunc func(*Simulation, *Unit) bool

func (spell *Spell) castFailureHelper(sim *Simulation, gracefulFailure bool, message string, vals ...any) bool {
func (spell *Spell) castFailureHelper(sim *Simulation, message string, vals ...any) bool {
if sim.CurrentTime < 0 && spell.Unit.IsUsingAPL {
spell.Unit.Rotation.ValidationWarning(fmt.Sprintf(spell.ActionID.String()+" failed to cast: "+message, vals...))
} else if gracefulFailure {
} else {
if sim.Log != nil && !spell.Flags.Matches(SpellFlagNoLogs) {
spell.Unit.Log(sim, fmt.Sprintf(spell.ActionID.String()+" failed to cast: "+message, vals...))
}
} else {
panic(fmt.Sprintf(spell.ActionID.String()+" failed to cast: "+message, vals...))
}
return false
}
Expand All @@ -94,13 +92,13 @@ func (spell *Spell) makeCastFunc(config CastConfig) CastSuccessFunc {

if spell.ExtraCastCondition != nil {
if !spell.ExtraCastCondition(sim, target) {
return spell.castFailureHelper(sim, true, "extra spell condition")
return spell.castFailureHelper(sim, "extra spell condition")
}
}

if spell.Cost != nil {
if !spell.Cost.MeetsRequirement(spell) {
return spell.castFailureHelper(sim, true, spell.Cost.CostFailureReason(sim, spell))
return spell.castFailureHelper(sim, spell.Cost.CostFailureReason(sim, spell))
}
}

Expand All @@ -113,26 +111,26 @@ func (spell *Spell) makeCastFunc(config CastConfig) CastSuccessFunc {
if config.CD.Timer != nil {
// By panicking if spell is on CD, we force each sim to properly check for their own CDs.
if !spell.CD.IsReady(sim) {
return spell.castFailureHelper(sim, false, "still on cooldown for %s, curTime = %s", spell.CD.TimeToReady(sim), sim.CurrentTime)
return spell.castFailureHelper(sim, "still on cooldown for %s, curTime = %s", spell.CD.TimeToReady(sim), sim.CurrentTime)
}
spell.CD.Set(sim.CurrentTime + spell.CurCast.CastTime + spell.CD.Duration)
}

if config.SharedCD.Timer != nil {
// By panicking if spell is on CD, we force each sim to properly check for their own CDs.
if !spell.SharedCD.IsReady(sim) {
return spell.castFailureHelper(sim, false, "still on shared cooldown for %s, curTime = %s", spell.SharedCD.TimeToReady(sim), sim.CurrentTime)
return spell.castFailureHelper(sim, "still on shared cooldown for %s, curTime = %s", spell.SharedCD.TimeToReady(sim), sim.CurrentTime)
}
spell.SharedCD.Set(sim.CurrentTime + spell.CurCast.CastTime + spell.SharedCD.Duration)
}

// By panicking if spell is on CD, we force each sim to properly check for their own CDs.
if spell.CurCast.GCD != 0 && !spell.Unit.GCD.IsReady(sim) {
return spell.castFailureHelper(sim, false, "GCD on cooldown for %s, curTime = %s", spell.Unit.GCD.TimeToReady(sim), sim.CurrentTime)
return spell.castFailureHelper(sim, "GCD on cooldown for %s, curTime = %s", spell.Unit.GCD.TimeToReady(sim), sim.CurrentTime)
}

if hc := spell.Unit.Hardcast; hc.Expires > sim.CurrentTime {
return spell.castFailureHelper(sim, false, "casting/channeling %v for %s, curTime = %s", hc.ActionID, hc.Expires-sim.CurrentTime, sim.CurrentTime)
return spell.castFailureHelper(sim, "casting/channeling %v for %s, curTime = %s", hc.ActionID, hc.Expires-sim.CurrentTime, sim.CurrentTime)
}

if effectiveTime := spell.CurCast.EffectiveTime(); effectiveTime != 0 {
Expand Down Expand Up @@ -204,14 +202,14 @@ func (spell *Spell) makeCastFuncSimple() CastSuccessFunc {
return func(sim *Simulation, target *Unit) bool {
if spell.ExtraCastCondition != nil {
if !spell.ExtraCastCondition(sim, target) {
return spell.castFailureHelper(sim, true, "extra spell condition")
return spell.castFailureHelper(sim, "extra spell condition")
}
}

if spell.CD.Timer != nil {
// By panicking if spell is on CD, we force each sim to properly check for their own CDs.
if !spell.CD.IsReady(sim) {
return spell.castFailureHelper(sim, false, "still on cooldown for %s, curTime = %s", spell.CD.TimeToReady(sim), sim.CurrentTime)
return spell.castFailureHelper(sim, "still on cooldown for %s, curTime = %s", spell.CD.TimeToReady(sim), sim.CurrentTime)
}

spell.CD.Set(sim.CurrentTime + spell.CD.Duration)
Expand All @@ -220,7 +218,7 @@ func (spell *Spell) makeCastFuncSimple() CastSuccessFunc {
if spell.SharedCD.Timer != nil {
// By panicking if spell is on CD, we force each sim to properly check for their own CDs.
if !spell.SharedCD.IsReady(sim) {
return spell.castFailureHelper(sim, false, "still on shared cooldown for %s, curTime = %s", spell.SharedCD.TimeToReady(sim), sim.CurrentTime)
return spell.castFailureHelper(sim, "still on shared cooldown for %s, curTime = %s", spell.SharedCD.TimeToReady(sim), sim.CurrentTime)
}

spell.SharedCD.Set(sim.CurrentTime + spell.SharedCD.Duration)
Expand Down
2 changes: 1 addition & 1 deletion sim/core/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func ColorIntersects(g proto.GemColor, o proto.GemColor) bool {
return true
}
if g == proto.GemColor_GemColorMeta {
return o == proto.GemColor_GemColorUnknown
return o == proto.GemColor_GemColorMeta
}
if g == proto.GemColor_GemColorRed {
return o == proto.GemColor_GemColorOrange || o == proto.GemColor_GemColorPurple
Expand Down
18 changes: 18 additions & 0 deletions sim/core/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ func (env *Environment) finalize(raidProto *proto.Raid, _ *proto.Encounter, raid

for _, target := range env.Encounter.Targets {
target.finalize()
if target.AI != nil {
target.Rotation = target.newCustomRotation()
}
}

for _, party := range env.Raid.Parties {
Expand All @@ -156,6 +159,7 @@ func (env *Environment) finalize(raidProto *proto.Raid, _ *proto.Encounter, raid
character.Finalize()
for _, pet := range character.Pets {
pet.Finalize()
pet.Rotation = pet.newCustomRotation()
}
}
}
Expand Down Expand Up @@ -257,6 +261,20 @@ func (env *Environment) NextTarget(target *Unit) *Target {
func (env *Environment) NextTargetUnit(target *Unit) *Unit {
return &env.NextTarget(target).Unit
}
func (env *Environment) GetAgentFromUnit(unit *Unit) Agent {
raidAgent := env.Raid.GetPlayerFromUnit(unit)
if raidAgent != nil {
return raidAgent
}

for _, target := range env.Encounter.Targets {
if unit == &target.Unit {
return target
}
}

return nil
}

func (env *Environment) GetUnit(ref *proto.UnitReference, contextUnit *Unit) *Unit {
if ref == nil {
Expand Down
3 changes: 3 additions & 0 deletions sim/core/mana.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func (character *Character) EnableResumeAfterManaWait(callback func(sim *Simulat
if callback == nil {
panic("attempted to setup a mana tick callback that was nil")
}
if character.IsUsingAPL {
return
}
character.OnManaTick = func(sim *Simulation) {
if character.FinishedWaitingForManaAndGCDReady(sim) {
callback(sim)
Expand Down
2 changes: 2 additions & 0 deletions sim/core/pet.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func NewPet(name string, owner *Character, baseStats stats.Stats, statInheritanc
PseudoStats: stats.NewPseudoStats(),
auraTracker: newAuraTracker(),
Metrics: NewUnitMetrics(),
IsUsingAPL: true,

StatDependencyManager: stats.NewStatDependencyManager(),
},
Expand Down Expand Up @@ -268,3 +269,4 @@ func (pet *Pet) GetCharacter() *Character {
func (pet *Pet) AddRaidBuffs(_ *proto.RaidBuffs) {}
func (pet *Pet) AddPartyBuffs(_ *proto.PartyBuffs) {}
func (pet *Pet) ApplyTalents() {}
func (pet *Pet) OnGCDReady(_ *Simulation) {}
1 change: 1 addition & 0 deletions sim/core/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func NewTarget(options *proto.Target, targetIndex int32) *Target {
preset := GetPresetTargetWithID(options.Id)
if preset != nil && preset.AI != nil {
target.AI = preset.AI()
target.IsUsingAPL = true
}

return target
Expand Down
Loading

0 comments on commit 80c3c43

Please sign in to comment.