#include "PrecompiledHeader.h" #include "GameObserver.h" #include "GameOptions.h" #include "CardGui.h" #include "Damage.h" #include "Rules.h" #include "ExtraCost.h" #include GameObserver * GameObserver::mInstance = NULL; GameObserver* GameObserver::GetInstance() { return mInstance; } void GameObserver::EndInstance() { SAFE_DELETE(mInstance); } void GameObserver::Init(Player * _players[], int _nbplayers){ mInstance = NEW GameObserver(_players, _nbplayers); } GameObserver::GameObserver(Player * _players[], int _nb_players){ for (int i = 0; i < _nb_players;i ++){ players[i] = _players[i]; } currentPlayer = NULL; currentActionPlayer = NULL; isInterrupting = NULL; currentPlayerId = 0; nbPlayers = _nb_players; currentGamePhase = -1; targetChooser = NULL; cardWaitingForTargets = NULL; waitForExtraPayment = NULL; reaction = 0; gameOver = NULL; phaseRing = NULL; replacementEffects = NEW ReplacementEffects(); combatStep = BLOCKERS; mRules = NULL; } int GameObserver::getCurrentGamePhase(){ return currentGamePhase; } Player * GameObserver::opponent(){ int index = (currentPlayerId+1)%nbPlayers; return players[index]; } void GameObserver::nextPlayer(){ turn++; currentPlayerId = (currentPlayerId+1)%nbPlayers; currentPlayer = players[currentPlayerId]; currentActionPlayer = currentPlayer; combatStep = BLOCKERS; } void GameObserver::nextGamePhase(){ Phase * cPhaseOld = phaseRing->getCurrentPhase(); if (cPhaseOld->id == Constants::MTG_PHASE_COMBATDAMAGE) if (FIRST_STRIKE == combatStep || END_FIRST_STRIKE == combatStep || DAMAGE == combatStep) { nextCombatStep(); return; } if (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS) if (BLOCKERS == combatStep || TRIGGERS == combatStep) { nextCombatStep(); return; } phaseRing->forward(); //Go directly to end of combat if no attackers if (cPhaseOld->id == Constants::MTG_PHASE_COMBATATTACKERS && !(currentPlayer->game->inPlay->getNextAttacker(NULL))){ phaseRing->forward(); phaseRing->forward(); } Phase * cPhase = phaseRing->getCurrentPhase(); currentGamePhase = cPhase->id; if (Constants::MTG_PHASE_COMBATDAMAGE == currentGamePhase) nextCombatStep(); if (currentPlayer != cPhase->player) nextPlayer(); //init begin of turn if (currentGamePhase == Constants::MTG_PHASE_BEFORE_BEGIN){ cleanupPhase(); currentPlayer->canPutLandsIntoPlay = 1; currentPlayer->castedspellsthisturn = 0; currentPlayer->opponent()->castedspellsthisturn = 0; currentPlayer->castcount = 0; currentPlayer->nocreatureinstant = 0; currentPlayer->nospellinstant = 0; currentPlayer->onlyoneinstant = 0; currentPlayer->damageCount = 0; currentPlayer->preventable = 0; mLayers->actionLayer()->cleanGarbage(); //clean abilities history for this turn; mLayers->stackLayer()->garbageCollect(); //clean stack history for this turn; mLayers->actionLayer()->Update(0); for (int i=0; i < 2; i++){ delete (players[i]->game->garbage); players[i]->game->garbage = NEW MTGGameZone(); players[i]->game->garbage->setOwner(players[i]); } combatStep = BLOCKERS; return nextGamePhase(); } for (int i = 0; i < 2; ++i) players[i]->getManaPool()->init(); if (currentGamePhase == Constants::MTG_PHASE_AFTER_EOT){ //Auto Hand cleaning, in case the player didn't do it himself while(currentPlayer->game->hand->nb_cards > 7 && currentPlayer->nomaxhandsize < 1) currentPlayer->game->putInGraveyard(currentPlayer->game->hand->cards[0]); mLayers->actionLayer()->Update(0); return nextGamePhase(); } //Phase Specific actions switch(currentGamePhase){ case Constants::MTG_PHASE_UNTAP: untapPhase(); break; case Constants::MTG_PHASE_DRAW: //mLayers->stackLayer()->addDraw(currentPlayer,1); break; case Constants::MTG_PHASE_COMBATBLOCKERS: receiveEvent(NEW WEventAttackersChosen()); break; default: break; } } int GameObserver::cancelCurrentAction(){ SAFE_DELETE(targetChooser); return mLayers->actionLayer()->cancelCurrentAction(); } void GameObserver::nextCombatStep() { switch (combatStep) { case BLOCKERS : receiveEvent(NEW WEventBlockersChosen()); receiveEvent(NEW WEventCombatStepChange(combatStep = TRIGGERS)); return; case TRIGGERS : receiveEvent(NEW WEventCombatStepChange(combatStep = ORDER)); return; case ORDER : receiveEvent(NEW WEventCombatStepChange(combatStep = FIRST_STRIKE)); return; case FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = END_FIRST_STRIKE)); return; case END_FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = DAMAGE)); return; case DAMAGE : receiveEvent(NEW WEventCombatStepChange(combatStep = END_DAMAGE)); return; case END_DAMAGE : ; // Nothing : go to next phase } } void GameObserver::userRequestNextGamePhase(){ if (mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) return; if (getCurrentTargetChooser()) return; bool executeNextPhaseImmediately = true; Phase * cPhaseOld = phaseRing->getCurrentPhase(); if ((cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS && combatStep == ORDER) || (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS && combatStep == TRIGGERS)|| cPhaseOld->id == Constants::MTG_PHASE_COMBATDAMAGE || opponent()->isAI() || options[Options::optionInterrupt(currentGamePhase)].number) { executeNextPhaseImmediately = false; } if (executeNextPhaseImmediately) { nextGamePhase(); } else { mLayers->stackLayer()->AddNextGamePhase(); } } int GameObserver::forceShuffleLibraries(){ int result = 0; if (players[0]->game->library->needShuffle) { players[0]->game->library->shuffle(); players[0]->game->library->needShuffle = false; ++result; } if (players[1]->game->library->needShuffle) { players[1]->game->library->shuffle(); players[1]->game->library->needShuffle = false; ++result; } return result; } void GameObserver::startGame(Rules * rules){ turn = 0; mRules = rules; if (rules) rules->initPlayers(); options.automaticStyle(players[0],players[1]); mLayers = NEW DuelLayers(); mLayers->init(); currentPlayerId = 0; currentPlayer = players[0]; currentActionPlayer = currentPlayer; phaseRing = NEW PhaseRing(players,nbPlayers); if (rules) rules->initGame(); //Preload images from hand if (!players[0]->isAI()){ for (int i=0; i< players[0]->game->hand->nb_cards; i++){ resources.RetrieveCard(players[0]->game->hand->cards[i],CACHE_THUMB); resources.RetrieveCard(players[0]->game->hand->cards[i]); } } startedAt = time(0); //Difficult mode special stuff if (!players[0]->isAI() && players[1]->isAI()){ int difficulty = options[Options::DIFFICULTY].number; if (options[Options::DIFFICULTY_MODE_UNLOCKED].number && difficulty) { Player * p = players[1]; for (int level=0; level < difficulty; level ++){ MTGCardInstance * card = NULL; MTGGameZone * z = p->game->library; for (int j = 0; jnb_cards; j++){ MTGCardInstance * _card = z->cards[j]; if (_card->hasType("land")){ card = _card; j = z->nb_cards; } } if (card){ MTGCardInstance * copy = p->game->putInZone(card, p->game->library, p->game->stack); Spell * spell = NEW Spell(copy); spell->resolve(); delete spell; } } } } } void GameObserver::addObserver(MTGAbility * observer){ mLayers->actionLayer()->Add(observer); } void GameObserver::removeObserver(ActionElement * observer){ if (observer)mLayers->actionLayer()->moveToGarbage(observer); else {} //TODO log error } GameObserver::~GameObserver(){ LOG("==Destroying GameObserver=="); SAFE_DELETE(targetChooser); SAFE_DELETE(mLayers); SAFE_DELETE(phaseRing); SAFE_DELETE(replacementEffects); for (int i = 0; i < nbPlayers; ++i){ SAFE_DELETE(players[i]); } LOG("==GameObserver Destroyed=="); } void GameObserver::Update(float dt){ Player * player = currentPlayer; if (Constants::MTG_PHASE_COMBATBLOCKERS == currentGamePhase && BLOCKERS == combatStep) player = player->opponent(); currentActionPlayer = player; if (isInterrupting) player = isInterrupting; mLayers->Update(dt,player); while (mLayers->actionLayer()->stuffHappened){ mLayers->actionLayer()->Update(0); } stateEffects(); oldGamePhase = currentGamePhase; if (combatStep == TRIGGERS) { if (!mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) { mLayers->stackLayer()->AddNextCombatStep(); } } //Auto skip Phases int skipLevel = (player->playMode == Player::MODE_TEST_SUITE) ? Constants::ASKIP_NONE : options[Options::ASPHASES].number; int nrCreatures = currentPlayer->game->inPlay->countByType("Creature"); if (skipLevel == Constants::ASKIP_SAFE || skipLevel == Constants::ASKIP_FULL) { if ((opponent()->isAI() && !(isInterrupting)) && ( currentGamePhase == Constants::MTG_PHASE_UNTAP || currentGamePhase == Constants::MTG_PHASE_DRAW || currentGamePhase == Constants::MTG_PHASE_COMBATBEGIN || ((currentGamePhase == Constants::MTG_PHASE_COMBATATTACKERS) && (nrCreatures == 0)) || currentGamePhase == Constants::MTG_PHASE_COMBATEND || currentGamePhase == Constants::MTG_PHASE_ENDOFTURN || ((currentGamePhase == Constants::MTG_PHASE_CLEANUP) && (currentPlayer->game->hand->nb_cards < 8)) )) userRequestNextGamePhase(); } if (skipLevel == Constants::ASKIP_FULL) { if ((opponent()->isAI() && !(isInterrupting)) && (currentGamePhase == Constants::MTG_PHASE_UPKEEP || currentGamePhase == Constants::MTG_PHASE_COMBATDAMAGE )) userRequestNextGamePhase(); } } //applies damage to creatures after updates //Players life test void GameObserver::stateEffects() { if (mLayers->stackLayer()->count(0,NOT_RESOLVED) != 0) return; if (mLayers->actionLayer()->menuObject) return; if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer()) return; for (int i =0; i < 2; i++){ MTGGameZone * zone = players[i]->game->inPlay; for (int j = zone->nb_cards-1 ; j>=0; j--){ MTGCardInstance * card = zone->cards[j]; card->afterDamage(); //Remove auras that don't have a valid target anymore if (card->target && !isInPlay(card->target) && !card->hasType("equipment")){ players[i]->game->putInGraveyard(card); } } } for (int i =0; i < 2; i++) if (players[i]->life <= 0){ int cantlosers = 0; MTGGameZone * z = players[i]->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)){ cantlosers++; } } MTGGameZone * k = players[i]->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){ gameOver = players[i]; } } for (int i =0; i < 2; i++) if (players[i]->poisonCount >= 10) gameOver = players[i]; } void GameObserver::Render() { mLayers->Render(); if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer()) JRenderer::GetInstance()->DrawRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(255,255,0,0)); if (waitForExtraPayment) waitForExtraPayment->Render(); for (int i = 0; i < nbPlayers; ++i){ players[i]->Render(); } } void GameObserver::ButtonPressed(PlayGuiObject * target){ DebugTrace("GAMEOBSERVER Click"); if (CardView* cardview = dynamic_cast(target)){ MTGCardInstance * card = cardview->getCard(); cardClick(card, card); } else if (GuiLibrary* library = dynamic_cast(target)){ if (library->showCards){ library->toggleDisplay(); forceShuffleLibraries(); } else { TargetChooser * _tc = this->getCurrentTargetChooser(); if (_tc && _tc->targetsZone(library->zone)){ library->toggleDisplay(); library->zone->needShuffle = true; } } } else if (GuiGraveyard* graveyard = dynamic_cast(target)) graveyard->toggleDisplay(); //opponenthand else if (GuiOpponentHand* opponentHand = dynamic_cast(target)) if (opponentHand->showCards){ opponentHand->toggleDisplay(); } else { TargetChooser * _tc = this->getCurrentTargetChooser(); if (_tc && _tc->targetsZone(opponentHand->zone)){ opponentHand->toggleDisplay(); } } //end opponenthand else if (GuiAvatar* avatar = dynamic_cast(target)){ cardClick(NULL, avatar->player); } } void GameObserver::stackObjectClicked(Interruptible * action){ if (targetChooser != NULL){ int result = targetChooser->toggleTarget(action); if (result == TARGET_OK_FULL){ cardClick(cardWaitingForTargets); }else{ return; } }else{ reaction = mLayers->actionLayer()->isReactingToTargetClick(action); if (reaction == -1) mLayers->actionLayer()->reactToTargetClick(action); } } void GameObserver::cardClick (MTGCardInstance * card, Targetable * object){ Player * clickedPlayer = NULL; if (!card) clickedPlayer = ((Player *)object); if (targetChooser){ int result; if (card) { if (card == cardWaitingForTargets){ int _result = targetChooser->ForceTargetListReady(); if (_result){ result = TARGET_OK_FULL; }else{ result = targetChooser->targetsReadyCheck(); } }else{ result = targetChooser->toggleTarget(card); } }else{ result = targetChooser->toggleTarget(clickedPlayer); } if (result == TARGET_OK_FULL) card = cardWaitingForTargets; else return; } if (waitForExtraPayment){ if (card){ waitForExtraPayment->tryToSetPayment(card); } if (waitForExtraPayment->isPaymentSet()){ mLayers->actionLayer()->reactToClick(waitForExtraPayment->action, waitForExtraPayment->source); waitForExtraPayment = NULL; } return; } if (ORDER == combatStep) { card->defenser->raiseBlockerRankOrder(card); return; } if (card && card->paymenttype <= 0){//card played as normal. /* Fix for Issue http://code.google.com/p/wagic/issues/detail?id=270 put into play is hopefully the only ability causing that kind of trouble If the same kind of issue occurs with other abilities, let's think of a cleaner solution */ if (targetChooser) { MTGAbility * a = mLayers->actionLayer()->getAbility(MTGAbility::PUT_INTO_PLAY); a->reactToClick(card); return; } reaction = mLayers->actionLayer()->isReactingToClick(card); if (reaction == -1) mLayers->actionLayer()->reactToClick(card); } /* added same fix for buyback and alternative cost, the varible "paymenttype = int" only serves one purpose, to tell this bug fix what menu item you clicked on...all alternative cost or play methods suffered from the fix because if the card contained "target=" it would automatically force the play method to putinplayrule...even charge you the original mana cost.*/ else if (card && card->paymenttype == 1){//this is alternitive cost if (targetChooser) { MTGAbility * a = mLayers->actionLayer()->getAbility(MTGAbility::ALTERNATIVE_COST); a->reactToClick(card); return; } reaction = mLayers->actionLayer()->isReactingToClick(card); if (reaction == -1) mLayers->actionLayer()->reactToClick(card); } //-------------- else if (card && card->paymenttype == 2){//this is buyback if (targetChooser) { MTGAbility * a = mLayers->actionLayer()->getAbility(MTGAbility::BUYBACK_COST); a->reactToClick(card); return; } reaction = mLayers->actionLayer()->isReactingToClick(card); if (reaction == -1) mLayers->actionLayer()->reactToClick(card); } //===================== else if (card && card->paymenttype == 3){//this is Flashback if (targetChooser) { MTGAbility * a = mLayers->actionLayer()->getAbility(MTGAbility::FLASHBACK_COST); a->reactToClick(card); return; } reaction = mLayers->actionLayer()->isReactingToClick(card); if (reaction == -1) mLayers->actionLayer()->reactToClick(card); } //===================== else if (card && card->paymenttype == 4){//this is retrace if (targetChooser) { MTGAbility * a = mLayers->actionLayer()->getAbility(MTGAbility::RETRACE_COST); a->reactToClick(card); return; } reaction = mLayers->actionLayer()->isReactingToClick(card); if (reaction == -1) mLayers->actionLayer()->reactToClick(card); } //===================== else{//this handles abilities on a menu...not just when card is being played reaction = mLayers->actionLayer()->isReactingToTargetClick(object); if (reaction == -1) mLayers->actionLayer()->reactToTargetClick(object); } if (reaction == -1) return; if (!card) return; //Current player's hand if (currentPlayer->game->hand->hasCard(card) && currentGamePhase == Constants::MTG_PHASE_CLEANUP && currentPlayer->game->hand->nb_cards > 7 && currentPlayer->nomaxhandsize < 1){ currentPlayer->game->putInGraveyard(card); }else if (reaction){ if (reaction == 1){ mLayers->actionLayer()->reactToClick(card); }else{ mLayers->actionLayer()->setMenuObject(object); } }else if (card->isTapped() && card->controller() == currentPlayer){ untap(card); } } int GameObserver::untap(MTGCardInstance * card) { if (!card->isUntapping()){ return 0; } if (card->has(Constants::DOESNOTUNTAP)) return 0; if (card->frozen > 0) return 0; card->attemptUntap(); return 1; } TargetChooser * GameObserver::getCurrentTargetChooser(){ TargetChooser * _tc = mLayers->actionLayer()->getCurrentTargetChooser(); if (_tc) return _tc; return targetChooser; } /* Returns true if the card is in one of the player's play zone */ int GameObserver::isInPlay(MTGCardInstance * card){ for (int i = 0; i < 2; i++){ if (players[i]->game->isInPlay(card)) return 1; } return 0; } void GameObserver::draw(){ currentPlayer->game->drawFromLibrary(); } void GameObserver::cleanupPhase(){ currentPlayer->cleanupPhase(); opponent()->cleanupPhase(); } void GameObserver::untapPhase(){ currentPlayer->inPlay()->untapAll(); } int GameObserver::receiveEvent(WEvent * e){ if (!e) return 0; eventsQueue.push(e); if (eventsQueue.size() > 1) return -1; //resolving events can generate more events int result = 0; while(eventsQueue.size()){ WEvent * ev = eventsQueue.front(); result += mLayers->receiveEvent(ev); for (int i = 0; i < 2; ++i){ result += players[i]->receiveEvent(ev); } SAFE_DELETE(ev); eventsQueue.pop(); } return result; } Player * GameObserver::currentlyActing(){ if (isInterrupting) return isInterrupting; return currentActionPlayer; } //TODO CORRECT THIS MESS int GameObserver::targetListIsSet(MTGCardInstance * card){ if (targetChooser == NULL){ TargetChooserFactory tcf; targetChooser = tcf.createTargetChooser(card); cardWaitingForTargets = card; if (targetChooser == NULL){ return 1; } } return (targetChooser->targetListSet()); }