forked from External/mage
refactor: remove notTarget targets from abilities (#13651)
Adds OneShotNonTargetEffect and PutCountersTargetCost Fixes the target timing of spells/abilities, as non-targets should be chosen on resolution.
This commit is contained in:
parent
8b2a81cb42
commit
3223d99b2a
28 changed files with 449 additions and 384 deletions
|
|
@ -1749,6 +1749,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) {
|
||||
if (targetAdjuster == null) {
|
||||
this.targetAdjuster = null;
|
||||
return this;
|
||||
}
|
||||
if (targetAdjuster instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) {
|
||||
throw new IllegalStateException("Target adjuster being added but no targets are set!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.abilities.common;
|
|||
import mage.MageObject;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
|
@ -18,7 +19,7 @@ import mage.target.targetpointer.FixedTarget;
|
|||
public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
protected FilterPermanent filter;
|
||||
private boolean setTargetPointer;
|
||||
private SetTargetPointer setTargetPointer;
|
||||
|
||||
public DiesCreatureTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(effect, optional, false);
|
||||
|
|
@ -33,7 +34,7 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (another) {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
this.setTargetPointer = (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public DiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) {
|
||||
|
|
@ -44,7 +45,14 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
this(Zone.BATTLEFIELD, effect, optional, filter, setTargetPointer);
|
||||
}
|
||||
|
||||
public DiesCreatureTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) {
|
||||
this(Zone.BATTLEFIELD, effect, false, new FilterCreaturePermanent("a creature"), setTargetPointer);
|
||||
}
|
||||
|
||||
public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) {
|
||||
this(zone, effect, optional, filter, (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE));
|
||||
}
|
||||
public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, SetTargetPointer setTargetPointer) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
|
|
@ -75,8 +83,17 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return false;
|
||||
}
|
||||
getEffects().setValue("creatureDied", zEvent.getTarget());
|
||||
if (setTargetPointer) {
|
||||
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
|
||||
switch (setTargetPointer) {
|
||||
case PLAYER:
|
||||
this.getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId(), game));
|
||||
break;
|
||||
case PERMANENT:
|
||||
this.getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported SetTargetPointer in DiesCreatureTriggeredAbility");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.Counter;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public class PutCountersTargetCost extends CostImpl {
|
||||
|
||||
private final Counter counter;
|
||||
|
||||
public PutCountersTargetCost(Counter counter){
|
||||
this(counter, new TargetControlledCreaturePermanent());
|
||||
}
|
||||
|
||||
public PutCountersTargetCost(Counter counter, TargetControlledPermanent target) {
|
||||
this.counter = counter.copy();
|
||||
target.withNotTarget(true);
|
||||
this.addTarget(target);
|
||||
this.text = "put " + counter.getDescription() + " on " + target.getDescription();
|
||||
}
|
||||
|
||||
public PutCountersTargetCost(PutCountersTargetCost cost) {
|
||||
super(cost);
|
||||
this.counter = cost.counter.copy();
|
||||
}
|
||||
|
||||
public PutCountersTargetCost copy() {
|
||||
return new PutCountersTargetCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
Player player = game.getPlayer(ability.getControllerId());
|
||||
if (player == null || !this.getTargets().choose(Outcome.Exile, controllerId, source.getSourceId(), source, game)) {
|
||||
return paid;
|
||||
}
|
||||
for (UUID targetId : this.getTargets().get(0).getTargets()) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
paid |= permanent.addCounters(counter, controllerId, ability, game);
|
||||
}
|
||||
return paid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return this.getTargets().canChoose(controllerId, source, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.game.Game;
|
||||
import mage.target.Target;
|
||||
import mage.target.targetadjustment.TargetAdjuster;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public class OneShotNonTargetEffect extends OneShotEffect {
|
||||
OneShotEffect effect;
|
||||
Target notTarget;
|
||||
TargetAdjuster adjuster;
|
||||
|
||||
public OneShotNonTargetEffect(OneShotEffect effect, Target notTarget) {
|
||||
this(effect, notTarget, null);
|
||||
}
|
||||
|
||||
public OneShotNonTargetEffect(OneShotEffect effect, Target notTarget, TargetAdjuster adjuster) {
|
||||
super(effect.outcome);
|
||||
this.effect = effect;
|
||||
this.notTarget = notTarget;
|
||||
this.notTarget.withNotTarget(true);
|
||||
this.adjuster = adjuster;
|
||||
if (effect.staticText == null || effect.staticText.equals("")){
|
||||
throw new IllegalArgumentException("Effect must use static text");
|
||||
}
|
||||
this.setText(effect.staticText);
|
||||
}
|
||||
|
||||
private OneShotNonTargetEffect(OneShotNonTargetEffect eff) {
|
||||
super(eff);
|
||||
this.effect = eff.effect.copy();
|
||||
this.notTarget = eff.notTarget.copy();
|
||||
this.adjuster = eff.adjuster;
|
||||
}
|
||||
|
||||
public OneShotNonTargetEffect copy() {
|
||||
return new OneShotNonTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean result = false;
|
||||
Target target = notTarget.copy();
|
||||
if (source.getTargetAdjuster() != null || !source.getTargets().isEmpty()){
|
||||
throw new IllegalStateException("source ability already has target but is using OneShotNonTargetEffect");
|
||||
}
|
||||
source.addTarget(target);
|
||||
if (adjuster != null) {
|
||||
adjuster.clearDefaultTargets();
|
||||
source.setTargetAdjuster(adjuster);
|
||||
source.adjustTargets(game);
|
||||
source.setTargetAdjuster(null);
|
||||
}
|
||||
|
||||
if (source.getTargets().choose(outcome, source.getControllerId(), source.getId(), source, game)) {
|
||||
result = effect.apply(game, source);
|
||||
}
|
||||
source.getTargets().clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OneShotEffect setTargetPointer(TargetPointer targetPointer) {
|
||||
if (targetPointer == null) {
|
||||
return null;
|
||||
}
|
||||
effect.setTargetPointer(targetPointer);
|
||||
return super.setTargetPointer(targetPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, Object value) {
|
||||
effect.setValue(key, value);
|
||||
super.setValue(key, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,6 +71,10 @@ public class ConditionalTargetAdjuster implements TargetAdjuster {
|
|||
blueprintTarget = ability.getTargets().get(0).copy();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void clearDefaultTargets() {
|
||||
blueprintTarget = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustTargets(Ability ability, Game game) {
|
||||
|
|
|
|||
|
|
@ -14,4 +14,8 @@ public abstract class GenericTargetAdjuster implements TargetAdjuster {
|
|||
throw new IllegalStateException("Wrong code usage: target adjuster already has blueprint target - " + blueprintTarget);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void clearDefaultTargets() {
|
||||
blueprintTarget = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,4 +20,7 @@ public interface TargetAdjuster extends Serializable {
|
|||
*/
|
||||
default void addDefaultTargets(Ability ability) {
|
||||
}
|
||||
|
||||
default void clearDefaultTargets() {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue