mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -547,6 +547,13 @@ public class GameAction {
|
||||
c.setCastSA(null);
|
||||
} else if (zoneTo.is(ZoneType.Stack)) {
|
||||
c.setCastFrom(zoneFrom.getZoneType());
|
||||
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard()) && !c.isCopiedSpell()) {
|
||||
cause.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
cause.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
c.setCastSA(cause);
|
||||
} else {
|
||||
c.setCastSA(null);
|
||||
}
|
||||
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
|
||||
c.setCastFrom(null);
|
||||
c.setCastSA(null);
|
||||
|
||||
@@ -22,8 +22,10 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
@@ -32,9 +34,15 @@ import forge.game.cost.Cost;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -363,10 +371,11 @@ public final class GameActionUtil {
|
||||
}
|
||||
SpellAbility result = null;
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final PlayerController pc = activator.getController();
|
||||
|
||||
host.getGame().getAction().checkStaticAbilities(false);
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
|
||||
boolean reset = false;
|
||||
|
||||
@@ -429,7 +438,60 @@ public final class GameActionUtil {
|
||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||
|
||||
if (v > 0) {
|
||||
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
|
||||
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(c.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(activator);
|
||||
|
||||
eff.setImageKey(c.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(host);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
|
||||
|
||||
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
|
||||
|
||||
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
||||
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(saAb);
|
||||
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
if (result == null) {
|
||||
result = sa.copy();
|
||||
}
|
||||
|
||||
@@ -6353,6 +6353,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
removeSVar("PayX"); // Temporary AI X announcement variable
|
||||
removeSVar("IsCastFromPlayEffect"); // Temporary SVar indicating that the spell is cast indirectly via AF Play
|
||||
setSunburstValue(0); // Sunburst
|
||||
setXManaCostPaid(0);
|
||||
setXManaCostPaidByColor(null);
|
||||
setKickerMagnitude(0);
|
||||
setPseudoMultiKickerMagnitude(0);
|
||||
}
|
||||
|
||||
public final int getFinalChapterNr() {
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.game.card;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -3010,24 +3009,43 @@ public class CardFactoryUtil {
|
||||
|
||||
inst.addTrigger(parsedTrigger);
|
||||
} else if (keyword.startsWith("Saga")) {
|
||||
// Saga there doesn't need Max value anymore?
|
||||
final String[] k = keyword.split(":");
|
||||
final String[] abs = k[2].split(",");
|
||||
final List<String> abs = Arrays.asList(k[2].split(","));
|
||||
if (abs.size() != Integer.valueOf(k[1])) {
|
||||
throw new RuntimeException("Saga max differ from Ability amount");
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (String ab : abs) {
|
||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||
sa.setChapter(i);
|
||||
int idx = 0;
|
||||
int skipId = 0;
|
||||
for(String ab : abs) {
|
||||
idx += 1;
|
||||
if (idx <= skipId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO better logic for Roman numbers
|
||||
// In the Description try to merge Chapter trigger with the Same Effect
|
||||
String trigStr = "Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield"
|
||||
+ "| CounterType$ LORE | CounterAmount$ EQ" + i
|
||||
+ "| TriggerDescription$ " + Strings.repeat("I", i) + " - " + sa.getDescription();
|
||||
final Trigger t = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
||||
t.setOverridingAbility(sa);
|
||||
inst.addTrigger(t);
|
||||
++i;
|
||||
skipId = idx + abs.subList(idx - 1, abs.size()).lastIndexOf(ab);
|
||||
StringBuilder desc = new StringBuilder();
|
||||
for (int i = idx; i <= skipId; i++) {
|
||||
if (i != idx) {
|
||||
desc.append(", ");
|
||||
}
|
||||
desc.append(TextUtil.toRoman(i));
|
||||
}
|
||||
|
||||
for (int i = idx; i <= skipId; i++) {
|
||||
SpellAbility sa = AbilityFactory.getAbility(card, ab);
|
||||
sa.setChapter(i);
|
||||
|
||||
StringBuilder trigStr = new StringBuilder("Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield");
|
||||
trigStr.append("| CounterType$ LORE | CounterAmount$ EQ").append(i);
|
||||
if (i != idx) {
|
||||
trigStr.append(" | Secondary$ True");
|
||||
}
|
||||
trigStr.append("| TriggerDescription$ ").append(desc).append(" — ").append(sa.getDescription());
|
||||
final Trigger t = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
|
||||
t.setOverridingAbility(sa);
|
||||
inst.addTrigger(t);
|
||||
}
|
||||
}
|
||||
} else if (keyword.equals("Soulbond")) {
|
||||
// Setup ETB trigger for card with Soulbond keyword
|
||||
|
||||
@@ -882,6 +882,10 @@ public class Combat {
|
||||
return true; // is blocking something at the moment
|
||||
}
|
||||
|
||||
if (!blocker.isLKI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
return null != lki && !lki.isAttacker; // was blocking something anyway
|
||||
}
|
||||
@@ -892,7 +896,11 @@ public class Combat {
|
||||
if (blockers != null && blockers.contains(blocker)) {
|
||||
return true; // is blocking the attacker's band at the moment
|
||||
}
|
||||
|
||||
|
||||
if (!blocker.isLKI()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
}
|
||||
}
|
||||
if (mana.addsCounters(sa)) {
|
||||
mana.getManaAbility().createETBCounters(host);
|
||||
mana.getManaAbility().createETBCounters(host, this.owner);
|
||||
}
|
||||
if (mana.triggersWhenSpent()) {
|
||||
mana.getManaAbility().addTriggersWhenSpent(sa, host);
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -19,9 +19,11 @@ package forge.game.spellability;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
@@ -34,6 +36,8 @@ import forge.game.replacement.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -46,7 +50,7 @@ import java.util.regex.Pattern;
|
||||
* <p>
|
||||
* Abstract AbilityMana class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -78,7 +82,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* Constructor for AbilityMana.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sourceCard
|
||||
* a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
@@ -111,7 +115,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* produceMana.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param produced
|
||||
* a {@link java.lang.String} object.
|
||||
* @param player
|
||||
@@ -169,7 +173,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* cannotCounterPaidWith.
|
||||
* </p>
|
||||
* @param saBeingPaid
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public boolean cannotCounterPaidWith(SpellAbility saBeingPaid) {
|
||||
@@ -186,7 +190,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* addKeywords.
|
||||
* </p>
|
||||
* @param saBeingPaid
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public boolean addKeywords(SpellAbility saBeingPaid) {
|
||||
@@ -205,7 +209,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* getKeywords.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String getKeywords() {
|
||||
@@ -217,7 +221,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* addsCounters.
|
||||
* </p>
|
||||
* @param saBeingPaid
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public boolean addsCounters(SpellAbility saBeingPaid) {
|
||||
@@ -227,10 +231,26 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
/**
|
||||
* createETBCounters
|
||||
*/
|
||||
public void createETBCounters(Card c) {
|
||||
public void createETBCounters(Card c, Player controller) {
|
||||
String[] parse = this.addsCounters.split("_");
|
||||
// Convert random SVars if there are other cards with this effect
|
||||
if (c.isValid(parse[0], c.getController(), c, null)) {
|
||||
final Game game = this.sourceCard.getGame();
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(sourceCard.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(controller);
|
||||
|
||||
eff.setImageKey(sourceCard.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(c);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
|
||||
+ " | ETB$ True | CounterNum$ " + parse[2];
|
||||
|
||||
@@ -240,15 +260,37 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
}
|
||||
CardFactoryUtil.setupETBReplacementAbility(sa);
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield "
|
||||
+ " | Secondary$ True | Description$ CARDNAME"
|
||||
+ " enters the battlefield with " + CounterType.valueOf(parse[1]).getName() + " counters.";
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, c, false);
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(sa);
|
||||
|
||||
c.addReplacementEffect(re);
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +311,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* getManaRestrictions.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String getManaRestrictions() {
|
||||
@@ -280,7 +322,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* meetsManaRestrictions.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @return a boolean.
|
||||
@@ -296,7 +338,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
if (restriction.equals("nonSpell")) {
|
||||
return !sa.isSpell();
|
||||
}
|
||||
|
||||
|
||||
if (restriction.equals("CumulativeUpkeep")) {
|
||||
if (sa.isCumulativeupkeep()) {
|
||||
return true;
|
||||
@@ -349,7 +391,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* mana.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public final String mana() {
|
||||
@@ -438,7 +480,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* canProduce.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param s
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
@@ -468,7 +510,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
* <p>
|
||||
* isBasic.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isBasic() {
|
||||
@@ -541,7 +583,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
public Card getSourceCard() {
|
||||
return sourceCard;
|
||||
}
|
||||
|
||||
|
||||
public void setSourceCard(final Card host) {
|
||||
sourceCard = host;
|
||||
}
|
||||
|
||||
@@ -1299,6 +1299,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
String announce = getParam("Announce");
|
||||
if (StringUtils.isBlank(announce)) {
|
||||
mapParams.put("Announce", variable);
|
||||
originalMapParams.put("Announce", variable);
|
||||
return;
|
||||
}
|
||||
String[] announcedOnes = TextUtil.split(announce, ',');
|
||||
@@ -1308,6 +1309,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
mapParams.put("Announce", announce + ";" + variable);
|
||||
originalMapParams.put("Announce", announce + ";" + variable);
|
||||
}
|
||||
|
||||
public boolean isXCost() {
|
||||
|
||||
Reference in New Issue
Block a user