- DelayedTriggerAi: port over AILogic NarsetRebound and SpellCopy (the latter doesn't quite work yet and the spell somehow magically fizzles with no trace).

This commit is contained in:
Hans Mackowiak
2020-11-04 08:56:41 +00:00
committed by Michael Kamensky
parent d5639f5395
commit 9b040b063b
109 changed files with 642 additions and 540 deletions

View File

@@ -38,7 +38,6 @@ import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.game.spellability.*;
import forge.game.zone.Zone;
import forge.util.collect.FCollection;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
@@ -151,7 +150,7 @@ public class HumanPlaySpellAbility {
// is only executed or evaluated if the first argument does not suffice to determine the value of the expression
final boolean prerequisitesMet = announceValuesLikeX()
&& announceType()
&& (!mayChooseTargets || setupTargets()) // if you can choose targets, then do choose them.
&& (!mayChooseTargets || ability.setupTargets()) // if you can choose targets, then do choose them.
&& (isFree || payment.payCost(new HumanCostDecision(controller, human, ability, ability.getHostCard())));
if (!prerequisitesMet) {
@@ -190,7 +189,7 @@ public class HumanPlaySpellAbility {
// no worries here. The same thread must resolve, and by this moment ability will have been resolved already
// Triggers haven't resolved yet ??
if (mayChooseTargets) {
clearTargets(ability);
ability.clearTargets();
}
if (manaTypeConversion || manaColorConversion || keywordColor) {
manapool.restoreColorReplacements();
@@ -199,47 +198,6 @@ public class HumanPlaySpellAbility {
return true;
}
private final boolean setupTargets() {
// Skip to paying if parent ability doesn't target and has no subAbilities.
// (or trigger case where its already targeted)
SpellAbility currentAbility = ability;
final Card source = ability.getHostCard();
do {
final TargetRestrictions tgt = currentAbility.getTargetRestrictions();
if (tgt != null && tgt.doesTarget()) {
clearTargets(currentAbility);
Player targetingPlayer;
if (currentAbility.hasParam("TargetingPlayer")) {
final FCollection<Player> candidates = AbilityUtils.getDefinedPlayers(source, currentAbility.getParam("TargetingPlayer"), currentAbility);
// activator chooses targeting player
targetingPlayer = ability.getActivatingPlayer().getController().chooseSingleEntityForEffect(
candidates, currentAbility, "Choose the targeting player", null);
} else {
targetingPlayer = ability.getActivatingPlayer();
}
currentAbility.setTargetingPlayer(targetingPlayer);
if (!targetingPlayer.getController().chooseTargetsFor(currentAbility)) {
return false;
}
}
final AbilitySub subAbility = currentAbility.getSubAbility();
if (subAbility != null) {
// This is necessary for "TargetsWithDefinedController$ ParentTarget"
subAbility.setParent(currentAbility);
}
currentAbility = subAbility;
} while (currentAbility != null);
return true;
}
public final void clearTargets(final SpellAbility ability) {
final TargetRestrictions tg = ability.getTargetRestrictions();
if (tg != null) {
ability.resetTargets();
tg.calculateStillToDivide(ability.getParam("DividedAsYouChoose"), ability.getHostCard(), ability);
}
}
private void rollbackAbility(final Zone fromZone, final int zonePosition, CostPayment payment) {
// cancel ability during target choosing
final Game game = ability.getActivatingPlayer().getGame();
@@ -251,7 +209,7 @@ public class HumanPlaySpellAbility {
game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null, null);
}
clearTargets(ability);
ability.clearTargets();
ability.resetOnceResolved();
payment.refundPayment();

View File

@@ -1671,7 +1671,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
List<SpellAbility> orderedSAs = activePlayerSAs;
if (activePlayerSAs.size() > 1) {
final String firstStr = activePlayerSAs.get(0).toString();
boolean needPrompt = false;
boolean needPrompt = !activePlayerSAs.get(0).isTrigger();
// for the purpose of pre-ordering, no need for extra granularity
Integer idxAdditionalInfo = firstStr.indexOf(" [");
@@ -1682,6 +1682,10 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
SpellAbility currentSa = activePlayerSAs.get(i);
String saStr = currentSa.toString();
// if current SA isn't a trigger and it uses Targeting, try to show prompt
if (!currentSa.isTrigger() && currentSa.usesTargeting()) {
needPrompt = true;
}
if (!needPrompt && !saStr.equals(firstStr)) {
needPrompt = true; // prompt by default unless all abilities
// are the same
@@ -1738,6 +1742,15 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
if (next.isTrigger()) {
HumanPlay.playSpellAbility(this, player, next);
} else {
if (next.isCopied()) {
// copied spell always add to stack
player.getGame().getStackZone().add(next.getHostCard());
// TODO check if static abilities needs to be run for things affecting the copy?
if (next.isMayChooseNewTargets() && !next.setupTargets()) {
// if targets can't be done, remove copy from existence
next.getHostCard().ceaseToExist();
}
}
player.getGame().getStack().add(next);
}
}