It is currently 13 Aug 2025, 21:05
   
Text Size

AbilityFactory TODO list

Post MTG Forge Related Programming Questions Here

Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins

AbilityFactory TODO list

Postby slapshot5 » 07 Mar 2011, 17:44

We have discussed in several different threads the remaining AbilityFactory classes that we would like to create.

Here is a central place we can track those ideas and related discussions.

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: AbilityFactory TODO list

Postby slapshot5 » 07 Mar 2011, 17:46

First up, Sol, I think you mentioned you were looking into an AF for Animate things like manlands, Mimics, etc. If you haven't started it yet, (or haven't gotten too far along), I'll tackle that one.

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: AbilityFactory TODO list

Postby Hellfish » 07 Mar 2011, 17:56

We talked briefly about AF's for spell copying, clashing and attaching auras/equipment. I have no plans for either of those as of right now,though that may change.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: AbilityFactory TODO list

Postby Chris H. » 07 Mar 2011, 18:15

AF_Animate will be tricky. Someone added some code to some of the manlands which would allow the AI to get some use out of these types of spells/abilities.

I remember some comments from the user base. The AI would animate a land and then tap the same land to pay for it's animation cost or to pay for some other cost for another spell or ability.

I spent a little time tracking down the AI mana code and found where it creates a list of untapped mana for the AI to use. I planned to sort his list so that the non-manland mana would appear first and the manlands would appear last in the list.

This would help to reduce the chance that the AI would waste it's mana to no effect. It might be worth it to create a flag that prevent an animate till EOT animation from tapping for mana.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: AbilityFactory TODO list

Postby Zirbert » 07 Mar 2011, 19:22

Some damage prevention would be good - "Prevent the next (x) damage that would be dealt to", "Prevent all damage that would be dealt by (x)", etc.
Zirbert
 
Posts: 512
Joined: 13 Oct 2010, 16:04
Has thanked: 104 times
Been thanked: 19 times

Re: AbilityFactory TODO list

Postby Sloth » 07 Mar 2011, 19:53

We need an AF to handle abilities like those of Serendib Sorcerer and Liquimetal Coating. Something like "DefineCharacteristic".
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: AbilityFactory TODO list

Postby friarsol » 07 Mar 2011, 20:56

I've been way too busy to work on anything lately. I have a list at home that I'll post tonight of what I think would be good things to do for AFs next.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: AbilityFactory TODO list

Postby friarsol » 08 Mar 2011, 02:28

Alright here's my list. This may be repeats of others already mentioned.

SetLife (mostly written below, someone else can feel free to finish it up)
AssignType (granting additional or overriding types Liquidmetal Coating)
AssignColor (granting additional or overriding colors)
SetPowerToughness (setting Power/Toughness Serendib Sorcerer)
Conditional AF (for Flip a Coin, or other things that need to check that state of something, basically just calls subAbilities if true/false)
Peek/Reveal (Impulse is what I would consider the standard Peek/Reveal card. You look at the X cards of your Library, put Y of them somewhere, then put the rest somewhere.)

I thought I had some more but I can't find them. Might have deleted it by accident.

