Fixing aura token creation (#10858)

* rework aura token creation

* add missing copy constructor

* add message for token if unable to be created

* add a few extra role tests
This commit is contained in:
Evan Kranzler 2023-08-18 13:25:48 -04:00 committed by GitHub
parent 93cfb86a0f
commit b892562b95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 148 additions and 63 deletions

View file

@ -42,6 +42,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
private final CardType additionalCardType;
private SubType additionalSubType;
private final UUID attackedPlayer;
private UUID attachedTo = null;
private final boolean attacking;
private boolean becomesArtifact;
private ObjectColor color;
@ -134,6 +135,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
this.additionalCardType = effect.additionalCardType;
this.additionalSubType = effect.additionalSubType;
this.attackedPlayer = effect.attackedPlayer;
this.attachedTo = effect.attachedTo;
this.attacking = effect.attacking;
this.becomesArtifact = effect.becomesArtifact;
this.color = effect.color;
@ -264,7 +266,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
}
}
token.putOntoBattlefield(number, game, source, playerId == null ? source.getControllerId() : playerId, tapped, attacking, attackedPlayer);
token.putOntoBattlefield(number, game, source, playerId == null ? source.getControllerId() : playerId, tapped, attacking, attackedPlayer, attachedTo);
for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield
Permanent tokenPermanent = game.getPermanent(tokenId);
if (tokenPermanent != null) {
@ -386,6 +388,11 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
return this;
}
public CreateTokenCopyTargetEffect setAttachedTo(UUID attachedTo) {
this.attachedTo = attachedTo;
return this;
}
public void sacrificeTokensCreatedAtNextEndStep(Game game, Ability source) {
this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_TURN, false);
}

View file

@ -5,7 +5,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.*;
import java.util.UUID;
import java.util.function.Supplier;
/**
@ -39,15 +38,7 @@ public enum RoleType {
public Token createToken(Permanent permanent, Game game, Ability source) {
Token token = supplier.get();
token.putOntoBattlefield(1, game, source);
for (UUID tokenId : token.getLastAddedTokenIds()) {
Permanent aura = game.getPermanent(tokenId);
if (aura == null || !aura.hasSubtype(SubType.AURA, game)) {
continue;
}
aura.getAbilities().get(0).getTargets().get(0).add(permanent.getId(), game);
aura.getAbilities().get(0).getEffects().get(0).apply(game, aura.getAbilities().get(0));
}
token.putOntoBattlefield(1, game, source, source.getControllerId(), false, false, null, permanent.getId());
return token;
}
}

View file

@ -35,7 +35,9 @@ public interface Token extends MageObject {
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created);
void setPower(int power);

View file

@ -197,11 +197,16 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
@Override
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer) {
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, true);
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, null);
}
@Override
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created) {
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo) {
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, attachedTo, true);
}
@Override
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created) {
Player controller = game.getPlayer(controllerId);
if (controller == null) {
return false;
@ -233,7 +238,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
it.remove();
}
}
putOntoBattlefieldHelper(event, game, source, tapped, attacking, attackedPlayer, created);
putOntoBattlefieldHelper(event, game, source, tapped, attacking, attackedPlayer, attachedTo, created);
event.getTokens()
.keySet()
.stream()
@ -247,7 +252,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
return false;
}
private static void putOntoBattlefieldHelper(CreateTokenEvent event, Game game, Ability source, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created) {
private static void putOntoBattlefieldHelper(CreateTokenEvent event, Game game, Ability source, boolean tapped, boolean attacking, UUID attackedPlayer, UUID attachedTo, boolean created) {
Player controller = game.getPlayer(event.getPlayerId());
if (controller == null) {
return;
@ -258,6 +263,18 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
Token token = entry.getKey();
int amount = entry.getValue();
// check if token needs to be attached to something
Permanent permanentAttachedTo;
if (attachedTo != null) {
permanentAttachedTo = game.getPermanent(attachedTo);
if (permanentAttachedTo == null || permanentAttachedTo.cantBeAttachedBy(token, source, game, true)) {
game.informPlayers(token.getName() + " will not be created as it cannot be attached to the chosen permanent");
continue;
}
} else {
permanentAttachedTo = null;
}
// choose token's set code due source
TokenInfo tokenInfo = TokenImpl.generateTokenInfo((TokenImpl) token, game, source == null ? null : source.getSourceId());
token.setExpansionSetCode(tokenInfo.getSetCode());
@ -317,7 +334,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
// if token was created (not a spell copy) handle auras coming into the battlefield
// code blindly copied from CopyPermanentEffect
// TODO: clean this up -- half the comments make no sense in the context of creating a token
if (created && permanent.getSubtype().contains(SubType.AURA)) {
if (created && permanent.getSubtype().contains(SubType.AURA) && attachedTo == null) {
Outcome auraOutcome = Outcome.BoostCreature;
Target auraTarget = null;
@ -375,6 +392,15 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
// end of aura code : just remove this line if everything works out well
if (permanentAttachedTo != null) {
if (permanent.hasSubtype(SubType.AURA, game)) {
permanent.getAbilities().get(0).getTargets().get(0).add(permanentAttachedTo.getId(), game);
permanent.getAbilities().get(0).getEffects().get(0).apply(game, permanent.getAbilities().get(0));
} else {
permanentAttachedTo.addAttachment(permanent.getId(), source, game);
}
}
// must attack
if (attacking && game.getCombat() != null && game.getActivePlayerId().equals(permanent.getControllerId())) {
game.getCombat().addAttackingCreature(permanent.getId(), game, attackedPlayer);

View file

@ -314,7 +314,7 @@ public class Spell extends StackObjectImpl implements Card {
if (isCopy()) {
Token token = CopyTokenFunction.createTokenCopy(card, game, this);
// The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25)
if (token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false)) {
if (token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, null, false)) {
permId = token.getLastAddedTokenIds().stream().findFirst().orElse(null);
flag = true;
} else {
@ -381,7 +381,7 @@ public class Spell extends StackObjectImpl implements Card {
} else if (isCopy()) {
Token token = CopyTokenFunction.createTokenCopy(card, game, this);
// The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25)
token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false);
token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, null, false);
return true;
} else {
return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);