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;