Code: Select all
   // *************************************************************************
   // ************************** SET LIFE *************************************
   // *************************************************************************

   public static SpellAbility createAbilitySetLife(final AbilityFactory AF){

      final SpellAbility abSetLife = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){
         private static final long serialVersionUID = 8869422603616247307L;
         
         final AbilityFactory af = AF;
      
         @Override
         public String getStackDescription(){
            // when getStackDesc is called, just build exactly what is happening
            return setLifeStackDescription(af, this);
         }

         public boolean canPlayAI()
         {
            return setLifeCanPlayAI(af, this);
         }
         
         @Override
         public void resolve() {
            setLifeResolve(af, this);
         }

         @Override
         public boolean doTrigger(boolean mandatory) {
            return setLifeDoTriggerAI(af, this, mandatory);
         }
         
      };
      return abSetLife;
   }
   
   public static SpellAbility createSpellSetLife(final AbilityFactory AF){
      final SpellAbility spSetLife = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){
         private static final long serialVersionUID = 6631124959690157874L;
         
         final AbilityFactory af = AF;
      
         @Override
         public String getStackDescription(){
         // when getStackDesc is called, just build exactly what is happening
            return setLifeStackDescription(af, this);
         }
         
         public boolean canPlayAI()
         {
            // if X depends on abCost, the AI needs to choose which card he would sacrifice first
            // then call xCount with that card to properly calculate the amount
            // Or choosing how many to sacrifice
            return setLifeCanPlayAI(af, this);
         }
         
         @Override
         public void resolve() {
            setLifeResolve(af, this);
         }
         
      };
      return spSetLife;
   }
   
   public static SpellAbility createDrawbackSetLife(final AbilityFactory AF){
      final SpellAbility dbSetLife = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()){
         private static final long serialVersionUID = 6631124959690157874L;
         
         final AbilityFactory af = AF;
      
         @Override
         public String getStackDescription(){
         // when getStackDesc is called, just build exactly what is happening
            return setLifeStackDescription(af, this);
         }
         
         public boolean canPlayAI()
         {
            // if X depends on abCost, the AI needs to choose which card he would sacrifice first
            // then call xCount with that card to properly calculate the amount
            // Or choosing how many to sacrifice
            return setLifeCanPlayAI(af, this);
         }
         
         @Override
         public void resolve() {
            setLifeResolve(af, this);
         }

         @Override
         public boolean chkAI_Drawback() {
            return true;
         }

         @Override
         public boolean doTrigger(boolean mandatory) {
            return setLifeDoTriggerAI(af, this, mandatory);
         }
         
      };
      return dbSetLife;
   }

   public static String setLifeStackDescription(AbilityFactory af, SpellAbility sa){
      StringBuilder sb = new StringBuilder();
      int amount = AbilityFactory.calculateAmount(af.getHostCard(), af.getMapParams().get("LifeAmount"), sa);

      if (!(sa instanceof Ability_Sub))
         sb.append(sa.getSourceCard().getName()).append(" -");

      sb.append(" ");

      ArrayList<Player> tgtPlayers;

      Target tgt = af.getAbTgt();
      if (tgt != null)
         tgtPlayers = tgt.getTargetPlayers();
      else
         tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), af.getMapParams().get("Defined"), sa);
      
      for(Player player : tgtPlayers)
         sb.append(player).append(" ");
      
      sb.append("life total becomes ").append(amount).append(".");

      Ability_Sub abSub = sa.getSubAbility();
      if (abSub != null) {
         sb.append(abSub.getStackDescription());
      }

      return sb.toString();
   }
   
   public static boolean setLifeCanPlayAI(final AbilityFactory af, final SpellAbility sa){
      Random r = new Random();
      Ability_Cost abCost = sa.getPayCosts();
      final Card source = sa.getSourceCard();
      int life = AllZone.ComputerPlayer.getLife();
      String amountStr = af.getMapParams().get("LifeAmount");

      if (!ComputerUtil.canPayCost(sa))
         return false;

      if (!AllZone.ComputerPlayer.canGainLife())
         return false;

      // TODO handle proper calculation of X values based on Cost and what would be paid
      int amount;
      if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")){
         // Set PayX here to maximum value.
         int xPay = ComputerUtil.determineLeftoverMana(sa);
         source.setSVar("PayX", Integer.toString(xPay));
         amount = xPay;
      }
      else
         amount = AbilityFactory.calculateAmount(af.getHostCard(), amountStr, sa);

      // prevent run-away activations - first time will always return true
      boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());

      Target tgt = sa.getTarget();
      if (tgt != null){
         tgt.resetTargets();
         if (tgt.canOnlyTgtOpponent())
            tgt.addTarget(AllZone.HumanPlayer);
         else
            tgt.addTarget(AllZone.ComputerPlayer);      
      }
      else{
         
      }

      return ((r.nextFloat() < .6667) && chance);
   }
   
   public static boolean setLifeDoTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory){
      if (!ComputerUtil.canPayCost(sa) && !mandatory)   // If there is a cost payment it's usually not mandatory
         return false;

      // If the Target is gaining life, target self.
      // if the Target is modifying how much life is gained, this needs to be handled better
      Target tgt = sa.getTarget();
      if (tgt != null){
         tgt.resetTargets();
         if (tgt.canOnlyTgtOpponent())
            tgt.addTarget(AllZone.HumanPlayer);
         else
            tgt.addTarget(AllZone.ComputerPlayer);      
      }

      Card source = sa.getSourceCard();
      String amountStr = af.getMapParams().get("LifeAmount");
      if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")){
         // Set PayX here to maximum value.
         int xPay = ComputerUtil.determineLeftoverMana(sa);
         source.setSVar("PayX", Integer.toString(xPay));
      }

      // check SubAbilities DoTrigger?
      Ability_Sub abSub = sa.getSubAbility();
      if (abSub != null) {
         return abSub.doTrigger(mandatory);
      }

      return true;
   }
   
   public static void setLifeResolve(final AbilityFactory af, final SpellAbility sa){
      HashMap<String,String> params = af.getMapParams();
      Card card = af.getHostCard();
      int lifeAmount = AbilityFactory.calculateAmount(af.getHostCard(), params.get("LifeAmount"), sa);
      ArrayList<Player> tgtPlayers;

      Target tgt = af.getAbTgt();
      if (tgt != null && !params.containsKey("Defined"))
         tgtPlayers = tgt.getTargetPlayers();
      else
         tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);
      
      for(Player p : tgtPlayers)
         if (tgt == null || p.canTarget(af.getHostCard()))
            p.gainLife(lifeAmount, sa.getSourceCard());
      
      if (af.hasSubAbility()){
         Ability_Sub abSub = sa.getSubAbility();
         if (abSub != null){
              abSub.resolve();
           }
         else{
            String DrawBack = params.get("SubAbility");
            if (af.hasSubAbility())
                CardFactoryUtil.doDrawBack(DrawBack, lifeAmount, card.getController(), card.getController().getOpponent(), tgtPlayers.get(0), card, null, sa);
         }
      }
   }
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: AbilityFactory TODO list

