mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
- Removing Static from Member Variables of AbilityFactory so they are not shared between cards.
- Create a new AbilityFactory for each time AF is used to prevent issues with Factory not being fresh. - Added AbilityFactory_Counters.java for AFs that deal with putting or removing counters from cards - Added createAbilityPutCounters for Abilities that put Counters on cards. - Added Amok, Fume Spitter, and Trigon of Corruption as Samples of using PutCounter
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -122,6 +122,7 @@ res/cardsfolder/ambassador_oak.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/ambitions_cost.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/ambush_party.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/amnesia.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/amok.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/amrou_kithkin.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/amrou_scout.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/amrou_seekers.txt -text svneol=native#text/plain
|
||||
@@ -1496,6 +1497,7 @@ res/cardsfolder/fruition.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/fugitive_wizard.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/fugue.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/fulminator_mage.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/fume_spitter.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/funeral_charm.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/fungal_shambler.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/furious_assault.txt -text svneol=native#text/plain
|
||||
@@ -4191,6 +4193,7 @@ res/cardsfolder/trevas_attendant.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/tribal_flames.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/tribal_forcemage.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/trickster_mage.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/trigon_of_corruption.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/trinket_mage.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/trip_noose.txt -text svneol=native#text/plain
|
||||
res/cardsfolder/trip_wire.txt -text svneol=native#text/plain
|
||||
@@ -5049,6 +5052,7 @@ src/com/cloudgarden/layout/AnchorLayout.java -text svneol=native#text/plain
|
||||
src/com/esotericsoftware/minlog/Log.java svneol=native#text/plain
|
||||
src/forge/Ability.java svneol=native#text/plain
|
||||
src/forge/AbilityFactory.java -text svneol=native#text/plain
|
||||
src/forge/AbilityFactory_Counters.java -text svneol=native#text/plain
|
||||
src/forge/Ability_Activated.java svneol=native#text/plain
|
||||
src/forge/Ability_Cost.java -text svneol=native#text/plain
|
||||
src/forge/Ability_Hand.java svneol=native#text/plain
|
||||
|
||||
9
res/cardsfolder/amok.txt
Normal file
9
res/cardsfolder/amok.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Amok
|
||||
ManaCost:1 R
|
||||
Types:Enchantment
|
||||
Text:no text
|
||||
A:AB$PutCounter|Cost$1 Discard<1/Random>|Tgt$TgtC|CounterType$P1P1|CounterNum$1|SpellDescription$Put a +1/+1 counter on target creature.
|
||||
SVar:Rarity:Rare
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/amok.jpg
|
||||
End
|
||||
10
res/cardsfolder/fume_spitter.txt
Normal file
10
res/cardsfolder/fume_spitter.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Fume Spitter
|
||||
ManaCost:B
|
||||
Types:Creature Horror
|
||||
PT:1/1
|
||||
Text:no text
|
||||
A:AB$PutCounter|Cost$Sac<1/CARDNAME>|Tgt$TgtC|IsCurse$True|CounterType$M1M1|CounterNum$1|SpellDescription$Put a -1/-1 counter on target creature.
|
||||
SVar:Rarity:Common
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/fume_spitter.jpg
|
||||
End
|
||||
12
res/cardsfolder/trigon_of_corruption.txt
Normal file
12
res/cardsfolder/trigon_of_corruption.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Trigon of Corruption
|
||||
ManaCost:4
|
||||
Types:Artifact
|
||||
Text:no text
|
||||
K:etbCounter:CHARGE:3
|
||||
A:AB$PutCounter|Cost$B B T|CounterType$CHARGE|CounterNum$1|SpellDescription$Put a charge counter on CARDNAME.
|
||||
A:AB$PutCounter|Cost$2 T SubCounter<1/CHARGE>|Tgt$TgtC|IsCurse$True|CounterType$M1M1|CounterNum$1|SpellDescription$Put a -1/-1 counter on target creature.
|
||||
SVar:Rarity:Common
|
||||
# SVar:RemAIDeck:True
|
||||
# I've run some tests where the AI does use both abilities of this card, but it can be flakey.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/trigon_of_corruption.jpg
|
||||
End
|
||||
@@ -1,26 +1,25 @@
|
||||
package forge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
public class AbilityFactory {
|
||||
|
||||
private static Card hostC = null;
|
||||
private Card hostC = null;
|
||||
|
||||
public Card getHostCard()
|
||||
{
|
||||
return hostC;
|
||||
}
|
||||
|
||||
private static HashMap<String,String> mapParams = new HashMap<String,String>();
|
||||
private HashMap<String,String> mapParams = new HashMap<String,String>();
|
||||
|
||||
public HashMap<String,String> getMapParams()
|
||||
{
|
||||
return mapParams;
|
||||
}
|
||||
|
||||
private static boolean isAb = false;
|
||||
private static boolean isSp = false;
|
||||
private boolean isAb = false;
|
||||
private boolean isSp = false;
|
||||
|
||||
public boolean isAbility()
|
||||
{
|
||||
@@ -32,16 +31,16 @@ public class AbilityFactory {
|
||||
return isSp;
|
||||
}
|
||||
|
||||
private static Ability_Cost abCost = null;
|
||||
private Ability_Cost abCost = null;
|
||||
|
||||
public Ability_Cost getAbCost()
|
||||
{
|
||||
return abCost;
|
||||
}
|
||||
|
||||
private static boolean isTargeted = false;
|
||||
private static boolean hasValid = false;
|
||||
private static Target abTgt = null;
|
||||
private boolean isTargeted = false;
|
||||
private boolean hasValid = false;
|
||||
private Target abTgt = null;
|
||||
|
||||
public boolean isTargeted()
|
||||
{
|
||||
@@ -58,7 +57,12 @@ public class AbilityFactory {
|
||||
return abTgt;
|
||||
}
|
||||
|
||||
private static boolean hasSubAb = false;
|
||||
private boolean isCurse = false;
|
||||
public boolean isCurse(){
|
||||
return isCurse;
|
||||
}
|
||||
|
||||
private boolean hasSubAb = false;
|
||||
|
||||
public boolean hasSubAbility()
|
||||
{
|
||||
@@ -75,8 +79,6 @@ public class AbilityFactory {
|
||||
public SpellAbility getAbility(String abString, final Card hostCard){
|
||||
SpellAbility SA = null;
|
||||
|
||||
//final HashMap<String,String> mapParams = new HashMap<String,String>();
|
||||
|
||||
hostC = hostCard;
|
||||
|
||||
if (!(abString.length() > 0))
|
||||
@@ -97,11 +99,9 @@ public class AbilityFactory {
|
||||
mapParams.put(aa[0], aa[1]);
|
||||
}
|
||||
|
||||
|
||||
//final boolean isAb[] = {false};
|
||||
//final boolean isSp[] = {false};
|
||||
String abAPI = "";
|
||||
String spAPI = "";
|
||||
|
||||
// additional ability types here
|
||||
if (mapParams.containsKey("AB"))
|
||||
{
|
||||
@@ -121,17 +121,10 @@ public class AbilityFactory {
|
||||
throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName());
|
||||
abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb);
|
||||
|
||||
|
||||
//final boolean isTargeted[] = {false};
|
||||
//final boolean hasValid[] = {false};
|
||||
//final Target abTgt[] = {null};
|
||||
if (mapParams.containsKey("ValidTgts"))
|
||||
{
|
||||
hasValid = true;
|
||||
isTargeted = true;
|
||||
abTgt = new Target("TgtV");
|
||||
abTgt.setValidTgts(mapParams.get("ValidTgts").split(","));
|
||||
abTgt.setVTSelection(mapParams.get("TgtPrompt"));
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("ValidCards"))
|
||||
@@ -140,26 +133,30 @@ public class AbilityFactory {
|
||||
if (mapParams.containsKey("Tgt"))
|
||||
{
|
||||
isTargeted = true;
|
||||
abTgt = new Target(mapParams.get("Tgt"));
|
||||
}
|
||||
|
||||
if (isTargeted)
|
||||
{
|
||||
if (hasValid)
|
||||
abTgt = new Target("TgtV", mapParams.get("TgtPrompt"), mapParams.get("ValidTgts").split(","));
|
||||
else
|
||||
abTgt = new Target(mapParams.get("Tgt"));
|
||||
}
|
||||
else{
|
||||
abTgt = null;
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("IsCurse")){
|
||||
isCurse = true;
|
||||
}
|
||||
|
||||
//final String SubAbility[] = {"none"};
|
||||
//final boolean hasSubAb[] = {false};
|
||||
if (mapParams.containsKey("SubAbility"))
|
||||
hasSubAb = true;
|
||||
//SubAbility[0] = mapParams;
|
||||
|
||||
//final String spDescription[] = {"none"};
|
||||
//final boolean hasSpDesc[] = {false};
|
||||
//String tmpSpDesc = mapParams.get("SpellDescription");
|
||||
|
||||
if (mapParams.containsKey("SpellDescription"))
|
||||
{
|
||||
hasSpDesc = true;
|
||||
//spDescription[0] = abCost.toString() + mapParams.get("SpellDescription");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (abAPI.equals("DealDamage"))
|
||||
{
|
||||
final int NumDmg[] = {-1};
|
||||
@@ -183,13 +180,23 @@ public class AbilityFactory {
|
||||
}
|
||||
|
||||
// additional keywords here
|
||||
|
||||
if (abAPI.equals("PutCounter")){
|
||||
if (isAb)
|
||||
SA = AbilityFactory_Counters.createAbilityPutCounters(this);
|
||||
if (isSp){
|
||||
// todo: createSpellPutCounters
|
||||
}
|
||||
}
|
||||
|
||||
// set universal properties of the SpellAbility
|
||||
if (isTargeted)
|
||||
SA.setTarget(abTgt);
|
||||
|
||||
SA.setPayCosts(abCost);
|
||||
if (isSp){
|
||||
// Ability_Activated sets abTgt and abCost in the constructor so this only needs to be set for Spells
|
||||
// Once Spells are more compatible with Tgt and abCost this block should be removed
|
||||
if (isTargeted)
|
||||
SA.setTarget(abTgt);
|
||||
|
||||
SA.setPayCosts(abCost);
|
||||
}
|
||||
|
||||
if (hasSpDesc)
|
||||
SA.setDescription(abCost.toString() + mapParams.get("SpellDescription"));
|
||||
|
||||
181
src/forge/AbilityFactory_Counters.java
Normal file
181
src/forge/AbilityFactory_Counters.java
Normal file
@@ -0,0 +1,181 @@
|
||||
package forge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
public class AbilityFactory_Counters {
|
||||
// An AbilityFactory subclass for Putting or Removing Counters on Cards.
|
||||
|
||||
public static SpellAbility createAbilityPutCounters(final AbilityFactory AF){
|
||||
|
||||
final SpellAbility abPutCounter = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){
|
||||
private static final long serialVersionUID = -1259638699008542484L;
|
||||
|
||||
final AbilityFactory af = AF;
|
||||
final HashMap<String,String> params = af.getMapParams();
|
||||
final int amount = calculateAmount(af.getHostCard(), params.get("CounterNum"));
|
||||
final String type = params.get("CounterType");
|
||||
|
||||
@Override
|
||||
public String getStackDescription(){
|
||||
// when getStackDesc is called, just build exactly what is happening
|
||||
Counters cType = Counters.valueOf(type);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String name = af.getHostCard().getName();
|
||||
sb.append(name).append(" - Put ").append(amount).append(" ").append(cType.getName()).append(" counter on ");
|
||||
Card tgt = getTargetCard();
|
||||
if (tgt != null)
|
||||
sb.append(tgt.getName());
|
||||
else
|
||||
sb.append(name);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean canPlay(){
|
||||
// super takes care of AdditionalCosts
|
||||
return (CardFactoryUtil.canUseAbility(af.getHostCard()) && super.canPlay());
|
||||
}
|
||||
|
||||
public boolean canPlayAI()
|
||||
{
|
||||
return putCanPlayAI(af, this, amount, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
putResolve(af, this, amount, type);
|
||||
}
|
||||
|
||||
};
|
||||
return abPutCounter;
|
||||
}
|
||||
|
||||
public static int calculateAmount(Card card, String counterNum){
|
||||
int amount;
|
||||
if (counterNum.matches("X"))
|
||||
{
|
||||
String calcX = card.getSVar(counterNum);
|
||||
if (calcX.startsWith("Count$"))
|
||||
{
|
||||
String kk[] = calcX.split("\\$");
|
||||
amount = kk[1].equals("none") ? 0 : CardFactoryUtil.xCount(card, kk[1]);
|
||||
}
|
||||
else
|
||||
amount = 0;
|
||||
}
|
||||
else
|
||||
amount = Integer.parseInt(counterNum);
|
||||
return amount;
|
||||
}
|
||||
|
||||
public static boolean putCanPlayAI(final AbilityFactory af, final SpellAbility sa, final int amount, final String type){
|
||||
// AI needs to be expanded, since this function can be pretty complex based on what the expected targets could be
|
||||
Random r = new Random();
|
||||
Ability_Cost abCost = sa.getPayCosts();
|
||||
Target abTgt = sa.getTarget();
|
||||
final Card source = sa.getSourceCard();
|
||||
CardList list;
|
||||
Card choice = null;
|
||||
|
||||
String player = af.isCurse() ? Constant.Player.Human : Constant.Player.Computer;
|
||||
|
||||
list = new CardList(AllZone.getZone(Constant.Zone.Play, player).getCards());
|
||||
list = list.filter(new CardListFilter() {
|
||||
public boolean addCard(Card c) {
|
||||
return AllZone.GameAction.canTarget(c, source);
|
||||
}
|
||||
});
|
||||
|
||||
if (abTgt != null){
|
||||
if (abTgt.canTgtCreature()){
|
||||
list = list.getType("creature");
|
||||
}
|
||||
else{
|
||||
list = list.getValidCards(abTgt.getValidTgts());
|
||||
}
|
||||
if (list.size() == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (abCost != null){
|
||||
// AI currently disabled for these costs
|
||||
if (abCost.getSacCost()) return false;
|
||||
if (abCost.getLifeCost()) return false;
|
||||
if (abCost.getDiscardCost()) return false;
|
||||
|
||||
if (abCost.getSubCounter()){
|
||||
// A card has a 25% chance per counter to be able to pass through here
|
||||
// 8+ counters will always pass. 0 counters will never
|
||||
int currentNum = source.getCounters(abCost.getCounterType());
|
||||
double percent = .25 * (currentNum / abCost.getCounterNum());
|
||||
if (percent <= r.nextFloat())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ComputerUtil.canPayCost(sa))
|
||||
return false;
|
||||
|
||||
// prevent run-away activations - first time will always return true
|
||||
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
|
||||
|
||||
// Targeting
|
||||
if (abTgt != null){
|
||||
if (af.isCurse()){
|
||||
if (type.equals("M1M1")){
|
||||
// try to kill the best killable creature, or reduce the best one
|
||||
CardList killable = list.filter(new CardListFilter() {
|
||||
public boolean addCard(Card c) {
|
||||
return c.getNetDefense() <= amount;
|
||||
}
|
||||
});
|
||||
if (killable.size() > 0)
|
||||
choice = CardFactoryUtil.AI_getBestCreature(killable);
|
||||
else
|
||||
choice = CardFactoryUtil.AI_getBestCreature(list);
|
||||
}
|
||||
else{
|
||||
// improve random choice here
|
||||
list.shuffle();
|
||||
choice = list.get(0);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (type.equals("P1P1")){
|
||||
choice = CardFactoryUtil.AI_getBestCreature(list);
|
||||
}
|
||||
else{
|
||||
// The AI really should put counters on cards that can use it.
|
||||
// Charge counters on things with Charge abilities, etc. Expand these above
|
||||
list.shuffle();
|
||||
choice = list.get(0);
|
||||
}
|
||||
}
|
||||
if (choice == null)
|
||||
return false;
|
||||
sa.setTargetCard(choice);
|
||||
}
|
||||
else{
|
||||
// Placeholder: No targeting necessary
|
||||
int currCounters = sa.getSourceCard().getCounters(Counters.valueOf(type));
|
||||
// each counter on the card is a 10% chance of not activating this ability.
|
||||
if (r.nextFloat() < .1 * currCounters)
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((r.nextFloat() < .6667) && chance);
|
||||
}
|
||||
|
||||
public static void putResolve(final AbilityFactory af, final SpellAbility sa, int counterAmount, final String type){
|
||||
HashMap<String,String> params = af.getMapParams();
|
||||
String DrawBack = params.get("SubAbility");
|
||||
Card card = af.getHostCard();
|
||||
|
||||
Card tgtCard = (sa.getTarget() == null) ? card : sa.getTargetCard();
|
||||
tgtCard.addCounter(Counters.valueOf(type), counterAmount);
|
||||
|
||||
if (af.hasSubAbility())
|
||||
CardFactoryUtil.doDrawBack(DrawBack, counterAmount, card.getController(), AllZone.GameAction.getOpponent(card.getController()), card.getController(), card, null, sa);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6093,14 +6093,14 @@ public class CardFactory implements NewConstants {
|
||||
// AbilityFactory cards
|
||||
ArrayList<String> IA = card.getIntrinsicAbilities();
|
||||
if (IA.size() > 0)
|
||||
{
|
||||
AbilityFactory AF = new AbilityFactory();
|
||||
|
||||
{
|
||||
if (card.isInstant() || card.isSorcery())
|
||||
card.clearSpellAbility();
|
||||
|
||||
for (int i=0; i<IA.size(); i++)
|
||||
for (int i=0; i<IA.size(); i++){
|
||||
AbilityFactory AF = new AbilityFactory();
|
||||
card.addSpellAbility(AF.getAbility(IA.get(i), card));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user