showRevealPack(cardIds) {
    const revealPromise = new Promise((resolve, reject) => {
      this._whenRevealResolve = resolve;

      this.whenRequiredResourcesReady().then((requestId) => {
        if (!this.getAreResourcesValid(requestId)) return; // resources invalidated/unloaded

        // randomize pack before showing
        this._cardCountsById = _.countBy(cardIds, (cardId) => cardId);
        cardIds = _.unique(cardIds);
        cardIds = _.sample(cardIds, cardIds.length);

        // Must come after the unique above
        this._cardCount = cardIds.length;

        // flare
        const flare = FXLensFlareSprite.create();
        flare.setBlendFunc(cc.SRC_ALPHA, cc.ONE);
        flare.setScale(2.0);
        flare.setPulseRate(0.0);
        flare.setSpeed(2.0);
        flare.setWispSize(0.2);
        flare.setAnchorPoint(cc.p(0.5, 0.5));
        this.innerLayer.addChild(flare);

        // second energy ball
        const energyBall2 = FXEnergyBall.create();
        energyBall2.setBlendFunc(cc.SRC_ALPHA, cc.ONE);
        energyBall2.setScale(4.0);
        energyBall2.setTimeScale(5.0);
        energyBall2.setNoiseLevel(15.0);
        energyBall2.setAnchorPoint(cc.p(0.5, 0.5));
        // energyBall2.runAction(cc.actionTween(1.0, "noiseLevel", 1.0, 2.0));
        // energyBall2.runAction(cc.actionTween(1.0, "timeScale", 1.0, 2.0));
        this.innerLayer.addChild(energyBall2);

        const fireRing = FXFireRing.create();
        fireRing.setBlendFunc(cc.SRC_ALPHA, cc.ONE);
        fireRing.setAnchorPoint(cc.p(0.5, 0.5));
        fireRing.setPosition(0, 0);
        fireRing.setScale(11.0);
        fireRing.setVisible(false);
        this.innerLayer.addChild(fireRing);

        // set energy ball opacity to 255
        this.energyBall.setOpacity(255);

        // fade second energy ball in
        energyBall2.runAction(cc.fadeIn(0.2));

        // power up flare
        flare.runAction(cc.scaleTo(0.8, 8.0));

        // bloom down to baseline
        this.runAction(cc.actionTween(0.3, TweenTypes.BLOOM_INTENSITY, this._bloomIntensity + 1.0, this._bloomIntensity).easing(cc.easeExponentialOut()));

        // play explode sfx
        audio_engine.current().play_effect(RSX.sfx_ui_booster_packexplode.audio);

        // extend flare and continue sequence
        flare.runAction(cc.sequence(
          cc.actionTween(0.8, 'armLength', 0.0, 1.0),
          cc.callFunc(() => {
            this.turbine.stopRotating();
            this.centerRing.stopRotating();

            this.getFXLayer().runAction(cc.sequence(
              Shake.create(0.5, 5.0, cc.p(0, 0)),
            ));

            this.vignette.stopActionByTag(CONFIG.FADE_TAG);
            const fadeAction = cc.sequence(
              cc.fadeTo(0.2, 100),
              cc.fadeTo(0.5, 200),
            );
            fadeAction.setTag(CONFIG.FADE_TAG);
            this.vignette.runAction(fadeAction);

            this.blurredOverlay.stopActionByTag(CONFIG.FADE_TAG);
            const fadeAction2 = cc.sequence(
              cc.delayTime(0.7),
              cc.fadeIn(0.5),
            );
            fadeAction2.setTag(CONFIG.FADE_TAG);
            this.blurredOverlay.runAction(fadeAction2);

            this.lines_particles.destroy();
            this.inward_particles.destroy();

            // animate global tone curve using scene
            this.getScene().runAction(ToneCurve.create(0.2, 1.0, 0.0));

            fireRing.setVisible(true);
            fireRing.runAction(cc.sequence(
              cc.EaseExponentialOut.create(cc.actionTween(3.0, 'phase', 1.0, -0.25)),
              cc.callFunc(() => {
                fireRing.destroy();
              }),
            ));

            this.energyBallParticles.stopSystem();
            this.energyBall.runAction(cc.fadeOut(0.1));
            energyBall2.destroy();

            const explosionParticles = cc.ParticleSystem.create(RSX.explosion.plist);
            explosionParticles.setAnchorPoint(cc.p(0.5, 0.5));
            this.innerLayer.addChild(explosionParticles);

            // reveal each card in the pack
            const unlockCardPromises = [];
            for (let i = 0; i < cardIds.length; i++) {
              unlockCardPromises.push(this._showCardMoveAndReveal(cardIds[i], i));
            }

            // when all cards revealed
            Promise.all(unlockCardPromises).then(() => {
              this._unlocking = false;
              this._opened = true;
              resolve();
            }).catch((error) => {
              console.error(error);
              throw error;
            });
          }),
          cc.fadeOut(0.1),
        ));
      });
    });

    return revealPromise;
  }
Shows pack unlocking and returns a promise that resolves when all cards have been revealed.
@param cardIds
@returns {Promise}