mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
CostAdjustment: add new Adjustment class for raising non-mana costs
should somehow merged with ManaCostAdjustment later
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -490,6 +490,7 @@ forge-game/src/main/java/forge/game/combat/CombatView.java -text
|
|||||||
forge-game/src/main/java/forge/game/combat/GlobalAttackRestrictions.java -text
|
forge-game/src/main/java/forge/game/combat/GlobalAttackRestrictions.java -text
|
||||||
forge-game/src/main/java/forge/game/cost/Cost.java svneol=native#text/plain
|
forge-game/src/main/java/forge/game/cost/Cost.java svneol=native#text/plain
|
||||||
forge-game/src/main/java/forge/game/cost/CostAddMana.java -text
|
forge-game/src/main/java/forge/game/cost/CostAddMana.java -text
|
||||||
|
forge-game/src/main/java/forge/game/cost/CostAdjustment.java -text
|
||||||
forge-game/src/main/java/forge/game/cost/CostChooseCreatureType.java -text
|
forge-game/src/main/java/forge/game/cost/CostChooseCreatureType.java -text
|
||||||
forge-game/src/main/java/forge/game/cost/CostDamage.java -text
|
forge-game/src/main/java/forge/game/cost/CostDamage.java -text
|
||||||
forge-game/src/main/java/forge/game/cost/CostDecisionMakerBase.java -text
|
forge-game/src/main/java/forge/game/cost/CostDecisionMakerBase.java -text
|
||||||
|
|||||||
255
forge-game/src/main/java/forge/game/cost/CostAdjustment.java
Normal file
255
forge-game/src/main/java/forge/game/cost/CostAdjustment.java
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
package forge.game.cost;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.card.CardStateName;
|
||||||
|
import forge.card.mana.ManaAtom;
|
||||||
|
import forge.card.mana.ManaCostShard;
|
||||||
|
import forge.game.Game;
|
||||||
|
import forge.game.GameObject;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCollection;
|
||||||
|
import forge.game.card.CardFactoryUtil;
|
||||||
|
import forge.game.card.CardLists;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.AbilityActivated;
|
||||||
|
import forge.game.spellability.Spell;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.spellability.TargetChoices;
|
||||||
|
import forge.game.spellability.TargetRestrictions;
|
||||||
|
import forge.game.staticability.StaticAbility;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
|
public class CostAdjustment {
|
||||||
|
|
||||||
|
public static Cost adjust(final Cost cost, final SpellAbility sa) {
|
||||||
|
final Player player = sa.getActivatingPlayer();
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = player.getGame();
|
||||||
|
|
||||||
|
if (sa.isTrigger()) {
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isStateChangeToFaceDown = false;
|
||||||
|
if (sa.isSpell() && ((Spell) sa).isCastFaceDown()) {
|
||||||
|
// Turn face down to apply cost modifiers correctly
|
||||||
|
host.setState(CardStateName.FaceDown, false);
|
||||||
|
isStateChangeToFaceDown = true;
|
||||||
|
} // isSpell
|
||||||
|
|
||||||
|
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
|
||||||
|
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack));
|
||||||
|
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Command));
|
||||||
|
if (!cardsOnBattlefield.contains(host)) {
|
||||||
|
cardsOnBattlefield.add(host);
|
||||||
|
}
|
||||||
|
final List<StaticAbility> raiseAbilities = new ArrayList<StaticAbility>();
|
||||||
|
|
||||||
|
// Sort abilities to apply them in proper order
|
||||||
|
for (Card c : cardsOnBattlefield) {
|
||||||
|
for (final StaticAbility stAb : c.getStaticAbilities()) {
|
||||||
|
if (stAb.getMapParams().get("Mode").equals("RaiseCost2")) {
|
||||||
|
raiseAbilities.add(stAb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Raise cost
|
||||||
|
for (final StaticAbility stAb : raiseAbilities) {
|
||||||
|
applyRaise(cost, sa, stAb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset card state (if changed)
|
||||||
|
if (isStateChangeToFaceDown) {
|
||||||
|
host.setState(CardStateName.Original, false);
|
||||||
|
}
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyRaise(final Cost cost, final SpellAbility sa, final StaticAbility st) {
|
||||||
|
final Map<String, String> params = st.getMapParams();
|
||||||
|
final Card hostCard = st.getHostCard();
|
||||||
|
final Card card = sa.getHostCard();
|
||||||
|
|
||||||
|
if (!checkRequirement(sa, st)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cost part = new Cost(params.get("Cost"), sa.isAbility());
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (params.containsKey("ForEachShard")) {
|
||||||
|
CostPartMana mc = cost.getCostMana();
|
||||||
|
if (mc != null) {
|
||||||
|
byte atom = ManaAtom.fromName(params.get("ForEachShard").toLowerCase());
|
||||||
|
for (ManaCostShard shard : mc.getManaCostFor(sa)) {
|
||||||
|
if ((shard.getColorMask() & atom) != 0) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (params.containsKey("Amount")) {
|
||||||
|
String amount = params.get("Amount");
|
||||||
|
if ("Escalate".equals(amount)) {
|
||||||
|
SpellAbility sub = sa;
|
||||||
|
while(sub != null) {
|
||||||
|
if (!sub.getSVar("CharmOrder").equals("")) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
sub = sub.getSubAbility();
|
||||||
|
}
|
||||||
|
} else if ("Strive".equals(amount)) {
|
||||||
|
for (TargetChoices tc : sa.getAllTargetChoices()) {
|
||||||
|
count += tc.getNumTargeted();
|
||||||
|
}
|
||||||
|
--count;
|
||||||
|
} else {
|
||||||
|
if (StringUtils.isNumeric(amount)) {
|
||||||
|
count = Integer.parseInt(amount);
|
||||||
|
} else {
|
||||||
|
if (params.containsKey("AffectedAmount")) {
|
||||||
|
count = CardFactoryUtil.xCount(card, hostCard.getSVar(amount));
|
||||||
|
} else {
|
||||||
|
count = AbilityUtils.calculateAmount(hostCard, amount, sa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Amount 1 as default
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < count; ++i) {
|
||||||
|
cost.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkRequirement(final SpellAbility sa, final StaticAbility st) {
|
||||||
|
if (st.isSuppressed() || !st.checkConditions()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, String> params = st.getMapParams();
|
||||||
|
final Card hostCard = st.getHostCard();
|
||||||
|
final Player activator = sa.getActivatingPlayer();
|
||||||
|
final Card card = sa.getHostCard();
|
||||||
|
|
||||||
|
if (params.containsKey("Type")) {
|
||||||
|
final String type = params.get("Type");
|
||||||
|
if (type.equals("Spell")) {
|
||||||
|
if (!sa.isSpell()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (params.containsKey("OnlyFirstSpell")) {
|
||||||
|
if (activator == null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CardCollection list = CardLists.filterControlledBy(activator.getGame().getStack().getSpellsCastThisTurn(), activator);
|
||||||
|
if (params.containsKey("ValidCard")) {
|
||||||
|
list = CardLists.getValidCards(list, params.get("ValidCard"), hostCard.getController(), hostCard);
|
||||||
|
}
|
||||||
|
if (list.size() > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type.equals("Ability")) {
|
||||||
|
if (!(sa instanceof AbilityActivated) || sa.isReplacementAbility()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("NonManaAbility")) {
|
||||||
|
if (!(sa instanceof AbilityActivated) || sa.isManaAbility() || sa.isReplacementAbility()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("Buyback")) {
|
||||||
|
if (!sa.isBuyBackAbility()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("Cycling")) {
|
||||||
|
if (!sa.isCycling()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("Dash")) {
|
||||||
|
if (!sa.isDash()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("Equip")) {
|
||||||
|
if (!(sa instanceof AbilityActivated) || !sa.hasParam("Equip")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("Flashback")) {
|
||||||
|
if (!sa.isFlashBackAbility()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("MorphUp")) {
|
||||||
|
if (!sa.isMorphUp()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("MorphDown")) {
|
||||||
|
if (!sa.isSpell() || !((Spell) sa).isCastFaceDown()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("SelfMonstrosity")) {
|
||||||
|
if (!(sa instanceof AbilityActivated) || !sa.hasParam("Monstrosity") || sa.isTemporary()) {
|
||||||
|
// Nemesis of Mortals
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (type.equals("SelfIntrinsicAbility")) {
|
||||||
|
if (!(sa instanceof AbilityActivated) || sa.isReplacementAbility() || sa.isTemporary()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (params.containsKey("AffectedZone")) {
|
||||||
|
List<ZoneType> zones = ZoneType.listValueOf(params.get("AffectedZone"));
|
||||||
|
boolean found = false;
|
||||||
|
for(ZoneType zt : zones) {
|
||||||
|
if(card.isInZone(zt))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (params.containsKey("ValidTarget")) {
|
||||||
|
TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
if (tgt == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean targetValid = false;
|
||||||
|
for (GameObject target : sa.getTargets().getTargets()) {
|
||||||
|
if (target.isValid(params.get("ValidTarget").split(","), hostCard.getController(), hostCard, sa)) {
|
||||||
|
targetValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!targetValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (params.containsKey("ValidSpellTarget")) {
|
||||||
|
TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
if (tgt == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean targetValid = false;
|
||||||
|
for (SpellAbility target : sa.getTargets().getTargetSpells()) {
|
||||||
|
Card targetCard = target.getHostCard();
|
||||||
|
if (targetCard.isValid(params.get("ValidSpellTarget").split(","), hostCard.getController(), hostCard, sa)) {
|
||||||
|
targetValid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!targetValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class CostPayment {
|
public class CostPayment {
|
||||||
private final Cost cost;
|
private final Cost cost;
|
||||||
|
private Cost adjustedCost;
|
||||||
private final SpellAbility ability;
|
private final SpellAbility ability;
|
||||||
private final List<CostPart> paidCostParts = new ArrayList<CostPart>();
|
private final List<CostPart> paidCostParts = new ArrayList<CostPart>();
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ public class CostPayment {
|
|||||||
*/
|
*/
|
||||||
public CostPayment(final Cost cost, final SpellAbility abil) {
|
public CostPayment(final Cost cost, final SpellAbility abil) {
|
||||||
this.cost = cost;
|
this.cost = cost;
|
||||||
|
this.adjustedCost = cost;
|
||||||
this.ability = abil;
|
this.ability = abil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ public class CostPayment {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public final boolean isFullyPaid() {
|
public final boolean isFullyPaid() {
|
||||||
for (final CostPart part : this.cost.getCostParts()) {
|
for (final CostPart part : adjustedCost.getCostParts()) {
|
||||||
if (!this.paidCostParts.contains(part)) {
|
if (!this.paidCostParts.contains(part)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -136,7 +138,8 @@ public class CostPayment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean payCost(final CostDecisionMakerBase decisionMaker) {
|
public boolean payCost(final CostDecisionMakerBase decisionMaker) {
|
||||||
final List<CostPart> costParts = this.getCost().getCostPartsWithZeroMana();
|
adjustedCost = CostAdjustment.adjust(cost, ability);
|
||||||
|
final List<CostPart> costParts = adjustedCost.getCostPartsWithZeroMana();
|
||||||
for (final CostPart part : costParts) {
|
for (final CostPart part : costParts) {
|
||||||
// Wrap the cost and push onto the cost stack
|
// Wrap the cost and push onto the cost stack
|
||||||
decisionMaker.getPlayer().getGame().costPaymentStack.push(new IndividualCostPaymentInstance(part, this));
|
decisionMaker.getPlayer().getGame().costPaymentStack.push(new IndividualCostPaymentInstance(part, this));
|
||||||
@@ -171,8 +174,9 @@ public class CostPayment {
|
|||||||
|
|
||||||
Map<CostPart, PaymentDecision> decisions = new HashMap<CostPart, PaymentDecision>();
|
Map<CostPart, PaymentDecision> decisions = new HashMap<CostPart, PaymentDecision>();
|
||||||
|
|
||||||
|
List<CostPart> parts = CostAdjustment.adjust(cost, ability).getCostParts();
|
||||||
// Set all of the decisions before attempting to pay anything
|
// Set all of the decisions before attempting to pay anything
|
||||||
for (final CostPart part : this.cost.getCostParts()) {
|
for (final CostPart part : parts) {
|
||||||
PaymentDecision decision = part.accept(decisionMaker);
|
PaymentDecision decision = part.accept(decisionMaker);
|
||||||
if (null == decision) return false;
|
if (null == decision) return false;
|
||||||
|
|
||||||
@@ -187,7 +191,7 @@ public class CostPayment {
|
|||||||
decisions.put(part, decision);
|
decisions.put(part, decision);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final CostPart part : this.cost.getCostParts()) {
|
for (final CostPart part : parts) {
|
||||||
// wrap the payment and push onto the cost stack
|
// wrap the payment and push onto the cost stack
|
||||||
decisionMaker.getPlayer().getGame().costPaymentStack.push(new IndividualCostPaymentInstance(part, this));
|
decisionMaker.getPlayer().getGame().costPaymentStack.push(new IndividualCostPaymentInstance(part, this));
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import forge.game.card.CardView;
|
|||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostAddMana;
|
import forge.game.cost.CostAddMana;
|
||||||
|
import forge.game.cost.CostAdjustment;
|
||||||
import forge.game.cost.CostDamage;
|
import forge.game.cost.CostDamage;
|
||||||
import forge.game.cost.CostDiscard;
|
import forge.game.cost.CostDiscard;
|
||||||
import forge.game.cost.CostDraw;
|
import forge.game.cost.CostDraw;
|
||||||
@@ -281,7 +282,7 @@ public class HumanPlay {
|
|||||||
current = Iterables.getFirst(AbilityUtils.getDefinedCards(source, sourceAbility.getParam("ShowCurrentCard"), sourceAbility), null);
|
current = Iterables.getFirst(AbilityUtils.getDefinedCards(source, sourceAbility.getParam("ShowCurrentCard"), sourceAbility), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<CostPart> parts = cost.getCostParts();
|
final List<CostPart> parts = CostAdjustment.adjust(cost, sourceAbility).getCostParts();
|
||||||
final List<CostPart> remainingParts = new ArrayList<CostPart>(parts);
|
final List<CostPart> remainingParts = new ArrayList<CostPart>(parts);
|
||||||
CostPart costPart = null;
|
CostPart costPart = null;
|
||||||
if (!parts.isEmpty()) {
|
if (!parts.isEmpty()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user