void StackAbility::Render() { string action = ability->getMenuText(); MTGCardInstance * source = ability->source; string alt1 = source->getName(); Targetable * _target = ability->target; if (ability->getActionTc()) { Targetable * t = ability->getActionTc()->getNextTarget(); if (t) _target = t; } Damageable * target = NULL; if (_target != ability->source && (dynamic_cast<MTGCardInstance *>(_target) || dynamic_cast<Player *>(_target))) { target = (Damageable *) _target; } JQuadPtr quad; string alt2 = ""; if (target) { quad = target->getIcon(); if (target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE) { alt2 = ((MTGCardInstance *) target)->name; } } Interruptible::Render(source, quad.get(), alt1, alt2, action); }
int MTGGameZone::seenThisTurn(TargetChooser * tc, int castMethod, bool lastTurn) { //The following 2 lines modify the passed TargetChooser. Call this function with care :/ tc->setAllZones(); // This is to allow targetting cards without caring about the actual zone tc->targetter = NULL; int count = 0; if (lastTurn) { for (vector<MTGCardInstance *>::iterator iter = cardsSeenLastTurn.begin(); iter != cardsSeenLastTurn.end(); ++iter) { MTGCardInstance * c = (*iter); if (c && c->matchesCastFilter(castMethod) && tc->canTarget(c)) count++; } } else { for (vector<MTGCardInstance *>::iterator iter = cardsSeenThisTurn.begin(); iter != cardsSeenThisTurn.end(); ++iter) { MTGCardInstance * c = (*iter); if (c->matchesCastFilter(castMethod) && tc->canTarget(c)) count++; } } return count; }
void AIStats::updateStatsCard(MTGCardInstance * cardInstance, Damage * damage, float multiplier) { MTGCard * card = cardInstance->model; if (!card) return; //card can be null because some special cardInstances (such as ExtraRules) don't have a "model" AIStat * stat = find(card); if (!stat) { stat = NEW AIStat(card->getMTGId(), 0, 1, 0); stats.push_back(stat); } if (damage->target == player) { stat->value += static_cast<int>(multiplier * STATS_PLAYER_MULTIPLIER * damage->damage); } else if (damage->target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE) { MTGCardInstance * target = (MTGCardInstance *) damage->target; if (target->controller() == player && !target->isInPlay(player->getObserver())) { //One of my creatures got lethal damage... stat->value += static_cast<int>(multiplier * STATS_CREATURE_MULTIPLIER * damage->damage); } } }
MTGCardInstance * TestSuiteAI::getCard(string action) { int mtgid = Rules::getMTGId(action); if (mtgid) return Rules::getCardByMTGId(observer, mtgid); //This mostly handles tokens std::transform(action.begin(), action.end(), action.begin(), ::tolower); for (int i = 0; i < 2; i++) { Player * p = observer->players[i]; MTGGameZone * zones[] = { p->game->library, p->game->hand, p->game->inPlay, p->game->graveyard }; for (int j = 0; j < 4; j++) { MTGGameZone * zone = zones[j]; for (int k = 0; k < zone->nb_cards; k++) { MTGCardInstance * card = zone->cards[k]; if (!card) return NULL; string name = card->getLCName(); if (name.compare(action) == 0) return card; } } } DebugTrace("TESTUISTEAI: Can't find card:" << action.c_str()); return NULL; }
int GuiCombat::resolve() // Returns the number of damage objects dealt this turn. { DamageStack* stack = NEW DamageStack(observer); for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it) { MTGCardInstance * attacker = (*it)->card; signed dmg = attacker->stepPower(step); for (vector<DefenserDamaged*>::iterator q = (*it)->blockers.begin(); q != (*it)->blockers.end(); ++q) { for (vector<Damage>::iterator d = (*q)->damages.begin(); d != (*q)->damages.end(); ++d) stack->Add(NEW Damage(*d)); dmg -= (*q)->sumDamages(); } if (dmg > 0 && ((!attacker->isBlocked()) || attacker->has(Constants::TRAMPLE))) stack->Add(NEW Damage(observer, (*it)->card, (Damageable*)attacker->isAttacking?(Damageable*)attacker->isAttacking:observer->opponent(), dmg, DAMAGE_COMBAT)); for (vector<Damage>::iterator d = (*it)->damages.begin(); d != (*it)->damages.end(); ++d) stack->Add(NEW Damage(*d)); } int v = stack->mObjects.size(); if (v > 0) { observer->mLayers->stackLayer()->Add(stack); observer->mLayers->stackLayer()->resolve(); // This will delete the damage stack which will in turn delete the Damage it contains } else SAFE_DELETE(stack); return v; }
//Returns true if a card with the given MTG ID exists bool AIHints::findSource(int sourceId) { for (int i = 0; i < mPlayer->game->inPlay->nb_cards; i++) { MTGCardInstance * c = mPlayer->game->inPlay->cards[i]; if (c->getMTGId() == sourceId) return true; } return false; }
MTGCardInstance * MTGInPlay::findALurer() { for (int i = 0; i < nb_cards; i++) { MTGCardInstance * current = cards[i]; if (current->isAttacker() && current->has(Constants::LURE)) { return current; } } return NULL; }
bool Player::hasPossibleAttackers() { MTGGameZone * z = game->inPlay; int nbcards = z->nb_cards; for (int j = 0; j < nbcards; ++j) { MTGCardInstance * c = z->cards[j]; if (!c->isTapped() && !c->hasSummoningSickness() && c->isCreature()) return true; } return false; }
bool Player::DeadLifeState(bool check) { if ((life <= 0)||(poisonCount >= 10)) { int cantlosers = 0; MTGGameZone * z = game->inPlay; int nbcards = z->nb_cards; for (int j = 0; j < nbcards; ++j) { MTGCardInstance * c = z->cards[j]; if (c->has(Constants::CANTLOSE) || (c->has(Constants::CANTLIFELOSE) && poisonCount < 10)) { cantlosers++; } } MTGGameZone * k = opponent()->game->inPlay; int onbcards = k->nb_cards; for (int m = 0; m < onbcards; ++m) { MTGCardInstance * e = k->cards[m]; if (e->has(Constants::CANTWIN)) { cantlosers++; } } if (cantlosers < 1) { if(!check) { ActionStack * stack = getObserver()->mLayers->stackLayer(); for (int i = stack->mObjects.size() - 1; i >= 0; i--) { Interruptible * current = ((Interruptible *) stack->mObjects[i]); Spell * spell = (Spell *) current; if (current->type == ACTION_SPELL) spell->source->controller()->game->putInGraveyard(spell->source); current->state = RESOLVED_NOK; } } if(check) game->owner->getObserver()->setLoser(this); return true; } } return false; }
//Returns opponents to this card for this turn. This * should * take into account banding MTGCardInstance * MTGCardInstance::getNextOpponent(MTGCardInstance * previous) { int foundprevious = 0; if (!previous) foundprevious = 1; if (attacker) { MTGInPlay * inPlay = observer->opponent()->game->inPlay; for (int i = 0; i < inPlay->nb_cards; i++) { MTGCardInstance * current = inPlay->cards[i]; if (current == previous) { foundprevious = 1; } else if (foundprevious) { MTGCardInstance * defensersOpponent = current->isDefenser(); if (defensersOpponent && (defensersOpponent == this || (banding && defensersOpponent->banding == banding))) { return current; } } } } else if (defenser) { MTGInPlay * inPlay = observer->currentPlayer->game->inPlay; for (int i = 0; i < inPlay->nb_cards; i++) { MTGCardInstance * current = inPlay->cards[i]; if (current == previous) { foundprevious = 1; } else if (foundprevious) { if (defenser == current || (current->banding && defenser->banding == current->banding)) { return current; } } } } return NULL; }
void MTGPlayerCards::drawFromLibrary() { if (!library->nb_cards) { if (inPlay->hasAbility(Constants::CANTLOSE) || inPlay->hasAbility(Constants::CANTMILLLOSE) || owner->opponent()->game->inPlay->hasAbility(Constants::CANTWIN)) { return; } library->owner->getObserver()->setLoser(library->owner); return; } MTGCardInstance * toMove = library->cards[library->nb_cards - 1]; library->lastCardDrawn = toMove; if (!library->miracle) { library->miracle = true; toMove->miracle = true; } // useability tweak - assume that the user is probably going to want to see the new card, // so prefetch it. // if we're not in text mode, always get the thumb if (library->owner->getObserver()->getCardSelector()->GetDrawMode() != DrawMode::kText && library->owner->getObserver()->getResourceManager()) { DebugTrace("Prefetching AI card going into play: " << toMove->getImageName()); library->owner->getObserver()->getResourceManager()->RetrieveCard(toMove, RETRIEVE_THUMB); // also cache the large image if we're using kNormal mode if (library->owner->getObserver()->getCardSelector()->GetDrawMode() == DrawMode::kNormal) { library->owner->getObserver()->getResourceManager()->RetrieveCard(toMove); } } if(putInZone(toMove, library, hand)) { toMove->currentZone = hand; } }
MTGCardInstance * Rules::getCardByMTGId(GameObserver* g, int mtgid) { for (int i = 0; i < 2; i++) { Player * p = g->players[i]; MTGGameZone * zones[] = { p->game->library, p->game->hand, p->game->inPlay, p->game->graveyard }; for (int j = 0; j < 4; j++) { MTGGameZone * zone = zones[j]; for (int k = 0; k < zone->nb_cards; k++) { MTGCardInstance * card = zone->cards[k]; if (!card) return NULL; if (card->getMTGId() == mtgid) return card; } } } return NULL; }
void MTGInPlay::untapAll() { int i; for (i = 0; i < nb_cards; i++) { MTGCardInstance * card = cards[i]; card->setUntapping(); if (!card->basicAbilities[(int)Constants::DOESNOTUNTAP]) { if (card->frozen < 1) { card->attemptUntap(); } if (card->frozen >= 1) { card->frozen = 0; } } } }
MTGCardInstance * MTGInPlay::getNextLurer(MTGCardInstance * previous) { int foundprevious = 0; if (previous == NULL) { foundprevious = 1; } for (int i = 0; i < nb_cards; i++) { MTGCardInstance * current = cards[i]; if (current == previous) { foundprevious = 1; } else if (foundprevious && current->isAttacker() && current->has(Constants::LURE)) { return current; } } return NULL; }
void ActionStack::Update(float dt) { //This is a hack to avoid updating the stack while tuto messages are being shown //Ideally, the tuto messages should be moved to a layer above this one //No need for Tuto when no human in game if (getCurrentTutorial() && (observer->players[0]->isHuman() || observer->players[1]->isHuman() ) ) return; askIfWishesToInterrupt = NULL; //modal = 0; TargetChooser * tc = observer->getCurrentTargetChooser(); int newState = observer->getCurrentGamePhase(); currentState = newState; if (!tc) checked = 0; //Select Stack's display mode if (mode == ACTIONSTACK_STANDARD && tc && !checked) { checked = 1; for (size_t i = 0; i < mObjects.size(); i++) { Interruptible * current = (Interruptible *) mObjects[i]; if (tc->canTarget(current)) { if (mCurr < (int) mObjects.size() && mObjects[mCurr]) mObjects[mCurr]->Leaving(JGE_BTN_UP); current->display = 1; mCurr = i; mObjects[mCurr]->Entering(); mode = ACTIONSTACK_TARGET; modal = 1; } else { current->display = 0; } } if (mode != ACTIONSTACK_TARGET) { } } else if (mode == ACTIONSTACK_TARGET && !tc) { mode = ACTIONSTACK_STANDARD; checked = 0; } if (mode == ACTIONSTACK_STANDARD) { modal = 0; if (getLatest(NOT_RESOLVED) && !tc) { Interruptible * currentSpell = (Interruptible *)getLatest(NOT_RESOLVED); MTGCardInstance * card = currentSpell->source; if(card && card->has(Constants::SPLITSECOND)) { resolve(); } else { int currentPlayerId = 0; int otherPlayerId = 1; if (observer->currentlyActing() != observer->players[0]) { currentPlayerId = 1; otherPlayerId = 0; } if (interruptDecision[currentPlayerId] == NOT_DECIDED) { askIfWishesToInterrupt = observer->players[currentPlayerId]; observer->isInterrupting = observer->players[currentPlayerId]; modal = 1; } else if (interruptDecision[currentPlayerId] == INTERRUPT) { observer->isInterrupting = observer->players[currentPlayerId]; } else { if (interruptDecision[otherPlayerId] == NOT_DECIDED) { askIfWishesToInterrupt = observer->players[otherPlayerId]; observer->isInterrupting = observer->players[otherPlayerId]; modal = 1; } else if (interruptDecision[otherPlayerId] == INTERRUPT) { observer->isInterrupting = observer->players[otherPlayerId]; } else { resolve(); } } } } } else if (mode == ACTIONSTACK_TARGET) { GuiLayer::Update(dt); } if (askIfWishesToInterrupt) { // WALDORF - added code to use a game option setting to determine how // long the Interrupt timer should be. If it is set to zero (0), the // game will wait for ever for the user to make a selection. if (options[Options::INTERRUPT_SECONDS].number > 0) { int extraTime = 0; //extraTime is a multiplier, it counts the number of unresolved stack actions //then is used to extend the time you have to interupt. //this prevents you from "running out of time" while deciding. //before this int was added, it was possible to run out of time if you had 10 stack actions //and set the timer to 4 secs. BUG FIX //http://code.google.com/p/wagic/issues/detail?id=464 extraTime = count(0, NOT_RESOLVED, 0); if (extraTime == 0) extraTime = 1;//we never want this int to be 0. if (timer < 0) timer = static_cast<float>(options[Options::INTERRUPT_SECONDS].number * extraTime); timer -= dt; if (timer < 0) cancelInterruptOffer(); } } }
void TestSuiteGame::assertGame() { mMutex.lock(); //compare the game state with the results char result[4096]; sprintf(result, "<h3>%s</h3>", filename.c_str()); Log(result); int error = 0; bool wasAI = false; if (observer->getCurrentGamePhase() != endState.phase) { sprintf(result, "<span class=\"error\">==phase problem. Expected [ %s ](%i), got [ %s ](%i)==</span><br />", Constants::MTGPhaseNames[endState.phase],endState.phase, Constants::MTGPhaseNames[observer->getCurrentGamePhase()], observer->getCurrentGamePhase()); Log(result); error++; } for (int i = 0; i < 2; i++) { TestSuiteAI * p = (TestSuiteAI *) (observer->players[i]); if (p->playMode == Player::MODE_AI) wasAI = true; if (p->life != endState.players[i]->life) { sprintf(result, "<span class=\"error\">==life problem for player %i. Expected %i, got %i==</span><br />", i, endState.players[i]->life, p->life); Log(result); error++; } if (p->poisonCount != endState.players[i]->poisonCount) { sprintf(result, "<span class=\"error\">==poison counter problem for player %i. Expected %i, got %i==</span><br />", i, endState.players[i]->poisonCount, p->poisonCount); Log(result); error++; } if (!p->getManaPool()->canAfford(endState.players[i]->getManaPool())) { sprintf(result, "<span class=\"error\">==Mana problem. Was expecting %i but got %i for player %i==</span><br />", endState.players[i]->getManaPool()->getConvertedCost(), p->getManaPool()->getConvertedCost(), i); Log(result); error++; } if (!endState.players[i]->getManaPool()->canAfford(p->getManaPool())) { sprintf(result, "<span class=\"error\">==Mana problem. Was expecting %i but got %i for player %i==</span><br />", endState.players[i]->getManaPool()->getConvertedCost(), p->getManaPool()->getConvertedCost(), i); Log(result); if ( endState.players[i]->getManaPool()->getConvertedCost() == p->getManaPool()->getConvertedCost()) { sprintf(result, "<span class=\"error\">====(Apparently Mana Color issues since converted cost is the same)==</span><br />"); Log(result); } error++; } MTGGameZone * playerZones[] = { p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay }; MTGGameZone * endstateZones[] = { endState.players[i]->game->graveyard, endState.players[i]->game->library, endState.players[i]->game->hand, endState.players[i]->game->inPlay }; for (int j = 0; j < 4; j++) { MTGGameZone * zone = playerZones[j]; if (zone->nb_cards != endstateZones[j]->nb_cards) { sprintf( result, "<span class=\"error\">==Card number not the same in player %i's %s==, expected %i, got %i</span><br />", i, zone->getName(), endstateZones[j]->nb_cards, zone->nb_cards); Log(result); error++; } for (size_t k = 0; k < (size_t)endstateZones[j]->nb_cards; k++) { MTGCardInstance* cardToCheck = (k<endstateZones[j]->cards.size())?endstateZones[j]->cards[k]:0; if(cardToCheck) { // Can be NULL if used "*" in the testcase. MTGCardInstance* card = Rules::getCardByMTGId(observer, cardToCheck->getId()); if (card != 0 && !zone->hasCard(card)) { sprintf(result, "<span class=\"error\">==Card ID not the same. Didn't find %i</span><br />", card->getId()); Log(result); error++; } } } } } handleResults(wasAI, error); if(!error) Log("<span class=\"success\">==Test Succesful !==</span>"); else Log("<span class=\"error\">==Test Failed !==</span>"); mMutex.unlock(); }