private static String millStackDescription(SpellAbility sa, AbilityFactory af) {
        HashMap<String, String> params = af.getMapParams();
        StringBuilder sb = new StringBuilder();
        int numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa);

        ArrayList<Player> tgtPlayers;

        Target tgt = af.getAbTgt();
        if (tgt != null)
            tgtPlayers = tgt.getTargetPlayers();
        else
            tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);

        if (!(sa instanceof Ability_Sub))
            sb.append(sa.getSourceCard().getName()).append(" - ");
        else
            sb.append(" ");

        String conditionDesc = params.get("ConditionDescription");
        if (conditionDesc != null)
            sb.append(conditionDesc).append(" ");

        for (Player p : tgtPlayers)
            sb.append(p.toString()).append(" ");

        String destination = params.get("Destination");
        if (destination == null || destination.equals(Constant.Zone.Graveyard))
            sb.append("mills ");
        else if (destination.equals(Constant.Zone.Exile))
            sb.append("exiles ");
        sb.append(numCards);
        sb.append(" card");
        if (numCards != 1) sb.append("s");
        sb.append(" from his or her library.");

        Ability_Sub abSub = sa.getSubAbility();
        if (abSub != null) {
            sb.append(abSub.getStackDescription());
        }

        return sb.toString();
    }