diff --git a/.gitattributes b/.gitattributes index c366b8f2144..4653a18f863 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8740,6 +8740,7 @@ src/forge/card/abilityFactory/AbilityFactory_ChangeZone.java -text svneol=native src/forge/card/abilityFactory/AbilityFactory_Choose.java -text svneol=native#text/plain src/forge/card/abilityFactory/AbilityFactory_Clash.java -text svneol=native#text/plain src/forge/card/abilityFactory/AbilityFactory_Combat.java -text svneol=native#text/plain +src/forge/card/abilityFactory/AbilityFactory_Copy.java -text svneol=native#text/plain src/forge/card/abilityFactory/AbilityFactory_CounterMagic.java -text svneol=native#text/plain src/forge/card/abilityFactory/AbilityFactory_Counters.java -text svneol=native#text/plain src/forge/card/abilityFactory/AbilityFactory_DealDamage.java -text svneol=native#text/plain diff --git a/src/forge/card/abilityFactory/AbilityFactory.java b/src/forge/card/abilityFactory/AbilityFactory.java index e03356e7313..7366f66c57d 100644 --- a/src/forge/card/abilityFactory/AbilityFactory.java +++ b/src/forge/card/abilityFactory/AbilityFactory.java @@ -631,6 +631,15 @@ public class AbilityFactory { SA = AbilityFactory_Choose.createDrawbackChooseType(this); } + if(API.equals("CopyPermanent")){ + if(isAb) + SA = AbilityFactory_Copy.createAbilityCopyPermanent(this); + else if(isSp) + SA = AbilityFactory_Copy.createSpellCopyPermanent(this); + else if(isDb) + SA = AbilityFactory_Copy.createDrawbackCopyPermanent(this); + } + if (SA == null) throw new RuntimeException("AbilityFactory : SpellAbility was not created for "+hostCard.getName()+". Looking for API: "+API); diff --git a/src/forge/card/abilityFactory/AbilityFactory_Copy.java b/src/forge/card/abilityFactory/AbilityFactory_Copy.java new file mode 100644 index 00000000000..6f4df594e59 --- /dev/null +++ b/src/forge/card/abilityFactory/AbilityFactory_Copy.java @@ -0,0 +1,358 @@ +package forge.card.abilityFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Random; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.Command; +import forge.ComputerUtil; +import forge.Constant; +import forge.MyRandom; +import forge.card.cardFactory.CardFactory; +import forge.card.cardFactory.CardFactoryUtil; +import forge.card.spellability.Ability; +import forge.card.spellability.Ability_Activated; +import forge.card.spellability.Ability_Sub; +import forge.card.spellability.Spell; +import forge.card.spellability.SpellAbility; +import forge.card.spellability.Target; +import forge.card.trigger.Trigger; + +public class AbilityFactory_Copy { + + // ************************************************************************* + // ************************* CopyPermanent ********************************* + // ************************************************************************* + + public static SpellAbility createAbilityCopyPermanent(final AbilityFactory af) { + + final SpellAbility abCopyPermanent = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { + private static final long serialVersionUID = 4557071554433108024L; + + @Override + public String getStackDescription() { + return copyPermanentStackDescription(af, this); + } + + @Override + public boolean canPlayAI() { + return copyPermanentCanPlayAI(af, this); + } + + @Override + public void resolve() { + copyPermanentResolve(af, this); + } + + @Override + public boolean doTrigger(boolean mandatory) { + return copyPermanentTriggerAI(af, this, mandatory); + } + + }; + return abCopyPermanent; + } + + public static SpellAbility createSpellCopyPermanent(final AbilityFactory af) { + final SpellAbility spCopyPermanent = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { + private static final long serialVersionUID = 3313370358993251728L; + + @Override + public String getStackDescription() { + return copyPermanentStackDescription(af, this); + } + + @Override + public boolean canPlayAI() { + return copyPermanentCanPlayAI(af, this); + } + + @Override + public void resolve() { + copyPermanentResolve(af, this); + } + + }; + return spCopyPermanent; + } + + public static SpellAbility createDrawbackCopyPermanent(final AbilityFactory af) { + final SpellAbility dbCopyPermanent = new Ability_Sub(af.getHostCard(), af.getAbTgt()) { + private static final long serialVersionUID = -7725564505830285184L; + + @Override + public String getStackDescription(){ + return copyPermanentStackDescription(af, this); + } + + @Override + public void resolve() { + copyPermanentResolve(af, this); + } + + @Override + public boolean chkAI_Drawback() { + return true; + } + + @Override + public boolean doTrigger(boolean mandatory) { + return copyPermanentTriggerAI(af, this, mandatory); + } + + }; + return dbCopyPermanent; + } + + private static String copyPermanentStackDescription(AbilityFactory af, SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + + if (!(sa instanceof Ability_Sub)) + sb.append(sa.getSourceCard().getName()).append(" - "); + else + sb.append(" "); + + ArrayList tgtCards; + + Target tgt = af.getAbTgt(); + if (tgt != null) + tgtCards = tgt.getTargetCards(); + else + tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), af.getMapParams().get("Defined"), sa); + + sb.append("Copy "); + Iterator it = tgtCards.iterator(); + while(it.hasNext()) { + sb.append(it.next()); + if(it.hasNext()) sb.append(", "); + } + sb.append("."); + + Ability_Sub abSub = sa.getSubAbility(); + if(abSub != null) { + sb.append(abSub.getStackDescription()); + } + + return sb.toString(); + } + + private static boolean copyPermanentCanPlayAI(final AbilityFactory af, final SpellAbility sa) { + //TODO - I'm sure someone can do this AI better + + HashMap params = af.getMapParams(); + if(params.containsKey("SacAtEOT") && !AllZone.Phase.is(Constant.Phase.Main1)) { + return false; + } + else return copyPermanentTriggerAI(af, sa, false); + } + + private static boolean copyPermanentTriggerAI(final AbilityFactory af, final SpellAbility sa, boolean mandatory){ + //HashMap params = af.getMapParams(); + Card source = sa.getSourceCard(); + + if (!ComputerUtil.canPayCost(sa) && !mandatory) + return false; + + double chance = .4; // 40 percent chance with instant speed stuff + if (AbilityFactory.isSorcerySpeed(sa)) + chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT) + Random r = MyRandom.random; + boolean randomReturn = r.nextFloat() <= Math.pow(chance, source.getAbilityUsed() + 1); + + ////// + // Targeting + + Target abTgt = sa.getTarget(); + + if (abTgt != null){ + CardList list = AllZoneUtil.getCardsInPlay(); + list = list.getValidCards(abTgt.getValidTgts(), source.getController(), source); + abTgt.resetTargets(); + // target loop + while(abTgt.getNumTargeted() < abTgt.getMaxTargets(sa.getSourceCard(), sa)){ + if (list.size() == 0){ + if (abTgt.getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa) || abTgt.getNumTargeted() == 0){ + abTgt.resetTargets(); + return false; + } + else{ + // todo is this good enough? for up to amounts? + break; + } + } + + Card choice; + if(list.filter(AllZoneUtil.creatures).size() > 0) { + choice = CardFactoryUtil.AI_getBestCreature(list); + } + else { + choice = CardFactoryUtil.AI_getMostExpensivePermanent(list, source, true); + } + + if (choice == null){ // can't find anything left + if (abTgt.getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa) || abTgt.getNumTargeted() == 0){ + abTgt.resetTargets(); + return false; + } + else{ + // todo is this good enough? for up to amounts? + break; + } + } + list.remove(choice); + abTgt.addTarget(choice); + } + } + else{ + //if no targeting, it should always be ok + } + + //end Targeting + + if (af.hasSubAbility()){ + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null){ + return randomReturn && abSub.chkAI_Drawback(); + } + } + return randomReturn; + } + + private static void copyPermanentResolve(final AbilityFactory af, final SpellAbility sa) { + HashMap params = af.getMapParams(); + Card card = af.getHostCard(); + ArrayList keywords = new ArrayList(); + if(params.containsKey("Keywords")) { + keywords.addAll(Arrays.asList(params.get("Keywords").split(" & "))); + } + + ArrayList tgtCards; + + Target tgt = af.getAbTgt(); + if (tgt != null) + tgtCards = tgt.getTargetCards(); + else + tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), af.getMapParams().get("Defined"), sa); + + for(Card c : tgtCards) { + if (tgt == null || CardFactoryUtil.canTarget(card, c)) { + + //start copied Kiki code + int multiplier = AllZoneUtil.getDoublingSeasonMagnitude(card.getController()); + Card[] crds = new Card[multiplier]; + + for(int i = 0; i < multiplier; i++) { + //TODO: Use central copy methods + Card copy; + if(!c.isToken()) { + //copy creature and put it onto the battlefield + copy = AllZone.CardFactory.getCard(c.getName(), c.getOwner()); + + //when copying something stolen: + copy.setController(c.getController()); + + copy.setToken(true); + copy.setCopiedToken(true); + } + else { //isToken() + copy = CardFactory.copyStats(c); + + copy.setName(c.getName()); + copy.setImageName(c.getImageName()); + + copy.setOwner(c.getController()); + copy.setController(c.getController()); + + copy.setManaCost(c.getManaCost()); + copy.setColor(c.getColor()); + copy.setToken(true); + + copy.setType(c.getType()); + + copy.setBaseAttack(c.getBaseAttack()); + copy.setBaseDefense(c.getBaseDefense()); + } + + //add keywords from params + for(String kw : keywords) { + copy.addIntrinsicKeyword(kw); + } + + //Slight hack in case we copy a creature with triggers. + for(Trigger t : copy.getTriggers()) { + AllZone.TriggerHandler.registerTrigger(t); + } + + copy.setCurSetCode(c.getCurSetCode()); + copy.setImageFilename(c.getImageFilename()); + + if(c.isFaceDown()) { + copy.setIsFaceDown(true); + copy.setManaCost(""); + copy.setBaseAttack(2); + copy.setBaseDefense(2); + copy.setIntrinsicKeyword(new ArrayList()); //remove all keywords + copy.setType(new ArrayList()); //remove all types + copy.addType("Creature"); + copy.clearSpellAbility(); //disallow "morph_up" + copy.setCurSetCode(""); + copy.setImageFilename("morph.jpg"); + } + + AllZone.GameAction.moveToPlay(copy); + crds[i] = copy; + } + + + //have to do this since getTargetCard() might change + //if Kiki-Jiki somehow gets untapped again + final Card[] target = new Card[multiplier]; + for(int i = 0; i < multiplier; i++) { + final int index = i; + target[index] = crds[index]; + + final SpellAbility sac = new Ability(target[index], "0") { + @Override + public void resolve() { + //technically your opponent could steal the token + //and the token shouldn't be sacrificed + if(AllZone.GameAction.isCardInPlay(target[index])) { + AllZone.GameAction.sacrifice(target[index]); //maybe do a setSacrificeAtEOT, but probably not. + //Slight hack in case we copy a creature with triggers + AllZone.TriggerHandler.removeAllFromCard(target[index]); + } + } + }; + + Command atEOT = new Command() { + private static final long serialVersionUID = -4184510100801568140L; + + public void execute() { + sac.setStackDescription("Sacrifice "+target[index]); + AllZone.Stack.addSimultaneousStackEntry(sac); + } + };//Command + if(params.containsKey("SacAtEOT")) { + AllZone.EndOfTurn.addAt(atEOT); + } + //end copied Kiki code + + } + }//end canTarget + + if(af.hasSubAbility()) { + Ability_Sub abSub = sa.getSubAbility(); + if(abSub != null) { + abSub.resolve(); + } + } + }//end foreach Card + }//end resolve + +}//end class AbilityFactory_Copy