1423 lines
44 KiB
C++
1423 lines
44 KiB
C++
#include "PrecompiledHeader.h"
|
|
|
|
#include "TargetChooser.h"
|
|
#include "CardDescriptor.h"
|
|
#include "MTGGameZones.h"
|
|
#include "GameObserver.h"
|
|
#include "Subtypes.h"
|
|
#include "Counters.h"
|
|
#include "WEvent.h"
|
|
#include "AllAbilities.h"
|
|
|
|
TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInstance * card, MTGAbility * ability)
|
|
{
|
|
if (!s.size()) return NULL;
|
|
|
|
int zones[10];
|
|
int nbzones = 0;
|
|
size_t found;
|
|
bool other = false;
|
|
|
|
found = s.find("mytgt");
|
|
if (found == 0)
|
|
{
|
|
MTGCardInstance * target = card->target;
|
|
if (ability) target = (MTGCardInstance *) (ability->target);
|
|
return NEW CardTargetChooser(target, card);
|
|
};
|
|
|
|
found = s.find("opponent");
|
|
if (found == 0)
|
|
{
|
|
int maxtargets = 1;
|
|
Player * opponent = card->controller()->opponent();
|
|
return NEW PlayerTargetChooser(card, maxtargets, opponent);
|
|
};
|
|
|
|
found = s.find("controller");
|
|
if (found == 0)
|
|
{
|
|
int maxtargets = 1;
|
|
Player * controller = card->controller();
|
|
return NEW PlayerTargetChooser(card, maxtargets, controller);
|
|
};
|
|
|
|
found = s.find("other ");
|
|
if (found == 0)
|
|
{
|
|
other = true;
|
|
s = s.substr(6);
|
|
}
|
|
|
|
found = s.find("trigger");
|
|
if (found == 0)
|
|
{
|
|
if (s.length() > 7 && s.find("[") == 7)
|
|
{
|
|
string s1 = s.substr(7, s.find("]"));
|
|
if (s1.find("to") != string::npos) return NEW TriggerTargetChooser(WEvent::TARGET_TO);
|
|
if (s1.find("from") != string::npos) return NEW TriggerTargetChooser(WEvent::TARGET_FROM);
|
|
}
|
|
return NEW TriggerTargetChooser(1);
|
|
}
|
|
|
|
found = s.find("player");
|
|
if (found != string::npos)
|
|
{
|
|
int maxtargets = 1;
|
|
size_t several = s.find_first_of('s', 5);
|
|
if (several != string::npos) maxtargets = -1;
|
|
found = s.find("creature");
|
|
if (found != string::npos) return NEW DamageableTargetChooser(card, maxtargets, other); //Any Damageable target (player, creature)
|
|
return NEW PlayerTargetChooser(card, maxtargets); //Any player
|
|
}
|
|
|
|
string s1;
|
|
found = s.find("|");
|
|
if (found != string::npos)
|
|
{
|
|
string s2;
|
|
s1 = s.substr(0, found);
|
|
s2 = s.substr(found + 1);
|
|
while (s2.size())
|
|
{
|
|
found = s2.find(",");
|
|
string zoneName;
|
|
if (found != string::npos)
|
|
{
|
|
zoneName = s2.substr(0, found);
|
|
s2 = s2.substr(found + 1);
|
|
}
|
|
else
|
|
{
|
|
zoneName = s2;
|
|
s2 = "";
|
|
}
|
|
zones[nbzones] = MTGGameZone::MY_BATTLEFIELD;
|
|
|
|
if (zoneName.compare("*") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::ALL_ZONES;
|
|
}
|
|
else if (zoneName.compare("graveyard") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::MY_GRAVEYARD;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_GRAVEYARD;
|
|
}
|
|
else if (zoneName.compare("battlefield") == 0 || zoneName.compare("inplay") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::MY_BATTLEFIELD;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_BATTLEFIELD;
|
|
}
|
|
else if (zoneName.compare("hand") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::MY_HAND;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_HAND;
|
|
}
|
|
else if (zoneName.compare("library") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::MY_LIBRARY;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_LIBRARY;
|
|
}
|
|
else if (zoneName.compare("nonbattlezone") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::MY_GRAVEYARD;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_GRAVEYARD;
|
|
zones[nbzones++] = MTGGameZone::MY_LIBRARY;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_LIBRARY;
|
|
zones[nbzones++] = MTGGameZone::MY_HAND;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_HAND;
|
|
zones[nbzones++] = MTGGameZone::MY_EXILE;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_EXILE;
|
|
}
|
|
else if (zoneName.compare("stack") == 0)
|
|
{
|
|
zones[nbzones++] = MTGGameZone::MY_STACK;
|
|
zones[nbzones++] = MTGGameZone::OPPONENT_STACK;
|
|
}
|
|
else
|
|
{
|
|
int zone = MTGGameZone::zoneStringToId(zoneName);
|
|
if (zone) zones[nbzones++] = zone;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s1 = s;
|
|
nbzones = 2;
|
|
zones[0] = MTGGameZone::MY_BATTLEFIELD;
|
|
zones[1] = MTGGameZone::OPPONENT_BATTLEFIELD;
|
|
}
|
|
|
|
TargetChooser * tc = NULL;
|
|
int maxtargets = 1;
|
|
CardDescriptor * cd = NULL;
|
|
//max targets allowed
|
|
size_t limit = s1.find('<');
|
|
if (limit != string::npos)
|
|
{
|
|
size_t end = s1.find(">", limit);
|
|
string howmany;
|
|
if (end != string::npos)
|
|
{
|
|
howmany = s1.substr(limit + 1, end - limit - 1);
|
|
WParsedInt * howmuch = NEW WParsedInt(howmany, NULL, card);
|
|
maxtargets = howmuch->getValue();
|
|
delete howmuch;
|
|
s1 = s1.substr(end + 1);
|
|
}
|
|
}
|
|
|
|
while (s1.size())
|
|
{
|
|
found = s1.find(",");
|
|
string typeName;
|
|
if (found != string::npos)
|
|
{
|
|
typeName = s1.substr(0, found);
|
|
s1 = s1.substr(found + 1);
|
|
}
|
|
else
|
|
{
|
|
typeName = s1;
|
|
s1 = "";
|
|
}
|
|
|
|
//Advanced cards caracteristics ?
|
|
found = typeName.find("[");
|
|
if (found != string::npos)
|
|
{
|
|
int nbminuses = 0;
|
|
int end = typeName.find("]");
|
|
string attributes = typeName.substr(found + 1, end - found - 1);
|
|
cd = NEW CardDescriptor();
|
|
while (attributes.size())
|
|
{
|
|
size_t found2 = attributes.find(";");
|
|
string attribute;
|
|
if (found2 != string::npos)
|
|
{
|
|
cd->mode = CD_OR;
|
|
attribute = attributes.substr(0, found2);
|
|
attributes = attributes.substr(found2 + 1);
|
|
}
|
|
else
|
|
{
|
|
attribute = attributes;
|
|
attributes = "";
|
|
}
|
|
int minus = 0;
|
|
if (attribute[0] == '-')
|
|
{
|
|
minus = 1;
|
|
nbminuses++;
|
|
attribute = attribute.substr(1);
|
|
}
|
|
int comparisonMode = COMPARISON_NONE;
|
|
int comparisonCriterion = 0;
|
|
if (attribute.size() > 1)
|
|
{
|
|
size_t operatorPosition = attribute.find("=", 1);
|
|
if (operatorPosition != string::npos)
|
|
{
|
|
string numberCD = attribute.substr(operatorPosition + 1, attribute.size() - operatorPosition - 1);
|
|
WParsedInt * val = NEW WParsedInt(numberCD,NULL, card);
|
|
comparisonCriterion = val->getValue();
|
|
/*atoi(attribute.substr(operatorPosition + 1, attribute.size() - operatorPosition - 1).c_str());*/
|
|
delete val;
|
|
switch (attribute[operatorPosition - 1])
|
|
{
|
|
case '<':
|
|
if (minus)
|
|
{
|
|
comparisonMode = COMPARISON_GREATER;
|
|
}
|
|
else
|
|
{
|
|
comparisonMode = COMPARISON_AT_MOST;
|
|
}
|
|
operatorPosition--;
|
|
break;
|
|
case '>':
|
|
if (minus)
|
|
{
|
|
comparisonMode = COMPARISON_LESS;
|
|
}
|
|
else
|
|
{
|
|
comparisonMode = COMPARISON_AT_LEAST;
|
|
}
|
|
operatorPosition--;
|
|
break;
|
|
default:
|
|
if (minus)
|
|
{
|
|
comparisonMode = COMPARISON_UNEQUAL;
|
|
}
|
|
else
|
|
{
|
|
comparisonMode = COMPARISON_EQUAL;
|
|
}
|
|
}
|
|
attribute = attribute.substr(0, operatorPosition);
|
|
}
|
|
}
|
|
|
|
//Attacker
|
|
if (attribute.find("attacking") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->attacker = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->attacker = 1;
|
|
}
|
|
}
|
|
//Blocker
|
|
else if (attribute.find("blocking") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->defenser = &MTGCardInstance::NoCard;
|
|
}
|
|
else
|
|
{
|
|
cd->defenser = &MTGCardInstance::AnyCard;
|
|
}
|
|
}
|
|
//Tapped, untapped
|
|
else if (attribute.find("tapped") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->unsecureSetTapped(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->unsecureSetTapped(1);
|
|
}
|
|
//Token
|
|
}
|
|
else if (attribute.find("token") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->isToken = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->isToken = 1;
|
|
}
|
|
//put in its zone this turn
|
|
}
|
|
else if (attribute.find("fresh") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->unsecuresetfresh(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->unsecuresetfresh(1);
|
|
}
|
|
}
|
|
//creature is a level up creature
|
|
else if (attribute.find("leveler") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->isLeveler = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->isLeveler = 1;
|
|
}
|
|
}
|
|
//creature is enchanted
|
|
else if (attribute.find("enchanted") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->CDenchanted = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->CDenchanted = 1;
|
|
}
|
|
}
|
|
//creature was damaged
|
|
else if (attribute.find("damaged") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->CDdamaged = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->CDdamaged = 1;
|
|
}
|
|
}
|
|
//creature dealt damage to opponent
|
|
else if (attribute.find("opponentdamager") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->CDopponentDamaged = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->CDopponentDamaged = 1;
|
|
}
|
|
}
|
|
//creature dealt damage to controller
|
|
else if (attribute.find("controllerdamager") != string::npos)
|
|
{
|
|
if (minus)
|
|
{
|
|
cd->CDcontrollerDamaged = -1;
|
|
}
|
|
else
|
|
{
|
|
cd->CDcontrollerDamaged = 1;
|
|
}
|
|
}
|
|
else if (attribute.find("multicolor") != string::npos)
|
|
{
|
|
//card is multicolored?
|
|
if (minus)
|
|
{
|
|
cd->setisMultiColored(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->setisMultiColored(1);
|
|
}
|
|
|
|
}
|
|
else if (attribute.find("blackandgreen") != string::npos)
|
|
{
|
|
//card is both colors?
|
|
if (minus)
|
|
{
|
|
cd->setisBlackAndGreen(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->setisBlackAndGreen(1);
|
|
}
|
|
|
|
}
|
|
else if (attribute.find("blackandwhite") != string::npos)
|
|
{
|
|
//card is both colors?
|
|
if (minus)
|
|
{
|
|
cd->setisBlackAndWhite(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->setisBlackAndWhite(1);
|
|
}
|
|
|
|
}
|
|
else if (attribute.find("redandblue") != string::npos)
|
|
{
|
|
//card is both colors?
|
|
if (minus)
|
|
{
|
|
cd->setisRedAndBlue(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->setisRedAndBlue(1);
|
|
}
|
|
|
|
}
|
|
else if (attribute.find("blueandgreen") != string::npos)
|
|
{
|
|
//card is both colors?
|
|
if (minus)
|
|
{
|
|
cd->setisBlueAndGreen(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->setisBlueAndGreen(1);
|
|
}
|
|
|
|
}
|
|
else if (attribute.find("redandwhite") != string::npos)
|
|
{
|
|
//card is both colors?
|
|
if (minus)
|
|
{
|
|
cd->setisRedAndWhite(-1);
|
|
}
|
|
else
|
|
{
|
|
cd->setisRedAndWhite(1);
|
|
}
|
|
|
|
}
|
|
else if (attribute.find("power") != string::npos)
|
|
{
|
|
//Power restrictions
|
|
cd->setPower(comparisonCriterion);
|
|
cd->powerComparisonMode = comparisonMode;
|
|
//Toughness restrictions
|
|
}
|
|
else if (attribute.find("toughness") != string::npos)
|
|
{
|
|
cd->setToughness(comparisonCriterion);
|
|
cd->toughnessComparisonMode = comparisonMode;
|
|
//Manacost restrictions
|
|
}
|
|
else if (attribute.find("manacost") != string::npos)
|
|
{
|
|
cd->convertedManacost = comparisonCriterion;
|
|
cd->manacostComparisonMode = comparisonMode;
|
|
//Counter Restrictions
|
|
}
|
|
else if (attribute.find("share!") != string::npos)
|
|
{
|
|
size_t start = attribute.find("share!");
|
|
size_t end = attribute.rfind("!");
|
|
string CDtype = attribute.substr(start + 6,end - start);
|
|
if( card && card->isSpell() && card->backupTargets[0]->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
//spells always store their targets in :targets[]
|
|
//however they are all erased as the spell resolves
|
|
//added a array to store these backups incase theyre needed
|
|
//again for effects such as these.
|
|
Spell * spell;
|
|
spell = (Spell*)card->backupTargets[0];
|
|
card->target = spell->source;
|
|
}
|
|
if( CDtype.find("name") != string::npos )
|
|
{
|
|
if(card->target)
|
|
cd->compareName = card->target->name;
|
|
else
|
|
cd->compareName = card->name;
|
|
|
|
cd->nameComparisonMode = COMPARISON_EQUAL;
|
|
}
|
|
else if( CDtype.find("color") != string::npos )
|
|
{
|
|
if(card->target)
|
|
cd->colors = card->target->colors;
|
|
else
|
|
cd->colors = card->colors;
|
|
|
|
cd->mode = CD_OR;
|
|
}
|
|
else if( CDtype.find("types") != string::npos )
|
|
{
|
|
if(card && card->target)
|
|
{
|
|
cd->types = card->target->types;
|
|
//remove main types because we only care about subtypes here.
|
|
cd->removeType("artifact");
|
|
cd->removeType("land");
|
|
cd->removeType("enchantment");
|
|
cd->removeType("instant");
|
|
cd->removeType("sorcery");
|
|
cd->removeType("legendary");
|
|
cd->removeType("creature");
|
|
}
|
|
else
|
|
{
|
|
cd->types = card->types;
|
|
}
|
|
cd->mode = CD_OR;
|
|
}
|
|
}
|
|
else if (attribute.find("counter") != string::npos)
|
|
{
|
|
if (attribute.find("{any}") != string::npos)
|
|
{
|
|
cd->anyCounter = 1;
|
|
}
|
|
else
|
|
{
|
|
size_t start = attribute.find("{");
|
|
size_t end = attribute.find("}");
|
|
string counterString = attribute.substr(start + 1, end - start - 1);
|
|
AbilityFactory abf;
|
|
Counter * counter = abf.parseCounter(counterString, card);
|
|
if (counter)
|
|
{
|
|
cd->counterName = counter->name;
|
|
cd->counterNB = counter->nb;
|
|
cd->counterPower = counter->power;
|
|
cd->counterToughness = counter->toughness;
|
|
delete (counter);
|
|
}
|
|
if (minus)
|
|
{
|
|
cd->counterComparisonMode = COMPARISON_LESS;
|
|
}
|
|
else
|
|
{
|
|
cd->counterComparisonMode = COMPARISON_AT_LEAST;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int attributefound = 0;
|
|
//Colors - remove Artifact and Land from the loop
|
|
for (int cid = 1; cid < Constants::MTG_NB_COLORS - 1; cid++)
|
|
{
|
|
if (attribute.find(Constants::MTGColorStrings[cid]) != string::npos)
|
|
{
|
|
attributefound = 1;
|
|
if (minus)
|
|
cd->SetExclusionColor(cid);
|
|
else
|
|
cd->setColor(cid);
|
|
}
|
|
}
|
|
if (!attributefound)
|
|
{
|
|
//Abilities
|
|
for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++)
|
|
{
|
|
if (attribute.find(Constants::MTGBasicAbilities[j]) != string::npos)
|
|
{
|
|
attributefound = 1;
|
|
if (minus)
|
|
cd->mAbilityExclusions.set(j);
|
|
else
|
|
cd->basicAbilities.set(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!attributefound)
|
|
{
|
|
//Subtypes
|
|
if (minus)
|
|
{
|
|
cd->setNegativeSubtype(attribute);
|
|
}
|
|
else
|
|
{
|
|
cd->setSubtype(attribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nbminuses)
|
|
cd->mode = CD_AND;
|
|
typeName = typeName.substr(0, found);
|
|
}
|
|
//X targets allowed ?
|
|
if (typeName.at(typeName.length() - 1) == 's' && !Subtypes::subtypesList->find(typeName, false) && typeName.compare("this")
|
|
!= 0)
|
|
{
|
|
typeName = typeName.substr(0, typeName.length() - 1);
|
|
maxtargets = -1;
|
|
}
|
|
if (cd)
|
|
{
|
|
if (!tc)
|
|
{
|
|
if (typeName.compare("*") != 0) cd->setSubtype(typeName);
|
|
|
|
tc = NEW DescriptorTargetChooser(cd, zones, nbzones, card, maxtargets, other);
|
|
}
|
|
else
|
|
{
|
|
delete (cd);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!tc)
|
|
{
|
|
if (typeName.compare("*") == 0)
|
|
{
|
|
return NEW TargetZoneChooser(zones, nbzones, card, maxtargets, other);
|
|
}
|
|
else if (typeName.compare("this") == 0)
|
|
{
|
|
return NEW CardTargetChooser(card, card, zones, nbzones);
|
|
}
|
|
else
|
|
{
|
|
tc = NEW TypeTargetChooser(typeName.c_str(), zones, nbzones, card, maxtargets, other);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
((TypeTargetChooser *) tc)->addType(typeName.c_str());
|
|
tc->maxtargets = maxtargets;
|
|
}
|
|
}
|
|
}
|
|
return tc;
|
|
}
|
|
|
|
TargetChooser * TargetChooserFactory::createTargetChooser(MTGCardInstance * card)
|
|
{
|
|
if(!card)
|
|
return NULL;
|
|
int id = card->getId();
|
|
string s = card->spellTargetType;
|
|
if (card->alias)
|
|
{
|
|
id = card->alias;
|
|
//TODO load target as well... ?
|
|
}
|
|
TargetChooser * tc = createTargetChooser(s, card);
|
|
if (tc) return tc;
|
|
//Any target than cannot be defined automatically is determined by its id
|
|
switch (id)
|
|
{
|
|
//Spell
|
|
case 1224: //Spell blast
|
|
{
|
|
#if defined (WIN32) || defined (LINUX)
|
|
DebugTrace("Counter Spell !\n");
|
|
#endif
|
|
return NEW SpellTargetChooser(card);
|
|
}
|
|
//Spell Or Permanent
|
|
case 1282: //ChaosLace
|
|
case 1152: //DeathLace
|
|
case 1358: //PureLace
|
|
case 1227: //ThoughLace
|
|
case 1257: //Lifelace
|
|
{
|
|
return NEW SpellOrPermanentTargetChooser(card);
|
|
}
|
|
//Red Spell or Permanent
|
|
case 1191: //Blue Elemental Blast
|
|
{
|
|
return NEW SpellOrPermanentTargetChooser(card, Constants::MTG_COLOR_RED);
|
|
}
|
|
//Blue Spell or Permanent
|
|
case 1312: //Red Elemental Blast
|
|
{
|
|
return NEW SpellOrPermanentTargetChooser(card, Constants::MTG_COLOR_BLUE);
|
|
}
|
|
//Damage History
|
|
case 1344: //Eye for an Eye
|
|
{
|
|
return NEW DamageTargetChooser(card, -1, 1, RESOLVED_OK);
|
|
}
|
|
default:
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
TargetChooser::TargetChooser(MTGCardInstance * card, int _maxtargets, bool _other) :
|
|
TargetsList()
|
|
{
|
|
forceTargetListReady = 0;
|
|
source = card;
|
|
targetter = card;
|
|
maxtargets = _maxtargets;
|
|
other = _other;
|
|
}
|
|
|
|
//Default targetter : every card can be targetted, unless it is protected from the targetter card
|
|
// For spells that do not "target" a specific card, set targetter to NULL
|
|
bool TargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (!target) return false;
|
|
if (target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
if (other)
|
|
{
|
|
MTGCardInstance * tempcard = card;
|
|
while (tempcard)
|
|
{
|
|
if (tempcard == source) return false;
|
|
tempcard = tempcard->previous;
|
|
}
|
|
}
|
|
if(source && ((source->hasSubtype(Subtypes::TYPE_AURA) || source->hasSubtype(Subtypes::TYPE_EQUIPMENT)) && source->target && source->target == card && source->target->isPhased && targetter->target == card))
|
|
return true;
|
|
//this is kinda cheating but by default we let auras and equipments always continue to target a phased creature.
|
|
else if(card->isPhased)
|
|
return false;
|
|
if (source && targetter && card->isInPlay() && !withoutProtections)
|
|
{
|
|
if (card->has(Constants::SHROUD)) return false;
|
|
if (card->protectedAgainst(targetter)) return false;
|
|
if (card->CantBeTargetby(targetter)) return false;
|
|
if ((targetter->controller() != card->controller()) && card->has(Constants::OPPONENTSHROUD)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
else if (target->typeAsTarget() == TARGET_STACKACTION) return true;
|
|
return false;
|
|
}
|
|
|
|
int TargetChooser::addTarget(Targetable * target)
|
|
{
|
|
if (canTarget(target))
|
|
{
|
|
TargetsList::addTarget(target);
|
|
}
|
|
|
|
return targetsReadyCheck();
|
|
}
|
|
|
|
int TargetChooser::ForceTargetListReady()
|
|
{
|
|
int state = targetsReadyCheck();
|
|
if (state == TARGET_OK)
|
|
{
|
|
forceTargetListReady = 1;
|
|
}
|
|
return forceTargetListReady;
|
|
}
|
|
|
|
int TargetChooser::targetsReadyCheck()
|
|
{
|
|
if (cursor <= 0)
|
|
{
|
|
return TARGET_NOK;
|
|
}
|
|
if (full())
|
|
{
|
|
return TARGET_OK_FULL;
|
|
}
|
|
if (!ready())
|
|
{
|
|
return TARGET_OK_NOT_READY;
|
|
}
|
|
return TARGET_OK;
|
|
}
|
|
|
|
int TargetChooser::targetListSet()
|
|
{
|
|
int state = targetsReadyCheck();
|
|
if (state == TARGET_OK_FULL || forceTargetListReady)
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool TargetChooser::validTargetsExist()
|
|
{
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
Player *p = GameObserver::GetInstance()->players[i];
|
|
if (canTarget(p)) return true;
|
|
MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->exile };
|
|
for (int k = 0; k < 5; k++)
|
|
{
|
|
MTGGameZone * z = zones[k];
|
|
if (targetsZone(z))
|
|
{
|
|
for (int j = 0; j < z->nb_cards; j++)
|
|
{
|
|
if (canTarget(z->cards[j])) return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
//This function always return 1 for now, since the default TargetChooser targets everything
|
|
//In the future we might need to check some of "targetter" settings to take protection into account...
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
a specific Card
|
|
**/
|
|
CardTargetChooser::CardTargetChooser(MTGCardInstance * _card, MTGCardInstance * source, int * _zones, int _nbzones) :
|
|
TargetZoneChooser(_zones, _nbzones, source)
|
|
{
|
|
validTarget = _card;
|
|
}
|
|
|
|
bool CardTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (!target) return false;
|
|
if (target->typeAsTarget() != TARGET_CARD) return false;
|
|
if (!nbzones && !TargetChooser::canTarget(target,withoutProtections)) return false;
|
|
if (nbzones && !TargetZoneChooser::canTarget(target,withoutProtections)) return false;
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
while (card)
|
|
{
|
|
if (card == validTarget) return true;
|
|
card = card->previous;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CardTargetChooser * CardTargetChooser::clone() const
|
|
{
|
|
CardTargetChooser * a = NEW CardTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool CardTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
CardTargetChooser * ctc = dynamic_cast<CardTargetChooser *> (tc);
|
|
if (!ctc)
|
|
return false;
|
|
|
|
if (validTarget != ctc->validTarget) //todo, check also previous cards, see "cantarget"...
|
|
return false;
|
|
|
|
return TargetZoneChooser::equals(tc);
|
|
}
|
|
|
|
|
|
/**
|
|
Choose anything that has a given list of types
|
|
**/
|
|
TypeTargetChooser::TypeTargetChooser(const char * _type, MTGCardInstance * card, int _maxtargets, bool other) :
|
|
TargetZoneChooser(card, _maxtargets, other)
|
|
{
|
|
int id = Subtypes::subtypesList->find(_type);
|
|
nbtypes = 0;
|
|
addType(id);
|
|
int default_zones[] = { MTGGameZone::MY_BATTLEFIELD, MTGGameZone::OPPONENT_BATTLEFIELD };
|
|
init(default_zones, 2);
|
|
}
|
|
|
|
TypeTargetChooser::TypeTargetChooser(const char * _type, int * _zones, int nbzones, MTGCardInstance * card, int _maxtargets,
|
|
bool other) :
|
|
TargetZoneChooser(card, _maxtargets, other)
|
|
{
|
|
int id = Subtypes::subtypesList->find(_type);
|
|
nbtypes = 0;
|
|
addType(id);
|
|
if (nbzones == 0)
|
|
{
|
|
int default_zones[] = { MTGGameZone::MY_BATTLEFIELD, MTGGameZone::OPPONENT_BATTLEFIELD };
|
|
init(default_zones, 2);
|
|
}
|
|
else
|
|
{
|
|
init(_zones, nbzones);
|
|
}
|
|
}
|
|
|
|
void TypeTargetChooser::addType(const char * _type)
|
|
{
|
|
int id = Subtypes::subtypesList->find(_type);
|
|
addType(id);
|
|
}
|
|
|
|
void TypeTargetChooser::addType(int type)
|
|
{
|
|
types[nbtypes] = type;
|
|
nbtypes++;
|
|
}
|
|
|
|
bool TypeTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (!TargetZoneChooser::canTarget(target,withoutProtections)) return false;
|
|
if (target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
for (int i = 0; i < nbtypes; i++)
|
|
{
|
|
if (card->hasSubtype(types[i])) return true;
|
|
if(card->getLCName().size())
|
|
{
|
|
if (Subtypes::subtypesList->find(card->getLCName()) == types[i]) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else if (target->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
|
|
{
|
|
Spell * spell = (Spell *) action;
|
|
MTGCardInstance * card = spell->source;
|
|
for (int i = 0; i < nbtypes; i++)
|
|
{
|
|
if (card->hasSubtype(types[i])) return true;
|
|
if (Subtypes::subtypesList->find(card->name) == types[i]) return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TypeTargetChooser * TypeTargetChooser::clone() const
|
|
{
|
|
TypeTargetChooser * a = NEW TypeTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool TypeTargetChooser ::equals(TargetChooser * tc)
|
|
{
|
|
|
|
TypeTargetChooser * ttc = dynamic_cast<TypeTargetChooser *> (tc);
|
|
if (!ttc)
|
|
return false;
|
|
|
|
if (nbtypes != ttc->nbtypes)
|
|
return false;
|
|
|
|
map<int,int> counts;
|
|
|
|
for (int i = 0; i < nbtypes; ++i)
|
|
{
|
|
counts[types[i]] +=1;
|
|
counts[ttc->types[i]] -=1;
|
|
}
|
|
|
|
for (int i = 0; i < nbtypes; ++i)
|
|
{
|
|
if (counts[types[i]] || counts[ttc->types[i]])
|
|
return false;
|
|
}
|
|
|
|
return TargetZoneChooser::equals(tc);
|
|
}
|
|
|
|
|
|
/**
|
|
A Target Chooser associated to a Card Descriptor object, for fine tuning of targets description
|
|
**/
|
|
DescriptorTargetChooser::DescriptorTargetChooser(CardDescriptor * _cd, MTGCardInstance * card, int _maxtargets, bool other) :
|
|
TargetZoneChooser(card, _maxtargets, other)
|
|
{
|
|
int default_zones[] = { MTGGameZone::MY_BATTLEFIELD, MTGGameZone::OPPONENT_BATTLEFIELD };
|
|
init(default_zones, 2);
|
|
cd = _cd;
|
|
}
|
|
|
|
DescriptorTargetChooser::DescriptorTargetChooser(CardDescriptor * _cd, int * _zones, int nbzones, MTGCardInstance * card,
|
|
int _maxtargets, bool other) :
|
|
TargetZoneChooser(card, _maxtargets, other)
|
|
{
|
|
if (nbzones == 0)
|
|
{
|
|
int default_zones[] = { MTGGameZone::MY_BATTLEFIELD, MTGGameZone::OPPONENT_BATTLEFIELD };
|
|
init(default_zones, 2);
|
|
}
|
|
else
|
|
{
|
|
init(_zones, nbzones);
|
|
}
|
|
cd = _cd;
|
|
}
|
|
|
|
bool DescriptorTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (!TargetZoneChooser::canTarget(target,withoutProtections)) return false;
|
|
if (target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (cd->match(_target))
|
|
return true;
|
|
}
|
|
else if (target->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
|
|
{
|
|
Spell * spell = (Spell *) action;
|
|
MTGCardInstance * card = spell->source;
|
|
if (cd->match(card)) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DescriptorTargetChooser::~DescriptorTargetChooser()
|
|
{
|
|
SAFE_DELETE(cd);
|
|
}
|
|
|
|
DescriptorTargetChooser * DescriptorTargetChooser::clone() const
|
|
{
|
|
DescriptorTargetChooser * a = NEW DescriptorTargetChooser(*this);
|
|
a->cd = NEW CardDescriptor(*cd);
|
|
return a;
|
|
}
|
|
|
|
bool DescriptorTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
DescriptorTargetChooser * dtc = dynamic_cast<DescriptorTargetChooser *> (tc);
|
|
if (!dtc)
|
|
return false;
|
|
|
|
//TODO Descriptors need to have an "equals" method too -_-
|
|
|
|
return TargetZoneChooser::equals(tc);
|
|
}
|
|
|
|
/* TargetzoneChooser targets everything in a given zone */
|
|
TargetZoneChooser::TargetZoneChooser(MTGCardInstance * card, int _maxtargets, bool other) :
|
|
TargetChooser(card, _maxtargets, other)
|
|
{
|
|
init(NULL, 0);
|
|
}
|
|
|
|
TargetZoneChooser::TargetZoneChooser(int * _zones, int _nbzones, MTGCardInstance * card, int _maxtargets, bool other) :
|
|
TargetChooser(card, _maxtargets, other)
|
|
{
|
|
init(_zones, _nbzones);
|
|
}
|
|
|
|
int TargetZoneChooser::init(int * _zones, int _nbzones)
|
|
{
|
|
for (int i = 0; i < _nbzones; i++)
|
|
{
|
|
zones[i] = _zones[i];
|
|
}
|
|
nbzones = _nbzones;
|
|
return nbzones;
|
|
}
|
|
|
|
int TargetZoneChooser::setAllZones()
|
|
{
|
|
int zones[] = { MTGGameZone::ALL_ZONES, };
|
|
|
|
init(zones, 1);
|
|
return 1;
|
|
}
|
|
|
|
bool TargetZoneChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (!TargetChooser::canTarget(target,withoutProtections)) return false;
|
|
if (target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
for (int i = 0; i < nbzones; i++)
|
|
{
|
|
if (zones[i] == MTGGameZone::ALL_ZONES) return true;
|
|
if (MTGGameZone::intToZone(zones[i], source, card)->hasCard(card)) return true;
|
|
}
|
|
}
|
|
else if (target->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
DebugTrace("CHECKING INTERRUPTIBLE\n");
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
|
|
{
|
|
Spell * spell = (Spell *) action;
|
|
MTGCardInstance * card = spell->source;
|
|
for (int i = 0; i < nbzones; i++)
|
|
if (MTGGameZone::intToZone(zones[i], source, card)->hasCard(card)) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TargetZoneChooser::targetsZone(MTGGameZone * z)
|
|
{
|
|
for (int i = 0; i < nbzones; i++)
|
|
if (MTGGameZone::intToZone(zones[i], source) == z) return true;
|
|
return false;
|
|
}
|
|
bool TargetZoneChooser::targetsZone(MTGGameZone * z,MTGCardInstance * mSource)
|
|
{
|
|
if(mSource)
|
|
source = mSource;
|
|
for (int i = 0; i < nbzones; i++)
|
|
if (MTGGameZone::intToZone(zones[i], source) == z) return true;
|
|
return false;
|
|
}
|
|
|
|
TargetZoneChooser * TargetZoneChooser::clone() const
|
|
{
|
|
TargetZoneChooser * a = NEW TargetZoneChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool TargetZoneChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
TargetZoneChooser * tzc = dynamic_cast<TargetZoneChooser *> (tc);
|
|
if (!tzc)
|
|
return false;
|
|
|
|
if (nbzones!= tzc->nbzones)
|
|
return false;
|
|
|
|
map<int,int> counts;
|
|
|
|
for (int i = 0; i < nbzones; ++i)
|
|
{
|
|
counts[zones[i]] +=1;
|
|
counts[tzc->zones[i]] -=1;
|
|
}
|
|
|
|
for (int i = 0; i < nbzones; ++i)
|
|
{
|
|
if (counts[zones[i]] || counts[tzc->zones[i]])
|
|
return false;
|
|
}
|
|
|
|
//TODO: ALL_ZONES should be equivalent to something actually targetting all zones...
|
|
|
|
return TargetChooser::equals(tc);
|
|
}
|
|
|
|
/* Player Target */
|
|
PlayerTargetChooser::PlayerTargetChooser(MTGCardInstance * card, int _maxtargets, Player *p) :
|
|
TargetChooser(card, _maxtargets), p(p)
|
|
{
|
|
}
|
|
|
|
bool PlayerTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (source && targetter && (targetter->controller() != targetter->controller()->opponent())
|
|
&& (targetter->controller()->opponent()->game->inPlay->hasAbility(Constants::CONTROLLERSHROUD))
|
|
&& targetter->controller() != target) return false;
|
|
if (source && targetter && (targetter->controller() == targetter->controller())
|
|
&& (targetter->controller()->opponent()->game->inPlay->hasAbility(Constants::PLAYERSHROUD))
|
|
&& targetter->controller()->opponent() == target) return false;
|
|
if (source && targetter && (targetter->controller() == targetter->controller())
|
|
&& (targetter->controller()->game->inPlay->hasAbility(Constants::PLAYERSHROUD)) && targetter->controller()
|
|
== target) return false;
|
|
return (target->typeAsTarget() == TARGET_PLAYER) && (!p || p == (Player*) target);
|
|
}
|
|
|
|
PlayerTargetChooser* PlayerTargetChooser::clone() const
|
|
{
|
|
PlayerTargetChooser * a = NEW PlayerTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool PlayerTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
PlayerTargetChooser * ptc = dynamic_cast<PlayerTargetChooser *> (tc);
|
|
if (!ptc)
|
|
return false;
|
|
|
|
if (p != ptc->p)
|
|
return false;
|
|
|
|
return TargetChooser::equals(tc);
|
|
}
|
|
|
|
/*Damageable Target */
|
|
bool DamageableTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
if (source && targetter && (targetter->controller() != targetter->controller()->opponent())
|
|
&& (targetter->controller()->opponent()->game->inPlay->hasAbility(Constants::CONTROLLERSHROUD))
|
|
&& targetter->controller() != target) return false;
|
|
if (source && targetter && (targetter->controller() == targetter->controller())
|
|
&& (targetter->controller()->opponent()->game->inPlay->hasAbility(Constants::PLAYERSHROUD))
|
|
&& targetter->controller()->opponent() == target) return false;
|
|
if (source && targetter && (targetter->controller() == targetter->controller())
|
|
&& (targetter->controller()->game->inPlay->hasAbility(Constants::PLAYERSHROUD)) && targetter->controller()
|
|
== target) return false;
|
|
if (target->typeAsTarget() == TARGET_PLAYER)
|
|
{
|
|
return true;
|
|
}
|
|
return TypeTargetChooser::canTarget(target,withoutProtections);
|
|
}
|
|
|
|
DamageableTargetChooser* DamageableTargetChooser::clone() const
|
|
{
|
|
DamageableTargetChooser * a = NEW DamageableTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool DamageableTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
DamageableTargetChooser * dtc = dynamic_cast<DamageableTargetChooser *> (tc);
|
|
if (!dtc)
|
|
return false;
|
|
|
|
return TypeTargetChooser::equals(tc);
|
|
}
|
|
|
|
/*Spell */
|
|
|
|
SpellTargetChooser::SpellTargetChooser(MTGCardInstance * card, int _color, int _maxtargets, bool other) :
|
|
TargetChooser(card, _maxtargets, other)
|
|
{
|
|
color = _color;
|
|
}
|
|
|
|
bool SpellTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
MTGCardInstance * card = NULL;
|
|
if (target->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
|
|
{
|
|
Spell * spell = (Spell *) action;
|
|
card = spell->source;
|
|
if (card && (color == -1 || card->hasColor(color))) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
SpellTargetChooser* SpellTargetChooser::clone() const
|
|
{
|
|
SpellTargetChooser * a = NEW SpellTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool SpellTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
SpellTargetChooser * stc = dynamic_cast<SpellTargetChooser *> (tc);
|
|
if (!stc)
|
|
return false;
|
|
|
|
if (color != stc->color)
|
|
return false;
|
|
|
|
return TargetChooser::equals(tc);
|
|
}
|
|
|
|
/*Spell or Permanent */
|
|
SpellOrPermanentTargetChooser::SpellOrPermanentTargetChooser(MTGCardInstance * card, int _color, int _maxtargets, bool other) :
|
|
TargetZoneChooser(card, _maxtargets, other)
|
|
{
|
|
int default_zones[] = { MTGGameZone::MY_BATTLEFIELD, MTGGameZone::OPPONENT_BATTLEFIELD };
|
|
init(default_zones, 2);
|
|
color = _color;
|
|
}
|
|
|
|
bool SpellOrPermanentTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
MTGCardInstance * card = NULL;
|
|
if (target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
card = (MTGCardInstance *) target;
|
|
if (color == -1 || card->hasColor(color)) return TargetZoneChooser::canTarget(target,withoutProtections);
|
|
}
|
|
else if (target->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
|
|
{
|
|
Spell * spell = (Spell *) action;
|
|
card = spell->source;
|
|
if (card && (color == -1 || card->hasColor(color))) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SpellOrPermanentTargetChooser* SpellOrPermanentTargetChooser::clone() const
|
|
{
|
|
SpellOrPermanentTargetChooser * a = NEW SpellOrPermanentTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool SpellOrPermanentTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
SpellOrPermanentTargetChooser * sptc = dynamic_cast<SpellOrPermanentTargetChooser *> (tc);
|
|
if (!sptc)
|
|
return false;
|
|
|
|
if (color != sptc->color)
|
|
return false;
|
|
|
|
return TargetZoneChooser::equals(tc);
|
|
}
|
|
|
|
/*Damage */
|
|
DamageTargetChooser::DamageTargetChooser(MTGCardInstance * card, int _color, int _maxtargets, int _state) :
|
|
TargetChooser(card, _maxtargets)
|
|
{
|
|
color = _color;
|
|
state = _state;
|
|
}
|
|
|
|
bool DamageTargetChooser::canTarget(Targetable * target,bool withoutProtections)
|
|
{
|
|
MTGCardInstance * card = NULL;
|
|
if (target->typeAsTarget() == TARGET_STACKACTION)
|
|
{
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (action->type == ACTION_DAMAGE && (action->state == state || state == -1))
|
|
{
|
|
Damage * damage = (Damage *) action;
|
|
card = damage->source;
|
|
if (card && (color == -1 || card->hasColor(color))) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DamageTargetChooser* DamageTargetChooser::clone() const
|
|
{
|
|
DamageTargetChooser * a = NEW DamageTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool DamageTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
DamageTargetChooser * dtc = dynamic_cast<DamageTargetChooser *> (tc);
|
|
if (!dtc)
|
|
return false;
|
|
|
|
if (color != dtc->color || state != dtc->state)
|
|
return false;
|
|
|
|
return TargetChooser::equals(tc);
|
|
}
|
|
|
|
TriggerTargetChooser::TriggerTargetChooser(int _triggerTarget)
|
|
{
|
|
triggerTarget = _triggerTarget;
|
|
target = NULL;
|
|
}
|
|
|
|
bool TriggerTargetChooser::targetsZone(MTGGameZone * z)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool TriggerTargetChooser::canTarget(Targetable * _target,bool withoutProtections)
|
|
{
|
|
if (_target == target) return true;
|
|
return false;
|
|
}
|
|
|
|
TriggerTargetChooser * TriggerTargetChooser::clone() const
|
|
{
|
|
TriggerTargetChooser * a = NEW TriggerTargetChooser(*this);
|
|
return a;
|
|
}
|
|
|
|
bool TriggerTargetChooser::equals(TargetChooser * tc)
|
|
{
|
|
|
|
TriggerTargetChooser * ttc = dynamic_cast<TriggerTargetChooser *> (tc);
|
|
if (!ttc)
|
|
return false;
|
|
|
|
if (triggerTarget != ttc->triggerTarget)
|
|
return false;
|
|
|
|
return TargetChooser::equals(tc);
|
|
} |