mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
- Added AF ChangeZoneAll with limited AI decision making
- Converted Evacuation and Feldon's Cane as examples - Groundwork for LastKnownInformation being used with Zone movement.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
Name:Evacuation
|
||||
ManaCost:3 U U
|
||||
Types:Instant
|
||||
Text:Return all creatures to their owners' hands.
|
||||
K:spBounceAll:Creature:Hand
|
||||
Text:no text
|
||||
A:SP$ChangeZoneAll | Cost$ 3 U U | ChangeType$ Creature | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return all creatures to their owners' hands.
|
||||
SVar:Rarity:Rare
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/evacuation.jpg
|
||||
SetInfo:8ED|Rare|http://magiccards.info/scans/en/8e/76.jpg
|
||||
|
||||
@@ -2,6 +2,7 @@ Name:Feldon's Cane
|
||||
ManaCost:1
|
||||
Types:Artifact
|
||||
Text:no text
|
||||
A:AB$ChangeZoneAll | Cost$ T Exile<1/CARDNAME> | ChangeType$ Card | Origin$ Graveyard | Destination$ Library | Shuffle$ True | SpellDescription$ Shuffle your graveyard into your library.
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Rarity:Uncommon
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/feldons_cane.jpg
|
||||
|
||||
@@ -242,6 +242,15 @@ public class AbilityFactory {
|
||||
SA = AbilityFactory_ChangeZone.createDrawbackChangeZone(this);
|
||||
}
|
||||
|
||||
if (API.equals("ChangeZoneAll")){
|
||||
if (isAb)
|
||||
SA = AbilityFactory_ChangeZone.createAbilityChangeZoneAll(this);
|
||||
else if (isSp)
|
||||
SA = AbilityFactory_ChangeZone.createSpellChangeZoneAll(this);
|
||||
else if (isDb)
|
||||
SA = AbilityFactory_ChangeZone.createDrawbackChangeZoneAll(this);
|
||||
}
|
||||
|
||||
// Fetch, Retrieve and Bounce should be converted ChangeZone
|
||||
/*
|
||||
if (API.equals("Fetch")){
|
||||
|
||||
@@ -179,7 +179,7 @@ public class AbilityFactory_ChangeZone {
|
||||
}
|
||||
if (abCost.getDiscardCost()) return false;
|
||||
|
||||
if (abCost.getSubCounter()) return true; // only card that uses it is Fertilid
|
||||
if (abCost.getSubCounter()) ; // SubCounter is fine
|
||||
|
||||
}
|
||||
|
||||
@@ -890,5 +890,227 @@ public class AbilityFactory_ChangeZone {
|
||||
return AbilityFactory.getDefinedCards(sa.getSourceCard(), defined, sa).get(0);
|
||||
}
|
||||
|
||||
// *************************************************************************************
|
||||
// ************************** ChangeZoneAll ********************************************
|
||||
// ************ All is non-targeted and should occur similarly to Hidden ***************
|
||||
// ******* Instead of choosing X of type on resolution, all on type go *****************
|
||||
// *************************************************************************************
|
||||
public static SpellAbility createAbilityChangeZoneAll(final AbilityFactory AF){
|
||||
final SpellAbility abChangeZone = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){
|
||||
private static final long serialVersionUID = 3728332812890211671L;
|
||||
|
||||
public boolean canPlayAI(){
|
||||
return changeZoneAllCanPlayAI(AF, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
changeZoneAllResolve(AF, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStackDescription(){
|
||||
return changeZoneAllDescription(AF, this);
|
||||
}
|
||||
|
||||
};
|
||||
setMiscellaneous(AF, abChangeZone);
|
||||
return abChangeZone;
|
||||
}
|
||||
|
||||
public static SpellAbility createSpellChangeZoneAll(final AbilityFactory AF){
|
||||
final SpellAbility spChangeZone = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
|
||||
private static final long serialVersionUID = 3270484211099902059L;
|
||||
|
||||
public boolean canPlayAI(){
|
||||
return changeZoneAllCanPlayAI(AF, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
changeZoneAllResolve(AF, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStackDescription(){
|
||||
return changeZoneAllDescription(AF, this);
|
||||
}
|
||||
};
|
||||
setMiscellaneous(AF, spChangeZone);
|
||||
return spChangeZone;
|
||||
}
|
||||
|
||||
public static SpellAbility createDrawbackChangeZoneAll(final AbilityFactory AF){
|
||||
final SpellAbility dbChangeZone = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) {
|
||||
private static final long serialVersionUID = 3270484211099902059L;
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
changeZoneAllResolve(AF, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean chkAI_Drawback() {
|
||||
return changeZoneAllPlayDrawbackAI(AF, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStackDescription(){
|
||||
return changeZoneAllDescription(AF, this);
|
||||
}
|
||||
};
|
||||
setMiscellaneous(AF, dbChangeZone);
|
||||
return dbChangeZone;
|
||||
}
|
||||
|
||||
|
||||
private static boolean changeZoneAllCanPlayAI(AbilityFactory af, SpellAbility sa){
|
||||
// Change Zone All, can be any type moving from one zone to another
|
||||
Ability_Cost abCost = af.getAbCost();
|
||||
Card source = af.getHostCard();
|
||||
HashMap<String,String> params = af.getMapParams();
|
||||
//String destination = params.get("Destination");
|
||||
String origin = params.get("Origin");
|
||||
|
||||
if (abCost != null){
|
||||
// AI currently disabled for these costs
|
||||
if (abCost.getSacCost()){
|
||||
// Sac is ok in general, but should add some decision making based off what we Sacrifice and what we might get
|
||||
}
|
||||
if (abCost.getLifeCost()){
|
||||
if (AllZone.ComputerPlayer.getLife() - abCost.getLifeAmount() < 4)
|
||||
return false;
|
||||
}
|
||||
if (abCost.getDiscardCost()) return false;
|
||||
|
||||
if (abCost.getSubCounter())
|
||||
; // subcounter is fine
|
||||
|
||||
}
|
||||
|
||||
if (!ComputerUtil.canPayCost(sa))
|
||||
return false;
|
||||
|
||||
Random r = new Random();
|
||||
// prevent run-away activations - first time will always return true
|
||||
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
|
||||
|
||||
// todo: targeting with ChangeZoneAll
|
||||
// really two types of targeting.
|
||||
// Target Player has all their types change zones
|
||||
// or target permanent and do something relative to that permanent
|
||||
// ex. "Return all Auras attached to target"
|
||||
// ex. "Return all blocking/blocked by target creature"
|
||||
|
||||
CardList humanType = AllZoneUtil.getCardsInZone(origin, AllZone.HumanPlayer);
|
||||
humanType = filterListByType(humanType, params, "ChangeType", sa);
|
||||
CardList computerType = AllZoneUtil.getCardsInZone(origin, AllZone.ComputerPlayer);
|
||||
computerType = filterListByType(computerType, params, "ChangeType", sa);
|
||||
|
||||
// todo: improve restrictions on when the AI would want to use this
|
||||
// spBounceAll has some AI we can compare to.
|
||||
if (origin.equals("Hand")){
|
||||
|
||||
}
|
||||
else if (origin.equals("Library")){
|
||||
|
||||
}
|
||||
else if (origin.equals("Battlefield")){
|
||||
// this statement is assuming the AI is trying to use this spell offensively
|
||||
// if the AI is using it defensively, then something else needs to occur
|
||||
if (computerType.size()+1 > humanType.size())
|
||||
return false;
|
||||
|
||||
if (humanType.size() <= 1) // unless that 1 thing is going to kill me soon?
|
||||
return false;
|
||||
|
||||
// Don't cast during main1?
|
||||
if (AllZone.Phase.is(Constant.Phase.Main1, AllZone.ComputerPlayer))
|
||||
return false;
|
||||
}
|
||||
else if (origin.equals("Graveyard")){
|
||||
|
||||
}
|
||||
else if (origin.equals("Exile")){
|
||||
|
||||
}
|
||||
else if (origin.equals("Stack")){
|
||||
// time stop can do something like this:
|
||||
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
||||
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
||||
// otherwise, this situation doesn't exist
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (origin.equals("Sideboard")){
|
||||
// This situation doesn't exist
|
||||
return false;
|
||||
}
|
||||
|
||||
Ability_Sub subAb = sa.getSubAbility();
|
||||
if (subAb != null)
|
||||
chance &= subAb.chkAI_Drawback();
|
||||
|
||||
return ((r.nextFloat() < .8) && chance);
|
||||
}
|
||||
|
||||
private static boolean changeZoneAllPlayDrawbackAI(AbilityFactory af, SpellAbility sa){
|
||||
// if putting cards from hand to library and parent is drawing cards
|
||||
// make sure this will actually do something:
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String changeZoneAllDescription(AbilityFactory af, SpellAbility sa){
|
||||
// TODO: build Stack Description will need expansion as more cards are added
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Card host = af.getHostCard();
|
||||
|
||||
if (!(sa instanceof Ability_Sub))
|
||||
sb.append(host.getName()).append(" -");
|
||||
|
||||
sb.append(" ");
|
||||
|
||||
String[] desc = sa.getDescription().split(":");
|
||||
|
||||
if (desc.length > 1)
|
||||
sb.append(desc[1]);
|
||||
else
|
||||
sb.append(desc[0]);
|
||||
|
||||
Ability_Sub abSub = sa.getSubAbility();
|
||||
if (abSub != null) {
|
||||
sb.append(abSub.getStackDescription());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void changeZoneAllResolve(AbilityFactory af, SpellAbility sa){
|
||||
HashMap<String,String> params = af.getMapParams();
|
||||
String destination = params.get("Destination");
|
||||
String origin = params.get("Origin");
|
||||
CardList cards = AllZoneUtil.getCardsInZone(origin);
|
||||
cards = filterListByType(cards, params, "ChangeType", sa);
|
||||
|
||||
// I don't know if library position is necessary. It's here if it is, just in case
|
||||
int libraryPos = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
|
||||
for(Card c : cards){
|
||||
if (params.containsKey("GainControl"))
|
||||
AllZone.GameAction.moveToPlay(c, sa.getActivatingPlayer());
|
||||
else
|
||||
AllZone.GameAction.moveTo(destination, c, libraryPos);
|
||||
}
|
||||
|
||||
// if Shuffle parameter exists, and any amount of cards were owned by that player, then shuffle that library
|
||||
if (params.containsKey("Shuffle")){
|
||||
if (cards.getOwner(AllZone.HumanPlayer).size() > 0)
|
||||
AllZone.HumanPlayer.shuffle();
|
||||
if (cards.getOwner(AllZone.ComputerPlayer).size() > 0)
|
||||
AllZone.ComputerPlayer.shuffle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6734,43 +6734,6 @@ public class CardFactory implements NewConstants {
|
||||
}//*************** END ************ END **************************
|
||||
*/
|
||||
|
||||
//*************** START *********** START **************************
|
||||
else if(cardName.equals("Feldon's Cane")) {
|
||||
/*
|
||||
* Tap, Exile Feldon's Cane: Shuffle your graveyard into your library.
|
||||
*/
|
||||
Ability_Cost abCost = new Ability_Cost("T Exile<1/CARDNAME>", cardName, true);
|
||||
final Ability_Activated ability = new Ability_Activated(card, abCost, null) {
|
||||
private static final long serialVersionUID = -1299603105585632846L;
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
final Player player = card.getController();
|
||||
CardList grave = AllZoneUtil.getPlayerGraveyard(player);
|
||||
|
||||
for(Card c:grave) {
|
||||
AllZone.GameAction.moveToLibrary(c);
|
||||
}
|
||||
|
||||
player.shuffle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayAI() {
|
||||
CardList lib = AllZoneUtil.getPlayerCardsInLibrary(AllZone.ComputerPlayer);
|
||||
return lib.size() < 5;
|
||||
}
|
||||
|
||||
};//SpellAbility
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(cardName).append(" - Player shuffles grave into library.");
|
||||
ability.setStackDescription(sb.toString());
|
||||
ability.setDescription(abCost+"Shuffle your graveyard into your library.");
|
||||
card.addSpellAbility(ability);
|
||||
}//*************** END ************ END **************************
|
||||
|
||||
|
||||
//*************** START *********** START **************************
|
||||
else if(cardName.equals("Elixir of Immortality")) {
|
||||
/*
|
||||
|
||||
@@ -157,6 +157,14 @@ public class CardList implements Iterable<Card> {
|
||||
});
|
||||
}
|
||||
|
||||
public CardList getOwner(final Player player) {
|
||||
return this.filter(new CardListFilter() {
|
||||
public boolean addCard(Card c) {
|
||||
return c.getOwner().isPlayer(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//cardType is like "Land" or "Goblin", returns a new CardList that is a subset of current CardList
|
||||
public CardList getType(final String cardType) {
|
||||
return this.filter(new CardListFilter() {
|
||||
|
||||
@@ -17,12 +17,6 @@ import forge.properties.ForgeProps;
|
||||
import forge.properties.NewConstants.LANG.GameAction.GAMEACTION_TEXT;
|
||||
|
||||
public class GameAction {
|
||||
// private StaticEffects staticEffects = new StaticEffects();
|
||||
|
||||
//private CardList humanList;
|
||||
//private CardList computerList;
|
||||
|
||||
//private boolean fantasyQuest = false;
|
||||
|
||||
public void resetActivationsPerTurn(){
|
||||
CardList all = AllZoneUtil.getCardsInGame();
|
||||
@@ -42,6 +36,8 @@ public class GameAction {
|
||||
String prevZone = "";
|
||||
PlayerZone p = AllZone.getZone(c);
|
||||
|
||||
Card lastKnownInfo = c;
|
||||
|
||||
if(p != null){
|
||||
if (p.is(Constant.Zone.Battlefield) && c.isCreature())
|
||||
AllZone.Combat.removeFromCombat(c);
|
||||
@@ -52,19 +48,19 @@ public class GameAction {
|
||||
// things that were just created will not have zones!
|
||||
//System.out.println(c.getName() + " " + zone.getZoneName());
|
||||
}
|
||||
Card moving = c;
|
||||
|
||||
// Don't add the Token, unless it's moving to the battlefield
|
||||
if (!c.isToken() || zone.is(Constant.Zone.Battlefield)){
|
||||
// If a nontoken card is moving from the Battlefield, to non-Battlefield zone copy it
|
||||
if (p != null && p.is(Constant.Zone.Battlefield) && !zone.is(Constant.Zone.Battlefield))
|
||||
moving = AllZone.CardFactory.copyCard(c);
|
||||
c = AllZone.CardFactory.copyCard(c);
|
||||
|
||||
moving.setUnearthed(c.isUnearthed()); // this might be unnecessary
|
||||
if (c.wasSuspendCast()) // these probably can be moved back to SubtractCounters
|
||||
moving = addSuspendTriggers(moving);
|
||||
c.setUnearthed(lastKnownInfo.isUnearthed()); // this might be unnecessary
|
||||
if (lastKnownInfo.wasSuspendCast()) // these probably can be moved back to SubtractCounters
|
||||
c = addSuspendTriggers(c);
|
||||
|
||||
// todo: if zone is battlefied and prevZone is battlefield, temporarily disable enters battlefield triggers
|
||||
zone.add(moving);
|
||||
zone.add(c);
|
||||
}
|
||||
|
||||
if (zone.is(Constant.Zone.Battlefield) && c.isAura()){
|
||||
@@ -73,14 +69,24 @@ public class GameAction {
|
||||
|
||||
//Run triggers
|
||||
HashMap<String,Object> runParams = new HashMap<String,Object>();
|
||||
// Should the MovedCard be the LKI, aka the original card that came in, not the card that's leaving?
|
||||
runParams.put("Card", c);
|
||||
//runParams.put("MovedCard",moving);
|
||||
|
||||
runParams.put("Card", lastKnownInfo);
|
||||
runParams.put("Origin", prevZone);
|
||||
runParams.put("Destination", zone.getZoneName());
|
||||
AllZone.TriggerHandler.runTrigger("ChangesZone", runParams);
|
||||
|
||||
return moving;
|
||||
return c;
|
||||
}
|
||||
|
||||
public Card getLastKnownInformation(Card card){
|
||||
// record last known information before moving zones
|
||||
Card lastKnown = AllZone.CardFactory.copyCard(card);
|
||||
|
||||
for(Card attach : card.getAttachedCards())
|
||||
lastKnown.attachCard(attach);
|
||||
lastKnown.setExtrinsicKeyword(card.getExtrinsicKeyword());
|
||||
|
||||
return lastKnown;
|
||||
}
|
||||
|
||||
public void changeController(CardList list, Player oldController, Player newController){
|
||||
@@ -112,7 +118,6 @@ public class GameAction {
|
||||
return moveTo(stack, c);
|
||||
}
|
||||
|
||||
//card can be anywhere like in Hand or in Play
|
||||
public Card moveToGraveyard(Card c) {
|
||||
|
||||
final PlayerZone grave = AllZone.getZone(Constant.Zone.Graveyard, c.getOwner());
|
||||
@@ -229,6 +234,7 @@ public class GameAction {
|
||||
}
|
||||
|
||||
public Card moveToPlay(Card c, Player p) {
|
||||
// move to a specific player's Battlefield
|
||||
PlayerZone play = AllZone.getZone(Constant.Zone.Battlefield, p);
|
||||
return moveTo(play, c);
|
||||
}
|
||||
@@ -261,6 +267,30 @@ public class GameAction {
|
||||
return c;
|
||||
}
|
||||
|
||||
public Card exile(Card c) {
|
||||
if(AllZone.GameAction.isCardExiled(c)) return c;
|
||||
|
||||
PlayerZone removed = AllZone.getZone(Constant.Zone.Exile, c.getOwner());
|
||||
|
||||
return AllZone.GameAction.moveTo(removed, c);
|
||||
}
|
||||
|
||||
public Card moveTo(String name, Card c, int libPosition){
|
||||
// Call specific functions to set PlayerZone, then move onto moveTo
|
||||
if (name.equals(Constant.Zone.Hand))
|
||||
return moveToHand(c);
|
||||
else if (name.equals(Constant.Zone.Library))
|
||||
return moveToLibrary(c, libPosition);
|
||||
else if (name.equals(Constant.Zone.Battlefield))
|
||||
return moveToPlay(c);
|
||||
else if (name.equals(Constant.Zone.Graveyard))
|
||||
return moveToGraveyard(c);
|
||||
else if (name.equals(Constant.Zone.Exile))
|
||||
return exile(c);
|
||||
else //if (name.equals(Constant.Zone.Stack))
|
||||
return moveToStack(c);
|
||||
}
|
||||
|
||||
public boolean AI_discardNumType(int numDiscard, String[] uTypes, SpellAbility sa) {
|
||||
CardList hand = new CardList();
|
||||
hand.addAll(AllZone.getZone(Constant.Zone.Hand, AllZone.ComputerPlayer).getCards());
|
||||
@@ -2024,8 +2054,11 @@ public class GameAction {
|
||||
}
|
||||
//tokens don't go into the graveyard
|
||||
//TODO: must change this if any cards have effects that trigger "when creatures go to the graveyard"
|
||||
|
||||
//resets the card, untaps the card, removes anything "extra", resets attack and defense
|
||||
|
||||
|
||||
|
||||
|
||||
Card newCard = moveToGraveyard(c);
|
||||
|
||||
// Destroy needs to be called with Last Known Information
|
||||
@@ -2065,18 +2098,18 @@ public class GameAction {
|
||||
AllZone.Stack.add(persistAb);
|
||||
}
|
||||
|
||||
if(newCard.getKeyword().contains(
|
||||
if(c.getKeyword().contains(
|
||||
"When CARDNAME is put into a graveyard from the battlefield, return CARDNAME to its owner's hand.")) {
|
||||
PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, newCard.getOwner());
|
||||
moveTo(hand, newCard);
|
||||
}
|
||||
|
||||
else if(newCard.getName().equals("Nissa's Chosen")) {
|
||||
else if(c.getName().equals("Nissa's Chosen")) {
|
||||
PlayerZone library = AllZone.getZone(Constant.Zone.Library, newCard.getOwner());
|
||||
moveTo(library, newCard);
|
||||
}
|
||||
|
||||
else if(newCard.getName().equals("Guan Yu, Sainted Warrior")) {
|
||||
else if(c.getName().equals("Guan Yu, Sainted Warrior")) {
|
||||
PlayerZone library = AllZone.getZone(Constant.Zone.Library, newCard.getOwner());
|
||||
newCard = moveTo(library, newCard);
|
||||
owner.shuffle();
|
||||
@@ -2155,18 +2188,6 @@ public class GameAction {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* exile a card
|
||||
* @param c the card to be exiled
|
||||
*/
|
||||
public void exile(Card c) {
|
||||
if(AllZone.GameAction.isCardExiled(c)) return;
|
||||
|
||||
PlayerZone removed = AllZone.getZone(Constant.Zone.Exile, c.getOwner());
|
||||
|
||||
AllZone.GameAction.moveTo(removed, c);
|
||||
}
|
||||
|
||||
//is this card a permanent that is in play?
|
||||
public boolean isCardInPlay(Card c) {
|
||||
return PlayerZoneUtil.isCardInZone(AllZone.Computer_Battlefield, c)
|
||||
|
||||
Reference in New Issue
Block a user