genshinsim / gcsim

monte carlo combat simulation for genshin impact
MIT License
271 stars 89 forks source link

NextQueueItemIsValid to use active char instead of next char #2174

Closed srliao closed 4 weeks ago

srliao commented 1 month ago

allows for char to also block swap if swap is not a valid next action

Charlie-Zheng commented 4 weeks ago

The follow changes fix the problem

diff --git a/pkg/simulation/run.go b/pkg/simulation/run.go
index addf1f6a5..010c219d7 100644
--- a/pkg/simulation/run.go
+++ b/pkg/simulation/run.go
@@ -152,7 +152,7 @@ func queuePhase(s *Simulation) (stateFn, error) {
    }
    // append swap if called for char is not active
    // check if NoChar incase this is some special action that does not require a character
-   if next.Char != keys.NoChar && next.Char != s.C.Player.ActiveChar().Base.Key {
+   if next.Char != keys.NoChar && next.Char != s.C.Player.ActiveChar().Base.Key && next.Action != action.ActionSwap {
        s.queue = append(s.queue, &action.Eval{
            Char:   next.Char,
            Action: action.ActionSwap,
@@ -256,13 +256,21 @@ func executeActionPhase(s *Simulation) (stateFn, error) {
        // wrap the error for more context
        return nil, fmt.Errorf("error encountered on %v executing %v: %w", q.Char.String(), q.Action.String(), err)
    }
+   next := skipUntilCanQueue
    //TODO: this check here is probably unnecessary
    if l := s.popQueue(); l > 0 {
        // don't go back to queue if there are more actions already queued
-       return actionReadyCheckPhase, nil
+       next = actionReadyCheckPhase
    }

-   return skipUntilCanQueue, nil
+   if q.Action == action.ActionSwap {
+       // if the action is swap, we need to advance frames by the SwapDelay so that the ready checks are being done on the correct character
+       // this is only needed because implicit swap will queue two actions immediately.
+       // an explicit swap will advance until the swap's CanQueueAfter before queue and calling ready check on the next action
+       return s.advanceFrames(s.C.Player.Delays.Swap, next)
+   }
+
+   return next, nil
 }

 func skipUntilCanQueue(s *Simulation) (stateFn, error) {