mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
Tibalt, Cosmic Impostor - fixed that emblem can't cast not owned cards (#7598)
* Fixed ability.canChooseTarget not using correct playerId
* Fixed Necrotic Plague
* Revert "Fixed Necrotic Plague"
This reverts commit 7659039670.
* Set target controller on Necrotic Plague and add check in canChooseTarget
* Add test for Tibalt + Ephemerate interaction
* Tests improved
Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
parent
b94af941df
commit
bb0a995541
20 changed files with 84 additions and 31 deletions
|
|
@ -320,7 +320,7 @@ public interface Ability extends Controllable, Serializable {
|
|||
|
||||
Modes getModes();
|
||||
|
||||
boolean canChooseTarget(Game game);
|
||||
boolean canChooseTarget(Game game, UUID playerId);
|
||||
|
||||
/**
|
||||
* Gets the list of sub-abilities associated with this ability.
|
||||
|
|
|
|||
|
|
@ -901,24 +901,36 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canChooseTarget(Game game) {
|
||||
public boolean canChooseTarget(Game game, UUID playerId) {
|
||||
if (this instanceof SpellAbility) {
|
||||
if (SpellAbilityType.SPLIT_FUSED.equals(((SpellAbility) this).getSpellAbilityType())) {
|
||||
Card card = game.getCard(getSourceId());
|
||||
if (card != null) {
|
||||
return canChooseTargetAbility(((SplitCard) card).getLeftHalfCard().getSpellAbility(), game, getControllerId())
|
||||
&& canChooseTargetAbility(((SplitCard) card).getRightHalfCard().getSpellAbility(), game, getControllerId());
|
||||
return canChooseTargetAbility(((SplitCard) card).getLeftHalfCard().getSpellAbility(), game, playerId)
|
||||
&& canChooseTargetAbility(((SplitCard) card).getRightHalfCard().getSpellAbility(), game, playerId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return canChooseTargetAbility(this, game, getControllerId());
|
||||
return canChooseTargetAbility(this, game, playerId);
|
||||
}
|
||||
|
||||
private static boolean canChooseTargetAbility(Ability ability, Game game, UUID controllerId) {
|
||||
int found = 0;
|
||||
for (Mode mode : ability.getModes().values()) {
|
||||
if (mode.getTargets().canChoose(ability.getSourceId(), ability.getControllerId(), game)) {
|
||||
boolean validTargets = true;
|
||||
for (Target target : mode.getTargets()) {
|
||||
UUID abilityControllerId = controllerId;
|
||||
if (target.getTargetController() != null) {
|
||||
abilityControllerId = target.getTargetController();
|
||||
}
|
||||
if (!target.canChoose(ability.getSourceId(), abilityControllerId, game)) {
|
||||
validTargets = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validTargets) {
|
||||
found++;
|
||||
if (ability.getModes().isEachModeMoreThanOnce()) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
|
|||
|| game.canPlaySorcery(playerId)
|
||||
|| null != approvingObject) {
|
||||
if (costs.canPay(this, this, playerId, game)
|
||||
&& canChooseTarget(game)) {
|
||||
&& canChooseTarget(game, playerId)) {
|
||||
this.activatorId = playerId;
|
||||
return new ActivationStatus(true, approvingObject);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,13 +118,13 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
// fused can be called from hand only, so not permitting object allows or other zones checks
|
||||
// see https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/251926-snapcaster-mage-and-fuse
|
||||
if (game.getState().getZone(splitCard.getId()) == Zone.HAND) {
|
||||
return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game)
|
||||
&& splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null);
|
||||
return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)
|
||||
&& splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId), null);
|
||||
}
|
||||
}
|
||||
return ActivationStatus.getFalse();
|
||||
} else {
|
||||
return new ActivationStatus(canChooseTarget(game), approvingObject);
|
||||
return new ActivationStatus(canChooseTarget(game, playerId), approvingObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
|
|||
+ cardToCast.getName() + " is no land and has no spell ability!");
|
||||
cancel = true;
|
||||
}
|
||||
if (cardToCast.getSpellAbility().canChooseTarget(game)) {
|
||||
if (cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) {
|
||||
cancel = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,8 +437,8 @@ public class StackAbility extends StackObjImpl implements Ability {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canChooseTarget(Game game) {
|
||||
return ability.canChooseTarget(game);
|
||||
public boolean canChooseTarget(Game game, UUID playerId) {
|
||||
return ability.canChooseTarget(game, playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1495,7 +1495,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (sourceObject != null) {
|
||||
sourceObject.adjustTargets(ability, game);
|
||||
}
|
||||
if (ability.canChooseTarget(game)) {
|
||||
if (ability.canChooseTarget(game, playerId)) {
|
||||
if (ability.isUsesStack()) {
|
||||
game.getStack().push(new StackAbility(ability, playerId));
|
||||
}
|
||||
|
|
@ -1539,28 +1539,28 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return useable;
|
||||
case SPLIT_FUSED:
|
||||
if (zone == Zone.HAND) {
|
||||
if (ability.canChooseTarget(game)) {
|
||||
if (ability.canChooseTarget(game, playerId)) {
|
||||
useable.put(ability.getId(), (SpellAbility) ability);
|
||||
}
|
||||
}
|
||||
case SPLIT:
|
||||
if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game)) {
|
||||
if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) {
|
||||
useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(),
|
||||
((SplitCard) object).getLeftHalfCard().getSpellAbility());
|
||||
}
|
||||
if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game)) {
|
||||
if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) {
|
||||
useable.put(((SplitCard) object).getRightHalfCard().getSpellAbility().getId(),
|
||||
((SplitCard) object).getRightHalfCard().getSpellAbility());
|
||||
}
|
||||
return useable;
|
||||
case SPLIT_AFTERMATH:
|
||||
if (zone == Zone.GRAVEYARD) {
|
||||
if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game)) {
|
||||
if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) {
|
||||
useable.put(((SplitCard) object).getRightHalfCard().getSpellAbility().getId(),
|
||||
((SplitCard) object).getRightHalfCard().getSpellAbility());
|
||||
}
|
||||
} else {
|
||||
if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game)) {
|
||||
if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) {
|
||||
useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(),
|
||||
((SplitCard) object).getLeftHalfCard().getSpellAbility());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import mage.abilities.Ability;
|
|||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public interface TargetAdjuster {
|
||||
|
||||
void adjustTargets(Ability ability, Game game);
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue