- Fixed empty ActionStack "interrupt" messages
- If no attackers are declared, go straight to Combat end phase
- Once First strike damage is declared, attacking player needs to "actively" request a next phase event to go to the next damage step
- Second Damage step is called "Combat Damage (2)"
- UserRequestNextPhase is to be used knowing that it might not succeed
- Step change for GuiCombat is now computed at GameObserver::nextGamePhase

Note: Combat damage to creatures is not assigned when AI attacks. As this seems to be a problem with the previous SVN version, I4m still committing this change
This commit is contained in:
wagic.the.homebrew@gmail.com
2009-09-13 12:42:18 +00:00
parent 1f67998d7a
commit 7ce2c563e1
29 changed files with 130 additions and 128 deletions
+1 -11
View File
@@ -709,17 +709,7 @@ type=Sorcery
mana={3}{U}{U} mana={3}{U}{U}
[/card] [/card]
[card]
text=Toxic Iguanar has deathtouch as long as you control a green permanent. (Whenever it deals damage to a creature, destroy that creature.)
id=170956
name=Toxic Iguanar
rarity=C
type=Creature
mana={R}
power=1
subtype=Lizard
toughness=1
[/card]
[card] [card]
text=Counter target spell. Basic landcycling {1}{U} ({1}{U}, Discard this card: Search your library for a basic land card, reveal it, and put it into your hand. Then shuffle your library.) text=Counter target spell. Basic landcycling {1}{U} ({1}{U}, Discard this card: Search your library for a basic land card, reveal it, and put it into your hand. Then shuffle your library.)
id=179508 id=179508
@@ -17,9 +17,7 @@ belligerent hatchling
next next
#blockers #blockers
next next
#first strike #first strike damage
next
#damage
next next
#end of combat #end of combat
[ASSERT] [ASSERT]
@@ -16,9 +16,7 @@ swamp
benalish knight benalish knight
benalish knight benalish knight
next next
#first strike #first strike damage
next
#damage
next next
#end #end
[ASSERT] [ASSERT]
@@ -11,8 +11,6 @@ next
1250 1250
next next
next next
next
1370
[ASSERT] [ASSERT]
COMBATEND COMBATEND
[PLAYER1] [PLAYER1]
@@ -10,8 +10,11 @@ white knight
next next
fire elemental fire elemental
next next
#combat damage first strike
next next
fire elemental #combat damage
next
#ocombat end
[ASSERT] [ASSERT]
COMBATEND COMBATEND
[PLAYER1] [PLAYER1]
@@ -13,7 +13,7 @@ next
1365 1365
next next
next next
1370 next
[ASSERT] [ASSERT]
COMBATEND COMBATEND
[PLAYER1] [PLAYER1]
@@ -9,10 +9,6 @@ next
next next
#attackers #attackers
next next
#blockers
next
#damage
next
#combat end #combat end
[ASSERT] [ASSERT]
COMBATEND COMBATEND
@@ -8,30 +8,7 @@ life:20
inplay:1302 inplay:1302
[DO] [DO]
next next
#untap 2 eot
next
#upkeep 2
next
#draw 2
next
#main 2
next
#combatbegin 2
next
#attackers 2
next
#blockers 2
next
#combatdamage 2
next
#endofcombat 2
next
#main phase II 2
next
#eot 2
next
#cleanup2
next
#untap #untap
next next
#upkeep #upkeep
+1 -3
View File
@@ -14,9 +14,7 @@ harpoon sniper
choice 1 choice 1
grizzly bears grizzly bears
next next
#damage #combat end (no combat damage because no attacking creatures)
next
#combat end
[ASSERT] [ASSERT]
COMBATEND COMBATEND
[PLAYER1] [PLAYER1]
+1 -17
View File
@@ -33,23 +33,7 @@ next
1393 1393
1229 1229
1308 1308
next eot
#combatbegin 2
next
#combatattackers 2
next
#combatblockers 2
next
#combatdamage 2
next
#endofcombat 2
next
#main phase II 2
next
#eot 2
next
#cleanup 2
next
#untap #untap
next next
#upkeep #upkeep
@@ -14,10 +14,6 @@ next
next next
#attack #attack
next next
#blockers
next
#dmage
next
#end #end
next next
#secondmain #secondmain
-4
View File
@@ -28,10 +28,6 @@ next
#attackers 2 #attackers 2
1250 1250
next next
#blockers 2
next
#combatdamage 2
next
#endofcombat 2 #endofcombat 2
1250 1250
[ASSERT] [ASSERT]
-4
View File
@@ -19,10 +19,6 @@ next
#attackers #attackers
1250 1250
next next
#blockers
next
#damages
next
#combat end #combat end
[ASSERT] [ASSERT]
COMBATEND COMBATEND
@@ -7,8 +7,6 @@ inplay:Steelclad Serpent
[DO] [DO]
Steelclad Serpent Steelclad Serpent
next next
next
next
[ASSERT] [ASSERT]
COMBATEND COMBATEND
[PLAYER1] [PLAYER1]
@@ -14,10 +14,6 @@ next
#attackers #attackers
grizzly bears grizzly bears
next next
#blockers
next
#damage
next
#combat ends #combat ends
[ASSERT] [ASSERT]
COMBATEND COMBATEND
+1 -1
View File
@@ -14,7 +14,7 @@ next
1370 1370
next next
next next
1370 next
[ASSERT] [ASSERT]
COMBATEND COMBATEND
[PLAYER1] [PLAYER1]
+1
View File
@@ -66,6 +66,7 @@ class Interruptible: public PlayGuiObject, public Targetable{
class NextGamePhase: public Interruptible { class NextGamePhase: public Interruptible {
public: public:
int resolve(); int resolve();
bool extraDamagePhase();
void Render(); void Render();
virtual ostream& toString(ostream& out) const; virtual ostream& toString(ostream& out) const;
NextGamePhase(int id); NextGamePhase(int id);
+1
View File
@@ -30,6 +30,7 @@ public:
ActionLayer * actionLayer(); ActionLayer * actionLayer();
ActionStack * stackLayer(); ActionStack * stackLayer();
GuiCombat * combatLayer();
void init(); void init();
virtual void Update(float dt, Player * player); virtual void Update(float dt, Player * player);
void CheckUserInput(int isAI); void CheckUserInput(int isAI);
+1
View File
@@ -31,6 +31,7 @@ class GuiCombat : public GuiLayer
~GuiCombat(); ~GuiCombat();
virtual void Update(float dt); virtual void Update(float dt);
virtual void Render(); virtual void Render();
bool clickOK();
virtual bool CheckUserInput(u32 key); virtual bool CheckUserInput(u32 key);
virtual int receiveEventPlus(WEvent* e); virtual int receiveEventPlus(WEvent* e);
virtual int receiveEventMinus(WEvent* e); virtual int receiveEventMinus(WEvent* e);
+1 -1
View File
@@ -27,7 +27,7 @@ class MTGAttackRule:public MTGAbility{
virtual ostream& toString(ostream& out) const; virtual ostream& toString(ostream& out) const;
MTGAttackRule(int _id); MTGAttackRule(int _id);
const char * getMenuText(){return "Attacker";} const char * getMenuText(){return "Attacker";}
void Update(float dt); int receiveEvent(WEvent * event);
virtual MTGAttackRule * clone() const; virtual MTGAttackRule * clone() const;
}; };
+4
View File
@@ -19,6 +19,8 @@ class Phase{
}; };
class PhaseRing{ class PhaseRing{
private:
static bool extraDamagePhase(int id);
public: public:
list<Phase *> ring; list<Phase *> ring;
list<Phase *>::iterator current; list<Phase *>::iterator current;
@@ -30,6 +32,8 @@ class PhaseRing{
int addPhase(Phase * phase); int addPhase(Phase * phase);
int addPhaseBefore(int id, Player* player,int after_id, Player * after_player, int allOccurences = 1); int addPhaseBefore(int id, Player* player,int after_id, Player * after_player, int allOccurences = 1);
int removePhase (int id, Player * player, int allOccurences = 1); int removePhase (int id, Player * player, int allOccurences = 1);
static const char * phaseName(int id);
}; };
#endif #endif
+2 -2
View File
@@ -497,8 +497,9 @@ int AIPlayer::chooseBlockers(){
int AIPlayer::orderBlockers(){ int AIPlayer::orderBlockers(){
GameObserver * g = GameObserver::GetInstance(); GameObserver * g = GameObserver::GetInstance();
if (BLOCKERS == g->combatStep && g->currentPlayer==this) if (ORDER == g->combatStep && g->currentPlayer==this)
{ {
OutputDebugString("AIPLAYER: order blockers\n");
g->userRequestNextGamePhase(); //TODO clever rank of blockers g->userRequestNextGamePhase(); //TODO clever rank of blockers
return 1; return 1;
} }
@@ -747,7 +748,6 @@ int AIPlayerBaka::Act(float dt){
} }
initTimer(); initTimer();
if (combatDamages()){ if (combatDamages()){
OutputDebugString("Damages and NOTHING ELSE\n");
return 0; return 0;
} }
interruptIfICan(); interruptIfICan();
+10 -5
View File
@@ -12,25 +12,30 @@
#include "../include/TargetChooser.h" #include "../include/TargetChooser.h"
#include "../include/CardGui.h" #include "../include/CardGui.h"
#include "../include/Translate.h" #include "../include/Translate.h"
/* /*
NextGamePhase requested by user NextGamePhase requested by user
*/ */
int NextGamePhase::resolve(){ int NextGamePhase::resolve(){
GameObserver::GetInstance()->userRequestNextGamePhase(); GameObserver::GetInstance()->nextGamePhase();
return 1; return 1;
} }
void NextGamePhase::Render(){ void NextGamePhase::Render(){
int nextPhase = (GameObserver::GetInstance()->getCurrentGamePhase() + 1) % Constants::MTG_PHASE_CLEANUP; GameObserver * g = GameObserver::GetInstance();
int nextPhase = (g->getCurrentGamePhase() + 1) % Constants::MTG_PHASE_CLEANUP;
JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT); JLBFont * mFont = resources.GetJLBFont(Constants::MAIN_FONT);
mFont->SetBase(0); mFont->SetBase(0);
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE); mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
char buffer[200]; char buffer[200];
int playerId = 1; int playerId = 1;
if (GameObserver::GetInstance()->currentActionPlayer == GameObserver::GetInstance()->players[1]) playerId = 2; if (g->currentActionPlayer == GameObserver::GetInstance()->players[1]) playerId = 2;
sprintf(buffer, "%s %i : -> %s", _("Player").c_str(), playerId, _(Constants::MTGPhaseNames[nextPhase]).c_str());
sprintf(buffer, "%s %i : -> %s", _("Player").c_str(), playerId, _(PhaseRing::phaseName(nextPhase)).c_str());
mFont->DrawString(buffer, x + 30 , y, JGETEXT_LEFT); mFont->DrawString(buffer, x + 30 , y, JGETEXT_LEFT);
} }
+4
View File
@@ -71,6 +71,10 @@ ActionStack * DuelLayers::stackLayer(){
return stack; return stack;
} }
GuiCombat * DuelLayers::combatLayer(){
return combat;
}
ActionLayer * DuelLayers::actionLayer(){ ActionLayer * DuelLayers::actionLayer(){
return action; return action;
} }
+27 -5
View File
@@ -81,11 +81,28 @@ void GameObserver::nextPlayer(){
combatStep = BLOCKERS; combatStep = BLOCKERS;
} }
void GameObserver::nextGamePhase(){ 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) { nextCombatStep(); return; }
phaseRing->forward(); 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(); Phase * cPhase = phaseRing->getCurrentPhase();
currentGamePhase = cPhase->id; currentGamePhase = cPhase->id;
if (Constants::MTG_PHASE_COMBATDAMAGE == currentGamePhase) if (Constants::MTG_PHASE_COMBATDAMAGE == currentGamePhase)
nextCombatStep(); nextCombatStep();
if (currentPlayer != cPhase->player) nextPlayer(); if (currentPlayer != cPhase->player) nextPlayer();
@@ -152,11 +169,16 @@ void GameObserver::userRequestNextGamePhase(){
if (mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) return; if (mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) return;
if (getCurrentTargetChooser()) return; if (getCurrentTargetChooser()) return;
Phase * cPhaseOld = phaseRing->getCurrentPhase(); 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 ((cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS && combatStep == ORDER) ||
if (BLOCKERS == combatStep) { nextCombatStep(); return; } cPhaseOld->id == Constants::MTG_PHASE_COMBATDAMAGE ||
nextGamePhase(); opponent()->isAI() ||
options[GameOptions::phaseInterrupts[currentGamePhase]].number)
mLayers->stackLayer()->AddNextGamePhase();
else
nextGamePhase();
} }
int GameObserver::forceShuffleLibraries(){ int GameObserver::forceShuffleLibraries(){
+30 -17
View File
@@ -81,7 +81,7 @@ void GuiCombat::validateDamage()
switch (step) switch (step)
{ {
case FIRST_STRIKE : resolve(); go->nextCombatStep(); break; case FIRST_STRIKE : resolve(); go->nextCombatStep(); break;
case DAMAGE : resolve(); go->userRequestNextGamePhase(); break; case DAMAGE : resolve(); go->nextGamePhase(); break;
default: cout << "COMBAT : Cannot validate damage in this phase" << endl; break; default: cout << "COMBAT : Cannot validate damage in this phase" << endl; break;
} }
} }
@@ -117,6 +117,19 @@ void GuiCombat::removeOne(DefenserDamaged* blocker, CombatStep step)
if (!(*it)->hasLethalDamage()) { (*it)->addDamage(1, activeAtk); break; } if (!(*it)->hasLethalDamage()) { (*it)->addDamage(1, activeAtk); break; }
} }
bool GuiCombat::clickOK(){
switch (step)
{
case BLOCKERS : assert(false); return false; // that should not happen
case ORDER : go->nextGamePhase(); return true;
case FIRST_STRIKE : return false;
case DAMAGE : validateDamage(); return true;
case END_FIRST_STRIKE : return false;
case END_DAMAGE : return false; // nothing;
}
return false;
}
bool GuiCombat::CheckUserInput(u32 key) bool GuiCombat::CheckUserInput(u32 key)
{ {
if (NONE == cursor_pos) return false; if (NONE == cursor_pos) return false;
@@ -147,15 +160,7 @@ bool GuiCombat::CheckUserInput(u32 key)
} }
else if (OK == cursor_pos) else if (OK == cursor_pos)
{ {
switch (step) clickOK();
{
case BLOCKERS : assert(false); break; // that should not happen
case ORDER : go->userRequestNextGamePhase(); break;
case FIRST_STRIKE :
case DAMAGE : validateDamage(); break;
case END_FIRST_STRIKE :
case END_DAMAGE : break; // nothing;
}
} }
break; break;
case PSP_CTRL_TRIANGLE: case PSP_CTRL_TRIANGLE:
@@ -297,9 +302,13 @@ int GuiCombat::resolve() // Returns the number of damage objects dealt this turn
for (vector<Damage>::iterator d = (*it)->damages.begin(); d != (*it)->damages.end(); ++d) for (vector<Damage>::iterator d = (*it)->damages.begin(); d != (*it)->damages.end(); ++d)
stack->Add(NEW Damage(*d)); stack->Add(NEW Damage(*d));
} }
go->mLayers->stackLayer()->Add(stack);
int v = stack->mCount; int v = stack->mCount;
if (v > 0) go->mLayers->stackLayer()->resolve(); // This will delete the damage stack which will in turn delete the Damage it contains if (v > 0){
go->mLayers->stackLayer()->Add(stack);
go->mLayers->stackLayer()->resolve(); // This will delete the damage stack which will in turn delete the Damage it contains
}else{
SAFE_DELETE(stack);
}
return v; return v;
} }
@@ -434,7 +443,7 @@ int GuiCombat::receiveEventMinus(WEvent* e)
step = ORDER; step = ORDER;
} }
else else
go->userRequestNextGamePhase(); go->nextGamePhase();
return 1; return 1;
} }
case FIRST_STRIKE: case FIRST_STRIKE:
@@ -449,12 +458,15 @@ int GuiCombat::receiveEventMinus(WEvent* e)
autoaffectDamage(*attacker, FIRST_STRIKE); autoaffectDamage(*attacker, FIRST_STRIKE);
if (0 == resolve()) if (0 == resolve())
go->nextCombatStep(); go->nextCombatStep();
else //else go->mLayers->stackLayer()->AddNextGamePhase(); //uncomment to add "interrupt" offer after first strike, rather than giving priority to current player
go->mLayers->stackLayer()->AddNextGamePhase();
return 1; return 1;
case DAMAGE: DAMAGE: case DAMAGE: DAMAGE:
step = event->step; step = event->step;
if (!go->currentPlayer->displayStack()) { resolve(); go->userRequestNextGamePhase(); return 1; } if (!go->currentPlayer->displayStack()) {
//resolve();
go->nextGamePhase();
return 1;
}
for (inner_iterator attacker = attackers.begin(); attacker != attackers.end(); ++attacker) for (inner_iterator attacker = attackers.begin(); attacker != attackers.end(); ++attacker)
autoaffectDamage(*attacker, step); autoaffectDamage(*attacker, step);
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it) for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
@@ -474,7 +486,8 @@ int GuiCombat::receiveEventMinus(WEvent* e)
return 1; return 1;
case END_DAMAGE: case END_DAMAGE:
step = END_DAMAGE; step = END_DAMAGE;
if (resolve()) go->userRequestNextGamePhase(); if (0 == resolve())
go->nextGamePhase();
return 1; return 1;
} }
return 0; return 0;
+11 -8
View File
@@ -103,16 +103,19 @@ int MTGAttackRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){
return 0; return 0;
} }
void MTGAttackRule::Update(float dt){ int MTGAttackRule::receiveEvent(WEvent *e){
if (currentPhase != newPhase && currentPhase == Constants::MTG_PHASE_COMBATATTACKERS){ if (WEventPhaseChange* event = dynamic_cast<WEventPhaseChange*>(e)) {
Player * p = game->currentPlayer; if (Constants::MTG_PHASE_COMBATATTACKERS == event->from->id) {
MTGGameZone * z = p->game->inPlay; Player * p = game->currentPlayer;
for (int i= 0; i < z->nb_cards; i++){ MTGGameZone * z = p->game->inPlay;
MTGCardInstance * card = z->cards[i]; for (int i= 0; i < z->nb_cards; i++){
if (!card->isAttacker() && card->has(Constants::MUSTATTACK)) reactToClick(card); MTGCardInstance * card = z->cards[i];
if (!card->isAttacker() && card->has(Constants::MUSTATTACK)) reactToClick(card);
}
return 1;
} }
} }
MTGAbility::Update(dt); return 0;
} }
int MTGAttackRule::reactToClick(MTGCardInstance * card){ int MTGAttackRule::reactToClick(MTGCardInstance * card){
+20
View File
@@ -24,6 +24,26 @@ PhaseRing::~PhaseRing(){
} }
} }
//This needs to be controlled either by GameObserver or PhaseRing in the future
bool PhaseRing::extraDamagePhase(int id){
GameObserver * g = GameObserver::GetInstance();
if (id != Constants::MTG_PHASE_COMBATEND) return false;
if (g->combatStep != END_FIRST_STRIKE) return false;
MTGGameZone * z = g->currentPlayer->game->inPlay;
for (int i= 0; i < z->nb_cards; ++i){
MTGCardInstance * card = z->cards[i];
if (card->isAttacker() && !(card->has(Constants::FIRSTSTRIKE) || card->has(Constants::DOUBLESTRIKE))) return true;
}
return false;
}
const char * PhaseRing::phaseName(int id){
if (extraDamagePhase(id)) return "Combat Damage (2)";
return Constants::MTGPhaseNames[id];
}
Phase * PhaseRing::getCurrentPhase(){ Phase * PhaseRing::getCurrentPhase(){
if (current == ring.end()){ if (current == ring.end()){
current = ring.begin(); current = ring.begin();
+6 -2
View File
@@ -3,6 +3,7 @@
#include "../include/MTGAbility.h" #include "../include/MTGAbility.h"
#include "../include/MTGRules.h" #include "../include/MTGRules.h"
#include "../include/ActionLayer.h" #include "../include/ActionLayer.h"
#include "../include/GuiCombat.h"
#include <string> #include <string>
using std::string; using std::string;
@@ -100,8 +101,11 @@ int TestSuiteAI::Act(float dt){
humanMode = 1; humanMode = 1;
return 1; return 1;
} }
else if (action.compare("next")==0) else if (action.compare("next")==0){
g->userRequestNextGamePhase(); GuiCombat * gc = g->mLayers->combatLayer();
if (ORDER == g->combatStep || DAMAGE == g->combatStep) gc->clickOK();
else g->userRequestNextGamePhase();
}
else if (action.compare("yes")==0) else if (action.compare("yes")==0)
g->mLayers->stackLayer()->setIsInterrupting(this); g->mLayers->stackLayer()->setIsInterrupting(this);
else if (action.compare("endinterruption")==0) else if (action.compare("endinterruption")==0)