Skip to content

Commit

Permalink
Merge pull request #3605 from wowsims/apl
Browse files Browse the repository at this point in the history
Add Wait Until APL action
  • Loading branch information
jimmyt857 authored Sep 3, 2023
2 parents a7f0959 + 7376e39 commit 43892cc
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 50 deletions.
7 changes: 6 additions & 1 deletion proto/apl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ message APLListItem {
APLAction action = 3; // The action to be performed.
}

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

Expand All @@ -49,6 +49,7 @@ message APLAction {
APLActionCancelAura cancel_aura = 10;
APLActionTriggerICD trigger_icd = 11;
APLActionWait wait = 4;
APLActionWaitUntil wait_until = 14;
}
}

Expand Down Expand Up @@ -188,6 +189,10 @@ message APLActionWait {
APLValue duration = 1;
}

message APLActionWaitUntil {
APLValue condition = 1;
}

///////////////////////////////////////////////////////////////////////////
// VALUES
///////////////////////////////////////////////////////////////////////////
Expand Down
30 changes: 6 additions & 24 deletions sim/core/apl.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ type APLRotation struct {
prepullActions []*APLAction
priorityList []*APLAction

// Current strict sequence
strictSequence *APLAction
// Action currently controlling this rotation (only used for certain actions, such as StrictSequence).
controllingAction APLActionImpl

// Used inside of actions/value to determine whether they will occur during the prepull or regular rotation.
parsingPrepull bool
Expand Down Expand Up @@ -143,7 +143,7 @@ func (rot *APLRotation) allPrepullActions() []*APLAction {
}

func (rot *APLRotation) reset(sim *Simulation) {
rot.strictSequence = nil
rot.controllingAction = nil
rot.inLoop = false
for _, action := range rot.allAPLActions() {
action.impl.Reset(sim)
Expand Down Expand Up @@ -174,37 +174,19 @@ func (apl *APLRotation) DoNextAction(sim *Simulation) {
}

if apl.unit.GCD.IsReady(sim) {
apl.unit.WaitUntil(sim, sim.CurrentTime+time.Millisecond*500)
apl.unit.WaitUntil(sim, sim.CurrentTime+time.Millisecond*50)
} else {
apl.unit.DoNothing()
}
}

func (apl *APLRotation) getNextAction(sim *Simulation) *APLAction {
if sim.CurrentTime < apl.unit.waitUntilTime {
return nil
}

if apl.strictSequence != nil {
ss := apl.strictSequence.impl.(*APLActionStrictSequence)
if ss.actions[ss.curIdx].IsReady(sim) {
return apl.strictSequence
} else if apl.unit.GCD.IsReady(sim) {
// If the GCD is ready when the next subaction isn't, it means the sequence is bad
// so reset and exit the sequence.
ss.curIdx = 0
apl.strictSequence = nil
} else {
// Return nil to wait for the GCD to become ready.
return nil
}
if apl.controllingAction != nil {
return apl.controllingAction.GetNextAction(sim)
}

for _, action := range apl.priorityList {
if action.IsReady(sim) {
if _, ok := action.impl.(*APLActionStrictSequence); ok {
apl.strictSequence = action
}
return action
}
}
Expand Down
14 changes: 10 additions & 4 deletions sim/core/apl_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ type APLActionImpl interface {
// Performs the action.
Execute(*Simulation)

// Called only while this action is controlling the rotation.
GetNextAction(sim *Simulation) *APLAction

// Pretty-print string for debugging.
String() string
}
Expand All @@ -81,10 +84,11 @@ type APLActionImpl interface {
type defaultAPLActionImpl struct {
}

func (impl defaultAPLActionImpl) GetInnerActions() []*APLAction { return nil }
func (impl defaultAPLActionImpl) GetAPLValues() []APLValue { return nil }
func (impl defaultAPLActionImpl) Finalize(*APLRotation) {}
func (impl defaultAPLActionImpl) Reset(*Simulation) {}
func (impl defaultAPLActionImpl) GetInnerActions() []*APLAction { return nil }
func (impl defaultAPLActionImpl) GetAPLValues() []APLValue { return nil }
func (impl defaultAPLActionImpl) Finalize(*APLRotation) {}
func (impl defaultAPLActionImpl) Reset(*Simulation) {}
func (impl defaultAPLActionImpl) GetNextAction(*Simulation) *APLAction { return nil }

func (rot *APLRotation) newAPLAction(config *proto.APLAction) *APLAction {
if config == nil {
Expand Down Expand Up @@ -138,6 +142,8 @@ func (rot *APLRotation) newAPLActionImpl(config *proto.APLAction) APLActionImpl
return rot.newActionTriggerICD(config.GetTriggerIcd())
case *proto.APLAction_Wait:
return rot.newActionWait(config.GetWait())
case *proto.APLAction_WaitUntil:
return rot.newActionWaitUntil(config.GetWaitUntil())
default:
return nil
}
Expand Down
73 changes: 64 additions & 9 deletions sim/core/apl_actions_core.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 @@ -208,38 +209,92 @@ type APLActionWait struct {
defaultAPLActionImpl
unit *Unit
duration APLValue

curWaitTime time.Duration
}

func (rot *APLRotation) newActionWait(config *proto.APLActionWait) APLActionImpl {
unit := rot.unit
durationVal := rot.coerceTo(rot.newAPLValue(config.Duration), proto.APLValueType_ValueTypeDuration)
if durationVal == nil {
return nil
}

return &APLActionWait{
unit: unit,
duration: rot.coerceTo(rot.newAPLValue(config.Duration), proto.APLValueType_ValueTypeDuration),
duration: durationVal,
}
}
func (action *APLActionWait) GetAPLValues() []APLValue {
return []APLValue{action.duration}
}
func (action *APLActionWait) IsReady(sim *Simulation) bool {
return action.duration != nil
return true
}

func (action *APLActionWait) Execute(sim *Simulation) {
waitUntilTime := sim.CurrentTime + action.duration.GetDuration(sim)
action.unit.waitUntilTime = waitUntilTime
action.unit.Rotation.controllingAction = action
action.curWaitTime = sim.CurrentTime + action.duration.GetDuration(sim)

if waitUntilTime > action.unit.GCD.ReadyAt() {
action.unit.WaitUntil(sim, waitUntilTime)
return
}
pa := &PendingAction{
Priority: ActionPriorityLow,
OnAction: action.unit.gcdAction.OnAction,
NextActionAt: waitUntilTime,
NextActionAt: action.curWaitTime,
}
sim.AddPendingAction(pa)
}

func (action *APLActionWait) GetNextAction(sim *Simulation) *APLAction {
if sim.CurrentTime >= action.curWaitTime {
action.unit.Rotation.controllingAction = nil
return action.unit.Rotation.getNextAction(sim)
} else {
return nil
}
}

func (action *APLActionWait) String() string {
return fmt.Sprintf("Wait(%s)", action.duration)
}

type APLActionWaitUntil struct {
defaultAPLActionImpl
unit *Unit
condition APLValue
}

func (rot *APLRotation) newActionWaitUntil(config *proto.APLActionWaitUntil) APLActionImpl {
unit := rot.unit
conditionVal := rot.coerceTo(rot.newAPLValue(config.Condition), proto.APLValueType_ValueTypeBool)
if conditionVal == nil {
return nil
}

return &APLActionWaitUntil{
unit: unit,
condition: conditionVal,
}
}
func (action *APLActionWaitUntil) GetAPLValues() []APLValue {
return []APLValue{action.condition}
}
func (action *APLActionWaitUntil) IsReady(sim *Simulation) bool {
return !action.condition.GetBool(sim)
}

func (action *APLActionWaitUntil) Execute(sim *Simulation) {
action.unit.Rotation.controllingAction = action
}

func (action *APLActionWaitUntil) GetNextAction(sim *Simulation) *APLAction {
if action.condition.GetBool(sim) {
action.unit.Rotation.controllingAction = nil
return action.unit.Rotation.getNextAction(sim)
} else {
return nil
}
}

func (action *APLActionWaitUntil) String() string {
return fmt.Sprintf("WaitUntil(%s)", action.condition)
}
24 changes: 20 additions & 4 deletions sim/core/apl_actions_sequences.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,28 @@ func (action *APLActionStrictSequence) IsReady(sim *Simulation) bool {
return true
}
func (action *APLActionStrictSequence) Execute(sim *Simulation) {
action.actions[action.curIdx].Execute(sim)
action.curIdx++
action.unit.Rotation.controllingAction = action
}
func (action *APLActionStrictSequence) GetNextAction(sim *Simulation) *APLAction {
if action.actions[action.curIdx].IsReady(sim) {
nextAction := action.actions[action.curIdx]

if action.curIdx == len(action.actions) {
action.curIdx++
if action.curIdx == len(action.actions) {
action.curIdx = 0
action.unit.Rotation.controllingAction = nil
}

return nextAction
} else if action.unit.GCD.IsReady(sim) {
// If the GCD is ready when the next subaction isn't, it means the sequence is bad
// so reset and exit the sequence.
action.curIdx = 0
action.unit.Rotation.strictSequence = nil
action.unit.Rotation.controllingAction = nil
return action.unit.Rotation.getNextAction(sim)
} else {
// Return nil to wait for the GCD to become ready.
return nil
}
}
func (action *APLActionStrictSequence) String() string {
Expand Down
3 changes: 0 additions & 3 deletions sim/core/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ type Unit struct {
waitingForEnergy float64
waitingForMana float64
waitStartTime time.Duration
waitUntilTime time.Duration

// Cached mana return values per tick.
manaTickWhileCasting float64
Expand Down Expand Up @@ -456,8 +455,6 @@ func (unit *Unit) reset(sim *Simulation, agent Agent) {
if unit.Rotation != nil {
unit.Rotation.reset(sim)
}

unit.waitUntilTime = 0
}

func (unit *Unit) startPull(sim *Simulation) {
Expand Down
4 changes: 2 additions & 2 deletions sim/hunter/TestAPL.results
Original file line number Diff line number Diff line change
Expand Up @@ -810,8 +810,8 @@ dps_results: {
dps_results: {
key: "TestAPL-AllItems-UndeadSlayer'sBlessedArmor"
value: {
dps: 5385.71714
tps: 4633.2316
dps: 5386.46454
tps: 4633.99134
}
}
dps_results: {
Expand Down
2 changes: 1 addition & 1 deletion sim/rogue/TestAssassination.results
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ character_stats_results: {
final_stats: 221
final_stats: 0
final_stats: 5636.84
final_stats: 469.94995
final_stats: 469.94994
final_stats: 2072.9756
final_stats: 221
final_stats: 94
Expand Down
2 changes: 1 addition & 1 deletion sim/rogue/TestCombat.results
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ character_stats_results: {
final_stats: 221
final_stats: 0
final_stats: 5862.3136
final_stats: 469.94995
final_stats: 469.94994
final_stats: 2164.78757
final_stats: 221
final_stats: 94
Expand Down
13 changes: 12 additions & 1 deletion ui/core/components/individual_sim_ui/apl_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
APLActionCancelAura,
APLActionTriggerICD,
APLActionWait,
APLActionWaitUntil,
APLValue,
APLActionMultishield,
} from '../../proto/apl.js';
Expand Down Expand Up @@ -402,7 +403,7 @@ const actionKindFactories: {[f in NonNullable<APLActionKind>]: ActionKindConfig<
['wait']: inputBuilder({
label: 'Wait',
submenu: ['Misc'],
shortDescription: 'Pauses the GCD for a specified amount of time.',
shortDescription: 'Pauses all APL actions for a specified amount of time.',
includeIf: (player: Player<any>, isPrepull: boolean) => !isPrepull,
newValue: () => APLActionWait.create({
duration: {
Expand All @@ -418,6 +419,16 @@ const actionKindFactories: {[f in NonNullable<APLActionKind>]: ActionKindConfig<
AplValues.valueFieldConfig('duration'),
],
}),
['waitUntil']: inputBuilder({
label: 'Wait Until',
submenu: ['Misc'],
shortDescription: 'Pauses all APL actions until the specified condition is <b>True</b>.',
includeIf: (player: Player<any>, isPrepull: boolean) => !isPrepull,
newValue: () => APLActionWaitUntil.create(),
fields: [
AplValues.valueFieldConfig('condition'),
],
}),
['changeTarget']: inputBuilder({
label: 'Change Target',
submenu: ['Misc'],
Expand Down

0 comments on commit 43892cc

Please sign in to comment.