diff --git a/projects/mtg/bin/Res/ai/baka/deck16.txt b/projects/mtg/bin/Res/ai/baka/deck16.txt new file mode 100644 index 000000000..d010e2651 --- /dev/null +++ b/projects/mtg/bin/Res/ai/baka/deck16.txt @@ -0,0 +1,73 @@ +#Black/White Deck 10E/RV +#4x The hive +135253 +135253 +135253 +135253 +#4x pestilence +1172 +1172 +1172 +1172 +#4x Ancestor chosen +130550 +130550 +130550 +130550 +#4x assassinate +135194 +135194 +135194 +135194 +#4x bottle gnomes +129495 +129495 +129495 +129495 +#4x Drudge skelettons +129529 +129529 +129529 +129529 +#4x Glorious Anthem +129572 +129572 +129572 +129572 +#4x Paladin en Vec +129668 +129668 +129668 +129668 +#2 x Deathmark +129910 +129910 +#2x Demistify +129524 +129524 +#12 swamps +129754 +129756 +129755 +129757 +129754 +129756 +129755 +129757 +129754 +129756 +129755 +129757 +#12x plains +129680 +129681 +129682 +129683 +129680 +129681 +129682 +129683 +129680 +129681 +129682 +129683 \ No newline at end of file diff --git a/projects/mtg/bin/Res/sets/POR/_cards.dat b/projects/mtg/bin/Res/sets/POR/_cards.dat index 44bc4c663..5133e86ee 100644 --- a/projects/mtg/bin/Res/sets/POR/_cards.dat +++ b/projects/mtg/bin/Res/sets/POR/_cards.dat @@ -122,7 +122,7 @@ type=Creature subtype=Zombie power=2 toughness=2 -abilites=swampwalk +abilities=swampwalk [/card] [card] text=Swampwalk (This creature is unblockable as long as defending player controls a Swamp.) @@ -271,12 +271,12 @@ type=Instant [card] text=Return target sorcery card from your graveyard to your hand. target=sorcery|myGraveyard -alias=1263 +auto=moveTo(myHand) id=4259 name=Deja vu rarity=C mana={2}{B} -type=sorcery +type=Sorcery [/card] [card] text=Flying @@ -288,7 +288,7 @@ type=Creature subtype=Drake power=2 toughness=2 -abilites=flying +abilities=flying [/card] [card] text=Destroy all creatures and lands @@ -824,7 +824,7 @@ type=Creature subtype=Illusion Warrior power=2 toughness=2 -abilites=unblockable +abilities=unblockable [/card] [card] text={T}: Add {W} to your mana pool. @@ -872,7 +872,7 @@ type=Creature subtype=Illusion Warrior power=2 toughness=2 -abilites=unblockable +abilities=unblockable [/card] [card] text=Pyroclasm deals 2 damage to each creature. diff --git a/projects/mtg/bin/Res/sets/RV/_cards.dat b/projects/mtg/bin/Res/sets/RV/_cards.dat index 4f2f859d0..06ffae23a 100644 --- a/projects/mtg/bin/Res/sets/RV/_cards.dat +++ b/projects/mtg/bin/Res/sets/RV/_cards.dat @@ -1630,7 +1630,7 @@ name=Prodigal Sorcerer auto={T}:damage:1 target(creature,player) rarity=C type=Creature -mana={2}{U} +mana={U} power=1 subtype=Human Wizard toughness=1 diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 74de86d91..5f72d7b31 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -9,6 +9,8 @@ #include "Player.h" +#include +using std::queue; #define INFO_NBCREATURES 0 #define INFO_CREATURESPOWER 1 @@ -16,13 +18,40 @@ class AIStats; +class AIAction{ +protected: + int efficiency; +public: + MTGAbility * ability; + Player * player; + MTGCardInstance * click; + MTGCardInstance * target; // TODO Improve + AIAction(MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL):ability(a),click(c),target(t){player = NULL; efficiency = -1;}; + AIAction(MTGCardInstance * c, MTGCardInstance * t = NULL):click(c),target(t){player = NULL; ability = NULL; efficiency = -1;}; + AIAction(Player * p):player(p){ability = NULL; target = NULL; click = NULL; efficiency = -1;}; + int getEfficiency(); + int Act(); + +}; + +class CmpAbilities { // compares Abilities efficiency + public: + bool operator()(AIAction * a1, AIAction * a2) const { + int e1 = a1->getEfficiency(); + int e2 = a2->getEfficiency(); + if (e1 == e2) return a1 > a2; //TODO improve + return (e1 > e2); + } +}; + class AIPlayer: public Player{ protected: MTGCardInstance * nextCardToPlay; ManaCost * potentialMana; + queue clickstream; void tapLandsForMana(ManaCost * potentialMana, ManaCost * cost); - int checkInterrupt(); int combatDamages(); + int interruptIfICan(); int chooseAttackers(); int chooseBlockers(); int effectBadOrGood(MTGCardInstance * card); @@ -30,7 +59,7 @@ class AIPlayer: public Player{ AIStats * getStats(); public: void End(){}; - virtual int displayStack(){return 0;}; + virtual int displayStack(){return 1;}; AIStats * stats; ManaCost * getPotentialMana(); AIPlayer(MTGPlayerCards * _deck, string deckFile); @@ -39,6 +68,9 @@ class AIPlayer: public Player{ virtual int chooseTarget(TargetChooser * tc = NULL); virtual int Act(float dt); int isAI(){return 1;}; + int selectAbility(); + int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, map * ranking); + int useAbility(); }; @@ -52,6 +84,7 @@ class AIPlayerBaka: public AIPlayer{ AIPlayerBaka(MTGPlayerCards * _deck, char * deckFile, char * avatarFile); virtual int Act(float dt); void initTimer(); + int computeActions(); }; class AIPlayerFactory{ diff --git a/projects/mtg/include/ActionElement.h b/projects/mtg/include/ActionElement.h index cc458eac9..edf49a6ce 100644 --- a/projects/mtg/include/ActionElement.h +++ b/projects/mtg/include/ActionElement.h @@ -14,6 +14,7 @@ class MTGCardInstance; +class ManaCost; class Targetable; class TargetChooser; class WEvent; @@ -37,9 +38,10 @@ class ActionElement: public JGuiObject{ virtual int destroy(){return 0;}; virtual bool CheckUserInput(u32 key){return false;}; ActionElement(int id); + virtual ~ActionElement(); virtual int isReactingToTargetClick(Targetable * card); virtual int reactToTargetClick(Targetable * card); - virtual int isReactingToClick(MTGCardInstance * card){return 0;}; + virtual int isReactingToClick(MTGCardInstance * card, ManaCost * man = NULL){return 0;}; virtual int stillInUse(MTGCardInstance * card){return 0;}; virtual int receiveEvent(WEvent * event){return 0;}; virtual int reactToClick(MTGCardInstance * card){return 0;}; diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 991e5b553..5c84621b9 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -396,7 +396,7 @@ class ASpellCastLife:public MTGAbility{ trigger.setColor(color); } - int isReactingToClick(MTGCardInstance * _card){ + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){ if (_card == source && game->currentlyActing()->game->inPlay->hasCard(source)){ if (game->currentlyActing()->getManaPool()->canAfford(cost)){ Interruptible * laststackitem = game->mLayers->stackLayer()->_(-1); @@ -430,7 +430,7 @@ class AUnBlocker:public MTGAbility{ } - int isReactingToClick(MTGCardInstance * _card){ + int isReactingToClick(MTGCardInstance * _card,ManaCost * mana = NULL){ if (_card == target && game->currentlyActing()->game->inPlay->hasCard(source) && (MTGCardInstance *) _card->isTapped()){ if (game->currentlyActing()->getManaPool()->canAfford(cost)){ return 1; @@ -462,10 +462,10 @@ class AUntaperOnceDuringTurn:public AUnBlocker{ AUnBlocker::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ if (onlyPlayerTurn && game->currentPlayer!=source->controller()) return 0; if (untappedThisTurn) return 0; - return AUnBlocker::isReactingToClick(card); + return AUnBlocker::isReactingToClick(card,mana); } int reactToClick(MTGCardInstance * card){ @@ -578,8 +578,8 @@ class APowerToughnessModifierUntilEndOfTurn: public ActivatedAbility{ return resolve(); } - int isReactingToClick(MTGCardInstance * card){ - if (!ActivatedAbility::isReactingToClick(card)) return 0; + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ + if (!ActivatedAbility::isReactingToClick(card,mana)) return 0; return (!maxcounters || (counters < maxcounters)); } @@ -694,6 +694,7 @@ class AStandardRegenerate:public ActivatedAbility{ public: AStandardRegenerate(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost):ActivatedAbility(_id,_source,_cost,0,0){ target = _target; + aType = MTGAbility::STANDARD_REGENERATE; } int resolve(){ @@ -933,6 +934,7 @@ class ADamager:public TargetAbility{ int damage; ADamager(int id, MTGCardInstance * card, ManaCost * _cost, int _damage, TargetChooser * _tc = NULL, int _tap = 1):TargetAbility(id,card, _tc, _cost,0,_tap),damage(_damage){ if (!tc) tc = NEW DamageableTargetChooser(card); + aType = MTGAbility::DAMAGER; } int resolve(){ Damageable * _target = tc->getNextDamageableTarget(); @@ -1271,7 +1273,7 @@ class AArmageddonClock:public MTGAbility{ } } } - int isReactingToClick(MTGCardInstance * _card){ + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){ if (counters > 0 && _card == source && currentPhase == Constants::MTG_PHASE_UPKEEP){ if (game->currentlyActing()->getManaPool()->canAfford( & cost)){ return 1; @@ -1357,7 +1359,7 @@ class AClockworkBeast:public MTGAbility{ } } } - int isReactingToClick(MTGCardInstance * _card){ + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){ if (counters < 7 && _card == source && currentPhase == Constants::MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source)){ if (game->currentlyActing()->getManaPool()->canAfford( & cost)){ return 1; @@ -1423,7 +1425,7 @@ class AConservator: public MTGAbility{ alterDamage(); } - int isReactingToClick(MTGCardInstance * _card){ + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){ if ( _card == source && game->currentlyActing()->game->inPlay->hasCard(source) && !_card->isTapped()){ if (game->currentlyActing()->getManaPool()->canAfford( & cost)){ return 1; @@ -1560,8 +1562,8 @@ class AFarmstead:public ActivatedAbility{ target = _target; } - int isReactingToClick(MTGCardInstance * card){ - if (!ActivatedAbility::isReactingToClick(card)) return 0; + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ + if (!ActivatedAbility::isReactingToClick(card,mana)) return 0; if (currentPhase == Constants::MTG_PHASE_UPKEEP) return 1; return 0; } @@ -1605,7 +1607,7 @@ class AGlassesOfUrza:public MTGAbility{ } } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ if ( card == source){ if (game->currentlyActing()->game->isInPlay(card) && !source->isTapped()){ return 1; @@ -1674,7 +1676,7 @@ class ALivingArtifact:public MTGAbility{ } } - int isReactingtoclick(MTGCardInstance * card){ + int isReactingtoclick(MTGCardInstance * card, ManaCost * mana = NULL){ if (currentPhase == Constants::MTG_PHASE_UPKEEP && card == source && game->currentPlayer == source->controller() && counters && !usedThisTurn){ return 1; } @@ -1709,9 +1711,9 @@ class ALordOfThePit: public TargetAbility{ TargetAbility::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ if (currentPhase != Constants::MTG_PHASE_UPKEEP || paidThisTurn) return 0; - return TargetAbility::isReactingToClick(card); + return TargetAbility::isReactingToClick(card,mana); } int resolve(){ @@ -1867,9 +1869,9 @@ class AJandorsRing:public ActivatedAbility{ cost->add(Constants::MTG_COLOR_ARTIFACT, 2); } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ if (!source->controller()->game->hand->hasCard(source->controller()->game->library->lastCardDrawn)) return 0; - return ActivatedAbility::isReactingToClick(card); + return ActivatedAbility::isReactingToClick(card,mana); } int resolve(){ @@ -1909,7 +1911,7 @@ class AKudzu: public TargetAbility{ TargetAbility::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ MTGCardInstance * _target = (MTGCardInstance *)target; if (card == source && (!_target || !_target->isInPlay())){ #if defined (WIN32) || defined (LINUX) @@ -2073,7 +2075,7 @@ class APowerLeak:public TriggeredAbility{ TriggeredAbility::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ MTGCardInstance * _target = (MTGCardInstance *) target; if (damagesToDealThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && card==source && _target->controller() == game->currentPlayer){ if (game->currentPlayer->getManaPool()->canAfford(& cost)) return 1; @@ -2186,7 +2188,7 @@ class AScavengingGhoul:public MTGAbility{ //TODO } - int isReactingToClick(MTGCardInstance * _card){ + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){ if (counters > 0 && _card == source && game->currentlyActing()->game->inPlay->hasCard(source)){ return 1; } @@ -2391,8 +2393,8 @@ class AForceOfNature:public ActivatedAbility{ ActivatedAbility::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ - return (dealDamageThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card)); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ + return (dealDamageThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card,mana)); } int resolve(){ @@ -2509,7 +2511,7 @@ class AIslandSanctuary:public MTGAbility{ } } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ if (card==source && game->currentPlayer == card->controller() && currentPhase == Constants::MTG_PHASE_DRAW){ Interruptible * action = game->mLayers->stackLayer()->_(-1); if (action->type == ACTION_DRAW) return 1; @@ -2582,10 +2584,10 @@ class ASoulNet:public ActivatedAbility{ newDead = latest; } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ newDead = ((PutInGraveyard *) GameObserver::GetInstance()->mLayers->stackLayer()->getPrevious(NULL,ACTION_PUTINGRAVEYARD,RESOLVED_OK)); if (newDead && newDead != latest && newDead->card->isACreature()) - return ActivatedAbility::isReactingToClick(card); + return ActivatedAbility::isReactingToClick(card,mana); return 0; } int resolve(){ @@ -2624,8 +2626,8 @@ class AStasis:public ActivatedAbility{ ActivatedAbility::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ - return (!paidThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card)); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ + return (!paidThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card,mana)); } int resolve(){ @@ -2769,9 +2771,9 @@ class AMinionofLeshrac: public TargetAbility{ TargetAbility::Update(dt); } - int isReactingToClick(MTGCardInstance * card){ + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){ if (currentPhase != Constants::MTG_PHASE_UPKEEP || paidThisTurn) return 0; - return TargetAbility::isReactingToClick(card); + return TargetAbility::isReactingToClick(card,mana); } int resolve(){ diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index ed6e87d6b..8ee16c580 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -37,14 +37,16 @@ class MTGAbility: public ActionElement{ GameObserver * game; public: + ManaCost * cost; Damageable * target; + int aType; MTGCardInstance * source; MTGAbility(int id, MTGCardInstance * card); MTGAbility(int id, MTGCardInstance * _source, Damageable * _target); virtual int testDestroy(); virtual ~MTGAbility(); virtual void Render(){}; - virtual int isReactingToClick(MTGCardInstance * card){return 0;}; + virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){return 0;}; virtual int reactToClick(MTGCardInstance * card){return 0;}; virtual int receiveEvent(WEvent * event){return 0;}; virtual void Update(float dt){}; @@ -52,7 +54,15 @@ class MTGAbility: public ActionElement{ virtual int stillInUse(MTGCardInstance * card){if (card==source) return 1; return 0;}; virtual int resolve(){return 0;}; + /*Poor man's casting */ + enum { + UNKNOWN = 0, + MANA_PRODUCER = 1, + MTG_ATTACK_RULE = 2, + DAMAGER = 3, + STANDARD_REGENERATE = 4, + }; }; @@ -69,12 +79,11 @@ class TriggeredAbility:public MTGAbility{ class ActivatedAbility:public MTGAbility{ public: - ManaCost * cost; int playerturnonly; int needsTapping; ActivatedAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); virtual int reactToClick(MTGCardInstance * card); - virtual int isReactingToClick(MTGCardInstance * card); + virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); virtual int reactToTargetClick(Targetable * object); virtual int resolve() = 0; virtual ~ActivatedAbility(); @@ -230,7 +239,7 @@ class AManaProducer: public MTGAbility{ AManaProducer(int id, MTGCardInstance * card, ManaCost * _output, ManaCost * _cost = NULL, int doTap = 1 ); void Update(float dt); void Render(); - int isReactingToClick(MTGCardInstance * _card); + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL); int resolve(); int reactToClick(MTGCardInstance * _card); const char * getMenuText(); diff --git a/projects/mtg/include/MTGRules.h b/projects/mtg/include/MTGRules.h index 7b0f862b7..5fbf07bd2 100644 --- a/projects/mtg/include/MTGRules.h +++ b/projects/mtg/include/MTGRules.h @@ -10,7 +10,7 @@ class MTGPutInPlayRule:public MTGAbility{ public: - int isReactingToClick(MTGCardInstance * card); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); int reactToClick(MTGCardInstance * card); int testDestroy(); MTGPutInPlayRule(int _id); @@ -20,7 +20,7 @@ class MTGPutInPlayRule:public MTGAbility{ class MTGAttackRule:public MTGAbility{ public: - int isReactingToClick(MTGCardInstance * card); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); int reactToClick(MTGCardInstance * card); int testDestroy(); MTGAttackRule(int _id); @@ -30,7 +30,7 @@ class MTGAttackRule:public MTGAbility{ class MTGBlockRule:public MTGAbility{ public: - int isReactingToClick(MTGCardInstance * card); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); int reactToClick(MTGCardInstance * card); int testDestroy(); MTGBlockRule(int _id); diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 13d5f5902..4b17d6359 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -4,9 +4,28 @@ #include "../include/DamageResolverLayer.h" #include "../include/DamagerDamaged.h" #include "../include/AIStats.h" +#include "../include/AllAbilities.h" const char * const MTG_LAND_TEXTS[] = {"artifact","forest","island","mountain","swamp","plains","other lands"}; +int AIAction::Act(){ + GameObserver * g = GameObserver::GetInstance(); + if (player){ + g->cardClick(NULL, player); + return 1; + } + if (ability){ + ability->reactToClick(click); + if (target) g->cardClick(target); + return 1; + }else if (click){ //Shouldn't be used, really... + g->cardClick(click); + if (target) g->cardClick(target); + return 1; + } + return 0; +} + AIPlayer::AIPlayer(MTGPlayerCards * _deck, string file): Player(_deck, file){ potentialMana = NEW ManaCost(); nextCardToPlay = NULL; @@ -45,7 +64,7 @@ void AIPlayer::tapLandsForMana(ManaCost * potentialMana, ManaCost * cost){ #if defined (WIN32) || defined (LINUX) OutputDebugString("tapping land for mana\n"); #endif - + if (!cost) return; ManaCost * diff = potentialMana->Diff(cost); GameObserver * gameObs = GameObserver::GetInstance(); CardDescriptor cd; @@ -64,7 +83,8 @@ void AIPlayer::tapLandsForMana(ManaCost * potentialMana, ManaCost * cost){ } } if (doTap){ - gameObs->cardClick(card); + AIAction * a = NEW AIAction(card); + clickstream.push(a); } } @@ -101,22 +121,140 @@ ManaCost * AIPlayer::getPotentialMana(){ } -//Default AI does not interrupt -int AIPlayer::checkInterrupt(){ - GameObserver * gameObs = GameObserver::GetInstance(); - if (gameObs->mLayers->stackLayer()->askIfWishesToInterrupt == this){ - gameObs->mLayers->stackLayer()->cancelInterruptOffer(); +int AIAction::getEfficiency(){ + //TODO add multiplier according to what the player wants + if (efficiency != -1) return efficiency; + if (!ability) return 0; + GameObserver * g = GameObserver::GetInstance(); + ActionStack * s = g->mLayers->stackLayer(); + Player * p = g->currentlyActing(); + if (s->has(ability)) return 0; + switch (ability->aType){ + case MTGAbility::DAMAGER: + { + ADamager * a = (ADamager *) ability; + if ( p == target->controller()){ + efficiency = 0; + }else if (a->damage >= target->toughness){ + efficiency = 100; + }else if (target->toughness){ + efficiency = (100 * a->damage) / target->toughness; + }else{ + efficiency = 0; + } + break; + } + case MTGAbility::STANDARD_REGENERATE: + { + MTGCardInstance * _target = (MTGCardInstance *)(ability->target); + PutInGraveyard * action = ((PutInGraveyard *) g->mLayers->stackLayer()->getNext(NULL,ACTION_PUTINGRAVEYARD,NOT_RESOLVED)); + int i = 0; + while(action){ + i++; + if (action->card == _target){ + efficiency = 95; + action = NULL; + }else{ + action = ((PutInGraveyard *) g->mLayers->stackLayer()->getNext(action,ACTION_PUTINGRAVEYARD,NOT_RESOLVED)); + } + } + char buf[4096]; + sprintf(buf,"Graveyard : %i\n", i); + OutputDebugString(buf); + if (efficiency == -1) efficiency = 0; + break; + } + case MTGAbility::MANA_PRODUCER: //can't use mana producers right now :/ + efficiency = 0; + break; + default: + if (target){ + efficiency = rand() % 5; //Small percentage of chance for other abilities + }else{ + efficiency = rand() % 10; + } + break; + } + return efficiency; +} + + + + +int AIPlayer::createAbilityTargets(MTGAbility * a, MTGCardInstance * c, map * ranking){ + if (!a->tc){ + AIAction * as = NEW AIAction(a,c,NULL); + (*ranking)[as] = 1; return 1; } + GameObserver * g = GameObserver::GetInstance(); + for (int i = 0; i < 2; i++){ + Player * p = g->players[i]; + MTGGameZone * playerZones[] = {p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay}; + for (int j = 0; j < 4; j++){ + MTGGameZone * zone = playerZones[j]; + for (int k=0; k < zone->nb_cards; k++){ + MTGCardInstance * t = zone->cards[k]; + if (a->tc->canTarget(t)){ + + AIAction * as = NEW AIAction(a,c,t); + (*ranking)[as] = 1; + } + } + } + } + return 1; +} + +int AIPlayer::selectAbility(){ + mapranking; + list::iterator it; + ManaCost * pMana = getPotentialMana(); + GameObserver * g = GameObserver::GetInstance(); + for (int i = 1; i < g->mLayers->actionLayer()->mCount; i++){ //0 is not a mtgability...hackish + //Make sure we can use the ability + MTGAbility * a = ((MTGAbility *)g->mLayers->actionLayer()->mObjects[i]); + for (int j=0; j < game->inPlay->nb_cards; j++){ + MTGCardInstance * card = game->inPlay->cards[j]; + if (a->isReactingToClick(card,pMana)){ + createAbilityTargets(a, card, &ranking); + } + } + } + + if (ranking.size()){ + OutputDebugString("We have a winner\n"); + AIAction * a = ranking.begin()->first; + int chance = 1 + rand() % 100; + if (a->getEfficiency() < chance){ + a = NULL; + }else{ +OutputDebugString("We REALLY have a winner\n"); + tapLandsForMana(pMana, a->ability->cost); + clickstream.push(a); + } + map::iterator it2; + for (it2 = ranking.begin(); it2!=ranking.end(); it2++){ + if (a != it2->first) delete(it2->first); + } + } + return 1; +} + + + +int AIPlayer::interruptIfICan(){ + GameObserver * g = GameObserver::GetInstance(); + + if (g->mLayers->stackLayer()->askIfWishesToInterrupt == this){ + g->mLayers->stackLayer()->setIsInterrupting(this); + return 1; + } return 0; } int AIPlayer::effectBadOrGood(MTGCardInstance * card){ int id = card->getMTGId(); - switch (id){ - default: - break; - } AbilityFactory * af = NEW AbilityFactory(); int autoGuess = af->magicText(id,NULL,card); delete af; @@ -182,14 +320,14 @@ int AIPlayer::chooseTarget(TargetChooser * tc){ case TARGET_CARD: { MTGCardInstance * card = ((MTGCardInstance *) potentialTargets[i]); - gameObs->cardClick(card); + clickstream.push(NEW AIAction(card)); return 1; break; } case TARGET_PLAYER: { Player * player = ((Player *) potentialTargets[i]); - gameObs->cardClick(NULL, player); + clickstream.push(NEW AIAction(player)); return 1; break; } @@ -236,7 +374,9 @@ int AIPlayer::chooseAttackers(){ cd.setType("creature"); MTGCardInstance * card = NULL; while((card = cd.nextmatch(game->inPlay, card))){ - GameObserver::GetInstance()->cardClick(card); + GameObserver * g = GameObserver::GetInstance(); + g->cardClick(card); + if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); } } return 1; @@ -270,7 +410,9 @@ int AIPlayer::chooseBlockers(){ opponentsToughness[attacker]-= card->power; set = 1; }else{ - GameObserver::GetInstance()->cardClick(card); + GameObserver * g = GameObserver::GetInstance(); + g->cardClick(card); + if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); } } } @@ -278,13 +420,19 @@ int AIPlayer::chooseBlockers(){ card = NULL; while((card = cd.nextmatch(game->inPlay, card))){ if (card->defenser && opponentsToughness[card->defenser] > 0){ - while (card->defenser) GameObserver::GetInstance()->cardClick(card); + while (card->defenser){ + GameObserver * g = GameObserver::GetInstance(); + g->cardClick(card); + if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); + } } } card = NULL; while((card = cd.nextmatch(game->inPlay, card))){ if(!card->defenser){ - GameObserver::GetInstance()->cardClick(card); + GameObserver * g = GameObserver::GetInstance(); + g->cardClick(card); + if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); int set = 0; while(!set){ if (!card->defenser){ @@ -292,7 +440,9 @@ int AIPlayer::chooseBlockers(){ }else{ MTGCardInstance * attacker = card->defenser; if (opponentsToughness[attacker] <= 0 || (card->toughness <= card->defenser->power && opponentForce*2 defenser->nbOpponents()>1){ - GameObserver::GetInstance()->cardClick(card); + GameObserver * g = GameObserver::GetInstance(); + g->cardClick(card); + if (g->mLayers->actionLayer()->menuObject) g->mLayers->actionLayer()->doReactTo(0); }else{ set = 1; } @@ -322,19 +472,19 @@ int AIPlayer::combatDamages(){ #endif DamagerDamaged * current = (DamagerDamaged *) drl->mObjects[i]; if (current->damageSelecter == this){ - result = 1; - DamagerDamaged * canardEmissaire = NULL; - for (int j = 0; j < drl->mCount; j++){ - DamagerDamaged * opponent = (DamagerDamaged *) drl->mObjects[j]; - if (drl->isOpponent(current, opponent)){ - if (!canardEmissaire) canardEmissaire = opponent; - int over = opponent->hasLethalDamage(); - while(!over){ - if(!current->dealOneDamage(opponent)){ - over = 1; - }else{ - over = opponent->hasLethalDamage(); - } + result = 1; + DamagerDamaged * canardEmissaire = NULL; + for (int j = 0; j < drl->mCount; j++){ + DamagerDamaged * opponent = (DamagerDamaged *) drl->mObjects[j]; + if (drl->isOpponent(current, opponent)){ + if (!canardEmissaire) canardEmissaire = opponent; + int over = opponent->hasLethalDamage(); + while(!over){ + if(!current->dealOneDamage(opponent)){ + over = 1; + }else{ + over = opponent->hasLethalDamage(); + } #if defined (WIN32) || defined (LINUX) char buf[4096]; sprintf(buf, "==========\n%s deals %i damages to %s\n=============\n", current->card->getName(), 1, opponent->card->getName()); @@ -455,9 +605,127 @@ AIPlayerBaka::AIPlayerBaka(MTGPlayerCards * _deck, char * file, char * avatarFil } void AIPlayerBaka::initTimer(){ - timer = 0.3; + timer = 0.1; } +int AIPlayerBaka::computeActions(){ + GameObserver * g = GameObserver::GetInstance(); + Player * p = g->currentPlayer; + if (!(g->currentlyActing() == this)) return 0; + if (chooseTarget()) return 1; + int currentGamePhase = g->getCurrentGamePhase(); + if (g->isInterrupting == this){ // interrupting + selectAbility(); + return 1; + }else if (p == this){ //standard actions + CardDescriptor cd; + MTGCardInstance * card = NULL; + switch(currentGamePhase){ + case Constants::MTG_PHASE_FIRSTMAIN: + case Constants::MTG_PHASE_SECONDMAIN: + if (canPutLandsIntoPlay){ + //Attempt to put land into play + cd.init(); + cd.setColor(Constants::MTG_COLOR_LAND); + card = cd.match(game->hand); + if (card){ + AIAction * a = NEW AIAction(card); + clickstream.push(a); + return 1; + } + } + + //No mana, try to get some + getPotentialMana(); + if (potentialMana->getConvertedCost() > 0){ + + + //look for the most expensive creature we can afford + nextCardToPlay = FindCardToPlay(potentialMana, "creature"); + //Let's Try an enchantment maybe ? + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "enchantment"); + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "artifact"); + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "instant"); + if (!nextCardToPlay) nextCardToPlay = FindCardToPlay(potentialMana, "sorcery"); + if (nextCardToPlay){ +#if defined (WIN32) || defined (LINUX) + char buffe[4096]; + sprintf(buffe, "Putting Card Into Play: %s", nextCardToPlay->getName()); + OutputDebugString(buffe); +#endif + + tapLandsForMana(potentialMana,nextCardToPlay->getManaCost()); + AIAction * a = NEW AIAction(nextCardToPlay); + clickstream.push(a); + return 1; + }else{ + selectAbility(); + } + }else{ + selectAbility(); + } + break; + case Constants::MTG_PHASE_COMBATATTACKERS: + chooseAttackers(); + break; + default: + selectAbility(); + break; + } + }else{ + switch(currentGamePhase){ + case Constants::MTG_PHASE_COMBATBLOCKERS: + chooseBlockers(); + break; + default: + break; + } + return 1; + } +}; + +int AIPlayerBaka::Act(float dt){ + GameObserver * g = GameObserver::GetInstance(); + int currentGamePhase = g->getCurrentGamePhase(); + + if (currentGamePhase == Constants::MTG_PHASE_CLEANUP && currentGamePhase != oldGamePhase){ + if (getStats()) getStats()->updateStats(); + } + oldGamePhase = currentGamePhase; + + timer-= dt; + if (AManaProducer::currentlyTapping || timer>0){ + return 0; + } + initTimer(); + if (combatDamages()){ + OutputDebugString("Damages and NOTHING ELSE\n"); + return 0; + } + interruptIfICan(); + if (!(g->currentlyActing() == this)){ + OutputDebugString("Cannot interrupt\n"); + return 0; + } + if (!clickstream.empty()){ + AIAction * action = clickstream.front(); + action->Act(); + SAFE_DELETE(action); + clickstream.pop(); + } + + if (clickstream.empty()) computeActions(); + if (clickstream.empty()){ + if (g->isInterrupting == this){ + g->mLayers->stackLayer()->cancelInterruptOffer(); //endOfInterruption(); + }else{ + g->userRequestNextGamePhase(); + } + } + return 1; +}; + +/* int AIPlayerBaka::Act(float dt){ GameObserver * gameObs = GameObserver::GetInstance(); int currentGamePhase = gameObs->getCurrentGamePhase(); @@ -472,13 +740,16 @@ int AIPlayerBaka::Act(float dt){ oldGamePhase = currentGamePhase; - if (checkInterrupt()) return 0; + //if (checkInterrupt()) return 0; + timer-= dt; - if (timer>0){ + if (AManaProducer::currentlyTapping || timer>0){ return 0; } initTimer(); + checkInterrupt(); + if (currentAbility) return (useAbility()); if (combatDamages()) return 0; if (chooseTarget()) return 0; @@ -575,3 +846,4 @@ int AIPlayerBaka::Act(float dt){ } return 1; } +*/ diff --git a/projects/mtg/src/ActionElement.cpp b/projects/mtg/src/ActionElement.cpp index 718d0ad49..be60891ab 100644 --- a/projects/mtg/src/ActionElement.cpp +++ b/projects/mtg/src/ActionElement.cpp @@ -12,6 +12,9 @@ ActionElement::ActionElement(int id):JGuiObject(id){ tc = NULL; } +ActionElement::~ActionElement(){ + SAFE_DELETE(tc); +} int ActionElement::getActivity(){ diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index 2b22c5a37..f7e75ff03 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -508,7 +508,7 @@ void ActionStack::Update(float dt){ } void ActionStack::cancelInterruptOffer(int cancelMode){ - if (askIfWishesToInterrupt == game->players[0]){ + if (game->isInterrupting == game->players[0]){ interruptDecision[0] = cancelMode; }else{ interruptDecision[1] = cancelMode; diff --git a/projects/mtg/src/CardDescriptor.cpp b/projects/mtg/src/CardDescriptor.cpp index 9dd6b998d..bc7074b01 100644 --- a/projects/mtg/src/CardDescriptor.cpp +++ b/projects/mtg/src/CardDescriptor.cpp @@ -54,8 +54,6 @@ MTGCardInstance * CardDescriptor::match_and(MTGCardInstance * card){ } for (int i = 0; i< Constants::MTG_NB_COLORS; i++){ if ((colors[i] == 1 && !card->hasColor(i))||(colors[i] == -1 && card->hasColor(i))){ -OutputDebugString ("Too bad for "); -OutputDebugString(card->getName()); match = NULL; } } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 3109aab66..fd070dfb3 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -1609,12 +1609,16 @@ MTGAbility::MTGAbility(int id, MTGCardInstance * card):ActionElement(id){ game = GameObserver::GetInstance(); source = card; target = card; + aType = MTGAbility::UNKNOWN; + cost = NULL; } MTGAbility::MTGAbility(int id, MTGCardInstance * _source,Damageable * _target ):ActionElement(id){ game = GameObserver::GetInstance(); source = _source; target = _target; + aType = MTGAbility::UNKNOWN; + cost = NULL; } MTGAbility::~MTGAbility(){ @@ -1644,16 +1648,21 @@ int MTGAbility::fireAbility(){ // -ActivatedAbility::ActivatedAbility(int id, MTGCardInstance * card, ManaCost * _cost, int _playerturnonly,int tap):MTGAbility(id,card), cost(_cost), playerturnonly(_playerturnonly), needsTapping(tap){ +ActivatedAbility::ActivatedAbility(int id, MTGCardInstance * card, ManaCost * _cost, int _playerturnonly,int tap):MTGAbility(id,card), playerturnonly(_playerturnonly), needsTapping(tap){ + cost = _cost; } -int ActivatedAbility::isReactingToClick(MTGCardInstance * card){ +int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ Player * player = game->currentPlayer; if (!playerturnonly) player = game->currentlyActing(); if (card == source && source->controller()==player && player==game->currentlyActing() && (!needsTapping || (!source->isTapped() && !source->hasSummoningSickness()))){ if (!cost) return 1; - if (!player->getManaPool()->canAfford(cost)) return 0; + if (!mana) mana = player->getManaPool(); + if (!mana->canAfford(cost)) return 0; + char buf[4096]; + sprintf(buf, "Will react to Click : %i\n", aType); + OutputDebugString(buf); return 1; } return 0; @@ -1751,11 +1760,9 @@ int TargetAbility::reactToClick(MTGCardInstance * card){ return 1; } }else{ - if (card == source){ - if (tc->targetsReadyCheck() == TARGET_OK || tc->targetsReadyCheck() == TARGET_OK_FULL){ - waitingForAnswer = 0; - return ActivatedAbility::reactToClick(source); - } + if (card == source && (tc->targetsReadyCheck() == TARGET_OK || tc->targetsReadyCheck() == TARGET_OK_FULL)){ + waitingForAnswer = 0; + return ActivatedAbility::reactToClick(source); }else{ if (tc->toggleTarget(card) == TARGET_OK_FULL){ @@ -2008,7 +2015,9 @@ other solutions need to be provided for abilities that add mana (ex: mana flare) AManaProducer::AManaProducer(int id, MTGCardInstance * card, ManaCost * _output, ManaCost * _cost , int doTap):MTGAbility(id, card), tap(doTap){ - LOG("==Creating ManaProducer Object"); + + LOG("==Creating ManaProducer Object"); + aType=MTGAbility::MANA_PRODUCER; cost = _cost; output=_output; x1 = 10; @@ -2072,10 +2081,11 @@ other solutions need to be provided for abilities that add mana (ex: mana flare) } - int AManaProducer::isReactingToClick(MTGCardInstance * _card){ + 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("land") || !tap || !source->hasSummoningSickness()) ){ - if (!cost || game->currentlyActing()->getManaPool()->canAfford(cost)) result = 1; + if (!cost || mana->canAfford(cost)) result = 1; } return result; } diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 6d9ad26b6..91b2f09d9 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -5,7 +5,7 @@ MTGPutInPlayRule::MTGPutInPlayRule(int _id):MTGAbility(_id, NULL){ } -int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card){ +int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ Player * player = game->currentlyActing(); Player * currentPlayer = game->currentPlayer; LOG("CANPUTINPLAY- check if card belongs to current player\n"); @@ -80,7 +80,7 @@ int MTGPutInPlayRule::testDestroy(){ MTGAttackRule::MTGAttackRule(int _id):MTGAbility(_id,NULL){ } -int MTGAttackRule::isReactingToClick(MTGCardInstance * card){ +int MTGAttackRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ if (currentPhase == Constants::MTG_PHASE_COMBATATTACKERS && card->controller() == game->currentPlayer && !card->isAttacker()){ if (card->canAttack()) return 1; } @@ -104,7 +104,7 @@ int MTGAttackRule::testDestroy(){ MTGBlockRule::MTGBlockRule(int _id):MTGAbility(_id,NULL){ } -int MTGBlockRule::isReactingToClick(MTGCardInstance * card){ +int MTGBlockRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ if (currentPhase == Constants::MTG_PHASE_COMBATBLOCKERS && !game->isInterrupting && card->controller() == game->opponent()){ if (card->canBlock()) return 1; } diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index 83cf53c2c..70e012de2 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -109,11 +109,6 @@ ManaCost::ManaCost(int _cost[], int nb_elems){ init(); int i; int total = nb_elems; -#if defined (WIN32) || defined (LINUX) - char buf[4096]; - sprintf(buf, "Create New MAnaCost, total Colors : %i\n", total); - OutputDebugString(buf); -#endif for (i = 0; i < total; i++){ cost[_cost[i*2]] = _cost[i*2 + 1]; }