Commit 8e38c7ed authored by Hellfish's avatar Hellfish

*Merged from trunk up to r26314

parents c8063233 6a4ec0e3
......@@ -101,6 +101,9 @@ public class ComputerUtilCost {
final CostDiscard disc = (CostDiscard) part;
final String type = disc.getType();
if (type.equals("CARDNAME") && source.getAbilityText().contains("Bloodrush")) {
continue;
}
final List<Card> typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Hand), type.split(","), source.getController(), source);
if (typeList.size() > ai.getMaxHandSize()) {
continue;
......
......@@ -239,20 +239,21 @@ public class DamageDealAi extends DamageAiBase {
final boolean divided = sa.hasParam("DividedAsYouChoose");
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
// target loop
TargetChoices tcs = sa.getTargets();
Player enemy = ai.getOpponent();
if ("PowerDmg".equals(sa.getParam("AILogic"))) {
// check if it is better to target the player instead, the original target is already set in PumpAi.pumpTgtAI()
if (tgt.canTgtCreatureAndPlayer() && this.shouldTgtP(ai, sa, dmg, noPrevention)){
sa.resetTargets();
tcs.add(enemy);
sa.getTargets().add(enemy);
}
return true;
}
sa.resetTargets();
// target loop
TargetChoices tcs = sa.getTargets();
if (tgt.getMaxTargets(source, sa) <= 0) {
return false;
}
......@@ -282,8 +283,8 @@ public class DamageDealAi extends DamageAiBase {
tcs.add(humanCreature);
tgt.addDividedAllocation(humanCreature, assignedDamage);
lastTgt = humanCreature;
dmg -= assignedDamage;
}
dmg -= assignedDamage;
if (!source.hasProtectionFrom(humanCreature)) {
dmgTaken += humanCreature.getNetAttack();
}
......
......@@ -272,6 +272,13 @@ public class GameAction {
c.setPairedWith(null);
}
}
// Reveal if face-down
if (c.isFaceDown()) {
c.setState(CardCharacteristicName.Original);
this.reveal(Collections.singleton(c), c.getOwner(), true, "Face-down card leaves the battlefield");
c.setState(CardCharacteristicName.FaceDown);
copied.setState(CardCharacteristicName.Original);
}
unattachCardLeavingBattlefield(copied);
} else if (toBattlefield) {
// reset timestamp in changezone effects so they have same timestamp if ETB simutaneously
......
......@@ -288,7 +288,7 @@ public class Match {
if (!lostOwnership.isEmpty()) {
List<PaperCard> lostPaperOwnership = new ArrayList<>();
for(Card c : lostOwnership) {
lostPaperOwnership.add(c.getPaperCard());
lostPaperOwnership.add((PaperCard)c.getPaperCard());
}
if (outcome.anteResult.containsKey(fromGame)) {
outcome.anteResult.get(fromGame).addLost(lostPaperOwnership);
......@@ -300,7 +300,7 @@ public class Match {
if (!gainedOwnership.isEmpty()) {
List<PaperCard> gainedPaperOwnership = new ArrayList<>();
for(Card c : gainedOwnership) {
gainedPaperOwnership.add(c.getPaperCard());
gainedPaperOwnership.add((PaperCard)c.getPaperCard());
}
if (outcome.anteResult.containsKey(fromGame)) {
outcome.anteResult.get(fromGame).addWon(gainedPaperOwnership);
......
......@@ -6,6 +6,7 @@ import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCharacteristics;
import forge.game.card.CardFactory;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardUtil;
......@@ -105,8 +106,7 @@ public class CloneEffect extends SpellAbilityEffect {
tgtCard.addAlternateState(CardCharacteristicName.Cloner);
tgtCard.switchStates(CardCharacteristicName.Original, CardCharacteristicName.Cloner);
tgtCard.setState(CardCharacteristicName.Original);
}
else {
} else {
//copy Original state to Cloned
tgtCard.addAlternateState(CardCharacteristicName.Cloned);
tgtCard.switchStates(CardCharacteristicName.Original, CardCharacteristicName.Cloned);
......@@ -114,51 +114,34 @@ public class CloneEffect extends SpellAbilityEffect {
tgtCard.setState(CardCharacteristicName.Original);
}
}
CardCharacteristicName stateToCopy = null;
final CardCharacteristicName origState = cardToCopy.getCurState();
if (copyingSelf) {
stateToCopy = CardCharacteristicName.Cloned;
}
else if (cardToCopy.isFlipCard()) {
stateToCopy = CardCharacteristicName.Original;
cardToCopy.setState(CardCharacteristicName.Cloned);
}
else {
stateToCopy = cardToCopy.getCurState();
CardFactory.copyCopiableCharacteristics(cardToCopy, tgtCard);
if (copyingSelf) {
cardToCopy.setState(origState);
}
CardFactory.copyState(cardToCopy, stateToCopy, tgtCard);
// must call this before addAbilityFactoryAbilities so cloned added abilities are handled correctly
addExtraCharacteristics(tgtCard, sa, origSVars);
CardFactoryUtil.addAbilityFactoryAbilities(tgtCard);
for (int i = 0; i < tgtCard.getStaticAbilityStrings().size(); i++) {
tgtCard.addStaticAbility(tgtCard.getStaticAbilityStrings().get(i));
}
CardFactory.copyCopiableAbilities(cardToCopy, tgtCard);
// restore name if it should be unchanged
if (keepName) {
tgtCard.setName(originalName);
tgtCard.setName(originalName);
}
// If target is a flipped card, also copy the flipped
// If target is a flip card, also set characteristics of the flipped
// state.
if (cardToCopy.isFlipCard()) {
if (!copyingSelf) {
tgtCard.addAlternateState(CardCharacteristicName.Flipped);
tgtCard.setState(CardCharacteristicName.Flipped);
}
CardFactory.copyState(cardToCopy, CardCharacteristicName.Flipped, tgtCard);
addExtraCharacteristics(tgtCard, sa, origSVars);
CardFactoryUtil.addAbilityFactoryAbilities(tgtCard);
for (int i = 0; i < tgtCard.getStaticAbilityStrings().size(); i++) {
tgtCard.addStaticAbility(tgtCard.getStaticAbilityStrings().get(i));
}
final CardCharacteristics flippedState = tgtCard.getState(CardCharacteristicName.Flipped);
if (keepName) {
tgtCard.setName(originalName);
flippedState.setName(originalName);
}
//keep the Clone card image for the cloned card
tgtCard.setImageKey(imageFileName);
if (tgtCard.getCurState() != CardCharacteristicName.Flipped) {
tgtCard.setState(CardCharacteristicName.Original);
}
flippedState.setImageKey(imageFileName);
}
//Clean up copy of cloned state
......@@ -263,7 +246,7 @@ public class CloneEffect extends SpellAbilityEffect {
}
}
// set power of clone
// set ETB tapped of clone
if (sa.hasParam("IntoPlayTapped")) {
tgtCard.setTapped(true);
}
......
......@@ -6,7 +6,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardCharacteristicName;
import forge.card.CardRulesPredicates;
import forge.game.Game;
import forge.game.GameEntity;
......@@ -15,7 +14,6 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardFactory;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
......@@ -148,144 +146,86 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
for (final Card c : tgtCards) {
if ((tgt == null) || c.canBeTargetedBy(sa)) {
boolean wasInAlt = false;
CardCharacteristicName stateName = CardCharacteristicName.Original;
if (c.isInAlternateState()) {
stateName = c.getCurState();
wasInAlt = true;
c.setState(CardCharacteristicName.Original);
}
// start copied Kiki code
int multiplier = numCopies * hostCard.getController().getTokenDoublersMagnitude();
final List<Card> crds = new ArrayList<Card>(multiplier);
for (int i = 0; i < multiplier; i++) {
// TODO Use central copy methods
Card copy;
if (!c.isToken() || c.isCopiedToken()) {
// copy creature and put it onto the battlefield
copy = CardFactory.getCard(c.getPaperCard(), sa.getActivatingPlayer());
copy.setToken(true);
copy.setCopiedToken(true);
} else { // isToken()
copy = CardFactory.copyStats(c, controller);
copy.setName(c.getName());
copy.setImageKey(c.getImageKey());
copy.setManaCost(c.getManaCost());
copy.setColor(c.getColor());
copy.setToken(true);
copy.setType(c.getType());
copy.setBaseAttack(c.getBaseAttack());
copy.setBaseDefense(c.getBaseDefense());
CardFactoryUtil.addAbilityFactoryAbilities(copy);
for (String s : copy.getStaticAbilityStrings()) {
copy.addStaticAbility(s);
}
final Card copy = CardFactory.copyCopiableCharacteristics(c, sa.getActivatingPlayer());
copy.setToken(true);
copy.setCopiedToken(true);
// add keywords from sa
for (final String kw : keywords) {
copy.addIntrinsicKeyword(kw);
}
for (final String type : types) {
copy.addType(type);
}
for (final String svar : svars) {
final String actualsVar = hostCard.getSVar(svar);
copy.setSVar(svar, actualsVar);
}
for (final String s : triggers) {
final String actualTrigger = hostCard.getSVar(s);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, copy, false);
copy.addTrigger(parsedTrigger);
}
CardFactory.copyCopiableAbilities(c, copy);
final Card copyInPlay = game.getAction().moveToPlay(copy);
// when copying something stolen:
copy.setController(controller, 0);
copy.setCurSetCode(c.getCurSetCode());
copyInPlay.setController(controller, 0);
copyInPlay.setCurSetCode(c.getCurSetCode());
if (c.isDoubleFaced()) { // Cloned DFC's can't transform
if (wasInAlt) {
copy.setState(CardCharacteristicName.Transformed);
}
copyInPlay.setCloneOrigin(hostCard);
sa.getHostCard().addClone(copyInPlay);
crds.add(copyInPlay);
if (sa.hasParam("RememberCopied")) {
hostCard.addRemembered(copyInPlay);
}
if (c.isFlipCard()) { // Cloned Flips CAN flip.
copy.setState(CardCharacteristicName.Original);
c.setState(CardCharacteristicName.Original);
copy.setImageKey(c.getImageKey());
if (!c.isInAlternateState()) {
copy.setState(CardCharacteristicName.Flipped);
}
c.setState(CardCharacteristicName.Flipped);
if (sa.hasParam("Tapped")) {
copyInPlay.setTapped(true);
}
if (c.isFaceDown()) {
c.setState(CardCharacteristicName.FaceDown);
if (sa.hasParam("CopyAttacking") && game.getPhaseHandler().inCombat()) {
final GameEntity defender = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("CopyAttacking"), sa).get(0);
game.getCombat().addAttacker(copyInPlay, defender);
}
if (sa.hasParam("AttachedTo")) {
List<Card> list = AbilityUtils.getDefinedCards(hostCard,
sa.getParam("AttachedTo"), sa);
if (list.isEmpty()) {
list = copy.getController().getGame().getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, sa.getParam("AttachedTo"), copy.getController(), copy);
list = copyInPlay.getController().getGame().getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, sa.getParam("AttachedTo"), copyInPlay.getController(), copyInPlay);
}
if (!list.isEmpty()) {
Card attachedTo = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(list, sa, copy + " - Select a card to attach to.");
if (copy.isAura()) {
if (attachedTo.canBeEnchantedBy(copy)) {
copy.enchantEntity(attachedTo);
Card attachedTo = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(list, sa, copyInPlay + " - Select a card to attach to.");
if (copyInPlay.isAura()) {
if (attachedTo.canBeEnchantedBy(copyInPlay)) {
copyInPlay.enchantEntity(attachedTo);
} else {//can't enchant
continue;
}
} else if (copy.isEquipment()) { //Equipment
if (attachedTo.canBeEquippedBy(copy)) {
copy.equipCard(attachedTo);
} else if (copyInPlay.isEquipment()) { //Equipment
if (attachedTo.canBeEquippedBy(copyInPlay)) {
copyInPlay.equipCard(attachedTo);
} else {
continue;
}
} else { // Fortification
copy.fortifyCard(attachedTo);
copyInPlay.fortifyCard(attachedTo);
}
} else {
continue;
}
}
// add keywords from sa
for (final String kw : keywords) {
copy.addIntrinsicKeyword(kw);
}
for (final String type : types) {
copy.addType(type);
}
for (final String svar : svars) {
final String actualsVar = hostCard.getSVar(svar);
copy.setSVar(svar, actualsVar);
}
for (final String s : triggers) {
final String actualTrigger = hostCard.getSVar(s);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, copy, false);
copy.addTrigger(parsedTrigger);
}
copy = game.getAction().moveToPlay(copy);
copy.setCloneOrigin(hostCard);
sa.getHostCard().addClone(copy);
crds.add(copy);
if (sa.hasParam("RememberCopied")) {
hostCard.addRemembered(copy);
}
if (sa.hasParam("Tapped")) {
copy.setTapped(true);
}
if (sa.hasParam("CopyAttacking") && game.getPhaseHandler().inCombat()) {
final GameEntity defender = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("CopyAttacking"), sa).get(0);
game.getCombat().addAttacker(copy, defender);
}
}
if (sa.hasParam("AtEOT")) {
final String location = sa.getParam("AtEOT");
registerDelayedTrigger(sa, location, crds);
}
if (wasInAlt) {
c.setState(stateName);
}
} // end canBeTargetedBy
} // end foreach Card
} // end resolve
......
......@@ -94,6 +94,10 @@ public class CounterEffect extends SpellAbilityEffect {
for (final SpellAbility tgtSA : sas) {
final Card tgtSACard = tgtSA.getHostCard();
// should remember even that spell cannot be countered, e.g. Dovescape
if (sa.hasParam("RememberCounteredCMC")) {
sa.getHostCard().addRemembered((Integer) tgtSACard.getCMC());
}
if (tgtSA.isSpell() && !CardFactoryUtil.isCounterableBy(tgtSACard, sa)) {
continue;
......@@ -108,10 +112,6 @@ public class CounterEffect extends SpellAbilityEffect {
continue;
}
if (sa.hasParam("RememberCounteredCMC")) {
sa.getHostCard().addRemembered((Integer) tgtSACard.getCMC());
}
this.removeFromStack(tgtSA, sa, si);
// Destroy Permanent may be able to be turned into a SubAbility
......
......@@ -79,7 +79,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
source.switchStates(CardCharacteristicName.Original, CardCharacteristicName.Cloner);
source.setState(CardCharacteristicName.Original);
CardCharacteristicName stateToCopy = random.getCurState();
CardFactory.copyState(random, stateToCopy, source);
CardFactory.copyState(random, stateToCopy, source, source.getCurState());
source.setImageKey(imageFileName);
source.setController(activator, 0);
......
......@@ -85,6 +85,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public class Card extends GameEntity implements Comparable<Card> {
private final int uniqueNumber;
private final IPaperCard paperCard;
private final Map<CardCharacteristicName, CardCharacteristics> characteristicsMap
= new EnumMap<CardCharacteristicName, CardCharacteristics>(CardCharacteristicName.class);
......@@ -247,10 +248,24 @@ public class Card extends GameEntity implements Comparable<Card> {
}
/**
* Instantiates a new card.
* Instantiates a new card not associated to any paper card.
* @param id the unique id of the new card.
*/
public Card(int id) {
public Card(final int id) {
this(id, null);
}
/**
* Instantiates a new card with a given paper card.
* @param id the unique id of the new card.
* @param paperCard the {@link IPaperCard} of which the new card is a
* representation, or {@code null} if this new {@link Card} doesn't represent any paper
* card.
* @see IPaperCard
*/
public Card(final int id, final IPaperCard paperCard) {
this.uniqueNumber = id;
this.paperCard = paperCard;
this.characteristicsMap.put(CardCharacteristicName.Original, new CardCharacteristics());
this.characteristicsMap.put(CardCharacteristicName.FaceDown, CardUtil.getFaceDownCharacteristic());
}
......@@ -8867,15 +8882,20 @@ public class Card extends GameEntity implements Comparable<Card> {
return fromPaperCard(pc, null);
}
public PaperCard getPaperCard() {
public IPaperCard getPaperCard() {
IPaperCard cp = this.paperCard;
if (cp != null) {
return cp;
}
final String name = getName();
final String set = getCurSetCode();
if (StringUtils.isNotBlank(set)) {
PaperCard cp = StaticData.instance().getVariantCards().getCard(name, set);
cp = StaticData.instance().getVariantCards().getCard(name, set);
return cp == null ? StaticData.instance().getCommonCards().getCard(name, set) : cp;
}
PaperCard cp = StaticData.instance().getVariantCards().getCard(name);
cp = StaticData.instance().getVariantCards().getCard(name);
return cp == null ? StaticData.instance().getCommonCards().getCardFromEdition(name, SetPreference.Latest) : cp;
}
......
......@@ -71,27 +71,13 @@ public class CardFactory {
* @return a {@link forge.game.card.Card} object.
*/
public final static Card copyCard(final Card in, boolean assignNewId) {
final CardCharacteristicName curState = in.getCurState();
boolean alternate = false;
if (in.isInAlternateState()) {
alternate = true;
in.setState(CardCharacteristicName.Original);
}
Card out = null;
Card out;
if (!in.isToken() || in.isCopiedToken()) {
out = assignNewId ? getCard(in.getPaperCard(), in.getOwner())
: getCard(in.getPaperCard(), in.getOwner(), in.getUniqueNumber());
} else { // token
out = assignNewId ? new Card(in.getGame().nextCardId()) : new Card(in.getUniqueNumber());
out = assignNewId ? new Card(in.getGame().nextCardId(), in.getPaperCard()) : new Card(in.getUniqueNumber(), in.getPaperCard());
out = CardFactory.copyStats(in, in.getController());
out.setName(in.getName());
out.setImageKey(in.getImageKey());
out.setManaCost(in.getManaCost());
out.setColor(in.getColor());
out.setType(in.getType());
out.setBaseAttack(in.getBaseAttack());
out.setBaseDefense(in.getBaseDefense());
out.setToken(true);
CardFactoryUtil.addAbilityFactoryAbilities(out);
......@@ -99,22 +85,11 @@ public class CardFactory {
out.addStaticAbility(s);
}
}
CardFactory.copyCharacteristics(in, out);
if (in.hasAlternateState()) {
for (final CardCharacteristicName state : in.getStates()) {
in.setState(state);
if (state == CardCharacteristicName.Cloner) {
out.addAlternateState(state);
}
out.setState(state);
CardFactory.copyCharacteristics(in, out);
}
}
if (alternate) {
in.setState(curState);
for (final CardCharacteristicName state : in.getStates()) {
CardFactory.copyState(in, state, out, state);
}
out.setState(curState);
out.setState(in.getCurState());
// I'm not sure if we really should be copying enchant/equip stuff over.
out.setEquipping(in.getEquipping());
......@@ -254,7 +229,7 @@ public class CardFactory {
public final static Card getCard(final IPaperCard cp, final Player owner, final int cardId) {
//System.out.println(cardName);
CardRules cardRules = cp.getRules();
final Card c = readCard(cardRules, cardId);
final Card c = readCard(cardRules, cp, cardId);
c.setRules(cardRules);
c.setOwner(owner);
buildAbilities(c);
......@@ -383,9 +358,9 @@ public class CardFactory {
card.setSVar("DamagePWY", "Count$YourLifeTotal");
}
private static Card readCard(final CardRules rules, int cardId) {
private static Card readCard(final CardRules rules, final IPaperCard paperCard, int cardId) {
final Card card = new Card(cardId);
final Card card = new Card(cardId, paperCard);
// 1. The states we may have:
CardSplitType st = rules.getSplitType();
......@@ -466,93 +441,170 @@ public class CardFactory {
c.setBaseDefenseString(face.getToughness());
}
}
/**
* Create a copy of a card, including its copiable characteristics (but not
* abilities).
* @param from
* @param newOwner
* @return
*/
public static Card copyCopiableCharacteristics(final Card from, final Player newOwner) {
int id = newOwner == null ? 0 : newOwner.getGame().nextCardId();
final Card c = new Card(id, from.getPaperCard());
c.setOwner(newOwner);
c.setCurSetCode(from.getCurSetCode());
copyCopiableCharacteristics(from, c);
return c;
}
/**