Goodbye MustAttackEffect

This commit is contained in:
tool4EvEr
2022-04-24 14:01:59 +02:00
parent 6ff64a831d
commit ce34bebc52
15 changed files with 49 additions and 240 deletions

View File

@@ -58,6 +58,9 @@ import forge.util.Expressions;
import forge.util.MyRandom;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.maps.LinkedHashMapToAmount;
import forge.util.maps.MapToAmount;
import forge.util.maps.MapToAmountUtil;
/**
@@ -689,33 +692,18 @@ public class AiAttackController {
}
GameEntity prefDefender = defs.contains(defendingOpponent) ? defendingOpponent : defs.get(0);
// Attempt to see if there's a defined entity that must be attacked strictly this turn...
GameEntity entity = ai.getMustAttackEntityThisTurn();
if (nextTurn || entity == null) {
// ...or during the attacking creature controller's turn
entity = ai.getMustAttackEntity();
// 1. assault the opponent if you can kill him
if (bAssault) {
return prefDefender;
}
if (null != entity) {
int n = defs.indexOf(entity);
if (-1 == n) {
System.out.println("getMustAttackEntity() or getMustAttackEntityThisTurn() returned something not in defenders.");
return prefDefender;
}
return entity;
} else {
// 1. assault the opponent if you can kill him
if (bAssault) {
return prefDefender;
}
// 2. attack planeswalkers
List<Card> pwDefending = c.getDefendingPlaneswalkers();
if (!pwDefending.isEmpty()) {
final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending);
return pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
} else {
return prefDefender;
}
// 2. attack planeswalkers
List<Card> pwDefending = c.getDefendingPlaneswalkers();
if (!pwDefending.isEmpty()) {
final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending);
return pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
}
return prefDefender;
}
final boolean LOG_AI_ATTACKS = false;
@@ -786,30 +774,41 @@ public class AiAttackController {
// because creatures not chosen can't attack.
if (!nextTurn) {
for (final Card attacker : this.attackers) {
boolean mustAttack = false;
// TODO this might result into trying to attack the wrong player
GameEntity mustAttackDef = null;
if (attacker.isGoaded()) {
mustAttack = true;
// TODO this might result into trying to attack the wrong player
mustAttackDef = defender;
} else if (attacker.getSVar("MustAttack").equals("True")) {
mustAttack = true;
mustAttackDef = defender;
} else if (attacker.hasSVar("EndOfTurnLeavePlay")
&& isEffectiveAttacker(ai, attacker, combat, defender)) {
mustAttack = true;
mustAttackDef = defender;
} else if (seasonOfTheWitch) {
//TODO: if there are other ways to tap this creature (like mana creature), then don't need to attack
mustAttack = true;
mustAttackDef = defender;
} else {
final List<GameEntity> e = StaticAbilityMustAttack.entitiesMustAttack(attacker);
if (!e.isEmpty()) {
mustAttack = true;
// TODO switch defender if there's one without a cost or it's not the specific player
} else if (attacker.getController().getMustAttackEntityThisTurn() != null &&
CombatUtil.getAttackCost(ai.getGame(), attacker, defender) == null) {
mustAttack = true;
final List<GameEntity> attackRequirements = StaticAbilityMustAttack.entitiesMustAttack(attacker);
if (attackRequirements.contains(attacker)) {
// TODO add cost check here and switch defender if there's one without a cost
// must attack anything
mustAttackDef = defender;
// next check if there's also a specific defender to attack, so don't count them
attackRequirements.removeAll(new CardCollection(attacker));
}
final MapToAmount<GameEntity> amounts = new LinkedHashMapToAmount<>();
amounts.addAll(attackRequirements);
while (!amounts.isEmpty()) {
// check defenders in order of maximum requirements
GameEntity mustAttackDefMaybe = MapToAmountUtil.max(amounts).getKey();
if (canAttackWrapper(attacker, mustAttackDefMaybe) && CombatUtil.getAttackCost(ai.getGame(), attacker, mustAttackDefMaybe) == null) {
mustAttackDef = mustAttackDefMaybe;
break;
}
amounts.remove(mustAttackDefMaybe);
}
}
if (mustAttack) {
combat.addAttacker(attacker, defender);
if (mustAttackDef != null) {
combat.addAttacker(attacker, mustAttackDef);
attackersLeft.remove(attacker);
numForcedAttackers++;
}

View File

@@ -114,12 +114,11 @@ public class ComputerUtilCombat {
return false;
}
final List<GameEntity> mustAttackEnts = StaticAbilityMustAttack.entitiesMustAttack(attacker);
final List<GameEntity> mustAttack = StaticAbilityMustAttack.entitiesMustAttack(attacker);
//if it contains only attacker, it only has a non-specific must attack
if (mustAttackEnts.size() > 1 || (mustAttackEnts.size() == 1 && mustAttackEnts.get(0) != attacker)) {
if (!mustAttackEnts.contains(defender)) {
return false;
}
mustAttack.removeAll(new CardCollection(attacker));
if (!mustAttack.isEmpty() && !mustAttack.contains(defender)) {
return false;
}
// TODO this should be a factor but needs some alignment with AttachAi

View File

@@ -111,7 +111,6 @@ public enum SpellApiToAi {
.put(ApiType.MoveCounter, CountersMoveAi.class)
.put(ApiType.MultiplePiles, CannotPlayAi.class)
.put(ApiType.MultiplyCounter, CountersMultiplyAi.class)
.put(ApiType.MustAttack, MustAttackAi.class)
.put(ApiType.MustBlock, MustBlockAi.class)
.put(ApiType.Mutate, MutateAi.class)
.put(ApiType.NameCard, ChooseCardNameAi.class)

View File

@@ -1635,8 +1635,6 @@ public class AttachAi extends SpellAbilityAi {
if (keyword.endsWith("CARDNAME can't attack.") || keyword.equals("Defender")
|| keyword.endsWith("CARDNAME can't attack or block.")) {
return card.getNetCombatDamage() >= 1 && ComputerUtilCombat.canAttackNextTurn(card);
} else if (keyword.endsWith("CARDNAME attacks each turn if able.") || keyword.endsWith("CARDNAME attacks each combat if able.")) {
return !ai.getCreaturesInPlay().isEmpty() && ComputerUtilCombat.canAttackNextTurn(card) && CombatUtil.canBlock(card, true);
} else if (keyword.endsWith("CARDNAME can't block.")) {
return CombatUtil.canBlock(card, true);
} else if (keyword.endsWith("CARDNAME's activated abilities can't be activated.")) {

View File

@@ -1,35 +0,0 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class MustAttackAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
// disabled for the AI for now. Only for Gideon Jura at this time.
return false;
}
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
// AI should only activate this during Human's turn
// TODO - implement AI
return false;
}
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
*/
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
boolean chance;
// TODO - implement AI
chance = false;
return chance;
}
}

View File

@@ -166,10 +166,6 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return false;
}
return ph.isPlayerTurn(ai) || (combat != null && combat.isAttacking(card) && card.getNetCombatDamage() > 0);
} else if (keyword.endsWith("CARDNAME attacks each turn if able.")
|| keyword.endsWith("CARDNAME attacks each combat if able.")) {
return !ph.isPlayerTurn(ai) && CombatUtil.canAttack(card, ai) && CombatUtil.canBeBlocked(card, ai)
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
} else if (keyword.endsWith("CARDNAME can't be regenerated.")) {
if (card.getShieldCount() > 0) {
return true;