forked from External/mage
Reworked and improved special mana payment abilities (convoke, delve, assist, improvise):
* now it can be used to calc and find available mana and playable abilities; * now tests and AI can use that abilities; * now it follows mtg's rules and restrictions for mana activation order (rule 601.2f, see #768);
This commit is contained in:
parent
bdaf6454de
commit
c2e7b02e13
9 changed files with 341 additions and 177 deletions
|
|
@ -1,18 +1,22 @@
|
|||
|
||||
|
||||
package mage.abilities;
|
||||
|
||||
import mage.abilities.costs.mana.AlternateManaPaymentAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* @author BetaSteward_at_googlemail.com, JayDi85
|
||||
*/
|
||||
public abstract class SpecialAction extends ActivatedAbilityImpl {
|
||||
|
||||
private boolean manaAction;
|
||||
private final AlternateManaPaymentAbility manaAbility; // mana actions generates on every pay cycle, no need to copy it
|
||||
protected ManaCost unpaidMana;
|
||||
|
||||
public SpecialAction() {
|
||||
|
|
@ -20,22 +24,23 @@ public abstract class SpecialAction extends ActivatedAbilityImpl {
|
|||
}
|
||||
|
||||
public SpecialAction(Zone zone) {
|
||||
this(zone, false);
|
||||
this(zone, null);
|
||||
}
|
||||
public SpecialAction(Zone zone, boolean manaAction) {
|
||||
|
||||
public SpecialAction(Zone zone, AlternateManaPaymentAbility manaAbility) {
|
||||
super(AbilityType.SPECIAL_ACTION, zone);
|
||||
this.usesStack = false;
|
||||
this.manaAction = manaAction;
|
||||
this.manaAbility = manaAbility;
|
||||
}
|
||||
|
||||
public SpecialAction(final SpecialAction action) {
|
||||
super(action);
|
||||
this.manaAction = action.manaAction;
|
||||
this.unpaidMana = action.unpaidMana;
|
||||
this.manaAbility = action.manaAbility;
|
||||
}
|
||||
|
||||
public boolean isManaAction() {
|
||||
return manaAction;
|
||||
return manaAbility != null;
|
||||
}
|
||||
|
||||
public void setUnpaidMana(ManaCost manaCost) {
|
||||
|
|
@ -45,4 +50,29 @@ public abstract class SpecialAction extends ActivatedAbilityImpl {
|
|||
public ManaCost getUnpaidMana() {
|
||||
return unpaidMana;
|
||||
}
|
||||
|
||||
public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
|
||||
if (manaAbility != null) {
|
||||
return manaAbility.getManaOptions(source, game, unpaid);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
if (isManaAction()) {
|
||||
// limit play mana abilities by steps
|
||||
int currentStepOrder = 0;
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
if (stackObject instanceof Spell) {
|
||||
currentStepOrder = ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep().getStepOrder();
|
||||
}
|
||||
}
|
||||
if (currentStepOrder > manaAbility.useOnActivationManaAbilityStep().getStepOrder()) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
return super.canActivate(playerId, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package mage.abilities.costs.mana;
|
||||
|
||||
/**
|
||||
* Some special AlternateManaPaymentAbility must be restricted to pay before or after mana abilities.
|
||||
* Game logic: if you use special mana ability then normal mana abilities must be restricted and vice versa,
|
||||
* see Convoke for more info and rules
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
public enum ActivationManaAbilityStep {
|
||||
BEFORE(0), // assist
|
||||
NORMAL(1), // all activated mana abilities
|
||||
AFTER(2); // convoke, delve, improvise
|
||||
|
||||
private final int stepOrder;
|
||||
|
||||
ActivationManaAbilityStep(int stepOrder) {
|
||||
this.stepOrder = stepOrder;
|
||||
}
|
||||
|
||||
public int getStepOrder() {
|
||||
return stepOrder;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
|
||||
package mage.abilities.costs.mana;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* Interface for abilities that allow the player to pay mana costs of a spell in alternate ways.
|
||||
* For the payment SpecialActions are used.
|
||||
*
|
||||
* <p>
|
||||
* Example of such an alternate payment ability: {@link mage.abilities.keyword.DelveAbility}
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AlternateManaPaymentAbility {
|
||||
/**
|
||||
* Adds the special action to the state, that allows the user to do the alternate mana payment
|
||||
|
|
@ -22,4 +21,21 @@ public interface AlternateManaPaymentAbility {
|
|||
* @param unpaid unapaid mana costs of the spell
|
||||
*/
|
||||
void addSpecialAction(Ability source, Game game, ManaCost unpaid);
|
||||
|
||||
/**
|
||||
* All possible mana payments that can make that ability (uses to find playable abilities)
|
||||
*
|
||||
* @param source
|
||||
* @param game
|
||||
* @param unpaid
|
||||
* @return
|
||||
*/
|
||||
ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid);
|
||||
|
||||
/**
|
||||
* Mana payment step where you can use it
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ActivationManaAbilityStep useOnActivationManaAbilityStep();
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package mage.abilities.effects;
|
||||
|
||||
import java.util.*;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.CompoundAbility;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.costs.mana.ActivationManaAbilityStep;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.DomainValue;
|
||||
import mage.abilities.dynamicvalue.common.SignInversionDynamicValue;
|
||||
|
|
@ -20,6 +20,8 @@ import mage.game.stack.StackObject;
|
|||
import mage.players.Player;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com, JayDi85
|
||||
*/
|
||||
|
|
@ -416,7 +418,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
StackObject stackObject = game.getStack().getFirst();
|
||||
return !(stackObject instanceof Spell)
|
||||
|| !Zone.LIBRARY.equals(((Spell) stackObject).getFromZone())
|
||||
|| ((Spell) stackObject).isDoneActivatingManaAbilities();
|
||||
|| ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep() == ActivationManaAbilityStep.AFTER; // mana payment finished
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import mage.constants.AsThoughEffectType;
|
|||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -54,10 +56,24 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
|
|||
&& null == game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
// check if player is in the process of playing spell costs and they are no longer allowed to use activated mana abilities (e.g. because they started to use improvise)
|
||||
|
||||
// check if player is in the process of playing spell costs and they are no longer allowed to use
|
||||
// activated mana abilities (e.g. because they started to use improvise or convoke)
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
if (stackObject instanceof Spell) {
|
||||
switch (((Spell) stackObject).getCurrentActivatingManaAbilitiesStep()) {
|
||||
case BEFORE:
|
||||
case NORMAL:
|
||||
break;
|
||||
case AFTER:
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//20091005 - 605.3a
|
||||
return new ActivationStatus(costs.canPay(this, sourceId, controllerId, game), null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue