diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/afr/VrondissRageOfAncientsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/afr/VrondissRageOfAncientsTest.java new file mode 100644 index 00000000000..482e8d1fa97 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/afr/VrondissRageOfAncientsTest.java @@ -0,0 +1,46 @@ +package org.mage.test.cards.single.afr; + +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + +/** + * {@link VrondissRageOfAncientsTest Vrondriss, Rage of Ancients} + * Enrage — Whenever Vrondiss, Rage of Ancients is dealt damage, + * you may create a 5/4 red and green Dragon Spirit creature token with + * “When this creature deals damage, sacrifice it.” + * Whenever you roll one or more dice, you may have Vrondiss deal 1 damage to itself. + */ +public class VrondissRageOfAncientsTest extends CardTestMultiPlayerBase { + + /** + * Reported bug: https://github.com/magefree/mage/issues/8189 + * + * Chaos Dragon attacking would cause Vrondiss to trigger off of every player's roll, not just Vrondiss' controller. + * + * {@link mage.cards.c.ChaosDragon Chaos Dragon} + * At the beginning of combat on your turn, each player rolls a d20. + * If one or more opponents had the highest result, + * Chaos Dragon can’t attack those players or planeswalkers they control this combat. + */ + @Test + public void testChaosDragonInteraction() { + addCard(Zone.BATTLEFIELD, playerA, "Vrondiss, Rage of Ancients"); + addCard(Zone.BATTLEFIELD, playerA, "Chaos Dragon"); + + setStrictChooseMode(true); + + attack(1, playerA, "Chaos Dragon", playerB); + setDieRollResult(playerA, 10); + setDieRollResult(playerB, 11); + // setDieRollResult(playerC, 12); NOTE: Range of influence of 1, so playerC does not have to roll + setDieRollResult(playerD, 13); + + // Set choice for Vrondiss triggered ability + setChoice(playerA, "No"); + // Should only be one trigger since it should only trigger off of playerA's roll + + execute(); + assertAllCommandsUsed(); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/OneOrMoreDiceRolledTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OneOrMoreDiceRolledTriggeredAbility.java index 28b0aa6b99c..1afeb97784a 100644 --- a/Mage/src/main/java/mage/abilities/common/OneOrMoreDiceRolledTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OneOrMoreDiceRolledTriggeredAbility.java @@ -8,6 +8,8 @@ import mage.game.events.DiceRolledEvent; import mage.game.events.GameEvent; /** + * Controller rolls one or more dice. + * * @author weirddan455 */ public class OneOrMoreDiceRolledTriggeredAbility extends TriggeredAbilityImpl { @@ -36,7 +38,7 @@ public class OneOrMoreDiceRolledTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { + if (!isControlledBy(event.getTargetId())) { return false; } int maxRoll = ((DiceRolledEvent) event) diff --git a/Mage/src/main/java/mage/game/events/DiceRolledEvent.java b/Mage/src/main/java/mage/game/events/DiceRolledEvent.java index 1c4a0b51fd1..8c01ea8fa47 100644 --- a/Mage/src/main/java/mage/game/events/DiceRolledEvent.java +++ b/Mage/src/main/java/mage/game/events/DiceRolledEvent.java @@ -4,6 +4,7 @@ import mage.abilities.Ability; import java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * @author TheElk801 @@ -13,8 +14,19 @@ public class DiceRolledEvent extends GameEvent { private final int sides; private final List results = new ArrayList<>(); // Integer for numerical and PlanarDieRollResult for planar - public DiceRolledEvent(int sides, List results, Ability source) { - super(EventType.DICE_ROLLED, source.getControllerId(), source, source.getControllerId()); + /** + * The target ID is used to keep track of the distinction between the player who controls the ability that + * started the dice roll and the player who does the rolling. + *

+ * The only times this distinction matters is for Chaos Dragon and Ricochet. + * + * @param sides + * @param results + * @param source + * @param targetId The player who rolled the die + */ + public DiceRolledEvent(int sides, List results, Ability source, UUID targetId) { + super(EventType.DICE_ROLLED, targetId, source, source.getControllerId()); this.sides = sides; this.results.addAll(results); } diff --git a/Mage/src/main/java/mage/game/events/RollDiceEvent.java b/Mage/src/main/java/mage/game/events/RollDiceEvent.java index ce45e477ac3..ddb309759c5 100644 --- a/Mage/src/main/java/mage/game/events/RollDiceEvent.java +++ b/Mage/src/main/java/mage/game/events/RollDiceEvent.java @@ -4,6 +4,8 @@ import mage.abilities.Ability; import mage.constants.RollDieType; import mage.util.CardUtil; +import java.util.UUID; + /** * @author TheElk801 */ @@ -13,8 +15,20 @@ public class RollDiceEvent extends GameEvent { private int ignoreLowestAmount = 0; // ignore the lowest results private final RollDieType rollDieType; - public RollDiceEvent(Ability source, RollDieType rollDieType, int sides, int rollsAmount) { - super(EventType.ROLL_DICE, source.getControllerId(), source, source.getControllerId(), rollsAmount, false); + /** + * The target ID is used to keep track of the distinction between the player who controls the ability that + * started the dice roll and the player who does the rolling. + *

+ * The only times this distinction matters is for Chaos Dragon and Ricochet. + * + * @param source + * @param targetId The player who is rolling the die + * @param rollDieType + * @param sides + * @param rollsAmount + */ + public RollDiceEvent(Ability source, UUID targetId, RollDieType rollDieType, int sides, int rollsAmount) { + super(EventType.ROLL_DICE, targetId, source, source.getControllerId(), rollsAmount, false); this.sides = sides; this.rollDieType = rollDieType; } diff --git a/Mage/src/main/java/mage/game/events/RollDieEvent.java b/Mage/src/main/java/mage/game/events/RollDieEvent.java index 8dee325adc1..7cc81b335d8 100644 --- a/Mage/src/main/java/mage/game/events/RollDieEvent.java +++ b/Mage/src/main/java/mage/game/events/RollDieEvent.java @@ -4,6 +4,8 @@ import mage.abilities.Ability; import mage.constants.RollDieType; import mage.util.CardUtil; +import java.util.UUID; + /** * @author TheElk801 */ @@ -16,8 +18,19 @@ public class RollDieEvent extends GameEvent { private int rollsAmount = 1; // rolls X times and choose result from it private int bigIdeaRollsAmount = 0; // rolls 2x and sum result - public RollDieEvent(Ability source, RollDieType rollDieType, int sides) { - super(EventType.ROLL_DIE, source.getControllerId(), source, source.getControllerId()); + /** + * The target ID is used to keep track of the distinction between the player who controls the ability that + * started the dice roll and the player who does the rolling. + *

+ * The only times this distinction matters is for Chaos Dragon and Ricochet. + * + * @param source + * @param targetId The player rolling the die + * @param rollDieType + * @param sides + */ + public RollDieEvent(Ability source, UUID targetId, RollDieType rollDieType, int sides) { + super(EventType.ROLL_DIE, targetId, source, source.getControllerId()); this.rollDieType = rollDieType; this.sides = sides; } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index cf6619bd5cf..d20e17702fa 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -3062,7 +3062,7 @@ public abstract class PlayerImpl implements Player, Serializable { private List rollDiceInner(Outcome outcome, Ability source, Game game, RollDieType rollDieType, int sidesAmount, int chaosSidesAmount, int planarSidesAmount, int rollsAmount, int ignoreLowestAmount) { - RollDiceEvent rollDiceEvent = new RollDiceEvent(source, rollDieType, sidesAmount, rollsAmount); + RollDiceEvent rollDiceEvent = new RollDiceEvent(source, this.getId(), rollDieType, sidesAmount, rollsAmount); if (ignoreLowestAmount > 0) { rollDiceEvent.incIgnoreLowestAmount(ignoreLowestAmount); } @@ -3079,7 +3079,7 @@ public abstract class PlayerImpl implements Player, Serializable { List dieRolls = new ArrayList<>(); for (int i = 0; i < rollDiceEvent.getAmount(); i++) { // ROLL SINGLE die - RollDieEvent rollDieEvent = new RollDieEvent(source, rollDiceEvent.getRollDieType(), rollDiceEvent.getSides()); + RollDieEvent rollDieEvent = new RollDieEvent(source, this.getId(), rollDiceEvent.getRollDieType(), rollDiceEvent.getSides()); game.replaceEvent(rollDieEvent); Object rollResult; @@ -3186,7 +3186,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (RollDieResult result : dieRolls) { game.fireEvent(new DieRolledEvent(source, rollDiceEvent.getRollDieType(), rollDiceEvent.getSides(), result.naturalResult, result.modifier, result.planarResult)); } - game.fireEvent(new DiceRolledEvent(rollDiceEvent.getSides(), dieResults, source)); + game.fireEvent(new DiceRolledEvent(rollDiceEvent.getSides(), dieResults, source, this.getId())); String resultString = dieResults .stream()