this commit adds AVRs new ability soulbond.
it requires an update to your mtg rules txt...
the coding is as follows
[card]
name=Arbor Elf
mana={g}
auto=soulbond 1/3
auto=soulbond {t}:add{g}
abilities=soulbond
type=Creature
subtype=Elf Druid
power=1
toughness=1
[/card]
auto=soulbond *any ability supported by wagic*
abilities=soulbond
the above arbor elf gives itself and its soulbond creature a 1/3 bonus and the ability to tap for green mana.
This commit is contained in:
@@ -1958,6 +1958,11 @@ public:
|
||||
MTGAbility * toAdd = ability->clone();
|
||||
toAdd->forceDestroy = -1;
|
||||
toAdd->target = target;
|
||||
if(toAdd->getActionTc())
|
||||
{
|
||||
toAdd->reactToTargetClick(source);
|
||||
return 1;
|
||||
}
|
||||
toAdd->addToGame();
|
||||
return 1;
|
||||
}
|
||||
@@ -1979,7 +1984,48 @@ public:
|
||||
SAFE_DELETE(ability);
|
||||
}
|
||||
};
|
||||
//generic addtogame
|
||||
class GenericAddToGame: public InstantAbility, public NestedAbility
|
||||
{
|
||||
public:
|
||||
GenericAddToGame(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
||||
InstantAbility(observer, _id, _source,_target), NestedAbility(ability)
|
||||
{
|
||||
ability->target = _target;
|
||||
}
|
||||
|
||||
int addToGame()
|
||||
{
|
||||
InstantAbility::addToGame();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int resolve()
|
||||
{
|
||||
MTGAbility * toAdd = ability->clone();
|
||||
toAdd->target = target;
|
||||
if(toAdd->getActionTc())
|
||||
return toAdd->reactToTargetClick(source);
|
||||
return toAdd->addToGame();
|
||||
}
|
||||
|
||||
const char * getMenuText()
|
||||
{
|
||||
return ability->getMenuText();
|
||||
}
|
||||
|
||||
GenericAddToGame * clone() const
|
||||
{
|
||||
GenericAddToGame * a = NEW GenericAddToGame(*this);
|
||||
a->ability = ability->clone();
|
||||
return a;
|
||||
}
|
||||
|
||||
~GenericAddToGame()
|
||||
{
|
||||
SAFE_DELETE(ability);
|
||||
}
|
||||
};
|
||||
//Circle of Protections
|
||||
class ACircleOfProtection: public TargetAbility
|
||||
{
|
||||
@@ -2233,7 +2279,7 @@ public:
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
delete (a);
|
||||
SAFE_DELETE(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2344,7 +2390,7 @@ public:
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
delete (a);
|
||||
SAFE_DELETE(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2463,8 +2509,11 @@ public:
|
||||
if (skills.find(card) != skills.end())
|
||||
{
|
||||
if(!game->removeObserver(skills[card]))
|
||||
{
|
||||
skills[card]->destroy();
|
||||
skills.erase(card);
|
||||
}
|
||||
if(skills[card])
|
||||
skills.erase(card);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -2489,7 +2538,7 @@ public:
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
delete (a);
|
||||
SAFE_DELETE(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2534,6 +2583,15 @@ public:
|
||||
AABlock * clone() const;
|
||||
};
|
||||
|
||||
/* assign a creature as a pair to target */
|
||||
class PairCard: public InstantAbility
|
||||
{
|
||||
public:
|
||||
PairCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
||||
int resolve();
|
||||
PairCard * clone() const;
|
||||
};
|
||||
|
||||
/* create a parent child association between cards */
|
||||
class AAConnect: public InstantAbility
|
||||
{
|
||||
@@ -2990,6 +3048,101 @@ public:
|
||||
|
||||
};
|
||||
///
|
||||
//a paired lord
|
||||
class APaired: public MTGAbility, public NestedAbility
|
||||
{
|
||||
public:
|
||||
MTGAbility * a;
|
||||
MTGAbility * b;
|
||||
APaired(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
||||
MTGAbility(observer, _id, _source, _target), NestedAbility(ability)
|
||||
{
|
||||
ability->source = source;
|
||||
ability->target = target;
|
||||
a = NULL;
|
||||
}
|
||||
|
||||
int removeFromGame()
|
||||
{
|
||||
return removeAbilityFromGame();
|
||||
}
|
||||
|
||||
int addToGame()
|
||||
{
|
||||
return MTGAbility::addToGame();
|
||||
}
|
||||
|
||||
void Update(float dt)
|
||||
{
|
||||
resolve();
|
||||
}
|
||||
|
||||
int resolve()
|
||||
{
|
||||
if (source->myPair)
|
||||
{
|
||||
addAbilityToGame();
|
||||
}
|
||||
else
|
||||
{
|
||||
removeAbilityFromGame();
|
||||
}
|
||||
if (ability->oneShot) a = NULL; //allows to call the effect several times
|
||||
return 1;
|
||||
}
|
||||
|
||||
int addAbilityToGame()
|
||||
{
|
||||
if (a && b) return 0;
|
||||
a = ability->clone();
|
||||
b = ability->clone();
|
||||
a->source = source;
|
||||
a->target = source;
|
||||
b->source = source->myPair;
|
||||
b->target = source->myPair;
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
b->resolve();
|
||||
SAFE_DELETE(a);
|
||||
SAFE_DELETE(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
a->addToGame();
|
||||
b->addToGame();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int destroy()
|
||||
{
|
||||
return removeAbilityFromGame();
|
||||
}
|
||||
|
||||
int removeAbilityFromGame()
|
||||
{
|
||||
if (!a && !b) return 0;
|
||||
game->removeObserver(a);
|
||||
a = NULL;
|
||||
game->removeObserver(b);
|
||||
b = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
~APaired()
|
||||
{
|
||||
SAFE_DELETE(ability);
|
||||
}
|
||||
|
||||
APaired * clone() const
|
||||
{
|
||||
APaired * a = NEW APaired(*this);
|
||||
a->ability = ability->clone();
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
//Foreach (plague rats...)
|
||||
class AForeach: public ListMaintainerAbility, public NestedAbility
|
||||
{
|
||||
@@ -3028,7 +3181,7 @@ public:
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
delete (a);
|
||||
SAFE_DELETE(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3138,7 +3291,7 @@ public:
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
delete (a);
|
||||
SAFE_DELETE(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3229,7 +3382,7 @@ public:
|
||||
if (a->oneShot)
|
||||
{
|
||||
a->resolve();
|
||||
delete (a);
|
||||
SAFE_DELETE(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -155,6 +155,7 @@ public:
|
||||
int isAttacker();
|
||||
Targetable * isAttacking;
|
||||
MTGCardInstance * storedCard;
|
||||
MTGCardInstance * myPair;
|
||||
MTGCardInstance * createSnapShot();
|
||||
MTGCardInstance * storedSourceCard;
|
||||
MTGCardInstance * isDefenser();
|
||||
|
||||
@@ -218,8 +218,9 @@ class Constants
|
||||
NOMANA = 97,
|
||||
ONLYMANA = 98,
|
||||
POISONDAMAGER = 99,
|
||||
soulbond = 100,
|
||||
|
||||
NB_BASIC_ABILITIES = 100,
|
||||
NB_BASIC_ABILITIES = 101,
|
||||
|
||||
|
||||
RARITY_S = 'S', //Special Rarity
|
||||
|
||||
@@ -255,7 +255,24 @@ public:
|
||||
virtual MTGBlockRule * clone() const;
|
||||
~MTGBlockRule();
|
||||
};
|
||||
|
||||
//soulbond rule
|
||||
class MTGSoulbondRule: public PermanentAbility
|
||||
{
|
||||
public:
|
||||
vector<MTGCardInstance*>soulbonders;
|
||||
TargetChooser * tcb;
|
||||
MTGAbility * pairAbility;
|
||||
MTGAbility * targetAbility;
|
||||
MTGAbility * targetAbility1;
|
||||
MTGAbility * mod;
|
||||
MTGAbility * activatePairing;
|
||||
vector<MTGAbility*>pairing;
|
||||
MTGSoulbondRule(GameObserver* observer, int _id);
|
||||
int receiveEvent(WEvent * event);
|
||||
virtual ostream& toString(ostream& out) const;
|
||||
virtual MTGSoulbondRule * clone() const;
|
||||
~MTGSoulbondRule();
|
||||
};
|
||||
/* Persist Rule */
|
||||
class MTGPersistRule: public PermanentAbility
|
||||
{
|
||||
|
||||
@@ -279,6 +279,25 @@ public:
|
||||
virtual bool equals(TargetChooser * tc);
|
||||
};
|
||||
|
||||
class pairableChooser: public TypeTargetChooser
|
||||
{
|
||||
public:
|
||||
bool withoutProtections;
|
||||
pairableChooser(GameObserver *observer, int * _zones, int _nbzones, MTGCardInstance * card = NULL, int _maxtargets = 1, bool other = false, bool targetMin = false) :
|
||||
TypeTargetChooser(observer, "creature|mybattlefield",_zones, _nbzones, card, _maxtargets, other, targetMin)
|
||||
{
|
||||
}
|
||||
;
|
||||
pairableChooser(GameObserver *observer, MTGCardInstance * card = NULL, int _maxtargets = 1, bool other = false,bool targetMin = false) :
|
||||
TypeTargetChooser(observer, "creature|mybattlefield", card, _maxtargets, other,targetMin)
|
||||
{
|
||||
}
|
||||
;
|
||||
virtual bool canTarget(Targetable * target, bool withoutProtections = false);
|
||||
virtual pairableChooser * clone() const;
|
||||
virtual bool equals(TargetChooser * tc);
|
||||
};
|
||||
|
||||
class myCursesChooser: public TypeTargetChooser
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -3161,9 +3161,9 @@ int MenuAbility::resolve()
|
||||
|
||||
const char * MenuAbility::getMenuText()
|
||||
{
|
||||
if(abilities.size())
|
||||
if((abilities.size() > 1 && must)||(abilities.size() > 2 && !must))
|
||||
return "choose one";
|
||||
return ability->getMenuText();
|
||||
return "Action";
|
||||
}
|
||||
|
||||
int MenuAbility::testDestroy()
|
||||
@@ -3198,7 +3198,11 @@ int MenuAbility::reactToChoiceClick(Targetable * object,int choice,int control)
|
||||
// abilities[i]->clone();//all get cloned for clean up purposes. EDIT:removed, cause memleaks.
|
||||
}
|
||||
if(!mClone)
|
||||
{
|
||||
if (source->controller() == game->isInterrupting)
|
||||
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
|
||||
return 0;
|
||||
}
|
||||
mClone->target = abilities[choice]->target;
|
||||
mClone->oneShot = true;
|
||||
mClone->forceDestroy = 1;
|
||||
@@ -4853,6 +4857,32 @@ AABlock * AABlock::clone() const
|
||||
return NEW AABlock(*this);
|
||||
}
|
||||
|
||||
// target becomes pair of source
|
||||
PairCard::PairCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
||||
InstantAbility(observer, id, card, target)
|
||||
{
|
||||
target = _target;
|
||||
oneShot = true;
|
||||
forceDestroy = 1;
|
||||
}
|
||||
|
||||
int PairCard::resolve()
|
||||
{
|
||||
MTGCardInstance * _target = (MTGCardInstance *) target;
|
||||
source = (MTGCardInstance*)source;
|
||||
if (_target && !_target->myPair && source)
|
||||
{
|
||||
source->myPair = _target;
|
||||
_target->myPair = source;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
PairCard * PairCard::clone() const
|
||||
{
|
||||
return NEW PairCard(*this);
|
||||
}
|
||||
|
||||
|
||||
// target becomes a parent of card(source)
|
||||
AAConnect::AAConnect(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
||||
|
||||
@@ -598,6 +598,11 @@ void GameObserver::gameStateBasedEffects()
|
||||
card->mPropertiesChangedSinceLastUpdate = false;
|
||||
if(card->hasType(Subtypes::TYPE_PLANESWALKER) && (!card->counters||!card->counters->hasCounter("loyalty",0,0)))
|
||||
players[i]->game->putInGraveyard(card);
|
||||
if(card->myPair && !isInPlay(card->myPair))
|
||||
{
|
||||
card->myPair->myPair = NULL;
|
||||
card->myPair = NULL;
|
||||
}
|
||||
///////////////////////////////////////////////////////
|
||||
//Remove auras that don't have a valid target anymore//
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
@@ -910,6 +910,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
observer->addObserver(NEW MTGBlockRule(observer, -1));
|
||||
return NULL;
|
||||
}
|
||||
//this rule handles blocking ability during blocker phase
|
||||
found = s.find("soulbondrule");
|
||||
if(found != string::npos)
|
||||
{
|
||||
observer->addObserver(NEW MTGSoulbondRule(observer, -1));
|
||||
return NULL;
|
||||
}
|
||||
//this rule handles combat related triggers. note, combat related triggered abilities will not work without it.
|
||||
found = s.find("combattriggerrule");
|
||||
if(found != string::npos)
|
||||
@@ -1625,6 +1632,17 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//soulbond lord style ability.
|
||||
found = s.find("soulbond ");
|
||||
if (found != string::npos)
|
||||
{
|
||||
string s1 = s.substr(found + 9);
|
||||
MTGAbility * a = parseMagicLine(s1, id, spell, card, false, activated);
|
||||
if(a)
|
||||
return NEW APaired(observer,id, card,card->myPair,a);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!activated && tc)
|
||||
{
|
||||
MTGAbility * a = parseMagicLine(sWithoutTc, id, spell, card);
|
||||
|
||||
@@ -169,6 +169,7 @@ void MTGCardInstance::initMTGCI()
|
||||
isAttacking = NULL;
|
||||
storedCard = NULL;
|
||||
storedSourceCard = NULL;
|
||||
myPair = NULL;
|
||||
|
||||
for (int i = 0; i < ManaCost::MANA_PAID_WITH_RETRACE +1; i++)
|
||||
alternateCostPaid[i] = 0;
|
||||
|
||||
@@ -128,7 +128,8 @@ const char* Constants::MTGBasicAbilities[] = {
|
||||
"notapability",
|
||||
"nomanaability",
|
||||
"onlymanaability",
|
||||
"poisondamager"//deals damage to players as poison counters.
|
||||
"poisondamager",//deals damage to players as poison counters.
|
||||
"soulbond"
|
||||
};
|
||||
|
||||
map<string,int> Constants::MTGBasicAbilitiesMap;
|
||||
|
||||
@@ -1981,6 +1981,114 @@ HUDDisplay * HUDDisplay::clone() const
|
||||
return NEW HUDDisplay(*this);
|
||||
}
|
||||
|
||||
/* soulbond */
|
||||
MTGSoulbondRule::MTGSoulbondRule(GameObserver* observer, int _id) :
|
||||
PermanentAbility(observer, _id)
|
||||
{
|
||||
tcb = NULL;
|
||||
pairAbility = NULL;
|
||||
targetAbility = NULL;
|
||||
mod = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
int MTGSoulbondRule::receiveEvent(WEvent * event)
|
||||
{
|
||||
if (event->type == WEvent::CHANGE_ZONE)
|
||||
{
|
||||
WEventZoneChange * e = (WEventZoneChange *) event;
|
||||
MTGCardInstance * card = e->card;
|
||||
if (!card || !card->isCreature()) return 0;
|
||||
int ok = 0;
|
||||
if(card->basicAbilities[(int)Constants::soulbond] || soulbonders.size())
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Player * p = game->players[i];
|
||||
if (e->to == p->game->inPlay)
|
||||
{
|
||||
ok = 1;
|
||||
if(card->basicAbilities[(int)Constants::soulbond])
|
||||
soulbonders.push_back(e->card);
|
||||
}
|
||||
}
|
||||
if(!soulbonders.size())
|
||||
ok = 0;
|
||||
else
|
||||
{
|
||||
MTGCardInstance * pairable = NULL;
|
||||
for(unsigned int k = 0;k < soulbonders.size();k++)
|
||||
{
|
||||
MTGCardInstance * check = soulbonders[k];
|
||||
if(check->controller() == e->card->controller())
|
||||
{
|
||||
if(!check->myPair)
|
||||
{
|
||||
if(check != card)
|
||||
pairable = check;
|
||||
else
|
||||
{
|
||||
MTGInPlay * zone = check->controller()->game->battlefield;
|
||||
for(unsigned int d = 0;d < zone->cards.size();++d)
|
||||
{
|
||||
if(zone->cards[d]->isCreature() && !zone->cards[d]->myPair && zone->cards[d] != check)
|
||||
{
|
||||
pairable = check;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(!pairable)
|
||||
ok = 0;
|
||||
}
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Player * p = game->players[i];
|
||||
if (e->to == p->game->inPlay)
|
||||
{
|
||||
TargetChooserFactory tf(card->getObserver());
|
||||
tcb = tf.createTargetChooser("pairable",card);
|
||||
tcb->targetter = NULL;
|
||||
pairAbility = NEW PairCard(game, game->mLayers->actionLayer()->getMaxId(), card,NULL);
|
||||
targetAbility = NEW GenericTargetAbility(game, "Pair Creature","",game->mLayers->actionLayer()->getMaxId(), card,tcb,pairAbility);
|
||||
targetAbility1 = NEW MayAbility(game,game->mLayers->actionLayer()->getMaxId(),targetAbility,card,false);
|
||||
activatePairing = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), card,NULL,targetAbility1);
|
||||
//this next line is ugly, but fixes a long running memleak which seems to be unfixable while maintaining the same order of activation.
|
||||
game->mLayers->actionLayer()->garbage.push_back(activatePairing);
|
||||
activatePairing->fireAbility();
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ostream& MTGSoulbondRule::toString(ostream& out) const
|
||||
{
|
||||
out << "MTGSoulbondRule ::: (";
|
||||
return MTGAbility::toString(out) << ")";
|
||||
}
|
||||
|
||||
MTGSoulbondRule * MTGSoulbondRule::clone() const
|
||||
{
|
||||
return NEW MTGSoulbondRule(*this);
|
||||
}
|
||||
MTGSoulbondRule::~MTGSoulbondRule()
|
||||
{
|
||||
for(size_t k = pairing.size()-1;k > 0; k--)
|
||||
{
|
||||
//SAFE_DELETE(pairing[k]);
|
||||
}
|
||||
}
|
||||
/* Persist */
|
||||
MTGPersistRule::MTGPersistRule(GameObserver* observer, int _id) :
|
||||
PermanentAbility(observer, _id)
|
||||
|
||||
@@ -25,6 +25,13 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
|
||||
return NEW BlockableChooser(observer, card, maxtargets);
|
||||
}
|
||||
|
||||
found = s.find("pairable");
|
||||
if (found != string::npos)
|
||||
{
|
||||
int maxtargets = 1;
|
||||
return NEW pairableChooser(observer, card, maxtargets);
|
||||
}
|
||||
|
||||
found = s.find("mytgt");
|
||||
if (found == 0)
|
||||
{
|
||||
@@ -1548,6 +1555,40 @@ bool BlockableChooser::equals(TargetChooser * tc)
|
||||
return TypeTargetChooser::equals(tc);
|
||||
}
|
||||
|
||||
/*display cards pairable by source */
|
||||
bool pairableChooser::canTarget(Targetable * target,bool withoutProtections)
|
||||
{
|
||||
if (MTGCardInstance * card = dynamic_cast<MTGCardInstance*>(target))
|
||||
{
|
||||
if(card->myPair || card == source)
|
||||
return false;
|
||||
if(!card->isCreature() || !card->isInPlay(observer))
|
||||
return false;
|
||||
if(card->controller() != source->controller())
|
||||
return false;
|
||||
if(!card->has(Constants::soulbond) && !source->has(Constants::soulbond))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pairableChooser* pairableChooser::clone() const
|
||||
{
|
||||
pairableChooser * a = NEW pairableChooser(*this);
|
||||
return a;
|
||||
}
|
||||
|
||||
bool pairableChooser::equals(TargetChooser * tc)
|
||||
{
|
||||
|
||||
pairableChooser * dtc = dynamic_cast<pairableChooser *> (tc);
|
||||
if (!dtc)
|
||||
return false;
|
||||
|
||||
return TypeTargetChooser::equals(tc);
|
||||
}
|
||||
|
||||
//-----------
|
||||
/*Proliferate Target */
|
||||
bool ProliferateChooser::canTarget(Targetable * target,bool withoutProtections)
|
||||
|
||||
Reference in New Issue
Block a user