ScoreForIntentFollowup = function (card, targetPosition, cardIntents, allFollowupCards, bestFollowupPositions) {
  const cardId = card.getBaseCardId();
  const scoreAndFollowups = {
    score: 0,
    position: targetPosition,
    followupPositions: [],
  };

  // cards can only have a single follow-up intent
  const validIntents = cardIntents != null ? CardIntent.filterIntentsByIntentType(cardIntents, CardIntentType.Followup) : CardIntent.getIntentsByIntentType(cardId, CardIntentType.Followup);
  const intent = validIntents[0];
  if (intent != null) {
    // inject properties into card to simulate card being played
    // this ensures the card's followup will have the correct source position
    card.setPosition(targetPosition);
    if (card instanceof Spell) {
      card.setApplyEffectPosition(targetPosition);
    }

    if (allFollowupCards == null) {
      const rootCard = card;

      // define method to find all followup cards recursively
      const generateFollowupCards = function (card) {
        const gameSession = card.getGameSession();
        let followupCards = [];
        const followups = card.getFollowups();
        if (followups != null && followups.length > 0) {
          for (let i = 0, il = followups.length; i < il; i++) {
            const followup = followups[i];
            const followupCard = gameSession.getExistingCardFromIndexOrCreateCardFromData(followup);
            if (followupCard != null) {
              setupFollowupCard(rootCard, card, followupCard, i);
              followupCards.push(followupCard);
            }

            // recursive find followups for followup
            followupCards = followupCards.concat(generateFollowupCards(followupCard));
          }
        }
        return followupCards;
      };

      // generate all followup cards in sequence starting at root card
      allFollowupCards = generateFollowupCards(card);
    }

    if (bestFollowupPositions == null) {
      // store reference to root followup positions list
      bestFollowupPositions = scoreAndFollowups.followupPositions;
    }

    // process all followup intents
    // intents that do not have an intent of followup should always process first
    const followupIntents = intent.followups;
    const followupIntentsWithIntentFollowup = _.groupBy(followupIntents, (intent) => intent.type === CardIntentType.Followup);
    const intentsFollowup = followupIntentsWithIntentFollowup.true;
    const notIntentsFollowup = followupIntentsWithIntentFollowup.false;
    if (notIntentsFollowup != null && notIntentsFollowup.length > 0) {
      findBestFollowup(targetPosition, notIntentsFollowup, scoreAndFollowups, allFollowupCards, bestFollowupPositions);
    }
    if (intentsFollowup != null && intentsFollowup.length > 0) {
      findBestFollowup(targetPosition, intentsFollowup, scoreAndFollowups, allFollowupCards, bestFollowupPositions);
    }

    // finished and at root card
    if (!card.getIsFollowup()) {
      // cleanup card and followup cards
      card.setPosition(null);
      if (card instanceof Spell) {
        card.setApplyEffectPosition(null);
      }
      for (let i = 0, il = allFollowupCards.length; i < il; i++) {
        cleanFollowupCard(allFollowupCards[i]);
      }
    }
  }

  return scoreAndFollowups;
}
Returns the Followup score for a card at a target position.
@param {Card} card
@param {Vec2} targetPosition
@param {Array} [cardIntents=null] forced card intents (won't use card's own card intents)
@param {Array} [allFollowupCards=null] all followup cards (don't pass in from outside, used internally)
@param {Array} [bestFollowupPositions=null] all followup positions in use (don't pass in from outside, used internally)
@returns {Object} object with "score" value and "followups" array (which is a recursive object of the same format)
@static
@public