mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
add a basic working AF for CopyPermanent. It will need tweaking as new cards are attempted. And the AI could be improved to be more flexible, but basically, it works, and changes related to copy can be made in a central location. Tested with Kiki-Jiki and Myr Propagator.
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
358
src/forge/card/abilityFactory/AbilityFactory_Copy.java
Normal file
358
src/forge/card/abilityFactory/AbilityFactory_Copy.java
Normal file
@@ -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<Card> 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<Card> 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<String,String> 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<String,String> 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<String,String> params = af.getMapParams();
|
||||
Card card = af.getHostCard();
|
||||
ArrayList<String> keywords = new ArrayList<String>();
|
||||
if(params.containsKey("Keywords")) {
|
||||
keywords.addAll(Arrays.asList(params.get("Keywords").split(" & ")));
|
||||
}
|
||||
|
||||
ArrayList<Card> 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<String>()); //remove all keywords
|
||||
copy.setType(new ArrayList<String>()); //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
|
||||
Reference in New Issue
Block a user