[LGN] Rework Whipgrass Entangler (#10802)

* Rework Whipgrass Entangler

Made a class for "Ability linked with an Effect", that also takes responsability of manually calling its effect's newId method.

* apply review & cleanup
This commit is contained in:
Susucre 2023-08-16 04:40:25 +02:00 committed by GitHub
parent 01e181013b
commit 92f0f84b23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 280 additions and 21 deletions

View file

@ -0,0 +1,82 @@
package mage.abilities.common;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/**
* Warning: please test with a lot of care when using this class for new things.
* <p>
* A static Ability linked to an Effect.
* The parent Ability does take responsability of setting the id for the child.
*
* @author Susucr
*/
public class LinkedEffectIdStaticAbility extends SimpleStaticAbility {
public interface ChildEffect extends Effect {
/**
* Set the link for the child.
*/
void setParentLinkHandshake(UUID parentLinkHandshake);
/**
* The child Id should only change on copy when the parent wants it to.
*/
void manualNewId();
}
/**
* The handshake UUID between this parent ability and its child.
*/
private UUID linkedHandshake;
public LinkedEffectIdStaticAbility(ChildEffect effect) {
this(Zone.BATTLEFIELD, effect);
}
public LinkedEffectIdStaticAbility(Zone zone, ChildEffect effect) {
super(Zone.BATTLEFIELD, effect);
this.linkedHandshake = UUID.randomUUID();
initHandshake();
setEffectIdManually();
}
private LinkedEffectIdStaticAbility(final LinkedEffectIdStaticAbility effect) {
super(effect);
this.linkedHandshake = UUID.randomUUID();
initHandshake();
}
@Override
public LinkedEffectIdStaticAbility copy() {
return new LinkedEffectIdStaticAbility(this);
}
private void initHandshake() {
this.linkedHandshake = UUID.randomUUID();
CardUtil.castStream(this.getEffects().stream(), ChildEffect.class)
.filter(Objects::nonNull)
.forEach(e -> e.setParentLinkHandshake(linkedHandshake));
}
public void setEffectIdManually() {
CardUtil.castStream(this.getEffects().stream(), ChildEffect.class)
.filter(Objects::nonNull)
.forEach(e -> e.manualNewId());
}
public boolean checkLinked(UUID handshake) {
return linkedHandshake.equals(handshake);
}
@Override
public void newId() {
super.newId();
}
}

View file

@ -8,7 +8,6 @@ import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
/**
* @author LevelX2
@ -26,7 +25,7 @@ public class CantAttackBlockUnlessPaysSourceEffect extends PayCostToAttackBlockE
staticText = "{this} can't " + restrictType.toString() + " unless you pay " + (manaCosts == null ? "" : manaCosts.getText());
}
public CantAttackBlockUnlessPaysSourceEffect(CantAttackBlockUnlessPaysSourceEffect effect) {
protected CantAttackBlockUnlessPaysSourceEffect(final CantAttackBlockUnlessPaysSourceEffect effect) {
super(effect);
}

View file

@ -3,13 +3,12 @@ package mage.abilities.effects.common.continuous;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.LinkedEffectIdStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.Card;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.util.CardUtil;
import java.util.*;
@ -18,7 +17,7 @@ import java.util.*;
*/
public class GainAbilityTargetEffect extends ContinuousEffectImpl {
protected Ability ability;
protected final Ability ability;
// shall a card gain the ability (otherwise a permanent)
private final boolean useOnCard; // only one card per ability supported
@ -43,7 +42,8 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
public GainAbilityTargetEffect(Ability ability, Duration duration, String rule, boolean useOnCard) {
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability.getEffects().getOutcome(ability, Outcome.AddAbility));
this.ability = ability;
this.ability = copyAbility(ability); // See the method's comment, ability.copy() is not enough.
this.staticText = rule;
this.useOnCard = useOnCard;
@ -52,8 +52,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
protected GainAbilityTargetEffect(final GainAbilityTargetEffect effect) {
super(effect);
this.ability = effect.ability.copy();
this.ability.newId(); // This is needed if the effect is copied e.g. by a clone so the ability can be added multiple times to permanents
this.ability = copyAbility(effect.ability); // See the method's comment, ability.copy() is not enough.
this.useOnCard = effect.useOnCard;
this.waitingCardPermanent = effect.waitingCardPermanent;
this.durationPhaseStep = effect.durationPhaseStep;
@ -202,6 +201,23 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
return affectedTargets > 0;
}
/**
* Copying the ability and providing ability is needed in a few situations,
* The copy in order to have internal fields be proper to that ability in particular.
* Id must be different for the copy, for a few things like the GainAbilityTargetEffect gained
* by a clone, or in the case of an activated ability, called multiple times on the same target,
* and thus the ability should be gained multiple times.
*/
private Ability copyAbility(Ability toCopyAbility) {
Ability ability = toCopyAbility.copy();
ability.newId();
if (ability instanceof LinkedEffectIdStaticAbility) {
((LinkedEffectIdStaticAbility) ability).setEffectIdManually();
}
return ability;
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {