showMove(action, sourceBoardPosition, targetBoardPosition) {
    let showDuration = 0.0;

    const scene = this.getScene();
    const gameLayer = scene && scene.getGameLayer();

    // show movement if positions are different
    if (gameLayer != null && sourceBoardPosition != null && targetBoardPosition != null && this.canChangeState() && !UtilsPosition.getPositionsAreEqual(sourceBoardPosition, targetBoardPosition)) {
      this.stopShowingMove();
      this.stopShowingTeleport();

      const sourceTileMapPosition = UtilsEngine.transformBoardToTileMap(sourceBoardPosition);
      const targetTileMapPosition = UtilsEngine.transformBoardToTileMap(targetBoardPosition);
      const moveTileCount = this.getMoveTileCount(sourceBoardPosition, targetBoardPosition);
      const moveDuration = this.getMoveDuration(moveTileCount);
      const animMoveAction = this.getAnimationActionFromAnimResource('walk') || this.getAnimationActionFromAnimResource('run') || this.getAnimationActionFromAnimResource('move');
      const animMoveDuration = animMoveAction != null ? animMoveAction.getDuration() : 0.0;
      const animMoveCorrection = animMoveDuration * CONFIG.ENTITY_MOVE_CORRECTION;
      const actualDuration = animMoveDuration * (moveTileCount + 1) - animMoveCorrection;

      // set show duration
      showDuration += Math.max(moveDuration, actualDuration + animMoveCorrection);

      // set the initial position in case of any movement conflicts
      this.setPosition(sourceTileMapPosition);

      // set final move position
      this.setMovePosition(targetTileMapPosition);

      // set moving state
      this.setIsMoving(true);

      // setup final method
      const onFinishMove = function () {
        // set the final position in case of any movement conflicts
        this.setPosition(targetTileMapPosition);

        // stop moving
        this.setIsMoving(false);

        // emit the completion of the move
        gameLayer.getEventBus().trigger(EVENTS.after_show_move, { type: EVENTS.after_show_move, action });
      }.bind(this);

      this.whenResourcesReady(this.getCardResourceRequestId()).then((cardResourceRequestId) => {
        if (!this.getAreResourcesValid(cardResourceRequestId)) return; // card has changed
        if (this.canChangeState()) {
          this.showNoState();

          // set the auto z order offset temporarily
          // (used by the base sprite's auto z order method)
          this.setAutoZOrderOffset(0.1);

          // flip, position, and set as moving
          if (sourceBoardPosition.x !== targetBoardPosition.x) {
            this.entitySprite.setFlippedX(sourceBoardPosition.x > targetBoardPosition.x);
          }

          // don't show readiness while moving
          this.removeReadinessVisualTag();

          // emit the start of the move
          gameLayer.getEventBus().trigger(EVENTS.before_show_move, { type: EVENTS.before_show_move, action });

          // walking sound
          const sfx_walk = this.getSoundResource() && this.getSoundResource().walk;
          audio_engine.current().play_effect(sfx_walk, false);

          // compose sequence
          const sequenceSteps = [];

          // compose animation
          let animationSequence;
          if (animMoveAction != null) {
            const animationSteps = [];
            for (let i = 0; i < moveTileCount + 1; i++) {
              animationSteps.push(animMoveAction.clone());
            }
            animationSequence = cc.targetedAction(this.entitySprite, cc.sequence(animationSteps).speed(actualDuration / moveDuration));
          }

          // spawn move and animations in parallel
          if (animationSequence != null) {
            sequenceSteps.push(cc.spawn(
              cc.moveTo(moveDuration, targetTileMapPosition),
              animationSequence,
            ));
          } else {
            sequenceSteps.push(cc.moveTo(moveDuration, targetTileMapPosition));
          }

          sequenceSteps.push(cc.callFunc(function () {
            this._showMoveAction = null;
            onFinishMove();
            this.showNextState();
          }, this));

          // run sequence
          const moveSequence = cc.sequence(sequenceSteps);
          moveSequence.setOnCancelledCallback(onFinishMove);
          this._showMoveAction = moveSequence;
          this.runAction(moveSequence);
        } else {
          onFinishMove();
        }
      });
    }

    return showDuration;
  }
Shows entity movement.
@param {SDK.MoveAction} action
@param {Vec2} sourceBoardPosition
@param {Vec2} targetBoardPosition
@returns {Number} duration of show