• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

WagicProject / wagic / 603050901

pending completion
603050901

push

travis-ci

EduardoMunozGomez
Bug fixes on primitives

Jace, Wielder of Mysteries
Chain Reaction
Rona, Tolarian Obliterator
Seize the Soul
Graveyard Marshal
Varina, Lich Queen

20314 of 53124 relevant lines covered (38.24%)

46567.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.27
/projects/mtg/src/MTGAbility.cpp
1
#include "PrecompiledHeader.h"
2

3
#include "MTGAbility.h"
4
#include "ManaCost.h"
5
#include "MTGGameZones.h"
6
#include "AllAbilities.h"
7
#include "Damage.h"
8
#include "TargetChooser.h"
9
#include "CardGui.h"
10
#include "MTGDeck.h"
11
#include "Translate.h"
12
#include "ThisDescriptor.h"
13
#include "ExtraCost.h"
14
#include "MTGRules.h"
15
#include "AbilityParser.h"
16

17

18
//Used for Lord/This parsing
19
const string kLordKeywords[] = { "lord(", "foreach(", "aslongas(", "teach(", "all(" };
2✔
20
const size_t kLordKeywordsCount = 5;
21

22
const string kThisKeywords[] = { "this(", "thisforeach(","while(", };
2✔
23
const size_t kThisKeywordsCount = 3;
24

25

26
// Used for the maxCast/maxPlay ability parsing
27
const string kMaxCastKeywords[] = { "maxplay(", "maxcast("};
2✔
28
const int kMaxCastZones[] = { MTGGameZone::BATTLEFIELD, MTGGameZone::STACK};
29
const size_t kMaxCastKeywordsCount = 2;
30

31
//Used for alternate Costs parsing
32
const string kAlternateCostKeywords[] = 
2✔
33
{ 
34
    "notpaid",
35
    "paidmana",
36
    "kicker", 
37
    "alternative", 
38
    "buyback", 
39
    "flashback", 
40
    "retrace", 
41
    "facedown",
42
    "suspended",
43
    "overload",
44
    "bestow"
45
}; 
1✔
46
const int kAlternateCostIds[] = 
47
{
48
    ManaCost::MANA_UNPAID,
49
    ManaCost::MANA_PAID,
50
    ManaCost::MANA_PAID_WITH_KICKER,
51
    ManaCost::MANA_PAID_WITH_ALTERNATIVE,
52
    ManaCost::MANA_PAID_WITH_BUYBACK, 
53
    ManaCost::MANA_PAID_WITH_FLASHBACK,
54
    ManaCost::MANA_PAID_WITH_RETRACE,
55
    ManaCost::MANA_PAID_WITH_MORPH,
56
    ManaCost::MANA_PAID_WITH_SUSPEND,
57
    ManaCost::MANA_PAID_WITH_OVERLOAD,
58
    ManaCost::MANA_PAID_WITH_BESTOW
59
};
60

61
//Used for "dynamic ability" parsing
62
const string kDynamicSourceKeywords[] = {"source", "mytgt", "myself", "myfoe"};
2✔
63
const int kDynamicSourceIds[] = { AADynamic::DYNAMIC_SOURCE_AMOUNT, AADynamic::DYNAMIC_MYTGT_AMOUNT, AADynamic::DYNAMIC_MYSELF_AMOUNT, AADynamic::DYNAMIC_MYFOE_AMOUNT};
64

65
const string kDynamicTypeKeywords[] = {"power", "toughness", "manacost", "colors", "age", "charge", "oneonecounters", "thatmuch"};
2✔
66
const int kDynamicTypeIds[] = {
67
    AADynamic::DYNAMIC_ABILITY_TYPE_POWER, AADynamic::DYNAMIC_ABILITY_TYPE_TOUGHNESS, AADynamic::DYNAMIC_ABILITY_TYPE_MANACOST, 
68
    AADynamic::DYNAMIC_ABILITY_TYPE_COLORS,  AADynamic::DYNAMIC_ABILITY_TYPE_AGE, AADynamic::DYNAMIC_ABILITY_TYPE_CHARGE, AADynamic::DYNAMIC_ABILITY_TYPE_ONEONECOUNTERS,
69
    AADynamic::DYNAMIC_ABILITY_TYPE_THATMUCH
70
};
71

72
const string kDynamicEffectKeywords[] = {"strike", "draw", "lifeloss", "lifegain", "pumppow", "pumptough", "pumpboth", "deplete", "countersoneone" };
2✔
73
const int kDynamicEffectIds[] = { 
74
    AADynamic::DYNAMIC_ABILITY_EFFECT_STRIKE, AADynamic::DYNAMIC_ABILITY_EFFECT_DRAW, AADynamic::DYNAMIC_ABILITY_EFFECT_LIFELOSS, AADynamic::DYNAMIC_ABILITY_EFFECT_LIFEGAIN,
75
    AADynamic::DYNAMIC_ABILITY_EFFECT_PUMPPOWER, AADynamic::DYNAMIC_ABILITY_EFFECT_PUMPTOUGHNESS,  AADynamic::DYNAMIC_ABILITY_EFFECT_PUMPBOTH, AADynamic::DYNAMIC_ABILITY_EFFECT_DEPLETE,
76
    AADynamic::DYNAMIC_ABILITY_EFFECT_COUNTERSONEONE
77
};
78

79

80
const string kDynamicWhoKeywords[] = {"eachother", "itself", "targetcontroller", "targetopponent", "tosrc", "srccontroller", "srcopponent" , "abilitycontroller" };
2✔
81
const int kDynamicWhoIds[] = { 
82
     AADynamic::DYNAMIC_ABILITY_WHO_EACHOTHER, AADynamic::DYNAMIC_ABILITY_WHO_ITSELF, AADynamic::DYNAMIC_ABILITY_WHO_TARGETCONTROLLER, AADynamic::DYNAMIC_ABILITY_WHO_TARGETOPPONENT,
83
     AADynamic::DYNAMIC_ABILITY_WHO_TOSOURCE,  AADynamic::DYNAMIC_ABILITY_WHO_SOURCECONTROLLER,  AADynamic::DYNAMIC_ABILITY_WHO_SOURCEOPPONENT, AADynamic::DYNAMIC_ABILITY_WHO_ABILITYCONTROLLER
84
};
85

86
int MTGAbility::allowedToCast(MTGCardInstance * card,Player * player)
2,264✔
87
{
88
    AbilityFactory af(game);
4,528✔
89
    return af.parseCastRestrictions(card,player,card->getRestrictions());
4,528✔
90
}
91

92
int MTGAbility::allowedToAltCast(MTGCardInstance * card,Player * player)
47✔
93
{
94
    AbilityFactory af(game);
94✔
95
    return af.parseCastRestrictions(card,player,card->getOtherRestrictions());
94✔
96
}
97

98
int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * player,string restrictions)
21,913✔
99
{
100
    vector <string> restriction = split(restrictions, ',');
43,826✔
101
    AbilityFactory af(observer);
43,826✔
102
    int cPhase = observer->getCurrentGamePhase();
21,913✔
103
    for(unsigned int i = 0;i < restriction.size();i++)
21,992✔
104
    {
105
        int checkPhaseBased = parseRestriction(restriction[i]);
19,629✔
106
        switch (checkPhaseBased)
19,629✔
107
        {
108
        case MTGAbility::PLAYER_TURN_ONLY:
109
            if (player != observer->currentPlayer)
×
110
                return 0;
×
111
            break;
×
112
        case MTGAbility::OPPONENT_TURN_ONLY:
113
            if (player == observer->currentPlayer)
×
114
                return 0;
×
115
            break;
×
116
        case MTGAbility::AS_SORCERY:
117
            if (player != observer->currentPlayer)
×
118
                return 0;
×
119
            if (cPhase != MTG_PHASE_FIRSTMAIN && cPhase != MTG_PHASE_SECONDMAIN)
×
120
                return 0;
×
121
            break;
×
122
        }
123
        if (checkPhaseBased >= MTGAbility::MY_BEFORE_BEGIN && checkPhaseBased <= MTGAbility::MY_AFTER_EOT)
19,629✔
124
        {
125
            if (player != observer->currentPlayer)
×
126
                return 0;
×
127
            if (cPhase != checkPhaseBased - MTGAbility::MY_BEFORE_BEGIN + MTG_PHASE_BEFORE_BEGIN)
×
128
                return 0;
×
129
        }
130
        if (checkPhaseBased >= MTGAbility::OPPONENT_BEFORE_BEGIN && checkPhaseBased <= MTGAbility::OPPONENT_AFTER_EOT)
19,629✔
131
        {
132
            if (player == observer->currentPlayer)
×
133
                return 0;
×
134
            if (cPhase != checkPhaseBased - MTGAbility::OPPONENT_BEFORE_BEGIN + MTG_PHASE_BEFORE_BEGIN)
×
135
                return 0;
×
136
        }
137
        if (checkPhaseBased >= MTGAbility::BEFORE_BEGIN && checkPhaseBased <= MTGAbility::AFTER_EOT)
19,629✔
138
        {
139
            if (cPhase != checkPhaseBased - MTGAbility::BEFORE_BEGIN + MTG_PHASE_BEFORE_BEGIN)
×
140
                return 0;
×
141
        }
142
        
143
        size_t typeRelated = restriction[i].find("type(");
19,629✔
144
        size_t seenType = restriction[i].find("lastturn(");
19,629✔
145
        size_t seenRelated = restriction[i].find("lastturn(");
19,629✔
146
        if(seenRelated == string::npos)
19,629✔
147
            seenRelated = restriction[i].find("thisturn(");
19,629✔
148
        size_t compRelated = restriction[i].find("compare(");
19,629✔
149

150
        size_t check = 0;
19,629✔
151
        if(typeRelated != string::npos || seenRelated != string::npos || compRelated != string::npos)
19,629✔
152
        {
153
            int firstAmount = 0;
19,603✔
154
            int secondAmount = 0;
19,603✔
155
            int mod=0;
19,603✔
156
            string rtc;
19,661✔
157
            vector<string> comparasion = split(restriction[i],'~');
19,661✔
158
            if(comparasion.size() != 3)
19,603✔
159
                return 0;//it was incorrectly coded, user should proofread card code.
×
160
            bool less = comparasion[1].find("lessthan") != string::npos;
19,603✔
161
            bool more = comparasion[1].find("morethan") != string::npos;
19,603✔
162
            bool equal = comparasion[1].find("equalto") != string::npos;
19,603✔
163
            for(unsigned int i = 0; i < comparasion.size(); i++)
78,412✔
164
            {
165
                check = comparasion[i].find("type(");
58,809✔
166
                if(check == string::npos)
58,809✔
167
                    check = comparasion[i].find("lastturn(");
39,215✔
168
                if(check == string::npos)
58,809✔
169
                    check = comparasion[i].find("thisturn(");
39,215✔
170
                if(check == string::npos)
58,809✔
171
                    check = comparasion[i].find("compare(");
39,215✔
172
                if( check != string::npos)
58,809✔
173
                {
174
                    size_t end = 0;
19,607✔
175
                    size_t foundType = comparasion[i].find("type(");
19,607✔
176
                    size_t foundComp = comparasion[i].find("compare(");
19,607✔
177
                    size_t foundSeen = comparasion[i].find("lastturn(");
19,607✔
178
                    if(foundSeen == string::npos)
19,607✔
179
                        foundSeen = comparasion[i].find("thisturn(");
19,607✔
180
                    if (foundType != string::npos)
19,607✔
181
                    {
182
                        end = comparasion[i].find(")", foundType);
19,594✔
183
                        rtc = comparasion[i].substr(foundType + 5, end - foundType - 5).c_str();
19,594✔
184
                        TargetChooserFactory tcf(observer);
19,594✔
185
                        TargetChooser * ttc = tcf.createTargetChooser(rtc,card);
19,594✔
186
                        mod = atoi(comparasion[i].substr(end+1).c_str());
19,594✔
187
                        bool withoutProtections = true;
19,594✔
188
                        if(i == 2)
19,594✔
189
                        {
190
                            secondAmount = ttc->countValidTargets(withoutProtections);
3✔
191
                            secondAmount += mod;
3✔
192
                        }
193
                        else
194
                        {
195
                            firstAmount = ttc->countValidTargets(withoutProtections);
19,591✔
196
                            firstAmount += mod;
19,591✔
197
                        }
198

199
                        SAFE_DELETE(ttc);
19,594✔
200
                    }
201
                    if (foundComp != string::npos)
19,607✔
202
                    {
203
                        end = comparasion[i].find(")", foundComp);
585✔
204
                        rtc = comparasion[i].substr(foundComp + 8, end - foundComp - 8).c_str();
585✔
205
                        mod = atoi(comparasion[i].substr(end+1).c_str());
585✔
206
                        if(i == 2)
585✔
207
                        {
208
                            WParsedInt * newAmount = NEW WParsedInt(rtc,card);
1✔
209
                            secondAmount = newAmount->getValue();
1✔
210
                            secondAmount += mod;
1✔
211
                            SAFE_DELETE(newAmount);
1✔
212
                        }
213
                        else
214
                        {
215
                            WParsedInt * newAmount = NEW WParsedInt(rtc,card);
584✔
216
                            firstAmount = newAmount->getValue();
584✔
217
                            firstAmount += mod;
584✔
218
                            SAFE_DELETE(newAmount);
584✔
219
                        }
220
                    }
221
                    if (foundSeen != string::npos)
19,607✔
222
                    {
223
                        end = comparasion[i].find(")", foundSeen);
×
224
                        rtc = comparasion[i].substr(foundSeen + 9, end - foundSeen - 9).c_str();
×
225
                        mod = atoi(comparasion[i].substr(end+1).c_str());
×
226

227
                        TargetChooserFactory tcf(observer);
×
228
                        TargetChooser * stc = tcf.createTargetChooser(rtc,card);
×
229
                        for (int w = 0; w < 2; ++w)
×
230
                        {
231
                            Player *p = observer->players[w];
×
232
                            MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->stack, p->game->exile, p->game->commandzone, p->game->sideboard, p->game->reveal };
×
233
                            for (int k = 0; k < 9; k++)
×
234
                            {
235
                                MTGGameZone * z = zones[k];
×
236
                                if (stc->targetsZone(z))
×
237
                                {
238
                                    if(i == 2)
×
239
                                    {
240
                                        secondAmount += seenType != string::npos ? z->seenLastTurn(rtc,Constants::CAST_ALL):z->seenThisTurn(rtc,Constants::CAST_ALL);
×
241

242
                                    }
243
                                    else
244
                                    {
245
                                        firstAmount += seenType != string::npos ? z->seenLastTurn(rtc,Constants::CAST_ALL):z->seenThisTurn(rtc,Constants::CAST_ALL);
×
246

247
                                    }
248
                                }
249
                            }
250
                        }
251
                        i == 2 ? secondAmount += mod:firstAmount += mod;
×
252
                        SAFE_DELETE(stc);
×
253
                    }
254
                }
255
                else if (i == 2)
39,202✔
256
                {
257
                    WParsedInt * secondA = NEW WParsedInt(comparasion[2].c_str(),NULL,card);
19,599✔
258
                    secondAmount = secondA->getValue();
19,599✔
259
                    SAFE_DELETE(secondA);
19,599✔
260
                }
261
            }
262
            if(firstAmount < secondAmount && !less && !more && !equal)
19,603✔
263
                return 0;
×
264
            if(equal && firstAmount != secondAmount)
19,603✔
265
                return 0;
6✔
266
            if(less && firstAmount >= secondAmount)
19,597✔
267
                return 0;
7✔
268
            if(more  && firstAmount <= secondAmount)
19,590✔
269
                return 0;
19,532✔
270
        }
271
        check = restriction[i].find("turn:");
84✔
272
        if(check != string::npos)
84✔
273
        {
274
            int Turn = 0;
×
275
            size_t start = restriction[i].find(":", check);
×
276
            size_t end = restriction[i].find(" ", check);
×
277
            WParsedInt* parser = NEW WParsedInt(restriction[i].substr(start + 1, end - start - 1), card);
×
278
            if(parser){
×
279
                Turn = parser->intValue;
×
280
                SAFE_DELETE(parser);
×
281
            }
282
            if(observer->turn < Turn-1)
×
283
                return 0;
×
284
        }
285
        check = restriction[i].find("casted a spell");
84✔
286
        if(check != string::npos)
84✔
287
        {
288
            if(player->game->stack->seenThisTurn("*", Constants::CAST_ALL) < 1)
×
289
                return 0;
×
290
        }
291
        check = restriction[i].find("casted(");
84✔
292
        if(check != string::npos)
84✔
293
        {
294
            size_t end = restriction[i].find(")",check);
3✔
295
            string tc = restriction[i].substr(check + 7,end - check - 7);
4✔
296
            if(tc.find("|mystack") != string::npos)
3✔
297
            {
298
                if(player->game->stack->seenThisTurn(tc, Constants::CAST_ALL) < 1)
×
299
                    return 0;
×
300
            }
301
            else if(tc.find("|opponentstack") != string::npos)
3✔
302
            {
303
                if(player->opponent()->game->stack->seenThisTurn(tc, Constants::CAST_ALL) < 1)
×
304
                    return 0;
×
305
            }
306
            else if(tc.find("this") != string::npos)
3✔
307
            {
308
                int count = 0;
3✔
309
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
6✔
310
                {
311
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
3✔
312
                    if(stackCard->next && stackCard->next == card)
3✔
313
                        count++;
1✔
314
                    if(stackCard == card)
3✔
315
                        count++;
×
316
                }
317
                if(!count)
3✔
318
                    return 0;
2✔
319
            }
320
        }
321
        check = restriction[i].find("gravecast");
82✔
322
        if(check != string::npos)
82✔
323
        {
324
                int count = 0;
×
325
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
×
326
                {
327
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
×
328
                    if(stackCard->next && stackCard->next == card && (card->previousZone == card->controller()->game->graveyard||card->previousZone == card->controller()->opponent()->game->graveyard))
×
329
                        count++;
×
330
                    if(stackCard == card && (card->previousZone == card->controller()->game->graveyard||card->previousZone == card->controller()->opponent()->game->graveyard))
×
331
                        count++;
×
332
                }
333
                if(!count)
×
334
                    return 0;
×
335
        }
336
        check = restriction[i].find("librarycast");
82✔
337
        if(check != string::npos)
82✔
338
        {
339
                int count = 0;
×
340
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
×
341
                {
342
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
×
343
                    if(stackCard->next && stackCard->next == card && (card->previousZone == card->controller()->game->library||card->previousZone == card->controller()->opponent()->game->library))
×
344
                        count++;
×
345
                    if(stackCard == card && (card->previousZone == card->controller()->game->library||card->previousZone == card->controller()->opponent()->game->library))
×
346
                        count++;
×
347
                }
348
                if(!count)
×
349
                    return 0;
×
350
        }
351
        check = restriction[i].find("exilecast");
82✔
352
        if(check != string::npos)
82✔
353
        {
354
                int count = 0;
×
355
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
×
356
                {
357
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
×
358
                    if(stackCard->next && stackCard->next == card && (card->previousZone == card->controller()->game->exile||card->previousZone == card->controller()->opponent()->game->exile))
×
359
                        count++;
×
360
                    if(stackCard == card && (card->previousZone == card->controller()->game->exile||card->previousZone == card->controller()->opponent()->game->exile))
×
361
                        count++;
×
362
                }
363
                if(!count)
×
364
                    return 0;
×
365
        }
366
        check = restriction[i].find("sidecast");
82✔
367
        if(check != string::npos)
82✔
368
        {
369
                int count = 0;
×
370
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
×
371
                {
372
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
×
373
                    if(stackCard->next && stackCard->next == card && (card->previousZone == card->controller()->game->sideboard||card->previousZone == card->controller()->opponent()->game->sideboard))
×
374
                        count++;
×
375
                    if(stackCard == card && (card->previousZone == card->controller()->game->sideboard||card->previousZone == card->controller()->opponent()->game->sideboard))
×
376
                        count++;
×
377
                }
378
                if(!count)
×
379
                    return 0;
×
380
        }
381
        check = restriction[i].find("commandzonecast");
82✔
382
        if(check != string::npos)
82✔
383
        {
384
                int count = 0;
×
385
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
×
386
                {
387
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
×
388
                    if(stackCard->next && stackCard->next == card && (card->previousZone == card->controller()->game->commandzone||card->previousZone == card->controller()->opponent()->game->commandzone))
×
389
                        count++;
×
390
                    if(stackCard == card && (card->previousZone == card->controller()->game->commandzone||card->previousZone == card->controller()->opponent()->game->commandzone))
×
391
                        count++;
×
392
                }
393
                if(!count)
×
394
                    return 0;
×
395
        }
396
        check = restriction[i].find("rebound");
82✔
397
        if(check != string::npos)
82✔
398
        {
399
                int count = 0;
×
400
                for(unsigned int k = 0; k < player->game->stack->cardsSeenThisTurn.size(); k++)
×
401
                {
402
                    MTGCardInstance * stackCard = player->game->stack->cardsSeenThisTurn[k];
×
403
                    if(stackCard->next && stackCard->next == card && (card->previousZone == card->controller()->game->hand||card->previousZone == card->controller()->opponent()->game->hand))
×
404
                        count++;
×
405
                    if(stackCard == card && (card->previousZone == card->controller()->game->hand||card->previousZone == card->controller()->opponent()->game->hand))
×
406
                        count++;
×
407
                }
408
                if(!count)
×
409
                    return 0;
×
410
        }
411
        check = restriction[i].find("revolt");
82✔
412
        if(check != string::npos)
82✔
413
        {
414
                int count = 0;
×
415
                for(unsigned int k = 0; k < player->game->hand->cardsSeenThisTurn.size(); k++)
×
416
                {
417
                    MTGCardInstance * tCard = player->game->hand->cardsSeenThisTurn[k];
×
418
                    if(tCard && tCard->previousZone == card->controller()->game->battlefield)
×
419
                        count++;
×
420
                }
421
                for(unsigned int k = 0; k < player->game->exile->cardsSeenThisTurn.size(); k++)
×
422
                {
423
                    MTGCardInstance * tCard = player->game->exile->cardsSeenThisTurn[k];
×
424
                    if(tCard && tCard->previousZone == card->controller()->game->battlefield)
×
425
                        count++;
×
426
                }
427
                for(unsigned int k = 0; k < player->game->library->cardsSeenThisTurn.size(); k++)
×
428
                {
429
                    MTGCardInstance * tCard = player->game->library->cardsSeenThisTurn[k];
×
430
                    if(tCard && tCard->previousZone == card->controller()->game->battlefield)
×
431
                        count++;
×
432
                }
433
                for(unsigned int k = 0; k < player->game->graveyard->cardsSeenThisTurn.size(); k++)
×
434
                {
435
                    MTGCardInstance * tCard = player->game->graveyard->cardsSeenThisTurn[k];
×
436
                    if(tCard && tCard->previousZone == card->controller()->game->battlefield)
×
437
                        count++;
×
438
                }
439
                if(!count)
×
440
                    return 0;
×
441
        }
442
        check = restriction[i].find("morbid");
82✔
443
        if(check != string::npos)
82✔
444
        {
445
            bool isMorbid = false;
2✔
446
            for(int cp = 0;cp < 2;cp++)
4✔
447
            {
448
                Player * checkCurrent = observer->players[cp];
3✔
449
                MTGGameZone * grave = checkCurrent->game->graveyard;
3✔
450
                for(unsigned int gy = 0;gy < grave->cardsSeenThisTurn.size();gy++)
3✔
451
                {
452
                    MTGCardInstance * checkCard = grave->cardsSeenThisTurn[gy];
1✔
453
                    if(checkCard->isCreature() &&
3✔
454
                        ((checkCard->previousZone == checkCurrent->game->battlefield)||
1✔
455
                        (checkCard->previousZone == checkCurrent->opponent()->game->battlefield))//died from battlefield
×
456
                        )
457
                    {
458
                        isMorbid = true;
1✔
459
                        break;
1✔
460
                    }
461
                }
462
                if(isMorbid)
3✔
463
                    break;
1✔
464
            }
465
            if(!isMorbid)
2✔
466
                return 0;
1✔
467
        }
468
        check = restriction[i].find("deadpermanent");
81✔
469
        if(check != string::npos)
81✔
470
        {
471
            bool deadpermanent = false;
×
472
            for(int cp = 0;cp < 2;cp++)
×
473
            {
474
                Player * checkCurrent = observer->players[cp];
×
475
                MTGGameZone * grave = checkCurrent->game->graveyard;
×
476
                for(unsigned int gy = 0;gy < grave->cardsSeenThisTurn.size();gy++)
×
477
                {
478
                    MTGCardInstance * checkCard = grave->cardsSeenThisTurn[gy];
×
479
                    if(checkCard->isPermanent() &&
×
480
                        ((checkCard->previousZone == checkCurrent->game->battlefield)||
×
481
                        (checkCard->previousZone == checkCurrent->opponent()->game->battlefield))//died from battlefield
×
482
                        )
483
                    {
484
                        deadpermanent = true;
×
485
                        break;
×
486
                    }
487
                }
488
                if(deadpermanent)
×
489
                    break;
×
490
            }
491
            if(!deadpermanent)
×
492
                return 0;
×
493
        }
494
        check = restriction[i].find("deadcreart");
81✔
495
        if(check != string::npos)
81✔
496
        {
497
            bool deadcreart = false;
×
498
            for(int cp = 0;cp < 2;cp++)
×
499
            {
500
                Player * checkCurrent = observer->players[cp];
×
501
                MTGGameZone * grave = checkCurrent->game->graveyard;
×
502
                for(unsigned int gy = 0;gy < grave->cardsSeenThisTurn.size();gy++)
×
503
                {
504
                    MTGCardInstance * checkCard = grave->cardsSeenThisTurn[gy];
×
505
                    if((checkCard->isCreature() || checkCard->hasType(Subtypes::TYPE_ARTIFACT)) &&
×
506
                        ((checkCard->previousZone == checkCurrent->game->battlefield)||
×
507
                        (checkCard->previousZone == checkCurrent->opponent()->game->battlefield))//died from battlefield
×
508
                        )
509
                    {
510
                        deadcreart = true;
×
511
                        break;
×
512
                    }
513
                }
514
                if(deadcreart)
×
515
                    break;
×
516
            }
517
            if(!deadcreart)
×
518
                return 0;
×
519
        }
520
        check = restriction[i].find("hasdead");
81✔
521
        if(check != string::npos)
81✔
522
        {
523
            bool hasdeadtype = false;
×
524
            string checktype = restriction[i].substr(7);
×
525
            for(int cp = 0;cp < 2;cp++)
×
526
            {
527
                Player * checkCurrent = observer->players[cp];
×
528
                MTGGameZone * grave = checkCurrent->game->graveyard;
×
529
                for(unsigned int gy = 0;gy < grave->cardsSeenThisTurn.size();gy++)
×
530
                {
531
                    MTGCardInstance * checkCard = grave->cardsSeenThisTurn[gy];
×
532
                    if(checkCard->hasType(checktype) &&
×
533
                        ((checkCard->previousZone == checkCurrent->game->battlefield)||
×
534
                        (checkCard->previousZone == checkCurrent->opponent()->game->battlefield))//died from battlefield
×
535
                        )
536
                    {
537
                        hasdeadtype = true;
×
538
                        break;
×
539
                    }
540
                }
541
                if(hasdeadtype)
×
542
                    break;
×
543
            }
544
            if(!hasdeadtype)
×
545
                return 0;
×
546
        }
547
        check = restriction[i].find("zerodead");
81✔
548
        if(check != string::npos)//returns true if zero
81✔
549
        {
550
            bool hasDeadCreature = false;
×
551
            Player * checkCurrent = card->controller();
×
552
            MTGGameZone * grave = checkCurrent->game->graveyard;
×
553
            for(unsigned int gy = 0;gy < grave->cardsSeenThisTurn.size();gy++)
×
554
            {
555
                MTGCardInstance * checkCard = grave->cardsSeenThisTurn[gy];
×
556
                if(checkCard->isCreature() &&
×
557
                    ((checkCard->previousZone == checkCurrent->game->battlefield))//died from your battlefield
×
558
                    )
559
                {
560
                    hasDeadCreature = true;
×
561
                    break;
×
562
                }
563
            }
564
            if(hasDeadCreature)
×
565
                return 0;
×
566
        }
567
        //Ensnaring Bridge
568
        check = restriction[i].find("powermorethanopponenthand");
81✔
569
        if (check != string::npos)//for opponent creatures
81✔
570
        {
571
            Player * checkCurrent = card->controller();
×
572
            if(card->power <= checkCurrent->opponent()->game->hand->nb_cards)
×
573
                return 0;
×
574
        }
575

576
        check = restriction[i].find("powermorethancontrollerhand");
81✔
577
        if (check != string::npos)//for controller creatures
81✔
578
        {
579
            Player * checkCurrent = card->controller();
×
580
            if(card->power <= checkCurrent->game->hand->nb_cards)
×
581
                return 0;
×
582
        }
583
        //end
584

585
        check = restriction[i].find("morecardsthanopponent");
81✔
586
        if (check != string::npos)
81✔
587
        {
588
            Player * checkCurrent = card->controller();
×
589
            if(checkCurrent->game->hand->nb_cards <= checkCurrent->opponent()->game->hand->nb_cards)
×
590
                return 0;
×
591
        }
592

593
        check = restriction[i].find("delirium");
81✔
594
        if (check != string::npos)
81✔
595
        {
596
                Player * checkCurrent = card->controller();
×
597
                MTGGameZone * grave = checkCurrent->game->graveyard;
×
598

599
                int checkTypesAmount = 0;
×
600
                if (grave->hasType("creature")) checkTypesAmount++;
×
601
                if (grave->hasType("enchantment")) checkTypesAmount++;
×
602
                if (grave->hasType("sorcery")) checkTypesAmount++;
×
603
                if (grave->hasType("instant")) checkTypesAmount++;
×
604
                if (grave->hasType("land")) checkTypesAmount++;
×
605
                if (grave->hasType("artifact")) checkTypesAmount++;
×
606
                if (grave->hasType("planeswalker")) checkTypesAmount++;
×
607
                if (grave->hasType("battle")) checkTypesAmount++;
×
608
                if (grave->hasType("tribal")) checkTypesAmount++;
×
609
                if (checkTypesAmount < 4)
×
610
                return 0;
×
611
        }
612

613
        check = restriction[i].find("notdelirum");
81✔
614
        if (check != string::npos)
81✔
615
        {
616
                Player * checkCurrent = card->controller();
×
617
                MTGGameZone * grave = checkCurrent->game->graveyard;
×
618

619
                int checkTypesAmount = 0;
×
620
                if (grave->hasType("creature")) checkTypesAmount++;
×
621
                if (grave->hasType("enchantment")) checkTypesAmount++;
×
622
                if (grave->hasType("sorcery")) checkTypesAmount++;
×
623
                if (grave->hasType("instant")) checkTypesAmount++;
×
624
                if (grave->hasType("land")) checkTypesAmount++;
×
625
                if (grave->hasType("artifact")) checkTypesAmount++;
×
626
                if (grave->hasType("planeswalker")) checkTypesAmount++;
×
627
                if (grave->hasType("battle")) checkTypesAmount++;
×
628
                if (grave->hasType("tribal")) checkTypesAmount++;
×
629
                if (checkTypesAmount > 3)
×
630
                return 0;
×
631
        }
632

633
        check = restriction[i].find("miracle");
81✔
634
        if(check != string::npos)
81✔
635
        {
636
            if(observer->turn < 1 && card->controller()->drawCounter < 1)
×
637
                return 0;
×
638
            if(card->previous && !card->previous->miracle)
×
639
                return 0;
×
640
        }
641

642
        check = restriction[i].find("madnessplayed");
81✔
643
        if (check != string::npos)
81✔
644
        {
645
            if (card->previous && !card->previous->MadnessPlay)
×
646
                return 0;
×
647
        }
648

649
        check = restriction[i].find("prowl");
81✔
650
        if(check != string::npos)
81✔
651
        {
652
            vector<string>typeToCheck = parseBetween(restriction[i],"prowl(",")");
×
653
            bool isProwled = false;
×
654
            if(typeToCheck.size())
×
655
            {
656
                vector<string>splitTypes = split(typeToCheck[1],' ');
×
657
                if(splitTypes.size())
×
658
                {
659
                    for (size_t k = 0; k < splitTypes.size(); ++k)
×
660
                    {
661
                        string theType = splitTypes[k];
×
662
                        for (size_t j = 0; j < card->controller()->prowledTypes.size(); ++j)
×
663
                        {
664
                            if ( card->controller()->prowledTypes[j] == splitTypes[k])
×
665
                            {
666
                                isProwled = true;
×
667
                                break;
×
668
                            }
669
                        }
670
                    }
671
                }
672
                else
673
                {
674
                    for (size_t j = 0; j < card->controller()->prowledTypes.size(); ++j)
×
675
                    {
676
                        if (  card->controller()->prowledTypes[j] == typeToCheck[1])
×
677
                        {
678
                            isProwled = true;
×
679
                            break;
×
680
                        }
681
                    }
682
                }
683

684
            }
685
            else
686
            {
687
                for (size_t j = 0; j < card->controller()->prowledTypes.size(); ++j)
×
688
                {
689
                    if ( card->hasSubtype( card->controller()->prowledTypes[j] ))
×
690
                    {
691
                        isProwled = true;
×
692
                        break;
×
693
                    }
694
                }
695
            }
696
            if(!isProwled)
×
697
                return 0;
×
698
        }
699

700
        check = restriction[i].find("spent(");
81✔
701
        if(check != string::npos)
81✔
702
        {
703
            vector<string>spentMana = parseBetween(restriction[i],"spent(",")");
×
704
            if(spentMana.size())
×
705
            {
706
                ManaCost * costToCheck = ManaCost::parseManaCost(restriction[i]);
×
707
                ManaCost * spent = card->getManaCost()->getManaUsedToCast();
×
708
                if(spent && costToCheck && !spent->canAfford(costToCheck,0))
×
709
                {
710
                    SAFE_DELETE(costToCheck);
×
711
                    return 0;
×
712
                }
713
                SAFE_DELETE(costToCheck);
×
714
            }
715
        }
716

717
        check = restriction[i].find("discarded");
81✔
718
        if(check != string::npos)
81✔
719
        {
720
            if(!card->discarded)
×
721
                return 0;
×
722
        }
723

724
        check = restriction[i].find("hasexerted");
81✔
725
        if(check != string::npos)
81✔
726
        {
727
            if(!card->exerted)
×
728
                return 0;
×
729
        }
730

731
        check = restriction[i].find("notexerted");
81✔
732
        if(check != string::npos)
81✔
733
        {
734
            if(card->exerted)
×
735
                return 0;
×
736
        }
737

738
        check = restriction[i].find("discardbyopponent");
81✔
739
        if(check != string::npos)
81✔
740
        {
741
            bool matchOpponent = false;
×
742
            if(card->discarderOwner)
×
743
                if(card->controller()->opponent() == card->discarderOwner)
×
744
                    matchOpponent = true;
×
745

746
            if(!matchOpponent)
×
747
                return 0;
×
748
        }
749

750
        check = restriction[i].find("copiedacard");
81✔
751
        if(check != string::npos)
81✔
752
        {
753
            if(!card->isACopier)
×
754
                return 0;
×
755
        }
756

757
        check = restriction[i].find("geared");
81✔
758
        if (check != string::npos)
81✔
759
        {
760
            if (card->equipment < 1)
×
761
                return 0;
×
762
        }
763

764
        check = restriction[i].find("canuntap");
81✔
765
        if(check != string::npos)
81✔
766
        {
767
            if(card->frozen >= 1 || card->basicAbilities[(int)Constants::DOESNOTUNTAP] || !card->isTapped())
×
768
                return 0;
×
769
        }
770

771
        check = restriction[i].find("raid");
81✔
772
        if(check != string::npos)
81✔
773
        {
774
            if(card->controller()->raidcount < 1)
×
775
                return 0;
×
776
        }
777
        
778

779
        check = restriction[i].find("opponentdamagedbycombat");
81✔
780
        if(check != string::npos)
81✔
781
        {
782
            if(card->controller()->dealsdamagebycombat < 1)
×
783
                return 0;
×
784
        }
785

786
        check = restriction[i].find("lessorequalcreatures");
81✔
787
        if(check != string::npos)
81✔
788
        {
789
            bool condition = (card->controller()->opponent()->inPlay()->countByType("creature") >= card->controller()->inPlay()->countByType("creature"));
×
790
            if(!condition)
×
791
                return 0;
×
792
        }
793

794
        check = restriction[i].find("lessorequallands");
81✔
795
        if(check != string::npos)
81✔
796
        {
797
            bool condition = (card->controller()->opponent()->inPlay()->countByType("land") >= card->controller()->inPlay()->countByType("land"));
×
798
            if(!condition)
×
799
                return 0;
×
800
        }
801

802
        check = restriction[i].find("outnumbered");//opponent controls atleast 4 or more creatures than you
81✔
803
        if(check != string::npos)
81✔
804
        {
805
            bool isoutnumbered = (card->controller()->opponent()->inPlay()->countByType("creature") - card->controller()->inPlay()->countByType("creature"))>3;
×
806
            if(!isoutnumbered)
×
807
                return 0;
×
808
        }
809

810
        check = restriction[i].find("hasdefender");
81✔
811
        if(check != string::npos)
81✔
812
        {
813
            if(!card->has(Constants::DEFENDER))
×
814
                return 0;
×
815
        }
816

817
        check = restriction[i].find("didblock");
81✔
818
        if(check != string::npos)
81✔
819
        {
820
            if(!card->didblocked)
×
821
                return 0;
×
822
        }
823

824
        check = restriction[i].find("didattack");
81✔
825
        if(check != string::npos)
81✔
826
        {
827
            if(!card->didattacked)
×
828
                return 0;
×
829
        }
830

831
        check = restriction[i].find("didntattack");
81✔
832
        if(check != string::npos)
81✔
833
        {
834
            if(card->didattacked)
×
835
                return 0;
×
836
        }
837
        
838
        check = restriction[i].find("didcombatdamagetofoe");
81✔
839
        if(check != string::npos)
81✔
840
        {
841
            if(card->combatdamageToOpponent == 0)
×
842
                return 0;
×
843
        }
844

845
        check = restriction[i].find("ownerscontrol");
81✔
846
        if(check != string::npos)
81✔
847
        {
848
            if(card->currentZone != card->owner->game->battlefield)
×
849
                return 0;
×
850
        }
851
        check = restriction[i].find("opponentscontrol");
81✔
852
        if(check != string::npos)
81✔
853
        {
854
            if(card->currentZone == card->owner->game->battlefield)
×
855
                return 0;
×
856
        }
857
        check = restriction[i].find("one of a kind");
81✔
858
        if(check != string::npos)
81✔
859
        {
860
            if(player->game->inPlay->hasName(card->name))
×
861
                return 0;
×
862
        }
863
        check = restriction[i].find("before attackers");
81✔
864
        if(check != string::npos)
81✔
865
        {
866
            if(cPhase > MTG_PHASE_COMBATBEGIN)
×
867
                return 0;
×
868
        }
869
        check = restriction[i].find("before battle damage");
81✔
870
        if(check != string::npos)
81✔
871
        {
872
            if(cPhase > MTG_PHASE_COMBATBLOCKERS)
8✔
873
                return 0;
×
874
        }
875
        check = restriction[i].find("after battle");
81✔
876
        if(check != string::npos)
81✔
877
        {
878
            if(cPhase < MTG_PHASE_COMBATBLOCKERS)
×
879
                return 0;
×
880
        }
881
        check = restriction[i].find("during battle");
81✔
882
        if(check != string::npos)
81✔
883
        {
884
            if(cPhase < MTG_PHASE_COMBATBEGIN ||cPhase > MTG_PHASE_COMBATEND )
×
885
                return 0;
×
886
        }
887
        check = restriction[i].find("during my main phases");
81✔
888
        if(check != string::npos)
81✔
889
        {
890
            if( player != observer->currentPlayer && (cPhase != MTG_PHASE_FIRSTMAIN ||cPhase != MTG_PHASE_SECONDMAIN) )
×
891
                return 0;
×
892
        }
893
        check = restriction[i].find("during my turn");
81✔
894
        if(check != string::npos)
81✔
895
        {
896
            if(player != observer->currentPlayer)
×
897
                return 0;
×
898
        }
899
        check = restriction[i].find("during opponent turn");
81✔
900
        if(check != string::npos)
81✔
901
        {
902
            if(player == observer->currentPlayer)
×
903
                return 0;
×
904
        }
905
        check = restriction[i].find("control snow land");
81✔
906
        if(check != string::npos)
81✔
907
        {
908
            if(!player->game->inPlay->hasPrimaryType("snow","land"))
×
909
                return 0;
×
910
        }
911
        check = restriction[i].find("control two or more vampires");
81✔
912
        if(check != string::npos)
81✔
913
        {
914
            restriction.push_back("type(vampire|mybattlefield)~morethan~1");
×
915
        }
916
        check = restriction[i].find("control less artifacts");
81✔
917
        if(check != string::npos)
81✔
918
        {
919
            restriction.push_back("type(artifact|mybattlefield)~lessthan~type(artifact|opponentbattlefield)");
×
920
        }
921
        check = restriction[i].find("control less enchantments");
81✔
922
        if(check != string::npos)
81✔
923
        {
924
            restriction.push_back("type(enchantment|mybattlefield)~lessthan~type(enchantment|opponentbattlefield)");
×
925
        }
926
        check = restriction[i].find("control less creatures");
81✔
927
        if(check != string::npos)
81✔
928
        {
929
            restriction.push_back("type(creature|mybattlefield)~lessthan~type(creature|opponentbattlefield)");
×
930
        }
931
        check = restriction[i].find("control less lands");
81✔
932
        if(check != string::npos)
81✔
933
        {
934
            restriction.push_back("type(land|mybattlefield)~lessthan~type(land|opponentbattlefield)");
×
935
        }
936
        check = restriction[i].find("control more creatures");
81✔
937
        if(check != string::npos)
81✔
938
        {
939
            restriction.push_back("type(creature|mybattlefield)~morethan~type(creature|opponentbattlefield)");
×
940
        }
941
        check = restriction[i].find("control more lands");
81✔
942
        if(check != string::npos)
81✔
943
        {
944
            restriction.push_back("type(land|mybattlefield)~morethan~type(land|opponentbattlefield)");
×
945
        }
946
        check = restriction[i].find("didnotcastnontoken");
81✔
947
        if(check != string::npos)
81✔
948
        {
949
            restriction.push_back("lastturn(*[-token]|opponentstack,opponentbattlefield)~lessthan~1");
×
950
        }
951
        check = restriction[i].find("control three or more lands with same name");
81✔
952
        if(check != string::npos)
81✔
953
        {
954
            if(player != observer->currentPlayer)
×
955
                return 0;
×
956
            bool found = false;
×
957
            for(unsigned int i = 0; i < observer->currentPlayer->game->inPlay->cards.size() && !found; i++){
×
958
                if(observer->currentPlayer->game->inPlay->cards[i]->hasType("land")){
×
959
                    for(unsigned int j = i+1; j < observer->currentPlayer->game->inPlay->cards.size() && !found; j++){
×
960
                        if(observer->currentPlayer->game->inPlay->cards[j]->hasType("land") && observer->currentPlayer->game->inPlay->cards[j]->name == observer->currentPlayer->game->inPlay->cards[i]->name){
×
961
                            for(unsigned int k = j+1; k < observer->currentPlayer->game->inPlay->cards.size() && !found; k++){
×
962
                                if(observer->currentPlayer->game->inPlay->cards[k]->hasType("land") && observer->currentPlayer->game->inPlay->cards[k]->name == observer->currentPlayer->game->inPlay->cards[i]->name){
×
963
                                    found = true;
×
964
                                }
965
                            }
966
                        }
967
                    }
968
                }
969
            }
970
            if(!found)
×
971
                return 0;
×
972
        }
973
        check = restriction[i].find("coven"); //Player controls three or more creatures with different powers
81✔
974
        if(check != string::npos)
81✔
975
        {
976
            if(player != observer->currentPlayer)
×
977
                return 0;
×
978
            bool found = false;
×
979
            for(unsigned int i = 0; i < observer->currentPlayer->game->inPlay->cards.size() && !found; i++){
×
980
                if(observer->currentPlayer->game->inPlay->cards[i]->hasType(Subtypes::TYPE_CREATURE)){
×
981
                    for(unsigned int j = i+1; j < observer->currentPlayer->game->inPlay->cards.size() && !found; j++){
×
982
                        if(observer->currentPlayer->game->inPlay->cards[j]->hasType(Subtypes::TYPE_CREATURE) && observer->currentPlayer->game->inPlay->cards[j]->power != observer->currentPlayer->game->inPlay->cards[i]->power){
×
983
                            for(unsigned int k = j+1; k < observer->currentPlayer->game->inPlay->cards.size() && !found; k++){
×
984
                                if(observer->currentPlayer->game->inPlay->cards[k]->hasType(Subtypes::TYPE_CREATURE) && (observer->currentPlayer->game->inPlay->cards[k]->power != observer->currentPlayer->game->inPlay->cards[i]->power && observer->currentPlayer->game->inPlay->cards[k]->power != observer->currentPlayer->game->inPlay->cards[j]->power)){
×
985
                                    found = true;
×
986
                                }
987
                            }
988
                        }
989
                    }
990
                }
991
            }
992
            if(!found)
×
993
                return 0;
×
994
        }
995
        check = restriction[i].find("trainer"); //Player controls an attacking creature with greater power than the current one.
81✔
996
        if(check != string::npos)
81✔
997
        {
998
            if(player != observer->currentPlayer || !card->isAttacker())
×
999
                return 0;
×
1000
            bool found = false;
×
1001
            for(unsigned int i = 0; i < observer->currentPlayer->game->inPlay->cards.size() && !found; i++){
×
1002
                if(observer->currentPlayer->game->inPlay->cards[i]->hasType(Subtypes::TYPE_CREATURE) && observer->currentPlayer->game->inPlay->cards[i]->isAttacker() && observer->currentPlayer->game->inPlay->cards[i]->power > card->power){
×
1003
                    found = true;
×
1004
                }
1005
            }
1006
            if(!found)
×
1007
                return 0;
×
1008
        }
1009
        check = restriction[i].find("can play");
81✔
1010
        if(check != string::npos)
81✔
1011
        {
1012
            if(!card->getId())
×
1013
                return 0; //Fixed a crash when AI plays.
×
1014
            string type = restriction[i];
×
1015
            type = type.replace(0,9,"");
×
1016
            MTGCardInstance* cardDummy = card->clone();
×
1017
            for (int i = ((int)cardDummy->types.size())-1; i >= 0; --i)
×
1018
                cardDummy->removeType(cardDummy->types[i]);
×
1019
            cardDummy->addType(type);
×
1020
            bool canplay = true;
×
1021
            if (cardDummy->isLand() && observer->currentActionPlayer->game->playRestrictions->canPutIntoZone(cardDummy, observer->currentActionPlayer->game->inPlay) == PlayRestriction::CANT_PLAY)
×
1022
                canplay = false;
×
1023
            if (!cardDummy->isLand() && observer->currentActionPlayer->game->playRestrictions->canPutIntoZone(cardDummy, observer->currentActionPlayer->game->stack) == PlayRestriction::CANT_PLAY)
×
1024
                canplay = false;
×
1025
            if (!cardDummy->hasType(Subtypes::TYPE_INSTANT) && !cardDummy->StackIsEmptyandSorcerySpeed())
×
1026
                canplay = false;
×
1027
            SAFE_DELETE(cardDummy);
×
1028
            if(!canplay)
×
1029
                return 0;
×
1030
        }
1031
        check = restriction[i].find("paid(");
81✔
1032
        if(check != string::npos)
81✔
1033
        {
1034
            vector<string>getPaid = parseBetween(restriction[i].c_str(),"paid(",")");
16✔
1035
            string paid = getPaid[1];
16✔
1036
            for (size_t j = 0; j < sizeof(kAlternateCostIds)/sizeof(kAlternateCostIds[0]); ++j)
90✔
1037
            {
1038
                string keyword = kAlternateCostKeywords[j];
164✔
1039
                if (paid.find(keyword) != string::npos)
83✔
1040
                {
1041
                    if (!(card->alternateCostPaid[j] > 0 ))
9✔
1042
                    {
1043
                        return 0;
2✔
1044
                    }
1045
                }
1046
            }
1047
        }
1048
        check = restriction[i].find("never");
79✔
1049
        if(check != string::npos)
79✔
1050
            return 0;
×
1051
    }
1052
    return 1;
2,363✔
1053
}
1054

1055
int AbilityFactory::parseRestriction(string s)
98,390✔
1056
{
1057
    if (s.find("myturnonly") != string::npos)
98,390✔
1058
        return ActivatedAbility::PLAYER_TURN_ONLY;
5✔
1059
    if (s.find("opponentturnonly") != string::npos)
98,385✔
1060
        return ActivatedAbility::OPPONENT_TURN_ONLY;
×
1061
    if (s.find("assorcery") != string::npos)
98,385✔
1062
        return ActivatedAbility::AS_SORCERY;
10✔
1063

1064
    string types[] = { "my", "opponent", "" };
196,750✔
1065
    int starts[] = { ActivatedAbility::MY_BEFORE_BEGIN, ActivatedAbility::OPPONENT_BEFORE_BEGIN, ActivatedAbility::BEFORE_BEGIN };
98,375✔
1066
    for (int j = 0; j < 3; ++j)
393,389✔
1067
    {
1068
        size_t found = s.find(types[j]);
295,051✔
1069
        if (found != string::npos)
295,051✔
1070
        {
1071
            for (int i = 0; i < NB_MTG_PHASES; i++)
1,637,916✔
1072
            {
1073
                string toFind = types[j];
3,057,421✔
1074
                toFind.append(Constants::MTGPhaseCodeNames[i]).append("only");
1,528,729✔
1075
                found = s.find(toFind);
1,528,729✔
1076
                if (found != string::npos)
1,528,729✔
1077
                {
1078
                    return starts[j] + i;
37✔
1079
                }
1080
            }
1081
        }
1082
    }
1083

1084
    return ActivatedAbility::NO_RESTRICTION;
98,338✔
1085
}
1086

1087
int AbilityFactory::countCards(TargetChooser * tc, Player * player, int option)
×
1088
{
1089
    int result = 0;
×
1090
    for (int i = 0; i < 2; i++)
×
1091
    {
1092
        if (player && player != observer->players[i])
×
1093
            continue;
×
1094
        MTGGameZone * zones[] = { observer->players[i]->game->inPlay, observer->players[i]->game->graveyard, observer->players[i]->game->hand, observer->players[i]->game->exile, observer->players[i]->game->commandzone };
×
1095
        for (int k = 0; k < 5; k++)
×
1096
        {
1097
            for (int j = zones[k]->nb_cards - 1; j >= 0; j--)
×
1098
            {
1099
                MTGCardInstance * current = zones[k]->cards[j];
×
1100
                if (tc->canTarget(current))
×
1101
                {
1102
                    switch (option)
×
1103
                    {
1104
                    case COUNT_POWER:
1105
                        result += current->power;
×
1106
                        break;
×
1107
                    default:
1108
                        result++;
×
1109
                        break;
×
1110
                    }
1111
                }
1112
            }
1113
        }
1114
    }
1115
    return result;
×
1116
}
1117

1118
Counter * AbilityFactory::parseCounter(string s, MTGCardInstance * target, Spell * spell)
2,470✔
1119
{
1120
    int nb = 1;
2,470✔
1121
    int maxNb = 0;
2,470✔
1122
    string name = "";
4,940✔
1123
    string nbstr = "1";
4,940✔
1124
    string maxNbstr = "0";
4,940✔
1125
    string spt = "";
4,940✔
1126

1127
    vector<string>splitCounter = split(s,',');
4,940✔
1128
    vector<string>splitCounterCheck = split(s,'.');
4,940✔
1129
    if(splitCounter.size() < splitCounterCheck.size())
2,470✔
1130
    {
1131
        splitCounter = splitCounterCheck;//use the one with the most results.
2,302✔
1132
    }
1133
    if(!splitCounter.size())
2,470✔
1134
    {
1135
        return NULL;
×
1136
    }
1137
    if(splitCounter.size() > 0)//counter(1/1)
2,470✔
1138
    {
1139
        spt = splitCounter[0];
2,470✔
1140
    }
1141
    if(splitCounter.size() > 1)//counter(1/1,1)
2,470✔
1142
    {
1143
        nbstr = splitCounter[1];
2,400✔
1144
    }
1145
    if(splitCounter.size() > 2)//counter(0/0,1,charge)
2,470✔
1146
    {
1147
        name = splitCounter[2];
2,351✔
1148
    }
1149
    if(splitCounter.size() > 3)//counter(0/0,1,charge,2)
2,470✔
1150
    {
1151
        maxNbstr = splitCounter[3];
×
1152
    }
1153
    WParsedInt * wpi;
1154
    if (target)
2,470✔
1155
    {
1156
        wpi = NEW WParsedInt(nbstr, spell, target);
1,612✔
1157
    }
1158
    else
1159
    {
1160
        wpi = NEW WParsedInt(atoi(nbstr.c_str()));
858✔
1161
    }
1162
    nb = wpi->getValue();
2,470✔
1163
    delete (wpi);
2,470✔
1164
    WParsedInt * wpinb;
1165
    if (target)
2,470✔
1166
    {
1167
        wpinb = NEW WParsedInt(maxNbstr, spell, target);
1,612✔
1168
    }
1169
    else
1170
    {
1171
        wpinb = NEW WParsedInt(atoi(maxNbstr.c_str()));
858✔
1172
    }
1173
    maxNb = wpinb->getValue();
2,470✔
1174
    delete(wpinb);
2,470✔
1175

1176
    int power, toughness;
1177
    if (parsePowerToughness(spt, &power, &toughness))
2,470✔
1178
    {
1179
        Counter * counter = NEW Counter(target, name.c_str(), power, toughness);
2,470✔
1180
        counter->nb = nb;
2,470✔
1181
        counter->maxNb = maxNb;
2,470✔
1182
        return counter;
2,470✔
1183
    }
1184
    return NULL;
×
1185
}
1186

1187
int AbilityFactory::parsePowerToughness(string s, int *power, int *toughness)
2,553✔
1188
{
1189
    vector<string>splitPT = split(s,'/');
5,106✔
1190
    vector<string>splitPTCheck = split(s,'%');
5,106✔
1191
    if(splitPT.size() < 2 && splitPT.size() < splitPTCheck.size())
2,553✔
1192
    {
1193
        splitPT = splitPTCheck;
840✔
1194
    }
1195
    if(!splitPT.size())
2,553✔
1196
    {
1197
        return 0;
×
1198
    }
1199
    *power = atoi(splitPT[0].c_str());
2,553✔
1200
    *toughness = atoi(splitPT[1].c_str());
2,553✔
1201
    return 1;
2,553✔
1202
}
1203

1204
TargetChooser * AbilityFactory::parseSimpleTC(const std::string& s, const std::string& _starter, MTGCardInstance * card, bool forceNoTarget)
2,117,580✔
1205
{
1206
    string starter = _starter;
4,235,160✔
1207
    starter.append("(");
2,117,580✔
1208

1209
    size_t found = s.find(starter);
2,117,580✔
1210
    if (found == string::npos)
2,117,580✔
1211
        return NULL;
2,116,011✔
1212

1213
    size_t start = found + starter.size(); 
1,569✔
1214

1215
    size_t end = s.find(")", start);
1,569✔
1216
    if (end == string::npos)
1,569✔
1217
    {
1218
        DebugTrace("malformed syntax " << s);
×
1219
        return NULL;
×
1220
    }
1221

1222
    string starget = s.substr(start , end - start);
3,138✔
1223
    TargetChooserFactory tcf(observer);
1,569✔
1224
    TargetChooser * tc =  tcf.createTargetChooser(starget, card);
1,569✔
1225

1226
    if (tc && forceNoTarget)
1,569✔
1227
        tc->targetter = NULL;
1,569✔
1228

1229
    return tc;
1,569✔
1230
}
1231

1232
// evaluate trigger ability
1233
// ie auto=@attacking(mytgt):destroy target(*)
1234
// eval only the text between the @ and the first :
1235
TriggeredAbility * AbilityFactory::parseTrigger(string s, string, int id, Spell *, MTGCardInstance *card,
43,727✔
1236
                                                Targetable * target)
1237
{
1238
    size_t found = string::npos;
43,727✔
1239

1240
    //restrictions on triggers  
1241
    bool once = (s.find("once") != string::npos);
43,727✔
1242
    bool sourceUntapped = (s.find("sourcenottap") != string::npos);
43,727✔
1243
    bool sourceTap = (s.find("sourcetap") != string::npos);
43,727✔
1244
    bool limitOnceATurn = (s.find("turnlimited") != string::npos);
43,727✔
1245
    bool isSuspended = (s.find("suspended") != string::npos);
43,727✔
1246
    bool opponentPoisoned = (s.find("opponentpoisoned") != string::npos);
43,727✔
1247
    bool lifelost = (s.find("foelost(") != string::npos);
43,727✔
1248
    int lifeamount = lifelost ? atoi(s.substr(s.find("foelost(") + 8,')').c_str()) : 0;
43,727✔
1249
    bool neverRemove = (s.find("dontremove") != string::npos);
43,727✔
1250

1251
    //Card Changed Zone
1252
    found = s.find("movedto(");
43,727✔
1253
    if (found != string::npos)
43,727✔
1254
    {
1255
        size_t end = s.find(")");
4,454✔
1256
        string starget = s.substr(found + 8, end - found - 8);
8,908✔
1257
        TargetChooserFactory tcf(observer);
4,454✔
1258

1259
        TargetChooser *toTc = NULL;
4,454✔
1260
        TargetChooser *toTcCard = NULL;
4,454✔
1261
        end = starget.find("|");
4,454✔
1262
        if (end == string::npos)
4,454✔
1263
        {
1264
            toTcCard = tcf.createTargetChooser("*", card);
18✔
1265
            found = 0;
18✔
1266
        }
1267
        else
1268
        {
1269
            toTcCard = tcf.createTargetChooser(starget.substr(0, end).append("|*"), card);
4,436✔
1270
            found = end + 1;
4,436✔
1271
        }
1272
        toTcCard->setAllZones();
4,454✔
1273
        toTcCard->targetter = NULL; //avoid protection from
4,454✔
1274
        starget = starget.substr(found, end - found).insert(0, "*|");
4,454✔
1275
        toTc = tcf.createTargetChooser(starget, card);
4,454✔
1276
        toTc->targetter = NULL; //avoid protection from
4,454✔
1277

1278
        TargetChooser *fromTc = NULL;
4,454✔
1279
        TargetChooser * fromTcCard = NULL;
4,454✔
1280
        found = s.find("from(");
4,454✔
1281
        if (found != string::npos)
4,454✔
1282
        {
1283
            end = s.find("|", found);
45✔
1284
            if (end == string::npos)
45✔
1285
            {
1286
                fromTcCard = tcf.createTargetChooser("*", card);
24✔
1287
                found = found + 5;
24✔
1288
            }
1289
            else
1290
            {
1291
                fromTcCard = tcf.createTargetChooser(s.substr(found + 5, end - found - 5).append("|*"), card);
21✔
1292
                found = end + 1;
21✔
1293
            }
1294
            fromTcCard->setAllZones();
45✔
1295
            fromTcCard->targetter=NULL; //avoid protection from
45✔
1296
            end = s.find(")", found);
45✔
1297
            starget = s.substr(found, end - found).insert(0, "*|");
45✔
1298
            fromTc = tcf.createTargetChooser(starget, card);
45✔
1299
            fromTc->targetter = NULL; //avoid protection from
45✔
1300
        }
1301
        TriggeredAbility * mover = NEW TrCardAddedToZone(observer, id, card, (TargetZoneChooser *) toTc, toTcCard, (TargetZoneChooser *) fromTc, fromTcCard, once, sourceUntapped, isSuspended, limitOnceATurn);
4,454✔
1302
        if(neverRemove && mover)
4,454✔
1303
        {
1304
            mover->forcedAlive = 1;
×
1305
            mover->forceDestroy = -1;
×
1306
        }
1307
        return mover;
4,454✔
1308
    }
1309

1310
    //Card unTapped
1311
    if (TargetChooser *tc = parseSimpleTC(s,"untapped", card))
39,273✔
1312
        return NEW TrCardTapped(observer, id, card, tc, false, once);
1,449✔
1313

1314
    //Card Tapped
1315
    if (TargetChooser *tc = parseSimpleTC(s,"tapped", card))
37,824✔
1316
        return NEW TrCardTapped(observer, id, card, tc, true, once);
9✔
1317

1318
    //Card Tapped for mana
1319
    if (TargetChooser *tc = parseSimpleTC(s,"tappedformana", card))
37,815✔
1320
        return NEW TrCardTappedformana(observer, id, card, tc, true, once);
3✔
1321

1322
    //Card Produced some mana
1323
    if (TargetChooser *tc = parseSimpleTC(s,"producedmana", card))
37,812✔
1324
        return NEW TrCardManaproduced(observer, id, card, tc, once, limitOnceATurn);
×
1325

1326
    //Card Transforms
1327
    if (TargetChooser *tc = parseSimpleTC(s,"transformed", card))
37,812✔
1328
        return NEW TrCardTransformed(observer, id, card, tc, once);
×
1329

1330
    //Card Faces Up
1331
    if (TargetChooser *tc = parseSimpleTC(s,"facedup", card))
37,812✔
1332
        return NEW TrCardFaceUp(observer, id, card, tc, once);
×
1333

1334
    //Card Phases In
1335
    if (TargetChooser *tc = parseSimpleTC(s,"phasedin", card))
37,812✔
1336
        return NEW TrCardPhasesIn(observer, id, card, tc, once);
×
1337

1338
    //Card Exerted
1339
    if (TargetChooser *tc = parseSimpleTC(s,"exerted", card))
37,812✔
1340
        return NEW TrCardExerted(observer, id, card, tc, once, limitOnceATurn);
×
1341

1342
//CombatTrigger
1343
    //Card card attacked and is blocked
1344
    found = s.find("combat(");
37,812✔
1345
    if (found != string::npos)
37,812✔
1346
    {
1347
        size_t end = s.find(")",found);
15✔
1348
        string combatTrigger = s.substr(found + 7, end - found - 7);
30✔
1349
        //find combat traits, only trigger types, the general restrictions are found earlier.
1350
        bool attackingTrigger = false;
15✔
1351
        bool attackedAloneTrigger = false;
15✔
1352
        bool notBlockedTrigger = false;
15✔
1353
        bool attackBlockedTrigger = false;
15✔
1354
        bool blockingTrigger = false;
15✔
1355
        vector <string> combatTriggerVector = split(combatTrigger, ',');
30✔
1356
        for (unsigned int i = 0 ; i < combatTriggerVector.size() ; i++)
35✔
1357
        { 
1358
            if(combatTriggerVector[i] == "attacking")
20✔
1359
                attackingTrigger = true;
9✔
1360
            if(combatTriggerVector[i] == "attackedalone")
20✔
1361
                attackedAloneTrigger = true;
×
1362
            if(combatTriggerVector[i] == "notblocked")
20✔
1363
                notBlockedTrigger = true;
×
1364
            if(combatTriggerVector[i] == "blocked")
20✔
1365
                attackBlockedTrigger = true;
5✔
1366
            if(combatTriggerVector[i] == "blocking")
20✔
1367
                blockingTrigger = true;
4✔
1368
        }  
1369
        //build triggers TCs
1370
        TargetChooser *tc = parseSimpleTC(s, "source", card);
15✔
1371
        if(!tc)//a source( is required, from( is optional.
15✔
1372
            return NULL;
×
1373

1374
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
15✔
1375
        
1376
        return NEW TrCombatTrigger(observer, id, card, tc, fromTc, once, limitOnceATurn, sourceUntapped, opponentPoisoned,
1377
            attackingTrigger, attackedAloneTrigger, notBlockedTrigger, attackBlockedTrigger, blockingTrigger);
15✔
1378
    }
1379

1380
    //poisoned player - controller of card
1381
    if (TargetChooser * tc = parseSimpleTC(s, "poisonedof", card)){
37,797✔
1382
        int plus = 0;
×
1383
        if(s.find("plus(1)") != string::npos)
×
1384
            plus = 1;
×
1385
        else if(s.find("plus(2)") != string::npos)
×
1386
            plus = 2;
×
1387
        else if(s.find("plus(3)") != string::npos)
×
1388
            plus = 3;
×
1389
        else if(s.find("plus(4)") != string::npos)
×
1390
            plus = 4;
×
1391
        else if(s.find("plus(5)") != string::npos)
×
1392
            plus = 5;
×
1393
        return NEW TrplayerPoisoned(observer, id, card, tc, once, true, false, plus);
×
1394
    }
1395

1396
    //poisoned player - opponent of card controller
1397
    if (TargetChooser * tc = parseSimpleTC(s, "poisonedfoeof", card)){
37,797✔
1398
        int plus = 0;
×
1399
        if(s.find("plus(1)") != string::npos)
×
1400
            plus = 1;
×
1401
        else if(s.find("plus(2)") != string::npos)
×
1402
            plus = 2;
×
1403
        else if(s.find("plus(3)") != string::npos)
×
1404
            plus = 3;
×
1405
        else if(s.find("plus(4)") != string::npos)
×
1406
            plus = 4;
×
1407
        else if(s.find("plus(5)") != string::npos)
×
1408
            plus = 5;
×
1409
        return NEW TrplayerPoisoned(observer, id, card, tc, once, false, true, plus);
×
1410
    }
1411

1412
    //energized player - controller of card
1413
    if (TargetChooser * tc = parseSimpleTC(s, "energizedof", card)){
37,797✔
1414
        int plus = 0;
×
1415
        if(s.find("plus(1)") != string::npos)
×
1416
            plus = 1;
×
1417
        else if(s.find("plus(2)") != string::npos)
×
1418
            plus = 2;
×
1419
        else if(s.find("plus(3)") != string::npos)
×
1420
            plus = 3;
×
1421
        else if(s.find("plus(4)") != string::npos)
×
1422
            plus = 4;
×
1423
        else if(s.find("plus(5)") != string::npos)
×
1424
            plus = 5;
×
1425
        return NEW TrplayerEnergized(observer, id, card, tc, once, true, false, plus);
×
1426
    }
1427

1428
    //energized player - opponent of card controller
1429
    if (TargetChooser * tc = parseSimpleTC(s, "energizedfoeof", card)){
37,797✔
1430
        int plus = 0;
×
1431
        if(s.find("plus(1)") != string::npos)
×
1432
            plus = 1;
×
1433
        else if(s.find("plus(2)") != string::npos)
×
1434
            plus = 2;
×
1435
        else if(s.find("plus(3)") != string::npos)
×
1436
            plus = 3;
×
1437
        else if(s.find("plus(4)") != string::npos)
×
1438
            plus = 4;
×
1439
        else if(s.find("plus(5)") != string::npos)
×
1440
            plus = 5;
×
1441
        return NEW TrplayerEnergized(observer, id, card, tc, once, false, true, plus);
×
1442
    }
1443

1444
    //experienced player - controller of card
1445
    if (TargetChooser * tc = parseSimpleTC(s, "experiencedof", card)){
37,797✔
1446
        int plus = 0;
×
1447
        if(s.find("plus(1)") != string::npos)
×
1448
            plus = 1;
×
1449
        else if(s.find("plus(2)") != string::npos)
×
1450
            plus = 2;
×
1451
        else if(s.find("plus(3)") != string::npos)
×
1452
            plus = 3;
×
1453
        else if(s.find("plus(4)") != string::npos)
×
1454
            plus = 4;
×
1455
        else if(s.find("plus(5)") != string::npos)
×
1456
            plus = 5;
×
1457
        return NEW TrplayerExperienced(observer, id, card, tc, once, true, false, plus);
×
1458
    }
1459

1460
    //experienced player - opponent of card controller
1461
    if (TargetChooser * tc = parseSimpleTC(s, "experiencedfoeof", card)){
37,797✔
1462
        int plus = 0;
×
1463
        if(s.find("plus(1)") != string::npos)
×
1464
            plus = 1;
×
1465
        else if(s.find("plus(2)") != string::npos)
×
1466
            plus = 2;
×
1467
        else if(s.find("plus(3)") != string::npos)
×
1468
            plus = 3;
×
1469
        else if(s.find("plus(4)") != string::npos)
×
1470
            plus = 4;
×
1471
        else if(s.find("plus(5)") != string::npos)
×
1472
            plus = 5;
×
1473
        return NEW TrplayerExperienced(observer, id, card, tc, once, false, true, plus);
×
1474
    }
1475

1476
    //becomes monarch - controller of card
1477
    if (TargetChooser * tc = parseSimpleTC(s, "becomesmonarchof", card))
37,797✔
1478
        return NEW TrplayerMonarch(observer, id, card, tc, once, true, false);
×
1479

1480
    //becomes monarch - opponent of card controller
1481
    if (TargetChooser * tc = parseSimpleTC(s, "becomesmonarchfoeof", card))
37,797✔
1482
        return NEW TrplayerMonarch(observer, id, card, tc, once, false, true);
×
1483

1484
    //takes the initiative - controller of card
1485
    if (TargetChooser * tc = parseSimpleTC(s, "takeninitiativeof", card))
37,797✔
1486
        return NEW TrplayerInitiative(observer, id, card, tc, once, true, false);
×
1487

1488
    //takes the initiative - opponent of card controller
1489
    if (TargetChooser * tc = parseSimpleTC(s, "takeninitiativeof", card))
37,797✔
1490
        return NEW TrplayerInitiative(observer, id, card, tc, once, false, true);
×
1491

1492
    //shuffled library - controller of card
1493
    if (TargetChooser * tc = parseSimpleTC(s, "shuffledof", card))
37,797✔
1494
        return NEW TrplayerShuffled(observer, id, card, tc, once, true, false);
×
1495

1496
    //shuffled library - opponent of card controller
1497
    if (TargetChooser * tc = parseSimpleTC(s, "shuffledfoeof", card))
37,797✔
1498
        return NEW TrplayerShuffled(observer, id, card, tc, once, false, true);
×
1499

1500
    //drawn player - controller of card - dynamic version drawof(player) -> returns current controller even with exchange of card controller
1501
    if (TargetChooser * tc = parseSimpleTC(s, "drawof", card))
37,797✔
1502
        return NEW TrcardDrawn(observer, id, card, tc, once, true, false, limitOnceATurn);
×
1503

1504
    //drawn player - opponent of card controller - dynamic version drawfoeof(player) -> returns current opponent even with exchange of card controller
1505
    if (TargetChooser * tc = parseSimpleTC(s, "drawfoeof", card))
37,797✔
1506
        return NEW TrcardDrawn(observer, id, card, tc, once, false, true, limitOnceATurn);
2✔
1507

1508
    //Card card is drawn - static version - drawn(player) - any player; drawn(controller) - owner forever; drawn(opponent) - opponent forever
1509
    if (TargetChooser * tc = parseSimpleTC(s, "drawn", card))
37,795✔
1510
        return NEW TrcardDrawn(observer, id, card, tc,once);
×
1511

1512
    //Card is mutated
1513
    if (TargetChooser * tc = parseSimpleTC(s, "mutated", card))
37,795✔
1514
        return NEW TrCardMutated(observer, id, card, tc, once, limitOnceATurn);
×
1515

1516
    //boast has been performed from a card
1517
    if (TargetChooser * tc = parseSimpleTC(s, "boasted", card))
37,795✔
1518
        return NEW TrCardBoasted(observer, id, card, tc, once, limitOnceATurn);
×
1519

1520
    //a battle card has been defeated
1521
    if (TargetChooser * tc = parseSimpleTC(s, "defeated", card))
37,795✔
1522
        return NEW TrCardDefeated(observer, id, card, tc, once, limitOnceATurn);
×
1523

1524
    //Surveil has been performed from a card
1525
    if (TargetChooser * tc = parseSimpleTC(s, "surveiled", card))
37,795✔
1526
        return NEW TrCardSurveiled(observer, id, card, tc, once, limitOnceATurn);
×
1527

1528
    //Foretell has been performed from a card
1529
    if (TargetChooser * tc = parseSimpleTC(s, "foretold", card))
37,795✔
1530
        return NEW TrCardForetold(observer, id, card, tc, once, limitOnceATurn);
×
1531

1532
    //Train has been performed from a card
1533
    if (TargetChooser * tc = parseSimpleTC(s, "trained", card))
37,795✔
1534
        return NEW TrCardTrained(observer, id, card, tc, once, limitOnceATurn);
×
1535

1536
    //Scry has been performed from a card
1537
    if (TargetChooser * tc = parseSimpleTC(s, "scryed", card))
37,795✔
1538
        return NEW TrCardScryed(observer, id, card, tc, once, limitOnceATurn);
×
1539

1540
    //Esplores has been performed from a cardr
1541
    if (TargetChooser * tc = parseSimpleTC(s, "explored", card))
37,795✔
1542
        return NEW TrCardExplored(observer, id, card, tc, once, limitOnceATurn);
×
1543

1544
    //Dungeon has been completer from a card
1545
    if (TargetChooser * tc = parseSimpleTC(s, "dungeoncompleted", card)){
37,795✔
1546
        int totaldng = 0;
×
1547
        vector<string>res = parseBetween(s, "total(",")");
×
1548
        if(res.size()){
×
1549
            totaldng = atoi(res[1].c_str());
×
1550
        }
1551
        string playerName = "";
×
1552
        vector<string>from = parseBetween(s, "from(",")");
×
1553
        if(from.size() && from[1] == "opponent"){
×
1554
            playerName = card->controller()->opponent()->getDisplayName();
×
1555
        } else if(from.size() && from[1] == "controller"){
×
1556
            playerName = card->controller()->getDisplayName();
×
1557
        } 
1558
        return NEW TrCardDungeonCompleted(observer, id, card, tc, once, limitOnceATurn, totaldng, playerName);
×
1559
    }
1560

1561
    //Roll die has been performed from a card
1562
    if (TargetChooser * tc = parseSimpleTC(s, "dierolled", card)){
37,795✔
1563
        int rollresult = 0;
×
1564
        vector<string>res = parseBetween(s, "result(",")");
×
1565
        if(res.size()){
×
1566
            if(res[1] == "max"){
×
1567
                rollresult = -1;
×
1568
            } else {
1569
                rollresult = atoi(res[1].c_str());
×
1570
            }
1571
        }
1572
        string playerName = "";
×
1573
        vector<string>from = parseBetween(s, "from(",")");
×
1574
        if(from.size() && from[1] == "opponent"){
×
1575
            playerName = card->controller()->opponent()->getDisplayName();
×
1576
        } else if(from.size() && from[1] == "controller"){
×
1577
            playerName = card->controller()->getDisplayName();
×
1578
        } 
1579
        return NEW TrCardRolledDie(observer, id, card, tc, once, limitOnceATurn, rollresult, playerName);
×
1580
    }
1581

1582
    //Fip coin has been performed from a card
1583
    if (TargetChooser * tc = parseSimpleTC(s, "coinflipped", card)){
37,795✔
1584
        int flipresult = -1;
×
1585
        vector<string>res = parseBetween(s, "result(",")");
×
1586
        if(res.size() && res[1] == "head"){
×
1587
            flipresult = 0;
×
1588
        } else if(res.size() && (res[1] == "tails" || res[1] == "tail")){
×
1589
            flipresult = 1;
×
1590
        } else if(res.size() && res[1] == "won"){
×
1591
            flipresult = 2;
×
1592
        } else if(res.size() && res[1] == "lost"){
×
1593
            flipresult = 3;
×
1594
        }
1595
        string playerName = "";
×
1596
        vector<string>from = parseBetween(s, "from(",")");
×
1597
        if(from.size() && from[1] == "opponent"){
×
1598
            playerName = card->controller()->opponent()->getDisplayName();
×
1599
        } else if(from.size() && from[1] == "controller"){
×
1600
            playerName = card->controller()->getDisplayName();
×
1601
        }
1602
        return NEW TrCardFlippedCoin(observer, id, card, tc, once, limitOnceATurn, flipresult, playerName);
×
1603
    }
1604

1605
    //Token has been created
1606
    if (TargetChooser * tc = parseSimpleTC(s, "tokencreated", card))
37,795✔
1607
        return NEW TrTokenCreated(observer, id, card, tc, once, limitOnceATurn);
×
1608

1609
    //Card is sacrificed
1610
    if (TargetChooser * tc = parseSimpleTC(s, "sacrificed", card))
37,795✔
1611
        return NEW TrCardSacrificed(observer, id, card, tc, once, limitOnceATurn);
10✔
1612

1613
    //Card is exploited
1614
    if (TargetChooser * tc = parseSimpleTC(s, "exploited", card))
37,785✔
1615
        return NEW TrCardExploited(observer, id, card, tc, once, limitOnceATurn);
×
1616

1617
    //Card is Discarded
1618
    if (TargetChooser * tc = parseSimpleTC(s, "discarded", card))
37,785✔
1619
        return NEW TrCardDiscarded(observer, id, card, tc, once, limitOnceATurn);
2✔
1620

1621
    //Card is cycled
1622
    if (TargetChooser * tc = parseSimpleTC(s, "cycled", card))
37,783✔
1623
        return NEW TrCardDiscarded(observer, id, card, tc, once, limitOnceATurn, true);
4✔
1624

1625
    //Card Damaging non combat current controller
1626
    if (TargetChooser * tc = parseSimpleTC(s, "noncombatdamageof", card))
37,779✔
1627
    {
1628
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1629
        return NEW TrDamaged(observer, id, card, tc, fromTc, 2, false, false, once, true, false);
×
1630
    }
1631

1632
    //Card Damaging non combat current opponent
1633
    if (TargetChooser * tc = parseSimpleTC(s, "noncombatdamagefoeof", card))
37,779✔
1634
    {
1635
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
1✔
1636
        return NEW TrDamaged(observer, id, card, tc, fromTc, 2, false, false, once, false, true);
1✔
1637
    }
1638

1639
    //Card Damaging non combat static
1640
    if (TargetChooser * tc = parseSimpleTC(s, "noncombatdamaged", card))
37,778✔
1641
    {
1642
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1643
        return NEW TrDamaged(observer, id, card, tc, fromTc, 2, once);
×
1644
    }
1645

1646
    //Card Damaging combat current controller
1647
    if (TargetChooser * tc = parseSimpleTC(s, "combatdamageof", card))
37,778✔
1648
    {
1649
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1650
        return NEW TrDamaged(observer, id, card, tc, fromTc, 1, sourceUntapped, limitOnceATurn, once, true, false);
×
1651
    }
1652

1653
    //Card Damaging combat current opponent
1654
    if (TargetChooser * tc = parseSimpleTC(s, "combatdamagefoeof", card))
37,778✔
1655
    {
1656
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
2✔
1657
        return NEW TrDamaged(observer, id, card, tc, fromTc, 1, sourceUntapped, limitOnceATurn, once, false, true);
2✔
1658
    }
1659

1660
    //Card Damaging combat static
1661
    if (TargetChooser * tc = parseSimpleTC(s, "combatdamaged", card))
37,776✔
1662
    {
1663
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
7✔
1664
        return NEW TrDamaged(observer, id, card, tc, fromTc, 1, sourceUntapped, limitOnceATurn, once);
7✔
1665
    }
1666

1667
    //Card Damaging current controller
1668
    if (TargetChooser * tc = parseSimpleTC(s, "damageof", card))
37,769✔
1669
    {
1670
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
2✔
1671
        return NEW TrDamaged(observer, id, card, tc, fromTc, 0, sourceUntapped, limitOnceATurn, once, true, false);
2✔
1672
    }
1673

1674
    //Card Damaging current opponent
1675
    if (TargetChooser * tc = parseSimpleTC(s, "damagefoeof", card))
37,767✔
1676
    {
1677
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
11✔
1678
        return NEW TrDamaged(observer, id, card, tc, fromTc, 0, sourceUntapped, limitOnceATurn, once, false, true);
11✔
1679
    }
1680

1681
    //Card Damaging static
1682
    if (TargetChooser * tc = parseSimpleTC(s, "damaged", card))
37,756✔
1683
    {
1684
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
9✔
1685
        return NEW TrDamaged(observer, id, card, tc, fromTc, 0, sourceUntapped, limitOnceATurn, once);
9✔
1686
    }
1687

1688
    //Lifed current controller
1689
    if (TargetChooser * tc = parseSimpleTC(s, "lifeof", card))
37,747✔
1690
    {
1691
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
6✔
1692
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a life gain/loss card exception in order to avoid life gain loop (eg. Angels of Vitality)
6✔
1693
        if(exception)
6✔
1694
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 0, sourceUntapped, once, true, false, limitOnceATurn, exception->source);
×
1695
        else
1696
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 0, sourceUntapped, once, true, false, limitOnceATurn);
6✔
1697
    }
1698

1699
    //Lifed current opponent
1700
    if (TargetChooser * tc = parseSimpleTC(s, "lifefoeof", card))
37,741✔
1701
    {
1702
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1703
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a life gain/loss card exception in order to avoid life gain loop (eg. Angels of Vitality)
×
1704
        if(exception)
×
1705
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 0, sourceUntapped,once, false, true, limitOnceATurn, exception->source); 
×
1706
        else
1707
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 0, sourceUntapped,once, false, true, limitOnceATurn);
×
1708
    }
1709

1710
    //Lifed static
1711
    if (TargetChooser * tc = parseSimpleTC(s, "lifed", card))
37,741✔
1712
    {
1713
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1714
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a life gain/loss card exception in order to avoid life gain loop (eg. Angels of Vitality)
×
1715
        if(exception)
×
1716
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 0, sourceUntapped, once, false, false, limitOnceATurn, exception->source);
×
1717
        else
1718
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 0, sourceUntapped, once, false, false, limitOnceATurn);
×
1719
    }
1720

1721
    //Life Loss current player
1722
    if (TargetChooser * tc = parseSimpleTC(s, "lifelostof", card))
37,741✔
1723
    {
1724
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1725
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a life gain/loss card exception in order to avoid life gain loop (eg. Angels of Vitality)
×
1726
        if(exception)
×
1727
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 1, sourceUntapped, once, true, false, limitOnceATurn, exception->source);
×
1728
        else
1729
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 1, sourceUntapped, once, true, false, limitOnceATurn);
×
1730
    }
1731

1732
    //Life Loss current opponent
1733
    if (TargetChooser * tc = parseSimpleTC(s, "lifelostfoeof", card))
37,741✔
1734
    {
1735
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
1✔
1736
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a life gain/loss card exception in order to avoid life gain loop (eg. Angels of Vitality)
1✔
1737
        if(exception)
1✔
1738
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 1, sourceUntapped, once, false, true, limitOnceATurn,exception->source);
×
1739
        else
1740
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 1, sourceUntapped, once, false, true, limitOnceATurn);
1✔
1741
    }
1742

1743
    //Life Loss static
1744
    if (TargetChooser * tc = parseSimpleTC(s, "lifeloss", card))
37,740✔
1745
    {
1746
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1747
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a life gain/loss card exception in order to avoid life gain loop (eg. Angels of Vitality)
×
1748
        if(exception)
×
1749
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 1, sourceUntapped, once, false, false, limitOnceATurn, exception->source);
×
1750
        else
1751
            return NEW TrLifeGained(observer, id, card, tc, fromTc, 1, sourceUntapped, once, false, false, limitOnceATurn);
×
1752
    }
1753

1754
    //Card Damaged and killed by a creature this turn
1755
    if (TargetChooser * tc = parseSimpleTC(s, "vampired", card))
37,740✔
1756
    {
1757
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
1✔
1758
        return NEW TrVampired(observer, id, card, tc, fromTc, once, limitOnceATurn);
1✔
1759
    }
1760

1761
    //when card becomes the target of a spell or ability
1762
    if (TargetChooser * tc = parseSimpleTC(s, "targeted", card))
37,739✔
1763
    {
1764
        TargetChooser *fromTc = parseSimpleTC(s, "from", card);
×
1765
        return NEW TrTargeted(observer, id, card, tc, fromTc, 0, once, limitOnceATurn);
×
1766
    }
1767

1768
    if (s.find("totalcounteradded(") != string::npos)
37,739✔
1769
    {
1770
        vector<string>splitCounter = parseBetween(s,"totalcounteradded(",")");
×
1771
        Counter * counter = NULL;
×
1772
        bool duplicate = false;
×
1773
        int plus = 0;
×
1774
        if(s.find("plus(1)") != string::npos)
×
1775
            plus = 1;
×
1776
        else if(s.find("plus(2)") != string::npos)
×
1777
            plus = 2;
×
1778
        else if(s.find("plus(3)") != string::npos)
×
1779
            plus = 3;
×
1780
        else if(s.find("plus(4)") != string::npos)
×
1781
            plus = 4;
×
1782
        else if(s.find("plus(5)") != string::npos)
×
1783
            plus = 5;
×
1784
        else if(s.find("(duplicateall)") != string::npos)
×
1785
            duplicate = true;
×
1786
        else if(s.find("(any)") == string::npos)
×
1787
            counter = parseCounter(splitCounter[1],card,NULL);
×
1788
        TargetChooser * tc = parseSimpleTC(s, "from", card);
×
1789
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a counter add/remove exception in order to avoid counter loop (eg. Doubling Season)
×
1790
        if(exception)
×
1791
            return NEW TrTotalCounter(observer, id, card, counter, tc, 1, once, duplicate, plus, limitOnceATurn, exception->source);
×
1792
        else
1793
            return NEW TrTotalCounter(observer, id, card, counter, tc, 1, once, duplicate, plus, limitOnceATurn);
×
1794
    }
1795

1796
    if (s.find("totalcounterremoved(") != string::npos)
37,739✔
1797
    {
1798
        vector<string>splitCounter = parseBetween(s,"totalcounterremoved(",")");
×
1799
        Counter * counter = NULL;
×
1800
        bool duplicate = false;
×
1801
        int plus = 0;
×
1802
        if(s.find("plus(1)") != string::npos)
×
1803
            plus = 1;
×
1804
        else if(s.find("plus(2)") != string::npos)
×
1805
            plus = 2;
×
1806
        else if(s.find("plus(3)") != string::npos)
×
1807
            plus = 3;
×
1808
        else if(s.find("plus(4)") != string::npos)
×
1809
            plus = 4;
×
1810
        else if(s.find("plus(5)") != string::npos)
×
1811
            plus = 5;
×
1812
        else if(s.find("(duplicateall)") != string::npos)
×
1813
            duplicate = true;
×
1814
        else if(s.find("(any)") == string::npos)
×
1815
            counter = parseCounter(splitCounter[1],card,NULL);
×
1816
        TargetChooser * tc = parseSimpleTC(s, "from", card);
×
1817
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a counter add/remove exception in order to avoid counter loop (eg. Doubling Season)
×
1818
        if(exception)
×
1819
            return NEW TrTotalCounter(observer, id, card, counter, tc, 0, once, duplicate, plus, limitOnceATurn, exception->source);
×
1820
        else
1821
            return NEW TrTotalCounter(observer, id, card, counter, tc, 0, once, duplicate, plus, limitOnceATurn);
×
1822
    }
1823

1824
    if (s.find("counteradded(") != string::npos)
37,739✔
1825
    {
1826
        vector<string>splitCounter = parseBetween(s,"counteradded(",")");
×
1827
        Counter * counter = NULL;
×
1828
        bool duplicate = false;
×
1829
        if(s.find("(duplicateall)") != string::npos)
×
1830
            duplicate = true;
×
1831
        else if(s.find("(any)") == string::npos)
×
1832
            counter = parseCounter(splitCounter[1],card,NULL);
×
1833
        TargetChooser * tc = parseSimpleTC(s, "from", card);
×
1834
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a counter add/remove exception in order to avoid counter loop (eg. Doubling Season)
×
1835
        if(exception)
×
1836
            return NEW TrCounter(observer, id, card, counter, tc, 1, once, duplicate, limitOnceATurn, exception->source);
×
1837
        else
1838
            return NEW TrCounter(observer, id, card, counter, tc, 1, once, duplicate, limitOnceATurn);
×
1839
    }
1840

1841
    if (s.find("counterremoved(") != string::npos)
37,739✔
1842
    {
1843
        vector<string>splitCounter = parseBetween(s,"counterremoved(",")");
×
1844
        Counter * counter = NULL;
×
1845
        bool duplicate = false;
×
1846
        if(s.find("(duplicateall)") != string::npos)
×
1847
            duplicate = true;
×
1848
        else if(s.find("(any)") == string::npos)
×
1849
            counter = parseCounter(splitCounter[1],card,NULL);
×
1850
        TargetChooser * tc = parseSimpleTC(s, "from", card);
×
1851
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a counter add/remove exception in order to avoid counter loop (eg. Doubling Season)
×
1852
        if(exception)
×
1853
            return NEW TrCounter(observer, id, card, counter, tc, 0, once, duplicate, limitOnceATurn, exception->source);
×
1854
        else
1855
            return NEW TrCounter(observer, id, card, counter, tc, 0, once, duplicate, limitOnceATurn);
×
1856
    }
1857

1858
    if (s.find("countermod(") != string::npos)
37,739✔
1859
    {
1860
        vector<string>splitCounter = parseBetween(s,"countermod(",")");
×
1861
        Counter * counter = NULL;
×
1862
        if(s.find("(any)") == string::npos)
×
1863
            counter = parseCounter(splitCounter[1],card,NULL);
×
1864
        TargetChooser * tc = parseSimpleTC(s, "from", card);
×
1865
        TargetChooser *exception = parseSimpleTC(s, "except", card); // Added a new keyword except to specify a counter add/remove exception in order to avoid counter loop (eg. Doubling Season)
×
1866
        if(exception)
×
1867
            return NEW TrCounter(observer, id, card, counter, tc, 2, once, false, limitOnceATurn, exception->source);
×
1868
        else
1869
            return NEW TrCounter(observer, id, card, counter, tc, 2, once, false, limitOnceATurn);
×
1870
    }
1871

1872
    int who = 0;
37,739✔
1873
    if (s.find(" my") != string::npos)
37,739✔
1874
        who = 1;
1,492✔
1875
    if (s.find(" opponent") != string::npos)
37,739✔
1876
        who = -1;
23✔
1877
    if (s.find(" targetcontroller") != string::npos)
37,739✔
1878
        who = -2;
9✔
1879
    if (s.find(" targetedplayer") != string::npos)
37,739✔
1880
        who = -3;
3✔
1881

1882
    //Next Time...
1883
    found = s.find("next");
37,739✔
1884
    if (found != string::npos)
37,739✔
1885
    {
1886
        for (int i = 0; i < NB_MTG_PHASES; i++)
15✔
1887
        {
1888
            found = s.find(Constants::MTGPhaseCodeNames[i]);
15✔
1889
            if (found != string::npos)
15✔
1890
            {
1891
                return NEW TriggerNextPhase(observer, id, card, target, i, who, sourceUntapped, once);
5✔
1892
            }
1893
        }
1894
    }
1895

1896
    //Each Time...
1897
    found = s.find("each");
37,734✔
1898
    if (found != string::npos)
37,734✔
1899
    {
1900
        for (int i = 0; i < NB_MTG_PHASES; i++)
285,570✔
1901
        {
1902
            found = s.find(Constants::MTGPhaseCodeNames[i]);
285,570✔
1903
            if (found != string::npos)
285,570✔
1904
            {
1905
                return NEW TriggerAtPhase(observer, id, card, target, i, who, sourceUntapped, sourceTap, lifelost, lifeamount, once);
37,734✔
1906
            }
1907
        }
1908
    }
1909

1910
    //rebound trigger controller upkeep...
1911
    found = s.find("rebounded");
×
1912
    if (found != string::npos)
×
1913
    {
1914
        return NEW TriggerRebound(observer, id, card, target, 2, 1, sourceUntapped, once);
×
1915
    }
1916

1917
    return NULL;
×
1918
}
1919

1920
// When abilities encapsulate each other, gets the deepest one (it is the one likely to have the most relevant information)
1921
MTGAbility * AbilityFactory::getCoreAbility(MTGAbility * a)
272,162✔
1922
{
1923
    if(AForeach * fea = dynamic_cast<AForeach*>(a))
272,162✔
1924
        return getCoreAbility(fea->ability);
45✔
1925
        
1926
    if( AAsLongAs * aea = dynamic_cast<AAsLongAs*>(a))
272,117✔
1927
        return getCoreAbility(aea->ability);
178✔
1928
        
1929
    if (GenericTargetAbility * gta = dynamic_cast<GenericTargetAbility*> (a))
271,939✔
1930
        return getCoreAbility(gta->ability);
1,247✔
1931

1932
    if (GenericActivatedAbility * gaa = dynamic_cast<GenericActivatedAbility*> (a))
270,692✔
1933
        return getCoreAbility(gaa->ability);
1,501✔
1934

1935
    if (MultiAbility * abi = dynamic_cast<MultiAbility*>(a))
269,191✔
1936
        return getCoreAbility(abi->abilities[0]);
365✔
1937

1938
    if (NestedAbility * na = dynamic_cast<NestedAbility*> (a))
268,826✔
1939
    {
1940
        if(na->ability)
110,415✔
1941
            //only atempt to return a nestedability if it contains a valid ability. example where this causes a bug otherwise. AEquip is considered nested, but contains no ability.
1942
            return getCoreAbility(na->ability);
109,927✔
1943
    }
1944
    
1945
    if (MenuAbility * ma = dynamic_cast<MenuAbility*>(a))
158,899✔
1946
        return getCoreAbility(ma->abilities[0]);
52✔
1947

1948
    return a;
158,847✔
1949
}
1950

1951
//Parses a string and returns the corresponding MTGAbility object
1952
//Returns NULL if parsing failed
1953
//Beware, Spell CAN be null when the function is called by the AI trying to analyze the effects of a given card
1954
MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, bool activated, bool forceUEOT,
143,505✔
1955
                                            MTGGameZone * dest)
1956
{
1957
    size_t found;
1958
    bool asAlternate = false;
143,505✔
1959
    trim(s);
143,505✔
1960
    //TODO This block redundant with calling function
1961
    if (!card && spell)
143,505✔
1962
        card = spell->source;
×
1963
    if (!card)
143,505✔
1964
        return NULL;
20✔
1965
    MTGCardInstance * target = card->target;
143,485✔
1966
    if (!target)
143,485✔
1967
        target = card;
142,713✔
1968
    //pay and castcard?
1969
    if(s.find("castcard(restricted") != string::npos && (s.find("pay(") != string::npos || s.find("pay[[") != string::npos))
143,485✔
1970
        asAlternate = true;
×
1971
    //MTG Specific rules
1972
    //adds the bonus credit system
1973
    found = s.find("bonusrule");
143,485✔
1974
    if(found != string::npos)
143,485✔
1975
    {
1976
        observer->addObserver(NEW MTGEventBonus(observer, -1));
724✔
1977
        return NULL;
724✔
1978
    }
1979
    //putinplay/cast rule.. this is a parent rule and is required for all cost related rules.
1980
    found = s.find("putinplayrule");
142,761✔
1981
    if(found != string::npos)
142,761✔
1982
    {
1983
        observer->addObserver(NEW MTGPutInPlayRule(observer, -1));
724✔
1984
        return NULL;
724✔
1985
    }
1986
    //rule for kicker handling
1987
    found = s.find("kickerrule");
142,037✔
1988
    if(found != string::npos)
142,037✔
1989
    {
1990
        observer->addObserver(NEW MTGKickerRule(observer, -1));
724✔
1991
        return NULL;
724✔
1992
    }
1993
    //alternative cost types rule, this is a parent rule and is required for all cost related rules.
1994
    found = s.find("alternativecostrule");
141,313✔
1995
    if(found != string::npos)
141,313✔
1996
    {
1997
        observer->addObserver(NEW MTGAlternativeCostRule(observer, -1));
724✔
1998
        return NULL;
724✔
1999
    }
2000
    //alternative cost type buyback
2001
    found = s.find("buybackrule");
140,589✔
2002
    if(found != string::npos)
140,589✔
2003
    {
2004
        observer->addObserver(NEW MTGBuyBackRule(observer, -1));
724✔
2005
        return NULL;
724✔
2006
    }
2007
    //alternative cost type flashback
2008
    found = s.find("flashbackrule");
139,865✔
2009
    if(found != string::npos)
139,865✔
2010
    {
2011
        observer->addObserver(NEW MTGFlashBackRule(observer, -1));
724✔
2012
        observer->addObserver(NEW MTGTempFlashBackRule(observer, -1));
724✔
2013
        return NULL;
724✔
2014
    }
2015
    //alternative cost type flashback
2016
    found = s.find("bestowrule");
139,141✔
2017
    if (found != string::npos)
139,141✔
2018
    {
2019
        observer->addObserver(NEW MTGBestowRule(observer, -1));
724✔
2020
        return NULL;
724✔
2021
    }
2022
    //alternative cost type retrace
2023
    found = s.find("retracerule");
138,417✔
2024
    if(found != string::npos)
138,417✔
2025
    {
2026
        observer->addObserver(NEW MTGRetraceRule(observer, -1));
724✔
2027
        return NULL;
724✔
2028
    }
2029
    //alternative cost type suspend
2030
    found = s.find("suspendrule");
137,693✔
2031
    if(found != string::npos)
137,693✔
2032
    {
2033
        observer->addObserver(NEW MTGSuspendRule(observer, -1));
724✔
2034
        return NULL;
724✔
2035
    }
2036
    //alternative cost type morph
2037
    found = s.find("morphrule");
136,969✔
2038
    if(found != string::npos)
136,969✔
2039
    {
2040
        observer->addObserver(NEW MTGMorphCostRule(observer, -1));
724✔
2041
        return NULL;
724✔
2042
    }
2043
    found = s.find("payzerorule");
136,245✔
2044
    if(found != string::npos)
136,245✔
2045
    {
2046
        observer->addObserver(NEW MTGPayZeroRule(observer, -1));
724✔
2047
        return NULL;
724✔
2048
    }
2049
    found = s.find("overloadrule");
135,521✔
2050
    if(found != string::npos)
135,521✔
2051
    {
2052
        observer->addObserver(NEW MTGOverloadRule(observer, -1));
724✔
2053
        return NULL;
724✔
2054
    }
2055
    //this rule handles attacking ability during attacker phase
2056
    found = s.find("attackrule");
134,797✔
2057
    if(found != string::npos)
134,797✔
2058
    {
2059
        observer->addObserver(NEW MTGAttackRule(observer, -1));
724✔
2060
        return NULL;
724✔
2061
    }
2062
    //this rule handles attacking cost ability during attacker phase
2063
    found = s.find("attackcostrule");
134,073✔
2064
    if(found != string::npos)
134,073✔
2065
    {
2066
        observer->addObserver(NEW MTGAttackCostRule(observer, -1));
724✔
2067
        return NULL;
724✔
2068
    }
2069
    //this rule handles blocking ability during blocker phase
2070
    found = s.find("blockrule");
133,349✔
2071
    if(found != string::npos)
133,349✔
2072
    {
2073
        observer->addObserver(NEW MTGBlockRule(observer, -1));
724✔
2074
        return NULL;
724✔
2075
    }
2076
    //this rule handles blocking cost ability during blocker phase
2077
    found = s.find("blockcostrule");
132,625✔
2078
    if(found != string::npos)
132,625✔
2079
    {
2080
        observer->addObserver(NEW MTGBlockCostRule(observer, -1));
724✔
2081
        return NULL;
724✔
2082
    }
2083
    //this rule handles cards that have soulbond
2084
    found = s.find("soulbondrule");
131,901✔
2085
    if(found != string::npos)
131,901✔
2086
    {
2087
        observer->addObserver(NEW MTGSoulbondRule(observer, -1));
724✔
2088
        return NULL;
724✔
2089
    }
2090
    //this rule handles cards that have dredge
2091
    found = s.find("dredgerule");
131,177✔
2092
    if(found != string::npos)
131,177✔
2093
    {
2094
        observer->replacementEffects->add(NEW MTGDredgeRule(observer, -1));
724✔
2095
        return NULL;
724✔
2096
    }
2097
    //this rule handles combat related triggers. note, combat related triggered abilities will not work without it.
2098
    found = s.find("combattriggerrule");
130,453✔
2099
    if(found != string::npos)
130,453✔
2100
    {
2101
        observer->addObserver(NEW MTGCombatTriggersRule(observer, -1));
724✔
2102
        return NULL;
724✔
2103
    }
2104
    //this handles the legend rule
2105
    found = s.find("legendrule");
129,729✔
2106
    if(found != string::npos)
129,729✔
2107
    {
2108
        //observer->addObserver(NEW MTGLegendRule(observer, -1));
2109
        observer->addObserver(NEW MTGNewLegend(observer, -1));
724✔
2110
        return NULL;
724✔
2111
    }
2112
    //this handles the planeswalker named legend rule which is dramatically different from above.
2113
    found = s.find("planeswalkerrule");
129,005✔
2114
    if(found != string::npos)
129,005✔
2115
    {
2116
        //observer->addObserver(NEW MTGPlaneWalkerRule(observer, -1));
2117
        observer->addObserver(NEW MTGNewPlaneswalker(observer, -1));
724✔
2118
        return NULL;
724✔
2119
    }
2120
    found = s.find("planeswalkerdamage");
128,281✔
2121
    if(found != string::npos)
128,281✔
2122
    {
2123
        observer->addObserver(NEW MTGPlaneswalkerDamage(observer, -1));
724✔
2124
        return NULL;
724✔
2125
    }
2126
    found = s.find("planeswalkerattack");
127,557✔
2127
    if(found != string::npos)
127,557✔
2128
    {
2129
        observer->addObserver(NEW MTGPlaneswalkerAttackRule(observer, -1));
724✔
2130
        return NULL;
724✔
2131
    }
2132
    
2133
        //this handles the clean up of tokens !!MUST BE ADDED BEFORE PERSIST RULE!!
2134
    found = s.find("tokencleanuprule");
126,833✔
2135
    if(found != string::npos)
126,833✔
2136
    {
2137
        observer->addObserver(NEW MTGTokensCleanup(observer, -1));
724✔
2138
        return NULL;
724✔
2139
    }
2140
        //this handles the returning of cards with persist to the battlefield.
2141
    found = s.find("persistrule");
126,109✔
2142
    if(found != string::npos)
126,109✔
2143
    {
2144
        observer->addObserver(NEW MTGPersistRule(observer, -1));
724✔
2145
        return NULL;
724✔
2146
    }
2147
    //this handles the vectors of cards which attacked and were attacked and later died during that turn.
2148
    found = s.find("vampirerule");
125,385✔
2149
    if(found != string::npos)
125,385✔
2150
    {
2151
        observer->addObserver(NEW MTGVampireRule(observer, -1));
724✔
2152
        return NULL;
724✔
2153
    }
2154
    //this handles the removel of cards which were unearthed.
2155
    found = s.find("unearthrule");
124,661✔
2156
    if(found != string::npos)
124,661✔
2157
    {
2158
        observer->addObserver(NEW MTGUnearthRule(observer, -1));
724✔
2159
        return NULL;
724✔
2160
    }
2161
    //this handles lifelink ability rules.
2162
    found = s.find("lifelinkrule");
123,937✔
2163
    if(found != string::npos)
123,937✔
2164
    {
2165
        observer->addObserver(NEW MTGLifelinkRule(observer, -1));
724✔
2166
        return NULL;
724✔
2167
    }
2168
    //this handles death touch ability rule.
2169
    found = s.find("deathtouchrule");
123,213✔
2170
    if(found != string::npos)
123,213✔
2171
    {
2172
        observer->addObserver(NEW MTGDeathtouchRule(observer, -1));
724✔
2173
        return NULL;
724✔
2174
    }
2175

2176
    
2177
    if (StartsWith(s, "chooseacolor ") || StartsWith(s, "chooseatype ") || StartsWith(s, "chooseaname"))
122,489✔
2178
    {
2179
        MTGAbility * choose = parseChooseActionAbility(s,card,spell,target,0,id);
1✔
2180
        choose = NEW GenericActivatedAbility(observer, "","",id, card,choose,NULL);
1✔
2181
        MayAbility * mainAbility = NEW MayAbility(observer, id, choose, card,true);
1✔
2182
        return mainAbility;
1✔
2183
    }
2184

2185
    //need to remove the section inside the transforms ability from the string before parsing
2186
    //TODO: store string values of "&&" so we can remove the classes added just to add support
2187
    //the current parser finds other abilities inside what should be nested abilities, and converts them into
2188
    //actual abilities, this is a limitation.
2189
    string unchangedS = "";
244,976✔
2190
    unchangedS.append(s);
122,488✔
2191

2192
    //Reveal:x remove the core so we dont build them prematurely
2193
    vector<string>transPayfound = parseBetween(s, "newability[pay(", " ");
244,976✔
2194
    vector<string>transfound = parseBetween(s,"newability[reveal:"," ");//if we are using reveal inside a newability, let transforms remove the string instead.    
244,976✔
2195
    vector<string>abilfound = parseBetween(s, "ability$!name(reveal) reveal:", " ");
244,976✔
2196
    if(!abilfound.size())
122,488✔
2197
        abilfound = parseBetween(s, "ability$!reveal:", " ");//see above. this allows us to nest reveals inside these 2 other master classes. while also allowing us to nest them inside reveals.
122,488✔
2198
    
2199
    found = s.find("pay(");
122,488✔
2200
    if (found != string::npos && storedPayString.empty() && !transPayfound.size())
122,488✔
2201
    {
2202
        size_t pos1 = s.find("transforms(("); // Try to handle pay ability inside ability$! or transforms keywords.
2✔
2203
        size_t pos2 = s.find("ability$!");
2✔
2204
        if(pos2 == string::npos)
2✔
2205
            pos2 = s.find("winability"); // Try to handle pay ability inside winability or loseability keywords.
2✔
2206
        if(pos2 == string::npos)
2✔
2207
            pos2 = s.find("loseability");
2✔
2208
        vector<string> splitMayPaystr = parseBetween(s, "pay(", ")", true);
4✔
2209
        if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && found < pos1 && found < pos2) || 
2✔
2210
            (pos2 == string::npos && pos1 != string::npos && found < pos1) || (pos1 == string::npos && pos2 != string::npos && found < pos2)){
×
2211
            if (splitMayPaystr.size()){
4✔
2212
                storedPayString.append(splitMayPaystr[2]);
2✔
2213
                s = splitMayPaystr[0];
2✔
2214
                s.append("pay(");
2✔
2215
                s.append(splitMayPaystr[1]);
2✔
2216
                s.append(")");
2✔
2217
            } 
2218
        } else 
2219
              storedPayString.clear();
×
2220
    }
2221

2222
    vector<string> splitGrant = parseBetween(s, "grant ", " grantend", false);
244,976✔
2223
    if (splitGrant.size() && storedAbilityString.empty())
122,488✔
2224
    {
2225
        storedAbilityString = splitGrant[1];
×
2226
        size_t pos1 = s.find("transforms(("); // Try to handle grant ability inside ability$! or transforms keywords.
×
2227
        size_t pos2 = s.find("ability$!");
×
2228
        if(pos2 == string::npos)
×
2229
            pos2 = s.find("winability"); // Try to handle grant ability inside winability or loseability keywords.
×
2230
        if(pos2 == string::npos)
×
2231
            pos2 = s.find("loseability");
×
2232
        size_t pos3 = s.find(splitGrant[1]);
×
2233
        if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && pos3 <= pos1 && pos3 <= pos2) || 
×
2234
            (pos2 == string::npos && pos1 != string::npos && pos3 <= pos1) || (pos1 == string::npos && pos2 != string::npos && pos3 <= pos2)){
×
2235
            s = splitGrant[0];
×
2236
            s.append("grant ");
×
2237
            s.append(splitGrant[2]);
×
2238
        } else 
2239
            storedAbilityString.clear();
×
2240
    }
2241

2242
    vector<string> splitRevealx = parseBetween(s, "reveal:", " revealend", false);
244,976✔
2243
    if (!abilfound.size() && !transfound.size() && splitRevealx.size() && storedAbilityString.empty())
122,488✔
2244
    {
2245
        storedAbilityString = splitRevealx[1];
23✔
2246
        size_t pos1 = s.find("transforms(("); // Try to handle reveal ability inside ability$! or transforms keywords.
23✔
2247
        size_t pos2 = s.find("ability$!");
23✔
2248
        if(pos2 == string::npos)
23✔
2249
            pos2 = s.find("winability"); // Try to handle reveal ability inside winability or loseability keywords.
23✔
2250
        if(pos2 == string::npos)
23✔
2251
            pos2 = s.find("loseability");
23✔
2252
        size_t pos3 = s.find(splitRevealx[1]);
23✔
2253
        if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && pos3 <= pos1 && pos3 <= pos2) || 
23✔
2254
            (pos2 == string::npos && pos1 != string::npos && pos3 <= pos1) || (pos1 == string::npos && pos2 != string::npos && pos3 <= pos2)){
15✔
2255
            s = splitRevealx[0];
14✔
2256
            s.append("reveal: ");
14✔
2257
            s.append(splitRevealx[2]);
14✔
2258
        } else 
2259
            storedAbilityString.clear();
9✔
2260
    }
2261

2262
    vector<string> splitScryx = parseBetween(s, "scry:", " scryend", false);
244,976✔
2263
    if (splitScryx.size() && storedAbilityString.empty())
122,488✔
2264
    {
2265
        storedAbilityString = splitScryx[1];
×
2266
        size_t pos1 = s.find("transforms(("); // Try to handle scry ability inside ability$! or transforms keywords.
×
2267
        size_t pos2 = s.find("ability$!");
×
2268
        if(pos2 == string::npos)
×
2269
            pos2 = s.find("winability"); // Try to handle scry ability inside winability or loseability keywords.
×
2270
        if(pos2 == string::npos)
×
2271
            pos2 = s.find("loseability");
×
2272
        size_t pos3 = s.find(splitScryx[1]);
×
2273
        if((pos1 == string::npos && pos2 == string::npos) || (pos2 != string::npos && pos1 != string::npos && pos3 <= pos1 && pos3 <= pos2) || 
×
2274
            (pos2 == string::npos && pos1 != string::npos && pos3 <= pos1) || (pos1 == string::npos && pos2 != string::npos && pos3 <= pos2)){
×
2275
            s = splitScryx[0];
×
2276
            s.append("scry: ");
×
2277
            s.append(splitScryx[2]);
×
2278
        } else 
2279
            storedAbilityString.clear();
×
2280
    }
2281

2282
    found = s.find("transforms((");
122,488✔
2283
    if (found != string::npos && storedString.empty())
122,488✔
2284
    {
2285
        size_t pos1 = s.find("ability$!"); // Try to handle transforms ability inside ability$! keyword.
4,447✔
2286
        if(pos1 == string::npos)
4,447✔
2287
            pos1 = s.find("winability"); // Try to handle transforms ability inside winability or loseability keywords.
2,998✔
2288
        if(pos1 == string::npos)
4,447✔
2289
            pos1 = s.find("loseability");
2,998✔
2290
        if(pos1 == string::npos || found < pos1){
4,447✔
2291
            size_t real_end = s.find("))", found);
2,999✔
2292
            size_t stypesStartIndex = found + 12;
2,999✔
2293
            for(unsigned int i = stypesStartIndex; i < s.size(); i++){
2,999✔
2294
                size_t pos2 = s.find("transforms((", i); // Try to handle transforms ability inside transforms keyword.
2,999✔
2295
                if(pos2 != string::npos && pos2 < real_end){
2,999✔
2296
                    i = pos2 + 11;
×
2297
                    real_end = s.find("))", real_end + 2);
×
2298
                } else
2299
                    break;
2300
            }
2301
            storedString.append(s.substr(stypesStartIndex, real_end - stypesStartIndex).c_str());
2,999✔
2302
            s.erase(stypesStartIndex, real_end - stypesStartIndex);
2,999✔
2303
        }
2304
    }
2305

2306
    found = s.find("ability$!");
122,488✔
2307
    if (found != string::npos && storedAbilityString.empty())
122,488✔
2308
    {
2309
        size_t real_end = s.find("!$", found);
23,187✔
2310
        size_t sIndex = found + 9;
23,187✔
2311
        storedAbilityString.append(s.substr(sIndex, real_end - sIndex).c_str());
23,187✔
2312
        s.erase(sIndex, real_end - sIndex);
23,187✔
2313
    }
2314
    else
2315
    {
2316
        found = unchangedS.find("ability$!");//did find it in a changed s, try unchanged.
99,301✔
2317
        if (found != string::npos && storedAbilityString.empty())
99,301✔
2318
        {
2319
            size_t real_end = unchangedS.find("!$", found);
1✔
2320
            size_t sIndex = found + 9;
1✔
2321
            storedAbilityString.append(unchangedS.substr(sIndex, real_end - sIndex).c_str());
1✔
2322
            unchangedS.erase(sIndex, real_end - sIndex);
1✔
2323
        }
2324
    }
2325

2326
    found = s.find("pay[[");
122,488✔
2327
    if (found != string::npos && storedPayString.empty())
122,488✔
2328
    {
2329
        vector<string> splitMayPaystr = parseBetween(s, "pay[[", " ", true);
8✔
2330
        if(splitMayPaystr.size())
4✔
2331
        {
2332
            storedPayString.append(splitMayPaystr[2]);
4✔
2333
            s = splitMayPaystr[0];
4✔
2334
            s.append("pay[[");
4✔
2335
            s.append(splitMayPaystr[1]);
4✔
2336
            s.append("]]");
4✔
2337
        }
2338
    }
2339

2340
    found = s.find("and!(");
122,488✔
2341
    if (found != string::npos && found + 6 != ')' && storedAndAbility.empty())
122,488✔
2342
    {
2343
        vector<string> splitAnd = parseBetween(s, "and!(", ")!",false);
42✔
2344
        if(splitAnd.size())
21✔
2345
        {
2346
            storedAndAbility.append(splitAnd[1]);
21✔
2347
            size_t real_end = s.find(")!", found);
21✔
2348
            size_t sIndex = found + 5;
21✔
2349
            s.erase(sIndex, real_end - sIndex);
21✔
2350
        }
2351
    }
2352

2353
    vector<string> splitTrigger = parseBetween(s, "@", ":");
244,976✔
2354
    if (splitTrigger.size())
122,488✔
2355
    {
2356
        TriggeredAbility * trigger = parseTrigger(splitTrigger[1], s, id, spell, card, target);
43,727✔
2357
        if (splitTrigger[1].find("restriction{") != string::npos)//using other/cast restrictions for abilities.
43,727✔
2358
        {
2359
            vector<string> splitRest = parseBetween(s,"restriction{","}");
43,536✔
2360
            if (splitRest.size())
21,768✔
2361
                trigger->castRestriction = splitRest[1];
21,768✔
2362
        }
2363
        if (splitTrigger[1].find("restriction{{") != string::npos)
43,727✔
2364
        {
2365
            vector<string> splitRest = parseBetween(s,"restriction{{","}}");
×
2366
            if (splitRest.size())
×
2367
                trigger->castRestriction = splitRest[1];
×
2368
        }
2369
        //Dirty way to remove the trigger text (could get in the way)
2370
        if (trigger)
43,727✔
2371
        {
2372
            MTGAbility * a = parseMagicLine(splitTrigger[2], id, spell, card, activated);
43,727✔
2373
            if (!a)
43,727✔
2374
            {
2375
                delete trigger;
×
2376
                return NULL;
×
2377
            }
2378
            return NEW GenericTriggeredAbility(observer, id, card, trigger, a, NULL, target);
43,727✔
2379
        }
2380
    }
2381

2382
    //This one is not a real ability, it displays a message on the screen. We use this for tutorials
2383
    // Triggers need to be checked above this one, as events are usuallly what will trigger (...) these messages
2384
    {
2385
        vector<string> splitMsg = parseBetween(s, "tutorial(", ")");
157,522✔
2386
        if (splitMsg.size())
78,761✔
2387
        {
2388
            string msg = splitMsg[1];
×
2389
            return NEW ATutorialMessage(observer, card, msg);
×
2390
        }
2391

2392
        splitMsg = parseBetween(s, "message(", ")");
78,761✔
2393
        if (splitMsg.size())
78,761✔
2394
        {
2395
            string msg = splitMsg[1];
×
2396
            return NEW ATutorialMessage(observer, card, msg, 0);
×
2397
        }
2398
    }
2399

2400
    int restrictions = parseRestriction(s);
78,761✔
2401
    string castRestriction = "";
157,522✔
2402
    if (s.find("restriction{") != string::npos)//using other/cast restrictions for abilities.
78,761✔
2403
    {
2404
        vector<string> splitRest = parseBetween(s,"restriction{","}");
16✔
2405
        if (splitRest.size())
8✔
2406
            castRestriction = splitRest[1];
8✔
2407
    }
2408
    if (s.find("restriction{{") != string::npos)
78,761✔
2409
    {
2410
        vector<string> splitRest = parseBetween(s,"restriction{{","}}");
×
2411
        if (splitRest.size())
×
2412
            castRestriction = splitRest[1];
×
2413
    }
2414
    string newName = "";
157,522✔
2415
    vector<string> splitName = parseBetween(s, "name(", ")");
157,522✔
2416
    if (splitName.size())
78,761✔
2417
    {
2418
        newName = splitName[1];
1,546✔
2419
        s = splitName[0];
1,546✔
2420
        s.append(splitName[2]);
1,546✔
2421
        //we erase the name section from the string to avoid 
2422
        //accidently building an mtg ability with the text meant for menuText.
2423
    }
2424

2425
    TargetChooser * tc = NULL;
78,761✔
2426
    string sWithoutTc = s;
157,522✔
2427
    string tcString = "";
157,522✔
2428
    //Target Abilities - We also handle the case "notatarget" here, for things such as copy effects
2429
    bool isTarget = true;
78,761✔
2430
    vector<string> splitTarget = parseBetween(s, "notatarget(", ")");
157,522✔
2431
    if (splitTarget.size())
78,761✔
2432
        isTarget = false;
31✔
2433
    else
2434
        splitTarget = parseBetween(s, "target(", ")");
78,730✔
2435

2436
    if (splitTarget.size())
78,761✔
2437
    {
2438
        TargetChooserFactory tcf(observer);
225✔
2439
        tc = tcf.createTargetChooser(splitTarget[1], card);
225✔
2440
        tcString = splitTarget[1];
225✔
2441

2442
        if (!isTarget)
225✔
2443
        {
2444
            tc->targetter->bypassTC = true;
31✔
2445
            tc->targetter = NULL;
31✔
2446
        }
2447
        else
2448
            if (tc->targetter)
194✔
2449
                tc->targetter->bypassTC = false;
194✔
2450
        sWithoutTc = splitTarget[0];
225✔
2451
        sWithoutTc.append(splitTarget[2]);
225✔
2452
    }
2453

2454
    size_t delimiter = sWithoutTc.find("}:");
78,761✔
2455
    size_t firstNonSpace = sWithoutTc.find_first_not_of(" ");
78,761✔
2456
    if (delimiter != string::npos && firstNonSpace != string::npos && sWithoutTc[firstNonSpace] == '{')
78,761✔
2457
    {
2458
        ManaCost * cost = ManaCost::parseManaCost(sWithoutTc.substr(0, delimiter + 1), NULL, card);
7,747✔
2459
        int doTap = 0; //Tap in the cost ?
7,747✔
2460
        if(cost && cost->extraCosts)
7,747✔
2461
        {
2462
            for(unsigned int i = 0; i < cost->extraCosts->costs.size();i++)
15,087✔
2463
            {
2464
                ExtraCost * tapper = dynamic_cast<TapCost*>(cost->extraCosts->costs[i]);
7,551✔
2465
                if(tapper)
7,551✔
2466
                    doTap = 1;
7,463✔
2467

2468
            }
2469
        }
2470
        if (doTap || cost)
7,747✔
2471
        {
2472
            string s1 = sWithoutTc.substr(delimiter + 2);
15,494✔
2473
            //grabbing the sideffect string and amount before parsing abilities.
2474
            //side effect ei:dragond whelp.
2475
            MTGAbility * sideEffect = NULL;
7,747✔
2476
            string usesBeforeSideEffect = "";
15,494✔
2477
            size_t limiteffect_str = s1.find("limit^");
7,747✔
2478
            if (limiteffect_str != string::npos)
7,747✔
2479
            {
2480
            size_t end = s1.rfind("^");
1✔
2481
            string sideEffectStr = s1.substr(limiteffect_str + 6,end - limiteffect_str - 6);
2✔
2482
            s1.erase(limiteffect_str,end - limiteffect_str);
1✔
2483
            end = s1.find("^");
1✔
2484
            usesBeforeSideEffect = s1.substr(end+1);
1✔
2485
            s1.erase(end-1);
1✔
2486
            sideEffect = parseMagicLine(sideEffectStr, id, spell, card, 1);
1✔
2487
            }
2488
            
2489
            
2490
            MTGAbility * a = parseMagicLine(s1, id, spell, card, 1);
7,747✔
2491
            if (!a)
7,747✔
2492
            {
2493
                DebugTrace("ABILITYFACTORY Error parsing: " << sWithoutTc);
×
2494
                return NULL;
×
2495
            }
2496
            string limit = "";
15,494✔
2497
            size_t limit_str = sWithoutTc.find("limit:");
7,747✔
2498
            if (limit_str != string::npos)
7,747✔
2499
            {
2500
                limit = sWithoutTc.substr(limit_str + 6);
11✔
2501
            }
2502
            ////A stupid Special case for ManaProducers, becuase Ai only understands manaabilities that are not nested.
2503
            AManaProducer * amp = dynamic_cast<AManaProducer*> (a);
7,747✔
2504
            if (amp)
7,747✔
2505
            {
2506
                amp->setCost(cost);
7,353✔
2507
                if (cost)
7,353✔
2508
                    cost->setExtraCostsAction(a, card);
7,353✔
2509
                amp->oneShot = 0;
7,353✔
2510
                amp->tap = doTap;
7,353✔
2511
                amp->limit = limit;
7,353✔
2512
                amp->sideEffect = sideEffect;
7,353✔
2513
                amp->usesBeforeSideEffects = usesBeforeSideEffect;
7,353✔
2514
                amp->restrictions = restrictions;
7,353✔
2515
                amp->castRestriction = castRestriction;
7,353✔
2516
                return amp;
7,353✔
2517
            }
2518
            
2519
            AEquip *ae = dynamic_cast<AEquip*> (a);
394✔
2520
            if (ae)
394✔
2521
            {
2522
                ae->setCost(cost);
56✔
2523
                if (!tc)
56✔
2524
                {
2525
                    TargetChooserFactory tcf(observer);
56✔
2526
                    tc = tcf.createTargetChooser("creature|mybattlefield", card);
56✔
2527
                }
2528
                ae->setActionTC(tc);
56✔
2529
                return ae;
56✔
2530
            }
2531
            if (tc)
338✔
2532
            {
2533
                tc->belongsToAbility = sWithoutTc;
116✔
2534
                return NEW GenericTargetAbility(observer, newName,castRestriction,id, card, tc, a, cost, limit,sideEffect,usesBeforeSideEffect, restrictions, dest,tcString);
116✔
2535
            }
2536
            return NEW GenericActivatedAbility(observer, newName,castRestriction,id, card, a, cost, limit,sideEffect,usesBeforeSideEffect,restrictions, dest);
222✔
2537
        }
2538
        SAFE_DELETE(cost);
×
2539
    }
2540

2541
    // figure out alternative cost effects
2542
    for (size_t i = 0; i < sizeof(kAlternateCostIds)/sizeof(kAlternateCostIds[0]); ++i)
852,152✔
2543
    {
2544
        const string& keyword = kAlternateCostKeywords[i];
781,140✔
2545
        if (s.find(keyword) == 0)
781,140✔
2546
        {
2547
            if (!(spell && spell->FullfilledAlternateCost(kAlternateCostIds[i])))
2✔
2548
            {
2549
                DebugTrace("INFO parseMagicLine: Alternative Cost was not fulfilled for " << spell << s);
×
2550
                SAFE_DELETE(tc);
×
2551
                return NULL;
×
2552
            }
2553
            return parseMagicLine(s.substr(keyword.length()), id, spell, card);
2✔
2554
        }
2555
    }
2556
    //if/ifnot COND then DO EFFECT.
2557
    const string ifKeywords[] = {"if ", "ifnot "};
142,024✔
2558
    int checkIf[] = { 1, 2 };
71,012✔
2559
    for (size_t i =0; i < sizeof(checkIf)/sizeof(checkIf[0]); ++i)
212,971✔
2560
    {
2561
        if (sWithoutTc.find(ifKeywords[i]) == 0)
141,992✔
2562
        {
2563
            string cond = sWithoutTc.substr(ifKeywords[i].length(),ifKeywords[i].length() + sWithoutTc.find(" then ")-6);
66✔
2564
            size_t foundElse = s.find(" else ");
33✔
2565
            MTGAbility * a2 = NULL;
33✔
2566
            if(foundElse != string::npos)
33✔
2567
            {
2568
                string s2 = s.substr(foundElse+6);
6✔
2569
                if(s2.size())
3✔
2570
                {
2571
                    s.erase(s.find(" else ")+1);
3✔
2572
                    a2 = parseMagicLine(s2, id, spell, card);
3✔
2573
                }
2574
            }
2575
            string s1 = s;
66✔
2576
            MTGAbility * a1 = NULL;
33✔
2577
            if (s1.find(" then ") != string::npos)
33✔
2578
            a1 = parseMagicLine(s1.substr(s1.find(" then ") + 6), id, spell, card);
33✔
2579
            if(!a1) return NULL;
33✔
2580
            MTGAbility * a = NEW IfThenAbility(observer, id, a1,a2, card,(Targetable*)target,checkIf[i],cond);
29✔
2581
            a->canBeInterrupted = false;
29✔
2582
            a->oneShot = true;
29✔
2583
            if(tc)
29✔
2584
                SAFE_DELETE(tc);
×
2585
            return a;
29✔
2586
        }
2587
    }
2588
    //may pay ability
2589
    vector<string> splitMayPay = parseBetween(s, "pay(", ")", true);
141,958✔
2590
    if(splitMayPay.size())
70,979✔
2591
    {
2592
        GenericPaidAbility * a = NEW GenericPaidAbility(observer, id, card, target,newName,castRestriction,splitMayPay[1],storedPayString,asAlternate);
2✔
2593
        a->oneShot = 1;
2✔
2594
        a->canBeInterrupted = false;
2✔
2595
        return a;
2✔
2596
    }
2597
    //When...comes into play, you may...
2598
    //When...comes into play, choose one...
2599
    const string mayKeywords[] = {"may ", "choice "};
141,954✔
2600
    const bool mayMust[] = { false, true };
70,977✔
2601
    for (size_t i =0; i < sizeof(mayMust)/sizeof(mayMust[0]); ++i)
212,809✔
2602
    {
2603
        if (sWithoutTc.find(mayKeywords[i]) == 0)
141,900✔
2604
        {
2605
            string s1 = sWithoutTc.substr(mayKeywords[i].length());
136✔
2606
            MTGAbility * a1 = parseMagicLine(s1, id, spell, card);
68✔
2607
            if (!a1)
68✔
2608
                return NULL;
×
2609

2610
            if (tc)
68✔
2611
                a1 = NEW GenericTargetAbility(observer, newName,castRestriction,id, card, tc, a1);
28✔
2612
            else
2613
                a1 = NEW GenericActivatedAbility(observer, newName,castRestriction,id, card, a1, NULL);
40✔
2614
            MayAbility * mainAbility = NEW MayAbility(observer, id, a1, card,mayMust[i],castRestriction);
68✔
2615
            return mainAbility;
68✔
2616
        }
2617
    }
2618
    // Generic "Until end of turn" effect
2619
    if (s.find("ueot ") == 0)
70,909✔
2620
    {
2621
        string s1 = s.substr(5);
32✔
2622
        MTGAbility * a1 = parseMagicLine(s1, id, spell, card);
16✔
2623
        if (!a1)
16✔
2624
            return NULL;
×
2625

2626
        return NEW GenericInstantAbility(observer, 1, card, (Damageable *) target, a1);
16✔
2627
    }
2628

2629
    //add an ability to the game, this is the "addToGame" version of the above ability.
2630
    if (s.find("activate ") == 0 || s.find(" activate ") == 0)
70,893✔
2631
    {
2632
        string s1 = s.substr(9);
24✔
2633
        MTGAbility * a1 = parseMagicLine(s1, id, spell, card);
12✔
2634
        if (!a1)
12✔
2635
            return NULL;
×
2636

2637
        return NEW GenericAddToGame(observer,1, card, (Damageable *) target,a1);
12✔
2638
    }
2639
    
2640
        // neverending effect
2641
    if (s.find("emblem ") == 0)
70,881✔
2642
    {
2643
        string s1 = s.substr(7);
6✔
2644
        MTGAbility * a1 = parseMagicLine(s1, id, spell, card);
3✔
2645
        if (!a1)
3✔
2646
            return NULL;
×
2647

2648
        return NEW GenericAbilityMod(observer, 1, card->controller()->getObserver()->ExtraRules,card->controller()->getObserver()->ExtraRules, a1);
3✔
2649
    }
2650

2651
    //choose a color
2652
    vector<string> splitChooseAColor = parseBetween(s, "activatechooseacolor ", " activatechooseend");
141,756✔
2653
    if (splitChooseAColor.size())
70,878✔
2654
    {
2655
        return parseChooseActionAbility(unchangedS,card,spell,target,restrictions,id);
×
2656
    }
2657

2658
    //choose a type
2659
    vector<string> splitChooseAType = parseBetween(s, "activatechooseatype ", " activatechooseend");
141,756✔
2660
    if (splitChooseAType.size())
70,878✔
2661
    {
2662
        return parseChooseActionAbility(unchangedS,card,spell,target,restrictions,id);
×
2663
    }
2664

2665
    //Upkeep Cost
2666
    found = s.find("upcostmulti");
70,878✔
2667
    if (found != string::npos)
70,878✔
2668
    {
2669
        return AbilityFactory::parseUpkeepAbility(s,card,spell,restrictions,id);
×
2670
    }
2671
    //Phase based actions
2672
    found = s.find("phaseactionmulti");
70,878✔
2673
    if (found != string::npos)
70,878✔
2674
    {
2675
        return parsePhaseActionAbility(s,card,spell,target,restrictions,id);
2✔
2676
    }
2677

2678
    int forcedalive = 0;
70,876✔
2679
    //force an ability to ignore destroy while source is still valid.
2680
    //allow the lords to remove the ability instead of gameobserver checks.
2681
    found = s.find("forcedalive");
70,876✔
2682
    if (found != string::npos)
70,876✔
2683
        forcedalive = 1;
8✔
2684
    bool neverRemove = false;
70,876✔
2685
    found = s.find("dontremove");
70,876✔
2686
    if (found != string::npos)
70,876✔
2687
        neverRemove = true;
2✔
2688
    //rather dirty way to stop thises and lords from conflicting with each other.
2689
    size_t lord = string::npos;
70,876✔
2690
    for (size_t j = 0; j < kLordKeywordsCount; ++j)
425,256✔
2691
    {
2692
        size_t found2 = s.find(kLordKeywords[j]);
354,380✔
2693
        if (found2 != string::npos && ((found == string::npos) || found2 < found))
354,380✔
2694
        {
2695
            lord = found2;
12,190✔
2696
        }
2697
    }
2698

2699
    //This, ThisForEach;
2700
    found = string::npos;
70,876✔
2701
    int i = -1;
70,876✔
2702
    for (size_t j = 0; j < kThisKeywordsCount; ++j)
283,504✔
2703
    {
2704
        size_t found2 = s.find(kThisKeywords[j]);
212,628✔
2705
        if (found2 != string::npos && ((found == string::npos) || found2 < found))
212,628✔
2706
        {
2707
            found = found2;
48✔
2708
            i = j;
48✔
2709
        }
2710
    }
2711
    if (found != string::npos && found < lord)
70,876✔
2712
    {
2713
        //why does tc even exist here? This shouldn't happen...
2714
        SAFE_DELETE(tc); //http://code.google.com/p/wagic/issues/detail?id=424
48✔
2715

2716
        size_t header = kThisKeywords[i].size();
48✔
2717
        size_t end = s.find(")", found + header);
48✔
2718
        string s1;
96✔
2719
        if (found == 0 || end != s.size() - 1)
48✔
2720
        {
2721
            s1 = s.substr(end + 1);
48✔
2722
        }
2723
        else
2724
        {
2725
            s1 = s.substr(0, found);
×
2726
        }
2727
        if (end != string::npos)
48✔
2728
        {
2729
            ThisDescriptor * td = NULL;
48✔
2730
            string thisDescriptorString = s.substr(found + header, end - found - header);
96✔
2731
            vector<string> splitRest = parseBetween(s, "restriction{", "}");
96✔
2732
            if (splitRest.size())
48✔
2733
            {
2734

2735

2736
            }
2737
            else
2738
            {
2739
                ThisDescriptorFactory tdf;
2740
                td = tdf.createThisDescriptor(observer, thisDescriptorString);
48✔
2741

2742
                if (!td)
48✔
2743
                {
2744
                    DebugTrace("MTGABILITY: Parsing Error:" << s);
6✔
2745
                    return NULL;
6✔
2746
                }
2747
            }
2748

2749
            MTGAbility * a = parseMagicLine(s1, id, spell, card, 0, activated);
42✔
2750
            if (!a)
42✔
2751
            {
2752
                SAFE_DELETE(td);
×
2753
                return NULL;
×
2754
            }
2755

2756
            MTGAbility * result = NULL;
42✔
2757
            bool oneShot = false;
42✔
2758
            found = s.find(" oneshot");
42✔
2759
            if (found != string::npos || activated ||
125✔
2760
                card->hasType(Subtypes::TYPE_SORCERY) ||
82✔
2761
                card->hasType(Subtypes::TYPE_INSTANT) ||
124✔
2762
                a->oneShot)
41✔
2763
            {
2764
                oneShot = true;
5✔
2765
            }
2766
            found = s.find("while ");
42✔
2767
            if (found != string::npos)
42✔
2768
                oneShot = false;
×
2769

2770
            Damageable * _target = NULL;
42✔
2771
            if (spell)
42✔
2772
                _target = spell->getNextDamageableTarget();
39✔
2773
            if (!_target)
42✔
2774
                _target = target;
42✔
2775

2776
            switch (i)
42✔
2777
            {
2778
            case 0:
2779
                result = NEW AThis(observer, id, card, _target, td, a);
36✔
2780
                break;
36✔
2781
            case 1:
2782
                result = NEW AThisForEach(observer, id, card, _target, td, a);
6✔
2783
                break;
6✔
2784
            case 2:
2785
                result = NEW AThis(observer, id, card, _target, NULL, a, thisDescriptorString);
×
2786
                break;
×
2787
            default:
2788
                result = NULL;
×
2789
            }
2790
            if (result)
42✔
2791
            {
2792
                result->oneShot = oneShot;
42✔
2793
                a->forcedAlive = forcedalive;
42✔
2794
                if(neverRemove)
42✔
2795
                {
2796
                    result->forceDestroy = -1;
×
2797
                    result->forcedAlive = 1;
×
2798
                    a->forceDestroy = -1;
×
2799
                    a->forcedAlive = 1;
×
2800
                }
2801

2802
            }
2803
            return result;
42✔
2804
        }
2805
        return NULL;
×
2806
    }
2807

2808
    //Multiple abilities for ONE cost
2809
    found = s.find("&&");
70,828✔
2810
    if (found != string::npos)
70,828✔
2811
    {
2812
        SAFE_DELETE(tc);
73✔
2813
        vector<string> multiEffects = split(s,'&');
146✔
2814
        MultiAbility * multi = NEW MultiAbility(observer, id, card, target, NULL);
73✔
2815
        for(unsigned int i = 0;i < multiEffects.size();i++)
310✔
2816
        {
2817
            if(!multiEffects[i].empty())
237✔
2818
            {
2819
                MTGAbility * addAbility = parseMagicLine(multiEffects[i], id, spell, card, activated);
155✔
2820
                multi->Add(addAbility);
155✔
2821
            }
2822
        }
2823
        multi->oneShot = 1;
73✔
2824
        return multi;
73✔
2825
    }
2826

2827

2828
    //Lord, foreach, aslongas
2829

2830
    found = string::npos;
70,755✔
2831
    i = -1;
70,755✔
2832
    for (size_t j = 0; j < kLordKeywordsCount; ++j)
424,530✔
2833
    {
2834
        size_t found2 = s.find(kLordKeywords[j]);
353,775✔
2835
        if (found2 != string::npos && ((found == string::npos) || found2 < found))
353,775✔
2836
        {
2837
            found = found2;
12,135✔
2838
            i = j;
12,135✔
2839
        }
2840
    }
2841
    if (found != string::npos)
70,755✔
2842
    {
2843
        SAFE_DELETE(tc);
12,131✔
2844
        size_t header = kLordKeywords[i].size();
12,131✔
2845
        size_t end = s.find(")", found + header);
12,131✔
2846
        string s1;
24,262✔
2847
        if (found == 0 || end != s.size() - 1)
12,131✔
2848
        {
2849
            s1 = s.substr(end + 1);
12,058✔
2850
        }
2851
        else
2852
        {
2853
            s1 = s.substr(0, found);
73✔
2854
        }
2855
        if (end != string::npos)
12,131✔
2856
        {
2857
            int lordIncludeSelf = 1;
12,131✔
2858
            size_t other = s1.find(" other");
12,131✔
2859
            if (other != string::npos)
12,131✔
2860
            {
2861
                lordIncludeSelf = 0;
8✔
2862
                s1.replace(other, 6, "");
8✔
2863
            }
2864
            string lordTargetsString = s.substr(found + header, end - found - header);
24,262✔
2865
            TargetChooserFactory tcf(observer);
12,131✔
2866
            TargetChooser * lordTargets = tcf.createTargetChooser(lordTargetsString, card);
12,131✔
2867

2868
            if (!lordTargets)
12,131✔
2869
            {
2870
                DebugTrace("MTGABILITY: Parsing Error: " << s);
×
2871
                return NULL;
×
2872
            }
2873

2874
            MTGAbility * a = parseMagicLine(s1, id, spell, card, false, activated); //activated lords usually force an end of turn ability
12,131✔
2875
            if (!a)
12,131✔
2876
            {
2877
                SAFE_DELETE(lordTargets);
4✔
2878
                return NULL;
4✔
2879
            }
2880
            MTGAbility * result = NULL;
12,127✔
2881
            bool oneShot = false;
12,127✔
2882
            if (activated || i == 4 || a->oneShot)
12,127✔
2883
                oneShot = true;
4,554✔
2884
            if (card->hasType(Subtypes::TYPE_SORCERY) || card->hasType(Subtypes::TYPE_INSTANT))
12,127✔
2885
                oneShot = true;
74✔
2886
            found = s.find("while ");
12,127✔
2887
            if (found != string::npos)
12,127✔
2888
                oneShot = false;
8✔
2889
            found = s.find(" oneshot");
12,127✔
2890
            if (found != string::npos)
12,127✔
2891
                oneShot = true;
1,457✔
2892
            Damageable * _target = NULL;
12,127✔
2893
            if (spell)
12,127✔
2894
                _target = spell->getNextDamageableTarget();
1,908✔
2895
            if (!_target)
12,127✔
2896
                _target = target;
12,071✔
2897

2898
            int mini = 0;
12,127✔
2899
            int maxi = 0;
12,127✔
2900
            bool miniFound = false;
12,127✔
2901
            bool maxiFound = false;
12,127✔
2902
            bool compareZone = false;
12,127✔
2903

2904
            found = s.find(" >");
12,127✔
2905
            if (found != string::npos)
12,127✔
2906
            {
2907
                mini = atoi(s.substr(found + 2, 3).c_str());
34✔
2908
                miniFound = true;
34✔
2909
            }
2910

2911
            found = s.find(" <");
12,127✔
2912
            if (found != string::npos)
12,127✔
2913
            {
2914
                maxi = atoi(s.substr(found + 2, 3).c_str());
20✔
2915
                maxiFound = true;
20✔
2916
            }
2917
            
2918
            found = s.find("compare");
12,127✔
2919
            if (found != string::npos)
12,127✔
2920
            {
2921
                compareZone = true;
×
2922
            }
2923
            
2924
            switch (i)
12,127✔
2925
            {
2926
            case 0:
2927
                result = NEW ALord(observer, id, card, lordTargets, lordIncludeSelf, a);
7,401✔
2928
                break;
7,401✔
2929
            case 1:
2930
                result = NEW AForeach(observer, id, card, _target, lordTargets, lordIncludeSelf, a, mini, maxi);
19✔
2931
                break;
19✔
2932
            case 2:
2933
                {
2934
                    if (!miniFound && !maxiFound)//for code without an operator treat as a mini.
80✔
2935
                    {
2936
                        miniFound = true;
33✔
2937
                    }
2938
                    result = NEW AAsLongAs(observer, id, card, _target, lordTargets, lordIncludeSelf, a, mini, maxi, miniFound, maxiFound, compareZone);
80✔
2939
                }
2940
                break;
80✔
2941
            case 3:
2942
                result = NEW ATeach(observer, id, card, lordTargets, lordIncludeSelf, a);
129✔
2943
                break;
129✔
2944
            case 4:
2945
                result = NEW ALord(observer, id, card, lordTargets, lordIncludeSelf, a);
4,498✔
2946
                break;
4,498✔
2947
            default:
2948
                result = NULL;
×
2949
            }
2950
            if (result)
12,127✔
2951
            {
2952
                result->oneShot = oneShot;
12,127✔
2953
                a->forcedAlive = forcedalive;
12,127✔
2954
                if (neverRemove)
12,127✔
2955
                {
2956
                    result->oneShot = false;
×
2957
                    result->forceDestroy = -1;
×
2958
                    result->forcedAlive = 1;
×
2959
                    a->forceDestroy = -1;
×
2960
                    a->forcedAlive = 1;
×
2961
                }
2962
            }
2963
            return result;
12,127✔
2964
        }
2965
        return NULL;
×
2966
    }
2967

2968
    //soulbond lord style ability.
2969
    found = s.find("soulbond ");
58,624✔
2970
    if (found != string::npos)
58,624✔
2971
    {
2972
        string s1 = s.substr(found + 9);
×
2973
        MTGAbility * a = parseMagicLine(s1, id, spell, card, false, activated);
×
2974
        if(a)
×
2975
            return NEW APaired(observer,id, card,card->myPair,a);
×
2976
        return NULL;
×
2977
    }
2978

2979
    //mana of the listed type doesnt get emptied from the pools.
2980
    vector<string>colorType = parseBetween(s,"poolsave(",")",false);
117,248✔
2981
    if (colorType.size())
58,624✔
2982
    {
2983
        return NEW AManaPoolSaver(observer, id, card, colorType[1], s.find("opponentpool")!=string::npos, s.find("terminate") != string::npos);// Added a way to terminate effect when source card leave battlefield.
×
2984
    }
2985

2986
    //opponent replace draw with
2987
    found = s.find("opponentreplacedraw ");
58,624✔
2988
    if (found != string::npos)
58,624✔
2989
    {
2990
        string s1 = s.substr(found + 19);
×
2991
        MTGAbility * a = NULL;
×
2992
        a = parseMagicLine(s1, id, spell, card, false, activated);
×
2993
        if(a)
×
2994
            return NEW ADrawReplacer(observer,id, card,a,true);
×
2995
        return NULL;
×
2996
    }
2997

2998
    //replace draw with
2999
    found = s.find("replacedraw ");
58,624✔
3000
    if (found != string::npos)
58,624✔
3001
    {
3002
        string s1 = s.substr(found + 11);
×
3003
        MTGAbility * a = NULL;
×
3004
        a = parseMagicLine(s1, id, spell, card, false, activated);
×
3005
        if(a)
×
3006
            return NEW ADrawReplacer(observer,id, card,a);
×
3007
        return NULL;
×
3008
    }
3009

3010
    if (!activated && tc)
58,624✔
3011
    {
3012
        MTGAbility * a = parseMagicLine(sWithoutTc, id, spell, card);
62✔
3013
        if (!a)
62✔
3014
        {
3015
            DebugTrace("ABILITYFACTORY Error parsing: " << s);
×
3016
            return NULL;
×
3017
        }
3018
        a = NEW GenericTargetAbility(observer, newName,castRestriction,id, card, tc, a);
62✔
3019
        return NEW MayAbility(observer, id, a, card, true);
62✔
3020
    }
3021

3022
    SAFE_DELETE(tc);
58,562✔
3023
    
3024
    //dynamic ability builder
3025
    vector<string> splitDynamic = parseBetween(s, "dynamicability<!", "!>");
117,124✔
3026
    if (splitDynamic.size())
58,562✔
3027
    {
3028
        string s1 = splitDynamic[1];
54✔
3029
        int type = 0;
27✔
3030
        int effect = 0;
27✔
3031
        int who = 0;
27✔
3032
        int amountsource = 0;
27✔
3033

3034
        //source
3035
        for (size_t i = 0; i < sizeof(kDynamicSourceIds)/sizeof(kDynamicSourceIds[0]); ++i)
70✔
3036
        {
3037
            size_t abilityamountsource = s1.find(kDynamicSourceKeywords[i]);
65✔
3038
            if (abilityamountsource != string::npos)
65✔
3039
            {
3040
                amountsource = kDynamicSourceIds[i];
22✔
3041
                break;
22✔
3042
            }
3043
        }
3044

3045
        //main variable or type
3046
        for (size_t i = 0; i < sizeof(kDynamicTypeIds)/sizeof(kDynamicTypeIds[0]); ++i)
48✔
3047
        {
3048
            size_t abilitytype = s1.find(kDynamicTypeKeywords[i]);
48✔
3049
            if (abilitytype != string::npos)
48✔
3050
            {
3051
                type = kDynamicTypeIds[i];
27✔
3052
                break;
27✔
3053
            }
3054
        }
3055

3056
        //effect
3057
        for (size_t i = 0; i < sizeof(kDynamicEffectIds)/sizeof(kDynamicEffectIds[0]); ++i)
111✔
3058
        {
3059
            size_t abilityeffect = s1.find(kDynamicEffectKeywords[i]);
111✔
3060
            if (abilityeffect != string::npos)
111✔
3061
            {
3062
                effect = kDynamicEffectIds[i];
27✔
3063
                break;
27✔
3064
            }
3065
        }
3066

3067
        //target
3068
        for (size_t i = 0; i < sizeof(kDynamicWhoIds)/sizeof(kDynamicWhoIds[0]); ++i)
131✔
3069
        {
3070
            size_t abilitywho = s1.find(kDynamicWhoKeywords[i]);
124✔
3071
            if (abilitywho != string::npos)
124✔
3072
            {
3073
                who = kDynamicWhoIds[i];
20✔
3074
                break;
20✔
3075
            }
3076
        }
3077

3078
        string sAbility = splitDynamic[2];
54✔
3079
        MTGAbility * stored = NULL;
27✔
3080
        if(!sAbility.empty())
27✔
3081
            stored = parseMagicLine(sAbility, id, spell, card);
27✔
3082

3083
        MTGAbility * a = NEW AADynamic(observer, id, card, target,type,effect,who,amountsource,stored);
27✔
3084
        a->oneShot = 1;
27✔
3085
        return a;
27✔
3086
   }
3087

3088
    //flip a coin
3089
    vector<string> splitFlipCoin = parseBetween(s, "flipacoin ", " flipend");
117,070✔
3090
    if (splitFlipCoin.size())
58,535✔
3091
    {
3092
        string a1 = splitFlipCoin[1];
×
3093
        int userchoice = 0;
×
3094
        if(a1[0] >= 48 && a1[0] <= 57){
×
3095
            userchoice = (a1[0] - 48);
×
3096
            a1 = a1.substr(2);
×
3097
        }
3098
        MTGAbility * a = NEW GenericFlipACoin(observer, id, card, target, a1, NULL, userchoice);
×
3099
        a->oneShot = 1;
×
3100
        a->canBeInterrupted = false;
×
3101
        return a;
×
3102
    }
3103

3104
    //roll a d4 die
3105
    vector<string> splitRollD4 = parseBetween(s, "rolld4 ", " rolld4end");
117,070✔
3106
    if (splitRollD4.size())
58,535✔
3107
    {
3108
        string a1 = splitRollD4[1];
×
3109
        int userchoice = 0;
×
3110
        if(a1[0] >= 48 && a1[0] <= 57){
×
3111
            userchoice = (a1[0] - 48);
×
3112
            a1 = a1.substr(2);
×
3113
        }
3114
        MTGAbility * a = NEW GenericRollDie(observer, id, card, target, a1, NULL, userchoice, 4);
×
3115
        a->oneShot = 1;
×
3116
        a->canBeInterrupted = false;
×
3117
        return a;
×
3118
    }
3119

3120
    //roll a d6 die
3121
    vector<string> splitRollD6 = parseBetween(s, "rolld6 ", " rolld6end");
117,070✔
3122
    if (splitRollD6.size())
58,535✔
3123
    {
3124
        string a1 = splitRollD6[1];
×
3125
        int userchoice = 0;
×
3126
        if(a1[0] >= 48 && a1[0] <= 57){
×
3127
            userchoice = (a1[0] - 48);
×
3128
            a1 = a1.substr(2);
×
3129
        }
3130
        MTGAbility * a = NEW GenericRollDie(observer, id, card, target, a1, NULL, userchoice, 6);
×
3131
        a->oneShot = 1;
×
3132
        a->canBeInterrupted = false;
×
3133
        return a;
×
3134
    }
3135

3136
    //roll a d8 die
3137
    vector<string> splitRollD8 = parseBetween(s, "rolld8 ", " rolld8end");
117,070✔
3138
    if (splitRollD8.size())
58,535✔
3139
    {
3140
        string a1 = splitRollD8[1];
×
3141
        int userchoice = 0;
×
3142
        if(a1[0] >= 48 && a1[0] <= 57){
×
3143
            userchoice = (a1[0] - 48);
×
3144
            a1 = a1.substr(2);
×
3145
        }
3146
        MTGAbility * a = NEW GenericRollDie(observer, id, card, target, a1, NULL, userchoice, 8);
×
3147
        a->oneShot = 1;
×
3148
        a->canBeInterrupted = false;
×
3149
        return a;
×
3150
    }
3151

3152
    //roll a d10 die
3153
    vector<string> splitRollD10 = parseBetween(s, "rolld10 ", " rolld10end");
117,070✔
3154
    if (splitRollD10.size())
58,535✔
3155
    {
3156
        string a1 = splitRollD10[1];
×
3157
        int userchoice = 0;
×
3158
        if(a1[0] >= 48 && a1[0] <= 57){
×
3159
            if(a1[1] >= 48 && a1[1] <= 57){
×
3160
                userchoice = (a1[0] - 48)*10 + (a1[1] - 48);
×
3161
                a1 = a1.substr(3);
×
3162
            } else {
3163
                userchoice = (a1[0] - 48);
×
3164
                a1 = a1.substr(2);
×
3165
            }
3166
        }
3167
        MTGAbility * a = NEW GenericRollDie(observer, id, card, target, a1, NULL, userchoice, 10);
×
3168
        a->oneShot = 1;
×
3169
        a->canBeInterrupted = false;
×
3170
        return a;
×
3171
    }
3172

3173
    //roll a d12 die
3174
    vector<string> splitRollD12 = parseBetween(s, "rolld12 ", " rolld12end");
117,070✔
3175
    if (splitRollD12.size())
58,535✔
3176
    {
3177
        string a1 = splitRollD12[1];
×
3178
        int userchoice = 0;
×
3179
        if(a1[0] >= 48 && a1[0] <= 57){
×
3180
            if(a1[1] >= 48 && a1[1] <= 57){
×
3181
                userchoice = (a1[0] - 48)*10 + (a1[1] - 48);
×
3182
                a1 = a1.substr(3);
×
3183
            } else {
3184
                userchoice = (a1[0] - 48);
×
3185
                a1 = a1.substr(2);
×
3186
            }
3187
        }
3188
        MTGAbility * a = NEW GenericRollDie(observer, id, card, target, a1, NULL, userchoice, 12);
×
3189
        a->oneShot = 1;
×
3190
        a->canBeInterrupted = false;
×
3191
        return a;
×
3192
    }
3193

3194
    //roll a d20 die
3195
    vector<string> splitRollD20 = parseBetween(s, "rolld20 ", " rolld20end");
117,070✔
3196
    if (splitRollD20.size())
58,535✔
3197
    {
3198
        string a1 = splitRollD20[1];
×
3199
        int userchoice = 0;
×
3200
        if(a1[0] >= 48 && a1[0] <= 57){
×
3201
            if(a1[1] >= 48 && a1[1] <= 57){
×
3202
                userchoice = (a1[0] - 48)*10 + (a1[1] - 48);
×
3203
                a1 = a1.substr(3);
×
3204
            } else {
3205
                userchoice = (a1[0] - 48);
×
3206
                a1 = a1.substr(2);
×
3207
            }
3208
        }
3209
        MTGAbility * a = NEW GenericRollDie(observer, id, card, target, a1, NULL, userchoice, 20);
×
3210
        a->oneShot = 1;
×
3211
        a->canBeInterrupted = false;
×
3212
        return a;
×
3213
    }
3214

3215
    //Phase based actions  
3216
    found = s.find("phaseaction");
58,535✔
3217
    if (found != string::npos)
58,535✔
3218
    {
3219
        return parsePhaseActionAbility(s,card,spell,target,restrictions,id);
18✔
3220
    }
3221

3222
    //may pay ability
3223
    vector<string> splitMayPaysub = parseBetween(s, "pay[[","]]", true);
117,034✔
3224
    if (splitMayPaysub.size())
58,517✔
3225
    {
3226
        GenericPaidAbility * a = NEW GenericPaidAbility(observer, id, card, target,newName,castRestriction,splitMayPaysub[1],storedPayString,asAlternate);
4✔
3227
        a->oneShot = 1;
4✔
3228
        a->canBeInterrupted = false;
4✔
3229
        return a;
4✔
3230
    }
3231

3232
    //Upkeep Cost
3233
    found = s.find("upcost");
58,513✔
3234
    if (found != string::npos)
58,513✔
3235
    {
3236
       return AbilityFactory::parseUpkeepAbility(s,card,spell,restrictions,id);
14✔
3237
    }
3238

3239
    //ninjutsu
3240
    found = s.find("ninjutsu");
58,499✔
3241
    if (found != string::npos)
58,499✔
3242
    {
3243
        MTGAbility * a = NEW ANinja(observer, id, card, target);
×
3244
        a->oneShot = 1;
×
3245
        return a;
×
3246
    }
3247

3248
    //combat removal
3249
    found = s.find("removefromcombat");
58,499✔
3250
    if (found != string::npos)
58,499✔
3251
    {
3252
        MTGAbility * a = NEW ACombatRemoval(observer, id, card, target);
2✔
3253
        a->oneShot = 1;
2✔
3254
        return a;
2✔
3255
    }
3256

3257
    //gain control until source is untapped or leaves battlefield
3258
    found = s.find("shackle");
58,497✔
3259
    if (found != string::npos)
58,497✔
3260
    {
3261
        MTGAbility * a = NEW AShackleWrapper(observer, id, card, target);
×
3262
        a->oneShot = 1;
×
3263
        return a;
×
3264
    }
3265

3266
    //grant ability until source is untapped or leaves battlefield
3267
    found = s.find("grant ");
58,497✔
3268
    if (found != string::npos)
58,497✔
3269
    {
3270
        MTGAbility * toGrant = parseMagicLine(storedAbilityString, id, spell, card);
×
3271
        MTGAbility * a = NEW AGrantWrapper(observer, id, card, target,toGrant);
×
3272
        a->oneShot = 1;
×
3273
        return a;
×
3274
    }
3275

3276
   //momentary blink
3277
    found = s.find("(blink)");
58,497✔
3278
    if (found != string::npos)
58,497✔
3279
    {
3280
        bool ueoteffect = (s.find("(blink)ueot") != string::npos);
2✔
3281
        bool forsource = (s.find("(blink)forsrc") != string::npos);
2✔
3282
        bool blinkhand = (s.find("hand(blink)") != string::npos);
2✔
3283
        size_t returnAbility = s.find("return(");
2✔
3284
        string sAbility = s.substr(returnAbility + 7);
4✔
3285
        MTGAbility * stored = NULL;
2✔
3286
        if(!sAbility.empty())
2✔
3287
        {
3288
            stored = parseMagicLine(sAbility, id, spell, card);
2✔
3289
        }
3290
        MTGAbility * a = NEW ABlinkGeneric(observer, id, card, target,ueoteffect,forsource,blinkhand,stored);
2✔
3291
        a->oneShot = 1;
2✔
3292
        return a;
2✔
3293
    }
3294

3295
    // Fizzle (counterspell...) and put to zone or Move spell to zone
3296
    // This should always be above "fizzle" section
3297
    vector<string> splitFizzle = parseBetween(s, "spellmover(", ")");
116,990✔
3298
    if (!splitFizzle.size())
58,495✔
3299
        splitFizzle = parseBetween(s, "fizzleto(", ")");
58,495✔
3300
    if (splitFizzle.size())
58,495✔
3301
    {
3302
        // currently only hand, exile and library are supported
3303
        string zone = splitFizzle[1];
8✔
3304
        ActionStack::FizzleMode fizzleMode = ActionStack::PUT_IN_GRAVEARD;
4✔
3305
        if (zone == "hand")
4✔
3306
        {
3307
            fizzleMode = ActionStack::PUT_IN_HAND;
1✔
3308
        } else if (zone == "exile")
3✔
3309
        {
3310
            fizzleMode = ActionStack::PUT_IN_EXILE;
2✔
3311
        }  else if (zone == "exileimp")
1✔
3312
        {
3313
            fizzleMode = ActionStack::PUT_IN_EXILE_IMPRINT;
×
3314
        } else if (zone == "librarytop")
1✔
3315
        {
3316
            fizzleMode = ActionStack::PUT_IN_LIBRARY_TOP;
1✔
3317
        } else if (zone == "librarysecond")
×
3318
        {
3319
            fizzleMode = ActionStack::PUT_IN_LIBRARY_SECOND;
×
3320
        } else if (zone == "librarybottom")
×
3321
        {
3322
            fizzleMode = ActionStack::PUT_IN_LIBRARY_BOTTOM;
×
3323
        }
3324
        Spell * starget = NULL;
4✔
3325
        if (spell)
4✔
3326
            starget = spell->getNextSpellTarget();
3✔
3327
        AAFizzler * a = NEW AAFizzler(observer, id, card, starget);
4✔
3328
        a->fizzleMode = fizzleMode;
4✔
3329
        a->spellMover = (s.find("spellmover(") != string::npos);
4✔
3330
        a->oneShot = 1;
4✔
3331
        return a;
4✔
3332
    }
3333

3334
    //Fizzle (counterspell...)
3335
    found = s.find("fizzle");
58,491✔
3336
    if (found != string::npos && s.find("nofizzle") == string::npos) //Fix to allow adding nofizzle ability to cards which originally have not (e.g. with transforms or lord keywords)
58,491✔
3337
    {
3338
        Spell * starget = NULL;
17✔
3339
        if (spell)
17✔
3340
            starget = spell->getNextSpellTarget();
15✔
3341
        MTGAbility * a = NEW AAFizzler(observer, id, card, starget);
17✔
3342
        a->oneShot = 1;
17✔
3343
        return a;
17✔
3344
    }
3345

3346
    //Describes a player target in many abilities
3347
    int who = TargetChooser::UNSET;
58,474✔
3348
    if (s.find(" controller") != string::npos)
58,474✔
3349
        who = TargetChooser::CONTROLLER;
23,289✔
3350
    if (s.find(" opponent") != string::npos)
58,474✔
3351
        who = TargetChooser::OPPONENT;
55✔
3352
    if (s.find(" targetcontroller") != string::npos)
58,474✔
3353
        who = TargetChooser::TARGET_CONTROLLER;
21✔
3354
    if (s.find(" targetedplayer") != string::npos)
58,474✔
3355
        who = TargetChooser::TARGETED_PLAYER;
3✔
3356
    if (s.find(" owner") != string::npos)
58,474✔
3357
        who = TargetChooser::OWNER;
×
3358

3359
    //ability creator the target has to do the ability.
3360
    if(s.find("ability$") != string::npos)
58,474✔
3361
    {
3362
        if (storedAbilityString.size())
23,187✔
3363
        {
3364
            vector<string> splitName = parseBetween(storedAbilityString, "name(", ")");
46,374✔
3365
            if (splitName.size())
23,187✔
3366
            {
3367
                newName = splitName[1];
1,453✔
3368
                storedAbilityString = splitName[0];
1,453✔
3369
                storedAbilityString.append(splitName[2]);
1,453✔
3370
                //we erase the name section from the string to avoid 
3371
                //accidently building an mtg ability with the text meant for menuText.
3372
            }
3373
            ATargetedAbilityCreator * abl = NEW ATargetedAbilityCreator(observer, id, card,target, NULL,newName, storedAbilityString, who);
23,187✔
3374
            abl->oneShot = 1;
23,187✔
3375
            storedString.clear();
23,187✔
3376
            storedAbilityString.clear();
23,187✔
3377
            return abl;
23,187✔
3378
        }
3379
    }
3380
    //livingweapon (used for token below)
3381
    bool aLivingWeapon = (s.find("livingweapon") != string::npos);
35,287✔
3382

3383
    //Token creator. Name, type, p/t, abilities
3384
    vector<string> splitToken = parseBetween(s, "token(", ")");
70,574✔
3385
    if (splitToken.size())
35,287✔
3386
    {
3387
        WParsedInt * multiplier = NULL;
56✔
3388
        size_t star = s.find("*");
56✔
3389
        string starfound = "";
112✔
3390
        if (star != string::npos)
56✔
3391
        {
3392
            starfound = s.substr(star + 1);
23✔
3393
            size_t starEnd= starfound.find_first_of(" ");
23✔
3394
            starfound = starfound.substr(0,starEnd);
23✔
3395
            multiplier = NEW WParsedInt(starfound, spell, card);
23✔
3396
        }
3397
        replace(splitToken[1].begin(), splitToken[1].end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using token keyword inside transforms)
56✔
3398
        int tokenId = atoi(splitToken[1].c_str());
56✔
3399
        if (tokenId)
56✔
3400
        {
3401
            MTGCard * safetycard = MTGCollection()->getCardById(tokenId);
8✔
3402
            if (!safetycard) //Error, card not foudn in DB
8✔
3403
                return NEW ATokenCreator(observer, id, card, target, NULL, "ID NOT FOUND", "ERROR ID",0, 0, "","", NULL,0);
×
3404

3405
            ATokenCreator * tok = NEW ATokenCreator(observer, id, card,target, NULL, tokenId, starfound, multiplier, who);
8✔
3406
            tok->oneShot = 1;
8✔
3407
            //andability
3408
            if(storedAndAbility.size())
8✔
3409
            {
3410
                string stored = storedAndAbility;
×
3411
                storedAndAbility.clear();
×
3412
                ((ATokenCreator*)tok)->andAbility = parseMagicLine(stored, id, spell, card);
×
3413
            }
3414
            return tok;
8✔
3415
        }
3416
        string tokenDesc = splitToken[1];
96✔
3417
        vector<string> tokenParameters = split(tokenDesc, ',');
96✔
3418
        string sabilities = "";
96✔
3419
        //lets try finding a token by card name.
3420
        if (splitToken[1].size() && (tokenParameters.size() ==1||tokenParameters.size() ==2))
48✔
3421
        {
3422
            string cardName = splitToken[1];
2✔
3423
            size_t triggerpos = cardName.find(",notrigger");
2✔
3424
            if(triggerpos != string::npos){
2✔
3425
                cardName.replace(triggerpos, 10, ""); // To allow the usage of notrigger even with named token (e.g. Academy Manufactor)
×
3426
                sabilities = "notrigger";
×
3427
            }
3428
            MTGCard * safetycard = MTGCollection()->getCardByName(cardName);
2✔
3429
            if (safetycard) //lets try constructing it then,we didnt find it by name
2✔
3430
            {
3431
                ATokenCreator * tok = NEW ATokenCreator(observer, id, card, target, NULL, cardName, starfound, multiplier, who);
2✔
3432
                tok->oneShot = 1;
2✔
3433
                if(!sabilities.empty())
2✔
3434
                    tok->sabilities = sabilities;
×
3435
                //andability
3436
                if(storedAndAbility.size())
2✔
3437
                {
3438
                    string stored = storedAndAbility;
×
3439
                    storedAndAbility.clear();
×
3440
                    ((ATokenCreator*)tok)->andAbility = parseMagicLine(stored, id, spell, card);
×
3441
                }
3442
                return tok;
2✔
3443
            }
3444
        }
3445
        if (tokenParameters.size() < 3)
46✔
3446
        {
3447
            DebugTrace("incorrect Parameters for Token" << tokenDesc);
×
3448
            return NULL;
×
3449
        }
3450
        string sname = tokenParameters[0];
92✔
3451
        string stypes = tokenParameters[1];
92✔
3452
        string spt = tokenParameters[2];
92✔
3453
        string cID = "";
92✔
3454
        //reconstructing string abilities from the split version,
3455
        // then we re-split it again in the token constructor,
3456
        // this needs to be improved
3457
        sabilities = (tokenParameters.size() > 3)? tokenParameters[3] : "";
46✔
3458
        for (size_t i = 4; i < tokenParameters.size(); ++i)
51✔
3459
        {
3460
            sabilities.append(",");
5✔
3461
            sabilities.append(tokenParameters[i]);
5✔
3462
        }
3463
        if(sabilities.find(",tnum.") != string::npos)
46✔
3464
        {
3465
            size_t begins = sabilities.find(",tnum.");
2✔
3466
            cID = sabilities.substr(begins+6);
2✔
3467
            sabilities = cReplaceString(sabilities,",tnum."+cID,"");
2✔
3468
        }
3469
        int value = 0;
46✔
3470
        if (spt.find("xx/xx") != string::npos)
46✔
3471
            value = card->X / 2;
1✔
3472
        else if (spt.find("x/x") != string::npos)
45✔
3473
            value = card->X;
×
3474

3475
        int power, toughness;
3476
        parsePowerToughness(spt, &power, &toughness);
46✔
3477
        
3478
        ATokenCreator * tok = NEW ATokenCreator(
3479
            observer, id, card,target, NULL, sname, stypes, power + value, toughness + value,
3480
            sabilities, starfound, multiplier, who, aLivingWeapon, spt, cID);
46✔
3481
        tok->oneShot = 1;
46✔
3482
        if(aLivingWeapon)
46✔
3483
            tok->forceDestroy = 1;
2✔
3484
        //andability
3485
        if(storedAndAbility.size())
46✔
3486
        {
3487
            string stored = storedAndAbility;
×
3488
            storedAndAbility.clear();
×
3489
            ((ATokenCreator*)tok)->andAbility = parseMagicLine(stored, id, spell, card);
×
3490
        }
3491
        return tok;  
46✔
3492
    }
3493

3494
    //Alternative Token creator. Name, type, p/t, abilities - uses ":" as delimeter
3495
    vector<string> makeToken = parseBetween(s, "create(", ")");
70,462✔
3496
    if (makeToken.size())
35,231✔
3497
    {
3498
        WParsedInt * multiplier = NULL;
26✔
3499
        size_t myMultiplier = s.find("*");
26✔
3500
        string myMultiplierfound = "";
52✔
3501
        if (myMultiplier != string::npos)
26✔
3502
        {
3503
            myMultiplierfound = s.substr(myMultiplier + 1);
15✔
3504
            size_t myMultiplierEnd= myMultiplierfound.find_first_of(" ");
15✔
3505
            myMultiplierfound = myMultiplierfound.substr(0,myMultiplierEnd);
15✔
3506
            multiplier = NEW WParsedInt(myMultiplierfound, spell, card);
15✔
3507
        }
3508
        
3509
        int mytokenId = atoi(makeToken[1].c_str());
26✔
3510
        if (mytokenId)
26✔
3511
        {
3512
            MTGCard * mysafetycard = MTGCollection()->getCardById(mytokenId);
×
3513
            if (!mysafetycard) //Error, card not foudn in DB
×
3514
                return NEW ATokenCreator(observer, id, card, target, NULL, "ID NOT FOUND", "ERROR ID",0, 0, "","", NULL,0);
×
3515

3516
            ATokenCreator * mtok = NEW ATokenCreator(observer, id, card,target, NULL, mytokenId, myMultiplierfound, multiplier, who);
×
3517
            mtok->oneShot = 1;
×
3518
            //andability
3519
            if(storedAndAbility.size())
×
3520
            {
3521
                string stored = storedAndAbility;
×
3522
                storedAndAbility.clear();
×
3523
                ((ATokenCreator*)mtok)->andAbility = parseMagicLine(stored, id, spell, card);
×
3524
            }
3525
            return mtok;
×
3526
        }
3527
        
3528
        string tokenDesc = makeToken[1];
52✔
3529
        vector<string> tokenParameters = split(tokenDesc, ':');
52✔
3530
        //lets try finding a token by card name.
3531
        if (makeToken[1].size() && tokenParameters.size() ==1)
26✔
3532
        {
3533
            string cardName = makeToken[1];
×
3534
            MTGCard * mysafetycard = MTGCollection()->getCardByName(cardName);
×
3535
            if (mysafetycard) //lets try constructing it then,we didnt find it by name
×
3536
            {
3537
                ATokenCreator * mtok = NEW ATokenCreator(observer, id, card, target, NULL, cardName, myMultiplierfound, multiplier, who);
×
3538
                mtok->oneShot = 1;
×
3539
                //andability
3540
                if(storedAndAbility.size())
×
3541
                {
3542
                    string stored = storedAndAbility;
×
3543
                    storedAndAbility.clear();
×
3544
                    ((ATokenCreator*)mtok)->andAbility = parseMagicLine(stored, id, spell, card);
×
3545
                }
3546
                return mtok;
×
3547
            }
3548
        }
3549
        if (tokenParameters.size() < 3)
26✔
3550
        {
3551
            DebugTrace("incorrect Parameters for Token" << tokenDesc);
×
3552
            return NULL;
×
3553
        }
3554
        string sname = tokenParameters[0];
52✔
3555
        string stypes = tokenParameters[1];
52✔
3556
        string spt = tokenParameters[2];
52✔
3557
        string cID = "";
52✔
3558
        //reconstructing string abilities from the split version,
3559
        // then we re-split it again in the token constructor,
3560
        // this needs to be improved
3561
        string sabilities = (tokenParameters.size() > 3)? tokenParameters[3] : "";
52✔
3562
        for (size_t i = 4; i < tokenParameters.size(); ++i)
29✔
3563
        {
3564
            sabilities.append(",");
3✔
3565
            sabilities.append(tokenParameters[i]);
3✔
3566
        }
3567
        if(sabilities.find(",tnum.") != string::npos)
26✔
3568
        {
3569
            size_t begins = sabilities.find(",tnum.");
2✔
3570
            cID = sabilities.substr(begins+6);
2✔
3571
            sabilities = cReplaceString(sabilities,",tnum."+cID,"");
2✔
3572
        }
3573
        int value = 0;
26✔
3574
        if (spt.find("xx/xx") != string::npos)
26✔
3575
            value = card->X / 2;
×
3576
        else if (spt.find("x/x") != string::npos)
26✔
3577
            value = card->X;
×
3578

3579
        int power, toughness;
3580
        parsePowerToughness(spt, &power, &toughness);
26✔
3581
        
3582
        ATokenCreator * mtok = NEW ATokenCreator(
3583
            observer, id, card,target, NULL, sname, stypes, power + value, toughness + value,
3584
            sabilities, myMultiplierfound, multiplier, who, aLivingWeapon, spt, cID);
26✔
3585
        mtok->oneShot = 1;
26✔
3586
        if(aLivingWeapon)
26✔
3587
            mtok->forceDestroy = 1;
×
3588
        //andability
3589
        if(storedAndAbility.size())
26✔
3590
        {
3591
            string stored = storedAndAbility;
×
3592
            storedAndAbility.clear();
×
3593
            ((ATokenCreator*)mtok)->andAbility = parseMagicLine(stored, id, spell, card);
×
3594
        }
3595
        return mtok;  
26✔
3596
    }
3597

3598
    //Equipment
3599
    found = s.find("equip");
35,205✔
3600
    if (found != string::npos)
35,205✔
3601
    {
3602
        return NEW AEquip(observer, id, card);
56✔
3603
    }
3604
    
3605
    // TODO: deprecate this ability in favor of retarget
3606
    //Equipment (attach)
3607
    found = s.find("attach");
35,149✔
3608
    if (found != string::npos)
35,149✔
3609
    {
3610
        return NEW AEquip(observer, id, card, 0, ActivatedAbility::NO_RESTRICTION);
×
3611
    }
3612

3613
    //MoveTo Move a card from a zone to another
3614
    vector<string> splitMove = parseBetween(s, "moveto(", ")");
70,298✔
3615
    if (splitMove.size())
35,149✔
3616
    {
3617
        //hack for http://code.google.com/p/wagic/issues/detail?id=120
3618
        //We assume that auras don't move their own target...
3619
        if (card->hasType(Subtypes::TYPE_AURA))
147✔
3620
            target = card;
4✔
3621

3622
        MTGAbility * a = NEW AAMover(observer, id, card, target, splitMove[1],newName);
147✔
3623
        a->oneShot = true;
147✔
3624
        ((AAMover*)a)->necro = s.find("hiddenmoveto") != string::npos?true:false;
147✔
3625
        if(storedAndAbility.size())
147✔
3626
        {
3627
            string stored = storedAndAbility;
36✔
3628
            storedAndAbility.clear();
18✔
3629
            ((AAMover*)a)->andAbility = parseMagicLine(stored, id, spell, card);
18✔
3630
        }
3631
        return a;
147✔
3632
    }
3633

3634
    //random mover
3635
     vector<string> splitRandomMove = parseBetween(s, "moverandom(", ")");
70,004✔
3636
    if (splitRandomMove.size())
35,002✔
3637
    {
3638
         vector<string> splitfrom = parseBetween(splitRandomMove[2], "from(", ")");
×
3639
         vector<string> splitto = parseBetween(splitRandomMove[2], "to(", ")");
×
3640
         if(!splitfrom.size() || !splitto.size())
×
3641
             return NULL;
×
3642
         MTGAbility * a = NEW AARandomMover(observer, id, card, target, splitRandomMove[1],splitfrom[1],splitto[1]);
×
3643
         a->oneShot = true;
×
3644
        if(storedAndAbility.size())
×
3645
        {
3646
            string stored = storedAndAbility;
×
3647
            storedAndAbility.clear();
×
3648
            ((AARandomMover*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3649
        }
3650
         return a;
×
3651
    }
3652

3653
    //put a card in a specifc position of owners library from the top
3654
    vector<string> splitPlaceFromTop = parseBetween(s, "placefromthetop(", ")");
70,004✔
3655
    if (splitPlaceFromTop.size())
35,002✔
3656
    {
3657
        int position = 0;
×
3658
        WParsedInt* parser = NEW WParsedInt(splitPlaceFromTop[1], card);
×
3659
        if(parser){
×
3660
            position = parser->intValue;
×
3661
            SAFE_DELETE(parser);
×
3662
        }
3663
        MTGAbility * a = NEW AALibraryPosition(observer, id, card, target, NULL, position);
×
3664
        a->oneShot = 1;
×
3665
        //andability
3666
        if(storedAndAbility.size())
×
3667
        {
3668
            string stored = storedAndAbility;
×
3669
            storedAndAbility.clear();
×
3670
            ((AALibraryBottom*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3671
        }
3672
        return a;
×
3673
    }
3674

3675
    //put a card on bottom of library
3676
    found = s.find("bottomoflibrary");
35,002✔
3677
    if (found != string::npos)
35,002✔
3678
    {
3679
        MTGAbility * a = NEW AALibraryBottom(observer, id, card, target);
×
3680
        a->oneShot = 1;
×
3681
        //andability
3682
        if(storedAndAbility.size())
×
3683
        {
3684
            string stored = storedAndAbility;
×
3685
            storedAndAbility.clear();
×
3686
            ((AALibraryBottom*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3687
        }
3688
        return a;
×
3689
    }
3690

3691
    //Copy a target
3692
    found = s.find("copy");
35,002✔
3693
    if (found != string::npos)
35,002✔
3694
    {
3695
        string options = "";
14✔
3696
        vector<string> splitOptions = parseBetween(s, "options(", ")");
14✔
3697
        if (splitOptions.size())
7✔
3698
        {
3699
            options = splitOptions[1];
×
3700
        }
3701
        MTGAbility * a = NEW AACopier(observer, id, card, target, NULL, options);
7✔
3702
        a->oneShot = 1;
7✔
3703
        a->canBeInterrupted = false;
7✔
3704
        ((AACopier*)a)->isactivated = activated;
7✔
3705
        //andability
3706
        if(storedAndAbility.size())
7✔
3707
        {
3708
            string stored = storedAndAbility;
×
3709
            storedAndAbility.clear();
×
3710
            ((AACopier*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3711
        }
3712
        return a;
7✔
3713
    }
3714

3715
    //imprint
3716
    found = s.find("imprint");
34,995✔
3717
    if (found != string::npos)
34,995✔
3718
    {
3719
        if (s.find("imprintedcard") == string::npos)
×
3720
        {
3721
            MTGAbility * a = NEW AAImprint(observer, id, card, target);
×
3722
            a->oneShot = 1;
×
3723
            //andability
3724
            if(storedAndAbility.size())
×
3725
            {
3726
                string stored = storedAndAbility;
×
3727
                storedAndAbility.clear();
×
3728
                ((AAImprint*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3729
            }
3730
            return a;
×
3731
        }
3732
    }
3733

3734
    //haunt a creature
3735
    found = s.find("haunt");
34,995✔
3736
    if (found != string::npos)
34,995✔
3737
    {
3738
        if (s.find("haunted") == string::npos)
×
3739
        {
3740
            MTGAbility * a = NEW AAHaunt(observer, id, card, target);
×
3741
            a->oneShot = 1;
×
3742
            //andability
3743
            if(storedAndAbility.size())
×
3744
            {
3745
                string stored = storedAndAbility;
×
3746
                storedAndAbility.clear();
×
3747
                ((AAHaunt*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3748
            }
3749
            return a;
×
3750
        }
3751
    }
3752

3753
    //train a creature
3754
    found = s.find("dotrain");
34,995✔
3755
    if (found != string::npos)
34,995✔
3756
    {
3757
        MTGAbility * a = NEW AATrain(observer, id, card, target);
×
3758
        a->oneShot = 1;
×
3759
        //andability
3760
        if(storedAndAbility.size())
×
3761
        {
3762
            string stored = storedAndAbility;
×
3763
            storedAndAbility.clear();
×
3764
            ((AATrain*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3765
        }
3766
        return a;
×
3767
    }
3768

3769
    //Conjure a card
3770
    found = s.find("conjure");
34,995✔
3771
    if (found != string::npos)
34,995✔
3772
    {
3773
        replace(s.begin(), s.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using conjure keyword inside transforms)
×
3774
        string cardName = "";
×
3775
        vector<string> splitCard = parseBetween(s, "cards(", ")");
×
3776
        if (splitCard.size())
×
3777
        {
3778
            cardName = splitCard[1];
×
3779
        }
3780
        if(cardName == "myname")
×
3781
            cardName = card->name; // Added to refer the orginal source card name (e.g. "Clone Crafter").
×
3782
        string cardZone = "";
×
3783
        vector<string> splitZone = parseBetween(s, "zone(", ")");
×
3784
        if (splitZone.size())
×
3785
        {
3786
            cardZone = splitZone[1];
×
3787
        }
3788
        MTGAbility * a = NEW AAConjure(observer, id, card, target, NULL, cardName, cardZone);
×
3789
        a->oneShot = 1;
×
3790
        a->canBeInterrupted = false;
×
3791
        //andability
3792
        if(storedAndAbility.size())
×
3793
        {
3794
            string stored = storedAndAbility;
×
3795
            storedAndAbility.clear();
×
3796
            ((AAConjure*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3797
        }
3798
        return a;
×
3799
    }
3800

3801
    //foretell
3802
    found = s.find("doforetell");
34,995✔
3803
    if (found != string::npos)
34,995✔
3804
    {
3805
        MTGAbility * a = NEW AAForetell(observer, id, card, target);
×
3806
        a->oneShot = 1;
×
3807
        return a;
×
3808
    }
3809

3810
    //phaseout
3811
    found = s.find("phaseout");
34,995✔
3812
    if (found != string::npos)
34,995✔
3813
    {
3814
        MTGAbility * a = NEW AAPhaseOut(observer, id, card, target);
1✔
3815
        a->oneShot = 1;
1✔
3816
        return a;
1✔
3817
    }
3818

3819
    //manifest
3820
    found = s.find("manifest");
34,994✔
3821
    if (found != string::npos)
34,994✔
3822
    {//for cloudform, rageform and lightform
3823
        bool withenchant = s.find("withenchant") != string::npos;
×
3824
        MTGAbility * a = NEW AManifest(observer, id, card, target);
×
3825
        a->oneShot = 1;
×
3826
        if(storedAndAbility.size())
×
3827
        {
3828
            string stored = storedAndAbility;
×
3829
            storedAndAbility.clear();
×
3830
            ((AManifest*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3831
        }
3832
        if(withenchant)
×
3833
            ((AManifest*)a)->withenchant = true;
×
3834
        return a;
×
3835
    }
3836
    //exert
3837
    found = s.find("exert");
34,994✔
3838
    if (found != string::npos)
34,994✔
3839
    {
3840
        MTGAbility * a = NEW AExert(observer, id, card, target);
×
3841
        a->oneShot = 1;
×
3842
        if(storedAndAbility.size())
×
3843
        {
3844
            string stored = storedAndAbility;
×
3845
            storedAndAbility.clear();
×
3846
            ((AExert*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3847
        }
3848
        return a;
×
3849
    }
3850
    //provoke
3851
    found = s.find("provoke");
34,994✔
3852
    if (found != string::npos)
34,994✔
3853
    {
3854
        MTGAbility * a = NEW AProvoke(observer, id, card, target);
×
3855
        a->oneShot = 1;
×
3856
        if(storedAndAbility.size())
×
3857
        {
3858
            string stored = storedAndAbility;
×
3859
            storedAndAbility.clear();
×
3860
            ((AProvoke*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3861
        }
3862
        return a;
×
3863
    }
3864
    //setblocker
3865
    found = s.find("setblocker");
34,994✔
3866
    if (found != string::npos)
34,994✔
3867
    {
3868
        MTGAbility * a = NEW AProvoke(observer, id, card, target);
×
3869
        a->oneShot = 1;
×
3870
        ((AProvoke*)a)->setblocker = true;
×
3871
        if(storedAndAbility.size())
×
3872
        {
3873
            string stored = storedAndAbility;
×
3874
            storedAndAbility.clear();
×
3875
            ((AProvoke*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3876
        }
3877
        return a;
×
3878
    }
3879

3880
    //clone
3881
    found = s.find("clone");
34,994✔
3882
    if (found != string::npos)
34,994✔
3883
    {
3884
        string with = "";
2✔
3885
        string types = "";
2✔
3886
        string options = "";
2✔
3887
        replace(s.begin(), s.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using clone keyword inside transforms)
1✔
3888
        vector<string> splitWith = parseBetween(s, "with(", ")");
2✔
3889
        if (splitWith.size())
1✔
3890
        {
3891
            with = splitWith[1];
×
3892
        }
3893
        vector<string> splitTypes = parseBetween(s, "addtype(", ")");
2✔
3894
        if (splitTypes.size())
1✔
3895
        {
3896
            types = splitTypes[1];
×
3897
        }
3898
        vector<string> splitOptions = parseBetween(s, "options(", ")");
2✔
3899
        if (splitOptions.size())
1✔
3900
        {
3901
            options = splitOptions[1];
×
3902
        }
3903
        MTGAbility * a = NEW AACloner(observer, id, card, target, 0, who, with, types, options);
1✔
3904
        a->oneShot = 1;
1✔
3905
        if(storedAndAbility.size())
1✔
3906
        {
3907
            string stored = storedAndAbility;
×
3908
            storedAndAbility.clear();
×
3909
            ((AACloner*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3910
        }
3911
        return a;
1✔
3912
    }
3913

3914
    //Bury, destroy, sacrifice, reject(discard)
3915
    if (s.find("bury") != string::npos)
34,993✔
3916
    {
3917
        MTGAbility *a = NEW AABuryCard(observer, id, card, target);
20✔
3918
        a->oneShot = 1;
20✔
3919
        if(storedAndAbility.size())
20✔
3920
        {
3921
            string stored = storedAndAbility;
×
3922
            storedAndAbility.clear();
×
3923
            ((AABuryCard*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3924
        }
3925
        return a;
20✔
3926
    }
3927

3928
    if (s.find("destroy") != string::npos)
34,973✔
3929
    {
3930
        MTGAbility * a = NEW AADestroyCard(observer, id, card, target);
92✔
3931
        a->oneShot = 1;
92✔
3932
        if(storedAndAbility.size())
92✔
3933
        {
3934
            string stored = storedAndAbility;
×
3935
            storedAndAbility.clear();
×
3936
            ((AADestroyCard*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3937
        }
3938
        return a;
92✔
3939
    }
3940

3941
    if (s.find("sacrifice") != string::npos || s.find("exploits") != string::npos)
34,881✔
3942
    {
3943
        MTGAbility *a = NEW AASacrificeCard(observer, id, card, target);
51✔
3944
        a->oneShot = 1;
51✔
3945
        ((AASacrificeCard*)a)->isExploited = (s.find("exploits") != string::npos)?true:false; // added to allow the Exploit trigger.
51✔
3946
        if(storedAndAbility.size())
51✔
3947
        {
3948
            string stored = storedAndAbility;
2✔
3949
            storedAndAbility.clear();
1✔
3950
            ((AASacrificeCard*)a)->andAbility = parseMagicLine(stored, id, spell, card);
1✔
3951
        }
3952
        return a;
51✔
3953
    }
3954

3955
    if (s.find("reject") != string::npos)
34,830✔
3956
    {
3957
        MTGAbility *a = NEW AADiscardCard(observer, id, card, target);
6✔
3958
        a->oneShot = 1;
6✔
3959
        if(storedAndAbility.size())
6✔
3960
        {
3961
            string stored = storedAndAbility;
×
3962
            storedAndAbility.clear();
×
3963
            ((AADiscardCard*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
3964
        }
3965
        return a;
6✔
3966
    }
3967

3968
    //cast a card without paying it's manacost
3969
    vector<string> splitCastCard = parseBetween(s, "castcard(", ")");
69,648✔
3970
    if (splitCastCard.size())
34,824✔
3971
    {
3972
        string builtHow = splitCastCard[1];
×
3973
        bool withRestrictions = splitCastCard[1].find("restricted") != string::npos;
×
3974
        bool asCopy = splitCastCard[1].find("copied") != string::npos;
×
3975
        bool asNormal = splitCastCard[1].find("normal") != string::npos;
×
3976
        bool asNormalMadness = splitCastCard[1].find("madness") != string::npos;
×
3977
        bool sendNoEvent = splitCastCard[1].find("noevent") != string::npos;
×
3978
        bool putinplay = splitCastCard[1].find("putinplay") != string::npos;
×
3979
        bool alternative = splitCastCard[1].find("alternative") != string::npos;
×
3980
        bool flipped = splitCastCard[1].find("flipped") != string::npos;
×
3981
        string nameCard = "";
×
3982
        if(splitCastCard[1].find("named!:") != string::npos)
×
3983
        {
3984
            vector<string> splitCastName = parseBetween(splitCastCard[1], "named!:", ":!");
×
3985
            if(splitCastName.size())
×
3986
            {
3987
                nameCard = splitCastName[1];
×
3988
            }
3989
        }
3990
        int kicked = 0;
×
3991
        if(splitCastCard[1].find("kicked!:") != string::npos)
×
3992
        {
3993
            vector<string> splitCastKicked = parseBetween(splitCastCard[1], "kicked!:", ":!");
×
3994
            if(splitCastKicked.size())
×
3995
            {
3996
                WParsedInt * val = NEW WParsedInt(splitCastKicked[1], NULL, card);
×
3997
                kicked = val->getValue();
×
3998
            }
3999
        }
4000
        int costx = 0;
×
4001
        if(splitCastCard[1].find("costx!:") != string::npos)
×
4002
        {
4003
            vector<string> splitCastCostX = parseBetween(splitCastCard[1], "costx!:", ":!");
×
4004
            if(splitCastCostX.size())
×
4005
            {
4006
                WParsedInt * val = NEW WParsedInt(splitCastCostX[1], NULL, card);
×
4007
                costx = val->getValue();
×
4008
            }
4009
        }
4010
        MTGAbility *a = NEW AACastCard(observer, id, card, target,withRestrictions,asCopy,asNormal,nameCard,newName,sendNoEvent,putinplay, asNormalMadness, alternative, kicked, costx, flipped);
×
4011
        a->oneShot = false;
×
4012
        if(splitCastCard[1].find("trigger[to]") != string::npos)
×
4013
        {
4014
            a->setActionTC(NEW TriggerTargetChooser(observer, WEvent::TARGET_TO));
×
4015
        }
4016
        if(storedAndAbility.size())
×
4017
        {
4018
            string stored = storedAndAbility;
×
4019
            storedAndAbility.clear();
×
4020
            ((AACastCard*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
4021
        }
4022
                MTGCardInstance * _target = NULL;
×
4023
                if (spell)
×
4024
                    _target = spell->getNextCardTarget();
×
4025
                if(!_target)
×
4026
                    _target = target;
×
4027
                a->target = _target;
×
4028
        return a;
×
4029
    }
4030

4031
    bool oneShot = false;
34,824✔
4032
    bool forceForever = false;
34,824✔
4033
    bool untilYourNextTurn = false;
34,824✔
4034
    bool untilYourNextEndTurn = false;
34,824✔
4035
    found = s.find("ueot");
34,824✔
4036
    if (found != string::npos)
34,824✔
4037
        forceUEOT = true;
103✔
4038
    found = s.find("oneshot");
34,824✔
4039
    if (found != string::npos)
34,824✔
4040
        oneShot = true;
1,462✔
4041
    found = s.find("forever");
34,824✔
4042
    if (found != string::npos)
34,824✔
4043
        forceForever = true;
22✔
4044
    found = s.find("uynt");
34,824✔
4045
    if (found != string::npos)
34,824✔
4046
        untilYourNextTurn = true;
×
4047
    found = s.find("uent");
34,824✔
4048
    if (found != string::npos)
34,824✔
4049
        untilYourNextEndTurn = true;
×
4050
    //Prevent Damage
4051
    const string preventDamageKeywords[] = { "preventallcombatdamage", "preventallnoncombatdamage", "preventalldamage", "fog" };
69,648✔
4052
    const int preventDamageTypes[] = {0, 2, 1, 0}; //TODO enum ?
34,824✔
4053
    const bool preventDamageForceOneShot[] = { false, false, false, true };
34,824✔
4054

4055
    for (size_t i = 0; i < sizeof(preventDamageTypes)/sizeof(preventDamageTypes[0]); ++i)
174,085✔
4056
    {
4057
        found = s.find(preventDamageKeywords[i]);
139,278✔
4058
        if (found != string::npos)
139,278✔
4059
        {
4060
            string to = "";
34✔
4061
            string from = "";
34✔
4062

4063
            vector<string> splitTo = parseBetween(s, "to(", ")");
34✔
4064
            if (splitTo.size())
17✔
4065
                to = splitTo[1];
6✔
4066

4067
            vector<string> splitFrom = parseBetween(s, "from(", ")");
34✔
4068
            if (splitFrom.size())
17✔
4069
                from = splitFrom[1];
6✔
4070

4071
            MTGAbility * ab;
4072
            if (forceUEOT || preventDamageForceOneShot[i])
17✔
4073
                ab = NEW APreventDamageTypesUEOT(observer, id, card, to, from, preventDamageTypes[i]);
12✔
4074
            else
4075
                ab = NEW APreventDamageTypes(observer, id, card, to, from, preventDamageTypes[i]);
5✔
4076

4077
            if (preventDamageForceOneShot[i])
17✔
4078
                ab->oneShot = 1;
9✔
4079

4080
            return ab;
17✔
4081
        }
4082
    }
4083
 
4084
    //Reset damages on cards
4085
    found = s.find("resetdamage");
34,807✔
4086
    if (found != string::npos)
34,807✔
4087
    {
4088
        MTGAbility * a = NEW AAResetDamage(observer, id, card, target);
1,448✔
4089
        a->oneShot = 1;
1,448✔
4090
        return a;
1,448✔
4091
    }
4092

4093
        //Do nothing
4094
    found = s.find("donothing");
33,359✔
4095
    if (found != string::npos)
33,359✔
4096
    {
4097
        
4098
        MTGAbility * a = NEW AAFakeAbility(observer, id, card, target,newName);
4✔
4099
        a->oneShot = 1;
4✔
4100
        return a;
4✔
4101
    }
4102

4103
        //Epic
4104
    found = s.find("epic");
33,355✔
4105
    if (found != string::npos)
33,355✔
4106
    {
4107
        
4108
        MTGAbility * a = NEW AAEPIC(observer, id, card, target,newName);
×
4109
        a->oneShot = 1;
×
4110
        return a;
×
4111
    }
4112

4113
        //Forcefield
4114
    found = s.find("forcefield");
33,355✔
4115
    if (found != string::npos)
33,355✔
4116
    {
4117
        
4118
        MTGAbility * a = NEW AAEPIC(observer, id, card, target,"Forcefield",NULL,true);
×
4119
        a->oneShot = 1;
×
4120
        return a;
×
4121
    }
4122

4123
    //Damage
4124
    vector<string> splitDamage = parseBetween(s, "damage:", " ", false);
66,710✔
4125
    if (splitDamage.size())
33,355✔
4126
    {
4127
        Targetable * t = spell ? spell->getNextTarget() : NULL;
206✔
4128
        MTGAbility * a = NEW AADamager(observer, id, card, t, splitDamage[1], NULL, who);
206✔
4129
        a->oneShot = 1;
206✔
4130
        return a;
206✔
4131
    }
4132

4133
    //remove poison
4134
    vector<string> splitPoison = parseBetween(s, "alterpoison:", " ", false);
66,298✔
4135
    if (splitPoison.size())
33,149✔
4136
    {
4137
        int poison = 0;
×
4138
        WParsedInt* parser = NEW WParsedInt(splitPoison[1], card);
×
4139
        if(parser){
×
4140
            poison = parser->intValue;
×
4141
            SAFE_DELETE(parser);
×
4142
        }
4143
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4144
        MTGAbility * a = NEW AAAlterPoison(observer, id, card, t, poison, NULL, who);
×
4145
        a->oneShot = 1;
×
4146
        return a;
×
4147
    }
4148

4149
    //alter energy
4150
    vector<string> splitEnergy = parseBetween(s, "alterenergy:", " ", false);
66,298✔
4151
    if (splitEnergy.size())
33,149✔
4152
    {
4153
        int energy = 0;
×
4154
        WParsedInt* parser = NEW WParsedInt(splitEnergy[1], card);
×
4155
        if(parser){
×
4156
            energy = parser->intValue;
×
4157
            SAFE_DELETE(parser);
×
4158
        }
4159
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4160
        MTGAbility * a = NEW AAAlterEnergy(observer, id, card, t, energy, NULL, who);
×
4161
        a->oneShot = 1;
×
4162
        return a;
×
4163
    }
4164

4165
    //alter experience
4166
    vector<string> splitExperience = parseBetween(s, "alterexperience:", " ", false);
66,298✔
4167
    if (splitExperience.size())
33,149✔
4168
    {
4169
        int exp = 0;
×
4170
        WParsedInt* parser = NEW WParsedInt(splitExperience[1], card);
×
4171
        if(parser){
×
4172
            exp = parser->intValue;
×
4173
            SAFE_DELETE(parser);
×
4174
        }
4175
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4176
        MTGAbility * a = NEW AAAlterExperience(observer, id, card, t, exp, NULL, who);
×
4177
        a->oneShot = 1;
×
4178
        return a;
×
4179
    }
4180

4181
    //alter dungeon completed
4182
    vector<string> splitDungeonCompleted = parseBetween(s, "completedungeon:", " ", false);
66,298✔
4183
    if (splitDungeonCompleted.size())
33,149✔
4184
    {
4185
        int dungeoncompleted = 0;
×
4186
        WParsedInt* parser = NEW WParsedInt(splitDungeonCompleted[1], card);
×
4187
        if(parser){
×
4188
            dungeoncompleted = parser->intValue;
×
4189
            SAFE_DELETE(parser);
×
4190
        }
4191
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4192
        MTGAbility * a = NEW AAAlterDungeonCompleted(observer, id, card, t, dungeoncompleted, NULL, who);
×
4193
        a->oneShot = 1;
×
4194
        return a;
×
4195
    }
4196

4197
    //alter yidaro counter
4198
    vector<string> splitYidaroCounter = parseBetween(s, "alteryidarocount:", " ", false);
66,298✔
4199
    if (splitYidaroCounter.size())
33,149✔
4200
    {
4201
        int yidarocount = 0;
×
4202
        WParsedInt* parser = NEW WParsedInt(splitYidaroCounter[1], card);
×
4203
        if(parser){
×
4204
            yidarocount = parser->intValue;
×
4205
            SAFE_DELETE(parser);
×
4206
        }
4207
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4208
        MTGAbility * a = NEW AAAlterYidaroCount(observer, id, card, t, yidarocount, NULL, who);
×
4209
        a->oneShot = 1;
×
4210
        return a;
×
4211
    }
4212

4213
    //becomes monarch
4214
    vector<string> splitMonarch = parseBetween(s, "becomesmonarch", " ", false);
66,298✔
4215
    if (splitMonarch.size())
33,149✔
4216
    {
4217
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4218
        MTGAbility * a = NEW AAAlterMonarch(observer, id, card, t, NULL, who);
×
4219
        a->oneShot = 1;
×
4220
        return a;
×
4221
    }
4222

4223
    //takes the initiative
4224
    vector<string> splitInitiative = parseBetween(s, "taketheinitiative", " ", false);
66,298✔
4225
    if (splitInitiative.size())
33,149✔
4226
    {
4227
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4228
        MTGAbility * a = NEW AAAlterInitiative(observer, id, card, t, NULL, who);
×
4229
        a->oneShot = 1;
×
4230
        return a;
×
4231
    }
4232

4233
    //alter mutation counter on target card with trigger activation
4234
    vector<string> splitMutated = parseBetween(s, "altermutationcounter:", " ", false);
66,298✔
4235
    if (splitMutated.size())
33,149✔
4236
    {
4237
        WParsedInt* parser = NEW WParsedInt(splitMutated[1], card);
×
4238
        if(parser){
×
4239
            card->mutation += parser->intValue;
×
4240
            SAFE_DELETE(parser);
×
4241
        }
4242
        WEvent * e = NEW WEventCardMutated(card);
×
4243
        card->getObserver()->receiveEvent(e);
×
4244
    }
4245

4246
    //set mutation counter on source card with no trigger activation
4247
    vector<string> splitMutatedOver = parseBetween(s, "mutationover:", " ", false);
66,298✔
4248
    if (splitMutatedOver.size())
33,149✔
4249
    {
4250
        WParsedInt* parser = NEW WParsedInt(splitMutated[1], card);
×
4251
        if(parser){
×
4252
            card->mutation += parser->intValue;
×
4253
            SAFE_DELETE(parser);
×
4254
        }
4255
    }
4256
    vector<string> splitMutatedUnder = parseBetween(s, "mutationunder:", " ", false);
66,298✔
4257
    if (splitMutatedUnder.size())
33,149✔
4258
    {
4259
        WParsedInt* parser = NEW WParsedInt(splitMutated[1], card);
×
4260
        if(parser){
×
4261
            card->mutation += parser->intValue;
×
4262
            SAFE_DELETE(parser);
×
4263
        }
4264
    }
4265

4266
    //perform boast
4267
    found = s.find("doboast");
33,149✔
4268
    if (found != string::npos)
33,149✔
4269
    {
4270
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4271
        MTGAbility * a = NEW AABoastEvent(observer, id, card, t, NULL, who);
×
4272
        a->oneShot = 1;
×
4273
        return a;
×
4274
    }
4275

4276
    //perform surveil
4277
    found = s.find("surveil");
33,149✔
4278
    if (found != string::npos)
33,149✔
4279
    {
4280
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4281
        MTGAbility * a = NEW AASurveilEvent(observer, id, card, t, NULL, who);
×
4282
        a->oneShot = 1;
×
4283
        return a;
×
4284
    }
4285

4286
    //perform explores
4287
    found = s.find("explores");
33,149✔
4288
    if (found != string::npos)
33,149✔
4289
    {
4290
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4291
        MTGAbility * a = NEW AAExploresEvent(observer, id, card, t, NULL, who);
×
4292
        a->oneShot = 1;
×
4293
        return a;
×
4294
    }
4295

4296
    //set surveil offset of a player (eg. Enhanced Surveillance)
4297
    vector<string> splitSurveilOffset = parseBetween(s, "altersurvoffset:", " ", false);
66,298✔
4298
    if (splitSurveilOffset.size())
33,149✔
4299
    {
4300
        int surveilOffset = 0;
×
4301
        WParsedInt* parser = NEW WParsedInt(splitSurveilOffset[1], card);
×
4302
        if(parser){
×
4303
            surveilOffset = parser->intValue;
×
4304
            SAFE_DELETE(parser);
×
4305
        }
4306
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4307
        MTGAbility * a = NEW AAAlterSurveilOffset(observer, id, card, t, surveilOffset, NULL, who);
×
4308
        a->oneShot = 1;
×
4309
        return a;
×
4310
    }
4311

4312
    //set devotion offset of a player (eg. Altar of the Pantheon)
4313
    vector<string> splitDevotionOffset = parseBetween(s, "alterdevoffset:", " ", false);
66,298✔
4314
    if (splitDevotionOffset.size())
33,149✔
4315
    {
4316
        int devotionOffset = 0;
×
4317
        WParsedInt* parser = NEW WParsedInt(splitDevotionOffset[1], card);
×
4318
        if(parser){
×
4319
            devotionOffset = parser->intValue;
×
4320
            SAFE_DELETE(parser);
×
4321
        }
4322
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4323
        MTGAbility * a = NEW AAAlterDevotionOffset(observer, id, card, t, devotionOffset, NULL, who);
×
4324
        a->oneShot = 1;
×
4325
        return a;
×
4326
    }
4327

4328
    //prevent next damage
4329
    vector<string> splitPrevent = parseBetween(s, "prevent:", " ", false);
66,298✔
4330
    if (splitPrevent.size())
33,149✔
4331
    {
4332
        int preventing = 0;
4✔
4333
        WParsedInt* parser = NEW WParsedInt(splitPrevent[1], card);
4✔
4334
        if(parser){
4✔
4335
            preventing = parser->intValue;
4✔
4336
            SAFE_DELETE(parser);
4✔
4337
        }
4338
        Targetable * t = spell ? spell->getNextTarget() : NULL;
4✔
4339
        MTGAbility * a = NEW AADamagePrevent(observer, id, card, t, preventing, NULL, who);
4✔
4340
        a->oneShot = 1;
4✔
4341
        return a;
4✔
4342
    }
4343

4344
    //modify hand size - reduce maximum or increase
4345
    vector<string> splitHandMod = parseBetween(s, "hmodifer:", " ", false);
66,290✔
4346
    if (splitHandMod.size())
33,145✔
4347
    {
4348
        Damageable * t = spell ? spell->getNextDamageableTarget() : NULL;
×
4349
        MTGAbility * a = NEW AModifyHand(observer, id, card, t, splitHandMod[1], who);
×
4350
        return a;
×
4351
    }
4352

4353
    //Extra for Bestow
4354
    vector<string> splitAuraIncreaseReduce = parseBetween(s, "modbenchant(", ")", true);
66,290✔
4355
    if(splitAuraIncreaseReduce.size())
33,145✔
4356
    {
4357
        if(splitAuraIncreaseReduce[1].size())
×
4358
        {
4359
            Damageable * t = spell ? spell->getNextDamageableTarget() : NULL;
×
4360
            vector<string> ccParameters = split( splitAuraIncreaseReduce[1], ':');
×
4361
            int amount = 0;
×
4362
            WParsedInt* parser = NEW WParsedInt(ccParameters[1], card);
×
4363
            if(parser){
×
4364
                amount = parser->intValue;
×
4365
                SAFE_DELETE(parser);
×
4366
            }
4367
            int color = Constants::GetColorStringIndex(ccParameters[0]);
×
4368
            if(ccParameters[0] == "colorless")
×
4369
                color = 0;
×
4370
            if(ccParameters[0].size() && ccParameters[1].size())
×
4371
            {
4372
                MTGAbility * a = NEW AAuraIncreaseReduce(observer, id, card, t, amount, color, who);
×
4373
                return a;
×
4374
            }
4375
        }
4376
    }
4377

4378
    //set hand size
4379
    vector<string> splitSetHand = parseBetween(s, "sethand:", " ", false);
66,290✔
4380
    if (splitSetHand.size())
33,145✔
4381
    {
4382
        int hand = 0;
1,448✔
4383
        WParsedInt* parser = NEW WParsedInt(splitSetHand[1], card);
1,448✔
4384
        if(parser){
1,448✔
4385
            hand = parser->intValue;
1,448✔
4386
            SAFE_DELETE(parser);
1,448✔
4387
        }
4388
        Damageable * t = spell ? spell->getNextDamageableTarget() : NULL;
1,448✔
4389
                MTGAbility * a = NEW AASetHand(observer, id, card, t, hand, NULL, who);
1,448✔
4390
        a->oneShot = 1;
1,448✔
4391
        return a;
1,448✔
4392
    }
4393

4394
    //set life total
4395
    vector<string> splitLifeset = parseBetween(s, "lifeset:", " ", false);
63,394✔
4396
    if (splitLifeset.size())
31,697✔
4397
    {
4398
        WParsedInt * life = NEW WParsedInt(splitLifeset[1], spell, card);
3✔
4399
        Damageable * t = spell ? spell->getNextDamageableTarget() : NULL;
3✔
4400
        MTGAbility * a = NEW AALifeSet(observer, id, card, t, life, NULL, who);
3✔
4401
        a->oneShot = 1;
3✔
4402
        return a;
3✔
4403
    }
4404

4405
    //gain/lose life
4406
    vector<string> splitLife = parseBetween(s, "life:", " ", false);
63,388✔
4407
    if (splitLife.size())
31,694✔
4408
    {
4409
        Targetable * t = spell ? spell->getNextTarget() : NULL;
83✔
4410
        MTGAbility * a = NEW AALifer(observer, id, card, t, splitLife[1], false, NULL, who);
83✔
4411
        a->oneShot = 1;
83✔
4412
        return a;
83✔
4413
    }
4414

4415
    //siphon life - gain life lost this way
4416
    vector<string> splitSiphonLife = parseBetween(s, "lifeleech:", " ", false);
63,222✔
4417
    if (splitSiphonLife.size())
31,611✔
4418
    {
4419
        Targetable * t = spell ? spell->getNextTarget() : NULL;
1✔
4420
        MTGAbility * a = NEW AALifer(observer, id, card, t, splitSiphonLife[1], true, NULL, who);
1✔
4421
        a->oneShot = 1;
1✔
4422
        return a;
1✔
4423
    }
4424

4425
    // Win the game
4426
    found = s.find("wingame");
31,610✔
4427
    if (found != string::npos)
31,610✔
4428
    {
4429
        Damageable * d = spell ?  spell->getNextDamageableTarget() : NULL;
×
4430
        MTGAbility * a = NEW AAWinGame(observer, id, card, d, NULL, who);
×
4431
        a->oneShot = 1;
×
4432
        return a;
×
4433
    }
4434

4435
    //Draw
4436
    vector<string> splitDraw = parseBetween(s, "draw:", " ", false);
63,220✔
4437
    if (splitDraw.size())
31,610✔
4438
    {
4439
        Targetable * t = spell ? spell->getNextTarget() : NULL;
1,530✔
4440
        MTGAbility * a = NEW AADrawer(observer, id, card, t, NULL,splitDraw[1], who,s.find("noreplace") != string::npos);
1,530✔
4441
        a->oneShot = 1;
1,530✔
4442
        return a;
1,530✔
4443
    }
4444

4445
    //Deplete
4446
    vector<string> splitDeplete = parseBetween(s, "deplete:", " ", false);
60,160✔
4447
    if (splitDeplete.size())
30,080✔
4448
    {
4449
        bool namerepeat = false;
8✔
4450
        bool colorrepeat = false;
8✔
4451
        if (splitDeplete[0].find("color") != string::npos)
8✔
4452
            colorrepeat = true;
×
4453
        if (splitDeplete[0].find("name") != string::npos)
8✔
4454
            namerepeat = true;
×
4455
        Targetable * t = spell ? spell->getNextTarget() : NULL;
8✔
4456
        MTGAbility * a = NEW AADepleter(observer, id, card, t , splitDeplete[1], NULL, who, false, colorrepeat, namerepeat);
8✔
4457
        a->oneShot = 1;
8✔
4458
        return a;
8✔
4459
    }
4460

4461
    //Ingest
4462
    vector<string> splitIngest = parseBetween(s, "ingest:", " ", false);
60,144✔
4463
    if (splitIngest.size())
30,072✔
4464
    {
4465
        bool namerepeat = false;
×
4466
        bool colorrepeat = false;
×
4467
        if (splitIngest[0].find("coloringest") != string::npos)
×
4468
            colorrepeat = true;
×
4469
        if (splitIngest[0].find("nameingest") != string::npos)
×
4470
            namerepeat = true;
×
4471
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
4472
        MTGAbility * a = NEW AADepleter(observer, id, card, t , splitIngest[1], NULL, who, true, colorrepeat, namerepeat);
×
4473
        a->oneShot = 1;
×
4474
        return a;
×
4475
    }
4476

4477
    //Cascade
4478
    vector<string> splitCascade = parseBetween(s, "cascade:", " ", false);
60,144✔
4479
    if (splitCascade.size())
30,072✔
4480
    {
4481
        MTGAbility * a = NEW AACascade(observer, id, card, target, splitCascade[1], NULL);
×
4482
        a->oneShot = 1;
×
4483
        return a;
×
4484
    }
4485

4486
    //modify turns
4487
    vector<string> splitModTurn = parseBetween(s, "turns:", " ", false);
60,144✔
4488
    if (splitModTurn.size())
30,072✔
4489
    {
4490
        Targetable * t = spell ? spell->getNextTarget() : NULL;
2✔
4491
        MTGAbility * a = NEW AAModTurn(observer, id, card, t , splitModTurn[1], NULL, who);
2✔
4492
        a->oneShot = 1;
2✔
4493
        return a;
2✔
4494
    }
4495

4496
    //Shuffle
4497
    found = s.find("shuffle");
30,070✔
4498
    if (found != string::npos)
30,070✔
4499
    {
4500
        Targetable * t = spell? spell->getNextTarget() : NULL;
2✔
4501
        MTGAbility * a = NEW AAShuffle(observer, id, card, t, NULL, who);
2✔
4502
        a->oneShot = 1;
2✔
4503
        return a;
2✔
4504
    }
4505

4506
    //Serum Powder
4507
    found = s.find("serumpowder");
30,068✔
4508
    if (found != string::npos)
30,068✔
4509
    {
4510
        Targetable * t = spell? spell->getNextTarget() : NULL;
×
4511
        MTGAbility * a = NEW AAMulligan(observer, id, card, t, NULL, who);
×
4512
        a->oneShot = 1;
×
4513
        return a;
×
4514
    }
4515

4516
    //Remove Mana from ManaPool
4517
    vector<string> splitRemove = parseBetween(s, "removemana(", ")");
60,136✔
4518
    if (splitRemove.size())
30,068✔
4519
    {
4520
        Targetable * t = spell? spell->getNextTarget() : NULL;
17,376✔
4521
        bool forceclean = false;
17,376✔
4522
        if(s.find("forceclean")!=string::npos)
17,376✔
4523
            forceclean = true;
×
4524
        MTGAbility *a = NEW AARemoveMana(observer, id, card, t, splitRemove[1], who, forceclean);
17,376✔
4525
        a->oneShot = 1;
17,376✔
4526
        return a;
17,376✔
4527
    }
4528

4529
    //Lose subtypes of a given type
4530
    vector<string> splitLoseTypes = parseBetween(s, "losesubtypesof(", ")");
25,384✔
4531
    if (splitLoseTypes.size())
12,692✔
4532
    {
4533
        int parentType = MTGAllCards::findType(splitLoseTypes[1]);
8✔
4534
        return NEW ALoseSubtypes(observer, id, card, target, parentType, false);
8✔
4535
    }
4536

4537
    //Lose a specific type (e.g. "Conversion").
4538
    vector<string> splitLoseSpecType = parseBetween(s, "losesatype(", ")");
25,368✔
4539
    if (splitLoseSpecType.size())
12,684✔
4540
    {
4541
        int typeToLose = MTGAllCards::findType(splitLoseSpecType[1]);
×
4542
        return NEW ALoseSubtypes(observer, id, card, target, typeToLose, true);
×
4543
    }
4544

4545
    //Cast/Play Restrictions
4546
    for (size_t i = 0; i < kMaxCastKeywordsCount; ++i)
35,146✔
4547
    {
4548
        vector<string> splitCast = parseBetween(s, kMaxCastKeywords[i], ")");
46,379✔
4549
        if (splitCast.size())
23,917✔
4550
        {
4551
            TargetChooserFactory tcf(observer);
1,455✔
4552
            TargetChooser * castTargets = tcf.createTargetChooser(splitCast[1], card);
1,455✔
4553

4554
            vector<string> splitValue = parseBetween(splitCast[2], "", " ", false);
2,910✔
4555
            if (!splitValue.size())
1,455✔
4556
            {
4557
                DebugTrace ("MTGABILITY: Error parsing Cast/Play Restriction" << s);
×
4558
                return NULL;
×
4559
            }
4560

4561
            string valueStr = splitValue[1];
2,910✔
4562
            bool modifyExisting = (valueStr.find("+") != string::npos || valueStr.find("-") != string::npos);
1,455✔
4563

4564
            WParsedInt * value = NEW WParsedInt(valueStr, spell, card);
1,455✔
4565
            Targetable * t = spell? spell->getNextTarget() : NULL;
1,455✔
4566
            if (((card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY)) && !forceForever && !untilYourNextEndTurn && !untilYourNextTurn) || forceUEOT)
1,455✔
4567
            {
4568
                return NEW AInstantCastRestrictionUEOT(observer, id, card, t, castTargets, value, modifyExisting, kMaxCastZones[i], who);
6✔
4569
            }
4570
            return NEW ACastRestriction(observer, id, card, t, castTargets, value, modifyExisting, kMaxCastZones[i], who);
1,449✔
4571
        }
4572
    }
4573

4574
    //Discard
4575
    vector<string> splitDiscard = parseBetween(s, "discard:", " ", false);
22,458✔
4576
    if (splitDiscard.size())
11,229✔
4577
    {
4578
        Targetable * t = spell ? spell->getNextTarget() : NULL;
9✔
4579
        MTGAbility * a = NEW AARandomDiscarder(observer, id, card, t, splitDiscard[1], NULL, who);
9✔
4580
        a->oneShot = 1;
9✔
4581
        return a;
9✔
4582
    }
4583

4584
    //rampage
4585
    vector<string> splitRampage = parseBetween(s, "rampage(", ")");
22,440✔
4586
    if (splitRampage.size())
11,220✔
4587
    {
4588
        replace(splitRampage[1].begin(), splitRampage[1].end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using rampage keyword inside transforms)
11✔
4589
        vector<string> rampageParameters = split(splitRampage[1], ',');
22✔
4590
        int power, toughness;
4591
        if (!parsePowerToughness(rampageParameters[0], &power, &toughness))
11✔
4592
        {
4593
            DebugTrace("MTGAbility Parse error in rampage" << s);
×
4594
            return NULL;
×
4595
        }
4596
        int MaxOpponent = 0;
11✔
4597
        WParsedInt* parser = NEW WParsedInt(rampageParameters[1], card);
11✔
4598
        if(parser){
11✔
4599
            MaxOpponent = parser->intValue;
11✔
4600
            SAFE_DELETE(parser);
11✔
4601
        }
4602
        return NEW ARampageAbility(observer, id, card, power, toughness, MaxOpponent);
11✔
4603
    }
4604

4605
    //evole
4606
    if (s.find("evolve") != string::npos)
11,209✔
4607
    {
4608
        return NEW AEvolveAbility(observer, id, card);
1✔
4609
    }
4610

4611
    //produce additional mana when tapped for mana
4612
    if (s.find("produceextra:") != string::npos)
11,208✔
4613
    {
4614
        return NEW AProduceMana(observer, id, card,s.substr(13));
14✔
4615
    }
4616

4617
    //produce additional mana when a mana is engaged
4618
    if (s.find("producecolor:") != string::npos)
11,194✔
4619
    {
4620
        return NEW AEngagedManaAbility(observer, id, card,s.substr(13));
×
4621
    }
4622

4623
    //reducelife to specific value
4624
    if (s.find("reduceto:") != string::npos)
11,194✔
4625
    {
4626
        return NEW AReduceToAbility(observer, id, card,s.substr(9));
×
4627
    }
4628

4629
    //attack cost
4630
    if (s.find("attackcost:") != string::npos)
11,194✔
4631
    {
4632
        return NEW AAttackSetCost(observer, id, card, s.substr(11));
×
4633
    }
4634

4635
    //attack cost + planeswalker
4636
    if (s.find("attackpwcost:") != string::npos)
11,194✔
4637
    {
4638
        return NEW AAttackSetCost(observer, id, card, s.substr(13),true);
×
4639
    }
4640

4641
    //block cost
4642
    if (s.find("blockcost:") != string::npos)
11,194✔
4643
    {
4644
        return NEW ABlockSetCost(observer, id, card, s.substr(10));
×
4645
    }
4646

4647
    //flanking
4648
    if (s.find("flanker") != string::npos)
11,194✔
4649
    {
4650
        return NEW AFlankerAbility(observer, id, card);
1✔
4651
    }
4652

4653
    //spirit link
4654
    //combat damage spirit link
4655
    if (s.find("spiritlink") != string::npos)
11,193✔
4656
    {
4657
        bool combatOnly = false;
10✔
4658
        if(s.find("combatspiritlink") != string::npos)
10✔
4659
        {
4660
            combatOnly = true;
×
4661
        }
4662
        return NEW ASpiritLinkAbility(observer, id, card, combatOnly);
10✔
4663
    }
4664

4665
    //bushido
4666
    vector<string> splitBushido = parseBetween(s, "bushido(", ")");
22,366✔
4667
    if (splitBushido.size())
11,183✔
4668
    {
4669
        string power, toughness;
4✔
4670
        vector<string>splitPT = split(splitBushido[1],'/');
4✔
4671
        if(!splitPT.size())
2✔
4672
            return NULL;
×
4673
        return NEW ABushidoAbility(observer, id, card,splitBushido[1],splitPT[0]);
2✔
4674
    }
4675
    vector<string> splitPhaseAlter = parseBetween(s, "phasealter(", ")");
22,362✔
4676
    if (splitPhaseAlter.size())
11,181✔
4677
    {
4678
        string power, toughness;
4✔
4679
        replace(splitPhaseAlter[1].begin(), splitPhaseAlter[1].end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using phasealter keyword inside transforms)
2✔
4680
        vector<string>splitPhaseAlter2 = split(splitPhaseAlter[1],',');
4✔
4681
        if(splitPhaseAlter2.size() < 3)
2✔
4682
            return NULL;
×
4683
        string after = "";
4✔
4684
        if(splitPhaseAlter2.size() > 3)
2✔
4685
        {
4686
            vector<string> splitPhaseAlterAfter = parseBetween(splitPhaseAlter2[3], "after<", ">");
×
4687
            if(splitPhaseAlterAfter.size())
×
4688
                after = splitPhaseAlterAfter[1];
×
4689
        }
4690
        MTGAbility * a1 = NEW APhaseAlter(observer, id, card, target,splitPhaseAlter2[0].find("add") != string::npos, splitPhaseAlter2[1],splitPhaseAlter2[2], s.find("nextphase") != string::npos,after);
2✔
4691
        a1->canBeInterrupted = false;
2✔
4692
        return NEW GenericAbilityMod(observer, 1, card,spell?spell->getNextDamageableTarget():(Damageable *) target, a1);
2✔
4693
    }
4694

4695
    //loseAbilities
4696
    if (s.find("loseabilities") != string::npos)
11,179✔
4697
    {
4698
        return NEW ALoseAbilities(observer, id, card, target);
13✔
4699
    }
4700

4701
    //counter
4702
    vector<string> splitCounter = parseBetween(s, "counter(", ")");
22,332✔
4703
    if (splitCounter.size())
11,166✔
4704
    {
4705
        string counterString = splitCounter[1];
146✔
4706
        Counter * counter = parseCounter(counterString, target, spell);
73✔
4707
        if (!counter)
73✔
4708
        {
4709
            DebugTrace("MTGAbility: can't parse counter:" << s);
×
4710
            return NULL;
×
4711
        }
4712
        bool noevent = (s.find("notrg") != string::npos)?true:false; // Added a way to don't trigger @counter effect.
73✔
4713
        MTGAbility * a =
4714
            NEW AACounter(observer, id, card, target,counterString, counter->name.c_str(), counter->power, counter->toughness, counter->nb, counter->maxNb, noevent);
73✔
4715
        delete (counter);
73✔
4716
        a->oneShot = 1;
73✔
4717
        return a;
73✔
4718
    }
4719

4720
    //bestow
4721
    found = s.find("bstw");
11,093✔
4722
    if (found != string::npos)
11,093✔
4723
    {
4724
        MTGAbility * a = NEW ABestow(observer, id, card, target);
×
4725
        a->oneShot = 1;
×
4726
        return a;
×
4727

4728
    }
4729

4730
    //no counters on target of optional type
4731
    vector<string> splitCounterShroud = parseBetween(s, "countershroud(", ")");
22,186✔
4732
    if (splitCounterShroud.size())
11,093✔
4733
    {
4734
        string counterShroudString = splitCounterShroud[1];
4✔
4735
        Counter * counter = NULL;
2✔
4736
        if(splitCounterShroud[1] == "any")
2✔
4737
        {
4738
            counter = NULL;
1✔
4739
        }
4740
        else
4741
        {
4742
            counter = parseCounter(counterShroudString, target, spell);
1✔
4743
            if (!counter)
1✔
4744
            {
4745
                DebugTrace("MTGAbility: can't parse counter:" << s);
×
4746
                return NULL;
×
4747
            }
4748
        }
4749
        TargetChooser * csTc = NULL;
2✔
4750
        if(splitCounterShroud[2].size() > 1)
2✔
4751
        {
4752
            TargetChooserFactory af(card->getObserver());
1✔
4753
            csTc = af.createTargetChooser(splitCounterShroud[2],card);
1✔
4754
        }
4755
        MTGAbility * a = NEW ACounterShroud(observer, id, card, target,csTc,counter);
2✔
4756
        return a;
2✔
4757
    }
4758
    //use counters to track by counters to track an efect by counter name.
4759
    vector<string> splitCounterTracking = parseBetween(s, "countertrack(", ")");
22,182✔
4760
    if (splitCounterTracking.size())
11,091✔
4761
    {
4762
        string splitCounterTrack = splitCounterTracking[1];
×
4763
        return NEW ACounterTracker(observer, id, card, target,splitCounterTrack);
×
4764
    }
4765
    //duplicate counters
4766
    vector<string> splitDuplicateCounters = parseBetween(s, "duplicatecounters(", ")");
22,182✔
4767
    if (splitDuplicateCounters.size())
11,091✔
4768
    {
4769
        MTGAbility * a = NEW AADuplicateCounters(observer, id, card, target, NULL);
×
4770
        a->oneShot = 1;
×
4771
        a->canBeInterrupted = false;
×
4772
        string counterString = splitDuplicateCounters[1];
×
4773
        if(counterString.find("all") != string::npos)
×
4774
            ((AADuplicateCounters*)a)->allcounters = true;
×
4775
        if(counterString.find("single") != string::npos)
×
4776
            ((AADuplicateCounters*)a)->single = true;
×
4777
        return a;
×
4778
    }
4779
    //remove single counter of any type
4780
    vector<string> splitRemoveSpecificCounters = parseBetween(s, "removesinglecountertype(", ")");
22,182✔
4781
    if (splitRemoveSpecificCounters.size())
11,091✔
4782
    {
4783
        int nb = 0;
×
4784
        WParsedInt* parser = NEW WParsedInt(splitRemoveSpecificCounters[1], card);
×
4785
        if(parser){
×
4786
            nb = parser->intValue;
×
4787
            SAFE_DELETE(parser);
×
4788
        }
4789
        MTGAbility * a = NEW AARemoveSingleCounter(observer, id, card, target, NULL, nb);
×
4790
        a->oneShot = 1;
×
4791
        a->canBeInterrupted = false;
×
4792
        return a;
×
4793
    }
4794
    //removes all counters of the specifified type.
4795
    vector<string> splitRemoveCounter = parseBetween(s, "removeallcounters(", ")");
22,182✔
4796
    if (splitRemoveCounter.size())
11,091✔
4797
    {
4798
        string counterString = splitRemoveCounter[1];
×
4799
        if(counterString.find("all") != string::npos)
×
4800
        {
4801
            MTGAbility * a = NEW AARemoveAllCounter(observer, id, card, target, "All", 0, 0, 1, true);
×
4802
            a->oneShot = 1;
×
4803
            return a;
×
4804
        }
4805

4806
        Counter * counter = parseCounter(counterString, target, spell);
×
4807
        if (!counter)
×
4808
        {
4809
            DebugTrace("MTGAbility: can't parse counter:" << s);
×
4810
            return NULL;
×
4811
        }
4812

4813
        MTGAbility * a =
4814
            NEW AARemoveAllCounter(observer, id, card, target, counter->name.c_str(), counter->power, counter->toughness, counter->nb,false);
×
4815
        delete (counter);
×
4816
        a->oneShot = 1;
×
4817
        return a;
×
4818
    }
4819
    
4820
    //Becomes... (animate artifact...: becomes(Creature, manacost/manacost)
4821
    vector<string> splitBecomes = parseBetween(s, "becomes(", ")");
22,182✔
4822
    if (splitBecomes.size())
11,091✔
4823
    {
4824
        replace(splitBecomes[1].begin(), splitBecomes[1].end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using becomes keyword inside transforms)
6✔
4825
        vector<string> becomesParameters = split(splitBecomes[1], ',');
12✔
4826
        string stypes = becomesParameters[0];
12✔
4827
        string newPower = "";
12✔
4828
        string newToughness = "";
12✔
4829
        bool ptFound = false;
6✔
4830
        if(becomesParameters.size() >1)
6✔
4831
        {
4832
            vector<string> pt = split(becomesParameters[1], '/');
10✔
4833
            if(pt.size() > 1)
5✔
4834
            {
4835
                newPower = pt[0];
4✔
4836
                newToughness = pt[1];
4✔
4837
                ptFound = true;
4✔
4838
            }
4839
        }
4840
        string sabilities = "";
12✔
4841
        unsigned int becomesSize = ptFound?2:1;
6✔
4842
        if(becomesParameters.size() > becomesSize)
6✔
4843
        {
4844
            for(unsigned int i = becomesSize;i < becomesParameters.size();i++)
2✔
4845
            { 
4846
                sabilities.append(becomesParameters[i].c_str());
1✔
4847
                if(i+1 < becomesParameters.size())
1✔
4848
                    sabilities.append(",");
×
4849
            }
4850
        }
4851
        if (oneShot || forceUEOT || forceForever)
6✔
4852
            return NEW ATransformerInstant(observer, id, card, target, stypes, sabilities,newPower,ptFound,newToughness,ptFound,vector<string>(),false,forceForever,untilYourNextTurn,untilYourNextEndTurn);
×
4853

4854
        return  NEW ATransformer(observer, id, card, target, stypes, sabilities,newPower,ptFound,newToughness,ptFound,vector<string>(),false,forceForever,untilYourNextTurn,untilYourNextEndTurn);
6✔
4855
    }
4856

4857
    //Remake... (animate artifact...: Remake(Creature: manacost/manacost) - alternative
4858
    vector<string> splitRemake = parseBetween(s, "remake(", ")");
22,170✔
4859
    if (splitRemake.size())
11,085✔
4860
    {
4861
        vector<string> RemakeParameters = split(splitRemake[1], ':');
×
4862
        string stypes = RemakeParameters[0];
×
4863
        string newPower = "";
×
4864
        string newToughness = "";
×
4865
        bool ptFound = false;
×
4866
        if(RemakeParameters.size() >1)
×
4867
        {
4868
            vector<string> pt = split(RemakeParameters[1], '/');
×
4869
            if(pt.size() > 1)
×
4870
            {
4871
                newPower = pt[0];
×
4872
                newToughness = pt[1];
×
4873
                ptFound = true;
×
4874
            }
4875
        }
4876
        string sabilities = "";
×
4877
        unsigned int RemakeSize = ptFound?2:1;
×
4878
        if(RemakeParameters.size() > RemakeSize)
×
4879
        {
4880
            for(unsigned int i = RemakeSize;i < RemakeParameters.size();i++)
×
4881
            { 
4882
                sabilities.append(RemakeParameters[i].c_str());
×
4883
                if(i+1 < RemakeParameters.size())
×
4884
                    sabilities.append(",");
×
4885
            }
4886
        }
4887
        if (oneShot || forceUEOT || forceForever)
×
4888
            return NEW ATransformerInstant(observer, id, card, target, stypes, sabilities,newPower,ptFound,newToughness,ptFound,vector<string>(),false,forceForever,untilYourNextTurn,untilYourNextEndTurn);
×
4889

4890
        return  NEW ATransformer(observer, id, card, target, stypes, sabilities,newPower,ptFound,newToughness,ptFound,vector<string>(),false,forceForever,untilYourNextTurn,untilYourNextEndTurn);
×
4891
    }
4892

4893
    //bloodthirst
4894
    vector<string> splitBloodthirst = parseBetween(s, "bloodthirst:", " ", false);
22,170✔
4895
    if (splitBloodthirst.size())
11,085✔
4896
    {
4897
        int nb = 0;
1✔
4898
        WParsedInt* parser = NEW WParsedInt(splitBloodthirst[1], card);
1✔
4899
        if(parser){
1✔
4900
            nb = parser->intValue;
1✔
4901
            SAFE_DELETE(parser);
1✔
4902
        }
4903
        return NEW ABloodThirst(observer, id, card, target, nb);
1✔
4904
    }
4905

4906
    //Vanishing
4907
    vector<string> splitVanishing = parseBetween(s, "vanishing:", " ", false);
22,168✔
4908
    if (splitVanishing.size())
11,084✔
4909
    {
4910
        int nb = 0;
4✔
4911
        WParsedInt* parser = NEW WParsedInt(splitVanishing[1], card);
4✔
4912
        if(parser){
4✔
4913
            nb = parser->intValue;
4✔
4914
            SAFE_DELETE(parser);
4✔
4915
        }
4916
        return NEW AVanishing(observer, id, card, NULL, restrictions, nb, "time");
4✔
4917
    }
4918

4919
    //Fading
4920
    vector<string> splitFading = parseBetween(s, "fading:", " ", false);
22,160✔
4921
    if (splitFading.size())
11,080✔
4922
    {
4923
        int nb = 0;
1✔
4924
        WParsedInt* parser = NEW WParsedInt(splitFading[1], card);
1✔
4925
        if(parser){
1✔
4926
            nb = parser->intValue;
1✔
4927
            SAFE_DELETE(parser);
1✔
4928
        }
4929
        return NEW AVanishing(observer, id, card, NULL, restrictions, nb, "fade");
1✔
4930
    }
4931

4932
    //Alter cost
4933
    if (s.find("altercost(") != string::npos)
11,079✔
4934
        return getManaReduxAbility(s.substr(s.find("altercost(") + 10), id, spell, card, target);
3✔
4935

4936
    //transforms (e.g. hivestone,living enchantment)
4937
    found = s.find("transforms((");
11,076✔
4938
    if (found != string::npos)
11,076✔
4939
    {
4940
        string extraTransforms = "";
5,998✔
4941
        string transformsParamsString = "";
5,998✔
4942
        transformsParamsString.append(storedString);//the string between found and real end is removed at start.
2,999✔
4943
        
4944
        found = transformsParamsString.find("transforms(("); // Try to handle transforms ability inside transforms keyword.
2,999✔
4945
        if (found != string::npos && extraTransforms.empty())
2,999✔
4946
        {
4947
            size_t real_end = transformsParamsString.find("))", found);
×
4948
            size_t stypesStartIndex = found + 12;
×
4949
            extraTransforms.append(transformsParamsString.substr(stypesStartIndex, real_end - stypesStartIndex).c_str());
×
4950
            transformsParamsString.erase(stypesStartIndex, real_end - stypesStartIndex);
×
4951
        }
4952
        vector<string> effectParameters = split( transformsParamsString, ',');
5,998✔
4953
        string stypes = effectParameters[0];
5,998✔
4954

4955
        string sabilities = transformsParamsString.substr(stypes.length());
5,998✔
4956
        bool newpowerfound = false;
2,999✔
4957
        string newpower = "";
5,998✔
4958
        bool newtoughnessfound = false;
2,999✔
4959
        string newtoughness = "";
5,998✔
4960
        vector <string> abilities = split(sabilities, ',');
5,998✔
4961
        bool newAbilityFound = false;
2,999✔
4962
        vector<string> newAbilitiesList;
5,998✔
4963
        storedString.erase();
2,999✔
4964
        storedString.append(extraTransforms);
2,999✔
4965
        extraTransforms.erase();
2,999✔
4966
        for (unsigned int i = 0 ; i < abilities.size() ; i++)
7,485✔
4967
        {
4968
            if(abilities[i].empty())
4,486✔
4969
                abilities.erase(abilities.begin()+i);
2,986✔
4970
        }            
4971
        for(unsigned int j = 0;j < abilities.size();j++)
7,485✔
4972
        {
4973
            vector<string> splitPower = parseBetween(abilities[j], "setpower=", ",", false);
8,972✔
4974
            if(splitPower.size())
4,486✔
4975
            {
4976
                newpowerfound = true;
13✔
4977
                newpower = splitPower[1];
13✔
4978
            }
4979
            vector<string> splitToughness = parseBetween(abilities[j], "settoughness=", ",", false);
8,972✔
4980
            if(splitToughness.size())
4,486✔
4981
            {
4982
                newtoughnessfound = true;
13✔
4983
                newtoughness = splitToughness[1];
13✔
4984
            }
4985
            if(abilities[j].find("newability[") != string::npos)
4,486✔
4986
            {
4987
                size_t NewSkill = abilities[j].find("newability[");
4,442✔
4988
                size_t NewSkillEnd = abilities[j].find_last_of("]");
4,442✔
4989
                string newAbilities = abilities[j].substr(NewSkill + 11,NewSkillEnd - NewSkill - 11);
8,884✔
4990
                size_t pos = newAbilities.find("transforms(())"); // Try to handle transforms ability inside transforms keyword.
4,442✔
4991
                if(pos != string::npos)
4,442✔
4992
                    newAbilities.replace(pos, 14, "transforms((" + storedString + "))");
×
4993
                newAbilitiesList.push_back(newAbilities);
4,442✔
4994
                newAbilityFound = true;
4,442✔
4995
            }
4996
        }
4997
        size_t pos = sabilities.find("transforms(())"); // Try to handle transforms ability inside transforms keyword.
2,999✔
4998
        if(pos != string::npos)
2,999✔
4999
            sabilities.replace(pos, 14, "transforms((" + storedString + "))");
×
5000

5001
        if (oneShot || forceUEOT || forceForever)
2,999✔
5002
            return NEW ATransformerInstant(observer, id, card, target, stypes, sabilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,forceForever,untilYourNextTurn,untilYourNextEndTurn,newName);
1,515✔
5003
        
5004
        return NEW ATransformer(observer, id, card, target, stypes, sabilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,forceForever,untilYourNextTurn,untilYourNextEndTurn,newName);
1,484✔
5005

5006
    }
5007
    
5008
    //Reveal:x (activate aility) 
5009
    vector<string> splitReveal = parseBetween(s, "reveal:", "revealend", false);
16,154✔
5010
    if (splitReveal.size())
8,077✔
5011
    {
5012
        string backup = storedAbilityString;
28✔
5013
        storedAbilityString = "";//we clear the string here for cards that contain more than 1 reveal.
14✔
5014
        GenericRevealAbility * a = NEW GenericRevealAbility(observer, id, card, target, backup);
14✔
5015
        a->oneShot = 1;
14✔
5016
        a->canBeInterrupted = false;
14✔
5017
        a->named = newName;
14✔
5018
        return a;
14✔
5019
    }
5020

5021
    //scry:x (activate aility) 
5022
    vector<string> splitScry = parseBetween(s, "scry:", "scryend", false);
16,126✔
5023
    if (splitScry.size())
8,063✔
5024
    {
5025
        string backup = storedAbilityString;
×
5026
        storedAbilityString = "";//we clear the string here for cards that contain more than 1 reveal.
×
5027
        GenericScryAbility * a = NEW GenericScryAbility(observer, id, card, target, backup);
×
5028
        a->oneShot = 1;
×
5029
        a->canBeInterrupted = false;
×
5030
        return a;
×
5031
    }
5032

5033
    //meld helper class
5034
    vector<string> splitMeldFrom = parseBetween(s, "meldfrom(", ")", true);
16,126✔
5035
    if (splitMeldFrom.size())
8,063✔
5036
    {
5037
        string splitMeldNames = "";
×
5038
        if (splitMeldFrom[1].size())
×
5039
        {
5040
            splitMeldNames = splitMeldFrom[1];
×
5041
            replace(splitMeldNames.begin(), splitMeldNames.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using meldfrom keyword inside transforms)
×
5042
        }
5043
        MTGAbility * a = NEW AAMeldFrom(observer, id, card, target, splitMeldNames);
×
5044
        a->oneShot = true;
×
5045
        return a;
×
5046
    }
5047

5048
    //meld
5049
    vector<string> splitMeld = parseBetween(s, "meld(", ")", true);
16,126✔
5050
    if (splitMeld.size())
8,063✔
5051
    {
5052
        string splitMeldName = "";
×
5053
        if (splitMeld[1].size())
×
5054
        {
5055
            splitMeldName = splitMeld[1];
×
5056
            replace(splitMeldName.begin(), splitMeldName.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using meld keyword inside transforms)
×
5057
        }
5058
        MTGAbility * a = NEW AAMeld(observer, id, card, target, splitMeldName);
×
5059
        a->oneShot = true;
×
5060
        return a;
×
5061
    }
5062

5063
    //doubleside
5064
    vector<string> splitSide = parseBetween(s, "doubleside(", ")", true);
16,126✔
5065
    if (splitSide.size() && card->currentZone != card->controller()->game->battlefield) // It's not allowed to turn side on battlefield.
8,063✔
5066
    {
5067
        string splitSideName = "";
×
5068
        if (splitSide[1].size())
×
5069
        {
5070
            splitSideName = splitSide[1];
×
5071
            replace(splitSideName.begin(), splitSideName.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using doubleside keyword inside transforms)
×
5072
        }
5073
        MTGAbility * a = NEW AATurnSide(observer, id, card, target, splitSideName);
×
5074
        a->oneShot = true;
×
5075
        return a;
×
5076
    }
5077

5078
    //flip
5079
    vector<string> splitFlipStat = parseBetween(s, "flip(", ")", true);
16,126✔
5080
    if(splitFlipStat.size())
8,063✔
5081
    {
5082
        string flipStats = "";
×
5083
        if(splitFlipStat[1].size())
×
5084
        {
5085
            /*vector<string>FlipStats = split(splitFlipStat[1],'%');*/
5086
            flipStats = splitFlipStat[1];
×
5087
            replace(flipStats.begin(), flipStats.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using flip keyword inside transforms)
×
5088
        }
5089
        vector<string> splitType = parseBetween(s, "forcetype(", ")", true); // Added to flip instants and sorceries as permanents (es. Zendikar Rising Modal Double Faced cards).
×
5090
        string forcetype = "";
×
5091
        if(splitType.size() && splitType[1].size())
×
5092
        {
5093
            forcetype = splitType[1];
×
5094
        }
5095
        bool backfromcopy = (s.find("undocpy") != string::npos)?true:false; // Added to undo the copy effect at end of turn (es. Scion of the Ur-Dragon).
×
5096
        bool transmode = card->getdoubleFaced() == "kamiflip"?true:false;
×
5097
        MTGAbility * a = NEW AAFlip(observer, id, card, target, flipStats, transmode, false, forcetype, backfromcopy);
×
5098
        //andability
5099
        if(storedAndAbility.size())
×
5100
        {
5101
            string stored = storedAndAbility;
×
5102
            storedAndAbility.clear();
×
5103
            ((AAFlip*)a)->andAbility = parseMagicLine(stored, id, spell, card);
×
5104
        }
5105
        return a;
×
5106
    }
5107

5108
    //changecost - alternate for altercost
5109
    vector<string> splitChangeCost = parseBetween(s, "changecost(", ")", true);
16,126✔
5110
    if(splitChangeCost.size())
8,063✔
5111
    {
5112
        if(splitChangeCost[1].size())
4✔
5113
        {
5114
            vector<string> ccParameters = split( splitChangeCost[1], ':');
4✔
5115
            int amount = 0;
4✔
5116
            WParsedInt * value = NEW WParsedInt(ccParameters[1], NULL, card);
4✔
5117
            if(value){
4✔
5118
                amount = value->getValue();
4✔
5119
                SAFE_DELETE(value);
4✔
5120
            }
5121
            int color = Constants::GetColorStringIndex(ccParameters[0]);
4✔
5122
            if(ccParameters[0] == "colorless")
4✔
5123
                color = 0;
4✔
5124
            if(ccParameters[0].size() && ccParameters[1].size())
4✔
5125
            {
5126
                MTGAbility * a = NEW AAlterCost(observer, id, card, target, amount, color);
4✔
5127
                return a;
4✔
5128
            }
5129
        }
5130
    }
5131

5132
    //Mana Producer
5133
    found = s.find("add");
8,059✔
5134
    if (found != string::npos)
8,059✔
5135
    {
5136
        bool doesntEmptyTilueot = s.find("doesntempty") != string::npos;
7,367✔
5137
        ManaCost * output = ManaCost::parseManaCost(s.substr(found),NULL,card);
7,367✔
5138
        Targetable * t = spell ? spell->getNextTarget() : NULL;
7,367✔
5139
        MTGAbility * a = NEW AManaProducer(observer, id, card, t, output, NULL, who,s.substr(found),doesntEmptyTilueot);
7,367✔
5140
        a->oneShot = 1;
7,367✔
5141
        if(newName.size())
7,367✔
5142
            ((AManaProducer*)a)->menutext = newName;
×
5143
        if(storedAndAbility.size())
7,367✔
5144
        {
5145
            string stored = storedAndAbility;
4✔
5146
            storedAndAbility.clear();
2✔
5147
            ((AManaProducer*)a)->andAbility = parseMagicLine(stored, id, spell, card);
2✔
5148
        }
5149
        return a;
7,367✔
5150
    }
5151

5152
    //another mana producer exempted for canproduce
5153
    found = s.find("out");
692✔
5154
    if (found != string::npos)
692✔
5155
    {
5156
        bool doesntEmptyTilueot = s.find("doesntempty") != string::npos;
×
5157
        ManaCost * output = ManaCost::parseManaCost(s.substr(found),NULL,card);
×
5158
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
5159
        MTGAbility * a = NEW AManaProducer(observer, id, card, t, output, NULL, who,s.substr(found),doesntEmptyTilueot);
×
5160
        a->oneShot = 1;
×
5161
        if(newName.size())
×
5162
            ((AManaProducer*)a)->menutext = newName;
×
5163
        return a;
×
5164
    }
5165

5166
    //Protection from...
5167
    vector<string> splitProtection = parseBetween(s, "protection from(", ")");
1,384✔
5168
    if (splitProtection.size())
692✔
5169
    {
5170
        TargetChooserFactory tcf(observer);
3✔
5171
        TargetChooser * fromTc = tcf.createTargetChooser(splitProtection[1], card);
3✔
5172
        if (!fromTc)
3✔
5173
            return NULL;
×
5174
        fromTc->setAllZones();
3✔
5175
        if (!activated)
3✔
5176
        {
5177
            if (((card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY)) && !forceForever && !untilYourNextEndTurn && !untilYourNextTurn) || forceUEOT)
3✔
5178
            {
5179
                MTGAbility * aPF = NEW AProtectionFrom(observer, id, card, target, fromTc, splitProtection[1]);
×
5180
                return NEW GenericInstantAbility(observer, 1, card, (Damageable *) target, aPF);
×
5181
            }
5182
            return NEW AProtectionFrom(observer, id, card, target, fromTc, splitProtection[1]);
3✔
5183
        }
5184
        return NULL; //TODO
×
5185
    }
5186

5187
    //targetter can not target...
5188
    vector<string> splitCantTarget = parseBetween(s, "cantbetargetof(", ")");
1,378✔
5189
    if (splitCantTarget.size())
689✔
5190
    {
5191
        TargetChooserFactory tcf(observer);
1✔
5192
        TargetChooser * fromTc = tcf.createTargetChooser(splitCantTarget[1], card);
1✔
5193
        if (!fromTc)
1✔
5194
            return NULL;
×
5195
        fromTc->setAllZones();
1✔
5196
        if (!activated)
1✔
5197
        {
5198
            if (((card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY)) && !forceForever && !untilYourNextEndTurn && !untilYourNextTurn) || forceUEOT)
1✔
5199
            {
5200
                return NULL; //TODO
×
5201
            }
5202
            return NEW ACantBeTargetFrom(observer, id, card, target, fromTc);
1✔
5203
        }
5204
        return NULL; //TODO
×
5205
    }
5206
    
5207
    //Can't be blocked by...need cantdefendagainst(
5208
    vector<string> splitCantBlock = parseBetween(s, "cantbeblockedby(", ")");
1,376✔
5209
    if (splitCantBlock.size())
688✔
5210
    {
5211
        TargetChooserFactory tcf(observer);
5✔
5212
        TargetChooser * fromTc = tcf.createTargetChooser(splitCantBlock[1], card);
5✔
5213
        if (!fromTc)
5✔
5214
            return NULL;
×
5215
        //default target zone to opponentbattlefield here?
5216
        if (!activated)
5✔
5217
        {
5218
            if (((card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY)) && !forceForever && !untilYourNextEndTurn && !untilYourNextTurn) || forceUEOT)
5✔
5219
            {
5220
                return NULL; //TODO
×
5221
            }
5222
            return NEW ACantBeBlockedBy(observer, id, card, target, fromTc);
5✔
5223
        }
5224
        return NULL; //TODO
×
5225
    }
5226

5227
    //cant be the blocker of targetchooser.
5228
    vector<string> splitCantBeBlock = parseBetween(s, "cantbeblockerof(", ")");
1,366✔
5229
    if (splitCantBeBlock.size())
683✔
5230
    {
5231
        TargetChooserFactory tcf(observer);
1✔
5232
        TargetChooser * fromTc = NULL;
1✔
5233
        if (splitCantBeBlock[1].find("this") == string::npos)
1✔
5234
        {
5235
            fromTc = tcf.createTargetChooser(splitCantBeBlock[1], card);
1✔
5236
        }
5237

5238
            if(fromTc)
1✔
5239
                return NEW ACantBeBlockerOf(observer, id, card, target, fromTc, false);//of a targetchooser
1✔
5240
            else
5241
                return NEW ACantBeBlockerOf(observer, id, card, target, fromTc, true);//blocker of the card source.
×
5242

5243
        return NULL;
5244
    }
5245
    
5246
    //Change Power/Toughness
5247
    WParsedPT * wppt = NEW WParsedPT(s, spell, card);
682✔
5248
    bool nonstatic = false;
682✔
5249
    if (wppt->ok)
682✔
5250
    {
5251
        if(s.find("nonstatic") != string::npos)
293✔
5252
            nonstatic = true;
13✔
5253
        if (!activated)
293✔
5254
        {
5255
            if (((card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY)) && !forceForever && !untilYourNextEndTurn && !untilYourNextTurn) || forceUEOT)
231✔
5256
            {
5257
                return NEW PTInstant(observer, id, card, target, wppt,s,nonstatic);
51✔
5258
            }
5259
            else if(s.find("cdaactive") != string::npos)
180✔
5260
            {
5261
                MTGAbility * a = NEW APowerToughnessModifier(observer, id, card, target, wppt,s,true);
42✔
5262
                a->forcedAlive = 1;
42✔
5263
                //a->forceDestroy = -1;
5264
                return a;
42✔
5265
                //return NEW APowerToughnessModifier(observer, id, card, target, wppt,s,true);
5266
            }
5267
            else
5268
                return NEW APowerToughnessModifier(observer, id, card, target, wppt,s,nonstatic);
138✔
5269
        }
5270
        return NEW PTInstant(observer, id, card, target, wppt,s,nonstatic);
62✔
5271
    }
5272
    else
5273
    {
5274
        delete wppt;
389✔
5275
    }   
5276

5277
    //affinity based on targetchooser
5278
    vector<string> splitNewAffinity = parseBetween(s, "affinity(", ")");
778✔
5279
    if (splitNewAffinity.size())
389✔
5280
    {
5281
        string tcString = splitNewAffinity[1];
×
5282
        string manaString = "";
×
5283
        vector<string> splitNewAffinityMana = parseBetween(splitNewAffinity[2], "reduce(", ")");
×
5284
        if(splitNewAffinityMana.size())
×
5285
            manaString = splitNewAffinityMana[1];
×
5286
        if(!manaString.size())
×
5287
            return NULL;
×
5288
        return NEW ANewAffinity(observer, id, card, tcString, manaString);
×
5289
    }
5290

5291
    //proliferate, rule changes in War of the Spark set
5292
    found = s.find("proliferate");
389✔
5293
    if (found != string::npos)
389✔
5294
    {
5295
        MTGAbility * a = NEW AAProliferate(observer, id, card, target);
3✔
5296
        a->oneShot = 1;
3✔
5297
        a->canBeInterrupted = false;
3✔
5298
        ((AAProliferate*)a)->allcounters = true;
3✔
5299
        return a;
3✔
5300
    }
5301
    //proliferate all counters
5302
    found = s.find("propagate");
386✔
5303
    if (found != string::npos)
386✔
5304
    {
5305
        MTGAbility * a = NEW AAProliferate(observer, id, card, target);
×
5306
        a->oneShot = 1;
×
5307
        a->canBeInterrupted = false;
×
5308
        ((AAProliferate*)a)->allcounters = true;
×
5309
        return a;
×
5310
    }
5311

5312
    //frozen, next untap this does not untap.
5313
    found = s.find("frozen");
386✔
5314
    if (found != string::npos)
386✔
5315
    {
5316
        MTGAbility * a = NEW AAFrozen(observer, id, card, target,false);
×
5317
        a->oneShot = 1;
×
5318
        return a;
×
5319
    }
5320

5321
    //frozen, next untap this does not untap.
5322
    found = s.find("freeze");
386✔
5323
    if (found != string::npos)
386✔
5324
    {
5325
        MTGAbility * a = NEW AAFrozen(observer, id, card, target,true);
×
5326
        a->oneShot = 1;
×
5327
        return a;
×
5328
    }
5329

5330
    //get a new target - retarget and newtarget makes the card refreshed - from exile to play (or from play to play)...
5331
    if ((s.find("retarget") != string::npos) || s.find("newtarget") != string::npos)
386✔
5332
    {
5333
        MTGAbility * a = NEW AANewTarget(observer, id, card, target, NULL, (s.find("retarget") != string::npos), false, false, 0, (s.find("fromplay") != string::npos));
4✔
5334
        a->oneShot = 1;
4✔
5335
        return a;
4✔
5336
    }
5337

5338
    //get a new target for puresteel paladin...etc for equipments inplay only.. newhook & rehook supports stone hewer basic... the card is reequipped
5339
    if ((s.find("rehook") != string::npos) || s.find("newhook") != string::npos)
382✔
5340
    {
5341
        MTGAbility * a = NEW AANewTarget(observer, id, card,target, NULL, false, true, (s.find("newhook") != string::npos));
7✔
5342
        a->oneShot = 1;
7✔
5343
        return a;
7✔
5344
    }
5345

5346
    //get a new target for mutations
5347
    if ((s.find("mutateover") != string::npos) || s.find("mutateunder") != string::npos)
375✔
5348
    {
5349
        MTGAbility * a = NEW AANewTarget(observer, id, card, target, NULL, false, false, false, (s.find("mutateover") != string::npos)?1:2);
×
5350
        a->oneShot = 1;
×
5351
        return a;
×
5352
    }
5353

5354
    //morph
5355
    found = s.find("morph");
375✔
5356
    if (found != string::npos)
375✔
5357
    {
5358
        MTGAbility * a = NEW AAMorph(observer, id, card, target);
4✔
5359
        a->oneShot = 1;
4✔
5360
        return a;
4✔
5361
    }
5362
    //morph
5363
    found = s.find("manafaceup");
371✔
5364
    if (found != string::npos)
371✔
5365
    {
5366
        MTGAbility * a = NEW AAMorph(observer, id, card, target);
×
5367
        a->oneShot = 1;
×
5368
        ((AAMorph*)a)->face = true;
×
5369
        return a;
×
5370
    }
5371

5372
    //identify what a leveler creature will max out at.
5373
    vector<string> splitMaxlevel = parseBetween(s, "maxlevel:", " ", false);
742✔
5374
    if (splitMaxlevel.size())
371✔
5375
    {
5376
        int level = 0;
2✔
5377
        WParsedInt* parser = NEW WParsedInt(splitMaxlevel[1], card);
2✔
5378
        if(parser){
2✔
5379
            level = parser->intValue;
2✔
5380
            SAFE_DELETE(parser);
2✔
5381
        }
5382
        MTGAbility * a = NEW AAWhatsMax(observer, id, card, card, NULL, level);
2✔
5383
        a->oneShot = 1;
2✔
5384
        return a;
2✔
5385
    }
5386

5387
    vector<string> splitCountObject = parseBetween(s, "count(", ")", false);
738✔
5388
    if (splitCountObject.size())
369✔
5389
    {
5390
        MTGAbility * a = NEW AACountObject(observer, id, card, card, NULL, splitCountObject[1]);
×
5391
        a->oneShot = 1;
×
5392
        return a;
×
5393
    }
5394

5395
    vector<string> splitCountObjectB = parseBetween(s, "countb(", ")", false);
738✔
5396
    if (splitCountObjectB.size())
369✔
5397
    {
5398
        MTGAbility * a = NEW AACountObjectB(observer, id, card, card, NULL, splitCountObjectB[1]);
×
5399
        a->oneShot = 1;
×
5400
        return a;
×
5401
    }
5402

5403
    //switch targest power with toughness
5404
    found = s.find("swap");
369✔
5405
    if (found != string::npos)
369✔
5406
    {
5407
        MTGAbility * a = NEW ASwapPTUEOT(observer, id, card, target);
1✔
5408
        a->oneShot = 1;
1✔
5409
        return a;
1✔
5410
    }
5411
    //exchange life with target; if creature then toughness is life.
5412
    found = s.find("exchangelife");
368✔
5413
    if (found != string::npos)
368✔
5414
    {
5415
        Targetable * t = spell ? spell->getNextTarget() : NULL;
×
5416
        MTGAbility * a = NEW AAExchangeLife(observer, id, card, t, NULL, who);
×
5417
        a->oneShot = 1;
×
5418
        return a;
×
5419
    }
5420

5421
    //Regeneration
5422
    found = s.find("regenerate");
368✔
5423
    if (found != string::npos)
368✔
5424
    {
5425
        MTGAbility * a = NEW AStandardRegenerate(observer, id, card, target);
23✔
5426
        a->oneShot = 1;
23✔
5427
        return a;
23✔
5428
    }
5429
    
5430
    //Gain/loose simple Ability
5431
    for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++)
29,351✔
5432
    {
5433
        found = s.find(Constants::MTGBasicAbilities[j]);
29,254✔
5434
        if (found == 0 || found == 1)
29,254✔
5435
        {
5436
            int modifier = 0;
248✔
5437
            if(s.find("absorb") || s.find("flanking"))
248✔
5438
                modifier += 1;
248✔
5439
            else
5440
                modifier = 1;
×
5441

5442
            if (found > 0 && s[found - 1] == '-')
248✔
5443
                modifier = 0;
5✔
5444
            else if(found > 0  && s[found - 1] == '-' && (s.find("absorb") || s.find("flanking")))
243✔
5445
                modifier -= 1;
×
5446

5447
            if (!activated)
248✔
5448
            {
5449
                if (((card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY)) && !forceForever && !untilYourNextEndTurn && !untilYourNextTurn) || forceUEOT)
223✔
5450
                {
5451
                    return NEW AInstantBasicAbilityModifierUntilEOT(observer, id, card, target, j, modifier);
26✔
5452
                }
5453
                return NEW ABasicAbilityModifier(observer, id, card, target, j, modifier);
197✔
5454
            }
5455
            return NEW ABasicAbilityAuraModifierUntilEOT(observer, id, card, target, NULL, j, modifier);
25✔
5456
        }
5457
    }
5458

5459
    //Untapper (Ley Druid...)
5460
    found = s.find("untap");
97✔
5461
    if (found != string::npos)
97✔
5462
    {
5463
        MTGAbility * a = NEW AAUntapper(observer, id, card, target);
33✔
5464
        a->oneShot = 1;
33✔
5465
        return a;
33✔
5466
    }
5467

5468
    //Tapper (icy manipulator)
5469
    found = s.find("tap");
64✔
5470
    if (found != string::npos)
64✔
5471
    {
5472
        MTGAbility * a = NEW AATapper(observer, id, card, target, NULL, bool(s.find("tap(noevent)") != string::npos));
31✔
5473
        a->oneShot = 1;
31✔
5474
        return a;
31✔
5475
    }
5476

5477
    //adds the rule to destroy children if parent dies
5478
    found = s.find("connectrule");
33✔
5479
    if(found != string::npos)
33✔
5480
    {
5481
        observer->addObserver(NEW ParentChildRule(observer, -1));
×
5482
        observer->connectRule = true;
×
5483
        return NULL;
×
5484
    }
5485
    //standard block
5486
        found = s.find("block");
33✔
5487
    if (found != string::npos)
33✔
5488
    {
5489
        MTGAbility * a = NEW AABlock(observer, id, card, target);
×
5490
        a->oneShot = 1;
×
5491
        return a;
×
5492
    }
5493

5494
    //create an association between cards.
5495
    found = s.find("connect");
33✔
5496
    if (found != string::npos)
33✔
5497
    {
5498
        MTGAbility * a = NEW AAConnect(observer, id, card, target);
×
5499
        a->oneShot = 1;
×
5500
        return a;
×
5501
    }
5502

5503
    //steal target until source leaves battlefield
5504
    found = s.find("steal");
33✔
5505
    if (found != string::npos)
33✔
5506
    {
5507
        MTGAbility * a = NEW ASeizeWrapper(observer, id, card, target);
×
5508
        a->oneShot = 1;
×
5509
        return a;
×
5510
    }
5511

5512
    DebugTrace(" no matching ability found. " << s);
33✔
5513
    return NULL;
33✔
5514
}
5515

5516
MTGAbility * AbilityFactory::parseUpkeepAbility(string s,MTGCardInstance * card,Spell * spell,int restrictions,int id)
14✔
5517
{
5518
    bool Cumulative = false;
14✔
5519
    size_t cumulative = s.find("cumulativeupcost");
14✔
5520
    if(cumulative != string::npos)
14✔
5521
        Cumulative = true;
×
5522
    size_t start = s.find("[");
14✔
5523
    size_t end = s.find("]", start);
14✔
5524
    string s1 = s.substr(start + 1, end - start - 1);
28✔
5525
    size_t seperator = s1.find(";");
14✔
5526
    int phase = MTG_PHASE_UPKEEP;
14✔
5527
    int once = 0;
14✔
5528
    if (seperator != string::npos)
14✔
5529
    {
5530
        for (int i = 0; i < NB_MTG_PHASES; i++)
3✔
5531
        {
5532
            if (s1.find("next") != string::npos)
3✔
5533
                once = 1;
3✔
5534

5535
            string phaseStr = Constants::MTGPhaseCodeNames[i];
5✔
5536
            if (s1.find(phaseStr) != string::npos)
3✔
5537
            {
5538
                phase = PhaseRing::phaseStrToInt(phaseStr);
1✔
5539
                break;
1✔
5540
            }
5541

5542
        }
5543
        s1 = s1.substr(0, seperator);
1✔
5544
    }
5545
    ManaCost * cost = ManaCost::parseManaCost(s1);
14✔
5546

5547
    if (!cost)
14✔
5548
    {
5549
        DebugTrace("MTGABILITY: Parsing Error: " << s);
×
5550
        return NULL;
×
5551
    }
5552

5553
    string sAbility = s.substr(end + 1);
28✔
5554
    MTGAbility * a = parseMagicLine(sAbility, id, spell, card);
14✔
5555

5556
    if (!a)
14✔
5557
    {
5558
        DebugTrace("MTGABILITY: Parsing Error: " << s);
×
5559
        delete (cost);
×
5560
        return NULL;
×
5561
    }
5562
    return  NEW AUpkeep(observer, id, card, a, cost, restrictions, phase, once,Cumulative);;
14✔
5563
}
5564

5565
MTGAbility * AbilityFactory::parsePhaseActionAbility(string s,MTGCardInstance * card,Spell * spell,MTGCardInstance * target, int restrictions,int id)
20✔
5566
{
5567
        vector<string> splitActions = parseBetween(s, "[", "]");
40✔
5568
        if (!splitActions.size())
20✔
5569
        {
5570
            DebugTrace("MTGABILITY:Parsing Error " << s);
×
5571
            return NULL;
×
5572
        }
5573
        string s1 = splitActions[1];
40✔
5574
        int phase = MTG_PHASE_UPKEEP;
20✔
5575
        for (int i = 0; i < NB_MTG_PHASES; i++)
166✔
5576
        {
5577
            string phaseStr = Constants::MTGPhaseCodeNames[i];
312✔
5578
            if (s1.find(phaseStr) != string::npos)
166✔
5579
            {
5580
                phase = PhaseRing::phaseStrToInt(phaseStr);
20✔
5581
                break;
20✔
5582
            }
5583
        }
5584

5585
        bool opponentturn = (s1.find("my") == string::npos);
20✔
5586
        bool myturn = (s1.find("opponent") == string::npos);
20✔
5587
        bool sourceinPlay = (s1.find("sourceinplay") != string::npos);
20✔
5588
        bool checkexile = (s1.find("checkex") != string::npos);
20✔
5589
        bool next = (s1.find("next") == string::npos); //Why is this one the opposite of the two others? That's completely inconsistent
20✔
5590
        bool once = (s1.find("once") != string::npos);
20✔
5591

5592
        MTGCardInstance * _target = NULL;
20✔
5593
        if (spell)
20✔
5594
            _target = spell->getNextCardTarget();
10✔
5595
        if(!_target)
20✔
5596
            _target = target;
20✔
5597
          return NEW APhaseActionGeneric(observer, id, card,_target, trim(splitActions[2]), restrictions, phase,sourceinPlay,next,myturn,opponentturn,once,checkexile);
20✔
5598
}
5599

5600
MTGAbility * AbilityFactory::parseChooseActionAbility(string s,MTGCardInstance * card,Spell *,MTGCardInstance * target, int, int id)
1✔
5601
{
5602
    vector<string> splitChooseAColor2 = parseBetween(s, "activatechooseacolor ", " activatechooseend");
2✔
5603
    if (splitChooseAColor2.size())
1✔
5604
    {
5605
        string a1 = splitChooseAColor2[1];
×
5606
        MTGAbility * a = NEW GenericChooseTypeColorName(observer, id, card, target,a1,true);
×
5607
        a->oneShot = 1;
×
5608
        a->canBeInterrupted = false;
×
5609
        return a;
×
5610
    }
5611
    //choose a type
5612
    vector<string> splitChooseAType2 = parseBetween(s, "activatechooseatype ", " activatechooseend");
2✔
5613
    if (splitChooseAType2.size())
1✔
5614
    {
5615
        string a1 = splitChooseAType2[1];
×
5616
        MTGAbility * a = NEW GenericChooseTypeColorName(observer, id, card, target,a1,false,false,false,s.find("nonwall")!=string::npos);
×
5617
        a->oneShot = 1;
×
5618
        a->canBeInterrupted = false;
×
5619
        return a;
×
5620
    }
5621
    //choose a color
5622
    vector<string> splitChooseAColor = parseBetween(s, "chooseacolor ", " chooseend");
2✔
5623
    if (splitChooseAColor.size())
1✔
5624
    {
5625
        string a1 = splitChooseAColor[1];
2✔
5626
        MTGAbility * a = NEW GenericChooseTypeColorName(observer, id, card, target,a1,true);
1✔
5627
        a->oneShot = 1;
1✔
5628
        a->canBeInterrupted = false;
1✔
5629
        return a;
1✔
5630
    }
5631
    //choose a type
5632
    vector<string> splitChooseAType = parseBetween(s, "chooseatype ", " chooseend");
×
5633
    if (splitChooseAType.size())
×
5634
    {
5635
        string a1 = splitChooseAType[1];
×
5636
        MTGAbility * a = NEW GenericChooseTypeColorName(observer, id, card, target,a1,false,false,false,s.find("nonwall")!=string::npos);
×
5637
        a->oneShot = 1;
×
5638
        a->canBeInterrupted = false;
×
5639
        return a;
×
5640
    }
5641
    //choose a name
5642
    vector<string> splitChooseAName = parseBetween(s, "chooseaname ", " chooseend");
×
5643
    vector<string> splitChooseAOppName = parseBetween(s, "chooseanameopp ", " chooseend");
×
5644
    if (splitChooseAName.size() || splitChooseAOppName.size())
×
5645
    {
5646
        bool oppName = (splitChooseAOppName.size() > 0);
×
5647
        string a1 = oppName?splitChooseAOppName[1]:splitChooseAName[1];
×
5648
        MTGAbility * a = NEW GenericChooseTypeColorName(observer, id, card, target,a1,false,!oppName,oppName,false,s.find("nonbasicland")!=string::npos,s.find("nonland")!=string::npos);
×
5649
        a->oneShot = 1;
×
5650
        a->canBeInterrupted = false;
×
5651
        return a;
×
5652
    }
5653
    return NULL;
×
5654
}
5655

5656
//Tells the AI if the ability should target itself or an ennemy
5657
int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, TargetChooser * tc,Targetable * target)
2✔
5658
{
5659
    if (!a)
2✔
5660
        return BAKA_EFFECT_DONTKNOW;
×
5661

5662
    if (GenericTargetAbility * abi = dynamic_cast<GenericTargetAbility*>(a))
2✔
5663
    {
5664
        if (mode == MODE_PUTINTOPLAY)
×
5665
            return BAKA_EFFECT_GOOD;
×
5666
        return abilityEfficiency(abi->ability, p, mode, abi->getActionTc());
×
5667
    }
5668
    if (GenericActivatedAbility * abi = dynamic_cast<GenericActivatedAbility*>(a))
2✔
5669
    {
5670
        if (mode == MODE_PUTINTOPLAY)
×
5671
            return BAKA_EFFECT_GOOD;
×
5672
        return abilityEfficiency(abi->ability, p, mode, tc);
×
5673
    }
5674
    if (MultiAbility * abi = dynamic_cast<MultiAbility*>(a))
2✔
5675
        return abilityEfficiency(abi->abilities[0], p, mode, tc);
×
5676
    if (MayAbility * abi = dynamic_cast<MayAbility*>(a))
2✔
5677
        return abilityEfficiency(abi->ability, p, mode, tc);
×
5678
    if (ALord * abi = dynamic_cast<ALord *>(a))
2✔
5679
    {
5680
        int myCards = countCards(abi->getActionTc(), p);
×
5681
        int theirCards = countCards(abi->getActionTc(), p->opponent());
×
5682
        int efficiency = abilityEfficiency(abi->ability, p, mode, tc);
×
5683
        if (efficiency == BAKA_EFFECT_GOOD)
×
5684
        {
5685
            myCards < theirCards? efficiency = BAKA_EFFECT_BAD : efficiency = BAKA_EFFECT_GOOD;
×
5686
        }
5687
        else if (efficiency == BAKA_EFFECT_BAD)
×
5688
        {
5689
            myCards >= theirCards? efficiency = BAKA_EFFECT_BAD : efficiency = BAKA_EFFECT_GOOD;
×
5690
        }
5691
        return efficiency;
×
5692
        /*this method below leads to too many undesired effects, basically it doesn't work how the original coder thought it would.
5693
        leaving it for reference to avoid it reaccuring during a refactor.
5694
        if ( ((myCards <= theirCards) && efficiency == BAKA_EFFECT_GOOD) || ((myCards >= theirCards) && efficiency == BAKA_EFFECT_BAD)   )
5695
        return efficiency;
5696
        return -efficiency; */
5697
    }
5698
    if (AAsLongAs * abi = dynamic_cast<AAsLongAs *>(a))
2✔
5699
        return abilityEfficiency(abi->ability, p, mode, tc);
×
5700
    if (AForeach * abi = dynamic_cast<AForeach *>(a))
2✔
5701
        return abilityEfficiency(abi->ability, p, mode, tc);
×
5702
    if (ATeach * abi = dynamic_cast<ATeach *>(a))
2✔
5703
        return abilityEfficiency(abi->ability, p, mode, tc);
×
5704
    if (ATargetedAbilityCreator * atac = dynamic_cast<ATargetedAbilityCreator *>(a))
2✔
5705
    {
5706
        Player * targetedPlyr;
5707
        switch(atac->who)
×
5708
        {
5709
        case TargetChooser::CONTROLLER:
5710
            targetedPlyr = atac->source->controller();
×
5711
            break;
×
5712
        case TargetChooser::OPPONENT:
5713
            targetedPlyr = atac->source->controller()->opponent();
×
5714
            break;
×
5715
        case TargetChooser::TARGET_CONTROLLER:
5716
            if(dynamic_cast<MTGCardInstance*>(target))
×
5717
            {
5718
                targetedPlyr = ((MTGCardInstance*)atac->target)->controller();
×
5719
                break;
×
5720
            }
5721
        case TargetChooser::TARGETED_PLAYER:
5722
            {
5723
                targetedPlyr = atac->source->playerTarget?atac->source->playerTarget:p;
×
5724
                break;
×
5725
            }
5726
        default:
5727
            targetedPlyr = atac->source->controller()->opponent();
×
5728
            break;
×
5729
        }
5730
        int result = 0;
×
5731
        if(targetedPlyr)
×
5732
        {
5733
            MTGCardInstance  * testDummy = NEW MTGCardInstance();
×
5734
            testDummy->setObserver(targetedPlyr->getObserver());
×
5735
            testDummy->owner = targetedPlyr;
×
5736
            testDummy->storedSourceCard = atac->source;
×
5737
            testDummy->lastController = targetedPlyr;
×
5738
            vector<string>magictextlines = split(atac->sabilities,'_');
×
5739
            if(magictextlines.size())
×
5740
            {
5741
                for(unsigned int i = 0; i < magictextlines.size(); i++)
×
5742
                {
5743
                    MTGAbility * ata = parseMagicLine(magictextlines[i],-1,NULL,testDummy);
×
5744
                    if(ata)
×
5745
                    {
5746
                        result += abilityEfficiency(getCoreAbility(ata), targetedPlyr,mode);
×
5747
                        SAFE_DELETE(ata);
×
5748
                    }
5749
                }
5750
            }
5751
            SAFE_DELETE(testDummy);
×
5752
        }
5753
        return result;
×
5754
    }
5755
    if (dynamic_cast<AAFizzler *> (a))
2✔
5756
        return BAKA_EFFECT_BAD;
×
5757
    if (dynamic_cast<AADamagePrevent *> (a))
2✔
5758
        return BAKA_EFFECT_GOOD;
×
5759
    if (dynamic_cast<AACloner *> (a))
2✔
5760
        return BAKA_EFFECT_GOOD;
×
5761
    if (dynamic_cast<ASwapPTUEOT *> (a))
2✔
5762
        return BAKA_EFFECT_BAD;
×
5763
    if (dynamic_cast<AAUntapper *> (a))
2✔
5764
        return BAKA_EFFECT_GOOD;
×
5765
    if (dynamic_cast<AATapper *> (a))
2✔
5766
        return BAKA_EFFECT_BAD;
×
5767
    if (dynamic_cast<AManaProducer *> (a))
2✔
5768
        return BAKA_EFFECT_GOOD;
×
5769
     if (dynamic_cast<AARemoveAllCounter *> (a))
2✔
5770
         return BAKA_EFFECT_BAD;
×
5771
     if (dynamic_cast<AAProliferate *> (a))
2✔
5772
         return BAKA_EFFECT_GOOD;
1✔
5773

5774
    // Equipment that gets immediately attached. Todo: check the abilities associated with Equip, to make sure they're good (for now it seems to be the majority of the cases)?
5775
    if (dynamic_cast<AEquip *> (a))
1✔
5776
        return BAKA_EFFECT_GOOD;
×
5777

5778
    // For now, ACounterTracker is only used for Creatures that "belong" to one of our domains, need to target one of our own lands, so we return a "positive" value
5779
    if (dynamic_cast<ACounterTracker *>(a))
1✔
5780
        return BAKA_EFFECT_GOOD;
×
5781

5782
    if (AACounter * ac = dynamic_cast<AACounter *>(a))
1✔
5783
    {
5784
        bool negative_effect = ac->power < 0 || ac->toughness < 0;
×
5785
        if ((ac->nb > 0 && negative_effect) || (ac->nb < 0 && !negative_effect))
×
5786
            return BAKA_EFFECT_BAD;
×
5787
        return BAKA_EFFECT_GOOD;
×
5788
    }
5789

5790
    if (dynamic_cast<ATokenCreator *> (a))
1✔
5791
        return BAKA_EFFECT_GOOD;
×
5792

5793
    if (AAMover * aam = dynamic_cast<AAMover *>(a))
1✔
5794
    {
5795
        MTGGameZone * z = aam->destinationZone(target);
×
5796
        if ((tc && tc->targetsZone(p->game->library)) || (tc && tc->targetsZone(p->game->graveyard)) || (tc && tc->targetsZone(p->game->hand)))
×
5797
        {
5798
            if (z == p->game->hand || z == p->game->inPlay)
×
5799
                return BAKA_EFFECT_GOOD;
×
5800
        }
5801
         return BAKA_EFFECT_BAD; //TODO
×
5802
    }
5803

5804
    if (dynamic_cast<AACopier *> (a))
1✔
5805
        return BAKA_EFFECT_GOOD;
×
5806
    if (dynamic_cast<AABuryCard *> (a))
1✔
5807
        return BAKA_EFFECT_BAD;
×
5808
    if (dynamic_cast<AADestroyCard *> (a))
1✔
5809
        return BAKA_EFFECT_BAD;
×
5810
    if (dynamic_cast<AStandardRegenerate *> (a))
1✔
5811
        return BAKA_EFFECT_GOOD;
×
5812
    if (AALifer * abi = dynamic_cast<AALifer *>(a))
1✔
5813
        return abi->getLife() > 0 ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD;
×
5814
    if (AAAlterPoison * abi = dynamic_cast<AAAlterPoison *>(a))
1✔
5815
        return abi->poison > 0 ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD;
×
5816
    if (dynamic_cast<AADepleter *> (a))
1✔
5817
        return BAKA_EFFECT_BAD;
×
5818
    if (dynamic_cast<AADrawer *> (a))
1✔
5819
        return BAKA_EFFECT_GOOD;
×
5820
    if (dynamic_cast<AARandomDiscarder *> (a))
1✔
5821
        return BAKA_EFFECT_BAD;
×
5822
    if (dynamic_cast<ARampageAbility *> (a))
1✔
5823
        return BAKA_EFFECT_GOOD;
×
5824
    if (dynamic_cast<ABushidoAbility *> (a))
1✔
5825
        return BAKA_EFFECT_GOOD;
×
5826
    if (dynamic_cast<AACascade *> (a))
1✔
5827
        return BAKA_EFFECT_GOOD;
×
5828
    if (dynamic_cast<AACastCard *> (a))
1✔
5829
        return BAKA_EFFECT_GOOD;
×
5830
    if (dynamic_cast<AAFlip *> (a))
1✔
5831
        return BAKA_EFFECT_GOOD;
×
5832
    if (dynamic_cast<AAImprint *> (a))
1✔
5833
        return BAKA_EFFECT_GOOD;
×
5834
    if (dynamic_cast<AAHaunt *> (a))
1✔
5835
        return BAKA_EFFECT_GOOD;
×
5836
    if (dynamic_cast<AATrain *> (a))
1✔
5837
        return BAKA_EFFECT_GOOD;
×
5838
    if (dynamic_cast<ABestow *> (a))
1✔
5839
        return BAKA_EFFECT_GOOD;
×
5840
    if (dynamic_cast<AExert *> (a))
1✔
5841
        return BAKA_EFFECT_GOOD;
×
5842
    if (dynamic_cast<ALoseAbilities *> (a))
1✔
5843
        return BAKA_EFFECT_BAD;
×
5844
    if (dynamic_cast<AModularAbility *> (a))
1✔
5845
        return BAKA_EFFECT_GOOD;
×
5846
    if (dynamic_cast<APaired *> (a))
1✔
5847
        return BAKA_EFFECT_GOOD;
×
5848
    if (dynamic_cast<AProduceMana *> (a))
1✔
5849
        return BAKA_EFFECT_GOOD;
×
5850
    if (dynamic_cast<AACloner *> (a))
1✔
5851
        return BAKA_EFFECT_GOOD;
×
5852
    if (dynamic_cast<AAModTurn *> (a))
1✔
5853
        return BAKA_EFFECT_GOOD;
×
5854
    if (dynamic_cast<ATransformer *> (a))
1✔
5855
        return BAKA_EFFECT_GOOD;
×
5856
    if (dynamic_cast<AADamager *> (a))
1✔
5857
        return BAKA_EFFECT_BAD;
×
5858

5859
    if (PTInstant * abi = dynamic_cast<PTInstant *>(a))
1✔
5860
        return (abi->wppt->power.getValue() >= 0 && abi->wppt->toughness.getValue() >= 0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD;
×
5861
    if (APowerToughnessModifier * abi = dynamic_cast<APowerToughnessModifier *>(a))
1✔
5862
        return (abi->wppt->power.getValue() >= 0 && abi->wppt->toughness.getValue() >= 0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD;
×
5863
    if (PTInstant * abi = dynamic_cast<PTInstant *>(a))
1✔
5864
        return abilityEfficiency(abi->ability, p, mode, tc);
×
5865

5866
    if (dynamic_cast<ACantBeBlockedBy *> (a))
1✔
5867
        return BAKA_EFFECT_GOOD;
×
5868
    if (dynamic_cast<AProtectionFrom *> (a))
1✔
5869
        return BAKA_EFFECT_GOOD;
×
5870

5871
    map<int, bool> badAbilities;
2✔
5872
    badAbilities[(int)Constants::CANTATTACK] = true;
1✔
5873
    badAbilities[(int)Constants::CANTBLOCK] = true;
1✔
5874
    badAbilities[(int)Constants::CLOUD] = true;
1✔
5875
    badAbilities[(int)Constants::DEFENDER] = true;
1✔
5876
    badAbilities[(int)Constants::DOESNOTUNTAP] = true;
1✔
5877
    badAbilities[(int)Constants::MUSTATTACK] = true;
1✔
5878
    badAbilities[(int)Constants::CANTREGEN] = true;
1✔
5879
    badAbilities[(int)Constants::NOACTIVATED] = true;
1✔
5880
    badAbilities[(int)Constants::NOACTIVATEDTAP] = true;
1✔
5881
    badAbilities[(int)Constants::NOMANA] = true;
1✔
5882
    badAbilities[(int)Constants::ONLYMANA] = true;
1✔
5883
    badAbilities[(int)Constants::EXILEDEATH] = true;
1✔
5884
    badAbilities[(int)Constants::GAINEDEXILEDEATH] = true;
1✔
5885
    badAbilities[(int)Constants::HANDDEATH] = true;
1✔
5886
    badAbilities[(int)Constants::GAINEDHANDDEATH] = true;
1✔
5887
    badAbilities[(int)Constants::INPLAYDEATH] = true;
1✔
5888
    badAbilities[(int)Constants::COUNTERDEATH] = true;
1✔
5889
    badAbilities[(int)Constants::INPLAYTAPDEATH] = true;
1✔
5890
    badAbilities[(int)Constants::DOUBLEFACEDEATH] = true;
1✔
5891
    badAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = true;
1✔
5892
    badAbilities[(int)Constants::WEAK] = true;
1✔
5893
    badAbilities[(int)Constants::NOLIFEGAIN] = true;
1✔
5894
    badAbilities[(int)Constants::NOLIFEGAINOPPONENT] = true;
1✔
5895
    badAbilities[(int)Constants::MUSTBLOCK] = true;
1✔
5896
    badAbilities[(int)Constants::FLYERSONLY] = true;
1✔
5897
    badAbilities[(int)Constants::TREASON] = true;
1✔
5898
    badAbilities[(int)Constants::SHACKLER] = true;
1✔
5899

5900
    if (AInstantBasicAbilityModifierUntilEOT * abi = dynamic_cast<AInstantBasicAbilityModifierUntilEOT *>(a))
1✔
5901
    {
5902
        int result = badAbilities[abi->ability] ? BAKA_EFFECT_BAD : BAKA_EFFECT_GOOD;
×
5903
        return (abi->value > 0) ? result : -result;
×
5904
    }
5905
    if (ABasicAbilityModifier * abi = dynamic_cast<ABasicAbilityModifier *>(a))
1✔
5906
    {
5907
        int result = (badAbilities[abi->ability]) ? BAKA_EFFECT_BAD : BAKA_EFFECT_GOOD;
×
5908
        return (abi->modifier > 0) ? result : -result;
×
5909
    }
5910
    if (ABasicAbilityAuraModifierUntilEOT * abi = dynamic_cast<ABasicAbilityAuraModifierUntilEOT *>(a))
1✔
5911
        return abilityEfficiency(abi->ability, p, mode);
×
5912
    if (dynamic_cast<AManaProducer*> (a))
1✔
5913
        return BAKA_EFFECT_GOOD;
×
5914
    return BAKA_EFFECT_DONTKNOW;
1✔
5915
}
5916

5917
//Returns the "X" cost that was paid for a spell
5918
int AbilityFactory::computeX(Spell * spell, MTGCardInstance * card)
1✔
5919
{
5920
    if (spell)
1✔
5921
        return spell->computeX(card);
1✔
5922
    if(card) return card->X;
×
5923
    return 0;
×
5924
}
5925

5926
int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card, int id, MTGGameZone * dest)
7,608✔
5927
{
5928
    if (!card && spell)
7,608✔
5929
        card = spell->source;
3,698✔
5930
    if (!card)
7,608✔
5931
        return 0;
×
5932

5933
    string magicText;
15,216✔
5934
    if (dest)
7,608✔
5935
    {
5936
        card->graveEffects = false;
3,872✔
5937
        card->exileEffects = false;
3,872✔
5938
        card->commandZoneEffects = false;
3,872✔
5939
        card->handEffects = false;
3,872✔
5940
        for (int i = 0; i < 2; ++i)
4,785✔
5941
        {
5942
            MTGPlayerCards * zones = observer->players[i]->game;
4,771✔
5943
            if (dest == zones->hand)
4,771✔
5944
            {
5945
                magicText = card->magicTexts["hand"];
874✔
5946
                card->handEffects = true;
874✔
5947
                break;
874✔
5948
            }
5949
            if (dest == zones->graveyard)
3,897✔
5950
            {
5951
                magicText = card->magicTexts["graveyard"];
878✔
5952
                card->graveEffects = true;
878✔
5953
                break;
878✔
5954
            }
5955
            if (dest == zones->stack)
3,019✔
5956
            {
5957
                magicText = card->magicTexts["stack"];
2,053✔
5958
                break;
2,053✔
5959
            }
5960
            if (dest == zones->exile)
966✔
5961
            {
5962
                magicText = card->magicTexts["exile"];
38✔
5963
                card->exileEffects = true;
38✔
5964
                break;
38✔
5965
            }
5966
            if (dest == zones->commandzone)
928✔
5967
            {
5968
                magicText = card->magicTexts["commandzone"];
×
5969
                card->commandZoneEffects = true;
×
5970
                break;
×
5971
            }
5972
            if (dest == zones->library)
928✔
5973
            {
5974
                magicText = card->magicTexts["library"];
15✔
5975
                break;
15✔
5976
            }
5977
            //Other zones needed ?
5978
        }
5979
    }
5980
    else
5981
    {
5982
        if(card->previous && card->previous->morphed && !card->turningOver)
3,736✔
5983
        {
5984
            magicText = card->magicTexts["facedown"];
4✔
5985
            card->power = 2;
4✔
5986
            card->life = 2;
4✔
5987
            card->toughness = 2;
4✔
5988
            card->setColor(0,1);
4✔
5989
            card->name = "Morph";
4✔
5990
            card->types.clear();
4✔
5991
            string cre = "Creature";
8✔
5992
            card->setType(cre.c_str());
4✔
5993
            if(card->has(Constants::ISCOMMANDER)){
4✔
5994
                card->basicAbilities[Constants::WASCOMMANDER] = 1;
×
5995
                card->basicAbilities[Constants::ISCOMMANDER] = 0;
×
5996
            }
5997
            for(size_t i = 0; i < card->basicAbilities.size(); i++) {
920✔
5998
                if(i != Constants::WASCOMMANDER && i != Constants::GAINEDEXILEDEATH && i != Constants::GAINEDHANDDEATH && i != Constants::GAINEDDOUBLEFACEDEATH && 
916✔
5999
                    i != Constants::DUNGEONCOMPLETED && i != Constants::PERPETUALDEATHTOUCH && i != Constants::PERPETUALLIFELINK)
896✔
6000
                    card->basicAbilities[i] = 0; // Try to keep the original special abilities on card morph.
888✔
6001
            }
6002
            card->getManaCost()->resetCosts();
8✔
6003
        }
6004
        else if(card && !card->morphed && card->turningOver)
3,732✔
6005
        {
6006
            card->power += card->origpower-2;
3✔
6007
            card->life += card->origtoughness-2;
3✔
6008
            card->toughness += card->origtoughness-2;
3✔
6009
            card->setColor(0,1);
3✔
6010
            card->name = card->model->data->name;
3✔
6011
            card->types = card->model->data->types;
3✔
6012
            card->colors = card->model->data->colors;
3✔
6013
            card->basicAbilities |= card->model->data->basicAbilities;
3✔
6014
            card->basicAbilities[Constants::ISCOMMANDER] = card->basicAbilities[Constants::WASCOMMANDER];
3✔
6015
            card->basicAbilities[Constants::WASCOMMANDER] = 0;
3✔
6016
            ManaCost * copyCost = card->model->data->getManaCost();
3✔
6017
            card->getManaCost()->copy(copyCost);
3✔
6018
            magicText = card->model->data->magicText;
3✔
6019
            string faceupC= card->magicTexts["faceup"];
6✔
6020
            magicText.append("\n");
3✔
6021
            magicText.append(faceupC);
6✔
6022
        }
6023
        else if(card && card->hasType(Subtypes::TYPE_EQUIPMENT) && card->target)
3,729✔
6024
        {
6025
            magicText = card->model->data->magicText;
27✔
6026
            string equipText = card->magicTexts["skill"];
54✔
6027
            magicText.append("\n");
27✔
6028
            magicText.append(equipText);
27✔
6029
        }
6030
        else
6031
        {
6032
            magicText = card->magicText;
3,702✔
6033
        }
6034
    }
6035
    if (card->alias && magicText.size() == 0 && !dest)
7,608✔
6036
    {
6037
        MTGCard * c = MTGCollection()->getCardById(card->alias);
28✔
6038
        if (!c)
28✔
6039
            return 0;
×
6040
        magicText = c->data->magicText;
28✔
6041
    }
6042
    string line;
15,216✔
6043
    int size = magicText.size();
7,608✔
6044
    if (size == 0)
7,608✔
6045
        return 0;
4,928✔
6046
    size_t found;
6047
    int result = id;
2,680✔
6048

6049
    magicText = AutoLineMacro::Process(magicText);
2,680✔
6050

6051

6052
    while (magicText.size())
9,238✔
6053
    {
6054
        found = magicText.find("\n");
3,279✔
6055
        if (found != string::npos)
3,279✔
6056
        {
6057
            line = magicText.substr(0, found);
2,093✔
6058
            magicText = magicText.substr(found + 1);
2,093✔
6059
        }
6060
        else
6061
        {
6062
            line = magicText;
1,186✔
6063
            magicText = "";
1,186✔
6064
        }
6065
        MTGAbility * a = parseMagicLine(line, result, spell, card, false, false, dest);
3,279✔
6066
        if (a)
3,279✔
6067
        {
6068
            v->push_back(a);
3,268✔
6069
            result++;
3,268✔
6070
        }
6071
        else
6072
        {
6073
            DebugTrace("ABILITYFACTORY ERROR: Parser returned NULL " + magicText);
11✔
6074
        }
6075
    }
6076
    return result;
2,680✔
6077
}
6078

6079
//Some basic functionalities that can be added automatically in the text file
6080
/*
6081
 * Several objects are computed from the text string, and have a direct influence on what action we should take
6082
 * (direct impact on the game such as draw a card immediately, or create a New GameObserver and add it to the Abilities,etc..)
6083
 * These objects are:
6084
 *   - trigger (if there is an "@" in the string, this is a triggered ability)
6085
 *   - target (if there ie a "target(" in the string, then this is a TargetAbility)
6086
 *   - doTap (a dirty way to know if tapping is included in the cost...
6087
 */
6088
int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc, MTGGameZone * dest)
7,571✔
6089
{try{
6090
    int dryMode = 0;
7,571✔
6091
    if (!spell && !dest)
7,571✔
6092
        dryMode = 1;
1✔
6093

6094
    vector<MTGAbility *> v;
15,142✔
6095
    int result = getAbilities(&v, spell, card, id, dest);
7,571✔
6096

6097
    for (size_t i = 0; i < v.size(); ++i)
10,740✔
6098
    {
6099
        MTGAbility * a = v[i];
3,170✔
6100
        if (!a)
3,170✔
6101
        {
6102
            DebugTrace("ABILITYFACTORY ERROR: Parser returned NULL");
×
6103
            continue;
×
6104
        }
6105

6106
        if (dryMode)
3,170✔
6107
        {
6108
            result = abilityEfficiency(a, card->controller(), mode, tc);
1✔
6109
            for (size_t i = 0; i < v.size(); ++i)
2✔
6110
                SAFE_DELETE(v[i]);
1✔
6111
            return result;
1✔
6112
        }
6113

6114
        if (!a->oneShot)
3,169✔
6115
        {
6116
            // Anything involving Mana Producing abilities cannot be interrupted
6117
            MTGAbility * core = getCoreAbility(a);
1,289✔
6118
            if (dynamic_cast<AManaProducer*> (core))
1,289✔
6119
                a->canBeInterrupted = false;
108✔
6120
        }
6121

6122
        bool moreThanOneTarget = spell && spell->tc && spell->tc->getNbTargets() > 1;
3,169✔
6123

6124
        if(moreThanOneTarget)
3,169✔
6125
            a->target = spell->getNextTarget();
11✔
6126

6127
        if(a->target && moreThanOneTarget) 
3,169✔
6128
        {
6129
            MayAbility * aMay = dynamic_cast<MayAbility*>(a);
11✔
6130
            while(a->target)
61✔
6131
            {
6132
                if(a->oneShot)
25✔
6133
                {
6134
                    a->resolve();
25✔
6135
                }
6136
                else
6137
                {
6138
                    if(!aMay || (aMay && a->target == spell->tc->getNextTarget(0)))
×
6139
                    {
6140
                        MTGAbility * mClone = a->clone();
×
6141
                        mClone->addToGame();
×
6142
                    }
6143
                }
6144
                a->target = spell->getNextTarget(a->target);
25✔
6145
            }
6146
            SAFE_DELETE(a);
11✔
6147
        }
6148
        else
6149
        {
6150
            if (a->oneShot)
3,158✔
6151
            {
6152
                a->resolve();
1,869✔
6153
                delete (a);
1,869✔
6154
            }
6155
            else
6156
            {
6157
                a->addToGame();
1,289✔
6158
                MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
1,289✔
6159
                if(!dontAdd)
1,289✔
6160
                {
6161
                if (a->source)
1,232✔
6162
                    a->source->cardsAbilities.push_back(a);
1,232✔
6163
                else if(spell && spell->source)
×
6164
                    spell->source->cardsAbilities.push_back(a);
×
6165
                }
6166
                //keep track of abilities being added to the game on each card it belongs to, this ignores p/t bonuses given
6167
                //from other cards, or ability bonuses, making it generally easier to strip a card of it's abilities.
6168
            }
6169
        }
6170
    }
6171

6172
    return result;
7,570✔
6173
    }
6174
    catch(exception) {
×
6175
     DebugTrace("MAGIC TEST ERROR: Parser returned NULL");
×
6176
    }
6177
    return 0;
×
6178
}
6179

6180
void AbilityFactory::addAbilities(int _id, Spell * spell)
3,700✔
6181
{
6182
    MTGCardInstance * card = spell->source;
3,700✔
6183

6184
    if (spell->getNbTargets() == 1)
3,700✔
6185
    {
6186
        card->target = spell->getNextCardTarget();
322✔
6187
        if (card->target && (!spell->tc->canTarget(card->target) || card->target->isPhased))
322✔
6188
        {
6189
            MTGPlayerCards * zones = card->controller()->game;
2✔
6190
            zones->putInZone(card, spell->from, card->owner->game->graveyard);
2✔
6191
            return; //fizzle
4✔
6192
        }
6193
        card->playerTarget = spell->getNextPlayerTarget();
320✔
6194
    } 
6195
    if(!card->playerTarget && card->previous && card->previous->playerTarget)
3,698✔
6196
        card->playerTarget = card->previous->playerTarget;//instants seem to forget as they travel from zone to zone.
×
6197
    _id = magicText(_id, spell);
3,698✔
6198

6199
    MTGPlayerCards * zones = card->controller()->game;
3,698✔
6200

6201
    int id = card->alias;
3,698✔
6202
    switch (id)
3,698✔
6203
    {
6204
    case 1092: //Aladdin's lamp
6205
    {
6206
        AAladdinsLamp * ability = NEW AAladdinsLamp(observer, _id, card);
×
6207
        observer->addObserver(ability);
×
6208
        break;
×
6209
    }
6210
    case 1095: //Armageddon clock
6211
    {
6212
        AArmageddonClock * ability = NEW AArmageddonClock(observer, _id, card);
×
6213
        observer->addObserver(ability);
×
6214
        break;
×
6215
    }
6216

6217
    case 1191: //Blue Elemental Blast
6218
    {
6219
        if (card->target)
1✔
6220
        {
6221
            card->target->controller()->game->putInGraveyard(card->target);
1✔
6222
        }
6223
        else
6224
        {
6225
            Spell * starget = spell->getNextSpellTarget();
×
6226
            observer->mLayers->stackLayer()->Fizzle(starget);
×
6227
        }
6228
        break;
1✔
6229
    }
6230
    case 1282: //Chaoslace
6231
    {
6232
        if (card->target)
×
6233
        {
6234
            card->target->setColor(Constants::MTG_COLOR_RED, 1);
×
6235
        }
6236
        else
6237
        {
6238
            Spell * starget = spell->getNextSpellTarget();
×
6239
            starget->source->setColor(Constants::MTG_COLOR_RED, 1);
×
6240
        }
6241
        break;
×
6242
    }
6243
    case 1335: //Circle of protection : black
6244
    {
6245
        observer->addObserver(NEW ACircleOfProtection(observer, _id, card, Constants::MTG_COLOR_BLACK));
3✔
6246
        break;
3✔
6247
    }
6248
    case 1336: //Circle of protection : blue
6249
    {
6250
        observer->addObserver(NEW ACircleOfProtection(observer, _id, card, Constants::MTG_COLOR_BLUE));
×
6251
        break;
×
6252
    }
6253
    case 1337: //Circle of protection : green
6254
    {
6255
        observer->addObserver(NEW ACircleOfProtection(observer, _id, card, Constants::MTG_COLOR_GREEN));
×
6256
        break;
×
6257
    }
6258
    case 1338: //Circle of protection : red
6259
    {
6260
        observer->addObserver(NEW ACircleOfProtection(observer, _id, card, Constants::MTG_COLOR_RED));
×
6261
        break;
×
6262
    }
6263
    case 1339: //Circle of protection : white
6264
    {
6265
        observer->addObserver(NEW ACircleOfProtection(observer, _id, card, Constants::MTG_COLOR_WHITE));
×
6266
        break;
×
6267
    }
6268
    case 1102: //Conservator
6269
    {
6270
        observer->addObserver(NEW AConservator(observer, _id, card));
×
6271
        break;
×
6272
    }
6273

6274
    case 1103: //Crystal Rod
6275
    {
6276
        
6277
        std::vector<int16_t> cost;
×
6278
        cost.push_back(Constants::MTG_COLOR_ARTIFACT);
×
6279
        cost.push_back(1);
×
6280
        ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_BLUE, NEW ManaCost(cost, 1), 1);
×
6281
        observer->addObserver(ability);
×
6282
        break;
×
6283
    }
6284
    case 1152: //Deathlace
6285
    {
6286
        if (card->target)
×
6287
        {
6288
            card->target->setColor(Constants::MTG_COLOR_BLACK, 1);
×
6289
        }
6290
        else
6291
        {
6292
            Spell * starget = spell->getNextSpellTarget();
×
6293
            starget->source->setColor(Constants::MTG_COLOR_BLACK, 1);
×
6294
        }
6295
        break;
×
6296
    }
6297

6298
    case 1291: //Fireball
6299
    {
6300
        int x = computeX(spell, card);
1✔
6301
        observer->addObserver(NEW AFireball(observer, _id, card, spell, x));
1✔
6302
        break;
1✔
6303
    }
6304
    case 1113: //Iron Star
6305
    {
6306
        
6307
        std::vector<int16_t> cost;
×
6308
        cost.push_back(Constants::MTG_COLOR_ARTIFACT);
×
6309
        cost.push_back(1);
×
6310
        ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_RED, NEW ManaCost(cost, 1), 1);
×
6311
        observer->addObserver(ability);
×
6312
        break;
×
6313
    }
6314
    case 1351: // Island Sanctuary
6315
    {
6316
        observer->addObserver(NEW AIslandSanctuary(observer, _id, card));
×
6317
        break;
×
6318
    }
6319
    case 1114: //Ivory cup
6320
    {
6321
        std::vector<int16_t> cost;
×
6322
        cost.push_back(Constants::MTG_COLOR_ARTIFACT);
×
6323
        cost.push_back(1);
×
6324
        ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_WHITE, NEW ManaCost(cost, 1), 1);
×
6325
        observer->addObserver(ability);
×
6326
        break;
×
6327
    }
6328
    case 1117: //Jandors Ring
6329
    {
6330
        observer->addObserver(NEW AJandorsRing(observer, _id, card));
×
6331
        break;
×
6332
    }
6333
    case 1257: //Lifelace
6334
    {
6335
        if (card->target)
×
6336
        {
6337
            card->target->setColor(Constants::MTG_COLOR_GREEN, 1);
×
6338
        }
6339
        else
6340
        {
6341
            Spell * starget = spell->getNextSpellTarget();
×
6342
            starget->source->setColor(Constants::MTG_COLOR_GREEN, 1);
×
6343
        }
6344
        break;
×
6345
    }
6346
    case 1124: //Mana Vault (the rest is softcoded!)
6347
    {
6348
        observer->addObserver(NEW ARegularLifeModifierAura(observer, _id + 2, card, card, MTG_PHASE_DRAW, -1, 1));
×
6349
        break;
×
6350
    }
6351
    case 1215: //Power Leak
6352
    {
6353
        observer->addObserver(NEW APowerLeak(observer, _id, card, card->target));
×
6354
        break;
×
6355
    }
6356
    case 1358: //Purelace
6357
    {
6358
        if (card->target)
×
6359
        {
6360
            card->target->setColor(Constants::MTG_COLOR_WHITE, 1);
×
6361
        }
6362
        else
6363
        {
6364
            Spell * starget = spell->getNextSpellTarget();
×
6365
            starget->source->setColor(Constants::MTG_COLOR_WHITE, 1);
×
6366
        }
6367
        break;
×
6368
    }
6369
    case 1312: //Red Elemental Blast
6370
    {
6371
        if (card->target)
×
6372
        {
6373
            card->target->controller()->game->putInGraveyard(card->target);
×
6374
        }
6375
        else
6376
        {
6377
            Spell * starget = spell->getNextSpellTarget();
×
6378
            observer->mLayers->stackLayer()->Fizzle(starget);
×
6379
        }
6380
        break;
×
6381
    }
6382

6383
    case 1139: //The Rack
6384
    {
6385
        observer->addObserver(NEW ALifeZoneLink(observer, _id, card, MTG_PHASE_UPKEEP, -3));
×
6386
        break;
×
6387
    }
6388

6389
    case 1140: //Throne of Bone
6390
    {
6391
        std::vector<int16_t> cost;
×
6392
        cost.push_back(Constants::MTG_COLOR_ARTIFACT);
×
6393
        cost.push_back(1);
×
6394
        ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_BLACK, NEW ManaCost(cost, 1), 1);
×
6395
        observer->addObserver(ability);
×
6396
        break;
×
6397
    }
6398

6399
    case 1142: //Wooden Sphere
6400
    {
6401
        std::vector<int16_t> cost;
×
6402
        cost.push_back(Constants::MTG_COLOR_ARTIFACT);
×
6403
        cost.push_back(1);
×
6404
        ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_GREEN, NEW ManaCost(cost, 1), 1);
×
6405
        observer->addObserver(ability);
×
6406
        break;
×
6407
    }
6408
    case 1143: //Animate Dead
6409
    {
6410
        AAnimateDead * a = NEW AAnimateDead(observer, _id, card, card->target);
4✔
6411
        observer->addObserver(a);
4✔
6412
        card->target = ((MTGCardInstance *) a->target);
4✔
6413
        break;
4✔
6414
    }
6415
    case 1156: //Drain Life
6416
    {
6417
        Damageable * target = spell->getNextDamageableTarget();
1✔
6418
        int x = spell->cost->getConvertedCost() - 2; //TODO Fix that !!! + X should be only black mana, that needs to be checked !
1✔
6419
        observer->mLayers->stackLayer()->addDamage(card, target, x);
1✔
6420
        if (target->life < x)
1✔
6421
            x = target->life;
×
6422
        observer->currentlyActing()->gainLife(x, card);
1✔
6423
        break;
1✔
6424
    }
6425
    case 1159: //Erg Raiders
6426
    {
6427
        AErgRaiders* ability = NEW AErgRaiders(observer, _id, card);
3✔
6428
        observer->addObserver(ability);
3✔
6429
        break;
3✔
6430
    }
6431
    case 1202: //Hurkyl's Recall
6432
    {
6433
        Player * player = spell->getNextPlayerTarget();
×
6434
        if (player)
×
6435
        {
6436
            for (int i = 0; i < 2; i++)
×
6437
            {
6438
                MTGInPlay * inplay = observer->players[i]->game->inPlay;
×
6439
                for (int j = inplay->nb_cards - 1; j >= 0; j--)
×
6440
                {
6441
                    MTGCardInstance * card = inplay->cards[j];
×
6442
                    if (card->owner == player && card->hasType(Subtypes::TYPE_ARTIFACT))
×
6443
                    {
6444
                        player->game->putInZone(card, inplay, player->game->hand);
×
6445
                    }
6446
                }
6447
            }
6448
        }
6449
        break;
×
6450
    }
6451
    case 1209: //Mana Short
6452
    {
6453
        Player * player = spell->getNextPlayerTarget();
×
6454
        if (player)
×
6455
        {
6456
            MTGInPlay * inplay = player->game->inPlay;
×
6457
            for (int i = 0; i < inplay->nb_cards; i++)
×
6458
            {
6459
                MTGCardInstance * current = inplay->cards[i];
×
6460
                if (current->hasType(Subtypes::TYPE_LAND))
×
6461
                    current->tap();
×
6462
            }
6463
            player->getManaPool()->Empty();
×
6464
        }
6465
        break;
×
6466
    }
6467
    case 1167: //Mind Twist
6468
    {
6469
        int xCost = computeX(spell, card);
×
6470
        for (int i = 0; i < xCost; i++)
×
6471
        {
6472
            observer->opponent()->game->discardRandom(observer->opponent()->game->hand, card);
×
6473
        }
6474
        break;
×
6475
    }
6476
    case 1176: //Sacrifice
6477
    {
6478
        ASacrifice * ability = NEW ASacrifice(observer, _id, card, card->target);
×
6479
        observer->addObserver(ability);
×
6480
        break;
×
6481
    }
6482
    case 1194: //Control Magic
6483
    {
6484
        observer->addObserver(NEW AControlStealAura(observer, _id, card, card->target));
9✔
6485
        break;
9✔
6486
    }
6487
    case 1231: //Volcanic Eruption
6488
    {
6489
        int x = computeX(spell, card);
×
6490
        int _x = x;
×
6491
        MTGCardInstance * target = spell->getNextCardTarget();
×
6492
        while (target && _x)
×
6493
        {
6494
            target->destroy();
×
6495
            _x--;
×
6496
            target = spell->getNextCardTarget(target);
×
6497
        }
6498
        x -= _x;
×
6499
        for (int i = 0; i < 2; i++)
×
6500
        {
6501
            observer->mLayers->stackLayer()->addDamage(card, observer->players[i], x);
×
6502
            for (int j = 0; j < observer->players[i]->game->inPlay->nb_cards; j++)
×
6503
            {
6504
                MTGCardInstance * current = observer->players[i]->game->inPlay->cards[j];
×
6505
                if (current->isCreature())
×
6506
                {
6507
                    observer->mLayers->stackLayer()->addDamage(card, current, x);
×
6508
                }
6509
            }
6510
        }
6511
        break;
×
6512
    }
6513
    case 1288: //EarthBind
6514
    {
6515
        observer->addObserver(NEW AEarthbind(observer, _id, card, card->target));
×
6516
        break;
×
6517
    }
6518
    case 1344: //Eye for an Eye
6519
    {
6520
        Damage * damage = spell->getNextDamageTarget();
×
6521
        if (damage)
×
6522
        {
6523
            observer->mLayers->stackLayer()->addDamage(card, damage->source->controller(), damage->damage);
×
6524
        }
6525
        break;
×
6526
    }
6527
    case 1243: //Fastbond
6528
    {
6529
        observer->addObserver(NEW AFastbond(observer, _id, card));
2✔
6530
        break;
2✔
6531
    }
6532
    case 1227: //Toughtlace
6533
    {
6534
        if (card->target)
×
6535
        {
6536
            card->target->setColor(Constants::MTG_COLOR_BLUE, 1);
×
6537
        }
6538
        else
6539
        {
6540
            Spell * starget = spell->getNextSpellTarget();
×
6541
            starget->source->setColor(Constants::MTG_COLOR_BLUE, 1);
×
6542
        }
6543
        break;
×
6544
    }
6545

6546
    case 2732: //Kjeldoran Frostbeast
6547
    {
6548
        observer->addObserver(NEW AKjeldoranFrostbeast(observer, _id, card));
3✔
6549
        break;
3✔
6550
    }
6551

6552
        // --- addon Mirage ---
6553

6554
    case 3410: //Seed of Innocence
6555
    {
6556
        for (int i = 0; i < 2; i++)
×
6557
        {
6558
            for (int j = 0; j < observer->players[i]->game->inPlay->nb_cards; j++)
×
6559
            {
6560
                MTGCardInstance * current = observer->players[i]->game->inPlay->cards[j];
×
6561
                if (current->hasType("Artifact"))
×
6562
                {
6563
                    observer->players[i]->game->putInGraveyard(current);
×
6564
                    current->controller()->gainLife(current->getManaCost()->getConvertedCost(), card);
×
6565
                }
6566
            }
6567
        }
6568
        break;
×
6569
    }
6570

6571
        //-- addon 10E---
6572

6573
    case 129767: //Threaten
6574
    {
6575
        observer->addObserver(NEW AInstantControlSteal(observer, _id, card, card->target));
×
6576
        break;
×
6577
    }
6578
  
6579
    case 129774: // Traumatize
6580
    {
6581
        int nbcards;
6582
        Player * player = spell->getNextPlayerTarget();
×
6583
        MTGLibrary * library = player->game->library;
×
6584
        nbcards = (library->nb_cards) / 2;
×
6585
        for (int i = 0; i < nbcards; i++)
×
6586
        {
6587
            if (library->nb_cards)
×
6588
                player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
×
6589
        }
6590
        break;
×
6591
    }
6592
    case 130553:// Beacon of Immortality
6593
    {
6594
        Player * player = spell->getNextPlayerTarget();
×
6595
        if (!player->inPlay()->hasAbility(Constants::CANTCHANGELIFE))
×
6596
        {
6597
            if (player->life < (INT_MAX / 4))
×
6598
                player->life += player->life;
×
6599
        }
6600
        zones->putInZone(card, spell->from, zones->library);
×
6601
        zones->library->shuffle();
×
6602
        break;
×
6603
    }
6604
    case 135262:// Beacon of Destruction & unrest
6605
    {
6606
        zones->putInZone(card, spell->from, zones->library);
3✔
6607
        zones->library->shuffle();
3✔
6608
        break;
3✔
6609
    }
6610
    case 129750: //Sudden Impact
6611
    {
6612
        Damageable * target = spell->getNextDamageableTarget();
×
6613
        Player * p = spell->getNextPlayerTarget();
×
6614
        MTGHand * hand = p->game->hand;
×
6615
        int damage = hand->nb_cards;
×
6616
        observer->mLayers->stackLayer()->addDamage(card, target, damage);
×
6617
        break;
×
6618
    }
6619
    case 130369: // Soulblast
6620
    {
6621
        int damage = 0;
1✔
6622
        Damageable * target = spell->getNextDamageableTarget();
1✔
6623
        for (int j = card->controller()->game->inPlay->nb_cards - 1; j >= 0; --j)
4✔
6624
        {
6625
            MTGCardInstance * current = card->controller()->game->inPlay->cards[j];
3✔
6626
            if (current->hasType(Subtypes::TYPE_CREATURE))
3✔
6627
            {
6628
                card->controller()->game->putInGraveyard(current);
2✔
6629
                damage += current->getCurrentPower();
2✔
6630
            }
6631
        }
6632
        observer->mLayers->stackLayer()->addDamage(card, target, damage);
1✔
6633
        break;
1✔
6634
    }
6635

6636
    case 129698: // Reminisce
6637
    {
6638
        int nbcards;
6639
        Player * player = spell->getNextPlayerTarget();
×
6640
        MTGLibrary * library = player->game->library;
×
6641
        MTGGraveyard * graveyard = player->game->graveyard;
×
6642
        nbcards = (graveyard->nb_cards);
×
6643
        for (int i = 0; i < nbcards; i++)
×
6644
        {
6645
            if (graveyard->nb_cards)
×
6646
                player->game->putInZone(graveyard->cards[graveyard->nb_cards - 1], graveyard, library);
×
6647
        }
6648
        library->shuffle();
×
6649
        break;
×
6650
    }
6651

6652
        // --- addon Ravnica---
6653

6654
    case 89114: //Psychic Drain
6655
    {
6656
        Player * player = spell->getNextPlayerTarget();
×
6657
        MTGLibrary * library = player->game->library;
×
6658
        int x = computeX(spell, card);
×
6659
        for (int i = 0; i < x; i++)
×
6660
        {
6661
            if (library->nb_cards)
×
6662
                player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
×
6663
        }
6664
        observer->currentlyActing()->gainLife(x, card);
×
6665
        break;
×
6666
    }
6667

6668
    default:
6669
        break;
3,667✔
6670
    }
6671

6672
    /* We want to get rid of these basicAbility things.
6673
     * basicAbilities themselves are alright, but creating New object depending on them is dangerous
6674
     * The main reason is that classes that add an ability to a card do NOT create these objects, and therefore do NOT
6675
     * Work.
6676
     * For example, setting EXALTED for a creature is not enough right now...
6677
     * It shouldn't be necessary to add an object. State based abilities could do the trick
6678
     */
6679

6680
    if (card->basicAbilities[(int)Constants::EXALTED])
3,698✔
6681
    {
6682
        observer->addObserver(NEW AExalted(observer, _id, card));
7✔
6683
    }
6684

6685
    if (card->basicAbilities[(int)Constants::FLANKING])
3,698✔
6686
    {
6687
        observer->addObserver(NEW AFlankerAbility(observer, _id, card));
4✔
6688
    }
6689

6690
    if(card->basicAbilities[(int)Constants::MODULAR])
3,698✔
6691
    {
6692
        AModularAbility * ability = NEW AModularAbility(observer, _id, card, card, card->getModularValue());
2✔
6693
        observer->addObserver(ability);
2✔
6694
    }
6695

6696
    const int HomeAbilities[] = {(int)Constants::FORESTHOME, (int)Constants::ISLANDHOME, (int)Constants::MOUNTAINHOME, (int)Constants::SWAMPHOME, (int)Constants::PLAINSHOME};
3,698✔
6697
    const char * HomeLands[] = {"forest", "island", "mountain", "swamp", "plains"};
3,698✔
6698

6699
    for (unsigned int i = 0; i < sizeof(HomeAbilities)/sizeof(HomeAbilities[0]); ++i)
22,188✔
6700
    {
6701
        if (card->basicAbilities[HomeAbilities[i]])
18,490✔
6702
            observer->addObserver(NEW AStrongLandLinkCreature(observer, _id, card, HomeLands[i]));
×
6703
    }
6704

6705
    if(card->previous && card->previous->previous && card->previous->previous->suspended)
3,698✔
6706
        card->basicAbilities[(int)Constants::HASTE] = 1;
×
6707

6708
    if (card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY))
3,698✔
6709
    {
6710
        MTGPlayerCards * zones = card->controller()->game;
335✔
6711
        MTGPlayerCards * Endzones = card->owner->game;//put them in thier owners respective zones as per rules.
335✔
6712
        if (card->basicAbilities[(int)Constants::EXILEDEATH] || card->basicAbilities[(int)Constants::GAINEDEXILEDEATH] || (card->basicAbilities[(int)Constants::HASDISTURB] && card->alternateCostPaid[ManaCost::MANA_PAID_WITH_RETRACE] == 1))
335✔
6713
        {
6714
            card->basicAbilities[(int)Constants::GAINEDEXILEDEATH] = 0;
×
6715
            card->controller()->game->putInZone(card, card->getCurrentZone(), card->owner->game->exile);
×
6716
        }
6717
        else if (card->basicAbilities[(int)Constants::DOUBLEFACEDEATH] || card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH])
335✔
6718
        {
6719
            card->basicAbilities[(int)Constants::GAINEDDOUBLEFACEDEATH] = 0;
×
6720
            card->controller()->game->putInZone(card, card->getCurrentZone(), card->owner->game->temp);
×
6721
        }
6722
        else if (card->basicAbilities[(int)Constants::HANDDEATH] || card->basicAbilities[(int)Constants::GAINEDHANDDEATH])
335✔
6723
        {
6724
            card->basicAbilities[(int)Constants::GAINEDHANDDEATH] = 0;
×
6725
            card->controller()->game->putInZone(card, card->getCurrentZone(), card->owner->game->hand);
×
6726
        }
6727
        else if (card->alternateCostPaid[ManaCost::MANA_PAID_WITH_BUYBACK] > 0)
335✔
6728
        {
6729
            card->alternateCostPaid[ManaCost::MANA_PAID_WITH_BUYBACK] = 0;
3✔
6730
            zones->putInZone(card, zones->stack, Endzones->hand);
3✔
6731
        }
6732
        else if (card->alternateCostPaid[ManaCost::MANA_PAID_WITH_FLASHBACK] > 0)
332✔
6733
        {
6734
            zones->putInZone(card, zones->stack, Endzones->exile);
1✔
6735
        }
6736
        else
6737
        {
6738
            zones->putInZone(card, zones->stack, Endzones->graveyard);
331✔
6739
        }
6740
    }
6741

6742
}
6743

6744
//mehods used in parseMagicLine()
6745

6746
//ManaRedux -> manaredux(colorless,+2)
6747
//          -> manaredux(green,-2)
6748
MTGAbility * AbilityFactory::getManaReduxAbility(string s, int id, Spell *, MTGCardInstance *card, MTGCardInstance *target)
3✔
6749
{
6750
    int color = -1;
3✔
6751
    string manaCost = s.substr(s.find(",") + 1);
6✔
6752
    replace(manaCost.begin(), manaCost.end(), ')', ' ');
3✔
6753
    trim(manaCost);
3✔
6754
    const string ColorStrings[] = { Constants::kManaColorless, Constants::kManaGreen, Constants::kManaBlue, Constants::kManaRed, Constants::kManaBlack, Constants::kManaWhite };
6✔
6755

6756
    for (unsigned int i = 0; i < sizeof(ColorStrings)/sizeof(ColorStrings[0]); ++i)
13✔
6757
    {
6758
        if (s.find(ColorStrings[i]) != string::npos)
13✔
6759
        {
6760
            color = i;
3✔
6761
            break;
3✔
6762
        }
6763
    }
6764
    if (color == -1)
3✔
6765
    {
6766
        DebugTrace("An error has happened in creating a Mana Redux Ability! " << s );
×
6767
        return NULL;
×
6768
    }
6769
    // figure out the mana cost
6770
    int amount = 0;
3✔
6771
    WParsedInt * value = NEW WParsedInt(manaCost, NULL, card);
3✔
6772
    if(value){
3✔
6773
        amount = value->getValue();
3✔
6774
        SAFE_DELETE(value);
3✔
6775
    }
6776
    return NEW AAlterCost(observer, id, card, target, amount, color);
3✔
6777
}
6778

6779
vector<void*> MTGAbility::deletedpointers;
1✔
6780

6781
MTGAbility::MTGAbility(const MTGAbility& a): ActionElement(a)
3,033✔
6782
{
6783
    //Todo get rid of menuText, it is only used as a placeholder in getMenuText, for something that could be a string
6784
    for (int i = 0; i < 50; ++i)
154,683✔
6785
    {
6786
        menuText[i] = a.menuText[i];
151,650✔
6787
    }
6788

6789
    game = a.game;
3,033✔
6790

6791
    oneShot = a.oneShot;
3,033✔
6792
    forceDestroy = a.forceDestroy;
3,033✔
6793
    forcedAlive = a.forcedAlive;
3,033✔
6794
    canBeInterrupted = a.canBeInterrupted;
3,033✔
6795

6796
    //costs get copied, and will be deleted in the destructor
6797
    mCost = a.mCost ? NEW ManaCost(a.mCost) : NULL;
3,033✔
6798

6799
    //alternative costs are not deleted in the destructor...who deletes them???
6800
    alternative = a.alternative; // ? NEW ManaCost(a.alternative) : NULL;
3,033✔
6801
    BuyBack = a.BuyBack; //? NEW ManaCost(a.BuyBack) : NULL;
3,033✔
6802
    FlashBack = a.FlashBack; // ? NEW ManaCost(a.FlashBack) : NULL;
3,033✔
6803
    Retrace = a.Retrace;// ? NEW ManaCost(a.Retrace) : NULL;
3,033✔
6804
    Bestow = a.Bestow;
3,033✔
6805
    morph =  a.morph;  //? NEW ManaCost(a.morph) : NULL;
3,033✔
6806
    suspend = a.suspend;// ? NEW ManaCost(a.suspend) : NULL;
3,033✔
6807

6808
    //Those two are pointers, but we don't delete them in the destructor, no need to copy them
6809
    target = a.target;
3,033✔
6810
    source = a.source;
3,033✔
6811

6812
    aType = a.aType;
3,033✔
6813
    naType = a.naType;
3,033✔
6814
    abilitygranted = a.abilitygranted;
3,033✔
6815
    
6816
};
3,033✔
6817

6818
MTGAbility::MTGAbility(GameObserver* observer, int id, MTGCardInstance * card) :
97,098✔
6819
    ActionElement(id)
97,098✔
6820
{
6821
    game = observer;
97,098✔
6822
    source = card;
97,098✔
6823
    target = card;
97,098✔
6824
    aType = MTGAbility::UNKNOWN;
97,098✔
6825
    mCost = NULL;
97,098✔
6826
    forceDestroy = 0;
97,098✔
6827
    forcedAlive = 0;
97,098✔
6828
    oneShot = 0;
97,098✔
6829
    canBeInterrupted = true;
97,098✔
6830
}
97,098✔
6831

6832
MTGAbility::MTGAbility(GameObserver* observer, int id, MTGCardInstance * _source, Targetable * _target) :
87,119✔
6833
    ActionElement(id)
87,119✔
6834
{
6835
    game = observer;
87,119✔
6836
    source = _source;
87,119✔
6837
    target = _target;
87,119✔
6838
    aType = MTGAbility::UNKNOWN;
87,119✔
6839
    mCost = NULL;
87,119✔
6840
    forceDestroy = 0;
87,119✔
6841
    forcedAlive = 0;
87,119✔
6842
    oneShot = 0;
87,119✔
6843
    canBeInterrupted = true;
87,119✔
6844
}
87,119✔
6845

6846
void MTGAbility::setCost(ManaCost * cost, bool forceDelete)
68,903✔
6847
{
6848
    if (mCost) {
68,903✔
6849
        DebugTrace("WARNING: Mtgability.cpp, attempt to set cost when previous cost is not null");
×
6850
        if (forceDelete)
×
6851
            delete(mCost);
×
6852
    }
6853
    mCost = cost;
68,903✔
6854
}
68,903✔
6855

6856
int MTGAbility::stillInUse(MTGCardInstance * card)
118,550✔
6857
{
6858
    if (card == source || card == target)
118,550✔
6859
        return 1;
10✔
6860
    return 0;
118,540✔
6861
}
6862

6863
MTGAbility::~MTGAbility()
374,474✔
6864
{
6865
    SAFE_DELETE(mCost);
187,237✔
6866
}
187,237✔
6867

6868
int MTGAbility::addToGame()
55,266✔
6869
{
6870
    game->addObserver(this);
55,266✔
6871
    return 1;
55,266✔
6872
}
6873

6874
int MTGAbility::removeFromGame()
26✔
6875
{
6876
    game->removeObserver(this);
26✔
6877
    return 1;
26✔
6878
}
6879

6880
//returns 1 if this ability needs to be removed from the list of active abilities
6881
int MTGAbility::testDestroy()
10,760,910✔
6882
{
6883
    if(waitingForAnswer)
10,760,910✔
6884
        return 0;
1,222✔
6885
    if(forceDestroy == 1)
10,759,688✔
6886
        return 1;
104✔
6887
    if(forceDestroy == -1)
10,759,584✔
6888
        return 0;
16,884✔
6889
    if(source->handEffects && game->isInHand(source))
10,742,700✔
6890
        return 0;
285✔
6891
    if(source->graveEffects && game->isInGrave(source))
10,742,415✔
6892
        return 0;
278✔
6893
    if(source->exileEffects && game->isInExile(source))
10,742,137✔
6894
        return 0;
×
6895
    if(source->commandZoneEffects && game->isInCommandZone(source))
10,742,137✔
6896
        return 0;
×
6897
    if(forcedAlive == 1)
10,742,137✔
6898
        return 0;
99,580✔
6899
    if(game->mLayers->stackLayer()->has(this)) //Moved here to avoid a random crash (e.g. blasphemous act)
10,642,557✔
6900
        return 0;
5,375✔
6901
    if(!game->isInPlay(source))
10,637,182✔
6902
        return 1;
757✔
6903
    if(target && !dynamic_cast<Player*>(target) && !game->isInPlay((MTGCardInstance *) target))
10,636,425✔
6904
        return 1;
24✔
6905
    return 0;
10,636,401✔
6906
}
6907

6908
int MTGAbility::fireAbility()
9,314✔
6909
{
6910
    if (canBeInterrupted)
9,314✔
6911
        game->mLayers->stackLayer()->addAbility(this);
509✔
6912
    else
6913
        resolve();
8,805✔
6914
    return 1;
9,314✔
6915
}
6916

6917
ostream& MTGAbility::toString(ostream& out) const
×
6918
{
6919
    return out << "MTGAbility ::: menuText : " << menuText << " ; game : " << game << " ; forceDestroy : " << forceDestroy
×
6920
                    << " ; mCost : " << mCost << " ; target : " << target << " ; aType : " << aType << " ; source : " << source;
×
6921
}
6922

6923
Player * MTGAbility::getPlayerFromTarget(Targetable * target)
10,068✔
6924
{
6925
    if (!target)
10,068✔
6926
        return NULL;
×
6927

6928
    if (MTGCardInstance * cTarget = dynamic_cast<MTGCardInstance *>(target))
10,068✔
6929
        return cTarget->controller();
9,999✔
6930

6931
    if (Player * cPlayer = dynamic_cast<Player *>(target))
69✔
6932
        return cPlayer;
67✔
6933

6934
    return ((Interruptible *) target)->source->controller();
2✔
6935
}
6936

6937
Player * MTGAbility::getPlayerFromDamageable(Damageable * target)
1,451✔
6938
{
6939
    if (!target)
1,451✔
6940
        return NULL;
×
6941

6942
    if (target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
1,451✔
6943
        return ((MTGCardInstance *) target)->controller();
1,448✔
6944

6945
    return (Player *) target;
3✔
6946
}
6947

6948
//
6949

6950
NestedAbility::NestedAbility(MTGAbility * _ability)
56,858✔
6951
{
6952
    ability = _ability;
56,858✔
6953
}
56,858✔
6954

6955
//
6956

6957
ActivatedAbility::ActivatedAbility(GameObserver* observer, int id, MTGCardInstance * card, ManaCost * _cost, int restrictions,string limit,MTGAbility * sideEffect,string usesBeforeSideEffects,string castRestriction) :
54,119✔
6958
    MTGAbility(observer, id, card), restrictions(restrictions), needsTapping(0),limit(limit),sideEffect(sideEffect),usesBeforeSideEffects(usesBeforeSideEffects),castRestriction(castRestriction)
54,119✔
6959
{
6960
    counters = 0;
54,119✔
6961
    setCost(_cost);
54,119✔
6962
    abilityCost = 0;
54,119✔
6963
    sa = NULL;
54,119✔
6964
}
54,119✔
6965

6966
int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
1,864✔
6967
{        
6968
    if(card->isPhased)
1,864✔
6969
        return 0;
×
6970
    Player * player = game->currentlyActing();
1,864✔
6971
    int cPhase = game->getCurrentGamePhase();
1,864✔
6972
    switch (restrictions)
1,864✔
6973
    {
6974
    case PLAYER_TURN_ONLY:
6975
        if (player != game->currentPlayer)
13✔
6976
            return 0;
1✔
6977
        break;
12✔
6978
    case OPPONENT_TURN_ONLY:
6979
        if (player == game->currentPlayer)
×
6980
            return 0;
×
6981
        break;
×
6982
    case AS_SORCERY:
6983
        if (player != game->currentPlayer)
173✔
6984
            return 0;
7✔
6985
        if (cPhase != MTG_PHASE_FIRSTMAIN && cPhase != MTG_PHASE_SECONDMAIN)
166✔
6986
            return 0;
31✔
6987
        if (player->opponent()->getObserver()->mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0||game->mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0||player->getObserver()->mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0)
135✔
6988
            return 0;
×
6989
        break;
135✔
6990
    }
6991
    if (restrictions >= MY_BEFORE_BEGIN && restrictions <= MY_AFTER_EOT)
1,825✔
6992
    {
6993
        if (player != game->currentPlayer)
68✔
6994
            return 0;
1✔
6995
        if (cPhase != restrictions - MY_BEFORE_BEGIN + MTG_PHASE_BEFORE_BEGIN)
67✔
6996
            return 0;
32✔
6997
    }
6998

6999
    if (restrictions >= OPPONENT_BEFORE_BEGIN && restrictions <= OPPONENT_AFTER_EOT)
1,792✔
7000
    {
7001
        if (player == game->currentPlayer)
×
7002
            return 0;
×
7003
        if (cPhase != restrictions - OPPONENT_BEFORE_BEGIN + MTG_PHASE_BEFORE_BEGIN)
×
7004
            return 0;
×
7005
    }
7006

7007
    if (restrictions >= BEFORE_BEGIN && restrictions <= AFTER_EOT)
1,792✔
7008
    {
7009
        if (cPhase != restrictions - BEFORE_BEGIN + MTG_PHASE_BEFORE_BEGIN)
×
7010
            return 0;
×
7011
    }
7012
    limitPerTurn = 0;
1,792✔
7013
    if(limit.size())
1,792✔
7014
    {
7015
        WParsedInt * value = NEW WParsedInt(limit.c_str(),NULL,source);
51✔
7016
        limitPerTurn = value->getValue();
51✔
7017
        delete value;
51✔
7018
        //only run this check if we have a valid limit string.
7019
        //limits on uses are based on when the ability is used, not when it is resolved
7020
        //incrementing of counters directly after the fireability()
7021
        //as ability limits are supposed to count the use regaurdless of the ability actually
7022
        //resolving. this check was previously located in genericactivated, and incrementing was handled in the resolve.
7023
        if (limitPerTurn && counters >= limitPerTurn)
51✔
7024
            return 0;
19✔
7025
    }
7026
    if(castRestriction.size())
1,773✔
7027
    {
7028
        AbilityFactory af(game);
19✔
7029
        int checkCond = af.parseCastRestrictions(card,card->controller(),castRestriction);
11✔
7030
        if(!checkCond)
11✔
7031
            return 0;
3✔
7032
    }
7033
    if (card == source && source->controller() == player && (!needsTapping || (!source->isTapped()
2,983✔
7034
                    && !source->hasSummoningSickness())))
1✔
7035
    {
7036
        ManaCost * cost = getCost();
1,213✔
7037
        if (!cost)
1,213✔
7038
            return 1;
153✔
7039
        if(card->hasType(Subtypes::TYPE_PLANESWALKER))
1,060✔
7040
        {
7041
            /*for(unsigned int k = 0;k < card->cardsAbilities.size();++k)
7042
            {
7043
                ActivatedAbility * check = dynamic_cast<ActivatedAbility*>(card->cardsAbilities[k]);
7044
                if(check && check->counters)
7045
                    return 0;
7046
            }*/
7047
            // Improved the check to avoid the multiple triggers in case of abilities gained from other cards (e.g. Kasmina, Enigma Sage)
7048
            bool turnSide = false;
×
7049
            for(unsigned int k = 0; k < card->getObserver()->mLayers->actionLayer()->mObjects.size(); ++k)
×
7050
            {
7051
                ActivatedAbility * check = dynamic_cast<ActivatedAbility*>(card->getObserver()->mLayers->actionLayer()->mObjects[k]);
×
7052
                turnSide = card->isFlipped > 0 && !card->isInPlay(card->getObserver());
×
7053
                if(!turnSide && check && check->source == card && check->counters)
×
7054
                    return 0;
×
7055
            }
7056
            if (player != game->currentPlayer)
×
7057
                return 0;
×
7058
            if (!turnSide && (cPhase != MTG_PHASE_FIRSTMAIN && cPhase != MTG_PHASE_SECONDMAIN))
×
7059
                return 0;
×
7060
        }
7061
        if (source->has(Constants::NOACTIVATED) || (source->mutation && source->parentCards.size() > 0)) // Mutated Over/Under card doesn't have to react to click anymore
1,060✔
7062
            return 0;
×
7063
        AbilityFactory af(game);
2,120✔
7064
        MTGAbility * fmp = NULL;
1,060✔
7065
        fmp = af.getCoreAbility(this);
1,060✔
7066
        AManaProducer * amp = dynamic_cast<AManaProducer *> (this);
1,060✔
7067
        AManaProducer * femp = dynamic_cast<AManaProducer *> (fmp);
1,060✔
7068
        if (source->has(Constants::NOMANA) && (amp||femp))
1,060✔
7069
            return 0;
×
7070
        if(source->has(Constants::ONLYMANA) && !(amp||femp))
1,060✔
7071
            return 0;
×
7072
        cost->setExtraCostsAction(this, card);
1,060✔
7073
        if (source->has(Constants::NOACTIVATEDTAP) && cost->extraCosts)
1,060✔
7074
        {
7075
            for(unsigned int i = 0;i < cost->extraCosts->costs.size();++i)
×
7076
            {
7077
                ExtraCost * eCost = cost->getExtraCost(i);
×
7078
                if(dynamic_cast<TapCost *>(eCost))
×
7079
                {
7080
                    return 0;
×
7081
                }
7082
            }
7083

7084
        }
7085

7086
        if (!mana)
1,060✔
7087
            mana = player->getManaPool();
1,060✔
7088
        if (!mana->canAfford(cost,card->has(Constants::ANYTYPEOFMANAABILITY)))
1,060✔
7089
            return 0;
110✔
7090
        if (!cost->canPayExtra())
950✔
7091
            return 0;
10✔
7092
        return 1;
940✔
7093
    }
7094
    return 0;
557✔
7095
}
7096

7097
int ActivatedAbility::reactToClick(MTGCardInstance * card)
298✔
7098
{
7099
    if (!isReactingToClick(card))
298✔
7100
        return 0;
9✔
7101
    Player * player = game->currentlyActing();
289✔
7102
    ManaCost * cost = getCost();
289✔
7103
    if (cost)
289✔
7104
    {
7105
        if (!cost->isExtraPaymentSet())
226✔
7106
        {
7107
            game->mExtraPayment = cost->extraCosts;
16✔
7108
            return 0;
16✔
7109
        }
7110
        if(cost->extraCosts){ // Added to check if the snow mana amount is enough to pay all the snow cost.
210✔
7111
            int countSnow = 0;
114✔
7112
            for(unsigned int i = 0; i < cost->extraCosts->costs.size(); i++){
243✔
7113
                if(dynamic_cast<SnowCost*> (cost->extraCosts->costs[i]))
129✔
7114
                    countSnow++;
×
7115
            }
7116
            if((source->controller()->snowManaG + source->controller()->snowManaU + source->controller()->snowManaR + 
342✔
7117
                source->controller()->snowManaB + source->controller()->snowManaW + source->controller()->snowManaC) < countSnow){
228✔
7118
                game->mExtraPayment = cost->extraCosts;
×
7119
                return 0;
×
7120
            }
7121
        }
7122
        ManaCost * previousManaPool = NEW ManaCost(player->getManaPool());
210✔
7123
        cost->doPayExtra(); // Bring here brefore the normal payment to solve Snow Mana payment bug.
210✔
7124
        game->currentlyActing()->getManaPool()->pay(cost);
210✔
7125
        SAFE_DELETE(abilityCost);
210✔
7126
        abilityCost = previousManaPool->Diff(player->getManaPool());
210✔
7127
        delete previousManaPool;
210✔
7128
    }
7129
    return ActivatedAbility::activateAbility();
273✔
7130
}
7131

7132
int ActivatedAbility::reactToTargetClick(Targetable * object)
60✔
7133
{
7134
    if (!isReactingToTargetClick(object))
60✔
7135
        return 0;
×
7136
    Player * player = game->currentlyActing();
60✔
7137
    ManaCost * cost = getCost();
60✔
7138
    if (cost)
60✔
7139
    {
7140
        if (MTGCardInstance * cObject = dynamic_cast<MTGCardInstance *>(object))
33✔
7141
            cost->setExtraCostsAction(this, cObject);
33✔
7142
        if (!cost->isExtraPaymentSet())
33✔
7143
        {
7144
            game->mExtraPayment = cost->extraCosts;
2✔
7145
            return 0;
2✔
7146
        }
7147
        if(cost->extraCosts){ // Added to check if the snow mana amount is enough to pay all the snow cost.
31✔
7148
            int countSnow = 0;
17✔
7149
            for(unsigned int i = 0; i < cost->extraCosts->costs.size(); i++){
34✔
7150
                if(dynamic_cast<SnowCost*> (cost->extraCosts->costs[i]))
17✔
7151
                    countSnow++;
×
7152
            }
7153
            if((source->controller()->snowManaG + source->controller()->snowManaU + source->controller()->snowManaR + 
51✔
7154
                source->controller()->snowManaB + source->controller()->snowManaW + source->controller()->snowManaC) < countSnow){
34✔
7155
                game->mExtraPayment = cost->extraCosts;
×
7156
                return 0;
×
7157
            }
7158
        }
7159
        ManaCost * previousManaPool = NEW ManaCost(player->getManaPool());
31✔
7160
        cost->doPayExtra(); // Bring here brefore the normal payment to solve Snow Mana payment bug.
31✔
7161
        game->currentlyActing()->getManaPool()->pay(cost);
31✔
7162
        SAFE_DELETE(abilityCost);
31✔
7163
        abilityCost = previousManaPool->Diff(player->getManaPool());
31✔
7164
        delete previousManaPool;
31✔
7165
    }
7166
    return ActivatedAbility::activateAbility();
58✔
7167
}
7168

7169
int ActivatedAbility::activateAbility()
584✔
7170
{
7171
    MTGAbility * fmp = NULL;
584✔
7172
    bool wasTappedForMana = false;
584✔
7173
    //taking foreach manaproducers off the stack and sending tapped for mana events.
7174
    AbilityFactory af(game);
1,168✔
7175
    fmp = af.getCoreAbility(this);
584✔
7176
    AManaProducer * amp = dynamic_cast<AManaProducer *> (this);
584✔
7177
    AManaProducer * femp = dynamic_cast<AManaProducer *> (fmp);
584✔
7178
    ManaCost * cost = getCost();
584✔
7179
    if((amp||femp) && cost && cost->extraCosts)
584✔
7180
    {
7181
        for(unsigned int i = 0; i < cost->extraCosts->costs.size();i++)
528✔
7182
        {
7183
            ExtraCost * tapper = dynamic_cast<TapCost*>(cost->extraCosts->costs[i]);
264✔
7184
            if(tapper)
264✔
7185
                needsTapping = 1;
261✔
7186
            wasTappedForMana = true;
264✔
7187
        }
264✔
7188
    }
7189
    else if(amp||femp)
320✔
7190
    {
7191
        if(amp)
3✔
7192
            needsTapping = amp->tap;
×
7193
        else
7194
            needsTapping = femp->tap;
3✔
7195
    }
7196
    if (needsTapping && (source->isInPlay(game)|| wasTappedForMana))
584✔
7197
    {
7198
        if (amp||femp)
261✔
7199
        {
7200
            WEvent * e = NEW WEventCardTappedForMana(source, 0, 1);
261✔
7201
            game->receiveEvent(e);
261✔
7202
        }
7203
    }
7204
    if (amp||femp)
584✔
7205
    {
7206
        counters++;
267✔
7207
        if(sideEffect && usesBeforeSideEffects.size())
267✔
7208
        {
7209
            activateSideEffect();
×
7210
        }
7211
        this->resolve();
267✔
7212
        return 1;
267✔
7213
    }
7214
    counters++;
317✔
7215
    if(sideEffect && usesBeforeSideEffects.size())
317✔
7216
    {
7217
        activateSideEffect();
7✔
7218
    }
7219
    fireAbility();
317✔
7220
    return 1;
317✔
7221
}
7222

7223
void ActivatedAbility::activateSideEffect()
7✔
7224
{
7225
    WParsedInt * use = NEW WParsedInt(usesBeforeSideEffects.c_str(),NULL,source);
7✔
7226
    uses = use->getValue();
7✔
7227
    delete use;
7✔
7228
    if(counters == uses)
7✔
7229
    {
7230
        sa = sideEffect->clone();
1✔
7231
        sa->target = this->target;
1✔
7232
        sa->source = this->source;
1✔
7233
        if(sa->oneShot)
1✔
7234
        {
7235
            sa->fireAbility();
1✔
7236
        }
7237
        else
7238
        {
7239
            GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), sa);
×
7240
            wrapper->addToGame();
×
7241
        }
7242
    }
7243
    return;
7✔
7244
}
7245

7246
ActivatedAbility::~ActivatedAbility()
112,114✔
7247
{
7248
    SAFE_DELETE(abilityCost);
56,057✔
7249
    SAFE_DELETE(sideEffect);
56,057✔
7250
    SAFE_DELETE(sa);
56,057✔
7251
}
56,057✔
7252

7253
ostream& ActivatedAbility::toString(ostream& out) const
×
7254
{
7255
    out << "ActivatedAbility ::: restrictions : " << restrictions << " ; needsTapping : " << needsTapping << " (";
×
7256
    return MTGAbility::toString(out) << ")";
×
7257
}
7258

7259
TargetAbility::TargetAbility(GameObserver* observer, int id, MTGCardInstance * card, TargetChooser * _tc, ManaCost * _cost, int _playerturnonly, string castRestriction) :
266✔
7260
    ActivatedAbility(observer, id, card, _cost, _playerturnonly, "", NULL, "", castRestriction), NestedAbility(NULL) //Todo fix this mess, why do we have to pass "", NULL, "" here before cast restrictions?
266✔
7261
{
7262
    tc = _tc;
266✔
7263
}
266✔
7264

7265
TargetAbility::TargetAbility(GameObserver* observer, int id, MTGCardInstance * card, ManaCost * _cost, int _playerturnonly, string castRestriction) :
×
7266
    ActivatedAbility(observer, id, card, _cost, _playerturnonly,  "", NULL, "", castRestriction), NestedAbility(NULL) //Todo fix this mess, why do we have to pass "", NULL, "" here before cast restrictions?
×
7267
{
7268
    tc = NULL;
×
7269
}
7270

7271
int TargetAbility::reactToTargetClick(Targetable * object)
87✔
7272
{
7273
    if (MTGCardInstance * cObject = dynamic_cast<MTGCardInstance *>(object))
87✔
7274
        return reactToClick(cObject);
71✔
7275

7276
    if (waitingForAnswer)
16✔
7277
    {
7278
        if (tc->toggleTarget(object) == TARGET_OK_FULL)
16✔
7279
        {
7280
            waitingForAnswer = 0;
15✔
7281
            game->mLayers->actionLayer()->setCurrentWaitingAction(NULL);
15✔
7282
            return ActivatedAbility::reactToClick(source);
15✔
7283
        }
7284
        return 1;
1✔
7285
    }
7286
    return 0;
×
7287
}
7288

7289
int TargetAbility::reactToClick(MTGCardInstance * card)
341✔
7290
{
7291
    if (!waitingForAnswer)
341✔
7292
    {
7293
        if (isReactingToClick(card))
175✔
7294
        {
7295
            ManaCost * cost = getCost();
168✔
7296
            if (cost && !cost->isExtraPaymentSet())
168✔
7297
            {
7298
                game->mExtraPayment = cost->extraCosts;
3✔
7299
                return 0;
3✔
7300
            }
7301
            if (!tc) return 0; // Fix crash on mutating cards
165✔
7302
            waitingForAnswer = 1;
165✔
7303
            game->mLayers->actionLayer()->setCurrentWaitingAction(this);
165✔
7304
            tc->initTargets();
165✔
7305
            return 1;
165✔
7306
        }
7307
    }
7308
    else
7309
    {
7310
        if (card == source && ((tc->targetsReadyCheck() == TARGET_OK && !tc->targetMin) || tc->targetsReadyCheck() == TARGET_OK_FULL))
166✔
7311
        {
7312
            waitingForAnswer = 0;
4✔
7313
            game->mLayers->actionLayer()->setCurrentWaitingAction(NULL);
4✔
7314
            return ActivatedAbility::reactToClick(source);
4✔
7315
        }
7316
        else
7317
        {
7318
            if (tc->toggleTarget(card) == TARGET_OK_FULL && tc->targetsReadyCheck() == TARGET_OK_FULL)
162✔
7319
            {
7320
                int result = ActivatedAbility::reactToClick(source);
144✔
7321
                if (result)
144✔
7322
                {
7323
                    waitingForAnswer = 0;
144✔
7324
                    game->mLayers->actionLayer()->setCurrentWaitingAction(NULL);
144✔
7325
                    if(tc->targetter)
144✔
7326
                    {
7327
                    WEvent * e = NEW WEventTarget(card,source);
127✔
7328
                    game->receiveEvent(e);
127✔
7329
                    }
7330
                }
7331
                return result;
144✔
7332
            }
7333
            return 1;
18✔
7334
        }
7335
    }
7336
    return 0;
7✔
7337
}
7338

7339
void TargetAbility::Render()
×
7340
{
7341
    //TODO ?
7342
}
7343

7344
int TargetAbility::resolve()
139✔
7345
{
7346
    Targetable * t = tc->getNextTarget();
139✔
7347
    if (t && ability)
139✔
7348
    {
7349
        if (abilityCost)
139✔
7350
        {
7351
            source->X = 0;
76✔
7352
            ManaCost * diff = abilityCost->Diff(getCost());
76✔
7353
            source->X = diff->hasX();
76✔
7354
            delete (diff);
76✔
7355
        }
7356

7357
        ability->target = t;
139✔
7358
        //do nothing if the target controller responded by phasing out the target.
7359
        if (dynamic_cast<MTGCardInstance *>(t) && ((MTGCardInstance*)t)->isPhased)
139✔
7360
            return 0;
×
7361
        Player * targetedPlyr = dynamic_cast<Player *>(t);
139✔
7362
        if (targetedPlyr)
139✔
7363
        {
7364
            source->playerTarget = targetedPlyr;
14✔
7365
        }
7366
        if (ability->oneShot)
139✔
7367
        {
7368
            while(t)
355✔
7369
            {
7370
                ability->resolve();
120✔
7371
                t = tc->getNextTarget(t);
120✔
7372
                ability->target = t;
120✔
7373
            }
7374
            tc->initTargets();
115✔
7375
            return 1;
115✔
7376
        }
7377
        else
7378
        {
7379
            while(t)
72✔
7380
            {
7381
                MTGAbility * a = ability->clone();
24✔
7382
                a->addToGame();
24✔
7383
                t = tc->getNextTarget(t);
24✔
7384
                ability->target = t;
24✔
7385
            }
7386
            tc->initTargets();
24✔
7387
            return 1;
24✔
7388
        }
7389
    }
7390
    return 0;
×
7391
}
7392

7393
const string TargetAbility::getMenuText()
×
7394
{
7395
    if (ability)
×
7396
        return ability->getMenuText();
×
7397
    return ActivatedAbility::getMenuText();
×
7398
}
7399

7400
TargetAbility::~TargetAbility()
736✔
7401
{
7402
    SAFE_DELETE(ability);
368✔
7403
}
368✔
7404

7405
ostream& TargetAbility::toString(ostream& out) const
×
7406
{
7407
    out << "TargetAbility ::: (";
×
7408
    return ActivatedAbility::toString(out) << ")";
×
7409
}
7410

7411
//
7412

7413

7414
TriggeredAbility::TriggeredAbility(GameObserver* observer, int id, MTGCardInstance * card, Targetable * _target) :
81,466✔
7415
    MTGAbility(observer, id, card, _target)
81,466✔
7416
{
7417
}
81,466✔
7418

7419
TriggeredAbility::TriggeredAbility(GameObserver* observer, int id, MTGCardInstance * card) :
5,999✔
7420
    MTGAbility(observer, id, card)
5,999✔
7421
{
7422
}
5,999✔
7423

7424
int TriggeredAbility::receiveEvent(WEvent * e)
4,026,232✔
7425
{
7426
    if (triggerOnEvent(e))
4,026,232✔
7427
    {
7428
    if(dynamic_cast<WEventTarget*>(e))
154✔
7429
    {
7430
        //@targetted trigger as per mtg rules is a state based trigger
7431
        //that resolves instantly before the event that targetted it.
7432
        resolve();
×
7433
        return 1;
×
7434
    }
7435
    if(dynamic_cast<WEventCardSacrifice*>(e))
154✔
7436
    {
7437
        //sacrificed event
7438
        //thraximundar vs bloodfore collosus, thraximundar 
7439
        //must be able to survive a sacrificed bloodfire collosus,
7440
        //same with mortician beetle vs phyrexian denouncer test
7441
        resolve();
8✔
7442
        return 1;
8✔
7443
    }
7444
    if(dynamic_cast<WEventCardExploited*>(e))
146✔
7445
    {
7446
        //exploited event must resolve instantly or by the time they do the cards that triggered them 
7447
        //have already been put in graveyard.
7448
        resolve();
×
7449
        return 1;
×
7450
    }
7451
    if(dynamic_cast<WEventCardDiscard*>(e))
146✔
7452
    {
7453
        //discard event must resolve instantly or by the time they do the cards that triggered them 
7454
        //have already been put in graveyard.
7455
        resolve();
2✔
7456
        return 1;
2✔
7457
    }
7458
    WEventCardCycle * cycleCheck = dynamic_cast<WEventCardCycle*>(e);
144✔
7459
    if(cycleCheck && cycleCheck->card == source)
144✔
7460
    {
7461
        resolve();
3✔
7462
        return 1;
3✔
7463
        //When you cycle this card, first the cycling ability goes on the stack, 
7464
        //then the triggered ability goes on the stack on top of it. 
7465
        //The triggered ability will resolve before you draw a card from the cycling ability.
7466
        //
7467
        //The cycling ability and the triggered ability are separate. 
7468
        //If the triggered ability is countered (with Stifle, for example, or if all its targets have become illegal), 
7469
        //the cycling ability will still resolve and you'll draw a card.
7470
    }
7471
    WEventZoneChange * stackCheck = dynamic_cast<WEventZoneChange*>(e);
141✔
7472
    if(stackCheck && (stackCheck->to == game->currentPlayer->game->stack||stackCheck->to == game->currentPlayer->opponent()->game->stack))
141✔
7473
    {
7474
        resolve();
16✔
7475
        return 1;
16✔
7476
        //triggers that resolve from stack events must resolve instantly or by the time they do the cards that triggered them 
7477
        //have already been put in play or graveyard.
7478
    }
7479
        fireAbility();
125✔
7480
        return 1;
125✔
7481
    }
7482
    return 0;
4,026,078✔
7483
}
7484

7485
void TriggeredAbility::Update(float)
5,107,833✔
7486
{
7487
    if (trigger())
5,107,833✔
7488
        fireAbility();
8,863✔
7489
}
5,107,833✔
7490

7491
ostream& TriggeredAbility::toString(ostream& out) const
×
7492
{
7493
    out << "TriggeredAbility ::: (";
×
7494
    return MTGAbility::toString(out) << ")";
×
7495
}
7496

7497
// Trigger
7498
Trigger::Trigger(GameObserver* observer, int id, MTGCardInstance * source, bool once, TargetChooser * _tc)
5,988✔
7499
    : TriggeredAbility(observer, id, source), mOnce(once), mActiveTrigger(true)
5,988✔
7500
{
7501
    tc = _tc;
5,988✔
7502
}
5,988✔
7503

7504
int  Trigger::triggerOnEvent(WEvent * event) {
548,792✔
7505
    if(!mActiveTrigger) 
548,792✔
7506
        return 0;
×
7507

7508
    //Abilities don't work if the card is phased
7509
    if(source->isPhased) return 0;
548,792✔
7510

7511
    if (!triggerOnEventImpl(event))
548,792✔
7512
        return 0;
537,128✔
7513

7514
    if(mOnce && mActiveTrigger)
11,664✔
7515
        mActiveTrigger = false;
×
7516

7517
    if(castRestriction.size())
11,664✔
7518
    {
7519
        if(!source)
11,517✔
7520
            return 1;//can't check these restrictions without a source aka:in a rule.txt
11,514✔
7521
        AbilityFactory af(game);
11,520✔
7522
        int checkCond = af.parseCastRestrictions(source,source->controller(),castRestriction);
11,517✔
7523

7524
        if(!checkCond)
11,517✔
7525
            return 0;
11,514✔
7526
    }
7527
    return 1;
150✔
7528
}
7529

7530
//
7531
InstantAbility::InstantAbility(GameObserver* observer, int _id, MTGCardInstance * source) :
55✔
7532
    MTGAbility(observer, _id, source)
55✔
7533
{
7534
    init = 0;
55✔
7535
}
55✔
7536

7537
void InstantAbility::Update(float)
34,904✔
7538
{
7539
    if (!init)
34,904✔
7540
    {
7541
        init = resolve();
23,290✔
7542
    }
7543
}
34,904✔
7544

7545
InstantAbility::InstantAbility(GameObserver* observer, int _id, MTGCardInstance * source, Targetable * _target) :
1,973✔
7546
    MTGAbility(observer, _id, source, _target)
1,973✔
7547
    {
7548
        init = 0;
1,973✔
7549
    }
1,973✔
7550

7551
//Instant abilities last generally until the end of the turn
7552
    int InstantAbility::testDestroy()
34,883✔
7553
    {
7554
        GamePhase newPhase = game->getCurrentGamePhase();
34,883✔
7555
        if (newPhase != currentPhase && newPhase == MTG_PHASE_AFTER_EOT)
34,883✔
7556
            return 1;
121✔
7557
        currentPhase = newPhase;
34,762✔
7558
        return 0;
34,762✔
7559
    }
7560

7561
ostream& InstantAbility::toString(ostream& out) const
×
7562
{
7563
    out << "InstantAbility ::: init : " << init << " (";
×
7564
    return MTGAbility::toString(out) << ")";
×
7565
}
7566

7567
bool ListMaintainerAbility::canTarget(MTGGameZone * zone)
15,893,046✔
7568
{
7569
    if (tc)
15,893,046✔
7570
        return tc->targetsZone(zone);
15,893,046✔
7571
    for (int i = 0; i < 2; i++)
×
7572
    {
7573
        Player * p = game->players[i];
×
7574
        if (zone == p->game->inPlay)
×
7575
            return true;
×
7576
    }
7577
    return false;
×
7578
}
7579

7580
void ListMaintainerAbility::updateTargets()
882,947✔
7581
{
7582
    //remove invalid ones
7583
    map<MTGCardInstance *, bool> temp;
1,765,894✔
7584
    for (map<MTGCardInstance *, bool>::iterator it = cards.begin(); it != cards.end(); ++it)
993,249✔
7585
    {
7586
        MTGCardInstance * card = (*it).first;
110,302✔
7587
        if (!canBeInList(card) || card->mPropertiesChangedSinceLastUpdate)
110,302✔
7588
        {
7589
            temp[card] = true;
336✔
7590
        }
7591
    }
7592

7593
    for (map<MTGCardInstance *, bool>::iterator it = temp.begin(); it != temp.end(); ++it)
883,283✔
7594
    {
7595
        MTGCardInstance * card = (*it).first;
336✔
7596
        cards.erase(card);
336✔
7597
        if(!card->mutation) // Fix crash on mutating card...
336✔
7598
            removed(card);
336✔
7599
    }
7600
    temp.clear();
882,947✔
7601
    //add New valid ones
7602
    for (int i = 0; i < 2; i++)
2,648,841✔
7603
    {
7604
        Player * p = game->players[i];
1,765,894✔
7605
        MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->stack, p->game->exile, p->game->commandzone, p->game->sideboard, p->game->reveal };
1,765,894✔
7606
        for (int k = 0; k < 9; k++)
17,658,940✔
7607
        {
7608
            MTGGameZone * zone = zones[k];
15,893,046✔
7609
            if (canTarget(zone))
15,893,046✔
7610
            {
7611
                for (int j = 0; j < zone->nb_cards; j++)
2,125,237✔
7612
                {
7613
                     MTGCardInstance * card = zone->cards[j];
1,222,800✔
7614
                    if (canBeInList(card))
1,222,800✔
7615
                    {
7616
                        if (cards.find(card) == cards.end())
117,440✔
7617
                        {
7618
                            temp[card] = true;
7,474✔
7619
                        }
7620
                    }
7621
                }
7622
            }
7623
        }
7624
    }
7625

7626
    for (map<MTGCardInstance *, bool>::iterator it = temp.begin(); it != temp.end(); ++it)
890,421✔
7627
    {
7628
        MTGCardInstance * card = (*it).first;
7,474✔
7629
        cards[card] = true;
7,474✔
7630
        added(card);
7,474✔
7631
    }
7632

7633
    temp.clear();
882,947✔
7634

7635
    for (int i = 0; i < 2; ++i)
2,648,841✔
7636
    {
7637
        Player * p = game->players[i];
1,765,894✔
7638
        if (!players[p] && canBeInList(p))
1,765,894✔
7639
        {
7640
            players[p] = true;
19✔
7641
            added(p);
19✔
7642
        }
7643
        else if (players[p] && !canBeInList(p))
1,765,875✔
7644
        {
7645
            players[p] = false;
×
7646
            removed(p);
×
7647
        }
7648
    }
7649

7650
}
882,947✔
7651

7652
void ListMaintainerAbility::checkTargets()
×
7653
{
7654
    //remove invalid ones
7655
    map<MTGCardInstance *, bool> tempCheck;
×
7656
    for (map<MTGCardInstance *, bool>::iterator it = checkCards.begin(); it != checkCards.end(); ++it)
×
7657
    {
7658
        MTGCardInstance * card = (*it).first;
×
7659
        if (!canBeInList(card) || card->mPropertiesChangedSinceLastUpdate)
×
7660
        {
7661
            tempCheck[card] = true;
×
7662
        }
7663
    }
7664

7665
    for (map<MTGCardInstance *, bool>::iterator it = tempCheck.begin(); it != tempCheck.end(); ++it)
×
7666
    {
7667
        MTGCardInstance * card = (*it).first;
×
7668
        checkCards.erase(card);
×
7669
    }
7670

7671
    tempCheck.clear();
×
7672

7673
    //add New valid ones
7674
    for (int i = 0; i < 2; i++)
×
7675
    {
7676
        Player * p = game->players[i];
×
7677
        MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->stack, p->game->exile, p->game->commandzone, p->game->sideboard, p->game->reveal };
×
7678
        for (int k = 0; k < 9; k++)
×
7679
        {
7680
            MTGGameZone * zone = zones[k];
×
7681
            if (canTarget(zone))
×
7682
            {
7683
                for (int j = 0; j < zone->nb_cards; j++)
×
7684
                {
7685
                     MTGCardInstance * card = zone->cards[j];
×
7686
                    if (canBeInList(card))
×
7687
                    {
7688
                        if (checkCards.find(card) == checkCards.end())
×
7689
                        {
7690
                            tempCheck[card] = true;
×
7691
                        }
7692
                    }
7693
                }
7694
            }
7695
        }
7696
    }
7697
    for (map<MTGCardInstance *, bool>::iterator it = tempCheck.begin(); it != tempCheck.end(); ++it)
×
7698
    {
7699
        MTGCardInstance * card = (*it).first;
×
7700
        checkCards[card] = true;
×
7701
    }
7702
}
7703

7704
void ListMaintainerAbility::Update(float)
877,278✔
7705
{
7706
    updateTargets();
877,278✔
7707
}
877,278✔
7708

7709
//Destroy the spell -> remove all targets
7710
int ListMaintainerAbility::destroy()
7,513✔
7711
{
7712
    map<MTGCardInstance *, bool>::iterator it = cards.begin();
7,513✔
7713

7714
    while (it != cards.end())
8,791✔
7715
    {
7716
        MTGCardInstance * card = (*it).first;
639✔
7717
        cards.erase(card);
639✔
7718
        if(!card->mutation) // Fix crash on mutating card...
639✔
7719
            removed(card);
639✔
7720
        it = cards.begin();
639✔
7721
    }
7722
    return 1;
7,513✔
7723
}
7724

7725
ostream& ListMaintainerAbility::toString(ostream& out) const
×
7726
{
7727
    out << "ListMaintainerAbility ::: (";
×
7728
    return MTGAbility::toString(out) << ")";
×
7729
}
7730

7731
TriggerAtPhase::TriggerAtPhase(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, int _phaseId, int who, bool sourceUntapped, bool sourceTap, bool lifelost, int lifeamount, bool once) :
37,739✔
7732
    TriggeredAbility(observer, id, source, target), phaseId(_phaseId), who(who), sourceUntapped(sourceUntapped), sourceTap(sourceTap),lifelost(lifelost),lifeamount(lifeamount),once(once)
37,739✔
7733
{
7734
    activeTrigger = true;
37,739✔
7735
    if (game)
37,739✔
7736
    {
7737
        newPhase = game->getCurrentGamePhase();
37,739✔
7738
        currentPhase = newPhase;
37,739✔
7739
    }
7740
}
37,739✔
7741

7742
    int TriggerAtPhase::trigger()
4,408,590✔
7743
    {
7744
        if(!activeTrigger) return 0;
4,408,590✔
7745
        if(source->isPhased) return 0;
4,408,590✔
7746
        if(lifelost)
4,408,590✔
7747
        {
7748
            int lifeloss = source->controller()->opponent()->lifeLostThisTurn;
×
7749
            if(lifeloss < lifeamount)
×
7750
                return 0;
×
7751
        }
7752
        if (sourceUntapped  && source->isTapped() == 1)
4,408,590✔
7753
            return 0;
×
7754
        if (sourceTap  && !source->isTapped())
4,408,590✔
7755
            return 0;
×
7756
        if (testDestroy())
4,408,590✔
7757
            return 0; // http://code.google.com/p/wagic/issues/detail?id=426
154✔
7758
        int result = 0;
4,408,436✔
7759
        if (currentPhase != newPhase && newPhase == phaseId)
4,408,436✔
7760
        {
7761
        result = 0;
17,133✔
7762
        switch (who)
17,133✔
7763
        {
7764
        case 1:
7765
            if (game->currentPlayer == source->controller())
525✔
7766
                result = 1;
277✔
7767
            break;
525✔
7768
        case -1:
7769
            if (game->currentPlayer != source->controller())
19✔
7770
                result = 1;
18✔
7771
            break;
19✔
7772
        case -2:
7773
            if (source->target)
14✔
7774
            {
7775
                if (game->currentPlayer == source->target->controller())
14✔
7776
                    result = 1;
7✔
7777
            }
7778
            else
7779
            {
7780
                if (game->currentPlayer == source->controller())
×
7781
                    result = 1;
×
7782
            }
7783
            break;
14✔
7784
        case -3:
7785
            if (source->playerTarget)
1✔
7786
            {
7787
                if (game->currentPlayer == source->playerTarget)
1✔
7788
                    result = 1;
1✔
7789
            }
7790
            break;
1✔
7791
        default:
7792
            result = 1;
16,574✔
7793
            break;
16,574✔
7794
        }
7795
        if(castRestriction.size())
17,133✔
7796
        {
7797
            if(!source)
8,039✔
7798
                result = 1;//can't check these restrictions without a source aka:in a rule.txt
×
7799
            AbilityFactory af(game);
16,078✔
7800
            int checkCond = af.parseCastRestrictions(source,source->controller(),castRestriction);
8,039✔
7801
            if(!checkCond)
8,039✔
7802
                result = 0;
8,018✔
7803
        }
7804

7805
    }
7806
        if(once && activeTrigger)
4,408,436✔
7807
            activeTrigger = false;
×
7808

7809
    return result;
4,408,436✔
7810
}
7811

7812
TriggerAtPhase* TriggerAtPhase::clone() const
×
7813
{
7814
    return NEW TriggerAtPhase(*this);
×
7815
}
7816

7817
TriggerNextPhase::TriggerNextPhase(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, int _phaseId, int who, bool sourceUntapped, bool sourceTap, bool once) :
5✔
7818
    TriggerAtPhase(observer, id, source, target, _phaseId, who, sourceUntapped, sourceTap, once)
5✔
7819
{
7820
    destroyActivated = 0;
5✔
7821
    activeTrigger = true;
5✔
7822
}
5✔
7823

7824
int TriggerNextPhase::testDestroy()
1,080✔
7825
{
7826
    //dirty hack because of http://code.google.com/p/wagic/issues/detail?id=426
7827
    if (newPhase <= phaseId && !destroyActivated)
1,080✔
7828
        destroyActivated = 1;
5✔
7829
    if (destroyActivated > 1 || (newPhase > phaseId && destroyActivated))
1,080✔
7830
    {
7831
        destroyActivated++;
145✔
7832
        return 1;
145✔
7833
    }
7834
    return 0;
935✔
7835
}
7836

7837
TriggerNextPhase* TriggerNextPhase::clone() const
×
7838
{
7839
    return NEW TriggerNextPhase(*this);
×
7840
}
7841

7842
TriggerRebound::TriggerRebound(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, int _phaseId, int who, bool sourceUntapped, bool sourceTap, bool once) :
×
7843
    TriggerAtPhase(observer, id, source, target, _phaseId, who, sourceUntapped, sourceTap, once)
×
7844
{
7845
    destroyActivated = 0;
×
7846
    activeTrigger = true;
×
7847
}
7848

7849
int TriggerRebound::testDestroy()
×
7850
{
7851
    if(newPhase <= phaseId && !destroyActivated && game->currentPlayer == source->controller())
×
7852
        destroyActivated=1;
×
7853
    if(destroyActivated > 1||(newPhase > phaseId && destroyActivated))
×
7854
    {
7855
        destroyActivated++;
×
7856
        return 1;
×
7857
    }
7858
    return 0;
×
7859
}
7860

7861
TriggerRebound* TriggerRebound::clone() const
×
7862
{
7863
    return NEW TriggerRebound(*this);
×
7864
}
7865

7866
GenericTriggeredAbility::GenericTriggeredAbility(GameObserver* observer, int id, MTGCardInstance * _source, TriggeredAbility * _t, MTGAbility * a,
43,727✔
7867
                MTGAbility * dc, Targetable * _target) :
7868
    TriggeredAbility(observer, id, _source, _target), NestedAbility(a)
43,727✔
7869
{
7870
    if (!target)
43,727✔
7871
        target = source;
×
7872
    t = _t;
43,727✔
7873
    destroyCondition = dc;
43,727✔
7874

7875
    t->source = source;
43,727✔
7876
    t->target = target;
43,727✔
7877
    ability->source = source;
43,727✔
7878
    ability->target = target;
43,727✔
7879
    if (destroyCondition)
43,727✔
7880
    {
7881
        destroyCondition->source = source;
×
7882
        destroyCondition->target = target;
×
7883
    }
7884
}
43,727✔
7885

7886
int GenericTriggeredAbility::trigger()
5,106,279✔
7887
{
7888
    return t->trigger();
5,106,279✔
7889
}
7890

7891
int GenericTriggeredAbility::triggerOnEvent(WEvent * e)
4,025,087✔
7892
{
7893
    if (t->triggerOnEvent(e))
4,025,087✔
7894
    {
7895
        targets.push(getTriggerTarget(e, ability));
150✔
7896
        return 1;
150✔
7897
    }
7898
    return 0;
4,024,937✔
7899
}
7900

7901
Targetable * GenericTriggeredAbility::getTriggerTarget(WEvent * e, MTGAbility * a)
217✔
7902
{
7903
    TriggerTargetChooser * ttc = dynamic_cast<TriggerTargetChooser *> (a->getActionTc());
217✔
7904
    if (ttc)
217✔
7905
        return e->getTarget(ttc->triggerTarget);
21✔
7906

7907
    NestedAbility * na = dynamic_cast<NestedAbility *> (a);
196✔
7908
    if (na)
196✔
7909
        return getTriggerTarget(e, na->ability);
56✔
7910

7911
    MultiAbility * ma = dynamic_cast<MultiAbility *> (a);
140✔
7912
    if (ma)
140✔
7913
    {
7914
        for (size_t i = 0; i < ma->abilities.size(); i++)
11✔
7915
        {
7916
            return getTriggerTarget(e, ma->abilities[i]);
11✔
7917
        }
7918
    }
7919

7920
    return NULL;
129✔
7921
}
7922

7923
void GenericTriggeredAbility::setTriggerTargets(Targetable * ta, MTGAbility * a)
254✔
7924
{
7925
    TriggerTargetChooser * ttc = dynamic_cast<TriggerTargetChooser *> (a->getActionTc());
254✔
7926
    if (ttc)
254✔
7927
    {
7928
        a->target = ta;
21✔
7929
        ttc->target = ta;
21✔
7930
    }
7931

7932
    NestedAbility * na = dynamic_cast<NestedAbility *> (a);
254✔
7933
    if (na)
254✔
7934
        setTriggerTargets(ta, na->ability);
83✔
7935

7936
    MultiAbility * ma = dynamic_cast<MultiAbility *> (a);
254✔
7937
    if (ma)
254✔
7938
    {
7939
        for (size_t i = 0; i < ma->abilities.size(); i++)
33✔
7940
        {
7941
            setTriggerTargets(ta, ma->abilities[i]);
22✔
7942
        }
7943
    }
7944
}
254✔
7945

7946
void GenericTriggeredAbility::Update(float dt)
5,106,279✔
7947
{
7948
    GamePhase newPhase = game->getCurrentGamePhase();
5,106,279✔
7949
    t->newPhase = newPhase;
5,106,279✔
7950
    TriggeredAbility::Update(dt);
5,106,279✔
7951
    t->currentPhase = newPhase;
5,106,279✔
7952
}
5,106,279✔
7953

7954
int GenericTriggeredAbility::resolve()
9,009✔
7955
{
7956
    if(source->isPhased) return 0;
9,009✔
7957
    if (targets.size())
9,009✔
7958
    {
7959
        setTriggerTargets(targets.front(), ability);
149✔
7960
        targets.pop();
149✔
7961
    }
7962
    if (ability->oneShot)
9,009✔
7963
        return ability->resolve();
8,958✔
7964
    MTGAbility * clone = ability->clone();
51✔
7965
    clone->addToGame();
51✔
7966
    return 1;
51✔
7967
}
7968

7969
int GenericTriggeredAbility::testDestroy()
5,106,337✔
7970
{
7971
    if (!TriggeredAbility::testDestroy())
5,106,337✔
7972
        return 0;
5,105,862✔
7973
    if (destroyCondition)
475✔
7974
        return (destroyCondition->testDestroy());
×
7975
    return t->testDestroy();
475✔
7976
}
7977

7978
GenericTriggeredAbility::~GenericTriggeredAbility()
131,181✔
7979
{
7980
    delete t;
43,727✔
7981
    delete ability;
43,727✔
7982
    SAFE_DELETE(destroyCondition);
43,727✔
7983
}
87,454✔
7984

7985
const string GenericTriggeredAbility::getMenuText()
×
7986
{
7987
    return ability->getMenuText();
×
7988
}
7989

7990
GenericTriggeredAbility* GenericTriggeredAbility::clone() const
×
7991
{
7992
    GenericTriggeredAbility * a =  NEW GenericTriggeredAbility(*this);
×
7993
    a->t = t->clone();
×
7994
    a->ability = ability->clone();
×
7995
    a->destroyCondition = destroyCondition->clone();
×
7996
    return a;
×
7997
}
7998

7999
/*Mana Producers (lands)
8000
 //These have a reactToClick function, and therefore two manaProducers on the same card conflict with each other
8001
 //That means the player has to choose one. although that is perfect for cards such as birds of paradise or badlands,
8002
 other solutions need to be provided for abilities that add mana (ex: mana flare)
8003
 */
8004

8005
AManaProducer::AManaProducer(GameObserver* observer, int id, MTGCardInstance * card, Targetable * t, ManaCost * _output, ManaCost * _cost,
7,375✔
8006
                int who,string producing, bool doesntEmpty) :
8007
    ActivatedAbilityTP(observer, id, card, t, _cost, who)
7,375✔
8008
{
8009

8010
    aType = MTGAbility::MANA_PRODUCER;
7,375✔
8011
    setCost(_cost);
7,375✔
8012
    output = _output;
7,375✔
8013
    tap = 0;
7,375✔
8014
    Producing = producing;
7,375✔
8015
    menutext = "";
7,375✔
8016
    DoesntEmpty = doesntEmpty;
7,375✔
8017
    andAbility = NULL;
7,375✔
8018
}
7,375✔
8019

8020
int AManaProducer::isReactingToClick(MTGCardInstance * _card, ManaCost * mana)
2,574✔
8021
{
8022
    int result = 0;
2,574✔
8023
    if (!mana)
2,574✔
8024
        mana = game->currentlyActing()->getManaPool();
2,574✔
8025
    //please do not condense the following, I broke it apart for readability, it was far to difficult to tell what exactly happened before with it all in a single line.
8026
    //and far to prone to bugs.
8027
    if (_card == source)
2,574✔
8028
    {
8029
        if (!tap || (tap && (!source->isTapped() && !source->hasSummoningSickness())))
595✔
8030
        {
8031
            if (game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness()))
590✔
8032
            {
8033
                if (!source->isPhased)
590✔
8034
                {
8035
                    ManaCost * cost = getCost();
590✔
8036
                    if (!cost || (mana->canAfford(cost,_card->has(Constants::ANYTYPEOFMANAABILITY)) && (!cost->extraCosts || cost->extraCosts->canPay())))/*counter cost bypass react to click*/
590✔
8037
                    {
8038
                        result = 1;
590✔
8039
                    }
8040
                }
8041
            }
8042
        }
8043
    }
8044
    return result;
2,574✔
8045
}
8046

8047
int AManaProducer::resolve()
283✔
8048
{
8049
    Targetable * _target = getTarget();
283✔
8050
    Player * player = getPlayerFromTarget(_target);
283✔
8051
    if (!player)
283✔
8052
        return 0;
×
8053
    
8054
    player->getManaPool()->add(output, source);
283✔
8055
    if(DoesntEmpty)
283✔
8056
        player->doesntEmpty->add(output);
×
8057
    source->getProducedMana()->copy(output);
283✔
8058
    WEventCardManaProduced * ev = NEW WEventCardManaProduced(source);
283✔
8059
    if(ev)
283✔
8060
        source->getObserver()->receiveEvent(ev);
283✔
8061
    if(andAbility)
283✔
8062
    {
8063
        MTGAbility * andAbilityClone = andAbility->clone();
1✔
8064
        andAbilityClone->target = source;
1✔
8065
        if(andAbility->oneShot)
1✔
8066
        {
8067
            andAbilityClone->resolve();
1✔
8068
            SAFE_DELETE(andAbilityClone);
1✔
8069
        }
8070
        else
8071
        {
8072
            andAbilityClone->addToGame();
×
8073
        }
8074
    }
8075
    return 1;
283✔
8076
}
8077

8078
int AManaProducer::reactToClick(MTGCardInstance * _card)
267✔
8079
{
8080
    if (!isReactingToClick(_card))
267✔
8081
        return 0;
14✔
8082
    if(!ActivatedAbility::isReactingToClick(_card))
253✔
8083
        return 0;
×
8084

8085
    ManaCost * cost = getCost();
253✔
8086
    if (cost)
253✔
8087
    {
8088
        cost->setExtraCostsAction(this, _card);
253✔
8089
        if (!cost->isExtraPaymentSet())
253✔
8090
        {
8091
            game->mExtraPayment = cost->extraCosts;
×
8092
            return 0;
×
8093
        }
8094
        if(cost->extraCosts){ // Added to check if the snow mana amount is enough to pay all the snow cost.
253✔
8095
            int countSnow = 0;
253✔
8096
            for(unsigned int i = 0; i < cost->extraCosts->costs.size(); i++){
506✔
8097
                if(dynamic_cast<SnowCost*> (cost->extraCosts->costs[i]))
253✔
8098
                    countSnow++;
×
8099
            }
8100
            if((source->controller()->snowManaG + source->controller()->snowManaU + source->controller()->snowManaR + 
759✔
8101
                source->controller()->snowManaB + source->controller()->snowManaW + source->controller()->snowManaC) < countSnow){
506✔
8102
                game->mExtraPayment = cost->extraCosts;
×
8103
                return 0;
×
8104
            }
8105
        }
8106
        cost->doPayExtra(); // Bring here brefore the normal payment to solve Snow Mana payment bug.
253✔
8107
        game->currentlyActing()->getManaPool()->pay(cost);
253✔
8108
    }
8109

8110
    if (options[Options::SFXVOLUME].number > 0)
253✔
8111
    {
8112
        WResourceManager::Instance()->PlaySample("mana.wav");
×
8113
    }
8114
    if(Producing.size())
253✔
8115
        if(Producing.find("manapool") != string::npos)//unique card doubling cube.
253✔
8116
            output->copy(source->controller()->getManaPool());
×
8117
    return ActivatedAbility::activateAbility();
253✔
8118
}
8119

8120
const string AManaProducer::getMenuText()
49✔
8121
{
8122
    if (menutext.size())
49✔
8123
        return menutext.c_str();
7✔
8124
    menutext = _("Add ");
42✔
8125
    char buffer[128];
8126
    int alreadyHasOne = 0;
42✔
8127
    for (int i = 0; i < 8; i++)
378✔
8128
    {
8129
        int value = output->getCost(i);
336✔
8130
        if (value)
336✔
8131
        {
8132
            if (alreadyHasOne)
42✔
8133
                menutext.append(",");
×
8134
            sprintf(buffer, "%i ", value);
42✔
8135
            menutext.append(buffer);
42✔
8136
            if (i == Constants::MTG_COLOR_WASTE)
42✔
8137
                menutext.append(_(" colorless"));
×
8138
            else if (i >= Constants::MTG_COLOR_GREEN && i <= Constants::MTG_COLOR_WASTE)
42✔
8139
                menutext.append(_(Constants::MTGColorStrings[i]));
37✔
8140
            
8141
            alreadyHasOne = 1;
42✔
8142
        }
8143
    }
8144
    menutext.append(_(" mana"));
42✔
8145
    return menutext.c_str();
42✔
8146
}
8147

8148
AManaProducer::~AManaProducer()
23,574✔
8149
{
8150
    SAFE_DELETE(output);
7,858✔
8151
}
15,716✔
8152

8153
AManaProducer * AManaProducer::clone() const
483✔
8154
{
8155
    AManaProducer * a = NEW AManaProducer(*this);
483✔
8156
    a->output = NEW ManaCost();
483✔
8157
    a->output->copy(output);
483✔
8158
    return a;
483✔
8159
}
8160

8161

8162
AbilityTP::AbilityTP(GameObserver* observer, int id, MTGCardInstance * card, Targetable * _target, int who) :
1,455✔
8163
    MTGAbility(observer, id, card), who(who)
1,455✔
8164
{
8165
    if (_target)
1,455✔
8166
        target = _target;
3✔
8167
}
1,455✔
8168

8169
Targetable * AbilityTP::getTarget()
1,454✔
8170
{
8171
    switch (who)
1,454✔
8172
    {
8173
    case TargetChooser::TARGET_CONTROLLER:
8174
        return getPlayerFromTarget(target);
×
8175
    case TargetChooser::CONTROLLER:
8176
        return source->controller();
×
8177
    case TargetChooser::OPPONENT:
8178
        return source->controller()->opponent();
1✔
8179
    case TargetChooser::OWNER:
8180
        return source->owner;
×
8181
    case TargetChooser::TARGETED_PLAYER:
8182
        return source->playerTarget;
×
8183
    default:
8184
        return target;
1,453✔
8185
    }
8186
    return NULL;
8187
}
8188

8189
ActivatedAbilityTP::ActivatedAbilityTP(GameObserver* observer, int id, MTGCardInstance * card, Targetable * _target, ManaCost * cost, int who) :
28,070✔
8190
    ActivatedAbility(observer, id, card, cost, 0), who(who)
28,070✔
8191
{
8192
    if (_target)
28,070✔
8193
        target = _target;
156✔
8194
}
28,070✔
8195

8196
Targetable * ActivatedAbilityTP::getTarget()
10,348✔
8197
{
8198
    switch (who)
10,348✔
8199
    {
8200
    case TargetChooser::TARGET_CONTROLLER:
8201
        return getPlayerFromTarget(target);
19✔
8202
    case TargetChooser::CONTROLLER:
8203
        return source->controller();
95✔
8204
    case TargetChooser::OPPONENT:
8205
        return source->controller()->opponent();
23✔
8206
    case TargetChooser::OWNER:
8207
        return source->owner;
×
8208
    case TargetChooser::TARGETED_PLAYER:
8209
        return source->playerTarget;
1✔
8210
    default:
8211
        return target;
10,210✔
8212
    }
8213
    return NULL;
8214
}
8215

8216
InstantAbilityTP::InstantAbilityTP(GameObserver* observer, int id, MTGCardInstance * card, Targetable * _target,int who) :
6✔
8217
    InstantAbility(observer, id, card), who(who)
6✔
8218
{
8219
    if (_target)
6✔
8220
        target = _target;    
3✔
8221
}
6✔
8222

8223
//This is the same as Targetable * ActivatedAbilityTP::getTarget(), anyway to move them together?
8224
Targetable * InstantAbilityTP::getTarget()
×
8225
{
8226
    switch (who)
×
8227
    {
8228
    case TargetChooser::TARGET_CONTROLLER:
8229
        return getPlayerFromTarget(target);
×
8230
    case TargetChooser::CONTROLLER:
8231
        return source->controller();
×
8232
    case TargetChooser::OPPONENT:
8233
        return source->controller()->opponent();
×
8234
    case TargetChooser::OWNER:
8235
        return source->owner;
×
8236
    default:
8237
        return target;
×
8238
    }
8239
    return NULL;
8240
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc