Erwan
- 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:
@@ -8,6 +8,16 @@ type=Sorcery
|
||||
auto=foreach(bird|myBattlefield) draw:1
|
||||
[/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.
|
||||
id=41463
|
||||
alias=1194
|
||||
|
||||
@@ -47,14 +47,6 @@ text=Creatures you control gain protection from the color of your choice until e
|
||||
rarity=U
|
||||
[/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
|
||||
name=Ancestor's Prophet
|
||||
mana={4}{W}
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
#Generic engine features
|
||||
########################
|
||||
generic/attacks_each_turn.txt
|
||||
generic/cycling.txt
|
||||
generic/deathtouch.txt
|
||||
generic/doesnotuntap.txt
|
||||
generic/doesnotuntap2.txt
|
||||
generic/double_strike.txt
|
||||
generic/equip_landfall_buff.txt
|
||||
generic/equip_reach.txt
|
||||
generic/equip_shroud.txt
|
||||
generic/equip_shroud2.txt
|
||||
generic/equip_wither.txt
|
||||
generic/fear.txt
|
||||
generic/fear_i147.txt
|
||||
generic/first_and_double_strike1_i187.txt
|
||||
|
||||
17
projects/mtg/bin/Res/test/generic/cycling.txt
Normal file
17
projects/mtg/bin/Res/test/generic/cycling.txt
Normal file
@@ -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]
|
||||
@@ -383,7 +383,8 @@ class GenericActivatedAbility:public ActivatedAbility{
|
||||
MTGAbility * ability;
|
||||
int limitPerTurn;
|
||||
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;
|
||||
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 */
|
||||
@@ -432,7 +440,8 @@ class GenericTargetAbility:public TargetAbility{
|
||||
public:
|
||||
int limitPerTurn;
|
||||
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;
|
||||
counters = 0;
|
||||
}
|
||||
@@ -460,10 +469,41 @@ public:
|
||||
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:
|
||||
|
||||
@@ -203,10 +203,10 @@ class AbilityFactory{
|
||||
int parsePowerToughness(string s, int *power, int *toughness);
|
||||
TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target);
|
||||
public:
|
||||
int getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card = NULL, int id = 0);
|
||||
MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 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,MTGGameZone * dest = 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);
|
||||
int destroyAllInPlay(TargetChooser * tc, int bury = 0);
|
||||
int moveAll(TargetChooser * tc, string destinationZone);
|
||||
|
||||
@@ -36,6 +36,7 @@ class MTGCard {
|
||||
|
||||
int colors[Constants::MTG_NB_COLORS];
|
||||
map<int,int> basicAbilities;
|
||||
map<string,string> magicTexts;
|
||||
string magicText;
|
||||
int alias;
|
||||
string spellTargetType;
|
||||
@@ -71,6 +72,7 @@ class MTGCard {
|
||||
const char * getText();
|
||||
|
||||
void addMagicText(string value);
|
||||
void addMagicText(string value, string zone);
|
||||
|
||||
void setName(string value);
|
||||
const string getName() const;
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
#include "../include/Counters.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{
|
||||
public:
|
||||
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
||||
|
||||
@@ -214,7 +214,7 @@ int AIAction::getEfficiency(){
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ void AIStats::Render(){
|
||||
MTGCard * card = GameApp::collection->getCardById(stat->source);
|
||||
if (card) {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ void DuelLayers::init(){
|
||||
action->Add(NEW MTGPersistRule(-1));
|
||||
action->Add(NEW MTGLifelinkRule(-1));
|
||||
action->Add(NEW MTGDeathtouchRule(-1));
|
||||
action->Add(NEW OtherAbilitiesEventReceiver(-1));
|
||||
//Other display elements
|
||||
action->Add(NEW HUDDisplay(-1));
|
||||
|
||||
|
||||
@@ -145,12 +145,10 @@ TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//Parses a string and returns the corresponding MTGAbility object
|
||||
// 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
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,myTurnOnly);
|
||||
return NEW GenericActivatedAbility(id, card, 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,dest);
|
||||
}
|
||||
SAFE_DELETE(cost);
|
||||
}
|
||||
@@ -362,6 +360,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
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...)
|
||||
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) return 0;
|
||||
MTGCardInstance * target = card->target;
|
||||
if (!target) target = card;
|
||||
string magicText = card->magicText;
|
||||
if (card->alias && magicText.size() == 0){
|
||||
string magicText;
|
||||
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);
|
||||
if (!c) return 0;
|
||||
magicText = c->magicText;
|
||||
@@ -870,7 +893,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
|
||||
magicText = "";
|
||||
}
|
||||
|
||||
MTGAbility * a = parseMagicLine(line, result, spell, card);
|
||||
MTGAbility * a = parseMagicLine(line, result, spell, card,0,0,dest);
|
||||
if (a){
|
||||
v->push_back(a);
|
||||
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)
|
||||
* - 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;
|
||||
if (!spell) dryMode = 1;
|
||||
if (!spell && !dest) dryMode = 1;
|
||||
|
||||
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){
|
||||
MTGAbility * a = v[i];
|
||||
|
||||
@@ -46,6 +46,9 @@ MTGCard::MTGCard(MTGCard * source){
|
||||
mtgid = source->mtgid;
|
||||
setId = source->setId;
|
||||
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;
|
||||
alias = source->alias;
|
||||
}
|
||||
@@ -63,6 +66,7 @@ int MTGCard::init(){
|
||||
setId = 0;
|
||||
mtgid = 0;
|
||||
magicText = "";
|
||||
magicTexts.clear();
|
||||
spellTargetType = "";
|
||||
alias = 0;
|
||||
rarity = Constants::RARITY_C;
|
||||
@@ -260,6 +264,12 @@ void MTGCard::addMagicText(string 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){
|
||||
name = value;
|
||||
lcname = value;
|
||||
|
||||
@@ -30,6 +30,9 @@ int MTGAllCards::processConfLine(string s, MTGCard *card){
|
||||
if(key.compare( "auto")==0){
|
||||
card->addMagicText(value);
|
||||
}
|
||||
else if(key.find("auto") == 0){
|
||||
card->addMagicText(value,key.substr(4));
|
||||
}
|
||||
else if(key.compare( "alias")==0){
|
||||
card->alias=atoi(value.c_str());
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
aType=MTGAbility::MTG_BLOCK_RULE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user