showNextAction() {
    let actionShowDelay = 0;

    // clean up existing action
    let sdkActionInterface = this._currentSdkActionInterface;
    if (sdkActionInterface != null) {
      var action = sdkActionInterface.getSdkAction();

      // keep a record of the most recent non modifier action we've shown with the highest index (later in the game state)
      if ((!this._lastShownSdkStateRecordingAction || action.getIndex() > this._lastShownSdkStateRecordingAction.getIndex())
        && !(action instanceof ModifierAction) && !(action instanceof SDK.ApplyModifierAction) && !(action instanceof SDK.RemoveModifierAction)) {
        this._lastShownSdkStateRecordingAction = action;
        this.updateShowingSdkNodeStats();
      }

      this.getEventBus().trigger(EVENTS.after_show_action, {
        type: EVENTS.after_show_action,
        action,
        sourceSdkCard: this._currentActionSourceSdkCard,
        sourceNode: this._currentActionSourceNode,
        targetSdkCard: this._currentActionTargetSdkCard,
        targetNode: this._currentActionTargetNode,
      });

      this._resetCurrentAction();

      // only show actions until we've shown all the actions that end the game
      if (this._currentSdkStepInterface.getSdkStep() === SDK.GameSession.getInstance().getGameEndingStep()) {
        if (this._currentSdkStepInterface.actionsEndingGame != null) {
          this._currentSdkStepInterface.actionsEndingGame = _.without(this._currentSdkStepInterface.actionsEndingGame, action);
          if (this._currentSdkStepInterface.actionsEndingGame.length === 0) {
            this._currentActionSequence = [];
          }
        }
      }
    }

    const numActions = this._currentActionSequence.length;
    if (numActions > 0 && this._nextActionIndex < numActions) {
      // get next action and advance action index
      sdkActionInterface = this._currentSdkActionInterface = this._currentActionSequence[this._nextActionIndex];
      this._nextActionIndex++;
    } else {
      sdkActionInterface = null;
    }

    // update the pseudo-action sequence
    if (sdkActionInterface == null || sdkActionInterface.isSequenceRoot) {
      this._setCurrentSequenceRootSdkActionInterface(sdkActionInterface);
    }

    if (sdkActionInterface != null) {
      var action = sdkActionInterface.getSdkAction();

      // delay showing action
      if (action instanceof SDK.EndTurnAction) {
        actionShowDelay += this.showEndTurn(action);
      } else if (action instanceof SDK.StartTurnAction) {
        actionShowDelay += this.showStartTurn(action);
      } else if (action.getIsAutomatic()) {
        // automatic action emphasis
        const card = action.getSource();
        if (card instanceof SDK.Unit && this._lastCardWithAutomaticAction !== card) {
          this._lastCardWithAutomaticAction = card;
          if (action instanceof SDK.MoveAction) {
            // show emphasis moving with card node
            actionShowDelay += this._showEmphasisSprite(EmphasisTriggeredSprite.create(), this.getNodeForSdkCard(card));
          } else {
            // show emphasis at location of action
            const boardPosition = action.getSourcePosition();
            const tilePosition = UtilsEngine.transformBoardToTileMap(boardPosition);
            tilePosition.y += CONFIG.TILESIZE * 0.75;
            actionShowDelay += this._showEmphasisSprite(EmphasisTriggeredSprite.create(), tilePosition);
          }
        }
      }
    }

    // rebind usability
    this.bottomDeckLayer.bindHandUsability();
    this.player1Layer.getSignatureCardNode().updateUsability();
    this.player2Layer.getSignatureCardNode().updateUsability();

    // show the current action
    if (sdkActionInterface != null) {
      var action = sdkActionInterface.getSdkAction();
      const myAction = action.getOwnerId() === this._player.getPlayerId();

      // show explicit play card and delay showing of action
      if (!action.getIsImplicit()
        && (action instanceof SDK.PlayCardFromHandAction || action instanceof SDK.PlaySignatureCardAction)
        && (SDK.GameSession.getInstance().getIsSpectateMode() || !myAction)) {
        let animateDuration;
        let showDuration;
        if (SDK.GameSession.getInstance().getIsSpectateMode()) {
          animateDuration = CONFIG.REPLAY_PLAYED_CARD_TRANSITION_DURATION;
          showDuration = CONFIG.REPLAY_PLAYED_CARD_SHOW_DURATION;
          actionShowDelay += CONFIG.REPLAY_PLAYED_CARD_DELAY;
        } else if (!myAction) {
          animateDuration = CONFIG.OPPONENT_PLAYED_CARD_TRANSITION_DURATION;
          showDuration = CONFIG.OPPONENT_PLAYED_CARD_SHOW_DURATION;
          actionShowDelay += CONFIG.OPPONENT_PLAYED_CARD_DELAY;
        }

        // show skippable play card
        this.showSkippablePlayCard(action, animateDuration, showDuration, actionShowDelay, () => {
          this._showAction(sdkActionInterface);
        });
      } else if (actionShowDelay > 0.0) {
        this._setupActionForShowSequence = this.runAction(cc.sequence(
          cc.delayTime(actionShowDelay),
          cc.callFunc(function () {
            this._setupActionForShowSequence = null;
            this._showAction(sdkActionInterface);
          }, this),
        ));
      } else {
        // show action immediately
        this._showAction(sdkActionInterface);
      }
    } else {
      this.showNextStep();
    }
  }
Shows the next action in the list of current actions, starting an automatic sequence of showing each action until no more are left to show.