Merge branch 'psychic_battle' into 'master'

Add Psychic Battle

See merge request core-developers/forge!4439
This commit is contained in:
Michael Kamensky
2021-04-09 07:09:52 +00:00
6 changed files with 45 additions and 13 deletions

View File

@@ -35,6 +35,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
final List<SpellAbility> sas = getTargetSpells(sa); final List<SpellAbility> sas = getTargetSpells(sa);
final boolean remember = sa.hasParam("RememberTargetedCard"); final boolean remember = sa.hasParam("RememberTargetedCard");
final Player activator = sa.getActivatingPlayer(); final Player activator = sa.getActivatingPlayer();
final Player chooser = sa.hasParam("Chooser") ? getDefinedPlayersOrTargeted(sa, "Chooser").get(0) : sa.getActivatingPlayer();
final MagicStack stack = activator.getGame().getStack(); final MagicStack stack = activator.getGame().getStack();
for (final SpellAbility tgtSA : sas) { for (final SpellAbility tgtSA : sas) {
@@ -45,7 +46,6 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
} }
SpellAbilityStackInstance changingTgtSI = si; SpellAbilityStackInstance changingTgtSI = si;
Player chooser = sa.getActivatingPlayer();
// Redirect rules read 'you MAY choose new targets' ... okay! // Redirect rules read 'you MAY choose new targets' ... okay!
// TODO: Don't even ask to change targets, if the SA and subs don't actually have targets // TODO: Don't even ask to change targets, if the SA and subs don't actually have targets
@@ -79,7 +79,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
// gets the divied value from old target // gets the divied value from old target
Integer div = oldTargetBlock.getDividedValue(oldTarget); Integer div = oldTargetBlock.getDividedValue(oldTarget);
newTargetBlock.remove(oldTarget); newTargetBlock.remove(oldTarget);
replaceIn.updateTarget(newTargetBlock); replaceIn.updateTarget(newTargetBlock, sa.getHostCard());
// 3. test if updated choices would be correct. // 3. test if updated choices would be correct.
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null); GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
@@ -88,10 +88,10 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
if (div != null) { if (div != null) {
newTargetBlock.addDividedAllocation(newTarget, div); newTargetBlock.addDividedAllocation(newTarget, div);
} }
replaceIn.updateTarget(newTargetBlock); replaceIn.updateTarget(newTargetBlock, sa.getHostCard());
} }
else { else {
replaceIn.updateTarget(oldTargetBlock); replaceIn.updateTarget(oldTargetBlock, sa.getHostCard());
} }
} }
else { else {
@@ -109,7 +109,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
changingTgtSA.addDividedAllocation(choice, div); changingTgtSA.addDividedAllocation(choice, div);
} }
changingTgtSI.updateTarget(changingTgtSA.getTargets()); changingTgtSI.updateTarget(changingTgtSA.getTargets(), sa.getHostCard());
} }
else if (sa.hasParam("DefinedMagnet")){ else if (sa.hasParam("DefinedMagnet")){
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null); GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
@@ -117,7 +117,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
int div = changingTgtSA.getTotalDividedValue(); int div = changingTgtSA.getTotalDividedValue();
changingTgtSA.resetTargets(); changingTgtSA.resetTargets();
changingTgtSA.getTargets().add(newTarget); changingTgtSA.getTargets().add(newTarget);
changingTgtSI.updateTarget(changingTgtSA.getTargets()); changingTgtSI.updateTarget(changingTgtSA.getTargets(), sa.getHostCard());
if (changingTgtSA.isDividedAsYouChoose()) { if (changingTgtSA.isDividedAsYouChoose()) {
changingTgtSA.addDividedAllocation(newTarget, div); changingTgtSA.addDividedAllocation(newTarget, div);
} }
@@ -127,9 +127,9 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
// Update targets, with a potential new target // Update targets, with a potential new target
Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, sa.getHostCard(), sa) : null; Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, sa.getHostCard(), sa) : null;
// TODO Creature.Other might not work yet as it should // TODO Creature.Other might not work yet as it should
TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseNewTargetsFor(changingTgtSA, filter, false); TargetChoices newTarget = chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
if (null != newTarget) { if (null != newTarget) {
changingTgtSI.updateTarget(newTarget); changingTgtSI.updateTarget(newTarget, sa.getHostCard());
} }
} }
} }

View File

