mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 21:12:04 -08:00
Improve attachment to permanent logic; implement [PIP] Codsworth, Handy Helper (#12098)
* [PIP] Implement Codsworth, Handy Helper * Fix Codsworth and Halvar * Write tests for attachments * Fix auras going to graveyard when attaching to illegal targets * Fix Captured by the Consulate interaction * Fix failing tests, add additional test * Add source name to log message * Implement requested changes * Revert removed null check * Remove filter check, clean up code * Add additional test * Fix failing roles test * Account for all current attachment edge cases * Implement rule 303.4g * Apply requested changes
This commit is contained in:
parent
9fcbfdeac6
commit
8d249aa691
10 changed files with 580 additions and 20 deletions
|
|
@ -774,7 +774,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if an event won't happen because of an rule modifying effect
|
||||
* Checks if an event won't happen because of a rule modifying effect
|
||||
*
|
||||
* @param event
|
||||
* @param targetAbility ability the event is attached to. can be null.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import mage.constants.*;
|
|||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
import mage.counters.Counters;
|
||||
import mage.filter.FilterOpponent;
|
||||
import mage.filter.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.GameState;
|
||||
import mage.game.ZoneChangeInfo;
|
||||
|
|
@ -1355,7 +1355,20 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
return !ability.getDoesntRemoveControlled() || isControlledBy(game.getControllerId(attachment.getId()));
|
||||
}
|
||||
}
|
||||
return game.getContinuousEffects().preventedByRuleModification(new StayAttachedEvent(this.getId(), attachment.getId(), source), null, game, silentMode);
|
||||
|
||||
boolean canAttach = true;
|
||||
Permanent attachmentPermanent = game.getPermanent(attachment.getId());
|
||||
// If attachment is an aura, ensures this permanent can still be legally enchanted, according to the enchantment's Enchant ability
|
||||
if (attachment.hasSubtype(SubType.AURA, game)
|
||||
&& attachmentPermanent != null
|
||||
&& attachmentPermanent.getSpellAbility() != null
|
||||
&& !attachmentPermanent.getSpellAbility().getTargets().isEmpty()) {
|
||||
// Line of code below functionally gets the target of the aura's Enchant ability, then compares to this permanent. Enchant improperly implemented in XMage, see #9583
|
||||
// Note: stillLegalTarget used exclusively to account for Dream Leash. Can be made canTarget in the event that that card is rewritten (and "stillLegalTarget" removed from TargetImpl).
|
||||
canAttach = attachmentPermanent.getSpellAbility().getTargets().get(0).copy().withNotTarget(true).stillLegalTarget(attachmentPermanent.getControllerId(), this.getId(), source, game);
|
||||
}
|
||||
|
||||
return !canAttach || game.getContinuousEffects().preventedByRuleModification(new StayAttachedEvent(this.getId(), attachment.getId(), source), null, game, silentMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -4836,6 +4836,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (enterTransformed != null && enterTransformed && !card.isTransformable()) {
|
||||
continue;
|
||||
}
|
||||
// 303.4g. If an Aura is entering the battlefield and there is no legal object or player for it to enchant,
|
||||
// the Aura remains in its current zone, unless that zone is the stack. In that case, the Aura is put into
|
||||
// its owner's graveyard instead of entering the battlefield. If the Aura is a token, it isn't created.
|
||||
if (card.hasSubtype(SubType.AURA, game)
|
||||
&& card.getSpellAbility() != null
|
||||
&& !card.getSpellAbility().getTargets().isEmpty()
|
||||
&& !card.getSpellAbility().getTargets().get(0).copy().withNotTarget(true).canChoose(byOwner ? card.getOwnerId() : getId(), game)) {
|
||||
continue;
|
||||
}
|
||||
ZoneChangeEvent event = new ZoneChangeEvent(card.getId(), source,
|
||||
byOwner ? card.getOwnerId() : getId(), fromZone, Zone.BATTLEFIELD, appliedEffects);
|
||||
infoList.add(new ZoneChangeInfo.Battlefield(event, faceDown, tapped, source));
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public interface Target extends Serializable {
|
|||
*/
|
||||
boolean canTarget(UUID id, Ability source, Game game);
|
||||
|
||||
boolean stillLegalTarget(UUID id, Ability source, Game game);
|
||||
boolean stillLegalTarget(UUID playerId, UUID id, Ability source, Game game);
|
||||
|
||||
boolean canTarget(UUID playerId, UUID id, Ability source, Game game);
|
||||
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ public abstract class TargetImpl implements Target {
|
|||
illegalTargets.add(targetId);
|
||||
continue;
|
||||
}
|
||||
if (!stillLegalTarget(targetId, source, game)) {
|
||||
if (!stillLegalTarget(source.getControllerId(), targetId, source, game)) {
|
||||
illegalTargets.add(targetId);
|
||||
}
|
||||
}
|
||||
|
|
@ -546,8 +546,8 @@ public abstract class TargetImpl implements Target {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean stillLegalTarget(UUID id, Ability source, Game game) {
|
||||
return canTarget(id, source, game);
|
||||
public boolean stillLegalTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
return canTarget(controllerId, id, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ public class TargetTappedPermanentAsYouCast extends TargetPermanent {
|
|||
|
||||
// See ruling: https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/253345-dream-leash
|
||||
@Override
|
||||
public boolean stillLegalTarget(UUID id, Ability source, Game game) {
|
||||
public boolean stillLegalTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
return permanent != null
|
||||
&& getFilter().match(permanent, game)
|
||||
&& super.canTarget(id, game); // check everything but leave out the tapped requirement
|
||||
&& super.canTarget(controllerId, id, source, game); // check everything but leave out the tapped requirement
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue