AbilityFactory-based SubAbilities
Post MTG Forge Related Programming Questions Here
Moderators: timmermac, Agetian, friarsol, Blacksmith, KrazyTheFox, CCGHQ Admins
15 posts
• Page 1 of 1
AbilityFactory-based SubAbilities
by Rob Cashwalker » 18 Nov 2010, 12:53
I'm not really sure how this is supposed to work anymore. I've gotten some basics started, but when it actually comes to constructing a SpellAbility object, I run into a bit of a wall. I'm going to paste up my changes so someone else, preferably friarsol, can figure out what to make of it next.
AbilityFactory_DealDamage:
SpellAbility:
My roadblock is creating the new Spell or should it be an Ability? Target? Cost? Drawbacks don't necessarily target, but their effect may need to be limited with syntax similar to Target. (need to add "You" as a "target"?) Drawbacks don't have a cost, but I don't know how a null Cost object would affect things.
AbilityFactory_DealDamage:
- Code: Select all
package forge;
import java.util.ArrayList;
import java.util.Random;
public class AbilityFactory_DealDamage {
public AbilityFactory_DealDamage(AbilityFactory newAF)
{
AF = newAF;
String tmpND = AF.getMapParams().get("NumDmg");
if (tmpND.length() > 0)
{
if (tmpND.matches("[xX]"))
XDamage = AF.getHostCard().getSVar(tmpND.substring(1));
else if (tmpND.matches("[0-9][0-9]?"))
nDamage = Integer.parseInt(tmpND);
}
if(AF.getMapParams().containsKey("Tgt"))
if (AF.getMapParams().get("Tgt").equals("TgtOpp"))
TgtOpp = true;
if(AF.hasSubAbility())
{
String sSub = AF.getMapParams().get("SubAbility");
if (sSub.matches("[xX]."))
sSub = AF.getHostCard().getSVar(sSub.substring(1));
if (sSub.startsWith("DB$"))
{
AbilityFactory afDB = new AbilityFactory();
subAbAF = afDB.getAbility(sSub, AF.getHostCard());
hasSubAbAF = true;
}
else
{
subAbStr = sSub;
hasSubAbStr = true;
}
}
}
private AbilityFactory AF = null;
private int nDamage = -1;
private String XDamage = "none";
private boolean TgtOpp = false;
private SpellAbility subAbAF = null;
private boolean hasSubAbAF = false;
private String subAbStr = "none";
private boolean hasSubAbStr = false;
//public SpellAbility getAbility(final AbilityFactory af, final int NumDmg, final String NumDmgX)
public SpellAbility getAbility()
{
//AF = af;
//nDamage = NumDmg;
//XDamage = NumDmgX;
// if(af.getMapParams().containsKey("Tgt"))
// if (AF.getMapParams().get("Tgt").equals("TgtOpp"))
// TgtOpp = true;
final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt())
{
private static final long serialVersionUID = -7560349014757367722L;
@Override
public boolean canPlay(){
return super.canPlay();
}
@Override
public boolean canPlayAI() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1);
}
};//Ability_Activated
return abDamage;
}
//public SpellAbility getSpell(final AbilityFactory af, final int NumDmg, final String NumDmgX)
public SpellAbility getSpell()
{
//AF = af;
//nDamage = NumDmg;
//XDamage = NumDmgX;
// if(AF.getMapParams().containsKey("Tgt"))
// if (AF.getMapParams().get("Tgt").equals("TgtOpp"))
// TgtOpp = true;
final SpellAbility spDealDamage = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
private static final long serialVersionUID = 7239608350643325111L;
@Override
public boolean canPlay(){
return super.canPlay();
}
@Override
public boolean canPlayAI() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
}
}; // Spell
return spDealDamage;
}
public SpellAbility getDrawback()
{
final SpellAbility dbDealDamage = new Spell(AF.getHostCard(), null, null) {
private static final long serialVersionUID = 7239608350643325111L;
@Override
public boolean canPlay(){
return super.canPlay();
}
@Override
public boolean chkAI_Drawback() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
}
}; // Spell
return dbDealDamage;
}
private int getNumDamage(SpellAbility saMe) {
if(nDamage != -1) return nDamage;
String calcX[] = XDamage.split("\\$");
if (calcX.length == 1 || calcX[1].equals("none"))
return 0;
if (calcX[0].startsWith("Count"))
{
return CardFactoryUtil.xCount(AF.getHostCard(), calcX[1]);
}
else if (calcX[0].startsWith("Sacrificed"))
{
return CardFactoryUtil.handlePaid(saMe.getSacrificedCost(), calcX[1]);
}
return 0;
}
private boolean shouldTgtP(int d) {
PlayerZone compHand = AllZone.getZone(Constant.Zone.Hand, AllZone.ComputerPlayer);
CardList hand = new CardList(compHand.getCards());
if(AF.isSpell() && hand.size() > 7) // anti-discard-at-EOT
return true;
if(AllZone.HumanPlayer.getLife() - d < 10) // if damage from this spell would drop the human to less than 10 life
return true;
return false;
}
private Card chooseTgtC(final int d) {
// Combo alert!!
PlayerZone compy = AllZone.getZone(Constant.Zone.Play, AllZone.ComputerPlayer);
CardList cPlay = new CardList(compy.getCards());
if(cPlay.size() > 0) for(int i = 0; i < cPlay.size(); i++)
if(cPlay.get(i).getName().equals("Stuffy Doll")) return cPlay.get(i);
PlayerZone human = AllZone.getZone(Constant.Zone.Play, AllZone.HumanPlayer);
CardList hPlay = new CardList(human.getCards());
hPlay = hPlay.filter(new CardListFilter() {
public boolean addCard(Card c) {
// will include creatures already dealt damage
return c.isCreature() && ((c.getNetDefense() + c.getDamage()) <= d)
&& CardFactoryUtil.canTarget(AF.getHostCard(), c);
}
});
if(hPlay.size() > 0) {
Card best = hPlay.get(0);
if(hPlay.size() > 1) {
for(int i = 1; i < hPlay.size(); i++) {
Card b = hPlay.get(i);
// choose best overall creature?
if(b.getSpellAbility().length > best.getSpellAbility().length
|| b.getKeyword().size() > best.getKeyword().size()
|| b.getNetAttack() > best.getNetAttack()) best = b;
}
}
return best;
}
return null;
}
private boolean doCanPlayAI(SpellAbility saMe)
{
// temporarily disabled until better AI
if (AF.getAbCost().getSacCost()) return false;
if (AF.getAbCost().getSubCounter()) return false;
if (AF.getAbCost().getLifeCost()) return false;
if (!ComputerUtil.canPayCost(saMe))
return false;
// TODO handle proper calculation of X values based on Cost
int damage = getNumDamage(saMe);
boolean rr = AF.isSpell();
if (AF.isAbility())
{
Random r = new Random(); // prevent run-away activations
if(r.nextFloat() <= Math.pow(.6667, AF.getHostCard().getAbilityUsed()))
rr = true;
}
Target tgt = AF.getAbTgt();
// AI handle multi-targeting?
tgt.resetTargets();
// target loop
while(tgt.getNumTargeted() < tgt.getMaxTargets()){
// TODO: Consider targeting the planeswalker
if(tgt.canTgtCreatureAndPlayer()) {
if(shouldTgtP(damage)) {
tgt.addTarget(AllZone.HumanPlayer);
continue;
}
Card c = chooseTgtC(damage);
if(c != null) {
tgt.addTarget(c);
continue;
}
}
if(tgt.canTgtPlayer() || TgtOpp) {
tgt.addTarget(AllZone.HumanPlayer);
continue;
}
if(tgt.canTgtCreature()) {
Card c = chooseTgtC(damage);
if(c != null) {
tgt.addTarget(c);
continue;
}
}
// fell through all the choices, no targets left?
if (tgt.getNumTargeted() < tgt.getMinTargets() || tgt.getNumTargeted() == 0){
tgt.resetTargets();
return false;
}
else{
// todo is this good enough? for up to amounts?
break;
}
}
return rr;
}
private String damageStackDescription(AbilityFactory af, SpellAbility sa){
// when damageStackDescription is called, just build exactly what is happening
StringBuilder sb = new StringBuilder();
String name = af.getHostCard().getName();
int damage = getNumDamage(sa);
ArrayList<Object> tgts;
Target tgt = AF.getAbTgt();
if (tgt != null)
tgts = tgt.getTargets();
else{
tgts = new ArrayList<Object>();
if (TgtOpp)
tgts.add(AF.getHostCard().getController().getOpponent());
}
sb.append(name).append(" - ");
sb.append("Deals ").append(damage).append(" damage to ");
for(int i = 0; i < tgts.size(); i++){
Object o = tgts.get(0);
if (o instanceof Player){
sb.append(((Player)o).getName());
}
else{
sb.append(((Card)o).getName());
}
sb.append(" ");
}
return sb.toString();
}
private void doResolve(SpellAbility saMe)
{
int damage = getNumDamage(saMe);
ArrayList<Object> tgts;
Target tgt = AF.getAbTgt();
if (tgt != null)
tgts = tgt.getTargets();
else{
tgts = new ArrayList<Object>();
if (TgtOpp)
tgts.add(AF.getHostCard().getController().getOpponent());
}
if (tgts.size() == 0){
System.out.println("No targets?");
return;
}
for(Object o : tgts){
if (o instanceof Card){
Card c = (Card)o;
if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(AF.getHostCard(), c))
c.addDamage(damage, AF.getHostCard());
}
else if (o instanceof Player){
Player p = (Player) o;
if (p.canTarget(AF.getHostCard()))
p.addDamage(damage, AF.getHostCard());
}
}
Object obj = tgts.get(0);
Player pl = null;
Card c = null;
if (obj instanceof Card){
c = (Card)obj;
pl = c.getController();
}
else{
pl = (Player)obj;
}
if (hasSubAbAF)
subAbAF.resolve();
else if (hasSubAbStr)
CardFactoryUtil.doDrawBack(subAbStr, damage, AF.getHostCard().getController(),
AF.getHostCard().getController().getOpponent(), pl, AF.getHostCard(), c, saMe);
}
}
- Code: Select all
public SpellAbility getDrawback()
{
final SpellAbility dbDealDamage = new Spell(AF.getHostCard(), null, null) {
private static final long serialVersionUID = 7239608350643325111L;
@Override
public boolean canPlay(){
return super.canPlay();
}
@Override
public boolean chkAI_Drawback() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
}
}; // Spell
return dbDealDamage;
}
- Code: Select all
private boolean isAb = false;
private boolean isSp = false;
private boolean isDb = false;
...
public boolean isSpell()
{
return isSp;
}
public boolean isDrawback()
{
return isDb;
}
private Ability_Cost abCost = null;
...
else if (mapParams.containsKey("SP"))
{
isSp = true;
API = mapParams.get("SP");
}
else if (mapParams.containsKey("DB"))
{
isDb = true;
API = mapParams.get("DB");
}
else
throw new RuntimeException("AbilityFactory : getAbility -- no API in " + hostCard.getName());
if (!mapParams.containsKey("Cost") && (!isDb))
throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName());
abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb);
...
if (API.equals("DealDamage"))
{
AbilityFactory_DealDamage dd = new AbilityFactory_DealDamage(this);
if (isAb)
SA = dd.getAbility();
else if (isSp)
SA = dd.getSpell();
else if (isDb)
SA = dd.getDrawback();
}
SpellAbility:
- Code: Select all
public boolean canPlayAI() {
return true;
}
public boolean chkAI_Drawback() {
return true;
}
public void chooseTargetAI() {
randomTarget.execute(this);
}
My roadblock is creating the new Spell or should it be an Ability? Target? Cost? Drawbacks don't necessarily target, but their effect may need to be limited with syntax similar to Target. (need to add "You" as a "target"?) Drawbacks don't have a cost, but I don't know how a null Cost object would affect things.
The Force will be with you, Always.
-

Rob Cashwalker - Programmer
- Posts: 2167
- Joined: 09 Sep 2008, 15:09
- Location: New York
- Has thanked: 5 times
- Been thanked: 40 times
Re: AbilityFactory-based SubAbilities
by friarsol » 18 Nov 2010, 17:53
I'll take a look this afternoon.
My first thought is maybe SubAbilities need to be their own class that inherit from SpellAbility. This way we don't have the confusion of "is this a spell or an ability?" Also, SubAbilities should never be on the stack by themselves, and don't have Costs. Sometimes they'll have Targets, which will need to be a consideration of Target_Selection.
I'll give you an update if I figure something out.
My first thought is maybe SubAbilities need to be their own class that inherit from SpellAbility. This way we don't have the confusion of "is this a spell or an ability?" Also, SubAbilities should never be on the stack by themselves, and don't have Costs. Sometimes they'll have Targets, which will need to be a consideration of Target_Selection.
I'll give you an update if I figure something out.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by friarsol » 18 Nov 2010, 19:06
Ok. I have two cards working right now:
Created Ability_Sub for Subabilities. I'm not sold on the name, just needed something I could use. This is used instead of your changes to SpellAbility
AbilityFactory only had one significant change, abCost is just ignored for Drawbacks.
- Code: Select all
Name:Orcish Cannonade
ManaCost:1 R R
Types:Instant
Text:no text
A:SP$DealDamage | Cost$ 1 R R | Tgt$ CP | NumDmg$2 | SubAbility$SVar=DB1 | SpellDescription$ Orcish Cannonade deals 2 damage to target creature or player and 3 damage to you. Draw a card.
SVar:DB1:DB$DealDamage | NumDmg$ 3 | Effected$ You
K:Draw a card.
SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/orcish_cannonade.jpg
End
- Code: Select all
Name:Psionic Entity
ManaCost:4 U
Types:Creature Illusion
Text:no text
PT:2/2
A:AB$DealDamage | Cost$ T | Tgt$ CP | NumDmg$2 | SubAbility$SVar=DB1 | SpellDescription$ Psionic Entity deals 2 damage to target creature or player and 3 damage to itself.
SVar:DB1:DB$DealDamage | NumDmg$ 3 | Effected$ Self
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/psionic_entity.jpg
End
Created Ability_Sub for Subabilities. I'm not sold on the name, just needed something I could use. This is used instead of your changes to SpellAbility
- Code: Select all
package forge;
abstract public class Ability_Sub extends SpellAbility implements java.io.Serializable {
private static final long serialVersionUID = 4650634415821733134L;
private SpellAbility parent = null;
public Ability_Sub(Card sourceCard, Target tgt) {
super(SpellAbility.Ability, sourceCard);
}
@Override
public boolean canPlay() {
// this should never be on the Stack by itself
return false;
}
abstract public boolean chkAI_Drawback();
public void setParent(SpellAbility parent) {
this.parent = parent;
this.setActivatingPlayer(parent.getActivatingPlayer());
}
public SpellAbility getParent() {
return parent;
}
}
- Code: Select all
package forge;
import java.util.ArrayList;
import java.util.Random;
public class AbilityFactory_DealDamage {
private AbilityFactory AF = null;
private int nDamage = -1;
private String XDamage = "none";
private boolean TgtOpp = false;
private Ability_Sub subAbAF = null;
private boolean hasSubAbAF = false;
private String subAbStr = "none";
private boolean hasSubAbStr = false;
public AbilityFactory_DealDamage(AbilityFactory newAF)
{
AF = newAF;
String tmpND = AF.getMapParams().get("NumDmg");
if (tmpND.length() > 0)
{
if (tmpND.matches("[xX]"))
XDamage = AF.getHostCard().getSVar(tmpND.substring(1));
else if (tmpND.matches("[0-9][0-9]?"))
nDamage = Integer.parseInt(tmpND);
}
if(AF.getMapParams().containsKey("Tgt"))
if (AF.getMapParams().get("Tgt").equals("TgtOpp"))
TgtOpp = true;
if(AF.hasSubAbility())
{
String sSub = AF.getMapParams().get("SubAbility");
if (sSub.startsWith("SVar="))
sSub = AF.getHostCard().getSVar(sSub.split("=")[1]);
if (sSub.startsWith("DB$"))
{
AbilityFactory afDB = new AbilityFactory();
subAbAF = (Ability_Sub)afDB.getAbility(sSub, AF.getHostCard());
hasSubAbAF = true;
}
else
{
subAbStr = sSub;
hasSubAbStr = true;
}
}
}
//public SpellAbility getAbility(final AbilityFactory af, final int NumDmg, final String NumDmgX)
public SpellAbility getAbility()
{
final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt())
{
private static final long serialVersionUID = -7560349014757367722L;
@Override
public boolean canPlay(){
return super.canPlay();
}
@Override
public boolean canPlayAI() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1);
}
};//Ability_Activated
return abDamage;
}
//public SpellAbility getSpell(final AbilityFactory af, final int NumDmg, final String NumDmgX)
public SpellAbility getSpell()
{
final SpellAbility spDealDamage = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
private static final long serialVersionUID = 7239608350643325111L;
@Override
public boolean canPlay(){
return super.canPlay();
}
@Override
public boolean canPlayAI() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
}
}; // Spell
return spDealDamage;
}
public SpellAbility getDrawback()
{
final SpellAbility dbDealDamage = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) {
private static final long serialVersionUID = 7239608350643325111L;
@Override
public boolean chkAI_Drawback() {
return doCanPlayAI(this);
}
@Override
public String getStackDescription() {
return damageStackDescription(AF, this);
}
@Override
public void resolve() {
doResolve(this);
}
}; // Spell
return dbDealDamage;
}
private int getNumDamage(SpellAbility saMe) {
if(nDamage != -1) return nDamage;
String calcX[] = XDamage.split("\\$");
if (calcX.length == 1 || calcX[1].equals("none"))
return 0;
if (calcX[0].startsWith("Count"))
{
return CardFactoryUtil.xCount(AF.getHostCard(), calcX[1]);
}
else if (calcX[0].startsWith("Sacrificed"))
{
return CardFactoryUtil.handlePaid(saMe.getSacrificedCost(), calcX[1]);
}
return 0;
}
private boolean shouldTgtP(int d) {
PlayerZone compHand = AllZone.getZone(Constant.Zone.Hand, AllZone.ComputerPlayer);
CardList hand = new CardList(compHand.getCards());
if(AF.isSpell() && hand.size() > 7) // anti-discard-at-EOT
return true;
if(AllZone.HumanPlayer.getLife() - d < 10) // if damage from this spell would drop the human to less than 10 life
return true;
return false;
}
private Card chooseTgtC(final int d) {
// Combo alert!!
PlayerZone compy = AllZone.getZone(Constant.Zone.Play, AllZone.ComputerPlayer);
CardList cPlay = new CardList(compy.getCards());
if(cPlay.size() > 0) for(int i = 0; i < cPlay.size(); i++)
if(cPlay.get(i).getName().equals("Stuffy Doll")) return cPlay.get(i);
PlayerZone human = AllZone.getZone(Constant.Zone.Play, AllZone.HumanPlayer);
CardList hPlay = new CardList(human.getCards());
hPlay = hPlay.filter(new CardListFilter() {
public boolean addCard(Card c) {
// will include creatures already dealt damage
return c.isCreature() && ((c.getNetDefense() + c.getDamage()) <= d)
&& CardFactoryUtil.canTarget(AF.getHostCard(), c);
}
});
if(hPlay.size() > 0) {
Card best = hPlay.get(0);
if(hPlay.size() > 1) {
for(int i = 1; i < hPlay.size(); i++) {
Card b = hPlay.get(i);
// choose best overall creature?
if(b.getSpellAbility().length > best.getSpellAbility().length
|| b.getKeyword().size() > best.getKeyword().size()
|| b.getNetAttack() > best.getNetAttack()) best = b;
}
}
return best;
}
return null;
}
private boolean doCanPlayAI(SpellAbility saMe)
{
// temporarily disabled until better AI
if (AF.getAbCost().getSacCost()) return false;
if (AF.getAbCost().getSubCounter()) return false;
if (AF.getAbCost().getLifeCost()) return false;
if (!ComputerUtil.canPayCost(saMe))
return false;
// TODO handle proper calculation of X values based on Cost
int damage = getNumDamage(saMe);
boolean rr = AF.isSpell();
if (AF.isAbility())
{
Random r = new Random(); // prevent run-away activations
if(r.nextFloat() <= Math.pow(.6667, AF.getHostCard().getAbilityUsed()))
rr = true;
}
Target tgt = AF.getAbTgt();
// AI handle multi-targeting?
tgt.resetTargets();
// target loop
while(tgt.getNumTargeted() < tgt.getMaxTargets()){
// TODO: Consider targeting the planeswalker
if(tgt.canTgtCreatureAndPlayer()) {
if(shouldTgtP(damage)) {
tgt.addTarget(AllZone.HumanPlayer);
continue;
}
Card c = chooseTgtC(damage);
if(c != null) {
tgt.addTarget(c);
continue;
}
}
if(tgt.canTgtPlayer() || TgtOpp) {
tgt.addTarget(AllZone.HumanPlayer);
continue;
}
if(tgt.canTgtCreature()) {
Card c = chooseTgtC(damage);
if(c != null) {
tgt.addTarget(c);
continue;
}
}
// fell through all the choices, no targets left?
if (tgt.getNumTargeted() < tgt.getMinTargets() || tgt.getNumTargeted() == 0){
tgt.resetTargets();
return false;
}
else{
// todo is this good enough? for up to amounts?
break;
}
}
return rr;
}
private String damageStackDescription(AbilityFactory af, SpellAbility sa){
// when damageStackDescription is called, just build exactly what is happening
StringBuilder sb = new StringBuilder();
String name = af.getHostCard().getName();
int damage = getNumDamage(sa);
ArrayList<Object> tgts = findTargets(sa);
if (!(sa instanceof Ability_Sub))
sb.append(name).append(" - ");
sb.append("Deals ").append(damage).append(" damage to ");
for(int i = 0; i < tgts.size(); i++){
if (i != 0)
sb.append(" ");
Object o = tgts.get(0);
if (o instanceof Player){
sb.append(((Player)o).getName());
}
else{
sb.append(((Card)o).getName());
}
}
sb.append(". ");
if (hasSubAbAF){
subAbAF.setParent(sa);
sb.append(subAbAF.getStackDescription());
}
return sb.toString();
}
private ArrayList<Object> findTargets(SpellAbility saMe){
Target tgt = AF.getAbTgt();
ArrayList<Object> tgts;
if (tgt != null)
tgts = tgt.getTargets();
else{
tgts = new ArrayList<Object>();
if (TgtOpp){
tgts.add(saMe.getActivatingPlayer().getOpponent());
}
else if (AF.getMapParams().containsKey("Effected")){
String effected = AF.getMapParams().get("Effected");
if (effected.equals("You"))
tgts.add(saMe.getActivatingPlayer());
else if (effected.equals("Self"))
tgts.add(saMe.getSourceCard());
}
}
return tgts;
}
private void doResolve(SpellAbility saMe)
{
int damage = getNumDamage(saMe);
ArrayList<Object> tgts = findTargets(saMe);
boolean targeted = (AF.getAbTgt() != null) || TgtOpp;
if (tgts == null || tgts.size() == 0){
System.out.println("No targets?");
return;
}
for(Object o : tgts){
if (o instanceof Card){
Card c = (Card)o;
if(AllZone.GameAction.isCardInPlay(c) && (!targeted || CardFactoryUtil.canTarget(AF.getHostCard(), c)))
c.addDamage(damage, AF.getHostCard());
}
else if (o instanceof Player){
Player p = (Player) o;
if (!targeted || p.canTarget(AF.getHostCard()))
p.addDamage(damage, AF.getHostCard());
}
}
if (hasSubAbAF) {
if (subAbAF.getParent() == null)
subAbAF.setParent(saMe);
subAbAF.resolve();
}
else if (hasSubAbStr){
Object obj = tgts.get(0);
Player pl = null;
Card c = null;
if (obj instanceof Card){
c = (Card)obj;
pl = c.getController();
}
else{
pl = (Player)obj;
}
CardFactoryUtil.doDrawBack(subAbStr, damage, AF.getHostCard().getController(),
AF.getHostCard().getController().getOpponent(), pl, AF.getHostCard(), c, saMe);
}
}
}
- Code: Select all
if(AF.hasSubAbility())
{
String sSub = AF.getMapParams().get("SubAbility");
// Look for SVar= instead of X to allow for multiple Drawbacks
if (sSub.startsWith("SVar="))
sSub = AF.getHostCard().getSVar(sSub.split("=")[1]);
if (sSub.startsWith("DB$"))
{
AbilityFactory afDB = new AbilityFactory();
subAbAF = (Ability_Sub)afDB.getAbility(sSub, AF.getHostCard());
hasSubAbAF = true;
}
else
{
subAbStr = sSub;
hasSubAbStr = true;
}
}
...
final SpellAbility dbDealDamage = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) {
AbilityFactory only had one significant change, abCost is just ignored for Drawbacks.
- Code: Select all
if (!isDb){
if (!mapParams.containsKey("Cost"))
throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName());
abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb);
}
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by slapshot5 » 18 Nov 2010, 19:16
I think the correct word here is "Affected", not "Effected".friarsol wrote:
- Code: Select all
SVar:DB1:DB$DealDamage | NumDmg$ 3 | Effected$ You
-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-based SubAbilities
by friarsol » 18 Nov 2010, 19:37
It's being influenced by an Effect, hence "Effected" proper English grammar be damned.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by Rob Cashwalker » 18 Nov 2010, 19:44
This is the same sort of argument I had with myself when first doing the AbilityFactory - Tgt, ValidTgt and just Valid. Tgt for the compatability to TgtP & TgtCP. ValidTgt for "TgtV". And Valid is for non-targeted effects that need to be restricted in what they AFFECT. But I didn't want to make the parameter "Affects$" because it would ultimately be linked to isValid.
BTW, good work up there. I had a feeling a subclass of SpellAbility would be needed, but I didn't know how to actually accomplish that.
BTW, good work up there. I had a feeling a subclass of SpellAbility would be needed, but I didn't know how to actually accomplish that.
The Force will be with you, Always.
-

Rob Cashwalker - Programmer
- Posts: 2167
- Joined: 09 Sep 2008, 15:09
- Location: New York
- Has thanked: 5 times
- Been thanked: 40 times
Re: AbilityFactory-based SubAbilities
by friarsol » 18 Nov 2010, 20:04
Ahhh... I had wondered what Valid was doing, I had removed it because there was a scenario where it conflicted with Shorthand Targeting, and nothing was using it yet, so I figured we could re-add it.
Maybe we should just go with NonTgt$ instead of Affected/Effected. Actually, I think I like that better anyway. Plus it'll be less letters for slap to type
Yea coming from a C++ background it makes handling some of these cases easier.
Maybe we should just go with NonTgt$ instead of Affected/Effected. Actually, I think I like that better anyway. Plus it'll be less letters for slap to type
Yea coming from a C++ background it makes handling some of these cases easier.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by Sloth » 18 Nov 2010, 20:44
I was searching for something like this when adding DestroyAll but couldn't find anything, so I made my own parameter "ValidCards". Obviously just "Valid" would also be ok.friarsol wrote:Ahhh... I had wondered what Valid was doing, I had removed it because there was a scenario where it conflicted with Shorthand Targeting, and nothing was using it yet, so I figured we could re-add it.
Maybe we should just go with NonTgt$ instead of Affected/Effected. Actually, I think I like that better anyway. Plus it'll be less letters for slap to type
"NonTgt" doesn't sound right to me though. It sounds like adding exceptions to targets or a boolean.
-

Sloth - Programmer
- Posts: 3498
- Joined: 23 Jun 2009, 19:40
- Has thanked: 125 times
- Been thanked: 507 times
Re: AbilityFactory-based SubAbilities
by friarsol » 18 Nov 2010, 21:13
So I was thinking about this a bit more and right now the SubAbility lives in the specific AbilityFactory right? I think it might be better if we assigned it to the parent SpellAbility as soon as we can. This way in Target_Selection, we have access to the SubAbilities as well and we can do extra targeting as necessary since each SA can chain to the next Child.
I'm testing out some additional changes. I can post them here when I'm done.
Edit: Here are the additional changes, we might have broken this wide open.
Target_Selection
I'm testing out some additional changes. I can post them here when I'm done.
Edit: Here are the additional changes, we might have broken this wide open.
Target_Selection
- Code: Select all
public boolean chooseTargets(){
// if not enough targets chosen, reset and cancel Ability
if (bCancel || (bDoneTarget && !target.isMinTargetsChosen())){
bCancel = true;
req.finishedTargeting();
return false;
}
else if (!doesTarget() || bDoneTarget && target.isMinTargetsChosen() || target.isMaxTargetsChosen()){
Ability_Sub abSub = ability.getSubAbility();
if (abSub == null){
// if no more SubAbilities finish targeting
req.finishedTargeting();
return true;
}
else{
// Has Sub Ability
Target_Selection ts = new Target_Selection(abSub.getTarget(), abSub);
ts.setRequirements(req);
return = ts.chooseTargets();
}
}
//targets still needed
changeInput.stopSetNext(input_targetValid(ability, target.getValidTgts(), target.getVTSelection(), this, req));
return false;
}
- Code: Select all
if (API.equals("DealDamage"))
{
AbilityFactory_DealDamage dd = new AbilityFactory_DealDamage(this);
if (isAb)
SA = dd.getAbility();
else if (isSp)
SA = dd.getSpell();
else if (isDb)
SA = dd.getDrawback();
SA.setSubAbility(dd.getSubAbility());
}
- Code: Select all
public Ability_Sub(Card sourceCard, Target tgt) {
super(SpellAbility.Ability, sourceCard);
setTarget(tgt);
}
- Code: Select all
private Ability_Sub subAbility = null;
public void setSubAbility(Ability_Sub subAbility) {
this.subAbility = subAbility;
}
public Ability_Sub getSubAbility() {
return this.subAbility;
}
- Code: Select all
Name:Lunge
ManaCost:2 R
Types:Instant
Text:no text
A:SP$DealDamage | Cost$ 2 R | Tgt$ C | NumDmg$2 | SubAbility$SVar=DB1 | SpellDescription$ Lunge deals 2 damage to target creature and 2 damage to target player.
SVar:DB1:DB$DealDamage | NumDmg$ 2 | Tgt$ P
SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/lunge.jpg
End
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by friarsol » 18 Nov 2010, 22:27
Rob, did you want me to submit this work, since I have it all setup and tested over here? I didn't want to step on your toes again.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by Rob Cashwalker » 19 Nov 2010, 04:11
hehe...
I'm washing my hands of your genius... I'm going back to sets.
I dunno, lately, I can't keep up with doing abilities and mechanics. I'm almost happier with doing these upgrades to the auxiliary functions.
I'm washing my hands of your genius... I'm going back to sets.
I dunno, lately, I can't keep up with doing abilities and mechanics. I'm almost happier with doing these upgrades to the auxiliary functions.
The Force will be with you, Always.
-

Rob Cashwalker - Programmer
- Posts: 2167
- Joined: 09 Sep 2008, 15:09
- Location: New York
- Has thanked: 5 times
- Been thanked: 40 times
Re: AbilityFactory-based SubAbilities
by friarsol » 19 Nov 2010, 04:22
I feel bamboozled :-d
Don't worry, there's been a huge onslaught of submissions lately. I'm thinking after Thanksgiving that will all slow down again and things won't be on such a frenetic pace.
Don't worry, there's been a huge onslaught of submissions lately. I'm thinking after Thanksgiving that will all slow down again and things won't be on such a frenetic pace.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: AbilityFactory-based SubAbilities
by Rob Cashwalker » 19 Nov 2010, 05:30
BTW, as this gets further refined, don't forget that the Drawback AI may not always be the same AI as if you were to play the same ability as a spell or activated ability.
Take Lunge, for example. There's no reason why the AI would not want to play it, however the normal Damage AI saves target player damage as a last resort. The primary Damage AI would likely play it.
More often than not, the default answer for chkAI_Drawback should be "Yes".
Take Lunge, for example. There's no reason why the AI would not want to play it, however the normal Damage AI saves target player damage as a last resort. The primary Damage AI would likely play it.
More often than not, the default answer for chkAI_Drawback should be "Yes".
Winston Zeddmore wrote:Ray, when someone asks if you're a god, you say YES!
The Force will be with you, Always.
-

Rob Cashwalker - Programmer
- Posts: 2167
- Joined: 09 Sep 2008, 15:09
- Location: New York
- Has thanked: 5 times
- Been thanked: 40 times
Re: AbilityFactory-based SubAbilities
by Sloth » 04 Dec 2010, 12:52
I wanted to implement Rite of Consumption via subability GainLife but the cost is not inherited to the subability, so using SVar X depending on the sacCost does not work. Is it possible to also inherit the abcost to the subability without problems?
Her would be the txt of Rite of Consumption:
Her would be the txt of Rite of Consumption:
- Code: Select all
Name:Rite of Consumption
ManaCost:1 B
Types:Sorcery
Text:no text
A:SP$DealDamage | Cost$ 1 B Sac<1/Creature> | Tgt$ TgtP | NumDmg$ X | SubAbility$ SVar=DB | SpellDescription$ CARDNAME deals damage equal to the sacrificed creature's power to target player. You gain life equal to the damage dealt this way.
SVar:DB:DB$GainLife | LifeAmount$ X
SVar:X:Sacrificed$CardPower
SVar:RemAIDeck:True
SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/rite_of_consumption.jpg
End
-

Sloth - Programmer
- Posts: 3498
- Joined: 23 Jun 2009, 19:40
- Has thanked: 125 times
- Been thanked: 507 times
Re: AbilityFactory-based SubAbilities
by friarsol » 04 Dec 2010, 15:04
I can do the same trick I'm doing with Targeted. If the SubAbility doesn't have a different target, it should look at parents until it finds one. I'm just worried that copying costs down might cause problems. And most spells (with SubAbilities) don't have more than one SubAbility anyway.Sloth wrote:I wanted to implement Rite of Consumption via subability GainLife but the cost is not inherited to the subability, so using SVar X depending on the sacCost does not work. Is it possible to also inherit the abcost to the subability without problems?
Actually at some point next week I'll move those into a Utility section for AbilityFactory so everything can use them.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
15 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 41 guests