790e2a2cdf
- improved the parser for "lord", now can take any kind of "target", not only creature types (so it can now be used to code abilities such as Bad moon and crusade) - fixed typos for goblin lord, zombie master, lord of atlantis - Added "moveTo" keyword - added a dozen cards
2995 lines
80 KiB
C++
2995 lines
80 KiB
C++
#ifndef _CARDS_H_
|
|
#define _CARDS_H_
|
|
|
|
#include "MTGAbility.h"
|
|
#include "ManaCost.h"
|
|
#include "CardDescriptor.h"
|
|
#include "AIPlayer.h"
|
|
#include "CardDisplay.h"
|
|
#include "Subtypes.h"
|
|
#include "CardGui.h"
|
|
#include "GameOptions.h"
|
|
|
|
#include <JGui.h>
|
|
#include <hge/hgeparticle.h>
|
|
|
|
|
|
#include <map>
|
|
using std::map;
|
|
|
|
/*
|
|
Generic classes
|
|
*/
|
|
|
|
|
|
//Drawer, allows to draw a card for a cost:
|
|
|
|
class ADrawer:public ActivatedAbility{
|
|
public:
|
|
int nbcards;
|
|
ADrawer(int _id, MTGCardInstance * card,ManaCost * _cost, int _nbcards = 1, int _tap = 1):ActivatedAbility(_id, card,_cost,0,_tap),nbcards(_nbcards){
|
|
}
|
|
|
|
int resolve(){
|
|
game->mLayers->stackLayer()->addDraw(source->controller(),nbcards);
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Draw";
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
//Moves Cards from a zone to another
|
|
class AZoneMover:public TargetAbility{
|
|
|
|
public:
|
|
string destinationZone;
|
|
|
|
AZoneMover(int _id, MTGCardInstance * _source, TargetChooser * _tc,string destZone, ManaCost * _cost = NULL):TargetAbility(_id,_source, _tc,_cost){
|
|
destinationZone = destZone;
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = tc->getNextCardTarget();
|
|
if(_target){
|
|
Player* p = _target->controller();
|
|
if (p){
|
|
MTGGameZone * fromZone = _target->getCurrentZone();
|
|
MTGGameZone * destZone = MTGGameZone::stringToZone(destinationZone, source);
|
|
p->game->putInZone(_target,fromZone,destZone);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
//Destroyer. TargetAbility
|
|
class ADestroyer:public TargetAbility{
|
|
public:
|
|
int bury;
|
|
ADestroyer(int _id, MTGCardInstance * _source, TargetChooser * _tc = NULL, int _bury = 0, ManaCost * _cost=NULL):TargetAbility(_id,_source, _tc,_cost),bury(_bury){
|
|
if (!tc) tc = NEW CreatureTargetChooser();
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = tc->getNextCardTarget();
|
|
if(_target){
|
|
if (bury){
|
|
_target->controller()->game->putInGraveyard(_target);
|
|
}else{
|
|
game->mLayers->stackLayer()->addPutInGraveyard(_target);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Destroy";
|
|
}
|
|
|
|
};
|
|
|
|
//Destroyer. TargetAbility
|
|
class ABurier:public ADestroyer{
|
|
public:
|
|
ABurier(int _id, MTGCardInstance * _source, TargetChooser * _tc = NULL,ManaCost * _cost=NULL):ADestroyer(_id,_source, _tc,1,_cost){
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Bury";
|
|
}
|
|
};
|
|
|
|
|
|
/*Changes one of the basic abilities of target
|
|
source : spell
|
|
target : spell target (creature)
|
|
modifier : 1 to add the ability, 0 to remove it
|
|
_ability : Id of the ability, as described in mtgdefinitions
|
|
*/
|
|
class ABasicAbilityModifier:public MTGAbility{
|
|
public:
|
|
int modifier;
|
|
int ability;
|
|
int value_before_modification;
|
|
ABasicAbilityModifier(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int _modifier = 1): MTGAbility(_id,_source,_target),modifier(_modifier),ability(_ability){
|
|
value_before_modification = ((MTGCardInstance * )target)->basicAbilities[ability];
|
|
((MTGCardInstance * )target)->basicAbilities[ability]=modifier;
|
|
}
|
|
|
|
int destroy(){
|
|
if (((MTGCardInstance * )target)->basicAbilities[ability] == modifier){
|
|
((MTGCardInstance * )target)->basicAbilities[ability] = value_before_modification;
|
|
return 1;
|
|
}else{
|
|
//BUG !!!
|
|
return 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
//Modifies an ability until end of turn. Needs a target
|
|
class ABasicAbilityModifierUntilEOT:public TargetAbility{
|
|
public:
|
|
MTGCardInstance * mTargets[50];
|
|
int nbTargets;
|
|
int modifier;
|
|
int stateBeforeActivation[50];
|
|
int ability;
|
|
ABasicAbilityModifierUntilEOT(int _id, MTGCardInstance * _source, int _ability, ManaCost * _cost, TargetChooser * _tc = NULL, int _modifier = 1): TargetAbility(_id,_source,_cost),modifier(_modifier), ability(_ability){
|
|
nbTargets = 0;
|
|
tc = _tc;
|
|
if (!tc) tc = NEW CreatureTargetChooser(_source);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
for (int i = 0; i < nbTargets; i++){
|
|
MTGCardInstance * mTarget = mTargets[i];
|
|
if(mTarget && mTarget->basicAbilities[ability]){
|
|
mTarget->basicAbilities[ability] = stateBeforeActivation[i];
|
|
}
|
|
}
|
|
nbTargets = 0;
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
|
|
int resolve(){
|
|
MTGCardInstance * mTarget = tc->getNextCardTarget();
|
|
if (mTarget){
|
|
mTargets[nbTargets] = mTarget;
|
|
stateBeforeActivation[nbTargets] = mTarget->basicAbilities[ability];
|
|
mTarget->basicAbilities[ability] = modifier;
|
|
nbTargets++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return MTGBasicAbilities[ability];
|
|
}
|
|
|
|
|
|
};
|
|
|
|
/*Instants that modifies a basic ability until end of turn */
|
|
class AInstantBasicAbilityModifierUntilEOT: public InstantAbility{
|
|
public:
|
|
int stateBeforeActivation;
|
|
int ability;
|
|
AInstantBasicAbilityModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int value):InstantAbility(_id, _source, _target),ability(_ability){
|
|
stateBeforeActivation = _target->basicAbilities[ability];
|
|
_target->basicAbilities[ability] = value;
|
|
}
|
|
|
|
int destroy(){
|
|
((MTGCardInstance *)target)->basicAbilities[ability] = stateBeforeActivation;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Alteration of Ability until of turn (Aura)
|
|
class ABasicAbilityAuraModifierUntilEOT: public ActivatedAbility{
|
|
public:
|
|
int stateBeforeActivation;
|
|
int ability;
|
|
int value;
|
|
ABasicAbilityAuraModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int _ability, int _value = 1):ActivatedAbility(_id,_source, _cost, 0,0), ability(_ability), value(_value){
|
|
target = _target;
|
|
stateBeforeActivation = _target->basicAbilities[ability];
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->basicAbilities[ability] = stateBeforeActivation;
|
|
}
|
|
ActivatedAbility::Update(dt);
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
stateBeforeActivation = _target->basicAbilities[ability];
|
|
_target->basicAbilities[ability] = value;
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return MTGBasicAbilities[ability];
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*Gives life each time a spell matching CardDescriptor's criteria are match . Optionnal manacost*/
|
|
class ASpellCastLife:public MTGAbility{
|
|
public:
|
|
CardDescriptor trigger;
|
|
ManaCost * cost;
|
|
int life;
|
|
MTGCardInstance * lastUsedOn;
|
|
MTGCardInstance * lastChecked;
|
|
ASpellCastLife(int id, MTGCardInstance * _source, CardDescriptor _trigger, ManaCost * _cost, int _life): MTGAbility(id, _source), trigger(_trigger), cost(_cost), life(_life){
|
|
}
|
|
ASpellCastLife(int id, MTGCardInstance * _source, int color, ManaCost * _cost, int _life): MTGAbility(id, _source), cost(_cost), life(_life){
|
|
trigger.setColor(color);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if (_card == source && game->currentlyActing()->game->inPlay->hasCard(source)){
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost)){
|
|
Interruptible * laststackitem = game->mLayers->stackLayer()->_(-1);
|
|
if (laststackitem && laststackitem->type == ACTION_SPELL){
|
|
Spell * spell = (Spell*)laststackitem;
|
|
if (spell->source != lastUsedOn && trigger.match(spell->source)){
|
|
lastChecked = spell->source;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
game->currentlyActing()->life+=life;
|
|
lastUsedOn = lastChecked;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Allows to untap at any moment for an amount of mana
|
|
class AUnBlocker:public MTGAbility{
|
|
public:
|
|
ManaCost * cost;
|
|
AUnBlocker(int id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost):MTGAbility(id, _source, _target), cost(_cost){
|
|
}
|
|
|
|
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if (_card == target && game->currentlyActing()->game->inPlay->hasCard(source) && (MTGCardInstance *) _card->isTapped()){
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost)){
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
_card->untap();
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//Allows to untap target card once per turn for a manaCost
|
|
class AUntaperOnceDuringTurn:public AUnBlocker{
|
|
public:
|
|
int untappedThisTurn;
|
|
int onlyPlayerTurn;
|
|
AUntaperOnceDuringTurn(int id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int _onlyPlayerTurn = 1):AUnBlocker(id, _source, _target, _cost){
|
|
onlyPlayerTurn = _onlyPlayerTurn;
|
|
untappedThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP) untappedThisTurn = 0;
|
|
AUnBlocker::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if (onlyPlayerTurn && game->currentPlayer!=source->controller()) return 0;
|
|
if (untappedThisTurn) return 0;
|
|
return AUnBlocker::isReactingToClick(card);
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * card){
|
|
untappedThisTurn = 1;
|
|
return AUnBlocker::reactToClick(card);
|
|
}
|
|
};
|
|
|
|
//Alteration of Power and Toughness (enchantments)
|
|
class APowerToughnessModifier: public MTGAbility{
|
|
public:
|
|
int power, toughness;
|
|
APowerToughnessModifier(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness):MTGAbility(id,_source,_target),power(_power),toughness(_toughness){
|
|
_target->power += power;
|
|
_target->addToToughness(toughness);
|
|
}
|
|
|
|
int destroy(){
|
|
((MTGCardInstance *)target)->power -= power;
|
|
((MTGCardInstance *)target)->addToToughness(-toughness);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
// Permanent life alteration evry turn of the target's controller. Useful only for unstable mutation currently
|
|
class APowerToughnessModifierRegularCounter:public MTGAbility{
|
|
public:
|
|
int power, toughness;
|
|
int phase;
|
|
APowerToughnessModifierRegularCounter(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _phase, int _power, int _toughness):MTGAbility(id,_source,_target),power(_power),toughness(_toughness), phase(_phase){
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase !=currentPhase && newPhase==phase && game->currentPlayer==((MTGCardInstance *)target)->controller()){
|
|
((MTGCardInstance *)target)->power += power;
|
|
((MTGCardInstance *)target)->addToToughness(toughness);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//Alteration of Power and Toughness until end of turn (TargetAbility)
|
|
// Gives +n/+m until end of turn to any card that's a target
|
|
class ATargetterPowerToughnessModifierUntilEOT: public TargetAbility{
|
|
public:
|
|
MTGCardInstance * mTargets[50];
|
|
int nbTargets;
|
|
int power, toughness;
|
|
|
|
ATargetterPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, int _power, int _toughness, ManaCost * _cost, TargetChooser * _tc = NULL):TargetAbility(_id,_source,_tc,_cost,0),power(_power),toughness(_toughness){
|
|
if (!tc) tc = NEW CreatureTargetChooser(_source);
|
|
nbTargets = 0;
|
|
}
|
|
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
for (int i = 0; i < nbTargets; i++){
|
|
MTGCardInstance * mTarget = mTargets[i];
|
|
if(mTarget){
|
|
mTarget->power-=power;
|
|
mTarget->addToToughness(-toughness);
|
|
}
|
|
}
|
|
nbTargets = 0;
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
|
|
int resolve(){
|
|
MTGCardInstance * mTarget = tc->getNextCardTarget();
|
|
if (mTarget){
|
|
mTargets[nbTargets] = mTarget;
|
|
mTarget->power+= power;
|
|
mTarget->addToToughness(toughness);
|
|
nbTargets++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//Alteration of Power and Toughness until end of turn (Aura)
|
|
class APowerToughnessModifierUntilEndOfTurn: public MTGAbility{
|
|
public:
|
|
int power, toughness;
|
|
int counters;
|
|
int maxcounters;
|
|
ManaCost * cost;
|
|
APowerToughnessModifierUntilEndOfTurn(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness, ManaCost * _cost, int _maxcounters = 0):MTGAbility(id,_source,_target),power(_power),toughness(_toughness),maxcounters(_maxcounters), cost(_cost){
|
|
counters = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
while(counters){
|
|
((MTGCardInstance *)target)->power -= power;
|
|
((MTGCardInstance *)target)->addToToughness(-toughness);
|
|
counters--;
|
|
}
|
|
}
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if (_card == source && (!maxcounters || counters < maxcounters) && game->currentlyActing()->game->inPlay->hasCard(source)){
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost)){
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
((MTGCardInstance *)target)->power += power;
|
|
((MTGCardInstance *)target)->addToToughness(toughness);
|
|
counters++;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
//Alteration of Power and toughness until end of turn (instant)
|
|
class AInstantPowerToughnessModifierUntilEOT: public InstantAbility{
|
|
public:
|
|
int power, toughness;
|
|
AInstantPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness): InstantAbility(_id, _source, _target), power(_power), toughness(_toughness){
|
|
}
|
|
|
|
int resolve(){
|
|
((MTGCardInstance *)target)->power +=power;
|
|
((MTGCardInstance *)target)->addToToughness(toughness);
|
|
return 1;
|
|
}
|
|
|
|
int destroy(){
|
|
((MTGCardInstance *)target)->power -=power;
|
|
((MTGCardInstance *)target)->addToToughness(-toughness);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
//Untap Blockers with simple Mana Mechanism
|
|
class AUntapManaBlocker: public Blocker{
|
|
public:
|
|
AUntapManaBlocker(int id, MTGCardInstance * card, ManaCost * _cost):Blocker(id, card, _cost){
|
|
}
|
|
|
|
AUntapManaBlocker(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost):Blocker(id, card,_target, _cost){
|
|
}
|
|
};
|
|
|
|
/* Spell Counters (Enchantment) for a mana cost */
|
|
//LifeForce
|
|
class ASpellCounterEnchantment:public TargetAbility{
|
|
public:
|
|
|
|
ASpellCounterEnchantment(int _id, MTGCardInstance * _source, ManaCost * _cost,int color = -1, int _tap = 0):TargetAbility(_id,_source,NEW SpellTargetChooser(_source,color),_cost,0,_tap){
|
|
}
|
|
|
|
int resolve(){
|
|
Spell * _target = tc->getNextSpellTarget();
|
|
if(_target){
|
|
game->mLayers->stackLayer()->Fizzle(_target);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
};
|
|
|
|
/*Mana Producers (lands)
|
|
//These have a reactToClick function, and therefore two manaProducers on the same card conflict with each other
|
|
//That means the player has to choose one. although that is perfect for cards such as birds of paradise or badlands,
|
|
other solutions need to be provided for abilities that add mana (ex: mana flare)
|
|
*/
|
|
/*
|
|
Currently the mana is added to the pool AFTER the animation
|
|
This is VERY BAD, since we don't have any control on the duration of the animation. This can lead to bugs with
|
|
the AI, who is expecting to have the mana in its manapool right after clicking the land card !!!
|
|
The sum of "dt" has to be 0.25 for the mana to be in the manapool currently
|
|
*/
|
|
|
|
class AManaProducer: public MTGAbility{
|
|
protected:
|
|
static int currentlyTapping;
|
|
ManaCost * cost;
|
|
ManaCost * output;
|
|
string menutext;
|
|
float x0,y0,x1,y1,x,y;
|
|
float animation;
|
|
Player * controller;
|
|
|
|
hgeParticleSystem * mParticleSys;
|
|
public:
|
|
AManaProducer(int id, MTGCardInstance * card, ManaCost * _output, ManaCost * _cost = NULL ):MTGAbility(id, card){
|
|
LOG("==Creating ManaProducer Object");
|
|
cost = _cost;
|
|
output=_output;
|
|
x1 = 10;
|
|
y1 = 220;
|
|
Player * player = card->controller();
|
|
if (player == game->players[1]) y1 = 100;
|
|
x = x1;
|
|
y = y1;
|
|
animation = 0.f;
|
|
mParticleSys = NULL;
|
|
menutext = "";
|
|
|
|
int landColor = output->getMainColor();
|
|
|
|
if (landColor == MTG_COLOR_RED){
|
|
mParticleSys = NEW hgeParticleSystem("graphics/manared.psi",GameApp::CommonRes->GetQuad("particles"));
|
|
}else if (landColor == MTG_COLOR_BLUE){
|
|
mParticleSys = NEW hgeParticleSystem("graphics/manablue.psi", GameApp::CommonRes->GetQuad("particles"));
|
|
}else if (landColor == MTG_COLOR_GREEN){
|
|
mParticleSys = NEW hgeParticleSystem("graphics/managreen.psi", GameApp::CommonRes->GetQuad("particles"));
|
|
}else if (landColor == MTG_COLOR_BLACK){
|
|
mParticleSys = NEW hgeParticleSystem("graphics/manablack.psi", GameApp::CommonRes->GetQuad("particles"));
|
|
}else if (landColor == MTG_COLOR_WHITE){
|
|
mParticleSys = NEW hgeParticleSystem("graphics/manawhite.psi", GameApp::CommonRes->GetQuad("particles"));
|
|
}else{
|
|
mParticleSys = NEW hgeParticleSystem("graphics/mana.psi", GameApp::CommonRes->GetQuad("particles"));
|
|
}
|
|
|
|
|
|
|
|
LOG("==ManaProducer Object Creation successful !");
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (mParticleSys) mParticleSys->Update(dt);
|
|
if (animation){
|
|
x = (1.f - animation)*x1 + animation * x0;
|
|
y = (1.f - animation)*y1 + animation * y0;
|
|
if (mParticleSys) mParticleSys->MoveTo(x, y);
|
|
if (mParticleSys && animation == 1.f) mParticleSys->Fire();
|
|
animation -= 4 *dt;
|
|
if (!animation) animation = -1;
|
|
if (animation < 0){
|
|
animation = 0;
|
|
currentlyTapping--;
|
|
controller->getManaPool()->add(output);
|
|
if (mParticleSys) mParticleSys->Stop();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void Render(){
|
|
JRenderer * renderer = JRenderer::GetInstance();
|
|
if (animation){
|
|
renderer->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE);
|
|
if (mParticleSys) mParticleSys->Render();
|
|
// set normal blending
|
|
renderer->SetTexBlend(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
int result = 0;
|
|
if (_card == source && !source->isTapped() && game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType("land") || !source->hasSummoningSickness()) ){
|
|
if (!cost || game->currentlyActing()->getManaPool()->canAfford(cost)) result = 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
source->tapped = 1;
|
|
currentlyTapping++;
|
|
if (cost) GameObserver::GetInstance()->currentlyActing()->getManaPool()->pay(cost);
|
|
animation = 1.f;
|
|
CardGui * cardg = game->mLayers->playLayer()->getByCard(source);
|
|
if (cardg){
|
|
x0 = cardg->x + 15;
|
|
y0 = cardg->y + 20;
|
|
}
|
|
controller = source->controller();
|
|
|
|
if (GameOptions::GetInstance()->values[OPTIONS_SFXVOLUME] > 0 && currentlyTapping < 3){
|
|
JSample * sample = SampleCache::GetInstance()->getSample("sound/sfx/mana.wav");
|
|
if (sample) JSoundSystem::GetInstance()->PlaySample(sample);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
if (menutext.size())return menutext.c_str();
|
|
menutext = "Add ";
|
|
char buffer[128];
|
|
int alreadyHasOne = 0;
|
|
for (int i= 0; i < 6; i++){
|
|
int value = output->getCost(i);
|
|
if (value){
|
|
if (alreadyHasOne) menutext.append(",");
|
|
sprintf(buffer, "%i ", value);
|
|
menutext.append(buffer);
|
|
switch (i){
|
|
case MTG_COLOR_RED:
|
|
menutext.append("red");
|
|
break;
|
|
case MTG_COLOR_BLUE:
|
|
menutext.append("blue");
|
|
break;
|
|
case MTG_COLOR_GREEN:
|
|
menutext.append("green");
|
|
break;
|
|
case MTG_COLOR_WHITE:
|
|
menutext.append("white");
|
|
break;
|
|
case MTG_COLOR_BLACK:
|
|
menutext.append("black");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
alreadyHasOne = 1;
|
|
}
|
|
}
|
|
menutext.append(" mana");
|
|
return menutext.c_str();
|
|
}
|
|
|
|
int testDestroy(){
|
|
if (animation >0) return 0;
|
|
return MTGAbility::testDestroy();
|
|
}
|
|
|
|
~AManaProducer(){
|
|
LOG("==Destroying ManaProducer Object");
|
|
SAFE_DELETE(cost);
|
|
SAFE_DELETE(output);
|
|
SAFE_DELETE(mParticleSys);
|
|
LOG("==Destroying ManaProducer Object Successful!");
|
|
}
|
|
|
|
};
|
|
int AManaProducer::currentlyTapping = 0;
|
|
|
|
/* Lifelink Ability */
|
|
class ALifeLink:public MTGAbility{
|
|
public:
|
|
int nbdamagesthisturn;
|
|
Damage * lastDamage;
|
|
ALifeLink(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
nbdamagesthisturn = 0;
|
|
lastDamage = NULL;
|
|
}
|
|
|
|
void Update(float dt){
|
|
ActionStack * as = game->mLayers->stackLayer();
|
|
int totaldamages = as->count(ACTION_DAMAGE,RESOLVED_OK);
|
|
if ( totaldamages > nbdamagesthisturn){
|
|
Damage * damage = ((Damage * )as->getNext(lastDamage,ACTION_DAMAGE, RESOLVED_OK));
|
|
while(damage){
|
|
lastDamage = damage;
|
|
if (damage->source == source){
|
|
source->controller()->life+= damage->damage;
|
|
}
|
|
damage = ((Damage * )as->getNext(lastDamage,ACTION_DAMAGE, RESOLVED_OK));
|
|
}
|
|
}else if (totaldamages ==0){
|
|
lastDamage = NULL;
|
|
}
|
|
nbdamagesthisturn = totaldamages;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//Circle of Protections
|
|
class ACircleOfProtection: public TargetAbility{
|
|
public:
|
|
ACircleOfProtection(int _id, MTGCardInstance * source, int _color):TargetAbility(_id,source,NEW DamageTargetChooser(source,_color),NEW ManaCost(),0,0){
|
|
cost->add(MTG_COLOR_ARTIFACT,1);
|
|
}
|
|
|
|
int resolve(){
|
|
Damage * damage = tc->getNextDamageTarget();
|
|
if (!damage) return 0;
|
|
game->mLayers->stackLayer()->Fizzle(damage);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//Basic regeneration mechanism for a Mana cost
|
|
class AStandardRegenerate:public ActivatedAbility{
|
|
public:
|
|
AStandardRegenerate(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost):ActivatedAbility(_id,_source,_cost,0,0){
|
|
target = _target;
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
_target->regenerate();
|
|
PutInGraveyard * action = ((PutInGraveyard *) game->mLayers->stackLayer()->getNext(NULL,ACTION_PUTINGRAVEYARD,NOT_RESOLVED));
|
|
while(action){
|
|
#if defined (WIN32) || defined (LINUX)
|
|
OutputDebugString("Fizzling due to regenerate! \n");
|
|
#endif
|
|
if (action->card == _target){
|
|
game->mLayers->stackLayer()->Fizzle(action);
|
|
}
|
|
action = ((PutInGraveyard *) game->mLayers->stackLayer()->getNext(action,ACTION_PUTINGRAVEYARD,NOT_RESOLVED));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Regenerate";
|
|
}
|
|
|
|
};
|
|
|
|
/*Gives protection to a target */
|
|
class AProtectionFrom:public MTGAbility{
|
|
public:
|
|
CardDescriptor * cd;
|
|
void initProtection(){
|
|
((MTGCardInstance *)target)->addProtection(cd);
|
|
}
|
|
|
|
AProtectionFrom(int _id, MTGCardInstance * _source, MTGCardInstance * _target, CardDescriptor * _cd):MTGAbility(_id, _source, _target),cd(_cd){
|
|
initProtection();
|
|
}
|
|
AProtectionFrom(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int color):MTGAbility(_id, _source, _target){
|
|
cd = NEW CardDescriptor();
|
|
cd->colors[color] = 1;
|
|
initProtection();
|
|
}
|
|
|
|
int destroy(){
|
|
((MTGCardInstance *)target)->removeProtection(cd);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Aura Enchantments that provide controller of target life or damages at a given phase of their turn
|
|
class ARegularLifeModifierAura:public MTGAbility{
|
|
public:
|
|
int life;
|
|
int phase;
|
|
int onlyIfTargetTapped;
|
|
ARegularLifeModifierAura(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _phase, int _life, int _onlyIfTargetTapped=0):MTGAbility(id,_source,_target),life(_life), phase(_phase),onlyIfTargetTapped(_onlyIfTargetTapped){
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase !=currentPhase && newPhase==phase && game->currentPlayer==((MTGCardInstance *)target)->controller()){
|
|
if (!onlyIfTargetTapped || ((MTGCardInstance *)target)->tapped){
|
|
if (life > 0){
|
|
game->currentPlayer->life+=life;
|
|
}else{
|
|
game->mLayers->stackLayer()->addDamage(source, game->currentPlayer, -life);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//ExaltedAbility (Shards of Alara)
|
|
class AExalted:public ListMaintainerAbility{
|
|
public:
|
|
int power, toughness;
|
|
MTGCardInstance * luckyWinner;
|
|
AExalted(int _id, MTGCardInstance * _source, int _power = 1, int _toughness = 1):ListMaintainerAbility(_id, _source),power(_power),toughness(_toughness){
|
|
luckyWinner = NULL;
|
|
}
|
|
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (card->isAttacker() && game->currentPlayer == source->controller()) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
if(cards.size() == 1){
|
|
luckyWinner = cards.begin()->first;
|
|
luckyWinner->addToToughness(toughness);
|
|
luckyWinner->power+=power;
|
|
}else if (cards.size() == 2){
|
|
luckyWinner->addToToughness(-toughness);
|
|
luckyWinner->power-=power;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
if(cards.size() == 1){
|
|
luckyWinner = cards.begin()->first;
|
|
luckyWinner->addToToughness(toughness);
|
|
luckyWinner->power+=power;
|
|
}else if (cards.size() == 0){
|
|
luckyWinner->addToToughness(-toughness);
|
|
luckyWinner->power-=power;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//ExaltedAbility for basic abilities (Shards of Alara)
|
|
class AExaltedAbility:public ListMaintainerAbility{
|
|
public:
|
|
int ability;
|
|
MTGCardInstance * luckyWinner;
|
|
AExaltedAbility(int _id, MTGCardInstance * _source, int _ability):ListMaintainerAbility(_id, _source),ability(_ability){
|
|
luckyWinner = NULL;
|
|
}
|
|
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (card->isAttacker() && game->currentPlayer == source->controller()) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
luckyWinner = cards.begin()->first;
|
|
if(cards.size() == 1){
|
|
luckyWinner->basicAbilities[ability]+=1;
|
|
}else if (cards.size() == 2){
|
|
luckyWinner->basicAbilities[ability]-=1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
if(cards.size() == 1){
|
|
luckyWinner->basicAbilities[ability]+=1;
|
|
}else if (cards.size() == 0){
|
|
luckyWinner->basicAbilities[ability]-=1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//Converts lands to creatures (Kormus bell, Living lands)
|
|
class AConvertLandToCreatures:public ListMaintainerAbility{
|
|
public:
|
|
int type;
|
|
int power, toughness;
|
|
AConvertLandToCreatures(int _id, MTGCardInstance * _source, const char * _type, int _power = 1, int _toughness = 1):ListMaintainerAbility(_id, _source),power(_power),toughness(_toughness){
|
|
type = Subtypes::subtypesList->Add(_type);
|
|
}
|
|
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (card->hasType(type)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
card->power = 1;
|
|
card->setToughness(1);
|
|
card->setSubtype("creature");
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
card->removeType("creature");
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Lords (Merfolk lord...) give power and toughness to OTHER creatures of their type, they can give them special abilities, regeneration
|
|
class ALord:public ListMaintainerAbility{
|
|
public:
|
|
TargetChooser * tc;
|
|
int power, toughness;
|
|
int ability;
|
|
ManaCost * regenCost;
|
|
int includeSelf;
|
|
map<MTGCardInstance *, MTGAbility *> regenerations;
|
|
ALord(int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, int _power = 0 , int _toughness = 0, int _ability = -1, ManaCost * _regenCost = NULL):ListMaintainerAbility(_id,card){
|
|
tc = _tc;
|
|
tc->source = NULL;
|
|
includeSelf = _includeSelf;
|
|
power = _power;
|
|
toughness = _toughness;
|
|
ability = _ability;
|
|
regenCost = _regenCost;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if ( (includeSelf || card!=source) && tc->canTarget(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
card->power += power;
|
|
card->addToToughness(toughness);
|
|
if (ability != -1) card->basicAbilities[ability] +=1;
|
|
if (regenCost){
|
|
ManaCost * _regenCost = NEW ManaCost(regenCost);
|
|
AStandardRegenerate * regen = NEW AStandardRegenerate(0, card, card, _regenCost);
|
|
regenerations[card] = regen;
|
|
game->addObserver(regen);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
card->power -= power;
|
|
card->addToToughness(-toughness);
|
|
if (ability != -1 && card->basicAbilities[ability]) card->basicAbilities[ability] -=1;
|
|
if (regenCost){
|
|
if(regenerations.find(card) != regenerations.end()){
|
|
if (game->isInPlay(card)) game->removeObserver(regenerations[card]);
|
|
regenerations.erase(card);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Standard Damager, can choose a NEW target each time the price is paid */
|
|
class ADamager:public TargetAbility{
|
|
public:
|
|
int damage;
|
|
ADamager(int id, MTGCardInstance * card, ManaCost * _cost, int _damage, TargetChooser * _tc = NULL, int _tap = 1):TargetAbility(id,card, _tc, _cost,0,_tap),damage(_damage){
|
|
if (!tc) tc = NEW DamageableTargetChooser(card);
|
|
}
|
|
int resolve(){
|
|
Damageable * _target = tc->getNextDamageableTarget();
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,_target, damage);
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Damage target";
|
|
}
|
|
|
|
|
|
};
|
|
|
|
/* Can tap a target for a cost */
|
|
class ATapper:public TargetAbility{
|
|
public:
|
|
int damage;
|
|
ATapper(int id, MTGCardInstance * card, ManaCost * _cost, TargetChooser * _chooser):TargetAbility(id,card, _chooser, _cost){
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = tc->getNextCardTarget();
|
|
if (_target){
|
|
_target->tapped = true;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Tap target";
|
|
}
|
|
|
|
};
|
|
|
|
//Ability to untap a target
|
|
class AUntaper:public TargetAbility{
|
|
public:
|
|
AUntaper(int _id, MTGCardInstance * card, ManaCost * _manacost, TargetChooser * _tc):TargetAbility(_id,card,_tc,_manacost){
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = tc->getNextCardTarget();
|
|
if (_target){
|
|
_target->tapped = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText(){
|
|
return "Untap target";
|
|
}
|
|
|
|
};
|
|
|
|
|
|
// Add life of gives damage if a given zone has more or less than [condition] cards at the beginning of [phase]
|
|
//Ex : the rack, ivory tower...
|
|
class ALifeZoneLink:public MTGAbility{
|
|
public:
|
|
int phase;
|
|
int condition;
|
|
int life;
|
|
int controller;
|
|
int nbcards;
|
|
MTGGameZone * zone;
|
|
ALifeZoneLink(int _id ,MTGCardInstance * card, int _phase, int _condition, int _life = -1, int _controller = 0, MTGGameZone * _zone = NULL):MTGAbility(_id, card){
|
|
phase = _phase;
|
|
condition = _condition;
|
|
controller = _controller;
|
|
life = _life;
|
|
zone = _zone;
|
|
if (zone == NULL){
|
|
if (controller){
|
|
zone = game->currentPlayer->game->hand;
|
|
}else{
|
|
zone = game->opponent()->game->hand;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == phase){
|
|
if ((controller && game->currentPlayer == source->controller()) ||(!controller && game->currentPlayer != source->controller()) ){
|
|
if ((condition < 0 && zone->nb_cards < - condition) ||(condition >0 && zone->nb_cards > condition)){
|
|
int diff = zone->nb_cards - condition;
|
|
if (condition < 0) diff = - condition - zone->nb_cards;
|
|
if (life > 0){
|
|
game->currentPlayer->life+=life*diff;
|
|
}else{
|
|
game->mLayers->stackLayer()->addDamage(source,game->currentPlayer,-life*diff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//Creatures that cannot attack if opponent has not a given type of land, and die if controller has not this type of land
|
|
//Ex : pirate ship...
|
|
class AStrongLandLinkCreature: public MTGAbility{
|
|
public:
|
|
char land[20];
|
|
AStrongLandLinkCreature(int _id, MTGCardInstance * _source, const char * _land):MTGAbility(_id, _source){
|
|
sprintf(land,"%s",_land);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (source->isAttacker()){
|
|
if (!game->opponent()->game->inPlay->hasType(land)){
|
|
source->attacker=0;
|
|
source->tapped = 0;
|
|
//TODO Improve, there can be race conditions here
|
|
}
|
|
}
|
|
Player * player = source->controller();
|
|
if(!player->game->inPlay->hasType(land)){
|
|
player->game->putInGraveyard(source);
|
|
}
|
|
}
|
|
};
|
|
|
|
//Steal control of a target
|
|
class AControlStealAura: public MTGAbility{
|
|
public:
|
|
Player * originalController;
|
|
AControlStealAura(int _id , MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source, _target){
|
|
originalController = _target->controller();
|
|
_target->changeController(game->currentlyActing());
|
|
}
|
|
|
|
int destroy(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
Player * p = _target->controller();
|
|
if (p && p->game->inPlay->hasCard(_target)){ //if the target is still in game -> spell was destroyed
|
|
_target->changeController(originalController);
|
|
}
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
//Same as StealControl Aura ???? Obsolete ?
|
|
class ATakeControlAura:public MTGAbility{
|
|
public:
|
|
Player * previousController;
|
|
ATakeControlAura(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source,_target){
|
|
previousController = _target->controller();
|
|
previousController->game->putInZone(_target, previousController->game->inPlay, source->controller()->game->inPlay);
|
|
|
|
}
|
|
|
|
int destroy(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
Player * p = _target->controller();
|
|
if (p && p->game->inPlay->hasCard(_target)){
|
|
p->game->putInZone(_target, p->game->inPlay, previousController->game->inPlay);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Creatures that kill their blockers
|
|
//Ex : Cockatrice
|
|
class AOldSchoolDeathtouch:public MTGAbility{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
AOldSchoolDeathtouch(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
nbOpponents = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase){
|
|
if( newPhase == MTG_PHASE_COMBATDAMAGE){
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent && !opponent->hasSubtype("wall")){
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents ++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
}else if (newPhase == MTG_PHASE_COMBATEND){
|
|
for (int i = 0; i < nbOpponents ; i++){
|
|
game->mLayers->stackLayer()->addPutInGraveyard(opponents[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int testDestroy(){
|
|
if(!game->isInPlay(source) && currentPhase != MTG_PHASE_UNTAP){
|
|
return 0;
|
|
}else{
|
|
return MTGAbility::testDestroy();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//Converts a card to a creature (Aura)
|
|
class AConvertToCreatureAura:public MTGAbility{
|
|
public:
|
|
AConvertToCreatureAura(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness):MTGAbility(_id, _source, _target){
|
|
_target->setSubtype("creature");
|
|
_target->power = _power;
|
|
_target->toughness = _toughness;
|
|
_target->life = _toughness;
|
|
//_target->afterDamage();
|
|
_target->doDamageTest = 1;
|
|
}
|
|
|
|
int destroy(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->removeType("creature");
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
/*
|
|
Specific Classes
|
|
*/
|
|
|
|
// 1092 Specific to Aladdin's Lamp
|
|
class AAladdinsLamp: public TargetAbility{
|
|
public:
|
|
CardDisplay cd;
|
|
int nbcards;
|
|
int init;
|
|
AAladdinsLamp(int _id, MTGCardInstance * card):TargetAbility(_id,card){
|
|
cost = NEW ManaCost();
|
|
cost->x();
|
|
cd = CardDisplay(1,game,SCREEN_WIDTH/2, SCREEN_HEIGHT/2,NULL);
|
|
MTGGameZone * zones[] = {game->currentPlayer->game->library};
|
|
tc = NEW TargetZoneChooser(zones,1,source);
|
|
nbcards = 0;
|
|
init = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (waitingForAnswer){
|
|
if (!init){
|
|
cd.resetObjects();
|
|
int wished = game->currentlyActing()->getManaPool()->getConvertedCost();
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
nbcards = 0;
|
|
MTGGameZone * library = game->currentlyActing()->game->library;
|
|
while (nbcards < wished && nbcards < library->nb_cards){
|
|
cd.AddCard(library->cards[library->nb_cards - 1 - nbcards]);
|
|
nbcards++;
|
|
}
|
|
init = 1;
|
|
}
|
|
cd.Update(dt);
|
|
// cd.CheckUserInput(dt);
|
|
}
|
|
}
|
|
|
|
void Render(float dt){
|
|
if (waitingForAnswer){
|
|
cd.Render();
|
|
}
|
|
}
|
|
|
|
|
|
int fireAbility(){
|
|
source->tapped = 1;
|
|
MTGLibrary * library = game->currentlyActing()->game->library;
|
|
library->removeCard(tc->getNextCardTarget());
|
|
library->shuffleTopToBottom(nbcards - 1 );
|
|
library->addCard(tc->getNextCardTarget());
|
|
init = 0;
|
|
return 1;
|
|
}
|
|
|
|
int resolve(){return 1;};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//Ankh of Mishra
|
|
class AAnkhOfMishra: public MTGAbility{
|
|
public:
|
|
int playerLands[2];
|
|
AAnkhOfMishra(int id, MTGCardInstance * _source):MTGAbility(id, _source){
|
|
for (int i=0; i< 2; i++){
|
|
playerLands[i] = GameObserver::GetInstance()->players[i]->game->inPlay->countByType("land");
|
|
}
|
|
}
|
|
|
|
void Update(float dt){
|
|
for (int i=0; i < 2; i++){
|
|
int lands = GameObserver::GetInstance()->players[i]->game->inPlay->countByType("land");
|
|
while (lands > playerLands[i]){
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,GameObserver::GetInstance()->players[i], 2);
|
|
playerLands[i]++;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
// Armageddon Clock
|
|
class AArmageddonClock:public MTGAbility{
|
|
public:
|
|
int counters;
|
|
ManaCost cost;
|
|
AArmageddonClock(int id, MTGCardInstance * _source):MTGAbility(id, _source){
|
|
counters = 0;
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 4};
|
|
cost = ManaCost(_cost,1);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase){
|
|
if (newPhase == MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source)){
|
|
counters ++;
|
|
}else if (newPhase == MTG_PHASE_DRAW && counters > 0 && game->currentPlayer->game->inPlay->hasCard(source)){ //End of upkeep = beginning of draw
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,GameObserver::GetInstance()->players[0], counters);
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,GameObserver::GetInstance()->players[1], counters);
|
|
}
|
|
}
|
|
}
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if (counters > 0 && _card == source && currentPhase == MTG_PHASE_UPKEEP){
|
|
if (game->currentlyActing()->getManaPool()->canAfford( & cost)){
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(& cost);
|
|
counters --;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
//Black Vise
|
|
class ABlackVise: public MTGAbility{
|
|
public:
|
|
int nbcards;
|
|
ABlackVise(int id, MTGCardInstance * _source):MTGAbility(id, _source){
|
|
nbcards = game->opponent()->game->hand->nb_cards;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase == MTG_PHASE_UPKEEP && GameObserver::GetInstance()->opponent()->game->inPlay->hasCard(source)){
|
|
nbcards = game->currentPlayer->game->hand->nb_cards;
|
|
}
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_DRAW && GameObserver::GetInstance()->opponent()->game->inPlay->hasCard(source)){
|
|
if ( nbcards > 4) game->mLayers->stackLayer()->addDamage(source,game->currentPlayer, nbcards - 4);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//Channel
|
|
class AChannel:public ActivatedAbility{
|
|
public:
|
|
|
|
AChannel(int _id, MTGCardInstance * card):ActivatedAbility(_id, card,0,0,0){
|
|
}
|
|
|
|
int isReactingToClick(PlayGuiObject * object){
|
|
if (object->type == GUI_AVATAR){
|
|
Player * player = ((GuiAvatar *)object)->player;
|
|
if (player == source->controller()) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
source->controller()->life--;
|
|
source->controller()->getManaPool()->add(MTG_COLOR_ARTIFACT, 1);
|
|
return 1;
|
|
}
|
|
|
|
int testDestroy(){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP) return 1;
|
|
currentPhase = newPhase;
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
// Clockwork Beast
|
|
class AClockworkBeast:public MTGAbility{
|
|
public:
|
|
int counters;
|
|
ManaCost cost;
|
|
AClockworkBeast(int id, MTGCardInstance * _source):MTGAbility(id, _source){
|
|
counters = 7;
|
|
((MTGCardInstance *)target)->power+=7;
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 1};
|
|
cost = ManaCost(_cost,1);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_COMBATEND){
|
|
if (((MTGCardInstance *)source)->isAttacker() || ((MTGCardInstance *)source)->isDefenser()){
|
|
counters--;
|
|
((MTGCardInstance *)target)->power-=1;
|
|
}
|
|
}
|
|
}
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if (counters < 7 && _card == source && currentPhase == MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source)){
|
|
if (game->currentlyActing()->getManaPool()->canAfford( & cost)){
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(& cost);
|
|
counters ++;
|
|
((MTGCardInstance *)target)->power++;
|
|
((MTGCardInstance *)target)->tapped = 1;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//1102: Conservator
|
|
class AConservator: public MTGAbility{
|
|
public:
|
|
int canprevent;
|
|
ManaCost cost;
|
|
AConservator(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
canprevent = 0;
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 2};
|
|
cost = ManaCost(_cost, 1);
|
|
}
|
|
|
|
int alterDamage(Damage * damage){
|
|
if (canprevent && damage->target == source->controller()){
|
|
if (damage->damage >= canprevent){
|
|
damage->damage-=canprevent;
|
|
canprevent = 0;
|
|
}else{
|
|
canprevent-=damage->damage;
|
|
damage->damage = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
int alterDamage(){
|
|
if (canprevent){
|
|
ActionStack * stack = game->mLayers->stackLayer();
|
|
for (int i = stack->mCount-1; i>=0; i--){
|
|
if (!canprevent) return 1;
|
|
Interruptible * current = ((Interruptible *)stack->mObjects[i]);
|
|
if (current->type == ACTION_DAMAGE && current->state==NOT_RESOLVED){
|
|
Damage * damage = (Damage *)current;
|
|
alterDamage(damage);
|
|
}else if (current->type == ACTION_DAMAGES && current->state == NOT_RESOLVED){
|
|
DamageStack * damages = (DamageStack *)current;
|
|
for (int j = damages->mCount-1;j >=0; j--){
|
|
alterDamage(((Damage *)damages->mObjects[j]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void Update(float dt){
|
|
alterDamage();
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if ( _card == source && game->currentlyActing()->game->inPlay->hasCard(source) && !_card->isTapped()){
|
|
if (game->currentlyActing()->getManaPool()->canAfford( & cost)){
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(& cost);
|
|
source->tapped = 1;
|
|
canprevent = 2;
|
|
alterDamage();
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//Creature bond
|
|
class ACreatureBond:public TriggeredAbility{
|
|
public:
|
|
int mTriggered;
|
|
int resolved;
|
|
ACreatureBond(int _id, MTGCardInstance * _source, MTGCardInstance * _target):TriggeredAbility(_id,_source,_target){
|
|
mTriggered = 0;
|
|
resolved = 0;
|
|
}
|
|
|
|
int trigger(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
for (int i = 0; i < 2; i++){
|
|
if (!mTriggered && game->players[i]->game->graveyard->hasCard(_target)){
|
|
mTriggered = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
game->mLayers->stackLayer()->addDamage(source,_target->controller(),_target->toughness);
|
|
resolved = 1;
|
|
return 1;
|
|
}
|
|
|
|
int testDestroy(){
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
if(_target->controller()->game->graveyard->hasCard(_target) && !resolved){
|
|
return 0;
|
|
}else{
|
|
return TriggeredAbility::testDestroy();
|
|
}
|
|
}
|
|
};
|
|
|
|
//1105: Dingus Egg
|
|
class ADingusEgg: public MTGAbility{
|
|
public:
|
|
int playerLands[2];
|
|
ADingusEgg(int id, MTGCardInstance * _source):MTGAbility(id, _source){
|
|
for (int i=0; i< 2; i++){
|
|
playerLands[i] = GameObserver::GetInstance()->players[i]->game->inPlay->countByType("land");
|
|
}
|
|
}
|
|
|
|
void Update(float dt){
|
|
for (int i=0; i < 2; i++){
|
|
int lands = GameObserver::GetInstance()->players[i]->game->inPlay->countByType("land");
|
|
while (lands < playerLands[i]){
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,GameObserver::GetInstance()->players[i], 2);
|
|
playerLands[i]--;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//1106 DisruptingScepter
|
|
class ADisruptingScepter:public TargetAbility{
|
|
public:
|
|
ADisruptingScepter(int id, MTGCardInstance * _source):TargetAbility(id,_source){
|
|
MTGGameZone * zones[] = {GameObserver::GetInstance()->opponent()->game->hand};
|
|
tc = NEW TargetZoneChooser(zones,1,_source);
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 3};
|
|
cost = NEW ManaCost(_cost,1);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (game->opponent()->isAI()){
|
|
if(waitingForAnswer){
|
|
MTGCardInstance * card = ((AIPlayer *)game->opponent())->chooseCard(tc, source);
|
|
if (card) tc->toggleTarget(card);
|
|
if (!card || tc->targetsReadyCheck() == TARGET_OK) waitingForAnswer = 0;
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}else{
|
|
TargetAbility::Update(dt);
|
|
}
|
|
}
|
|
|
|
int resolve(){
|
|
game->opponent()->game->putInGraveyard(tc->getNextCardTarget());
|
|
return 1;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
//1108 Ebony Horse
|
|
class AEbonyHorse:public TargetAbility{
|
|
public:
|
|
|
|
AEbonyHorse(int _id, MTGCardInstance * _source):TargetAbility(_id,_source, NEW CreatureTargetChooser()){
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 2};
|
|
cost = NEW ManaCost(_cost,1);
|
|
}
|
|
|
|
int resolve(){
|
|
tc->getNextCardTarget()->attacker = 0;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1345 Farmstead
|
|
class AFarmstead:public ActivatedAbility{
|
|
public:
|
|
AFarmstead(int _id, MTGCardInstance * source, MTGCardInstance * _target):ActivatedAbility(_id, source,0,1,0){
|
|
int _cost[] = {MTG_COLOR_WHITE, 2};
|
|
cost = NEW ManaCost(_cost,1);
|
|
target = _target;
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if (!ActivatedAbility::isReactingToClick(card)) return 0;
|
|
if (currentPhase == MTG_PHASE_UPKEEP) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
source->controller()->life++;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1110 Glasses of Urza
|
|
class AGlassesOfUrza:public MTGAbility{
|
|
public:
|
|
CardDisplay * display;
|
|
bool isActive;
|
|
AGlassesOfUrza(int _id, MTGCardInstance * _source):MTGAbility(_id, _source),isActive(false){
|
|
display = NEW CardDisplay(0, game,SCREEN_WIDTH/2, SCREEN_HEIGHT/2,NULL);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if(isActive){
|
|
display->Update(dt);
|
|
}
|
|
}
|
|
|
|
bool CheckUserInput(u32 key){
|
|
if (isActive){
|
|
if (display->CheckUserInput(key)) return true;
|
|
if (PSP_CTRL_CROSS == key){
|
|
isActive = false;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Render(float dt){
|
|
if (isActive){
|
|
display->Render();
|
|
}
|
|
|
|
}
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if ( card == source){
|
|
if (game->currentlyActing()->game->isInPlay(card) && !source->isTapped()){
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * card){
|
|
if (!isReactingToClick(card)) return 0;
|
|
source->tapped = 1;
|
|
isActive = true;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1112 Howling Mine
|
|
class AHowlingMine:public MTGAbility{
|
|
public:
|
|
AHowlingMine(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_DRAW && !source->tapped){
|
|
game->mLayers->stackLayer()->addDraw(game->currentPlayer);
|
|
}
|
|
}
|
|
};
|
|
|
|
//1119 Jayemdae Tome
|
|
class AJayemdaeTome:public ActivatedAbility{
|
|
public:
|
|
AJayemdaeTome(int _id, MTGCardInstance * card):ActivatedAbility(_id, card){
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 4};
|
|
cost = NEW ManaCost(_cost,1);
|
|
}
|
|
|
|
int resolve(){
|
|
game->mLayers->stackLayer()->addDraw(source->controller());
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
//Living Artifact
|
|
class ALivingArtifact:public MTGAbility{
|
|
public:
|
|
int usedThisTurn;
|
|
int counters;
|
|
Damage * latest;
|
|
ALivingArtifact(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id,_source,_target){
|
|
usedThisTurn = 0;
|
|
counters = 0;
|
|
latest = NULL;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP) usedThisTurn = 0;
|
|
Damage * damage = ((Damage *)game->mLayers->stackLayer()->getNext(latest,ACTION_DAMAGE,RESOLVED_OK));
|
|
while (damage){
|
|
if (damage->target == source->controller()){
|
|
counters += damage->damage;
|
|
}
|
|
latest = damage;
|
|
damage = ((Damage *)game->mLayers->stackLayer()->getNext(damage,ACTION_DAMAGE,RESOLVED_OK));
|
|
}
|
|
}
|
|
|
|
int isReactingtoclick(MTGCardInstance * card){
|
|
if (currentPhase == MTG_PHASE_UPKEEP && card == source && game->currentPlayer == source->controller() && counters && !usedThisTurn){
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * card){
|
|
source->controller()->life+=1;
|
|
counters--;
|
|
usedThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Lord of the Pit
|
|
class ALordOfThePit: public TargetAbility{
|
|
public:
|
|
int paidThisTurn;
|
|
ALordOfThePit(int _id, MTGCardInstance * source):TargetAbility(_id, source, NEW CreatureTargetChooser(),0,1,0){
|
|
paidThisTurn = 1;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && source->controller() == game->currentPlayer){
|
|
if (newPhase == MTG_PHASE_UNTAP){
|
|
paidThisTurn = 0;
|
|
}else if( newPhase == MTG_PHASE_UPKEEP + 1 && !paidThisTurn){
|
|
game->mLayers->stackLayer()->addDamage(source,source->controller(), 7);
|
|
}
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if (currentPhase != MTG_PHASE_UPKEEP || paidThisTurn) return 0;
|
|
return TargetAbility::isReactingToClick(card);
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * card = tc->getNextCardTarget();
|
|
if (card && card != source && card->controller() == source->controller()){
|
|
card->controller()->game->putInGraveyard(card);
|
|
paidThisTurn = 1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
};
|
|
//1143 Animate Dead
|
|
class AAnimateDead:public MTGAbility{
|
|
public:
|
|
AAnimateDead(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source, _target){
|
|
MTGCardInstance * card = _target;
|
|
card->power--;
|
|
card->life = card->toughness;
|
|
//Put the card in play again, with all its abilities !
|
|
//AbilityFactory af;
|
|
Spell * spell = NEW Spell(card);
|
|
//af.addAbilities(game->mLayers->actionLayer()->getMaxId(), spell);
|
|
source->controller()->game->putInZone(card, _target->controller()->game->graveyard, source->controller()->game->stack);
|
|
spell->resolve();
|
|
delete spell;
|
|
}
|
|
|
|
int destroy(){
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
card->power++;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
//1159 Erg Raiders
|
|
class AErgRaiders:public MTGAbility{
|
|
public:
|
|
int init;
|
|
int dealDamage;
|
|
AErgRaiders(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
init = 0;
|
|
dealDamage = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase){
|
|
Player * controller = source->controller();
|
|
if (newPhase == MTG_PHASE_COMBATDAMAGE && game->currentPlayer == controller){
|
|
if (!source->isAttacker() && init){
|
|
dealDamage = 1;
|
|
}
|
|
}else if (newPhase == MTG_PHASE_UNTAP && game->currentPlayer != controller){
|
|
if (dealDamage){
|
|
game->mLayers->stackLayer()->addDamage(source, controller,2);
|
|
}
|
|
init = 1;
|
|
dealDamage = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
//Fastbond
|
|
class AFastbond:public TriggeredAbility{
|
|
public:
|
|
int alreadyPlayedALand;
|
|
AFastbond(int _id, MTGCardInstance * card):TriggeredAbility(_id, card){
|
|
alreadyPlayedALand = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase!=currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
alreadyPlayedALand = 0;
|
|
}
|
|
TriggeredAbility::Update(dt);
|
|
}
|
|
|
|
int trigger(){
|
|
if(source->controller()->canPutLandsIntoPlay==0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
source->controller()->canPutLandsIntoPlay = 1;
|
|
if (alreadyPlayedALand){
|
|
game->mLayers->stackLayer()->addDamage(source, source->controller(), 1);
|
|
}
|
|
alreadyPlayedALand = 1;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//1165 Hypnotic Specter
|
|
class AHypnoticSpecter:public MTGAbility{
|
|
public:
|
|
int nbdamagesthisturn[2];
|
|
AHypnoticSpecter(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
currentPhase = -1;
|
|
for (int i = 0; i < 2; i++){
|
|
nbdamagesthisturn[i] = 0;
|
|
}
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
for (int i = 0; i < 2; i++){
|
|
nbdamagesthisturn[i] = 0;
|
|
}
|
|
}
|
|
|
|
ActionStack * as = game->mLayers->stackLayer();
|
|
int nbdamages[2];
|
|
for (int i = 0; i < 2; i++){
|
|
nbdamages[i] = 0;
|
|
}
|
|
|
|
Damage * current = ((Damage *)as->getNext(NULL,ACTION_DAMAGE,RESOLVED_OK));
|
|
while(current){
|
|
if (current->source == source){
|
|
for (int j=0; j < 2; j++){
|
|
if(current->target == game->players[j]) nbdamages[j]++;
|
|
}
|
|
}
|
|
current = ((Damage *)as->getNext(current,ACTION_DAMAGE,RESOLVED_OK));
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 2; i++){
|
|
while(nbdamages[i] > nbdamagesthisturn[i]){
|
|
nbdamagesthisturn[i]++;
|
|
game->players[i]->game->discardRandom(game->players[i]->game->hand);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
//1117 Jandor's Ring
|
|
class AJandorsRing:public ActivatedAbility{
|
|
public:
|
|
AJandorsRing(int _id, MTGCardInstance * _source):ActivatedAbility(_id,_source, NEW ManaCost()){
|
|
cost->add(MTG_COLOR_ARTIFACT, 2);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if (!source->controller()->game->hand->hasCard(source->controller()->game->library->lastCardDrawn)) return 0;
|
|
return ActivatedAbility::isReactingToClick(card);
|
|
}
|
|
|
|
int resolve(){
|
|
source->controller()->game->putInGraveyard(source->controller()->game->library->lastCardDrawn);
|
|
game->mLayers->stackLayer()->addDraw(source->controller());
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//Kudzu.
|
|
//What happens when there are no targets ???
|
|
class AKudzu: public TargetAbility{
|
|
public:
|
|
int previouslyTapped;
|
|
AKudzu(int _id, MTGCardInstance * card, MTGCardInstance * _target):TargetAbility(_id,card, NEW TypeTargetChooser("land",card)){
|
|
tc->toggleTarget(_target);
|
|
target = _target;
|
|
previouslyTapped = 0;
|
|
if (_target->tapped) previouslyTapped = 1;
|
|
}
|
|
|
|
|
|
void Update(float dt){
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
if (!_target->tapped){
|
|
previouslyTapped = 0;
|
|
}else if (!previouslyTapped){
|
|
#if defined (WIN32) || defined (LINUX)
|
|
OutputDebugString("Kudzu Strikes !\n");
|
|
#endif
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
_target->controller()->game->putInGraveyard(_target);
|
|
reactToClick(source); // ????
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
if (card == source && (!_target || !_target->isInPlay())){
|
|
#if defined (WIN32) || defined (LINUX)
|
|
OutputDebugString("Kudzu Reacts to click !\n");
|
|
#endif
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
int reactToClick(MTGCardInstance * card){
|
|
if (!waitingForAnswer) {
|
|
}else{
|
|
tc->toggleTarget(card);
|
|
}
|
|
return 1;
|
|
}
|
|
*/
|
|
|
|
int resolve(){
|
|
target = tc->getNextCardTarget();
|
|
source->target = (MTGCardInstance *) target;
|
|
previouslyTapped = 0;
|
|
if (source->target->tapped) previouslyTapped = 1;
|
|
return 1;
|
|
}
|
|
|
|
int testDestroy(){
|
|
GameObserver * g = GameObserver::GetInstance();
|
|
int stillLandsInPlay = 0;
|
|
for (int i = 0; i < 2; i++){
|
|
if (game->players[i]->game->inPlay->hasType("land")) stillLandsInPlay = 1;
|
|
}
|
|
if (!stillLandsInPlay){
|
|
source->controller()->game->putInGraveyard(source);
|
|
return 1;
|
|
}
|
|
|
|
if (!game->isInPlay(source)){
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
//Millstone
|
|
class AMillstone:public TargetAbility{
|
|
public:
|
|
AMillstone(int _id, MTGCardInstance * card):TargetAbility(_id,card, NEW PlayerTargetChooser(), NEW ManaCost()){
|
|
cost->add(MTG_COLOR_ARTIFACT, 2);
|
|
}
|
|
|
|
int resolve(){
|
|
Player * player = tc->getNextPlayerTarget();
|
|
if (!player) return 0;
|
|
MTGLibrary * library = player->game->library;
|
|
for (int i = 0; i < 2; i++){
|
|
if (library->nb_cards)
|
|
player->game->putInZone(library->cards[library->nb_cards-1],library, player->game->graveyard);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1170: Nightmare
|
|
class ANightmare:public ListMaintainerAbility{
|
|
public:
|
|
ANightmare(int _id, MTGCardInstance * _source):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller()->game->inPlay->hasCard(card) && card->hasType("swamp") ) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
source->power += 1;
|
|
source->addToToughness(1);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
source->power -= 1;
|
|
source->addToToughness(-1);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//1172 Pestilence
|
|
class APestilence: public ActivatedAbility{
|
|
public:
|
|
APestilence(int _id, MTGCardInstance * card):ActivatedAbility(_id, card, NEW ManaCost(), 0,0){
|
|
cost->add(MTG_COLOR_BLACK, 1);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase !=currentPhase && newPhase == MTG_PHASE_EOT){
|
|
if (!game->players[0]->game->inPlay->hasType("creature") && !game->players[1]->game->inPlay->hasType("creature")){
|
|
source->controller()->game->putInGraveyard(source);
|
|
}
|
|
}
|
|
}
|
|
|
|
int resolve(){
|
|
for (int i = 0; i < 2 ; i++){
|
|
MTGInPlay * inplay = game->players[i]->game->inPlay;
|
|
for (int j = inplay->nb_cards - 1 ; j >=0; j--){
|
|
if (inplay->cards[j]->isACreature()) game->mLayers->stackLayer()->addDamage(source,inplay->cards[j],1);
|
|
}
|
|
game->mLayers->stackLayer()->addDamage(source,game->players[i],1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Plague Rats and similar. Power and toughness equal to number of cards that share a name
|
|
class APlagueRats:public ListMaintainerAbility{
|
|
public:
|
|
string name;
|
|
APlagueRats(int _id, MTGCardInstance * _source, const char * _name):ListMaintainerAbility(_id,_source){
|
|
name = _name;
|
|
std::transform(name.begin(), name.end(), name.begin(),::tolower );
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (card == source) return 0;
|
|
string compared = card->name;
|
|
std::transform( compared.begin(), compared.end(), compared.begin(),::tolower );
|
|
if (name.compare(compared) == 0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
source->power += 1;
|
|
source->addToToughness(1);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
source->power -= 1;
|
|
source->addToToughness(-1);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Power Leak
|
|
class APowerLeak:public TriggeredAbility{
|
|
public:
|
|
int damagesToDealThisTurn;
|
|
ManaCost cost;
|
|
APowerLeak(int _id, MTGCardInstance * _source, MTGCardInstance * _target):TriggeredAbility(_id, _source, _target){
|
|
cost.add(MTG_COLOR_ARTIFACT, 1);
|
|
damagesToDealThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UPKEEP && _target->controller() == game->currentPlayer){
|
|
damagesToDealThisTurn = 2;
|
|
}
|
|
TriggeredAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (damagesToDealThisTurn && currentPhase == MTG_PHASE_UPKEEP && card==source && _target->controller() == game->currentPlayer){
|
|
if (game->currentPlayer->getManaPool()->canAfford(& cost)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToclick(MTGCardInstance * card){
|
|
game->currentPlayer->getManaPool()->pay( & cost);
|
|
damagesToDealThisTurn--;
|
|
return 1;
|
|
}
|
|
|
|
int trigger(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_DRAW && _target->controller() == game->currentPlayer){
|
|
if (damagesToDealThisTurn) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
game->mLayers->stackLayer()->addDamage(source,_target->controller(), damagesToDealThisTurn);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//Power Surge
|
|
class APowerSurge:public TriggeredAbility{
|
|
public:
|
|
int totalLands;
|
|
APowerSurge(int _id, MTGCardInstance * _source):TriggeredAbility(_id,_source){
|
|
totalLands = 0;
|
|
}
|
|
|
|
int trigger(){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_EOT){
|
|
//That's ugly but untapped land at the beginning of the turn are opponent's untapped lands at the end of the turn
|
|
totalLands = 0;
|
|
MTGInPlay * inPlay = game->opponent()->game->inPlay;
|
|
for (int i = 0; i < inPlay->nb_cards; i++){
|
|
MTGCardInstance * card = inPlay->cards[i];
|
|
if (!card->tapped && card->hasType("land")){
|
|
totalLands++;
|
|
}
|
|
}
|
|
}
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UPKEEP && totalLands){
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
if (totalLands) game->mLayers->stackLayer()->addDamage(source,game->currentPlayer,totalLands);
|
|
totalLands = 0;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//1175 Royal Assassin
|
|
class ARoyalAssassin:public TargetAbility{
|
|
public:
|
|
|
|
ARoyalAssassin(int _id, MTGCardInstance * _source):TargetAbility(_id,_source, NEW CreatureTargetChooser()){
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = tc->getNextCardTarget();
|
|
if(_target && _target->tapped){
|
|
_target->controller()->game->putInGraveyard(_target);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//1176 Sacrifice
|
|
class ASacrifice:public InstantAbility{
|
|
public:
|
|
ASacrifice(int _id, MTGCardInstance * _source, MTGCardInstance * _target):InstantAbility(_id, _source){
|
|
target = _target;
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target->isInPlay()){
|
|
game->currentlyActing()->game->putInGraveyard(_target);
|
|
int x = _target->getManaCost()->getConvertedCost();
|
|
game->currentlyActing()->getManaPool()->add(MTG_COLOR_BLACK, x);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1178 Scavenging Ghoul
|
|
class AScavengingGhoul:public MTGAbility{
|
|
public:
|
|
int counters;
|
|
AScavengingGhoul(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source, _target){
|
|
counters = 0;
|
|
}
|
|
|
|
|
|
void Update(float dt){
|
|
//TODO
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card){
|
|
if (counters > 0 && _card == source && game->currentlyActing()->game->inPlay->hasCard(source)){
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card){
|
|
if (!isReactingToClick( _card)) return 0;
|
|
counters--;
|
|
source->regenerate();
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1218 Psychic Venom
|
|
class APsychicVenom:public MTGAbility{
|
|
public:
|
|
int tapped;
|
|
APsychicVenom(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source,_target){
|
|
tapped = _target->tapped;
|
|
}
|
|
|
|
void Update(float dt){
|
|
MTGCardInstance* _target = (MTGCardInstance* )target;
|
|
int newState = _target->isTapped();
|
|
if (newState != tapped && newState == 1){
|
|
game->mLayers->stackLayer()->addDamage(source,_target->controller(),2);
|
|
}
|
|
tapped = newState;
|
|
}
|
|
};
|
|
|
|
|
|
//1221 Serendib Efreet
|
|
class ASerendibEfreet:public MTGAbility{
|
|
public:
|
|
ASerendibEfreet(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase == MTG_PHASE_UPKEEP && newPhase != currentPhase && game->currentPlayer == source->controller()){
|
|
game->mLayers->stackLayer()->addDamage(source,game->currentPlayer,1);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//1235 Aspect of Wolf
|
|
class AAspectOfWolf:public ListMaintainerAbility{
|
|
public:
|
|
int color;
|
|
AAspectOfWolf(int _id, MTGCardInstance * _source, MTGCardInstance * _target):ListMaintainerAbility(_id, _source, _target){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
|
|
if (card->controller() == source->controller() && card->hasType("forest")) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
int size = cards.size();
|
|
if (size % 2 == 0){
|
|
_target->power += 1;
|
|
}else{
|
|
_target->addToToughness(1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
int size = cards.size();
|
|
if (size % 2 == 1){
|
|
_target->power -= 1;
|
|
}else{
|
|
_target->addToToughness(-1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1276 Wanderlust, 1148 Cursed Lands
|
|
class AWanderlust:public TriggeredAbility{
|
|
public:
|
|
AWanderlust(int _id, MTGCardInstance * _source, MTGCardInstance * _target):TriggeredAbility(_id,_source, _target){}
|
|
|
|
int trigger(){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UPKEEP && ((MTGCardInstance *) target)->controller()==game->currentPlayer){
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
game->mLayers->stackLayer()->addDamage(source,((MTGCardInstance *) target)->controller(),1);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//1280 Atog
|
|
class AAtog:public TargetAbility{
|
|
public:
|
|
Player * currentController;
|
|
int counters;
|
|
AAtog(int _id, MTGCardInstance * _source):TargetAbility(_id, _source,NULL, NULL, 0,0){
|
|
currentController = source->controller();
|
|
MTGGameZone * zones[] = {currentController->game->inPlay};
|
|
tc = NEW TypeTargetChooser("artifact", zones, 1, source);
|
|
counters = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP){
|
|
for (int i = 0; i < counters; i++){
|
|
source->power-=2;
|
|
source->addToToughness(-2);
|
|
}
|
|
counters = 0;
|
|
}
|
|
TargetAbility::Update(dt);
|
|
Player * newController = source->controller();
|
|
if (newController != currentController){
|
|
SAFE_DELETE(tc);
|
|
MTGGameZone * zones[] = {newController->game->inPlay}; //In case Atog's controller changes
|
|
tc = NEW TypeTargetChooser("artifact", zones, 1, source);
|
|
currentController = newController;
|
|
}
|
|
}
|
|
|
|
int resolve(){
|
|
tc->getNextCardTarget()->controller()->game->putInGraveyard(tc->getNextCardTarget());
|
|
source->power+=2;
|
|
source->addToToughness(2);
|
|
counters ++;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
//1284 Dragon Whelp
|
|
class ADragonWhelp: public APowerToughnessModifierUntilEndOfTurn{
|
|
public:
|
|
ADragonWhelp(int id, MTGCardInstance * card):APowerToughnessModifierUntilEndOfTurn(id, card, card, 1, 0, NEW ManaCost()){
|
|
cost->add(MTG_COLOR_RED, 1);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP && counters > 3){
|
|
source->controller()->game->putInGraveyard(source);
|
|
}
|
|
APowerToughnessModifierUntilEndOfTurn::Update(dt);
|
|
}
|
|
|
|
};
|
|
|
|
//1288 EarthBind
|
|
class AEarthbind:public ABasicAbilityModifier{
|
|
public:
|
|
AEarthbind(int _id, MTGCardInstance * _source, MTGCardInstance * _target):ABasicAbilityModifier(_id,_source,_target,FLYING,0){
|
|
if (value_before_modification) game->mLayers->stackLayer()->addDamage(source,target,2);
|
|
}
|
|
};
|
|
|
|
//1291 Fireball
|
|
class AFireball:public InstantAbility{
|
|
public:
|
|
AFireball(int _id, MTGCardInstance * card, Spell * spell, int x):InstantAbility(_id, card){
|
|
int nbtargets = spell->cursor;
|
|
int totaldamage = x+1-nbtargets;
|
|
int individualdamage = totaldamage / nbtargets;
|
|
Damageable * _target = spell->getNextDamageableTarget();
|
|
while(_target){
|
|
game->mLayers->stackLayer()->addDamage(source,_target,individualdamage);
|
|
_target = spell->getNextDamageableTarget(_target);
|
|
}
|
|
}
|
|
};
|
|
|
|
//1245 ForceOfNature
|
|
class AForceOfNature:public ActivatedAbility{
|
|
public:
|
|
int dealDamageThisTurn;
|
|
AForceOfNature(int _id, MTGCardInstance * card):ActivatedAbility(_id,card, NEW ManaCost(),1,0){
|
|
dealDamageThisTurn = 0;
|
|
cost->add(MTG_COLOR_GREEN,4);
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase !=currentPhase){
|
|
if (newPhase == MTG_PHASE_UNTAP){
|
|
dealDamageThisTurn = 1;
|
|
}else if (newPhase == MTG_PHASE_DRAW && dealDamageThisTurn && game->currentPlayer==source->controller() ){
|
|
game->mLayers->stackLayer()->addDamage(source,source->controller(),8);
|
|
}
|
|
}
|
|
ActivatedAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
return (dealDamageThisTurn && currentPhase == MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card));
|
|
}
|
|
|
|
int resolve(){
|
|
dealDamageThisTurn = 0;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//1301 KeldonWarlord
|
|
class AKeldonWarlord:public ListMaintainerAbility{
|
|
public:
|
|
AKeldonWarlord(int _id, MTGCardInstance * _source):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller()->game->inPlay->hasCard(card) && card->isACreature() && !card->hasType("wall") ) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
source->power += 1;
|
|
source->addToToughness(1);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
source->power -= 1;
|
|
source->addToToughness(-1);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1302 : Kird Ape
|
|
class AKirdApe:public MTGAbility{
|
|
public:
|
|
int init;
|
|
AKirdApe(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
init = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (source->controller()->game->inPlay->hasType("forest")){
|
|
if(!init){
|
|
init = 1;
|
|
source->power+=1;
|
|
source->addToToughness(2);
|
|
}
|
|
}else{
|
|
if (init){
|
|
init = 0;
|
|
source->power-=1;
|
|
source->addToToughness(-2);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//1309 Orcish Artilery
|
|
class AOrcishArtillery: public ADamager{
|
|
public:
|
|
AOrcishArtillery(int _id,MTGCardInstance * card): ADamager(_id, card, NEW ManaCost(), 2){
|
|
}
|
|
|
|
int resolve(){
|
|
ADamager::resolve();
|
|
game->mLayers->stackLayer()->addDamage(source,source->controller(), 3);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1310 Orcish Oriflame
|
|
class AOrcishOriflame:public ListMaintainerAbility{
|
|
public:
|
|
int color;
|
|
AOrcishOriflame(int _id, MTGCardInstance * _source):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller() == game->currentPlayer && game->currentPlayer->game->inPlay->hasCard(card) && card->attacker) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
card->power += 1;
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
card->power -= 1;
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//1334 Castle
|
|
class ACastle:public ListMaintainerAbility{
|
|
public:
|
|
ACastle(int _id, MTGCardInstance * _source):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller()->game->inPlay->hasCard(card) && card->isACreature() && !card->isAttacker() && !card->tapped) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
card->addToToughness(2);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
card->addToToughness(-2);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
//1351 Island Sanctuary
|
|
class AIslandSanctuary:public MTGAbility{
|
|
public:
|
|
int initThisTurn;
|
|
AIslandSanctuary(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
initThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (currentPhase == MTG_PHASE_UNTAP && game->currentPlayer == source->controller()) initThisTurn = 0;
|
|
|
|
if (initThisTurn && currentPhase == MTG_PHASE_COMBATATTACKERS && game->currentPlayer != source->controller()){
|
|
MTGGameZone * zone = game->currentPlayer->game->inPlay;
|
|
for (int i = 0; i < zone->nb_cards; i++){
|
|
MTGCardInstance * card = zone->cards[i];
|
|
if (card->isAttacker() && !card->basicAbilities[FLYING] && !card->basicAbilities[ISLANDWALK]) card->attacker=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if (card==source && game->currentPlayer == card->controller() && currentPhase == MTG_PHASE_DRAW){
|
|
Interruptible * action = game->mLayers->stackLayer()->_(-1);
|
|
if (action->type == ACTION_DRAW) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int reactToClick(MTGCardInstance * card){
|
|
if (!isReactingToClick(card)) return 0;
|
|
game->mLayers->stackLayer()->Remove(game->mLayers->stackLayer()->_(-1));
|
|
initThisTurn = 1;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//1352 Karma
|
|
class AKarma: public TriggeredAbility{
|
|
public:
|
|
AKarma(int _id, MTGCardInstance * _source):TriggeredAbility(_id, _source){
|
|
}
|
|
|
|
int trigger(){
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UPKEEP) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int resolve(){
|
|
int totaldamage = 0;
|
|
MTGGameZone * zone = game->currentPlayer->game->inPlay;
|
|
for (int i = 0; i < zone->nb_cards; i++){
|
|
if (zone->cards[i]->hasType("swamp")) totaldamage++;;
|
|
}
|
|
if (totaldamage) game->mLayers->stackLayer()->addDamage(source,game->currentPlayer, totaldamage);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
//1355 Norther Paladin
|
|
class ANorthernPaladin:public TargetAbility{
|
|
public:
|
|
ANorthernPaladin(int _id, MTGCardInstance * card):TargetAbility(_id, card){
|
|
int _cost[] = {MTG_COLOR_WHITE, 2};
|
|
cost = NEW ManaCost(_cost,1);
|
|
tc = NEW TargetChooser();
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * card = tc->getNextCardTarget();
|
|
if (card->hasColor(MTG_COLOR_BLACK)){
|
|
card->controller()->game->putInGraveyard(card);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
//Sedge Troll
|
|
class ASedgeTroll:public MTGAbility{
|
|
public:
|
|
int init;
|
|
ASedgeTroll(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
init = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (source->controller()->game->inPlay->hasType("swamp")){
|
|
if(!init){
|
|
init = 1;
|
|
source->power+=1;
|
|
source->addToToughness(1);
|
|
}
|
|
}else{
|
|
if (init){
|
|
init = 0;
|
|
source->power-=1;
|
|
source->addToToughness(-1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//Soul Net
|
|
class ASoulNet:public ActivatedAbility{
|
|
public:
|
|
PutInGraveyard * latest;
|
|
PutInGraveyard * newDead;
|
|
ASoulNet(int _id, MTGCardInstance * card):ActivatedAbility(_id, card,0,0,0){
|
|
int _cost[] = {MTG_COLOR_ARTIFACT, 1};
|
|
cost = NEW ManaCost(_cost,1);
|
|
latest = ((PutInGraveyard *) GameObserver::GetInstance()->mLayers->stackLayer()->getPrevious(NULL,ACTION_PUTINGRAVEYARD,RESOLVED_OK));
|
|
newDead = latest;
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
newDead = ((PutInGraveyard *) GameObserver::GetInstance()->mLayers->stackLayer()->getPrevious(NULL,ACTION_PUTINGRAVEYARD,RESOLVED_OK));
|
|
if (newDead && newDead != latest && newDead->card->isACreature())
|
|
return ActivatedAbility::isReactingToClick(card);
|
|
return 0;
|
|
}
|
|
int resolve(){
|
|
latest = newDead;
|
|
source->controller()->life++;
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
//Stasis
|
|
class AStasis:public ActivatedAbility{
|
|
public:
|
|
int paidThisTurn;
|
|
AStasis(int _id, MTGCardInstance * card):ActivatedAbility(_id,card, NEW ManaCost(),1,0){
|
|
paidThisTurn = 1;
|
|
cost->add(MTG_COLOR_BLUE,1);
|
|
}
|
|
|
|
void Update(float dt){
|
|
//Upkeep Cost
|
|
if (newPhase !=currentPhase){
|
|
if (newPhase == MTG_PHASE_UPKEEP){
|
|
paidThisTurn = 0;
|
|
}else if (!paidThisTurn && newPhase > MTG_PHASE_UPKEEP && game->currentPlayer==source->controller() ){
|
|
game->currentPlayer->game->putInGraveyard(source);
|
|
paidThisTurn = 1;
|
|
}
|
|
}
|
|
//Stasis Effect
|
|
for (int i = 0; i < 2; i++){
|
|
game->phaseRing->removePhase(MTG_PHASE_UNTAP,game->players[i]);
|
|
}
|
|
|
|
//Parent Class Method Call
|
|
ActivatedAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
return (!paidThisTurn && currentPhase == MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card));
|
|
}
|
|
|
|
int resolve(){
|
|
paidThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
int destroy(){
|
|
for (int i = 0; i < 2; i++){
|
|
game->phaseRing->addPhaseBefore(MTG_PHASE_UNTAP,game->players[i],MTG_PHASE_UPKEEP,game->players[i]);
|
|
}
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//--------------Addon Abra------------------
|
|
//ShieldOfTheAge
|
|
class AShieldOfTheAge: public TargetAbility{
|
|
public:
|
|
AShieldOfTheAge(int _id, MTGCardInstance * card):TargetAbility(_id,card,NEW DamageTargetChooser(card,_id),NEW ManaCost(),0,0){
|
|
cost->add(MTG_COLOR_ARTIFACT,2);
|
|
}
|
|
|
|
int resolve(){
|
|
Damage * damage = tc->getNextDamageTarget();
|
|
if (!damage) return 0;
|
|
game->mLayers->stackLayer()->Fizzle(damage);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
// People of the Woods
|
|
class APeopleOfTheWoods:public ListMaintainerAbility{
|
|
public:
|
|
APeopleOfTheWoods(int _id, MTGCardInstance * _source):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller()->game->inPlay->hasCard(card) && card->hasType("forest") ) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
source->addToToughness(1);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
source->addToToughness(-1);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//Abomination Kill blocking creature if white or green
|
|
class AAbomination :public MTGAbility{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
AAbomination (int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
|
|
nbOpponents = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase){
|
|
if( newPhase == MTG_PHASE_COMBATDAMAGE){
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while ((opponent && opponent->hasColor(MTG_COLOR_GREEN)) || opponent->hasColor(MTG_COLOR_WHITE)){
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents ++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
}else if (newPhase == MTG_PHASE_COMBATEND){
|
|
for (int i = 0; i < nbOpponents ; i++){
|
|
game->mLayers->stackLayer()->addPutInGraveyard(opponents[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int testDestroy(){
|
|
if(!game->isInPlay(source) && currentPhase != MTG_PHASE_UNTAP){
|
|
return 0;
|
|
}else{
|
|
return MTGAbility::testDestroy();
|
|
}
|
|
}
|
|
};
|
|
|
|
// GiveLifeForTappedType
|
|
class AGiveLifeForTappedType:public MTGAbility{
|
|
public:
|
|
char type[20];
|
|
int nbtypestapped;
|
|
|
|
int counttypesTapped(){
|
|
int result = 0;
|
|
MTGInPlay * inplay = source->controller()->opponent()->game->inPlay;
|
|
for (int i = 0; i < inplay->nb_cards; i++){
|
|
MTGCardInstance * card = inplay->cards[i];
|
|
if (card->tapped && card->hasType(type)) result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
AGiveLifeForTappedType(int _id, MTGCardInstance * source, const char * _type):MTGAbility(_id, source){
|
|
sprintf(type,"%s",_type);{
|
|
nbtypestapped = counttypesTapped();
|
|
}
|
|
}
|
|
|
|
void Update(float dt){
|
|
int newcount = counttypesTapped();
|
|
for (int i=0; i < newcount - nbtypestapped; i++){
|
|
source->controller()->life++;
|
|
}
|
|
nbtypestapped = newcount;
|
|
}
|
|
};
|
|
|
|
//Minion of Leshrac
|
|
class AMinionofLeshrac: public TargetAbility{
|
|
public:
|
|
int paidThisTurn;
|
|
AMinionofLeshrac(int _id, MTGCardInstance * source):TargetAbility(_id, source, NEW CreatureTargetChooser(),0,1,0){
|
|
paidThisTurn = 1;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (newPhase != currentPhase && source->controller() == game->currentPlayer){
|
|
if (newPhase == MTG_PHASE_UNTAP){
|
|
paidThisTurn = 0;
|
|
}else if( newPhase == MTG_PHASE_UPKEEP + 1 && !paidThisTurn){
|
|
game->mLayers->stackLayer()->addDamage(source,source->controller(), 5);
|
|
source->tapped = 1;
|
|
}
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card){
|
|
if (currentPhase != MTG_PHASE_UPKEEP || paidThisTurn) return 0;
|
|
return TargetAbility::isReactingToClick(card);
|
|
}
|
|
|
|
int resolve(){
|
|
MTGCardInstance * card = tc->getNextCardTarget();
|
|
if (card && card != source && card->controller() == source->controller()){
|
|
card->controller()->game->putInGraveyard(card);
|
|
paidThisTurn = 1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
};
|
|
|
|
//2703 Lost Order of Jarkeld
|
|
class ALostOrderofJarkeld:public ListMaintainerAbility{
|
|
public:
|
|
ALostOrderofJarkeld(int _id, MTGCardInstance * _source):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller()->opponent()->game->inPlay->hasCard(card) && card->isACreature() ) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
source->power += 1;
|
|
source->addToToughness(1);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
source->power -= 1;
|
|
source->addToToughness(-1);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//CreaturePowerToughnessModifierForAllTypeControlled
|
|
class ACreaturePowerToughnessModifierForAllTypeControlled:public ListMaintainerAbility{
|
|
public:
|
|
char type[20];
|
|
ACreaturePowerToughnessModifierForAllTypeControlled(int _id, MTGCardInstance * _source, const char * _type):ListMaintainerAbility(_id, _source){
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card){
|
|
if (source->controller()->game->inPlay->hasCard(card) && card->hasType(type) ) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card){
|
|
source->power += 1;
|
|
source->addToToughness(1);
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card){
|
|
source->power -= 1;
|
|
source->addToToughness(-1);
|
|
return 1;
|
|
}
|
|
|
|
};
|
|
|
|
//GenericKirdApe
|
|
//Erwan 2008/11/15 : this cannot work as type is never initialized...
|
|
class AGenericKirdApe:public MTGAbility{
|
|
public:
|
|
int init;
|
|
char type [20];
|
|
int power;
|
|
int toughness;
|
|
AGenericKirdApe(int _id, MTGCardInstance * _source, const char * _type, int _power, int _toughness):MTGAbility(_id, _source){
|
|
init = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (source->controller()->game->inPlay->hasType(type)){
|
|
if(!init){
|
|
init = 1;
|
|
source->power+=power;
|
|
source->addToToughness(toughness);
|
|
}
|
|
}else{
|
|
if (init){
|
|
init = 0;
|
|
source->power-=power;
|
|
source->addToToughness(-toughness);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//Rampage ability Tentative 2
|
|
class ARampageAbility:public MTGAbility{
|
|
public:
|
|
int nbOpponents;
|
|
int PowerModifier;
|
|
int ToughnessModifier;
|
|
int modifier;
|
|
ARampageAbility(int _id, MTGCardInstance * _source,int _PowerModifier, int _ToughnessModifier):MTGAbility(_id, _source){
|
|
modifier=0;
|
|
}
|
|
void Update(float dt){
|
|
if (source->isAttacker()){
|
|
MTGInPlay * inPlay = game->opponent()->game->inPlay;
|
|
for (int i = 0; i < inPlay->nb_cards; i ++){
|
|
MTGCardInstance * current = inPlay->cards[i];
|
|
if (current->isDefenser()){
|
|
modifier++;
|
|
}
|
|
}
|
|
source->power+= (PowerModifier * modifier);
|
|
source->addToToughness(ToughnessModifier * modifier);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//Rampage ability Tentative 1 - Did not work as expected
|
|
class A1RampageAbility:public MTGAbility{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
int PowerModifier;
|
|
int ToughnessModifier;
|
|
A1RampageAbility(int _id, MTGCardInstance * _source,int _PowerModifier, int _ToughnessModifier):MTGAbility(_id, _source){
|
|
nbOpponents = 0;
|
|
}
|
|
|
|
void Update(float dt){
|
|
if (source->isAttacker()){
|
|
if (newPhase != currentPhase){
|
|
if( newPhase == MTG_PHASE_COMBATDAMAGE){
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent){
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents ++;
|
|
source->power+= PowerModifier;
|
|
source->addToToughness(ToughnessModifier);
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif
|