@@ -250,7 +250,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
return playersWithValidTargets; return playersWithValidTargets;
} }
public void updateTarget(TargetChoices target) { public void updateTarget(TargetChoices target, Card cause) {
if (target != null) { if (target != null) {
TargetChoices oldTarget = tc; TargetChoices oldTarget = tc;
tc = target; tc = target;
@@ -280,11 +280,16 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
runParams.put(AbilityKey.Target, tgt); runParams.put(AbilityKey.Target, tgt);
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false); getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
} }
// Only run BecomesTargetOnce when at least one target is changed
if (!distinctObjects.isEmpty()) {
runParams = AbilityKey.newMap(); runParams = AbilityKey.newMap();
runParams.put(AbilityKey.SourceSA, ability);
runParams.put(AbilityKey.Targets, distinctObjects); runParams.put(AbilityKey.Targets, distinctObjects);
runParams.put(AbilityKey.Cause, cause);
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false); getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false);
} }
} }
}
public boolean addTriggeringObject(AbilityKey trigObj, Object value) { public boolean addTriggeringObject(AbilityKey trigObj, Object value) {
if (!triggeringObjects.containsKey(trigObj)) { if (!triggeringObjects.containsKey(trigObj)) {

View File

@@ -62,6 +62,9 @@ public class TriggerBecomesTargetOnce extends Trigger {
if (!matchesValidParam("ValidTarget", runParams.get(AbilityKey.Targets))) { if (!matchesValidParam("ValidTarget", runParams.get(AbilityKey.Targets))) {
return false; return false;
} }
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
return false;
}
return true; return true;
} }

View File

@@ -380,6 +380,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
game.getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
} }
runParams.put(AbilityKey.Targets, tc); runParams.put(AbilityKey.Targets, tc);
runParams.put(AbilityKey.Cause, s.getHostCard());
game.getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false);
} }
} }
@@ -392,6 +393,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
game.getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
runParams.put(AbilityKey.Targets, Lists.newArrayList(sp.getTargetCard())); runParams.put(AbilityKey.Targets, Lists.newArrayList(sp.getTargetCard()));
runParams.put(AbilityKey.Cause, sp.getHostCard());
game.getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false);
} }

View File

@@ -0,0 +1,22 @@
Name:Psychic Battle
ManaCost:3 U U
Types:Enchantment
T:Mode$ BecomesTargetOnce | ValidCause$ Card.notnamedPsychic Battle | TriggerZones$ Battlefield | Execute$ TrigReveal | TriggerDescription$ Whenever a player chooses one or more targets, each player reveals the top card of their library. The player who reveals the card with the highest converted mana cost may change the target or targets. If two or more cards are tied for highest cost, the target or targets remain unchanged. Changing targets this way doesn't trigger abilities of permanents named Psychic Battle.
SVar:TrigReveal:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBDig | SubAbility$ DBChangeTargets
SVar:DBDig:DB$ Dig | Defined$ Remembered | DigNum$ 1 | Reveal$ True | NoMove$ True | RememberRevealed$ True | SubAbility$ DBCheckLibrary
SVar:DBCheckLibrary:DB$ Branch | BranchConditionSVar$ NumRememberedCard | TrueSubAbility$ DBCheckImprinted
SVar:DBCheckImprinted:DB$ Branch | BranchConditionSVar$ NumImprintedCard | TrueSubAbility$ DBCompareCMC | FalseSubAbility$ DBImprint | SubAbility$ DBCleanupRemembered
SVar:DBCompareCMC:DB$ Branch | BranchConditionSVar$ CMCRememberedCard | BranchConditionSVarCompare$ GTCMCImprintedCard | TrueSubAbility$ DBImprintForget | FalseSubAbility$ DBCompareCMC2
SVar:DBCompareCMC2:DB$ Branch | BranchConditionSVar$ CMCRememberedCard | BranchConditionSVarCompare$ EQCMCImprintedCard | TrueSubAbility$ DBCleanupImprinted
SVar:DBImprintForget:DB$ Cleanup | ClearImprinted$ True | SubAbility$ DBImprint
SVar:DBImprint:DB$ Pump | ImprintCards$ Remembered
SVar:DBChangeTargets:DB$ ChangeTargets | Defined$ TriggeredSourceSA | Chooser$ ImprintedOwner | Optional$ True | ConditionDefined$ Imprinted | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanupImprinted
SVar:DBCleanupRemembered:DB$ Cleanup | ClearRemembered$ True
SVar:DBCleanupImprinted:DB$ Cleanup | ClearImprinted$ True
SVar:NumRememberedCard:Remembered$Valid Card
SVar:NumImprintedCard:Imprinted$Valid Card
SVar:CMCRememberedCard:Remembered$CardManaCost
SVar:CMCImprintedCard:Imprinted$CardManaCost
SVar:NonStackingEffect:True
AI:RemoveDeck:Random
Oracle:Whenever a player chooses one or more targets, each player reveals the top card of their library. The player who reveals the card with the highest converted mana cost may change the target or targets. If two or more cards are tied for highest cost, the target or targets remain unchanged. Changing targets this way doesn't trigger abilities of permanents named Psychic Battle.

View File

@@ -1130,7 +1130,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
} }
final TargetChoices oldTarget = sa.getTargets(); final TargetChoices oldTarget = sa.getTargets();
final TargetSelection select = new TargetSelection(this, sa); final TargetSelection select = new TargetSelection(this, sa);
sa.resetTargets(); sa.clearTargets();
if (select.chooseTargets(oldTarget.size(), Lists.newArrayList(oldTarget.getDividedValues()), filter, optional)) { if (select.chooseTargets(oldTarget.size(), Lists.newArrayList(oldTarget.getDividedValues()), filter, optional)) {
return sa.getTargets(); return sa.getTargets();
} else { } else {