showReveal(sdkCard, sourceScreenPosition, targetScreenPosition, moveDelay) {
    if (moveDelay == null) { moveDelay = 0.0; }

    // reset
    if (sdkCard != null) {
      this.setSdkCard(sdkCard);
    } else {
      this.hideSnapshot();
    }

    if (this.sdkCard != null) {
      return new Promise((resolve, reject) => {
        // stop running animations
        this.stopAnimations();

        // hide everything until snapshot taken
        this._containerNode.setVisible(false);

        this._isAnimationInProgress = true;

        // wait for resources
        Promise.all([
          this.whenRequiredResourcesReady(),
          this.whenResourcesReady(this.getCardResourceRequestId()),
        ])
          .spread((requiredRequestId, cardResourceRequestId) => {
            if (!this.getAreResourcesValid(requiredRequestId) || !this.getAreResourcesValid(cardResourceRequestId)) return; // load invalidated or resources changed

            this._outlineGlow.setVisible(true);
            this._outlineGlowBlurred.setVisible(true);

            // snapshot self
            this.recordSnapshot();

            const centerPosition = this.getCenterPosition();
            const cardBackgroundContentSize = this.getCardBackgroundContentSize();
            const moveDuration = (targetScreenPosition == null || cc.pSameAs(sourceScreenPosition, targetScreenPosition)) ? 0 : CONFIG.MOVE_MEDIUM_DURATION;
            const delayBeforeSetVisible = 0.0; // (moveDuration > 0) ? 0 : CONFIG.MOVE_MEDIUM_DURATION;
            const revealDuration = CONFIG.FADE_MEDIUM_DURATION * 2.0;

            // bg shadow
            this.bg_shadow = new BaseSprite(RSX.card_shadow_map.img);
            this.bg_shadow.setOpacity(0);
            this.bg_shadow.setPosition(centerPosition);
            this.addChild(this.bg_shadow, -9999);

            // particles
            var fxFluidPuff = BaseSprite.create({
              spriteIdentifier: RSX.fxFluidCardShapePuff.name,
              color: new cc.Color(100, 200, 255, 255),
              scale: 9.0,
              blendSrc: cc.SRC_ALPHA,
              blendDst: cc.ONE,
            });
            fxFluidPuff.setPosition(centerPosition);
            const fluidPuffAction = cc.sequence(
              UtilsEngine.getAnimationAction(RSX.fxFluidCardShapePuff.name, false),
              cc.fadeOut(0.1),
              cc.removeSelf(),
            );
            this.addAnimationAction(fluidPuffAction);
            fxFluidPuff.runAction(fluidPuffAction);
            this.addChild(fxFluidPuff, -1);

            const fxFluidPuff2 = BaseSprite.create({
              spriteIdentifier: RSX.fxFluidCardShapePuff.name,
              color: new cc.Color(0, 100, 255, 255),
              scale: 8.0,
              flippedX: true,
              blendSrc: cc.SRC_ALPHA,
              blendDst: cc.ONE,
            });
            fxFluidPuff2.setPosition(centerPosition);
            const fluidPuff2Action = cc.sequence(
              cc.delayTime(0.15),
              UtilsEngine.getAnimationAction(RSX.fxFluidCardShapePuff.name, false),
              cc.fadeOut(0.1),
              cc.removeSelf(),
            );
            this.addAnimationAction(fluidPuff2Action);
            fxFluidPuff2.runAction(fluidPuff2Action);
            this.addChild(fxFluidPuff2, -1);

            const particles = new cc.ParticleSystem(RSX.ptcl_card_appear.plist);
            particles.setPosVar(cc.p(cardBackgroundContentSize.width * 0.5, cardBackgroundContentSize.height * 0.5));
            particles.setPosition(centerPosition);
            particles.setAutoRemoveOnFinish(true);
            this.addChild(particles, 1);

            // glow
            this._glowMapNode.setVisible(true);
            this._glowMapNode.setOpacity(255.0);
            this._glowMapNode.stopAllActions();

            // // highlight
            // this._snapshotSprite.setLeveled(true);
            // this._snapshotSprite.setLevelsInWhite(180);
            // this._snapshotSprite.setLevelsInBlack(30);
            // this._snapshotSprite.setHighlighted(true);
            // this._snapshotSprite.setHighlightIntensity(6.0);
            this._snapshotSprite.setTint(new cc.Color(255, 255, 255, 255));

            // fade out glow map
            const glowMapFadeAction = cc.fadeOut(0.4).easing(cc.easeIn(3.0));
            this.addAnimationAction(glowMapFadeAction);
            this._glowMapNode.runAction(glowMapFadeAction);

            this._outlineGlowBlurred.setLeveled(true);
            this._outlineGlowBlurred.setLevelsInWhite(20);

            // tint and animate
            const tintAction = cc.sequence(
              cc.delayTime(revealDuration),
              cc.actionTween(revealDuration / 2.0, TweenTypes.TINT_FADE, 255.0, 0.0).easing(cc.easeOut(3.0)),
              cc.targetedAction(this._outlineGlowBlurred, cc.spawn(
                cc.actionTween(0.5, 'levelsInWhite', 20, 255),
                cc.actionTween(0.5, 'levelsInBlack', 0, 20),
                cc.targetedAction(this._outlineGlowBlurred, cc.fadeOut(0.6)),
              )),

              // cc.callFunc(function () {
              //   this._snapshotSprite.fadeOutHighlight(revealDuration);
              //   var levelsAction = cc.sequence(
              //     cc.spawn(
              //       cc.actionTween(revealDuration, "levelsInWhite", 180.0, 255.0),
              //       cc.actionTween(revealDuration, "levelsInBlack", 30.0, 0.0)
              //     ),
              //     cc.callFunc(function () {
              //       this._snapshotSprite.setLeveled(false);
              //     }.bind(this))
              //   );
              //   this.addAnimationAction(levelsAction);
              //   this._snapshotSprite.runAction(levelsAction);
              // }.bind(this))
            );
            this.addAnimationAction(tintAction);
            this._snapshotSprite.runAction(tintAction);

            // show shadow
            const shadowAction = cc.sequence(
              cc.delayTime(revealDuration),
              cc.fadeTo(CONFIG.FADE_FAST_DURATION, 150.0),
            );
            this.addAnimationAction(shadowAction);
            this.bg_shadow.runAction(shadowAction);

            // assemble show reveal sequence
            const showRevealSequence = [];

            if (sourceScreenPosition != null) {
              this.setPosition(sourceScreenPosition);
            }

            if (this.sdkCard.rarityId > SDK.Rarity.Common) {
              // Show rarity flare
              var fxFluidPuff = BaseSprite.create(RSX.fx_fluid_sphere.name);
              fxFluidPuff.setBlendFunc(cc.SRC_ALPHA, cc.ONE);
              fxFluidPuff.setScale(1.0);
              fxFluidPuff.setPosition(this.getCenterPosition());
              fxFluidPuff.setVisible(false);
              fxFluidPuff.setShaderProgram(cc.shaderCache.programForKey('Colorize'));
              this.addChild(fxFluidPuff, 0);

              const rarityFlare = new FXRarityFlareSprite();
              rarityFlare.setBlendFunc(cc.SRC_ALPHA, cc.ONE);
              rarityFlare.setPosition(this.getCenterPosition().x, this.getCenterPosition().y + 50);
              rarityFlare.setVisible(false);
              rarityFlare.setScale(10.0);
              this.addChild(rarityFlare, 10);

              // Run rarity flare action
              const rarityColor = SDK.RarityFactory.rarityForIdentifier(this.sdkCard.rarityId).color;

              const fluidSpriteAnimation = UtilsEngine.getAnimationAction(RSX.fx_fluid_sphere.name);

              rarityFlare.setColor(rarityColor);
              rarityFlare.setVisible(false);

              fxFluidPuff.setColor(rarityColor);
              fxFluidPuff.setVisible(false);
              fxFluidPuff.setFlippedX(Math.random() > 0.5);
              fxFluidPuff.setFlippedY(Math.random() > 0.5);

              // Begin the rarity effect (this happens outside the main sequence so it doesn't stall resolving the returned promise)
              this.runAction(cc.targetedAction(fxFluidPuff, cc.sequence(
                cc.spawn(
                  cc.callFunc(((rarityFlare, fxFluidPuff) => {
                    rarityFlare.setVisible(true);
                    rarityFlare.setOpacity(0);
                    rarityFlare.setPhase(1.0);
                    rarityFlare.runAction(
                      cc.sequence(
                        cc.fadeIn(0.5),
                        cc.delayTime(1.0 + Math.random() * 0.6),
                        cc.spawn(
                          cc.actionTween(1.0, 'phase', 1.0, 0.25),
                        ),
                        cc.removeSelf(),
                      ),
                    );

                    fxFluidPuff.setVisible(true);
                    fxFluidPuff.setOpacity(0);
                  }).bind(this, rarityFlare, fxFluidPuff)),
                  cc.spawn(
                    cc.fadeIn(1.0),
                    fluidSpriteAnimation,
                    cc.sequence(
                      cc.delayTime(fluidSpriteAnimation.getDuration() - CONFIG.ANIMATE_FAST_DURATION),
                      cc.fadeOut(CONFIG.ANIMATE_FAST_DURATION),
                    ),
                  ),
                ),
                cc.callFunc(() => {
                  // explosion particles
                  const particles = cc.ParticleSystem.create(RSX.core_gem_particles.plist);
                  particles.setStartColor(rarityColor);
                  particles.setEndColor(rarityColor);
                  particles.setPosition(this.getCenterPosition().x, this.getCenterPosition().y - 25);
                  this.addChild(particles, 10);

                  // gem quick rarity polar flare
                  const polarFlare = FXFbmPolarFlareSprite.create();
                  polarFlare.phase = 1.0;
                  polarFlare.timeScale = 1.0;
                  polarFlare.setScale(0.75);
                  polarFlare.setBlendFunc(cc.SRC_ALPHA, cc.ONE);
                  polarFlare.setTextureRect(cc.rect(0, 0, 100, 100));
                  polarFlare.flareColor = rarityColor;
                  polarFlare.setPosition(this.getCenterPosition().x, this.getCenterPosition().y - 25);
                  this.addChild(polarFlare, 10);
                  polarFlare.runAction(cc.sequence(
                    cc.actionTween(1.0, 'phase', 0.01, 1.0).easing(cc.easeExponentialIn()),
                    cc.callFunc(() => {
                      particles.stopSystem();
                    }),
                    cc.actionTween(1.0, 'phase', 1.00, 0.01).easing(cc.easeExponentialOut()),
                    cc.fadeOut(0.1),
                    cc.delayTime(1.0), // let particles die out
                    cc.callFunc(() => {
                      particles.destroy();
                    }),
                    cc.removeSelf(),
                  ));

                  // this._voronoiEffectSprite = new VoronoiPrismaticSprite();
                  // this._voronoiEffectSprite.setVisible(false);
                  // this._voronoiEffectSprite.setPosition(this.getCenterPosition().x, this.getCenterPosition().y);
                  // this._voronoiEffectSprite.setTextureRect(cc.rect(0, 0, 200, 200));
                  // this.addChild(this._voronoiEffectSprite, 10);
                  // // this._voronoiPrismaticSprite.setScale(0.5);
                  //
                  // // start voronoi at half phase to show it exploding out
                  // var voronoiPhase = 0.0;
                  // this._voronoiEffectSprite.setPhase(voronoiPhase);
                  // this._voronoiEffectSprite.runAction(cc.sequence(
                  //   cc.delayTime(0.5),
                  //   cc.show(),
                  //   cc.spawn(
                  //     cc.fadeTo(0.2, 255.0).easing(cc.easeCubicActionOut()),
                  //     cc.actionTween(0.5, voronoiPhase, 1.0)
                  //   ),
                  //   cc.hide(),
                  //   cc.removeSelf(),
                  //   cc.callFunc(function(){
                  //     this._voronoiEffectSprite.destroy()
                  //   }.bind(this))
                  // ))
                  //
                }),
                cc.removeSelf(),
              )));
            }

            // Add a delay to account for movement to reveal sequence
            showRevealSequence.push(cc.delayTime(revealDuration * 1.5 + moveDelay + delayBeforeSetVisible));

            if (targetScreenPosition != null) {
              showRevealSequence.push(cc.moveTo(moveDuration, targetScreenPosition).easing(cc.easeOut(2.0)));
            }

            // Final clean up in reveal sequence
            showRevealSequence.push(cc.callFunc(() => {
              this.factionNameLabel.setVisible(true);
              // this._glowMapNode.setVisible(false);
              // hide snapshot and show animated card
              this.fadeOutSnapshot(0.1);
            }));

            // fade out outline
            const outlineDelay = (this.sdkCard.rarityId > SDK.Rarity.Common) ? 0.25 : 0.0;
            const outlineGlowAction = cc.sequence(
              cc.delayTime(outlineDelay),
              cc.fadeOut(0.8 + outlineDelay).easing(cc.easeExponentialOut()),
              cc.hide(),
            );
            showRevealSequence.push(cc.targetedAction(this._outlineGlow, outlineGlowAction));
            this.addAnimationAction(outlineGlowAction);

            // add slight extra delay
            // showRevealSequence.push(cc.delayTime(revealDuration));

            // animations complete
            showRevealSequence.push(cc.callFunc(() => {
              resolve();
              this._isAnimationInProgress = false;
            }));

            const showRevealAction = cc.sequence(showRevealSequence);
            this.addAnimationAction(showRevealAction);
            this.runAction(showRevealAction);
          });
      })
        .catch((error) => { EventBus.getInstance().trigger(EVENTS.error, error); });
    }
    return Promise.resolve();
  }
Shows a card being revealed.
@param sdkCard
@param sourceScreenPosition
@param targetScreenPosition
@param [moveDelay=0.0] delay before card is moved to target position
@returns {Promise}