mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 04:08:01 +00:00
Merge pull request #2626 from tool4ever/fixnpe6
Fix Churning Reservoir NPE
This commit is contained in:
@@ -358,7 +358,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
return c.getSVar("Targeting").equals("Dies")
|
||||
|| (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d)
|
||||
&& !ComputerUtil.canRegenerate(ai, c)
|
||||
&& !(c.getSVar("SacMe").length() > 0)
|
||||
&& !c.hasSVar("SacMe")
|
||||
&& !ComputerUtilCard.hasActiveUndyingOrPersist(c);
|
||||
}
|
||||
});
|
||||
@@ -437,7 +437,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
return c.getSVar("Targeting").equals("Dies")
|
||||
|| (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d)
|
||||
&& !ComputerUtil.canRegenerate(ai, c)
|
||||
&& !(c.getSVar("SacMe").length() > 0);
|
||||
&& !c.hasSVar("SacMe");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1460,20 +1460,6 @@ public class AbilityUtils {
|
||||
if (unlessCost.equals("CardManaCost")) {
|
||||
cost = new Cost(source.getManaCost(), true);
|
||||
}
|
||||
else if (unlessCost.equals("TriggeredSpellManaCost")) {
|
||||
SpellAbility triggered = (SpellAbility) sa.getRootAbility().getTriggeringObject(AbilityKey.SpellAbility);
|
||||
Card triggeredCard = triggered.getHostCard();
|
||||
if (triggeredCard.getManaCost() == null) {
|
||||
cost = new Cost(ManaCost.ZERO, true);
|
||||
} else {
|
||||
int xCount = triggeredCard.getManaCost().countX();
|
||||
int xPaid = triggeredCard.getXManaCostPaid() * xCount;
|
||||
ManaCostBeingPaid toPay = new ManaCostBeingPaid(triggeredCard.getManaCost());
|
||||
toPay.decreaseShard(ManaCostShard.X, xCount);
|
||||
toPay.increaseGenericMana(xPaid);
|
||||
cost = new Cost(toPay.toManaCost(), true);
|
||||
}
|
||||
}
|
||||
else if (unlessCost.equals("ChosenManaCost")) {
|
||||
if (!source.hasChosenCard()) {
|
||||
cost = new Cost(ManaCost.ZERO, true);
|
||||
@@ -2734,12 +2720,12 @@ public class AbilityUtils {
|
||||
|
||||
// Count$ThisTurnEntered <ZoneDestination> [from <ZoneOrigin>] <Valid>
|
||||
if (sq[0].startsWith("ThisTurnEntered")) {
|
||||
final String[] workingCopy = l[0].split("_");
|
||||
final String[] workingCopy = l[0].split("_", 5);
|
||||
|
||||
ZoneType destination = ZoneType.smartValueOf(workingCopy[1]);
|
||||
final boolean hasFrom = workingCopy[2].equals("from");
|
||||
ZoneType origin = hasFrom ? ZoneType.smartValueOf(workingCopy[3]) : null;
|
||||
String validFilter = workingCopy[hasFrom ? 4 : 2] ;
|
||||
String validFilter = workingCopy[hasFrom ? 4 : 2];
|
||||
|
||||
final List<Card> res = CardUtil.getThisTurnEntered(destination, origin, validFilter, c, ctb);
|
||||
return doXMath(res.size(), expr, c, ctb);
|
||||
@@ -2747,12 +2733,12 @@ public class AbilityUtils {
|
||||
|
||||
// Count$LastTurnEntered <ZoneDestination> [from <ZoneOrigin>] <Valid>
|
||||
if (sq[0].startsWith("LastTurnEntered")) {
|
||||
final String[] workingCopy = l[0].split("_");
|
||||
final String[] workingCopy = l[0].split("_", 5);
|
||||
|
||||
ZoneType destination = ZoneType.smartValueOf(workingCopy[1]);
|
||||
final boolean hasFrom = workingCopy[2].equals("from");
|
||||
ZoneType origin = hasFrom ? ZoneType.smartValueOf(workingCopy[3]) : null;
|
||||
String validFilter = workingCopy[hasFrom ? 4 : 2] ;
|
||||
String validFilter = workingCopy[hasFrom ? 4 : 2];
|
||||
|
||||
final List<Card> res = CardUtil.getLastTurnEntered(destination, origin, validFilter, c, ctb);
|
||||
return doXMath(res.size(), expr, c, ctb);
|
||||
|
||||
@@ -905,7 +905,7 @@ public abstract class SpellAbilityEffect {
|
||||
return activator.getController().chooseSingleEntityForEffect(options, sa, Localizer.getInstance().getMessage("lblChoosePlayer"), null);
|
||||
}
|
||||
|
||||
public void handleExiledWith(final Card movedCard, final SpellAbility cause) {
|
||||
public static void handleExiledWith(final Card movedCard, final SpellAbility cause) {
|
||||
Card exilingSource = cause.getHostCard();
|
||||
// during replacement LKI might be used
|
||||
if (cause.isReplacementAbility() && exilingSource.isLKI()) {
|
||||
|
||||
@@ -1501,32 +1501,13 @@ public class CardProperty {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// syntax example: countersGE9 P1P1 or countersLT12TIME (greater number
|
||||
// than 99 not supported)
|
||||
/*
|
||||
* slapshot5 - fair warning, you cannot use numbers with 2 digits
|
||||
* (greater number than 9 not supported you can use X and the
|
||||
* SVar:X:Number$12 to get two digits. This will need a better fix, and
|
||||
* I have the beginnings of a regex below
|
||||
*/
|
||||
else if (property.startsWith("counters")) {
|
||||
/*
|
||||
* Pattern p = Pattern.compile("[a-z]*[A-Z][A-Z][X0-9]+.*$");
|
||||
* String[] parse = ???
|
||||
* System.out.println("Parsing completed of: "+Property); for (int i
|
||||
* = 0; i < parse.length; i++) {
|
||||
* System.out.println("parse["+i+"]: "+parse[i]); }
|
||||
*/
|
||||
|
||||
// TODO get a working regex out of this pattern so the amount of
|
||||
// digits doesn't matter
|
||||
// syntax example: counters_GE9_P1P1 or counters_LT12_TIME
|
||||
final String[] splitProperty = property.split("_");
|
||||
final String strNum = splitProperty[1].substring(2);
|
||||
final String comparator = splitProperty[1].substring(0, 2);
|
||||
String counterType;
|
||||
int number = AbilityUtils.calculateAmount(source, strNum, spellAbility);
|
||||
counterType = splitProperty[2];
|
||||
final String counterType = splitProperty[2];
|
||||
final int number = AbilityUtils.calculateAmount(source, strNum, spellAbility);
|
||||
|
||||
final int actualnumber = card.getCounters(CounterType.getType(counterType));
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package forge.game.cost;
|
||||
|
||||
import forge.card.CardType;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
@@ -187,11 +188,8 @@ public class CostExile extends CostPartWithList {
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) {
|
||||
final Game game = targetCard.getGame();
|
||||
final Card host = ability.getHostCard();
|
||||
Card newCard = game.getAction().exile(targetCard, null);
|
||||
host.addExiledCard(newCard);
|
||||
newCard.setExiledWith(host);
|
||||
newCard.setExiledBy(ability.getActivatingPlayer());
|
||||
SpellAbilityEffect.handleExiledWith(newCard, ability);
|
||||
return newCard;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,21 +70,11 @@ public class CostUnattach extends CostPartWithList {
|
||||
*/
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
|
||||
final String type = this.getType();
|
||||
if (type.equals("CARDNAME")) {
|
||||
return source.isEquipping();
|
||||
} else if (type.equals("OriginalHost")) {
|
||||
Card originalEquipment = ability.getOriginalHost();
|
||||
return originalEquipment.isEquipping();
|
||||
} else {
|
||||
return CardLists.getValidCards(source.getEquippedBy(), type, payer, source, ability).size() > 0;
|
||||
}
|
||||
return findCardToUnattach(ability.getHostCard(), payer, ability) != null;
|
||||
}
|
||||
|
||||
public Card findCardToUnattach(final Card source, Player activator, SpellAbility ability) {
|
||||
if (getType().equals("CARDNAME")) {
|
||||
if (payCostFromSource()) {
|
||||
if (source.isEquipping()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -503,8 +503,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
|
||||
if (thisHasFizzled) { // Fizzle
|
||||
if (sa.isBestow()) {
|
||||
// 702.102d: if its target is illegal,
|
||||
// the effect making it an Aura spell ends.
|
||||
// 702.102e: if its target is illegal, the effect making it an Aura spell ends.
|
||||
// It continues resolving as a creature spell.
|
||||
source.unanimateBestow();
|
||||
SpellAbility first = source.getFirstSpellAbility();
|
||||
@@ -834,10 +833,9 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
}
|
||||
|
||||
if (activator.equals(activePlayer)) {
|
||||
adjustAuraHost(sa);
|
||||
activePlayerSAs.add(sa);
|
||||
}
|
||||
|
||||
adjustAuraHost(sa);
|
||||
}
|
||||
simultaneousStackEntryList.removeAll(activePlayerSAs);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Churning Reservoir
|
||||
ManaCost:R
|
||||
Types:Artifact
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, put an oil counter on another target nontoken artifact or creature you control.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Artifact.nonToken+YouCtrl+Other,Creature.YouCtrl+Other+nonToken | CounterType$ OIL| CounterNum$ 1
|
||||
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Artifact.nonToken+YouCtrl+Other,Creature.YouCtrl+Other+nonToken | CounterType$ OIL | CounterNum$ 1
|
||||
A:AB$ Token | Cost$ 2 T | TokenScript$ r_1_1_phyrexian_goblin | CheckSVar$ CountCountersRemoved | CheckSVarCompare$ GE1 | SpellDescription$ Create a 1/1 red Phyrexian Goblin creature token. Activate only if an oil counter was removed from a permanent you controlled this turn or a permanent with an oil counter on it was put into a graveyard this turn.
|
||||
SVar:CountCountersRemoved:Count$CountersRemovedThisTurn OIL Card.YouCtrl+inRealZoneBattlefield/Plus.X
|
||||
SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Permanent.YouCtrl+counters_GE1_OIL
|
||||
|
||||
@@ -5,9 +5,8 @@ PT:3/2
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl+Other,Artifact.YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature or artifact you control is put into a graveyard from the battlefield, put an oil counter on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever CARDNAME attacks, you may remove two oil counters from it. When you do, target creature can't block this turn.
|
||||
SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ SubCounter<2/OIL> | SubAbility$ TrigPump | TriggerDescription$ When you do, target creature can't block this turn.
|
||||
SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ SubCounter<2/OIL> | Execute$ TrigPump | TriggerDescription$ When you do, target creature can't block this turn.
|
||||
SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | IsCurse$ True | KW$ HIDDEN CARDNAME can't block.
|
||||
SVar:X:Count$CardCounters.OIL
|
||||
SVar:HasAttackEffect:TRUE
|
||||
DeckHints:Ability$Counters
|
||||
Oracle:Whenever another creature or artifact you control is put into a graveyard from the battlefield, put an oil counter on Forgehammer Centurion.\nWhenever Forgehammer Centurion attacks, you may remove two oil counters from it. When you do, target creature can't block this turn.
|
||||
|
||||
@@ -5,9 +5,9 @@ PT:1/1
|
||||
K:etbCounter:P1P1:3
|
||||
K:Flying
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ At the beginning of your upkeep, you may remove any number of +1/+1 counters from CARDNAME. If you do, create that many 1/1 colorless Tetravite artifact creature tokens. They each have flying and "This creature can't be enchanted."
|
||||
SVar:TrigToken:AB$Token | Cost$ SubCounter<X/P1P1> | TokenAmount$ X | TokenScript$ c_1_1_a_tetravite_flying_noenchant | TokenOwner$ You | RememberTokens$ True
|
||||
SVar:TrigToken:AB$ Token | Cost$ SubCounter<X/P1P1> | TokenAmount$ X | TokenScript$ c_1_1_a_tetravite_flying_noenchant | TokenOwner$ You | RememberTokens$ True
|
||||
SVar:X:Count$xPaid
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounters | TriggerDescription$ At the beginning of your upkeep, you may exile any number of tokens created with CARDNAME. If you do, put that many +1/+1 counters on CARDNAME.
|
||||
SVar:TrigPutCounters:AB$PutCounter | Cost$ Exile<X/Creature.IsRemembered/Tetravite> | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | CostDesc$ Exile any number of tokens put onto the battlefield with CARDNAME.
|
||||
SVar:TrigPutCounters:AB$ PutCounter | Cost$ Exile<X/Creature.IsRemembered/Tetravite> | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | CostDesc$ Exile any number of tokens put onto the battlefield with CARDNAME.
|
||||
DeckHas:Ability$Token|Counters
|
||||
Oracle:Flying\nTetravus enters the battlefield with three +1/+1 counters on it.\nAt the beginning of your upkeep, you may remove any number of +1/+1 counters from Tetravus. If you do, create that many 1/1 colorless Tetravite artifact creature tokens. They each have flying and "This creature can't be enchanted."\nAt the beginning of your upkeep, you may exile any number of tokens created with Tetravus. If you do, put that many +1/+1 counters on Tetravus.
|
||||
|
||||
@@ -379,8 +379,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
private PaymentDecision exileFromMiscZone(final CostExile cost, final SpellAbility sa, final int nNeeded, final CardCollection typeList) {
|
||||
if (typeList.size() < nNeeded) { return null; }
|
||||
|
||||
final List<ZoneType> origin = Lists.newArrayList();
|
||||
origin.add(cost.from);
|
||||
final List<ZoneType> origin = Lists.newArrayList(cost.from);
|
||||
final CardCollection exiled = new CardCollection();
|
||||
|
||||
final List<Card> chosen = controller.chooseCardsForZoneChange(ZoneType.Exile, origin, sa, typeList, mandatory ? nNeeded : 0,
|
||||
|
||||
Reference in New Issue
Block a user