Postby slapshot5 » 08 Mar 2011, 04:21

SetLife was the other one I keep putting off. I'll take your code and finish that one up.

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: AbilityFactory TODO list

Postby slapshot5 » 08 Mar 2011, 13:35

Chris H. wrote:I remember some comments from the user base. The AI would animate a land and then tap the same land to pay for it's animation cost or to pay for some other cost for another spell or ability.

I spent a little time tracking down the AI mana code and found where it creates a list of untapped mana for the AI to use. I planned to sort his list so that the non-manland mana would appear first and the manlands would appear last in the list.
Seems like in the Pay Mana code, we could add the following checks:
1. if this land is also a creature, don't tap it for mana
2. if a spell (or maybe specifically an animate spell even) is targeting a land, or has Defined$ Self, don't use that land to lay the cost.

That should help. Fortunately, most everything for Animate is Defined$ Self. There are very few things I have found like Kamahl, Fist of Krosa.

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: AbilityFactory TODO list

Postby friarsol » 08 Mar 2011, 13:49

slapshot5 wrote:That should help. Fortunately, most everything for Animate is Defined$ Self. There are very few things I have found like Kamahl, Fist of Krosa.
There are a few AnimateAlls but we can handle those afterwards.

Oh I also wanted an Effect AF. I'm not sure how the AI would work for it yet, but basically it would create an Effect "permanent", which is sorta like a token Card, but generally without any types. It would be similar to Token since you could give it abilities and whatnot. We could have Emblems use this AF, as well as Effects that last a certain amount of time that we have no way to keep track of right now. Like Dauntless Escort's ability.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: AbilityFactory TODO list

Postby Replika » 08 Mar 2011, 15:32

An Effect AF would be very nice to handle abilities that have to stay even if its source dies, like Ethersworn Shieldmage.

I would also like a "Repeat this until" to do things like Treasure Hunt or the often requested Ad Nauseam.
Replika
 
Posts: 115
Joined: 30 Jan 2011, 21:15
Has thanked: 2 times
Been thanked: 3 times

Re: AbilityFactory TODO list

Postby slapshot5 » 08 Mar 2011, 17:46

I was going to enchance the generic aura code to Enchant <valid>, but since it should be an AF eventually, I'll just go to that instead.

Has anyone thought about what the Enchant/Equip AF would look like? I see them both going into an AbilityFactory_Attach.java file.

Then, do we add a fourth category when getting abilties in AbilityFactory?
Code: Select all
if(API.equals("Enchant")) {
         if(isAb)
            SA = AbilityFactory_Attach.createAbilityEnchant(this);
         else if(isSp)
            SA = AbilityFactory_Attach.createSpellEnchant(this);
         else if(isDb)
            SA = AbilityFactory_Attach.createDrawbackEnchant(this);
         else if(isAttachment)
            SA = AbilityFactory_Attach.createAttachmentEnchant(this);
      }
That way, we can handle the Aura itself, plus permanent abilities that say attach target aura to target creature (for example) and spell that say "move target enchantment from one creature to another).

Is there a better/different way anyone had thought of?

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: AbilityFactory TODO list

Postby Hellfish » 08 Mar 2011, 18:03

I found some old notes I'd made about a generic attach AF a while ago.Don't remember why I didn't implement it fully, probably AI problems.

Equip:
Code: Select all
AB$Attach | Cost$ 2 | DefinedAttachee$ Self | ValidAttachTo$ Creature.YouCtrl | PreCostDesc$ Equip -
Aura Finesse:
Code: Select all
SP$Attach | Cost$ U | ValidAttachee$ Aura.YouCtrl | ValidAttachTo$ Creature | SubAbility$SVar=DBDraw | SpellDescription$ Attach target Aura you control to target creature.
Handling all these cases in a single AF. ValidAttachee and ValidAttachTo are interchangeable with DefinedAttachee(as seen in the Equip example) and DefinedAttachTo(For use with Triggered-variables). I made a note to scavenge useful code from the eqPump code in CardFactoryUtil.java, row 1525 and onwards.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: AbilityFactory TODO list

Postby Rob Cashwalker » 09 Mar 2011, 14:36

Something else to consider before going crazy adding new mechanics:

Charms and Commands

A Charm AF would wrap around three sub abilities, checking each AI, then chooses one from the positive returns, albeit randomly....
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Next

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 30 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 30 users online :: 0 registered, 0 hidden and 30 guests (based on users active over the past 10 minutes)
Most users ever online was 7303 on 15 Jul 2025, 20:46

Users browsing this forum: No registered users and 30 guests

Login Form