mirror of
https://github.com/magefree/mage.git
synced 2026-01-10 12:52:06 -08:00
Simplify implementation and fix bugs of Kaito, Dancing Shadow (#10639)
Implement PermanentReferenceInCollectionPredicate used by that convert existing cards to use new Predicate as needed Replaces PermanentInListPredicate for longer term effects
This commit is contained in:
parent
8128e9935d
commit
c3f7a9ab21
5 changed files with 84 additions and 166 deletions
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -16,9 +17,8 @@ import mage.constants.CardType;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.PermanentInListPredicate;
|
||||
import mage.filter.predicate.permanent.PermanentReferenceInCollectionPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
|
@ -77,22 +77,10 @@ class BrineHagEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
|
||||
List<Permanent> list = new ArrayList<>();
|
||||
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Permanent creature : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game)) {
|
||||
if (sourcePermanent.getDealtDamageByThisTurn().contains(new MageObjectReference(creature.getId(), game))) {
|
||||
list.add(creature);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
Set<MageObjectReference> set = new HashSet<>(sourcePermanent.getDealtDamageByThisTurn());
|
||||
if (!set.isEmpty()) {
|
||||
FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||
filter.add(new PermanentInListPredicate(list));
|
||||
filter.add(new PermanentReferenceInCollectionPredicate(set));
|
||||
game.addEffect(new SetBasePowerToughnessAllEffect(0, 2, Duration.Custom, filter, true), source);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package mage.cards.k;
|
||||
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.Filter;
|
||||
import mage.MageObjectReference;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -10,31 +10,29 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.LoyaltyAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.DealCombatDamageControlledTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.combat.CantAttackTargetEffect;
|
||||
import mage.abilities.effects.common.combat.CantBlockTargetEffect;
|
||||
import mage.constants.*;
|
||||
import mage.filter.predicate.permanent.PermanentReferenceInCollectionPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.DamagedPlayerBatchEvent;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.DroneToken;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetObject;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author @stwalsh4118
|
||||
* @author notgreat
|
||||
*/
|
||||
public final class KaitoDancingShadow extends CardImpl {
|
||||
|
||||
|
|
@ -46,7 +44,8 @@ public final class KaitoDancingShadow extends CardImpl {
|
|||
this.setStartingLoyalty(3);
|
||||
|
||||
// Whenever one or more creatures you control deal combat damage to a player, you may return one of them to its owner's hand. If you do, you may activate loyalty abilities of Kaito twice this turn rather than only once.
|
||||
Ability ability = new KaitoDancingShadowTriggeredAbility();
|
||||
Ability ability = new DealCombatDamageControlledTriggeredAbility(Zone.BATTLEFIELD, new KaitoDancingShadowEffect(), true);
|
||||
ability.addWatcher(new KaitoDancingShadowWatcher());
|
||||
this.addAbility(ability);
|
||||
|
||||
// +1: Up to one target creature can't attack or block until your next turn.
|
||||
|
|
@ -72,45 +71,6 @@ public final class KaitoDancingShadow extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class KaitoDancingShadowTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
KaitoDancingShadowTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new KaitoDancingShadowEffect());
|
||||
this.setTriggerPhrase("Whenever one or more creatures you control deal combat damage to a player, ");
|
||||
this.addWatcher(new KaitoDancingShadowWatcher());
|
||||
|
||||
}
|
||||
|
||||
private KaitoDancingShadowTriggeredAbility(final KaitoDancingShadowTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedPlayerBatchEvent dEvent = (DamagedPlayerBatchEvent) event;
|
||||
for (DamagedEvent damagedEvent : dEvent.getEvents()) {
|
||||
if (!damagedEvent.isCombatDamage()) {
|
||||
continue;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(damagedEvent.getSourceId());
|
||||
if (permanent != null && permanent.isControlledBy(getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KaitoDancingShadowTriggeredAbility copy() {
|
||||
return new KaitoDancingShadowTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
||||
class KaitoDancingShadowEffect extends OneShotEffect {
|
||||
|
||||
KaitoDancingShadowEffect() {
|
||||
|
|
@ -133,13 +93,18 @@ class KaitoDancingShadowEffect extends OneShotEffect {
|
|||
if (watcher == null) {
|
||||
return false;
|
||||
}
|
||||
TargetCreatureThatDealtCombatDamage target = new TargetCreatureThatDealtCombatDamage(0, 1, watcher.getPermanents());
|
||||
Player damagedPlayer = game.getPlayer(targetPointer.getFirst(game, source));
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
if (controller == null || damagedPlayer == null) {
|
||||
return false;
|
||||
}
|
||||
if (controller.chooseUse(outcome, "Return a creature card that dealt damage to hand?", source, game) && target.chooseTarget(Outcome.ReturnToHand, source.getControllerId(), source, game)) {
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
filter.add(new PermanentReferenceInCollectionPredicate(
|
||||
watcher.getPermanents(controller.getId(),damagedPlayer.getId())));
|
||||
TargetPermanent target = new TargetPermanent(0, 1, filter, true);
|
||||
target.setTargetName("creature to return to hand?");
|
||||
if (target.chooseTarget(Outcome.ReturnToHand, source.getControllerId(), source, game)) {
|
||||
Card card = game.getPermanent(target.getFirstTarget());
|
||||
if (card != null) {
|
||||
controller.moveCards(card, Zone.HAND, source, game);
|
||||
|
||||
|
|
@ -147,13 +112,18 @@ class KaitoDancingShadowEffect extends OneShotEffect {
|
|||
game.addEffect(effect, source);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class KaitoDancingShadowWatcher extends Watcher {
|
||||
//A creature you control that dealt damage to a player - does not apply across multiple combat steps
|
||||
|
||||
private final List<Permanent> permanents = new ArrayList<>();
|
||||
//Player ID -> List of permanents they controlled that dealt damage
|
||||
private final Map<UUID, List<MageObjectReference>> permanents = new HashMap<>();
|
||||
//MOR -> Player they dealt damage to
|
||||
private final Map<MageObjectReference, UUID> damageTarget = new HashMap<>();
|
||||
|
||||
KaitoDancingShadowWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
|
|
@ -163,6 +133,7 @@ class KaitoDancingShadowWatcher extends Watcher {
|
|||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) {
|
||||
permanents.clear();
|
||||
damageTarget.clear();
|
||||
return;
|
||||
}
|
||||
if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER
|
||||
|
|
@ -173,91 +144,18 @@ class KaitoDancingShadowWatcher extends Watcher {
|
|||
if (creature == null) {
|
||||
return;
|
||||
}
|
||||
permanents.add(creature);
|
||||
MageObjectReference mor = new MageObjectReference(creature, game);
|
||||
damageTarget.put(mor, event.getPlayerId());
|
||||
|
||||
List<MageObjectReference> list = permanents.computeIfAbsent(creature.getControllerId(), (key) -> new ArrayList<>());
|
||||
list.add(mor);
|
||||
}
|
||||
|
||||
public List<Permanent> getPermanents() {
|
||||
return permanents;
|
||||
}
|
||||
}
|
||||
|
||||
class TargetCreatureThatDealtCombatDamage extends TargetObject {
|
||||
|
||||
protected List<Permanent> permanents;
|
||||
private Permanent firstTarget = null;
|
||||
|
||||
public TargetCreatureThatDealtCombatDamage() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TargetCreatureThatDealtCombatDamage(final TargetCreatureThatDealtCombatDamage target) {
|
||||
super(target);
|
||||
this.firstTarget = target.firstTarget;
|
||||
}
|
||||
|
||||
|
||||
public TargetCreatureThatDealtCombatDamage(int minNumTargets, int maxNumTargets, List<Permanent> permanents) {
|
||||
super(minNumTargets, maxNumTargets, Zone.BATTLEFIELD, true);
|
||||
this.permanents = permanents;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Game game) {
|
||||
Card card = game.getCard(id);
|
||||
if (card != null && game.getState().getZone(card.getId()) == Zone.BATTLEFIELD) {
|
||||
for (Permanent permanent : permanents) {
|
||||
if (permanent.getId().equals(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||
firstTarget = game.getPermanent(source.getFirstTarget());
|
||||
return super.chooseTarget(Outcome.Benefit, playerId, source, game);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TargetCreatureThatDealtCombatDamage copy() {
|
||||
return new TargetCreatureThatDealtCombatDamage(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
||||
return permanents.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||
Set<UUID> possibleTargets = new HashSet<>();
|
||||
for (Permanent permanent : permanents) {
|
||||
if (permanent != null && permanent.isControlledBy(sourceControllerId)) {
|
||||
possibleTargets.add(permanent.getId());
|
||||
}
|
||||
}
|
||||
return possibleTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canChoose(UUID sourceControllerId, Game game) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
//Return the set of permanents that the controller controlled which dealt combat damage to the player
|
||||
public Set<MageObjectReference> getPermanents(UUID controllerID, UUID damagedPlayerID) {
|
||||
return permanents.get(controllerID).stream()
|
||||
.filter((mor) -> damagedPlayerID.equals(damageTarget.get(mor)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,13 +176,12 @@ class KaitoDancingShadowIncreaseLoyaltyUseEffect extends ContinuousEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
||||
StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER,
|
||||
source.getControllerId(), source, game
|
||||
)) {
|
||||
permanent.setLoyaltyActivationsAvailable(2);
|
||||
Permanent kaito = source.getSourcePermanentIfItStillExists(game);
|
||||
if (kaito == null) {
|
||||
discard();
|
||||
return false;
|
||||
}
|
||||
|
||||
kaito.setLoyaltyActivationsAvailable(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import mage.filter.common.FilterControlledCreaturePermanent;
|
|||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.filter.predicate.permanent.PermanentInListPredicate;
|
||||
import mage.filter.predicate.permanent.PermanentReferenceInCollectionPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -136,10 +136,10 @@ class RagingRiverEffect extends OneShotEffect {
|
|||
|
||||
|
||||
if (controller.choosePile(outcome, attacker.getName() + ": attacking " + defender.getName(), leftLog, rightLog, game)) {
|
||||
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(left))));
|
||||
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentReferenceInCollectionPredicate(left, game))));
|
||||
game.informPlayers(attacker.getLogName() + ": attacks left (" + defender.getLogName() + ")");
|
||||
} else {
|
||||
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(right))));
|
||||
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentReferenceInCollectionPredicate(right, game))));
|
||||
game.informPlayers(attacker.getLogName() + ": attacks right (" + defender.getLogName() + ")");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,6 @@ public class PermanentInListPredicate implements Predicate<Permanent> {
|
|||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return permanents.contains(input);
|
||||
return (permanents != null && permanents.contains(input));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
package mage.filter.predicate.permanent;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author notgreat
|
||||
*/
|
||||
public class PermanentReferenceInCollectionPredicate implements Predicate<Permanent> {
|
||||
private final Collection<MageObjectReference> references;
|
||||
|
||||
public PermanentReferenceInCollectionPredicate(Collection<MageObjectReference> references) {
|
||||
//Note: it is assumed that the collection passed in isn't ever mutated afterwards
|
||||
this.references = references;
|
||||
}
|
||||
public PermanentReferenceInCollectionPredicate(Collection<Permanent> permanents, Game game) {
|
||||
this.references = permanents.stream().map((p) -> new MageObjectReference(p, game))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return (references != null &&
|
||||
references.contains(new MageObjectReference(input, game)));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue