mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
Merge branch 'counters' into 'master'
CountersAi: skip for only one against Vorinclex See merge request core-developers/forge!5536
This commit is contained in:
@@ -2253,6 +2253,7 @@ public class AiController {
|
||||
return true;
|
||||
}
|
||||
|
||||
// AI logic for choosing which replacement effect to apply happens here.
|
||||
public ReplacementEffect chooseSingleReplacementEffect(List<ReplacementEffect> list) {
|
||||
// no need to choose anything
|
||||
if (list.size() <= 1) {
|
||||
@@ -2291,7 +2292,8 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
// AI logic for choosing which replacement effect to apply happens here.
|
||||
// TODO always lower counters with Vorinclex first, might turn it from 1 to 0 as final
|
||||
|
||||
return Iterables.getFirst(list, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -1488,8 +1488,8 @@ public class ComputerUtilCard {
|
||||
if (combat.isAttacking(c) && opp.getLife() > 0) {
|
||||
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true);
|
||||
int pumpedDmg = ComputerUtilCombat.damageIfUnblocked(pumped, opp, pumpedCombat, true);
|
||||
int poisonOrig = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
|
||||
int poisonPumped = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
|
||||
int poisonOrig = ComputerUtilCombat.poisonIfUnblocked(c, ai);
|
||||
int poisonPumped = ComputerUtilCombat.poisonIfUnblocked(pumped, ai);
|
||||
|
||||
// predict Infect
|
||||
if (pumpedDmg == 0 && c.hasKeyword(Keyword.INFECT)) {
|
||||
|
||||
@@ -235,17 +235,25 @@ public class ComputerUtilCombat {
|
||||
* @return a int.
|
||||
*/
|
||||
public static int poisonIfUnblocked(final Card attacker, final Player attacked) {
|
||||
if (!attacked.canReceiveCounters(CounterEnumType.POISON)) {
|
||||
return 0;
|
||||
}
|
||||
int damage = attacker.getNetCombatDamage();
|
||||
int poison = 0;
|
||||
damage += predictPowerBonusOfAttacker(attacker, null, null, false);
|
||||
if (attacker.hasKeyword(Keyword.INFECT)) {
|
||||
int pd = predictDamageTo(attacked, damage, attacker, true);
|
||||
// opponent can always order it so that he gets 0
|
||||
if (pd == 1 && Iterables.any(attacker.getController().getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) {
|
||||
pd = 0;
|
||||
}
|
||||
poison += pd;
|
||||
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
poison += pd;
|
||||
}
|
||||
}
|
||||
if (attacker.hasKeyword(Keyword.POISONOUS) && (damage > 0)) {
|
||||
if (attacker.hasKeyword(Keyword.POISONOUS) && damage > 0) {
|
||||
// TODO need to check for magnitude 1, each of their triggers could be replaced to 0
|
||||
poison += attacker.getKeywordMagnitude(Keyword.POISONOUS);
|
||||
}
|
||||
return poison;
|
||||
|
||||
@@ -20,15 +20,20 @@ package forge.ai.ability;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
|
||||
@@ -40,7 +45,7 @@ import forge.util.Aggregates;
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class CountersAi {
|
||||
public abstract class CountersAi extends SpellAbilityAi {
|
||||
// An AbilityFactory subclass for Putting or Removing Counters on Cards.
|
||||
|
||||
/**
|
||||
@@ -54,10 +59,17 @@ public abstract class CountersAi {
|
||||
* a {@link java.lang.String} object.
|
||||
* @param amount
|
||||
* a int.
|
||||
* @param newParam TODO
|
||||
* @return a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
public static Card chooseCursedTarget(final CardCollectionView list, final String type, final int amount) {
|
||||
public static Card chooseCursedTarget(final CardCollectionView list, final String type, final int amount, final Player ai) {
|
||||
Card choice;
|
||||
|
||||
// opponent can always order it so that he gets 0
|
||||
if (amount == 1 && Iterables.any(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.equals("M1M1")) {
|
||||
// try to kill the best killable creature, or reduce the best one
|
||||
// but try not to target a Undying Creature
|
||||
@@ -87,6 +99,7 @@ public abstract class CountersAi {
|
||||
*/
|
||||
public static Card chooseBoonTarget(final CardCollectionView list, final String type) {
|
||||
Card choice = null;
|
||||
|
||||
if (type.equals("P1P1")) {
|
||||
choice = ComputerUtilCard.getBestCreatureAI(list);
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
if (!c.canReceiveCounters(counterType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) {
|
||||
// has negative counter it would double
|
||||
@@ -146,7 +145,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||
if (!aiList.isEmpty()) {
|
||||
// counter type list to check
|
||||
// first loyalty, then P1P!, then Charge Counter
|
||||
// first loyalty, then P1P1, then Charge Counter
|
||||
List<CounterEnumType> typeList = Lists.newArrayList(CounterEnumType.LOYALTY, CounterEnumType.P1P1, CounterEnumType.CHARGE);
|
||||
for (CounterEnumType type : typeList) {
|
||||
// enough targets
|
||||
@@ -182,7 +181,6 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
|
||||
private void addTargetsByCounterType(final Player ai, final SpellAbility sa, final CardCollection list,
|
||||
final CounterType type) {
|
||||
|
||||
CardCollection newList = CardLists.filter(list, CardPredicates.hasCounter(type));
|
||||
if (newList.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
|
||||
boolean opponentPoison = false;
|
||||
|
||||
for (final Player o : ai.getOpponents()) {
|
||||
opponentPoison |= o.getPoisonCounters() >= 1;
|
||||
opponentPoison |= o.getPoisonCounters() > 0 && o.canReceiveCounters(CounterEnumType.POISON);
|
||||
hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card crd) {
|
||||
|
||||
@@ -53,7 +53,7 @@ import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
public class CountersPutAi extends SpellAbilityAi {
|
||||
public class CountersPutAi extends CountersAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -179,7 +179,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
if (abTgt.canTgtCreature()) {
|
||||
// try to kill creature with -1/-1 counters if it can
|
||||
// receive counters, execpt it has undying
|
||||
// receive counters, except it has undying
|
||||
CardCollection oppCreat = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
|
||||
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1));
|
||||
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
|
||||
@@ -549,7 +549,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (sa.isCurse()) {
|
||||
choice = CountersAi.chooseCursedTarget(list, type, amount);
|
||||
choice = chooseCursedTarget(list, type, amount, ai);
|
||||
} else {
|
||||
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||
for (Card c : list) {
|
||||
@@ -564,15 +564,15 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (abCost == null
|
||||
|| (ph.is(PhaseType.END_OF_TURN) && ph.getPlayerTurn().isOpponentOf(ai))) {
|
||||
// only use at opponent EOT unless it is free
|
||||
choice = CountersAi.chooseBoonTarget(list, type);
|
||||
choice = chooseBoonTarget(list, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Dromoka's Command")) {
|
||||
choice = CountersAi.chooseBoonTarget(list, type);
|
||||
choice = chooseBoonTarget(list, type);
|
||||
}
|
||||
} else {
|
||||
choice = CountersAi.chooseBoonTarget(list, type);
|
||||
choice = chooseBoonTarget(list, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,7 +681,6 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
sa.resetTargets();
|
||||
// target loop
|
||||
while (sa.canAddMoreTarget()) {
|
||||
|
||||
if (list.isEmpty()) {
|
||||
if (!sa.isTargetNumberValid()
|
||||
|| sa.getTargets().size() == 0) {
|
||||
@@ -693,7 +692,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (sa.isCurse()) {
|
||||
choice = CountersAi.chooseCursedTarget(list, type, amount);
|
||||
choice = chooseCursedTarget(list, type, amount, ai);
|
||||
} else {
|
||||
CardCollection lands = CardLists.filter(list, CardPredicates.Presets.LANDS);
|
||||
SpellAbility animate = sa.findSubAbilityByType(ApiType.Animate);
|
||||
@@ -702,7 +701,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
} else if ("BoonCounterOnOppCreature".equals(logic)) {
|
||||
choice = ComputerUtilCard.getWorstCreatureAI(list);
|
||||
} else {
|
||||
choice = CountersAi.chooseBoonTarget(list, type);
|
||||
choice = chooseBoonTarget(list, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,7 +837,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
// Choose targets here:
|
||||
if (sa.isCurse()) {
|
||||
if (preferred) {
|
||||
choice = CountersAi.chooseCursedTarget(list, type, amount);
|
||||
choice = chooseCursedTarget(list, type, amount, ai);
|
||||
if (choice == null && mandatory) {
|
||||
choice = Aggregates.random(list);
|
||||
}
|
||||
@@ -852,7 +851,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
} else {
|
||||
if (preferred) {
|
||||
list = ComputerUtil.getSafeTargets(ai, sa, list);
|
||||
choice = CountersAi.chooseBoonTarget(list, type);
|
||||
choice = chooseBoonTarget(list, type);
|
||||
if (choice == null && mandatory) {
|
||||
choice = Aggregates.random(list);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
if (!betterTgts.isEmpty()) {
|
||||
tgts = betterTgts;
|
||||
} else if (mandatory) {
|
||||
// no better choice but better than hiting himself
|
||||
// no better choice but better than hitting himself
|
||||
sa.getTargets().add(tgts.getFirst());
|
||||
return true;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
// need to target something, try to target allies
|
||||
PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa));
|
||||
if (!allies.isEmpty()) {
|
||||
// some ally would be uneffected
|
||||
// some ally would be unaffected
|
||||
PlayerCollection betterAllies = allies.filter(new Predicate<Player>() {
|
||||
@Override
|
||||
public boolean apply(Player input) {
|
||||
|
||||
@@ -145,7 +145,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
}
|
||||
|
||||
public boolean requirementsCheck(Game game, Map<String,String> params) {
|
||||
|
||||
if (this.isSuppressed()) {
|
||||
return false; // Effect removed by effect
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user