mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
tests: added verify check for wrong target tags usage, improved work with tagged targets (related to 8b2a81cb42)
This commit is contained in:
parent
3223d99b2a
commit
71b0613355
4 changed files with 121 additions and 36 deletions
|
|
@ -1,23 +1,26 @@
|
||||||
package mage.cards.f;
|
package mage.cards.f;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.*;
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SuperType;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
import mage.filter.predicate.other.AnotherTargetPredicate;
|
import mage.filter.predicate.other.AnotherTargetPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.target.Target;
|
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Susucr
|
* @author Susucr
|
||||||
*/
|
*/
|
||||||
public final class FriendlyRivalry extends CardImpl {
|
public final class FriendlyRivalry extends CardImpl {
|
||||||
|
|
@ -31,20 +34,18 @@ public final class FriendlyRivalry extends CardImpl {
|
||||||
|
|
||||||
public FriendlyRivalry(UUID ownerId, CardSetInfo setInfo) {
|
public FriendlyRivalry(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}");
|
||||||
|
|
||||||
|
|
||||||
// Target creature you control and up to one other target legendary creature you control
|
// Target creature you control and up to one other target legendary creature you control each deal damage equal to their power to target creature you don't control.
|
||||||
// each deal damage equal to their power to target creature you don't control.
|
|
||||||
this.getSpellAbility().addEffect(new FriendlyRivalryEffect());
|
this.getSpellAbility().addEffect(new FriendlyRivalryEffect());
|
||||||
|
|
||||||
TargetControlledCreaturePermanent target1 = new TargetControlledCreaturePermanent();
|
TargetControlledCreaturePermanent target1 = new TargetControlledCreaturePermanent();
|
||||||
this.getSpellAbility().addTarget(target1.setTargetTag(1));
|
this.getSpellAbility().addTarget(target1.setTargetTag(1).withChooseHint("to deal damage"));
|
||||||
|
|
||||||
TargetControlledCreaturePermanent target2 = new TargetControlledCreaturePermanent(0, 1, filter2, false);
|
TargetControlledCreaturePermanent target2 = new TargetControlledCreaturePermanent(0, 1, filter2, false);
|
||||||
this.getSpellAbility().addTarget(target2.setTargetTag(2));
|
this.getSpellAbility().addTarget(target2.setTargetTag(2).withChooseHint("to deal damage"));
|
||||||
|
|
||||||
TargetCreaturePermanent target3 = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL);
|
TargetCreaturePermanent target3 = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL);
|
||||||
this.getSpellAbility().addTarget(target3);
|
this.getSpellAbility().addTarget(target3.setTargetTag(3).withChooseHint("to take damage"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private FriendlyRivalry(final FriendlyRivalry card) {
|
private FriendlyRivalry(final FriendlyRivalry card) {
|
||||||
|
|
@ -62,7 +63,7 @@ class FriendlyRivalryEffect extends OneShotEffect {
|
||||||
FriendlyRivalryEffect() {
|
FriendlyRivalryEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
staticText = "Target creature you control and up to one other target legendary " +
|
staticText = "Target creature you control and up to one other target legendary " +
|
||||||
"creature you control each deal damage equal to their power to target creature you don't control.";
|
"creature you control each deal damage equal to their power to target creature you don't control.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private FriendlyRivalryEffect(final FriendlyRivalryEffect effect) {
|
private FriendlyRivalryEffect(final FriendlyRivalryEffect effect) {
|
||||||
|
|
@ -77,34 +78,31 @@ class FriendlyRivalryEffect extends OneShotEffect {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
int size = source.getTargets().size();
|
int size = source.getTargets().size();
|
||||||
if (size < 2) {
|
if (size < 3) {
|
||||||
|
throw new IllegalArgumentException("Wrong code usage. Lost targets list, must has 3, but found: " + source.getTargets());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Permanent> toDealDamage = new ArrayList<>();
|
||||||
|
source.getTargets().getTargetsByTag(1).stream()
|
||||||
|
.map(game::getPermanent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(toDealDamage::add);
|
||||||
|
source.getTargets().getTargetsByTag(2).stream()
|
||||||
|
.map(game::getPermanent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(toDealDamage::add);
|
||||||
|
Permanent toTakeDamage = source.getTargets().getTargetsByTag(3).stream()
|
||||||
|
.map(game::getPermanent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
if (toDealDamage.isEmpty() || toTakeDamage == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Target damageTarget1 = source.getTargets().get(0);
|
toDealDamage.forEach(permanent -> {
|
||||||
Target damageTarget2 = size == 3 ? source.getTargets().get(1) : null;
|
toTakeDamage.damage(permanent.getPower().getValue(), permanent.getId(), source, game, false, true);
|
||||||
|
});
|
||||||
|
|
||||||
Target destTarget = source.getTargets().get(size-1);
|
|
||||||
if ((damageTarget1.getTargets().isEmpty() && (damageTarget2 == null || damageTarget2.getTargets().isEmpty()))
|
|
||||||
|| destTarget.getTargets().isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Permanent permanentDamage1 = damageTarget1.getTargets().isEmpty() ? null
|
|
||||||
: game.getPermanent(damageTarget1.getTargets().get(0));
|
|
||||||
Permanent permanentDamage2 = damageTarget2 == null || damageTarget2.getTargets().isEmpty() ? null
|
|
||||||
: game.getPermanent(damageTarget2.getTargets().get(0));
|
|
||||||
Permanent permanentDest = game.getPermanent(destTarget.getTargets().get(0));
|
|
||||||
if (permanentDest == null){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permanentDamage1 != null) {
|
|
||||||
permanentDest.damage(permanentDamage1.getPower().getValue(), permanentDamage1.getId(), source, game, false, true);
|
|
||||||
}
|
|
||||||
if (permanentDamage2 != null) {
|
|
||||||
permanentDest.damage(permanentDamage2.getPower().getValue(), permanentDamage2.getId(), source, game, false, true);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.mage.test.cards.single.ltr;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class FriendlyRivalryTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_target_both() {
|
||||||
|
// Target creature you control and up to one other target legendary creature you control each deal damage
|
||||||
|
// equal to their power to target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Friendly Rivalry"); // {R}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Aesi, Tyrant of Gyre Strait"); // legendary, 5/5
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Agonasaur Rex"); // 8/8
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Friendly Rivalry");
|
||||||
|
addTarget(playerA, "Grizzly Bears");
|
||||||
|
addTarget(playerA, "Aesi, Tyrant of Gyre Strait");
|
||||||
|
addTarget(playerA, "Agonasaur Rex");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertDamageReceived(playerB, "Agonasaur Rex", 2 + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_target_single() {
|
||||||
|
// Target creature you control and up to one other target legendary creature you control each deal damage
|
||||||
|
// equal to their power to target creature you don't control.
|
||||||
|
addCard(Zone.HAND, playerA, "Friendly Rivalry"); // {R}{G}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Aesi, Tyrant of Gyre Strait"); // legendary, 5/5
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Agonasaur Rex"); // 8/8
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Friendly Rivalry");
|
||||||
|
addTarget(playerA, "Grizzly Bears");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // skip second target due "up to"
|
||||||
|
addTarget(playerA, "Agonasaur Rex");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertDamageReceived(playerB, "Agonasaur Rex", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,6 +43,7 @@ import mage.game.permanent.token.TokenImpl;
|
||||||
import mage.game.permanent.token.custom.CreatureToken;
|
import mage.game.permanent.token.custom.CreatureToken;
|
||||||
import mage.game.permanent.token.custom.XmageToken;
|
import mage.game.permanent.token.custom.XmageToken;
|
||||||
import mage.sets.TherosBeyondDeath;
|
import mage.sets.TherosBeyondDeath;
|
||||||
|
import mage.target.Target;
|
||||||
import mage.target.targetpointer.TargetPointer;
|
import mage.target.targetpointer.TargetPointer;
|
||||||
import mage.tournament.cubes.CubeFromDeck;
|
import mage.tournament.cubes.CubeFromDeck;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
@ -2279,6 +2280,19 @@ public class VerifyCardDataTest {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// special check: target tags must be used for all targets and starts with 1
|
||||||
|
card.getAbilities().stream().filter(a -> a.getTargets().size() >= 2).forEach(a -> {
|
||||||
|
Set<Integer> tags = a.getTargets().stream().map(Target::getTargetTag).collect(Collectors.toSet());
|
||||||
|
if (tags.size() == 1 && tags.stream().findFirst().get().equals(0)) {
|
||||||
|
// no tags usage
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tags.size() != a.getTargets().size() || (tags.size() > 1 && tags.contains(0))) {
|
||||||
|
// how-to fix: make sure each target has it's own target tag like 1 and 2 (don't use 0 because it's default)
|
||||||
|
fail(card, "abilities", "wrong target tags: miss tag in one of the targets, current list: " + tags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// spells have only 1 ability
|
// spells have only 1 ability
|
||||||
if (card.isInstantOrSorcery()) {
|
if (card.isInstantOrSorcery()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,18 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Target getByTag(int tag) {
|
||||||
|
return this.stream().filter(t -> t.getTargetTag() == tag).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UUID> getTargetsByTag(int tag) {
|
||||||
|
Target target = getByTag(tag);
|
||||||
|
if (target == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return target.getTargets();
|
||||||
|
}
|
||||||
|
|
||||||
public Target getNextUnchosen(Game game) {
|
public Target getNextUnchosen(Game game) {
|
||||||
return getNextUnchosen(game, 0);
|
return getNextUnchosen(game, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue