mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 21:12:04 -08:00
Cost reduction effects - refactor, removed redundant custom effects, added card hints;
This commit is contained in:
parent
e4ebf50d42
commit
cf3feff76a
35 changed files with 506 additions and 643 deletions
|
|
@ -1,38 +1,60 @@
|
|||
|
||||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class AttackingCreatureCount implements DynamicValue {
|
||||
|
||||
private String message;
|
||||
private FilterCreaturePermanent filter;
|
||||
|
||||
public AttackingCreatureCount() {
|
||||
this("attacking creature");
|
||||
}
|
||||
|
||||
public AttackingCreatureCount(FilterCreaturePermanent filter) {
|
||||
this(filter, "attacking " + filter.getMessage());
|
||||
}
|
||||
|
||||
public AttackingCreatureCount(String message) {
|
||||
this(null, message);
|
||||
}
|
||||
|
||||
public AttackingCreatureCount(FilterCreaturePermanent filter, String message) {
|
||||
this.message = message;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public AttackingCreatureCount(final AttackingCreatureCount dynamicValue) {
|
||||
super();
|
||||
this.message = dynamicValue.message;
|
||||
this.filter = dynamicValue.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
int count = 0;
|
||||
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
||||
count += combatGroup.getAttackers().size();
|
||||
for (UUID permId : combatGroup.getAttackers()) {
|
||||
if (filter != null) {
|
||||
Permanent attacker = game.getPermanent(permId);
|
||||
if (attacker != null && filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) {
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
package mage.abilities.dynamicvalue.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
|
||||
*/
|
||||
public class AttackingFilterCreatureCount implements DynamicValue {
|
||||
|
||||
private FilterCreaturePermanent filter;
|
||||
private String message;
|
||||
|
||||
public AttackingFilterCreatureCount(FilterCreaturePermanent filter) {
|
||||
this(filter, "attacking creature");
|
||||
}
|
||||
|
||||
public AttackingFilterCreatureCount(FilterCreaturePermanent filter, String message) {
|
||||
this.filter = filter;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public AttackingFilterCreatureCount(final AttackingFilterCreatureCount dynamicValue) {
|
||||
super();
|
||||
this.message = dynamicValue.message;
|
||||
this.filter = dynamicValue.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
int count = 0;
|
||||
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
||||
for (UUID permId : combatGroup.getAttackers()) {
|
||||
Permanent attacker = game.getPermanent(permId);
|
||||
if (filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttackingFilterCreatureCount copy() {
|
||||
return new AttackingFilterCreatureCount(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
}
|
||||
}
|
||||
|
|
@ -32,11 +32,11 @@ public enum GateYouControlCount implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
return "1"; // uses "for each" effects, so must be 1, not X
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "gate you control";
|
||||
return "Gate you control";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.Iterator;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
|
@ -16,8 +14,9 @@ import mage.filter.common.FilterCreaturePermanent;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class BoostControlledEffect extends ContinuousEffectImpl {
|
||||
|
|
@ -56,10 +55,10 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
|
|||
* @param power
|
||||
* @param toughness
|
||||
* @param duration
|
||||
* @param filter AnotherPredicate is not working, you need to use the
|
||||
* excludeSource option
|
||||
* @param lockedIn if true, power and toughness will be calculated only
|
||||
* once, when the ability resolves
|
||||
* @param filter AnotherPredicate is not working, you need to use the
|
||||
* excludeSource option
|
||||
* @param lockedIn if true, power and toughness will be calculated only
|
||||
* once, when the ability resolves
|
||||
* @param excludeSource
|
||||
*/
|
||||
public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource, boolean lockedIn) {
|
||||
|
|
@ -105,7 +104,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (this.affectedObjectsSet) {
|
||||
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) {
|
||||
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext(); ) {
|
||||
Permanent permanent = it.next().getPermanent(game);
|
||||
if (permanent != null) {
|
||||
permanent.addPower(power.calculate(game, source, this));
|
||||
|
|
@ -126,7 +125,6 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
private void setText() {
|
||||
String message = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (excludeSource) {
|
||||
sb.append("other ");
|
||||
|
|
@ -150,6 +148,9 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
|
|||
sb.append(t);
|
||||
|
||||
sb.append((duration == Duration.EndOfTurn ? " until end of turn" : ""));
|
||||
|
||||
// where X
|
||||
String message = null;
|
||||
if (t.equals("X")) {
|
||||
message = toughness.getMessage();
|
||||
} else if (p.equals("X")) {
|
||||
|
|
@ -158,6 +159,17 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
|
|||
if (message != null && !message.isEmpty()) {
|
||||
sb.append(", where X is ").append(message);
|
||||
}
|
||||
|
||||
// for each
|
||||
if (message == null) {
|
||||
message = toughness.getMessage();
|
||||
if (message.isEmpty()) {
|
||||
message = power.getMessage();
|
||||
}
|
||||
if (!message.isEmpty()) {
|
||||
sb.append(" for each " + message);
|
||||
}
|
||||
}
|
||||
staticText = sb.toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ import mage.game.Game;
|
|||
/**
|
||||
* Simple implementation of a {@link CostModificationEffect} offering simplified
|
||||
* construction to setup the object for use by the mage framework.
|
||||
* <p>
|
||||
* WARNING, if you implement custom effect and it can works on stack only (e.g. it need spell's targets to check) then
|
||||
* use different apply code:
|
||||
* - one for get playable mode before spell puts on stack (apply maximum possible cost reduction, use game.inCheckPlayableState()).
|
||||
* - one for normal mode after spell puts on stack (apply real cost reduction)
|
||||
* Example: TorgaarFamineIncarnate
|
||||
*
|
||||
* @author maurer.it_at_gmail.com
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.CostModificationType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author Styxo
|
||||
*/
|
||||
public class SourceCostReductionForEachCardInGraveyardEffect extends CostModificationEffectImpl {
|
||||
|
||||
private FilterCard filter;
|
||||
|
||||
public SourceCostReductionForEachCardInGraveyardEffect() {
|
||||
this(StaticFilters.FILTER_CARD);
|
||||
}
|
||||
|
||||
public SourceCostReductionForEachCardInGraveyardEffect(FilterCard filter) {
|
||||
super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
this.filter = filter;
|
||||
staticText = "{this} costs {1} less to cast for each " + filter.getMessage() + " in your graveyard";
|
||||
}
|
||||
|
||||
private SourceCostReductionForEachCardInGraveyardEffect(SourceCostReductionForEachCardInGraveyardEffect effect) {
|
||||
super(effect);
|
||||
this.filter = effect.filter.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
int reductionAmount = player.getGraveyard().count(filter, game);
|
||||
CardUtil.reduceCost(abilityToModify, reductionAmount);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) {
|
||||
return game.getCard(abilityToModify.getSourceId()) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceCostReductionForEachCardInGraveyardEffect copy() {
|
||||
return new SourceCostReductionForEachCardInGraveyardEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.constants.CostModificationType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class SpellCostReductionForEachSourceEffect extends CostModificationEffectImpl {
|
||||
|
||||
private final DynamicValue eachAmount;
|
||||
private ManaCosts<ManaCost> reduceManaCosts;
|
||||
private final int reduceGenericMana;
|
||||
|
||||
public SpellCostReductionForEachSourceEffect(int reduceGenericMana, DynamicValue eachAmount) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
this.eachAmount = eachAmount;
|
||||
this.reduceManaCosts = null;
|
||||
this.reduceGenericMana = reduceGenericMana;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("this spell costs {")
|
||||
.append(this.reduceGenericMana)
|
||||
.append("} less to cast for each ")
|
||||
.append(this.eachAmount.getMessage());
|
||||
this.staticText = sb.toString();
|
||||
}
|
||||
|
||||
public SpellCostReductionForEachSourceEffect(ManaCosts<ManaCost> reduceManaCosts, DynamicValue eachAmount) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
this.eachAmount = eachAmount;
|
||||
this.reduceManaCosts = reduceManaCosts;
|
||||
this.reduceGenericMana = 0;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("this spell costs ");
|
||||
for (String manaSymbol : reduceManaCosts.getSymbols()) {
|
||||
sb.append(manaSymbol);
|
||||
}
|
||||
sb.append(" less to cast for each ").append(this.eachAmount.getMessage());
|
||||
this.staticText = sb.toString();
|
||||
}
|
||||
|
||||
|
||||
protected SpellCostReductionForEachSourceEffect(final SpellCostReductionForEachSourceEffect effect) {
|
||||
super(effect);
|
||||
this.eachAmount = effect.eachAmount;
|
||||
this.reduceManaCosts = effect.reduceManaCosts;
|
||||
this.reduceGenericMana = effect.reduceGenericMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
int needReduceAmount = eachAmount.calculate(game, source, this);
|
||||
if (needReduceAmount > 0) {
|
||||
if (reduceManaCosts != null) {
|
||||
// color reduce
|
||||
ManaCosts<ManaCost> needReduceMana = new ManaCostsImpl<>();
|
||||
for (int i = 0; i <= needReduceAmount; i++) {
|
||||
needReduceMana.add(reduceManaCosts.copy());
|
||||
}
|
||||
CardUtil.adjustCost((SpellAbility) abilityToModify, needReduceMana, false);
|
||||
} else {
|
||||
// generic reduce
|
||||
CardUtil.reduceCost(abilityToModify, needReduceAmount * this.reduceGenericMana);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellCostReductionForEachSourceEffect copy() {
|
||||
return new SpellCostReductionForEachSourceEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,11 +37,10 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
|
|||
for (String manaSymbol : manaCostsToReduce.getSymbols()) {
|
||||
sb.append(manaSymbol);
|
||||
}
|
||||
sb.append(" less");
|
||||
sb.append(" less to cast");
|
||||
if (this.condition != null) {
|
||||
sb.append(" to if ").append(this.condition.toString());
|
||||
sb.append(" if ").append(this.condition.toString());
|
||||
}
|
||||
|
||||
this.staticText = sb.toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.constants.CostModificationType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
public class SpellCostReductionSourceForOpponentsEffect extends CostModificationEffectImpl {
|
||||
|
||||
public SpellCostReductionSourceForOpponentsEffect() {
|
||||
this("undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>");
|
||||
}
|
||||
|
||||
public SpellCostReductionSourceForOpponentsEffect(String newStaticText) {
|
||||
super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
staticText = newStaticText;
|
||||
}
|
||||
|
||||
public SpellCostReductionSourceForOpponentsEffect(final SpellCostReductionSourceForOpponentsEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
int count = game.getOpponents(source.getControllerId()).size();
|
||||
CardUtil.reduceCost(abilityToModify, count);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellCostReductionSourceForOpponentsEffect copy() {
|
||||
return new SpellCostReductionSourceForOpponentsEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionSourceForOpponentsEffect;
|
||||
import mage.abilities.dynamicvalue.common.OpponentsCount;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
|
|
@ -15,7 +11,7 @@ import mage.constants.Zone;
|
|||
public class UndauntedAbility extends SimpleStaticAbility {
|
||||
|
||||
public UndauntedAbility() {
|
||||
super(Zone.ALL, new SpellCostReductionSourceForOpponentsEffect("undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>"));
|
||||
super(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, OpponentsCount.instance));
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
|
|
@ -28,4 +24,8 @@ public class UndauntedAbility extends SimpleStaticAbility {
|
|||
return new UndauntedAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue