Files
wagic/projects/mtg/src/TargetChooser.cpp
wagic.the.homebrew@gmail.com ee3286ff2a Erwan
-Fix issue 206 (rain of filth)
2009-12-13 09:33:51 +00:00

683 lines
21 KiB
C++

#include "../include/config.h"
#include "../include/TargetChooser.h"
#include "../include/CardDescriptor.h"
#include "../include/MTGGameZones.h"
#include "../include/GameObserver.h"
#include "../include/Subtypes.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("other ");
if (found == 0){
other = true;
s=s.substr(6);
}
found = s.find("player");
if (found != string::npos){
int maxtargets = 1;
unsigned int 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;
//Graveyards
if(zoneName.compare("graveyard") == 0){
zones[nbzones] = MTGGameZone::MY_GRAVEYARD;
nbzones++;
zones[nbzones] = MTGGameZone::OPPONENT_GRAVEYARD;
}else if(zoneName.compare("battlefield") == 0 || zoneName.compare("inplay") == 0){
zones[nbzones] = MTGGameZone::MY_BATTLEFIELD;
nbzones++;
zones[nbzones] = MTGGameZone::OPPONENT_BATTLEFIELD;
}else if(zoneName.compare("stack") == 0){
zones[nbzones] = MTGGameZone::MY_STACK;
nbzones++;
zones[nbzones] = MTGGameZone::OPPONENT_STACK;
}else{
int zone = MTGGameZone::zoneStringToId(zoneName);
if (zone) zones[nbzones] = zone;
}
nbzones++;
}
}else{
s1 = s;
nbzones = 2;
zones[0]= MTGGameZone::MY_BATTLEFIELD;
zones[1]= MTGGameZone::OPPONENT_BATTLEFIELD;
}
TargetChooser * tc = NULL;
int maxtargets = 1;
CardDescriptor * cd = NULL;
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()){
unsigned int 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);
}
//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);
}
}else{
int attributefound = 0;
//Colors
for (int cid = 0; cid < Constants::MTG_NB_COLORS; cid++){
if (attribute.find(Constants::MTGColorStrings[cid]) != string::npos){
attributefound = 1;
if (minus){
cd->colors[cid] = -1;
}else{
cd->colors[cid] = 1;
}
}
}
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->basicAbilities[j] = -1;
}else{
cd->basicAbilities[j] = 1;
}
}
}
}
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){
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)
OutputDebugString ("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){
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 && targetter && card->isInPlay() && (card->has(Constants::SHROUD)|| card->protectedAgainst(targetter) )) return false;
if (source && targetter && card->isInPlay() && (targetter->controller() != card->controller()) && (card->has(Constants::OPPONENTSHROUD) || card->protectedAgainst(targetter))) 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;
}
/**
a specific Card
**/
CardTargetChooser::CardTargetChooser(MTGCardInstance * _card, MTGCardInstance * source,int * _zones, int _nbzones):TargetZoneChooser(_zones,_nbzones,source){
validTarget = _card;
}
bool CardTargetChooser::canTarget(Targetable * target ){
if (!target) return false;
if (target->typeAsTarget() != TARGET_CARD) return false;
if (!nbzones && !TargetChooser::canTarget(target)) return false;
if (nbzones && !TargetZoneChooser::canTarget(target)) 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;
}
/**
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){
if (!TargetZoneChooser::canTarget(target)) 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 (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;
}
/**
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){
if (!TargetZoneChooser::canTarget(target)) 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;
}
/**
Choose a creature
**/
CreatureTargetChooser::CreatureTargetChooser( MTGCardInstance * card, int _maxtargets, bool other):TargetZoneChooser(card, _maxtargets, other){
int default_zones[] = {MTGGameZone::MY_BATTLEFIELD, MTGGameZone::OPPONENT_BATTLEFIELD};
init(default_zones,2);
maxpower= -1;
maxtoughness= -1;
}
CreatureTargetChooser::CreatureTargetChooser(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);
}
maxpower = -1;
maxtoughness= -1;
}
bool CreatureTargetChooser::canTarget(Targetable * target){
if (!TargetZoneChooser::canTarget(target)) return false;
if (target->typeAsTarget() == TARGET_CARD){
MTGCardInstance * card = (MTGCardInstance *) target;
if (maxpower != -1 && card->power > maxpower) return false;
if (maxtoughness != -1 && card->toughness > maxtoughness) return false;
return card->isCreature();
}
return false;
}
CreatureTargetChooser * CreatureTargetChooser::clone() const{
CreatureTargetChooser * a = NEW CreatureTargetChooser(*this);
return a;
}
/* 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::MY_BATTLEFIELD,
MTGGameZone::MY_EXILE,
MTGGameZone::MY_GRAVEYARD,
MTGGameZone::MY_HAND,
MTGGameZone::MY_LIBRARY,
MTGGameZone::MY_STACK,
MTGGameZone::OPPONENT_BATTLEFIELD,
MTGGameZone::OPPONENT_EXILE,
MTGGameZone::OPPONENT_GRAVEYARD,
MTGGameZone::OPPONENT_HAND,
MTGGameZone::OPPONENT_LIBRARY,
MTGGameZone::OPPONENT_STACK
};
init(zones,12);
return 1;
}
bool TargetZoneChooser::canTarget(Targetable * target){
if (!TargetChooser::canTarget(target)) return false;
if (target->typeAsTarget() == TARGET_CARD){
MTGCardInstance * card = (MTGCardInstance *) target;
for (int i = 0; i<nbzones; i++)
if (MTGGameZone::intToZone(zones[i],source,card)->hasCard(card)) return true;
}else if (target->typeAsTarget() == TARGET_STACKACTION){
OutputDebugString ("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;
}
TargetZoneChooser * TargetZoneChooser::clone() const{
TargetZoneChooser * a = NEW TargetZoneChooser(*this);
return a;
}
/* Player Target */
PlayerTargetChooser::PlayerTargetChooser(MTGCardInstance * card, int _maxtargets, Player *p):TargetChooser(card, _maxtargets), p(p){
}
bool PlayerTargetChooser::canTarget(Targetable * target){
return (target->typeAsTarget() == TARGET_PLAYER) && (!p || p == (Player*)target);
}
PlayerTargetChooser* PlayerTargetChooser::clone() const{
PlayerTargetChooser * a = NEW PlayerTargetChooser(*this);
return a;
}
/*Damageable Target */
bool DamageableTargetChooser::canTarget(Targetable * target){
if (target->typeAsTarget() == TARGET_PLAYER){
return true;
}
return CreatureTargetChooser::canTarget(target);
}
DamageableTargetChooser* DamageableTargetChooser::clone() const{
DamageableTargetChooser * a = NEW DamageableTargetChooser(*this);
return a;
}
/*Spell */
SpellTargetChooser::SpellTargetChooser(MTGCardInstance * card,int _color, int _maxtargets, bool other ):TargetChooser(card, _maxtargets, other){
color = _color;
}
bool SpellTargetChooser::canTarget(Targetable * target){
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;
}
/*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){
MTGCardInstance * card = NULL;
if (target->typeAsTarget() == TARGET_CARD){
card = (MTGCardInstance *) target;
if (color == -1 || card->hasColor(color)) return TargetZoneChooser::canTarget(target);
}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;
}
/*Damage */
DamageTargetChooser::DamageTargetChooser(MTGCardInstance * card,int _color, int _maxtargets, int _state):TargetChooser(card, _maxtargets){
color = _color;
state = _state;
}
bool DamageTargetChooser::canTarget(Targetable * target){
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;
}