From 53b1c4a742e6cdef9e3d33395f54b7afdbcdb48b Mon Sep 17 00:00:00 2001 From: "omegablast2002@yahoo.com" Date: Sun, 25 Dec 2011 01:01:20 +0000 Subject: [PATCH] 2 major bug fixes and 1 minor fix first somehow accidentally line 3616 mtgability.cpp removefromgame was adding an observer instead of removing it, this explains "abilities sometimes acting strangely or not being removed"...I'm surprised it didn't create memleaks or extremely visible side-effects... 2nd fixed a bug where triggered abilities would share a menu with activated abilities of a card when ever you had enough mana floating to pay an activated ability before the trigger resolved. adjust the way ai calculates if it should use cards like wrath of god, though it is still open to using it at a random chance, i noticed that the method we use might not be the best. an ability with an eff of 1 for example actually has a 10% chance of being choosen.... lets say rng rolls 3402 when you % this it simply takes the 2 last numbers making this roll 2...meaning that unless we assign no "random chance to do blah" the actual chance of ai using a stupid ability is 10%...I'm leaving that logic how it is tho I "unfactored"(?) it to make it easier to track the numbers, also added a debug trace to help see how often we hit "lottery chance" ... fixed a minor crash from multiability trying to fetch menutext when no abilities existed in the vector anymore. this patch introduces a new subkeyword for "may " which is syntax pay(manacost) auto=may pay({w}) untap this is to allow the card group that was coded using the activated ability loophole i described at the start. it works the same way as it did with the loophole only it is actually something we want to happen instead of a flaw in the engine...you float the mana same as before and when the may line is triggered it will check if payment can be made with exist mana if so then it displays the menutext for the ability, if that ability is choosen it then charges you the mana directly before activation. this patch also include flip( ability, tho not intended originally for this version, I had previously had it finished and was polishing it right before I noticed the bugs above. since this keyword is not intended to add cards for this version, I wont go into massive details about it at this time. --- projects/mtg/include/ActionLayer.h | 1 + projects/mtg/include/AllAbilities.h | 13 +++ projects/mtg/include/CardPrimitive.h | 4 +- projects/mtg/include/MTGCardInstance.h | 1 + projects/mtg/src/AIPlayerBaka.cpp | 22 ++++- projects/mtg/src/ActionLayer.cpp | 29 +++++- projects/mtg/src/AllAbilities.cpp | 120 ++++++++++++++++++++++++- projects/mtg/src/CardGui.cpp | 14 ++- projects/mtg/src/MTGAbility.cpp | 55 ++++++++++-- projects/mtg/src/MTGCardInstance.cpp | 1 + 10 files changed, 242 insertions(+), 18 deletions(-) diff --git a/projects/mtg/include/ActionLayer.h b/projects/mtg/include/ActionLayer.h index c2bf50e5f..8c066c3df 100644 --- a/projects/mtg/include/ActionLayer.h +++ b/projects/mtg/include/ActionLayer.h @@ -24,6 +24,7 @@ public: vector garbage; Targetable * menuObject; SimpleMenu * abilitiesMenu; + SimpleMenu * abilitiesTriggered; MTGCardInstance * currentActionCard; int stuffHappened; virtual void Render(); diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index a3bb3b170..bad435579 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -880,6 +880,7 @@ public: bool must; Player * previousInterrupter; MTGAbility * mClone; + ManaCost * optionalCost; MayAbility(GameObserver* observer, int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must = false); @@ -3054,6 +3055,18 @@ public: const char * getMenuText(); AAMorph * clone() const; }; +/* flip*/ +class AAFlip: public InstantAbility +{ +public: + vector currentAbilities; + string flipStats; + AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats); + int resolve(); + int testDestroy(); + const char * getMenuText(); + AAFlip * clone() const; +}; /* dynamic ability build*/ class AADynamic: public ActivatedAbility { diff --git a/projects/mtg/include/CardPrimitive.h b/projects/mtg/include/CardPrimitive.h index cf6d197f1..c64a48217 100644 --- a/projects/mtg/include/CardPrimitive.h +++ b/projects/mtg/include/CardPrimitive.h @@ -43,8 +43,6 @@ class CardPrimitive #endif { private: - string text; - vector formattedText; CastRestrictions * restrictions; protected: @@ -52,6 +50,8 @@ protected: ManaCost manaCost; public: + vector formattedText; + string text; string name; int init(); diff --git a/projects/mtg/include/MTGCardInstance.h b/projects/mtg/include/MTGCardInstance.h index d872daf9e..67cd6fc18 100644 --- a/projects/mtg/include/MTGCardInstance.h +++ b/projects/mtg/include/MTGCardInstance.h @@ -80,6 +80,7 @@ public: bool morphed; bool turningOver; bool isMorphed; + bool isFlipped; bool isPhased; int phasedTurn; bool graveEffects; diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 195212e8b..44b4eb72a 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -1619,7 +1619,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty { TargetChooserFactory tcf(observer); TargetChooser * tc = tcf.createTargetChooser(card); - int shouldPlayPercentage = 10; + int shouldPlayPercentage = 0; if (tc) { int hasTarget = chooseTarget(tc,NULL,NULL,true); @@ -1662,8 +1662,18 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty } else if (BAKA_EFFECT_DONTKNOW == shouldPlay) { - shouldPlayPercentage = 80; + //previously shouldPlayPercentage = 80;, I found this a little to high + //for cards which AI had no idea how to use. + shouldPlayPercentage = 60; } + else + { + // shouldPlay == baka_effect_bad giving it a 1 for odd ball lottery chance. + shouldPlayPercentage = 1; + } + DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl + <<"shouldPlayPercentage = "<< shouldPlayPercentage); + } //Reduce the chances of playing a spell with X cost if available mana is low if (hasX) @@ -1694,8 +1704,14 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if(!canPlay) continue; } - if (randomGenerator.random() % 100 > shouldPlayPercentage) + int randomChance = randomGenerator.random(); + int chance = randomChance % 100; + if (chance > shouldPlayPercentage) continue; + if(shouldPlayPercentage < 10) + { + DebugTrace("shouldPlayPercentage was less than 10 this was a lottery roll on RNG"); + } nextCardToPlay = card; maxCost = currentCost; if (hasX) diff --git a/projects/mtg/src/ActionLayer.cpp b/projects/mtg/src/ActionLayer.cpp index 122052744..02ee4e6eb 100644 --- a/projects/mtg/src/ActionLayer.cpp +++ b/projects/mtg/src/ActionLayer.cpp @@ -5,6 +5,7 @@ #include "Targetable.h" #include "WEvent.h" #include "AllAbilities.h" +#include "MTGRules.h" MTGAbility* ActionLayer::getAbility(int type) { @@ -372,17 +373,42 @@ void ActionLayer::setMenuObject(Targetable * object, bool must) menuObject = object; SAFE_DELETE(abilitiesMenu); + abilitiesTriggered = NULL; abilitiesMenu = NEW SimpleMenu(observer->getInput(), 10, this, Fonts::MAIN_FONT, 100, 100, object->getDisplayName().c_str()); + abilitiesTriggered = NEW SimpleMenu(observer->getInput(), 10, this, Fonts::MAIN_FONT, 100, 100, object->getDisplayName().c_str()); currentActionCard = NULL; for (size_t i = 0; i < mObjects.size(); i++) { ActionElement * currentAction = (ActionElement *) mObjects[i]; if (currentAction->isReactingToTargetClick(object)) { - abilitiesMenu->Add(i, currentAction->getMenuText()); + if(dynamic_cast(currentAction)->getCost()||dynamic_cast(currentAction)) + { + abilitiesMenu->Add(i, currentAction->getMenuText()); + } + else + { + //the only time this condiation is hit is when we are about to display a menu of abilities + //which were triggered through a triggered ability or abilities such as multiple target( + //and may abilities appearing on cards ie: auto=may draw:1 + //this prevents abilities activated otherwise from displaying on the same menu as "triggered" and + //"put in play" abilities. an activated ability of a card should never share a menu with + //a triggered or may ability as it leads to exploits. + //only exception is perminent abilities such as "cast card normally" which can share the menu with autohand= + abilitiesTriggered->Add(i, currentAction->getMenuText()); + } } } + if(abilitiesTriggered->mCount) + { + SAFE_DELETE(abilitiesMenu); + abilitiesMenu = abilitiesTriggered; + } + else + { + SAFE_DELETE(abilitiesTriggered); + } if (!must) abilitiesMenu->Add(kCancelMenuID, "Cancel"); else @@ -503,6 +529,7 @@ ActionLayer::ActionLayer(GameObserver *observer) { menuObject = NULL; abilitiesMenu = NULL; + abilitiesTriggered = NULL; stuffHappened = 0; currentWaitingAction = NULL; cantCancel = 0; diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index d7e80ceac..d0ba169c9 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -1009,6 +1009,108 @@ AAMorph * AAMorph::clone() const a->forceDestroy = 1; return a; } +// flip a card +AAFlip::AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats) : +InstantAbility(observer, id, card, _target),flipStats(flipStats) +{ + target = _target; +} + +int AAFlip::resolve() +{ + MTGCardInstance * Flipper = (MTGCardInstance*)source; + if(Flipper->isFlipped) + { + game->removeObserver(this); + return 0; + } + MTGCardInstance * _target = (MTGCardInstance *) target; + if (_target) + { + while (_target->next) + _target = _target->next; + + AbilityFactory af(game); + _target->isFlipped = true; + GameObserver * game = _target->getObserver(); + if(flipStats.size()) + { + MTGCard * fcard = MTGCollection()->getCardByName(flipStats); + MTGCardInstance * myFlip = NEW MTGCardInstance(fcard, _target->controller()->game); + _target->name = myFlip->name; + _target->colors = myFlip->colors; + _target->power = (myFlip->power + _target->power) - _target->origpower; + _target->addToToughness(myFlip->toughness - _target->origtoughness); + _target->types = myFlip->types; + _target->text = myFlip->text; + _target->formattedText = myFlip->formattedText; + ActionLayer * al = game->mLayers->actionLayer(); + for (int k = (int)(al->mObjects.size()) - 1; k > 0; k--) + { + MTGAbility * a = dynamic_cast(game->mLayers->actionLayer()->mObjects[k]); + if(a && a->source == _target) + { + a->forceDestroy = 1; + a->destroy(); + a->removeFromGame(); + al->removeFromGame(a); + } + } + _target->magicText = myFlip->magicText; + af.getAbilities(¤tAbilities, NULL, _target); + for (size_t i = 0; i < currentAbilities.size(); ++i) + { + MTGAbility * a = currentAbilities[i]; + a->source = (MTGCardInstance *) _target; + if (a) + { + if (a->oneShot) + { + a->resolve(); + delete (a); + } + else + { + a->addToGame(); + } + } + } + SAFE_DELETE(myFlip); + } + _target->mPropertiesChangedSinceLastUpdate = true; + currentAbilities.clear(); + testDestroy(); + } + return 1; +} + +int AAFlip::testDestroy() +{ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(target) + { + if(_target->isFlipped) + { + this->forceDestroy = 1; + _target->getObserver()->removeObserver(this); + _target->isFlipped = false; + return 1; + } + } + return 0; +} + +const char * AAFlip::getMenuText() +{ + return "Flip"; +} + +AAFlip * AAFlip::clone() const +{ + AAFlip * a = NEW AAFlip(*this); + a->forceDestroy = 1; + return a; +} // AADYNAMIC: dynamic ability builder AADynamic::AADynamic(GameObserver* observer, int id, MTGCardInstance * card, Damageable * _target,int type,int effect,int who,int amountsource,MTGAbility * storedAbility, ManaCost * _cost) : ActivatedAbility(observer, id, card, _cost, 0),type(type),effect(effect),who(who),amountsource(amountsource),storedAbility(storedAbility) @@ -2212,6 +2314,7 @@ MayAbility::MayAbility(GameObserver* observer, int _id, MTGAbility * _ability, M { triggered = 0; mClone = NULL; + optionalCost = NULL; } void MayAbility::Update(float dt) @@ -2220,6 +2323,8 @@ void MayAbility::Update(float dt) if (!triggered && !game->getCurrentTargetChooser()) { triggered = 1; + if(optionalCost && !source->controller()->getManaPool()->canAfford(optionalCost)) + return; if (TargetAbility * ta = dynamic_cast(ability)) { if (!ta->getActionTc()->validTargetsExist()) @@ -2254,13 +2359,22 @@ int MayAbility::testDestroy() int MayAbility::isReactingToTargetClick(Targetable * card) { if (card == source) - return 1; + { + if(!optionalCost || source->controller()->getManaPool()->canAfford(optionalCost)) + return 1; + } return 0; } int MayAbility::reactToTargetClick(Targetable * object) { mClone = ability->clone(); + if(optionalCost) + { + source->controller()->getManaPool()->pay(optionalCost); + optionalCost->setExtraCostsAction(this, source); + optionalCost->doPayExtra(); + } mClone->addToGame(); mClone->forceDestroy = 1; return mClone->reactToTargetClick(object); @@ -2270,12 +2384,14 @@ MayAbility * MayAbility::clone() const { MayAbility * a = NEW MayAbility(*this); a->ability = ability->clone(); + a->optionalCost = this->optionalCost; return a; } MayAbility::~MayAbility() { SAFE_DELETE(ability); + SAFE_DELETE(optionalCost); } //Menu building ability Abilities @@ -2466,7 +2582,7 @@ int MultiAbility::destroy() const char * MultiAbility::getMenuText() { - if (abilities.size()) + if (abilities.size() && abilities[0]) return abilities[0]->getMenuText(); return ""; } diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index fdd71162f..d111b64e5 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -140,7 +140,11 @@ void CardGui::Render() bool alternate = true; JQuadPtr quad = game? game->getResourceManager()->RetrieveCard(card, CACHE_THUMB):WResourceManager::Instance()->RetrieveCard(card, CACHE_THUMB); - + if(card && card->name != card->model->data->name) + { + MTGCard * fcard = MTGCollection()->getCardByName(card->name); + quad = game->getResourceManager()->RetrieveCard(fcard, CACHE_THUMB); + } if (quad.get()) alternate = false; else @@ -227,7 +231,6 @@ void CardGui::Render() } } } - if (quad) { quad->SetColor(ARGB(static_cast(actA),255,255,255)); @@ -1031,6 +1034,12 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos) float x = pos.actX; JQuadPtr quad = WResourceManager::Instance()->RetrieveCard(card); + MTGCardInstance * kcard = dynamic_cast(card); + if(kcard && kcard->name != kcard->model->data->name) + { + MTGCard * fcard = MTGCollection()->getCardByName(kcard->name); + quad = WResourceManager::Instance()->RetrieveCard(fcard); + } if (quad.get()) { if (quad->mHeight < quad->mWidth) @@ -1043,7 +1052,6 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos) RenderCountersBig(card, pos); return; } - //DebugTrace("Unable to fetch image: " << card->getImageName()); // If we come here, we do not have the picture. diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index a0be2c69f..9167b6c94 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -1120,12 +1120,24 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG //When...comes into play, choose one... const string mayKeywords[] = {"may ", "choice "}; const bool mayMust[] = { false, true }; + ManaCost * mayCost = NULL; for (size_t i =0; i < sizeof(mayMust)/sizeof(mayMust[0]); ++i) { if (sWithoutTc.find(mayKeywords[i]) == 0) { string s1 = sWithoutTc.substr(mayKeywords[i].length()); - MTGAbility * a1 = parseMagicLine(s1, id, spell, card); + MTGAbility * a1 = NULL; + //may pay a cost for this ability + vector splitMayPay = parseBetween(s1, "pay(", ")", true); + if(splitMayPay.size()) + { + a1 = parseMagicLine(splitMayPay[2], id, spell, card); + mayCost = ManaCost::parseManaCost(splitMayPay[1], NULL, card); + } + else + { + a1 = parseMagicLine(s1, id, spell, card); + } if (!a1) return NULL; @@ -1133,7 +1145,10 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG a1 = NEW GenericTargetAbility(observer, newName,castRestriction,id, card, tc, a1); else a1 = NEW GenericActivatedAbility(observer, newName,castRestriction,id, card, a1, NULL); - return NEW MayAbility(observer, id, a1, card,mayMust[i]); + MayAbility * mainAbility = NEW MayAbility(observer, id, a1, card,mayMust[i]); + if(mayCost) + mainAbility->optionalCost = mayCost; + return mainAbility; } } @@ -1147,7 +1162,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return NEW GenericInstantAbility(observer, 1, card, (Damageable *) target, a1); } - + //Upkeep Cost found = s.find("upcostmulti"); if (found != string::npos) @@ -2194,6 +2209,21 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG } + //flip + vector splitFlipStat = parseBetween(s, "flip(", ")", true); + if(splitFlipStat.size()) + { + string flipStats = ""; + if(splitFlipStat[1].size()) + { + /*vectorFlipStats = split(splitFlipStat[1],'%');*/ + flipStats = splitFlipStat[1]; + } + MTGAbility * a = NEW AAFlip(observer, id, card, target,flipStats); + a->oneShot = 1; + return a; + } + //Change Power/Toughness WParsedPT * wppt = NEW WParsedPT(s, spell, card); bool nonstatic = false; @@ -2529,9 +2559,20 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ int myCards = countCards(abi->getActionTc(), p); int theirCards = countCards(abi->getActionTc(), p->opponent()); int efficiency = abilityEfficiency(abi->ability, p, mode, tc); - if ( ((myCards < theirCards) && efficiency == BAKA_EFFECT_GOOD) || ((myCards > theirCards) && efficiency == BAKA_EFFECT_BAD) ) - return efficiency; - return -efficiency; + if (efficiency == BAKA_EFFECT_GOOD) + { + myCards < theirCards? efficiency = BAKA_EFFECT_BAD : efficiency = BAKA_EFFECT_GOOD; + } + else if (efficiency == BAKA_EFFECT_BAD) + { + myCards >= theirCards? efficiency = BAKA_EFFECT_BAD : efficiency = BAKA_EFFECT_GOOD; + } + return efficiency; + /*this method below leads to too many undesired effects, basically it doesn't work how the original coder thought it would. + leaving it for reference to avoid it reaccuring during a refactor. + if ( ((myCards <= theirCards) && efficiency == BAKA_EFFECT_GOOD) || ((myCards >= theirCards) && efficiency == BAKA_EFFECT_BAD) ) + return efficiency; + return -efficiency; */ } if (AAsLongAs * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability, p, mode, tc); @@ -3572,7 +3613,7 @@ int MTGAbility::addToGame() int MTGAbility::removeFromGame() { - game->addObserver(this); + game->removeObserver(this); return 1; } diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index fa0bed265..a9dcb4636 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -131,6 +131,7 @@ void MTGCardInstance::initMTGCI() morphed = false; turningOver = false; isMorphed = false; + isFlipped = false; isPhased = false; phasedTurn = -1; didattacked = 0;