- Adding cycling. Check Akroma's vengeance in ONS for an example. Note that this uses autohand instead of auto, this is important! You can also use autograveyard. 
- All "auto" activated abilities should work with autohand, so this is not only for cycling, but could be used for other abilities as well. For example autohand={3}:cycling can also be written autohand={3}{S}:Draw:1
This commit is contained in:
wagic.the.homebrew@gmail.com
2009-12-12 11:09:13 +00:00
parent 3d23a4f3c8
commit 512f649147
15 changed files with 164 additions and 30 deletions
+10
View File
@@ -8,6 +8,16 @@ type=Sorcery
auto=foreach(bird|myBattlefield) draw:1 auto=foreach(bird|myBattlefield) draw:1
[/card] [/card]
[card] [card]
id=41168
name=Akroma's Vengeance
mana={4}{W}{W}
type=Sorcery
autohand={3}:cycling
auto=destroy all(artifact,creature,enchantment)
text=Destroy all artifacts, creatures, and enchantments. Cycling {3} ({3}, Discard this card: Draw a card.)
rarity=R
[/card]
[card]
text=Enchant land (Target a land as you play this. This card enters the battlefield attached to that land.) You control enchanted land. text=Enchant land (Target a land as you play this. This card enters the battlefield attached to that land.) You control enchanted land.
id=41463 id=41463
alias=1194 alias=1194
-8
View File
@@ -47,14 +47,6 @@ text=Creatures you control gain protection from the color of your choice until e
rarity=U rarity=U
[/card] [/card]
[card] [card]
id=41168
name=Akroma's Vengeance
mana={4}{W}{W}
type=Sorcery
text=Destroy all artifacts, creatures, and enchantments. Cycling {3} ({3}, Discard this card: Draw a card.)
rarity=R
[/card]
[card]
id=39882 id=39882
name=Ancestor's Prophet name=Ancestor's Prophet
mana={4}{W} mana={4}{W}
+1 -3
View File
@@ -2,15 +2,13 @@
#Generic engine features #Generic engine features
######################## ########################
generic/attacks_each_turn.txt generic/attacks_each_turn.txt
generic/cycling.txt
generic/deathtouch.txt generic/deathtouch.txt
generic/doesnotuntap.txt generic/doesnotuntap.txt
generic/doesnotuntap2.txt generic/doesnotuntap2.txt
generic/double_strike.txt generic/double_strike.txt
generic/equip_landfall_buff.txt generic/equip_landfall_buff.txt
generic/equip_reach.txt generic/equip_reach.txt
generic/equip_shroud.txt
generic/equip_shroud2.txt
generic/equip_wither.txt
generic/fear.txt generic/fear.txt
generic/fear_i147.txt generic/fear_i147.txt
generic/first_and_double_strike1_i187.txt generic/first_and_double_strike1_i187.txt
@@ -0,0 +1,17 @@
#Testing Cycling
[INIT]
FIRSTMAIN
[PLAYER1]
hand:Akroma's Vengeance
library:forest
manapool:{3}
[PLAYER2]
[DO]
Akroma's Vengeance
[ASSERT]
FIRSTMAIN
[PLAYER1]
graveyard:Akroma's Vengeance
hand:forest
[PLAYER2]
[END]
+42 -2
View File
@@ -383,7 +383,8 @@ class GenericActivatedAbility:public ActivatedAbility{
MTGAbility * ability; MTGAbility * ability;
int limitPerTurn; int limitPerTurn;
int counters; int counters;
GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int myTurnOnly = 0):ActivatedAbility(_id, card,_cost,myTurnOnly,_tap),ability(a),limitPerTurn(limit){ MTGGameZone * activeZone;
GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int myTurnOnly = 0, MTGGameZone * dest = NULL):ActivatedAbility(_id, card,_cost,myTurnOnly,_tap),ability(a),limitPerTurn(limit),activeZone(dest){
counters = 0; counters = 0;
target = ability->target; target = ability->target;
} }
@@ -424,6 +425,13 @@ class GenericActivatedAbility:public ActivatedAbility{
} }
} }
int testDestroy(){
if (!activeZone) return ActivatedAbility::testDestroy();
if (activeZone->hasCard(source)) return 0;
return 1;
}
}; };
/* Generic TargetAbility */ /* Generic TargetAbility */
@@ -432,7 +440,8 @@ class GenericTargetAbility:public TargetAbility{
public: public:
int limitPerTurn; int limitPerTurn;
int counters; int counters;
GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int myTurnOnly = 0):TargetAbility(_id,_source, _tc,_cost,myTurnOnly,_tap),limitPerTurn(limit){ MTGGameZone * activeZone;
GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int myTurnOnly = 0, MTGGameZone * dest = NULL):TargetAbility(_id,_source, _tc,_cost,myTurnOnly,_tap),limitPerTurn(limit), activeZone(dest){
ability = a; ability = a;
counters = 0; counters = 0;
} }
@@ -460,10 +469,41 @@ public:
TargetAbility::Update(dt); TargetAbility::Update(dt);
} }
int testDestroy(){
if (!activeZone) return TargetAbility::testDestroy();
if (activeZone->hasCard(source)) return 0;
return 1;
}
}; };
//Cycling
class ACycle:public ActivatedAbility{
public:
ACycle(int _id, MTGCardInstance * card,Targetable * _target):ActivatedAbility(_id, card){
target = _target;
}
int resolve(){
source->controller()->game->putInGraveyard(source);
source->controller()->game->drawFromLibrary();
return 1;
}
const char * getMenuText(){
return "Cycling";
}
ACycle * clone() const{
ACycle * a = NEW ACycle(*this);
a->isClone = 1;
return a;
}
};
//Drawer, allows to draw a card for a cost: //Drawer, allows to draw a card for a cost:
+3 -3
View File
@@ -203,10 +203,10 @@ class AbilityFactory{
int parsePowerToughness(string s, int *power, int *toughness); int parsePowerToughness(string s, int *power, int *toughness);
TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target); TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target);
public: public:
int getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card = NULL, int id = 0); int getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card = NULL, int id = 0,MTGGameZone * dest = NULL);
MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0); MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0,MTGGameZone * dest = NULL);
int abilityEfficiency(MTGAbility * a, Player * p, int mode = MODE_ABILITY, TargetChooser * tc = NULL); int abilityEfficiency(MTGAbility * a, Player * p, int mode = MODE_ABILITY, TargetChooser * tc = NULL);
int magicText(int id, Spell * spell, MTGCardInstance * card = NULL, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL); int magicText(int id, Spell * spell, MTGCardInstance * card = NULL, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL, MTGGameZone * dest = NULL);
static int computeX(Spell * spell, MTGCardInstance * card); static int computeX(Spell * spell, MTGCardInstance * card);
int destroyAllInPlay(TargetChooser * tc, int bury = 0); int destroyAllInPlay(TargetChooser * tc, int bury = 0);
int moveAll(TargetChooser * tc, string destinationZone); int moveAll(TargetChooser * tc, string destinationZone);
+2
View File
@@ -36,6 +36,7 @@ class MTGCard {
int colors[Constants::MTG_NB_COLORS]; int colors[Constants::MTG_NB_COLORS];
map<int,int> basicAbilities; map<int,int> basicAbilities;
map<string,string> magicTexts;
string magicText; string magicText;
int alias; int alias;
string spellTargetType; string spellTargetType;
@@ -71,6 +72,7 @@ class MTGCard {
const char * getText(); const char * getText();
void addMagicText(string value); void addMagicText(string value);
void addMagicText(string value, string zone);
void setName(string value); void setName(string value);
const string getName() const; const string getName() const;
+8
View File
@@ -8,6 +8,14 @@
#include "../include/Counters.h" #include "../include/Counters.h"
#include "../include/WEvent.h" #include "../include/WEvent.h"
class OtherAbilitiesEventReceiver:public MTGAbility{
public:
int testDestroy();
int receiveEvent(WEvent * event);
OtherAbilitiesEventReceiver(int _id);
OtherAbilitiesEventReceiver * clone() const;
};
class MTGPutInPlayRule:public MTGAbility{ class MTGPutInPlayRule:public MTGAbility{
public: public:
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
+1 -1
View File
@@ -214,7 +214,7 @@ int AIAction::getEfficiency(){
} }
break; break;
} }
if (p->game->hand->nb_cards == 0) efficiency *= 1.3; //increase chance of using ability if hand is empty if (p->game->hand->nb_cards == 0) efficiency = (int) ((float) efficiency * 1.3); //increase chance of using ability if hand is empty
return efficiency; return efficiency;
} }
+1 -1
View File
@@ -163,7 +163,7 @@ void AIStats::Render(){
MTGCard * card = GameApp::collection->getCardById(stat->source); MTGCard * card = GameApp::collection->getCardById(stat->source);
if (card) { if (card) {
sprintf(buffer, "%s %i", card->getName().c_str(), stat->value); sprintf(buffer, "%s %i", card->getName().c_str(), stat->value);
f->DrawString(buffer,x0+5,10 + 16 *i); f->DrawString(buffer,x0+5,10 + 16 *(float)i);
i++; i++;
} }
} }
+1
View File
@@ -27,6 +27,7 @@ void DuelLayers::init(){
action->Add(NEW MTGPersistRule(-1)); action->Add(NEW MTGPersistRule(-1));
action->Add(NEW MTGLifelinkRule(-1)); action->Add(NEW MTGLifelinkRule(-1));
action->Add(NEW MTGDeathtouchRule(-1)); action->Add(NEW MTGDeathtouchRule(-1));
action->Add(NEW OtherAbilitiesEventReceiver(-1));
//Other display elements //Other display elements
action->Add(NEW HUDDisplay(-1)); action->Add(NEW HUDDisplay(-1));
+35 -12
View File
@@ -145,12 +145,10 @@ TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell
//Parses a string and returns the corresponding MTGAbility object //Parses a string and returns the corresponding MTGAbility object
// Returns NULL if parsing failed // Returns NULL if parsing failed
//Beware, Spell CAN be null when the function is called by the AI trying to analyze the effects of a given card //Beware, Spell CAN be null when the function is called by the AI trying to analyze the effects of a given card
MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated, int forceUEOT){ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated, int forceUEOT, MTGGameZone * dest){
size_t found; size_t found;
string whitespaces (" \t\f\v\n\r"); string whitespaces (" \t\f\v\n\r");
@@ -240,8 +238,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
tc = tcf.createTargetChooser(starget, card); tc = tcf.createTargetChooser(starget, card);
} }
if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,myTurnOnly); if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,myTurnOnly,dest);
return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,myTurnOnly); return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,myTurnOnly,dest);
} }
SAFE_DELETE(cost); SAFE_DELETE(cost);
} }
@@ -362,6 +360,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return NULL; return NULL;
} }
//Cycling
found = s.find("cycling");
if (found != string::npos){
MTGAbility * a = NEW ACycle(id,card,target);
a->oneShot = 1;
return a;
}
//Fizzle (counterspell...) //Fizzle (counterspell...)
found = s.find("fizzle"); found = s.find("fizzle");
@@ -842,14 +847,32 @@ int AbilityFactory::computeX(Spell * spell, MTGCardInstance * card){
} }
int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card, int id){ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card, int id, MTGGameZone * dest){
if (!card && spell) card = spell->source; if (!card && spell) card = spell->source;
if (!card) return 0; if (!card) return 0;
MTGCardInstance * target = card->target; MTGCardInstance * target = card->target;
if (!target) target = card; if (!target) target = card;
string magicText = card->magicText; string magicText;
if (card->alias && magicText.size() == 0){ if (dest) {
GameObserver * g = GameObserver::GetInstance();
for (int i = 0; i < 2 ; ++i){
MTGPlayerCards * zones = g->players[i]->game;
if (dest == zones->hand){
magicText = card->magicTexts["hand"];
break;
}
if (dest == zones->graveyard){
magicText = card->magicTexts["graveyard"];
break;
}
//Other zones needed ?
return 0;
}
}else{
magicText = card->magicText;
}
if (card->alias && magicText.size() == 0 && !dest){
MTGCard * c = GameApp::collection->getCardById(card->alias); MTGCard * c = GameApp::collection->getCardById(card->alias);
if (!c) return 0; if (!c) return 0;
magicText = c->magicText; magicText = c->magicText;
@@ -870,7 +893,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
magicText = ""; magicText = "";
} }
MTGAbility * a = parseMagicLine(line, result, spell, card); MTGAbility * a = parseMagicLine(line, result, spell, card,0,0,dest);
if (a){ if (a){
v->push_back(a); v->push_back(a);
result++; result++;
@@ -890,12 +913,12 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
* - target (if there ie a "target(" in the string, then this is a TargetAbility) * - target (if there ie a "target(" in the string, then this is a TargetAbility)
* - doTap (a dirty way to know if tapping is included in the cost... * - doTap (a dirty way to know if tapping is included in the cost...
*/ */
int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc){ int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc,MTGGameZone * dest){
int dryMode = 0; int dryMode = 0;
if (!spell) dryMode = 1; if (!spell && !dest) dryMode = 1;
vector<MTGAbility *> v; vector<MTGAbility *> v;
int result = getAbilities(&v,spell,card,id); int result = getAbilities(&v,spell,card,id,dest);
for (size_t i = 0; i < v.size(); ++i){ for (size_t i = 0; i < v.size(); ++i){
MTGAbility * a = v[i]; MTGAbility * a = v[i];
+10
View File
@@ -46,6 +46,9 @@ MTGCard::MTGCard(MTGCard * source){
mtgid = source->mtgid; mtgid = source->mtgid;
setId = source->setId; setId = source->setId;
magicText = source->magicText; magicText = source->magicText;
for(map<string,string>::const_iterator it = source->magicTexts.begin(); it != source->magicTexts.end(); ++it){
magicTexts[it->first] = source->magicTexts[it->first];
}
spellTargetType = source->spellTargetType; spellTargetType = source->spellTargetType;
alias = source->alias; alias = source->alias;
} }
@@ -63,6 +66,7 @@ int MTGCard::init(){
setId = 0; setId = 0;
mtgid = 0; mtgid = 0;
magicText = ""; magicText = "";
magicTexts.clear();
spellTargetType = ""; spellTargetType = "";
alias = 0; alias = 0;
rarity = Constants::RARITY_C; rarity = Constants::RARITY_C;
@@ -260,6 +264,12 @@ void MTGCard::addMagicText(string value){
magicText.append(value); magicText.append(value);
} }
void MTGCard::addMagicText(string value, string key){
std::transform( value.begin(), value.end(), value.begin(),::tolower );
if (magicTexts[key].size()) magicTexts[key].append("\n");
magicTexts[key].append(value);
}
void MTGCard::setName( string value){ void MTGCard::setName( string value){
name = value; name = value;
lcname = value; lcname = value;
+3
View File
@@ -30,6 +30,9 @@ int MTGAllCards::processConfLine(string s, MTGCard *card){
if(key.compare( "auto")==0){ if(key.compare( "auto")==0){
card->addMagicText(value); card->addMagicText(value);
} }
else if(key.find("auto") == 0){
card->addMagicText(value,key.substr(4));
}
else if(key.compare( "alias")==0){ else if(key.compare( "alias")==0){
card->alias=atoi(value.c_str()); card->alias=atoi(value.c_str());
} }
+30
View File
@@ -164,6 +164,36 @@ ostream& MTGAttackRule::toString(ostream& out) const
} }
OtherAbilitiesEventReceiver::OtherAbilitiesEventReceiver(int _id):MTGAbility(_id,NULL){
}
int OtherAbilitiesEventReceiver::receiveEvent(WEvent *e){
if (WEventZoneChange* event = dynamic_cast<WEventZoneChange*>(e)) {
if (event->to && (event->to != event->from)){
GameObserver * g = GameObserver::GetInstance();
for (int i = 0; i < 2; ++i){
if (event->to == g->players[i]->game->inPlay) return 0;
}
AbilityFactory af;
af.magicText(g->mLayers->actionLayer()->getMaxId(), NULL, event->card, 1, 0,event->to);
return 1;
}
}
return 0;
}
int OtherAbilitiesEventReceiver::testDestroy(){
return 0;
}
OtherAbilitiesEventReceiver * OtherAbilitiesEventReceiver::clone() const{
OtherAbilitiesEventReceiver * a = NEW OtherAbilitiesEventReceiver(*this);
a->isClone = 1;
return a;
}
MTGBlockRule::MTGBlockRule(int _id):MTGAbility(_id,NULL){ MTGBlockRule::MTGBlockRule(int _id):MTGAbility(_id,NULL){
aType=MTGAbility::MTG_BLOCK_RULE; aType=MTGAbility::MTG_BLOCK_RULE;
} }