| 875 | } |
| 876 | |
| 877 | public AiPlayDecision canPlaySa(SpellAbility sa) { |
| 878 | if (!checkAiSpecificRestrictions(sa)) { |
| 879 | return AiPlayDecision.CantPlayAi; |
| 880 | } |
| 881 | if (sa instanceof WrappedAbility) { |
| 882 | return canPlaySa(((WrappedAbility) sa).getWrappedAbility()); |
| 883 | } |
| 884 | |
| 885 | if (!sa.canCastTiming(player)) { |
| 886 | return AiPlayDecision.AnotherTime; |
| 887 | } |
| 888 | |
| 889 | final Card card = sa.getHostCard(); |
| 890 | |
| 891 | // Trying to play a card that has Buyback without a Buyback cost, look for possible additional considerations |
| 892 | if (getBoolProperty(AiProps.TRY_TO_PRESERVE_BUYBACK_SPELLS) && card.hasKeyword(Keyword.BUYBACK) |
| 893 | && !sa.isBuyback() && !canPlaySpellWithoutBuyback(card, sa)) { |
| 894 | return AiPlayDecision.NeedsToPlayCriteriaNotMet; |
| 895 | } |
| 896 | |
| 897 | // TODO before suspending some spells try to predict if relevant targets can be expected |
| 898 | if (sa.getApi() != null) { |
| 899 | |
| 900 | String msg = "AiController:canPlaySa: AI checks for if can PlaySa"; |
| 901 | Breadcrumb bread = new Breadcrumb(msg); |
| 902 | bread.setData("Api", sa.getApi().toString()); |
| 903 | bread.setData("Card", card.getName()); |
| 904 | bread.setData("SA", sa.toString()); |
| 905 | Sentry.addBreadcrumb(bread); |
| 906 | |
| 907 | // add Extra for debugging |
| 908 | Sentry.setExtra("Card", card.getName()); |
| 909 | Sentry.setExtra("SA", sa.toString()); |
| 910 | |
| 911 | boolean canPlay = SpellApiToAi.Converter.get(sa).canPlayWithSubs(player, sa).willingToPlay(); |
| 912 | |
| 913 | // remove added extra |
| 914 | Sentry.removeExtra("Card"); |
| 915 | Sentry.removeExtra("SA"); |
| 916 | |
| 917 | if (!canPlay) { |
| 918 | return AiPlayDecision.CantPlayAi; |
| 919 | } |
| 920 | } else { |
| 921 | Cost payCosts = sa.getPayCosts(); |
| 922 | if (payCosts != null) { |
| 923 | ManaCost mana = payCosts.getTotalMana(); |
| 924 | if (mana.countX() > 0) { |
| 925 | // Set PayX here to maximum value. |
| 926 | final int xPay = ComputerUtilCost.setMaxXValue(sa, player, sa.isTrigger()); |
| 927 | if (xPay <= 0) { |
| 928 | return AiPlayDecision.CantAffordX; |
| 929 | } |
| 930 | } else if (mana.isZero()) { |
| 931 | // if mana is zero, but card mana cost does have X, then something is wrong |
| 932 | ManaCost cardCost = card.getManaCost(); |
| 933 | if (cardCost != null && cardCost.countX() > 0) { |
| 934 | return AiPlayDecision.CantPlayAi; |