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:
omegablast2002@yahoo.com
2012-07-16 14:59:46 +00:00
parent 7024d195fa
commit 0dbcd86b89
12 changed files with 407 additions and 12 deletions

View File

@@ -1958,6 +1958,11 @@ public:
MTGAbility * toAdd = ability->clone(); MTGAbility * toAdd = ability->clone();
toAdd->forceDestroy = -1; toAdd->forceDestroy = -1;
toAdd->target = target; toAdd->target = target;
if(toAdd->getActionTc())
{
toAdd->reactToTargetClick(source);
return 1;
}
toAdd->addToGame(); toAdd->addToGame();
return 1; return 1;
} }
@@ -1979,7 +1984,48 @@ public:
SAFE_DELETE(ability); 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 //Circle of Protections
class ACircleOfProtection: public TargetAbility class ACircleOfProtection: public TargetAbility
{ {
@@ -2233,7 +2279,7 @@ public:
if (a->oneShot) if (a->oneShot)
{ {
a->resolve(); a->resolve();
delete (a); SAFE_DELETE(a);
} }
else else
{ {
@@ -2344,7 +2390,7 @@ public:
if (a->oneShot) if (a->oneShot)
{ {
a->resolve(); a->resolve();
delete (a); SAFE_DELETE(a);
} }
else else
{ {
@@ -2463,8 +2509,11 @@ public:
if (skills.find(card) != skills.end()) if (skills.find(card) != skills.end())
{ {
if(!game->removeObserver(skills[card])) if(!game->removeObserver(skills[card]))
{
skills[card]->destroy(); skills[card]->destroy();
skills.erase(card); }
if(skills[card])
skills.erase(card);
} }
return 1; return 1;
} }
@@ -2489,7 +2538,7 @@ public:
if (a->oneShot) if (a->oneShot)
{ {
a->resolve(); a->resolve();
delete (a); SAFE_DELETE(a);
} }
else else
{ {
@@ -2534,6 +2583,15 @@ public:
AABlock * clone() const; 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 */ /* create a parent child association between cards */
class AAConnect: public InstantAbility 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...) //Foreach (plague rats...)
class AForeach: public ListMaintainerAbility, public NestedAbility class AForeach: public ListMaintainerAbility, public NestedAbility
{ {
@@ -3028,7 +3181,7 @@ public:
if (a->oneShot) if (a->oneShot)
{ {
a->resolve(); a->resolve();
delete (a); SAFE_DELETE(a);
} }
else else
{ {
@@ -3138,7 +3291,7 @@ public:
if (a->oneShot) if (a->oneShot)
{ {
a->resolve(); a->resolve();
delete (a); SAFE_DELETE(a);
} }
else else
{ {
@@ -3229,7 +3382,7 @@ public:
if (a->oneShot) if (a->oneShot)
{ {
a->resolve(); a->resolve();
delete (a); SAFE_DELETE(a);
} }
else else
{ {

View File

@@ -155,6 +155,7 @@ public:
int isAttacker(); int isAttacker();
Targetable * isAttacking; Targetable * isAttacking;
MTGCardInstance * storedCard; MTGCardInstance * storedCard;
MTGCardInstance * myPair;
MTGCardInstance * createSnapShot(); MTGCardInstance * createSnapShot();
MTGCardInstance * storedSourceCard; MTGCardInstance * storedSourceCard;
MTGCardInstance * isDefenser(); MTGCardInstance * isDefenser();

View File

@@ -218,8 +218,9 @@ class Constants
NOMANA = 97, NOMANA = 97,
ONLYMANA = 98, ONLYMANA = 98,
POISONDAMAGER = 99, POISONDAMAGER = 99,
soulbond = 100,
NB_BASIC_ABILITIES = 100, NB_BASIC_ABILITIES = 101,
RARITY_S = 'S', //Special Rarity RARITY_S = 'S', //Special Rarity

View File

@@ -255,7 +255,24 @@ public:
virtual MTGBlockRule * clone() const; virtual MTGBlockRule * clone() const;
~MTGBlockRule(); ~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 */ /* Persist Rule */
class MTGPersistRule: public PermanentAbility class MTGPersistRule: public PermanentAbility
{ {

View File

@@ -279,6 +279,25 @@ public:
virtual bool equals(TargetChooser * tc); 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 class myCursesChooser: public TypeTargetChooser
{ {
public: public:

View File

@@ -3161,9 +3161,9 @@ int MenuAbility::resolve()
const char * MenuAbility::getMenuText() const char * MenuAbility::getMenuText()
{ {
if(abilities.size()) if((abilities.size() > 1 && must)||(abilities.size() > 2 && !must))
return "choose one"; return "choose one";
return ability->getMenuText(); return "Action";
} }
int MenuAbility::testDestroy() 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. // abilities[i]->clone();//all get cloned for clean up purposes. EDIT:removed, cause memleaks.
} }
if(!mClone) if(!mClone)
{
if (source->controller() == game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
return 0; return 0;
}
mClone->target = abilities[choice]->target; mClone->target = abilities[choice]->target;
mClone->oneShot = true; mClone->oneShot = true;
mClone->forceDestroy = 1; mClone->forceDestroy = 1;
@@ -4853,6 +4857,32 @@ AABlock * AABlock::clone() const
return NEW AABlock(*this); 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) // target becomes a parent of card(source)
AAConnect::AAConnect(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) : AAConnect::AAConnect(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :

View File

@@ -598,6 +598,11 @@ void GameObserver::gameStateBasedEffects()
card->mPropertiesChangedSinceLastUpdate = false; card->mPropertiesChangedSinceLastUpdate = false;
if(card->hasType(Subtypes::TYPE_PLANESWALKER) && (!card->counters||!card->counters->hasCounter("loyalty",0,0))) if(card->hasType(Subtypes::TYPE_PLANESWALKER) && (!card->counters||!card->counters->hasCounter("loyalty",0,0)))
players[i]->game->putInGraveyard(card); 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// //Remove auras that don't have a valid target anymore//
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////

View File

@@ -910,6 +910,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
observer->addObserver(NEW MTGBlockRule(observer, -1)); observer->addObserver(NEW MTGBlockRule(observer, -1));
return NULL; 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. //this rule handles combat related triggers. note, combat related triggered abilities will not work without it.
found = s.find("combattriggerrule"); found = s.find("combattriggerrule");
if(found != string::npos) if(found != string::npos)
@@ -1625,6 +1632,17 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return NULL; 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) if (!activated && tc)
{ {
MTGAbility * a = parseMagicLine(sWithoutTc, id, spell, card); MTGAbility * a = parseMagicLine(sWithoutTc, id, spell, card);

View File

@@ -169,6 +169,7 @@ void MTGCardInstance::initMTGCI()
isAttacking = NULL; isAttacking = NULL;
storedCard = NULL; storedCard = NULL;
storedSourceCard = NULL; storedSourceCard = NULL;
myPair = NULL;
for (int i = 0; i < ManaCost::MANA_PAID_WITH_RETRACE +1; i++) for (int i = 0; i < ManaCost::MANA_PAID_WITH_RETRACE +1; i++)
alternateCostPaid[i] = 0; alternateCostPaid[i] = 0;

View File

@@ -128,7 +128,8 @@ const char* Constants::MTGBasicAbilities[] = {
"notapability", "notapability",
"nomanaability", "nomanaability",
"onlymanaability", "onlymanaability",
"poisondamager"//deals damage to players as poison counters. "poisondamager",//deals damage to players as poison counters.
"soulbond"
}; };
map<string,int> Constants::MTGBasicAbilitiesMap; map<string,int> Constants::MTGBasicAbilitiesMap;

View File

@@ -1981,6 +1981,114 @@ HUDDisplay * HUDDisplay::clone() const
return NEW HUDDisplay(*this); 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 */ /* Persist */
MTGPersistRule::MTGPersistRule(GameObserver* observer, int _id) : MTGPersistRule::MTGPersistRule(GameObserver* observer, int _id) :
PermanentAbility(observer, _id) PermanentAbility(observer, _id)

View File

@@ -25,6 +25,13 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
return NEW BlockableChooser(observer, card, maxtargets); 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"); found = s.find("mytgt");
if (found == 0) if (found == 0)
{ {
@@ -1548,6 +1555,40 @@ bool BlockableChooser::equals(TargetChooser * tc)
return TypeTargetChooser::equals(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 */ /*Proliferate Target */
bool ProliferateChooser::canTarget(Targetable * target,bool withoutProtections) bool ProliferateChooser::canTarget(Targetable * target,bool withoutProtections)