From 01fd8fa406010d6f97ac073d36f282482b3ef0a8 Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Wed, 13 Jul 2016 23:57:30 -0400 Subject: [PATCH 1/6] recoded cascade, the logic was incorrect. also the ability was coded incorrectly. ALL the cards get exiled, then placed on the bottom, the card that cost less is also exiled then MAY be cast otherwise its placed in the library bottom with the rest. I also now tell AI to ALWAYS use cast card if available. as the effects are always better. --- projects/mtg/src/AIPlayerBaka.cpp | 3 + projects/mtg/src/AllAbilities.cpp | 122 +++++++++++++++++------------- projects/mtg/src/ExtraCost.cpp | 2 + 3 files changed, 74 insertions(+), 53 deletions(-) diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 4262b0624..c1f792a09 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -104,6 +104,9 @@ int OrderedAIAction::getEfficiency() { target = a->source; } + + if (AACastCard * CC = dynamic_cast (a)) + return 99; switch (a->aType) { diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 0369afc0c..83056f210 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -1144,75 +1144,89 @@ AACascade::AACascade(GameObserver* observer, int _id, MTGCardInstance * _source, oldOrder.clear(); newOrder.clear(); } - int AACascade::resolve() +int AACascade::resolve() +{ + Player * player = source->controller(); + if (!player) + return 0; + WParsedInt numCards(nbcardsStr, NULL, source); + MTGLibrary * library = player->game->library; + MTGRemovedFromGame * exile = player->game->exile; + MTGCardInstance * viable = NULL; + int counter = 0; + bool found = false; + for (int i = 0; i < numCards.getValue(); i++) { - Player * player = source->controller(); - if (player) + //*//*//*// + if (found) + continue; + ////////////////////////////////////////////// + if (!library->nb_cards) + continue; + ////////////////////////////////////////////// + while (library->nb_cards && !found) { - WParsedInt numCards(nbcardsStr, NULL, source); - MTGLibrary * library = player->game->library; - MTGRemovedFromGame * exile = player->game->exile; - MTGCardInstance * viable = NULL; - int counter = 0; - for (int i = 0; i < numCards.getValue(); i++) + viable = library->cards[library->nb_cards -1]; + if (!found) { - if (library->nb_cards) + if (!viable->isLand() && (viable->getManaCost()->getConvertedCost() < source->getManaCost()->getConvertedCost())) { - for(int z = library->nb_cards-1; z >= 0; z--) - { - if(!library->cards[z]->isLand() && (library->cards[z]->getManaCost()->getConvertedCost() < source->getManaCost()->getConvertedCost())) - { - viable = library->cards[z]; - player->game->putInZone(viable, library, exile); - { - for(int j=0; j < library->nb_cards; j++) - { - if(library->cards[j]->isCascaded) - { - library->cards[j]->isCascaded = false; - selectedCards.push_back(library->cards[j]); - } - } - if(selectedCards.size()) - { - std::random_shuffle ( selectedCards.begin(), selectedCards.end() ); - for(unsigned int i = 0; i < selectedCards.size();++i) - { - oldOrder = library->cards; - newOrder.push_back(selectedCards[i]); - for(unsigned int k = 0;k < oldOrder.size();++k) - { - MTGCardInstance * rearranged = oldOrder[k]; - if(rearranged != selectedCards[i]) - newOrder.push_back(rearranged); - } - library->cards = newOrder; - } - } - } - toCastCard(viable->next); - return 1; - } - else - { - library->cards[library->nb_cards - 1]->isCascaded=true; - counter++; - } - } + + toCastCard(viable); + viable = player->game->putInZone(viable, library, exile); + viable->isCascaded = true; + found = true; + } + else + { + viable = player->game->putInZone(viable, library, exile); + viable->isCascaded = true; + counter++; } } } - return 1; + + //*//*//*//* } + //////////////////////////////////////////// + for (int j = 0; j < exile->nb_cards; j++) + { + if (exile->cards[j]->isCascaded) + { + MTGCardInstance * CardToPutBack = exile->cards[j];//player->game->putInZone(exile->cards[j], exile, library); + CardToPutBack->isCascaded = false; + selectedCards.push_back(CardToPutBack); + } + } + ////////////////////////////////////////// + if (selectedCards.size()) + { + do + { + MTGCardInstance * toMove = selectedCards.back(); + if (toMove) + { + MTGAbility * a = NEW AALibraryBottom(game, game->mLayers->actionLayer()->getMaxId(), source, toMove); + a->oneShot = 1; + a->resolve(); + SAFE_DELETE(a); + selectedCards.pop_back(); + } + } while (selectedCards.size()); + } + ////////////////////////////////////// + return 1; +} void AACascade::toCastCard(MTGCardInstance * thisCard) { MTGAbility *ac = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), thisCard, thisCard,false,false,true,"","",false,false); - MayAbility *ma1 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ac->clone(), thisCard,true); + MayAbility *ma1 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ac->clone(), thisCard,false); MTGAbility *ga1 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), thisCard,NULL,ma1->clone()); SAFE_DELETE(ac); SAFE_DELETE(ma1); ga1->resolve(); + SAFE_DELETE(ga1); return; } @@ -7291,6 +7305,7 @@ MTGCardInstance * AACastCard::makeCard() MTGCardInstance * card = NULL; MTGCard * cardData = MTGCollection()->getCardByName(cardNamed); card = NEW MTGCardInstance(cardData, source->controller()->game); + card->owner = source->controller(); source->controller()->game->temp->addCard(card); return card; } @@ -7311,6 +7326,7 @@ int AACastCard::resolveSpell() MTGCard * cardToCopy = MTGCollection()->getCardById(_target->getId()); MTGCardInstance * myDummy = NULL; myDummy = NEW MTGCardInstance(cardToCopy, source->controller()->game); + myDummy->setObserver(source->controller()->getObserver()); source->controller()->game->garbage->addCard(myDummy); _target = myDummy; _target->isToken = 1; diff --git a/projects/mtg/src/ExtraCost.cpp b/projects/mtg/src/ExtraCost.cpp index fbae3701c..33efc8d53 100644 --- a/projects/mtg/src/ExtraCost.cpp +++ b/projects/mtg/src/ExtraCost.cpp @@ -249,6 +249,8 @@ LifeCost::LifeCost(TargetChooser *_tc) int LifeCost::canPay() { MTGCardInstance * _target = (MTGCardInstance *) target; + if (!_target) + return 0; if (_target->controller()->life <= 0 || _target->controller()->inPlay()->hasAbility(Constants::CANTCHANGELIFE) || _target->controller()->opponent()->game->battlefield->hasAbility(Constants::CANTPAYLIFE) || _target->controller()->game->battlefield->hasAbility(Constants::CANTPAYLIFE)) From e81346f8812c1003c234fe396ef8ba3a5dfaa5b5 Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Thu, 14 Jul 2016 20:28:19 -0400 Subject: [PATCH 2/6] more work on cascade, I think I figured out what was causing a weird crash. --- projects/mtg/include/AllAbilities.h | 1 + projects/mtg/include/GameObserver.h | 1 + projects/mtg/include/MTGCardInstance.h | 1 + projects/mtg/src/AllAbilities.cpp | 13 +++++++++--- projects/mtg/src/GameObserver.cpp | 10 +++++++++ projects/mtg/src/MTGAbility.cpp | 28 ++++++++++++++++++++------ 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 392c89eb4..5681d7994 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -6208,6 +6208,7 @@ class AACascade: public ActivatedAbility { public: string nbcardsStr; + MTGCardInstance * castingThis; vectorselectedCards; vectoroldOrder; vectornewOrder; diff --git a/projects/mtg/include/GameObserver.h b/projects/mtg/include/GameObserver.h index 6b5fddb79..2f520043c 100644 --- a/projects/mtg/include/GameObserver.h +++ b/projects/mtg/include/GameObserver.h @@ -133,6 +133,7 @@ class GameObserver{ int isInPlay(MTGCardInstance * card); int isInGrave(MTGCardInstance * card); int isInExile(MTGCardInstance * card); + int isInHand(MTGCardInstance * card); virtual void Update(float dt); void Render(); void ButtonPressed(PlayGuiObject*); diff --git a/projects/mtg/include/MTGCardInstance.h b/projects/mtg/include/MTGCardInstance.h index 4a59bb206..b06b2e656 100644 --- a/projects/mtg/include/MTGCardInstance.h +++ b/projects/mtg/include/MTGCardInstance.h @@ -92,6 +92,7 @@ public: bool isPhased; bool isCascaded; int phasedTurn; + bool handEffects; bool graveEffects; bool exileEffects; bool suspended; diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 83056f210..e5ced563c 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -1143,6 +1143,7 @@ AACascade::AACascade(GameObserver* observer, int _id, MTGCardInstance * _source, selectedCards.clear(); oldOrder.clear(); newOrder.clear(); + castingThis = NULL; } int AACascade::resolve() { @@ -1171,10 +1172,9 @@ int AACascade::resolve() { if (!viable->isLand() && (viable->getManaCost()->getConvertedCost() < source->getManaCost()->getConvertedCost())) { - - toCastCard(viable); viable = player->game->putInZone(viable, library, exile); viable->isCascaded = true; + castingThis = viable; found = true; } else @@ -1193,7 +1193,7 @@ int AACascade::resolve() { if (exile->cards[j]->isCascaded) { - MTGCardInstance * CardToPutBack = exile->cards[j];//player->game->putInZone(exile->cards[j], exile, library); + MTGCardInstance * CardToPutBack = exile->cards[j];; CardToPutBack->isCascaded = false; selectedCards.push_back(CardToPutBack); } @@ -1213,6 +1213,13 @@ int AACascade::resolve() selectedCards.pop_back(); } } while (selectedCards.size()); + + if (castingThis) + { + while (castingThis->next) + castingThis = castingThis->next; + toCastCard(castingThis); + } } ////////////////////////////////////// return 1; diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index faa869fdb..7cca9d03e 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -1568,7 +1568,17 @@ int GameObserver::isInExile(MTGCardInstance * card) } return 0; } +int GameObserver::isInHand(MTGCardInstance * card) +{ + for (int i = 0; i < 2; i++) + { + MTGGameZone * hand = players[i]->game->hand; + if (players[i]->game->isInZone(card, hand)) + return 1; + } + return 0; +} void GameObserver::cleanupPhase() { currentPlayer->cleanupPhase(); diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index dbd525a22..42285fc82 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -3982,13 +3982,14 @@ int AbilityFactory::getAbilities(vector * v, Spell * spell, MTGCar { card->graveEffects = false; card->exileEffects = false; - + card->handEffects = false; for (int i = 0; i < 2; ++i) { MTGPlayerCards * zones = observer->players[i]->game; if (dest == zones->hand) { magicText = card->magicTexts["hand"]; + card->handEffects = true; break; } if (dest == zones->graveyard) @@ -4891,6 +4892,8 @@ int MTGAbility::testDestroy() return 1; if (forceDestroy == -1) return 0; + if (source->handEffects && game->isInHand(source)) + return 0; if(source->graveEffects && game->isInGrave(source)) return 0; if(source->exileEffects && game->isInExile(source)) @@ -5934,13 +5937,26 @@ int AManaProducer::isReactingToClick(MTGCardInstance * _card, ManaCost * mana) int result = 0; if (!mana) mana = game->currentlyActing()->getManaPool(); - if (_card == source && (!tap || !source->isTapped()) && game->currentlyActing()->game->inPlay->hasCard(source) - && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness()) && !source->isPhased) + //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. + //and far to prone to bugs. + if (_card == source) { - ManaCost * cost = getCost(); - if (!cost || (mana->canAfford(cost) && (!cost->extraCosts || cost->extraCosts->canPay())))/*counter cost bypass react to click*/ + if (!tap || (tap && !source->isTapped())) { - result = 1; + if (!source->hasSummoningSickness()) + { + if (game->currentlyActing()->game->inPlay->hasCard(source) && source->hasType(Subtypes::TYPE_LAND)) + { + if (!source->isPhased) + { + ManaCost * cost = getCost(); + if (!cost || (mana->canAfford(cost) && (!cost->extraCosts || cost->extraCosts->canPay())))/*counter cost bypass react to click*/ + { + result = 1; + } + } + } + } } } return result; From 9a56817bf84631b56e0ed08fcc623adc10ee5f09 Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Thu, 14 Jul 2016 20:44:21 -0400 Subject: [PATCH 3/6] fixed a bug I introduced --- projects/mtg/src/MTGAbility.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 42285fc82..1262e0a36 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -5945,8 +5945,8 @@ int AManaProducer::isReactingToClick(MTGCardInstance * _card, ManaCost * mana) { if (!source->hasSummoningSickness()) { - if (game->currentlyActing()->game->inPlay->hasCard(source) && source->hasType(Subtypes::TYPE_LAND)) - { + if (game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness())) + { if (!source->isPhased) { ManaCost * cost = getCost(); From 2fc52c9abd54c271329013ba1866eee502de2f51 Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Thu, 14 Jul 2016 21:25:04 -0400 Subject: [PATCH 4/6] changed suspend to use castcard. --- projects/mtg/src/Counters.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/projects/mtg/src/Counters.cpp b/projects/mtg/src/Counters.cpp index b01c57644..a08c4402e 100644 --- a/projects/mtg/src/Counters.cpp +++ b/projects/mtg/src/Counters.cpp @@ -2,6 +2,7 @@ #include "Counters.h" #include "MTGCardInstance.h" +#include "AllAbilities.h" Counter::Counter(MTGCardInstance * _target, int _power, int _toughness) { @@ -187,10 +188,13 @@ int Counters::removeCounter(const char * _name, int _power, int _toughness) if (target->suspended && !target->counters->hasCounter("time",0,0)) { GameObserver * game = target->getObserver(); - MTGCardInstance * copy = target->controller()->game->putInZone(target, target->currentZone, target->controller()->game->stack); - - game->mLayers->stackLayer()->addSpell(copy, game->targetChooser, NULL,1, 0); - game->targetChooser = NULL; + MTGAbility *ac = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), target, target, false, false, true, "", "", false, false); + MayAbility *ma1 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ac->clone(), target, true); + MTGAbility *ga1 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), target, NULL, ma1->clone()); + SAFE_DELETE(ac); + SAFE_DELETE(ma1); + ga1->resolve(); + SAFE_DELETE(ga1); } return mCount; } From 69158d55c1dd9594297bc11ae24be5e8795d33c2 Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Sat, 16 Jul 2016 14:46:40 -0400 Subject: [PATCH 5/6] more fixes --- projects/mtg/src/MTGAbility.cpp | 17 +++++++---------- projects/mtg/src/MTGCardInstance.cpp | 20 ++++++++++++++++---- projects/mtg/src/MTGGameZones.cpp | 2 +- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 1262e0a36..005b028c6 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -5941,19 +5941,16 @@ int AManaProducer::isReactingToClick(MTGCardInstance * _card, ManaCost * mana) //and far to prone to bugs. if (_card == source) { - if (!tap || (tap && !source->isTapped())) + if (!tap || (tap && (!source->isTapped() && !source->hasSummoningSickness()))) { - if (!source->hasSummoningSickness()) + if (game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness())) { - if (game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness())) - { - if (!source->isPhased) + if (!source->isPhased) + { + ManaCost * cost = getCost(); + if (!cost || (mana->canAfford(cost) && (!cost->extraCosts || cost->extraCosts->canPay())))/*counter cost bypass react to click*/ { - ManaCost * cost = getCost(); - if (!cost || (mana->canAfford(cost) && (!cost->extraCosts || cost->extraCosts->canPay())))/*counter cost bypass react to click*/ - { - result = 1; - } + result = 1; } } } diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index 67783aa97..3daf1646d 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -76,10 +76,22 @@ MTGCardInstance::MTGCardInstance(MTGCard * card, MTGPlayerCards * arg_belongs_to MTGCardInstance * MTGCardInstance::createSnapShot() { - MTGCardInstance * snapShot = NEW MTGCardInstance(*this); - snapShot->previous = NULL; - snapShot->counters = NEW Counters(snapShot); - controller()->game->garbage->addCard(snapShot); + //the below section of code was changed without all possible side effects checked + //the reason was becuase while NEW MTGCardInstance(*this); does indeed return an exact copy + //the lower layer cardprimitive data is pointed to from the original source. + //this would cause cards like lotus bloom, which contain a restriction, to already has deleted the restriction + //which belonged to the original card before getting to the safe_delete, + //it was leaving a dangling pointer which leads to + //a total crash on "cleanup()" calls from garbage zone. + //snapshots are created for extra cost, they are used for abilities contained after the cost through storecard variable. + //TODO:fix this correctly. I want this to use an exact copy of the card in its current state for stored. + //making it safe_delete these "copies" leads to the same crash, as they are still pointing to the original data. + MTGCardInstance * snapShot = this; + //below is how we used to handle this. + // MTGCardInstance * snapShot = NEW MTGCardInstance(*this); + //snapShot->previous = NULL; + // snapShot->counters = NEW Counters(snapShot); + //controller()->game->garbage->addCard(snapShot); return snapShot; } diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 5e664c3f8..6ea83a866 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -577,7 +577,7 @@ MTGCardInstance * MTGGameZone::removeCard(MTGCardInstance * card, int createCopy copy->storedSourceCard = card->storedSourceCard; copy->lastController = card->controller(); copy->previousController = card->controller(); - for (int i = 0; i < ManaCost::MANA_PAID_WITH_OVERLOAD +1; i++) + for (int i = 0; i < ManaCost::MANA_PAID_WITH_BESTOW +1; i++) copy->alternateCostPaid[i] = card->alternateCostPaid[i]; //stupid bug with tokens... From 542845667afa95093f048ce237d0c863a9721ab7 Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Sat, 16 Jul 2016 15:59:45 -0400 Subject: [PATCH 6/6] stupid case for travis --- projects/mtg/src/AIPlayerBaka.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index c1f792a09..a4d437c08 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -105,7 +105,8 @@ int OrderedAIAction::getEfficiency() target = a->source; } - if (AACastCard * CC = dynamic_cast (a)) + AACastCard * CC = dynamic_cast (a); + if (CC) return 99; switch (a->aType)