replaced literals with constants in some places. migrated some abilities out of headers and into implementation files.
939 lines
27 KiB
C++
939 lines
27 KiB
C++
/*
|
|
The Action Stack contains all information for Game Events that can be interrupted (Interruptible)
|
|
*/
|
|
#include "PrecompiledHeader.h"
|
|
|
|
#include "ActionStack.h"
|
|
#include "CardGui.h"
|
|
#include "CardSelectorSingleton.h"
|
|
#include "Damage.h"
|
|
#include "GameObserver.h"
|
|
#include "GameOptions.h"
|
|
#include "ManaCost.h"
|
|
#include "MTGAbility.h"
|
|
#include "TargetChooser.h"
|
|
#include "Translate.h"
|
|
#include "WResourceManager.h"
|
|
|
|
#include <typeinfo>
|
|
|
|
/*
|
|
NextGamePhase requested by user
|
|
*/
|
|
|
|
int NextGamePhase::resolve(){
|
|
GameObserver::GetInstance()->nextGamePhase();
|
|
return 1;
|
|
}
|
|
|
|
const string NextGamePhase::getDisplayName() const
|
|
{
|
|
std::ostringstream stream;
|
|
stream << "NextGamePhase. (Current phase is: " << PhaseRing::phaseName(GameObserver::GetInstance()->getCurrentGamePhase()) << ")";
|
|
|
|
return stream.str();
|
|
}
|
|
|
|
void NextGamePhase::Render(){
|
|
GameObserver * g = GameObserver::GetInstance();
|
|
int nextPhase = (g->getCurrentGamePhase() + 1) % Constants::MTG_PHASE_CLEANUP;
|
|
|
|
WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetBase(0);
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
char buffer[200];
|
|
int playerId = 1;
|
|
if (g->currentActionPlayer == GameObserver::GetInstance()->players[1]) playerId = 2;
|
|
|
|
sprintf(buffer, "%s %i : -> %s", _("Player").c_str(), playerId, _(PhaseRing::phaseName(nextPhase)).c_str());
|
|
|
|
mFont->DrawString(buffer, x + 30 , y, JGETEXT_LEFT);
|
|
}
|
|
|
|
NextGamePhase::NextGamePhase(int id): Interruptible(id){
|
|
mHeight = 40;
|
|
type = ACTION_NEXTGAMEPHASE;
|
|
}
|
|
|
|
ostream& NextGamePhase::toString(ostream& out) const
|
|
{
|
|
out << "NextGamePhase ::: ";
|
|
return out;
|
|
}
|
|
|
|
const string Interruptible::getDisplayName() const
|
|
{
|
|
return typeid(*this).name();
|
|
}
|
|
|
|
void Interruptible::Render(MTGCardInstance * source, JQuad * targetQuad, string alt1, string alt2, string action, bool bigQuad){
|
|
WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetColor(ARGB(255,255,255,255));
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
mFont->DrawString(_(action).c_str(), x + 30 , y, JGETEXT_LEFT);
|
|
JRenderer * renderer = JRenderer::GetInstance();
|
|
JQuad * quad = resources.RetrieveCard(source,CACHE_THUMB);
|
|
if (!quad)
|
|
quad = CardGui::alternateThumbQuad(source);
|
|
if (quad){
|
|
quad->SetColor(ARGB(255,255,255,255));
|
|
float scale = mHeight / quad->mHeight;
|
|
renderer->RenderQuad(quad, x+10*scale , y+15*scale , 0,scale,scale);
|
|
}else if (alt1.size()){
|
|
mFont->DrawString(_(alt1).c_str(),x,y-15);
|
|
}
|
|
|
|
if (bigQuad){
|
|
int showMode = CardSelectorSingleton::Instance()->GetDrawMode();
|
|
Pos pos = Pos(CardGui::BigWidth / 2, CardGui::BigHeight / 2 - 10, 1.0, 0.0, 220);
|
|
switch(showMode){
|
|
case BIG_MODE_SHOW:
|
|
CardGui::RenderBig(source,pos);
|
|
break;
|
|
case BIG_MODE_TEXT:
|
|
CardGui::alternateRender(source, pos);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (targetQuad){
|
|
float backupX = targetQuad->mHotSpotX;
|
|
float backupY = targetQuad->mHotSpotY;
|
|
targetQuad->SetColor(ARGB(255,255,255,255));
|
|
targetQuad->SetHotSpot(targetQuad->mWidth / 2, targetQuad->mHeight / 2);
|
|
float scale = mHeight / targetQuad->mHeight;
|
|
renderer->RenderQuad(targetQuad, x + 150 , y+15*scale , 0,scale,scale);
|
|
targetQuad->SetHotSpot(backupX, backupY);
|
|
}else if (alt2.size()){
|
|
mFont->DrawString(_(alt2).c_str(),x+120,y);
|
|
}
|
|
}
|
|
|
|
/* Ability */
|
|
int StackAbility::resolve(){
|
|
return (ability->resolve());
|
|
}
|
|
void StackAbility::Render(){
|
|
string action = ability->getMenuText();
|
|
MTGCardInstance * source = ability->source;
|
|
string alt1 = source->getName();
|
|
|
|
Targetable * _target = ability->target;
|
|
if (ability->tc){
|
|
Targetable * t = ability->tc->getNextTarget();
|
|
if(t) _target = t;
|
|
}
|
|
Damageable * target = NULL;
|
|
if (_target!= ability->source && (_target->typeAsTarget() == TARGET_CARD || _target->typeAsTarget() == TARGET_PLAYER)){
|
|
target = (Damageable *) _target;
|
|
}
|
|
|
|
JQuad * quad = NULL;
|
|
string alt2 = "";
|
|
if (target){
|
|
quad = target->getIcon();
|
|
if (target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE) {
|
|
alt2 = ((MTGCardInstance *)target)->name;
|
|
}
|
|
}
|
|
|
|
Interruptible::Render(source,quad,alt1,alt2,action);
|
|
}
|
|
StackAbility::StackAbility(int id,MTGAbility * _ability): Interruptible(id),ability(_ability){
|
|
type=ACTION_ABILITY;
|
|
}
|
|
|
|
ostream& StackAbility::toString(ostream& out) const
|
|
{
|
|
out << "StackAbility ::: ability : " << ability;
|
|
return out;
|
|
}
|
|
|
|
const string StackAbility::getDisplayName() const
|
|
{
|
|
std::ostringstream stream;
|
|
stream << "StackAbility. (Source: " << ability->source->getDisplayName() << ")";
|
|
|
|
return stream.str();
|
|
}
|
|
|
|
/* Spell Cast */
|
|
|
|
Spell::Spell(MTGCardInstance * _source): Interruptible(0){
|
|
source = _source;
|
|
mHeight= 40;
|
|
type = ACTION_SPELL;
|
|
cost = NEW ManaCost();
|
|
tc = NULL;
|
|
from = _source->getCurrentZone();
|
|
}
|
|
|
|
|
|
Spell::Spell(int id, MTGCardInstance * _source, TargetChooser * tc, ManaCost * _cost, int payResult): Interruptible(id), tc(tc),cost(_cost), payResult(payResult){
|
|
source = _source;
|
|
mHeight = 40;
|
|
type = ACTION_SPELL;
|
|
from = _source->getCurrentZone();
|
|
}
|
|
|
|
int Spell::computeX(MTGCardInstance * card){
|
|
ManaCost * c = cost->Diff(card->getManaCost());
|
|
int x = c->getCost(Constants::MTG_NB_COLORS);
|
|
delete c;
|
|
return x;
|
|
}
|
|
|
|
int Spell::computeXX(MTGCardInstance * card){
|
|
ManaCost * c = cost->Diff(card->getManaCost());
|
|
int xx = c->getCost(Constants::MTG_NB_COLORS)/2;
|
|
delete c;
|
|
return xx;
|
|
}
|
|
|
|
|
|
bool Spell::FullfilledAlternateCost(const int &costType)
|
|
{
|
|
bool hasFullfilledAlternateCost = false;
|
|
|
|
switch(costType)
|
|
{
|
|
case ManaCost::MANA_PAID_WITH_KICKER:
|
|
hasFullfilledAlternateCost = (payResult == ManaCost::MANA_PAID_WITH_KICKER);
|
|
break;
|
|
case ManaCost::MANA_PAID_WITH_ALTERNATIVE:
|
|
hasFullfilledAlternateCost = (payResult == ManaCost::MANA_PAID_WITH_ALTERNATIVE);
|
|
break;
|
|
case ManaCost::MANA_PAID_WITH_BUYBACK:
|
|
hasFullfilledAlternateCost = (payResult == ManaCost::MANA_PAID_WITH_BUYBACK);
|
|
break;
|
|
case ManaCost::MANA_PAID_WITH_FLASHBACK:
|
|
hasFullfilledAlternateCost = (payResult == ManaCost::MANA_PAID_WITH_FLASHBACK);
|
|
break;
|
|
case ManaCost::MANA_PAID_WITH_RETRACE:
|
|
hasFullfilledAlternateCost = (payResult == ManaCost::MANA_PAID_WITH_RETRACE);
|
|
break;
|
|
}
|
|
|
|
return hasFullfilledAlternateCost;
|
|
}
|
|
|
|
const string Spell::getDisplayName() const {
|
|
return source->getName();
|
|
}
|
|
Spell::~Spell(){
|
|
SAFE_DELETE(cost);
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
int Spell::resolve(){
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
if (!source->hasType("instant") && !source->hasType("sorcery")){
|
|
Player * p = source->controller();
|
|
source = p->game->putInZone(source,from,p->game->battlefield);
|
|
from = p->game->battlefield;
|
|
}
|
|
|
|
//Play SFX
|
|
if (options[Options::SFXVOLUME].number > 0){
|
|
JSample * sample = source->getSample();
|
|
if (sample){
|
|
JSoundSystem::GetInstance()->PlaySample(sample);
|
|
}
|
|
}
|
|
|
|
AbilityFactory af;
|
|
af.addAbilities(game->mLayers->actionLayer()->getMaxId(), this);
|
|
return 1;
|
|
}
|
|
|
|
MTGCardInstance * Spell::getNextCardTarget(MTGCardInstance * previous){
|
|
if (!tc) return NULL;
|
|
return tc->getNextCardTarget(previous);
|
|
}
|
|
Player * Spell::getNextPlayerTarget(Player * previous){
|
|
if (!tc) return NULL;
|
|
return tc->getNextPlayerTarget(previous);
|
|
}
|
|
Damageable * Spell::getNextDamageableTarget(Damageable * previous){
|
|
if (!tc) return NULL;
|
|
return tc->getNextDamageableTarget(previous);
|
|
}
|
|
Interruptible * Spell::getNextInterruptible(Interruptible * previous, int type){
|
|
if (!tc) return NULL;
|
|
return tc->getNextInterruptible(previous,type);
|
|
}
|
|
Spell * Spell::getNextSpellTarget(Spell * previous){
|
|
if (!tc) return NULL;
|
|
return tc->getNextSpellTarget(previous);
|
|
}
|
|
Damage * Spell::getNextDamageTarget(Damage * previous){
|
|
if (!tc) return NULL;
|
|
return tc->getNextDamageTarget(previous);
|
|
}
|
|
Targetable * Spell::getNextTarget(Targetable * previous, int type ){
|
|
if (!tc) return NULL;
|
|
return tc->getNextTarget(previous,type);
|
|
}
|
|
|
|
int Spell::getNbTargets(){
|
|
if (!tc) return 0;
|
|
return tc->cursor;
|
|
}
|
|
|
|
void Spell::Render(){
|
|
string action = source->getName();
|
|
string alt1 = "";
|
|
|
|
JQuad * quad = NULL;
|
|
string alt2 = "";
|
|
Damageable * target = getNextDamageableTarget();
|
|
if (target){
|
|
quad = target->getIcon();
|
|
if (target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE) {
|
|
alt2 = ((MTGCardInstance *)target)->name;
|
|
}
|
|
}
|
|
Interruptible::Render(source,quad,alt1,alt2,action, true);
|
|
}
|
|
|
|
ostream& Spell::toString(ostream& out) const
|
|
{
|
|
out << "Spell ::: cost : " << cost;
|
|
return out;
|
|
}
|
|
|
|
|
|
/* Put a card in graveyard */
|
|
|
|
PutInGraveyard::PutInGraveyard(int id, MTGCardInstance * _card):Interruptible(id){
|
|
card = _card;
|
|
removeFromGame = 0;
|
|
type = ACTION_PUTINGRAVEYARD;
|
|
}
|
|
|
|
int PutInGraveyard::resolve(){
|
|
GameObserver * g = GameObserver::GetInstance();
|
|
MTGGameZone * zone = card->getCurrentZone();
|
|
if (zone == g->players[0]->game->inPlay || zone == g->players[1]->game->inPlay){
|
|
card->owner->game->putInZone(card,zone,card->owner->game->graveyard);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void PutInGraveyard::Render(){
|
|
WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetBase(0);
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
if (!removeFromGame){
|
|
mFont->DrawString(_("goes to graveyard").c_str(), x + 30 , y, JGETEXT_LEFT);
|
|
}else{
|
|
mFont->DrawString(_("is exiled").c_str(), x + 30 , y, JGETEXT_LEFT);
|
|
}
|
|
JRenderer * renderer = JRenderer::GetInstance();
|
|
JQuad * quad = resources.RetrieveCard(card,CACHE_THUMB);
|
|
if (quad){
|
|
quad->SetColor(ARGB(255,255,255,255));
|
|
float scale = 30 / quad->mHeight;
|
|
renderer->RenderQuad(quad, x , y , 0,scale,scale);
|
|
}else{
|
|
mFont->DrawString(_(card->name).c_str(),x,y-15);
|
|
}
|
|
}
|
|
|
|
ostream& PutInGraveyard::toString(ostream& out) const
|
|
{
|
|
out << "PutInGraveyard ::: removeFromGame : " << removeFromGame;
|
|
return out;
|
|
}
|
|
|
|
/* Draw a Card */
|
|
DrawAction::DrawAction(int id, Player * _player, int _nbcards):Interruptible(id), nbcards(_nbcards), player(_player){
|
|
}
|
|
|
|
int DrawAction::resolve(){
|
|
for (int i = 0 ; i < nbcards ; i++){
|
|
player->game->drawFromLibrary();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void DrawAction::Render(){
|
|
WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetBase(0);
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
char buffer[200];
|
|
int playerId = 1;
|
|
if (player == GameObserver::GetInstance()->players[1]) playerId = 2;
|
|
sprintf(buffer, _("Player %i draws %i card").c_str(), playerId, nbcards);
|
|
mFont->DrawString(buffer, x + 20 , y, JGETEXT_LEFT);
|
|
}
|
|
|
|
ostream& DrawAction::toString(ostream& out) const
|
|
{
|
|
out << "DrawAction ::: nbcards : " << nbcards << " ; player : " << player;
|
|
return out;
|
|
}
|
|
|
|
/* The Action Stack itself */
|
|
int ActionStack::addPutInGraveyard(MTGCardInstance * card){
|
|
PutInGraveyard * death = NEW PutInGraveyard(mCount,card);
|
|
addAction(death);
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::addAbility(MTGAbility * ability){
|
|
StackAbility * stackAbility = NEW StackAbility(mCount,ability);
|
|
int result = addAction(stackAbility);
|
|
if (!game->players[0]->isAI() &&
|
|
ability->source->controller()==game->players[0] &&
|
|
0 == options[Options::INTERRUPTMYABILITIES].number)
|
|
interruptDecision[0] = DONT_INTERRUPT;
|
|
return result;
|
|
}
|
|
|
|
int ActionStack::addDraw(Player * player, int nb_cards){
|
|
DrawAction * draw = NEW DrawAction(mCount,player, nb_cards);
|
|
addAction(draw);
|
|
GameObserver *g = GameObserver::GetInstance();
|
|
WEvent * e = NEW WEventcardDraw(player,nb_cards);
|
|
g->receiveEvent(e);
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::addDamage(MTGCardInstance * _source, Damageable * _target, int _damage){
|
|
Damage * damage = NEW Damage(_source, _target, _damage);
|
|
addAction(damage);
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::AddNextGamePhase(){
|
|
if (getNext(NULL,NOT_RESOLVED)) return 0;
|
|
NextGamePhase * next = NEW NextGamePhase(mCount);
|
|
addAction(next);
|
|
int playerId = 0;
|
|
game->currentActionPlayer = game->GetInstance()->currentActionPlayer;
|
|
if (game->currentActionPlayer == game->players[1]){ playerId = 1;}
|
|
interruptDecision[playerId] = 1;
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::AddNextCombatStep(){
|
|
if (getNext(NULL,NOT_RESOLVED)) return 0;
|
|
NextGamePhase * next = NEW NextGamePhase(mCount);
|
|
addAction(next);
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::setIsInterrupting(Player * player){
|
|
if (player == game->players[0]){
|
|
interruptDecision[0] = -1;
|
|
}else{
|
|
interruptDecision[1] = -1;
|
|
}
|
|
game->isInterrupting = player;
|
|
askIfWishesToInterrupt = NULL;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int ActionStack::addAction(Interruptible * action){
|
|
for (int i=0; i<2; i++){
|
|
interruptDecision[i] = 0;
|
|
}
|
|
Add(action);
|
|
DebugTrace("Action added to stack: " << action->getDisplayName());
|
|
|
|
return 1;
|
|
}
|
|
|
|
Spell * ActionStack::addSpell(MTGCardInstance * _source, TargetChooser * tc, ManaCost * mana, int payResult,int storm){
|
|
DebugTrace("ACTIONSTACK Add spell");
|
|
if(storm > 0){ mana = NULL;}
|
|
if(mana < 0) {mana = 0;}
|
|
Spell * spell = NEW Spell(mCount,_source,tc, mana,payResult);
|
|
addAction(spell);
|
|
if (!game->players[0]->isAI() &&
|
|
_source->controller()==game->players[0] &&
|
|
0 == options[Options::INTERRUPTMYSPELLS].number)
|
|
interruptDecision[0] = DONT_INTERRUPT;
|
|
return spell;
|
|
}
|
|
|
|
|
|
Interruptible * ActionStack::getAt(int id){
|
|
if (id < 0) id = mCount + id;
|
|
if (id > mCount -1) return NULL;
|
|
return (Interruptible *)mObjects[id];
|
|
}
|
|
|
|
ActionStack::ActionStack(GameObserver* game) : game(game){
|
|
for (int i=0; i<2; i++)
|
|
interruptDecision[i] = 0;
|
|
askIfWishesToInterrupt = NULL;
|
|
timer = -1;
|
|
currentState = -1;
|
|
mode = ACTIONSTACK_STANDARD;
|
|
checked = 0;
|
|
|
|
}
|
|
|
|
int ActionStack::has(MTGAbility * ability){
|
|
for (int i = 0; i < mCount ; i++){
|
|
if (((Interruptible *)mObjects[i])->type==ACTION_ABILITY){
|
|
StackAbility * action = ((StackAbility *)mObjects[i]);
|
|
if (action->state == NOT_RESOLVED && action->ability == ability) return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ActionStack::has(Interruptible * action){
|
|
for (int i = 0; i < mCount ; i++){
|
|
if (mObjects[i] == action) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ActionStack::resolve(){
|
|
Interruptible * action = getLatest(NOT_RESOLVED);
|
|
|
|
if (!action)
|
|
return 0;
|
|
|
|
DebugTrace("Resolving Action on stack: " << action->getDisplayName());
|
|
if (action->resolve()){
|
|
action->state = RESOLVED_OK;
|
|
}else{
|
|
action->state = RESOLVED_NOK;
|
|
}
|
|
if (action->type == ACTION_DAMAGE) ((Damage * )action)->target->afterDamage();
|
|
|
|
if (!getNext(NULL,NOT_RESOLVED)){
|
|
for (int i = 0; i< 2 ; i++){
|
|
interruptDecision[i] = 0;
|
|
}
|
|
}else{
|
|
for (int i = 0; i< 2 ; i++){
|
|
if (interruptDecision[i] != 2) interruptDecision[i] = 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Interruptible * ActionStack::getPrevious(Interruptible * next, int type, int state, int display){
|
|
int n = getPreviousIndex( next, type, state, display);
|
|
if (n==-1) return NULL;
|
|
return ((Interruptible *) mObjects[n]);
|
|
}
|
|
|
|
int ActionStack::getPreviousIndex(Interruptible * next, int type, int state, int display){
|
|
int found = 0;
|
|
if (!next) found = 1;
|
|
for (int i = mCount -1; i >= 0 ; i--){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if (found && (type == 0 || current->type == type) && (state == 0 || current->state == state) && (display == -1 || current->display == display)){
|
|
return i;
|
|
}
|
|
if (current == next) found = 1;
|
|
}
|
|
if (!found) return getPreviousIndex(NULL,type, state, display);
|
|
return -1;
|
|
}
|
|
|
|
int ActionStack::count( int type, int state, int display){
|
|
int result = 0;
|
|
for (int i = 0; i < mCount ; i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if((type == 0 || current->type == type) && (state == 0 || current->state == state) && (display == -1 || current->display == display)){
|
|
result++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Interruptible * ActionStack::getNext(Interruptible * previous, int type, int state, int display){
|
|
int n = getNextIndex( previous, type, state, display);
|
|
if (n==-1) return NULL;
|
|
return ((Interruptible *) mObjects[n]);
|
|
}
|
|
|
|
int ActionStack::getNextIndex(Interruptible * previous, int type, int state, int display){
|
|
int found = 0;
|
|
if (!previous) found = 1;
|
|
for (int i = 0; i < mCount ; i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if (found && (type == 0 || current->type == type) && (state == 0 || current->state == state) && (display == -1 || current->display == display)){
|
|
return i;
|
|
}
|
|
if (current == previous) found = 1;
|
|
}
|
|
if (!found) return getNextIndex(NULL,type, state, display);
|
|
return -1;
|
|
}
|
|
|
|
|
|
Interruptible * ActionStack::getLatest(int state){
|
|
for (int i = mCount-1; i >=0; i--){
|
|
Interruptible * action = ((Interruptible *)mObjects[i]);
|
|
if (action->state == state) return action;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int ActionStack::receiveEventPlus(WEvent * event) {
|
|
int result = 0;
|
|
for (int i = 0; i < mCount ; ++i){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
result += current->receiveEvent(event);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ActionStack::Update(float dt){
|
|
askIfWishesToInterrupt = NULL;
|
|
//modal = 0;
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
TargetChooser * tc = game->getCurrentTargetChooser();
|
|
int newState = game->getCurrentGamePhase();
|
|
currentState = newState;
|
|
if (!tc) checked = 0;
|
|
|
|
//Select Stack's display mode
|
|
if (mode==ACTIONSTACK_STANDARD && tc && !checked){
|
|
checked = 1;
|
|
for (int i = 0; i < mCount ; 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)){
|
|
int currentPlayerId = 0;
|
|
int otherPlayerId = 1;
|
|
if (game->currentlyActing() != game->players[0]){
|
|
currentPlayerId = 1;
|
|
otherPlayerId = 0;
|
|
}
|
|
if (interruptDecision[currentPlayerId] == NOT_DECIDED){
|
|
askIfWishesToInterrupt = game->players[currentPlayerId];
|
|
game->isInterrupting = game->players[currentPlayerId];
|
|
modal = 1;
|
|
}else if (interruptDecision[currentPlayerId] == INTERRUPT){
|
|
game->isInterrupting = game->players[currentPlayerId];
|
|
|
|
}else{
|
|
if (interruptDecision[otherPlayerId] == NOT_DECIDED){
|
|
askIfWishesToInterrupt = game->players[otherPlayerId];
|
|
game->isInterrupting = game->players[otherPlayerId];
|
|
modal = 1;
|
|
}else if (interruptDecision[otherPlayerId] == INTERRUPT){
|
|
game->isInterrupting = game->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)
|
|
{
|
|
if (timer < 0) timer = options[Options::INTERRUPT_SECONDS].number;
|
|
timer -= dt;
|
|
if (timer < 0) cancelInterruptOffer();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActionStack::cancelInterruptOffer(int cancelMode){
|
|
if (game->isInterrupting == game->players[0]){
|
|
interruptDecision[0] = cancelMode;
|
|
}else{
|
|
interruptDecision[1] = cancelMode;
|
|
}
|
|
askIfWishesToInterrupt = NULL;
|
|
game->isInterrupting = NULL;
|
|
timer = -1;
|
|
}
|
|
|
|
void ActionStack::endOfInterruption(){
|
|
if (game->isInterrupting == game->players[0]){
|
|
interruptDecision[0] = 0;
|
|
}else{
|
|
interruptDecision[1] = 0;
|
|
}
|
|
game->isInterrupting = NULL;
|
|
}
|
|
|
|
|
|
bool ActionStack::CheckUserInput(JButton key){
|
|
JButton trigger = (options[Options::REVERSETRIGGERS].number ? JGE_BTN_NEXT : JGE_BTN_PREV);
|
|
if (mode == ACTIONSTACK_STANDARD){
|
|
if (askIfWishesToInterrupt){
|
|
if (JGE_BTN_SEC == key){
|
|
setIsInterrupting(askIfWishesToInterrupt);
|
|
return true;
|
|
}else if ((JGE_BTN_OK == key) || (trigger == key) ){
|
|
cancelInterruptOffer();
|
|
return true;
|
|
}else if ((JGE_BTN_PRI == key)){
|
|
cancelInterruptOffer(2);
|
|
return true;
|
|
}
|
|
return true;
|
|
}else if (game->isInterrupting){
|
|
if (JGE_BTN_SEC == key){
|
|
endOfInterruption();
|
|
return true;
|
|
}
|
|
}
|
|
}else if (mode == ACTIONSTACK_TARGET){
|
|
if (modal){
|
|
if (JGE_BTN_UP == key){
|
|
if( mObjects[mCurr]){
|
|
int n = getPreviousIndex(((Interruptible *) mObjects[mCurr]), 0, 0, 1);
|
|
if (n != -1 && n != mCurr && mObjects[mCurr]->Leaving(JGE_BTN_UP)){
|
|
mCurr = n;
|
|
mObjects[mCurr]->Entering();
|
|
DebugTrace("ACTIONSTACK UP TO mCurr = " << mCurr);
|
|
}
|
|
}
|
|
return true;
|
|
}else if (JGE_BTN_DOWN == key){
|
|
if( mObjects[mCurr]){
|
|
int n = getNextIndex(((Interruptible *) mObjects[mCurr]), 0, 0, 1);
|
|
if (n!= -1 && n != mCurr && mObjects[mCurr]->Leaving(JGE_BTN_DOWN)){
|
|
mCurr = n;
|
|
mObjects[mCurr]->Entering();
|
|
DebugTrace("ACTIONSTACK DOWN TO mCurr " << mCurr);
|
|
}
|
|
}
|
|
return true;
|
|
}else if (JGE_BTN_OK == key){
|
|
DebugTrace("ACTIONSTACK CLICKED mCurr = " << mCurr);
|
|
|
|
game->stackObjectClicked(((Interruptible *) mObjects[mCurr]));
|
|
return true;
|
|
}
|
|
return true; //Steal the input to other layers if we're visible
|
|
}
|
|
if (JGE_BTN_CANCEL == key){
|
|
if (modal) modal = 0; else modal = 1;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//Cleans history of last turn
|
|
int ActionStack::garbageCollect(){
|
|
std::vector<JGuiObject *>::iterator iter = mObjects.begin() ;
|
|
|
|
while( iter != mObjects.end() ){
|
|
Interruptible * current = ((Interruptible *) *iter);
|
|
if (current->state != NOT_RESOLVED){
|
|
iter = mObjects.erase( iter ) ;
|
|
mCount--;
|
|
SAFE_DELETE(current);
|
|
} else
|
|
++iter;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void ActionStack::Fizzle(Interruptible * action){
|
|
if (!action){
|
|
DebugTrace("ACTIONSTACK ==ERROR==: action is NULL in ActionStack::Fizzle");
|
|
return;
|
|
}
|
|
if (action->type == ACTION_SPELL){
|
|
Spell * spell = (Spell *) action;
|
|
spell->source->controller()->game->putInGraveyard(spell->source);
|
|
}
|
|
action->state = RESOLVED_NOK;
|
|
}
|
|
|
|
void ActionStack::Render(){
|
|
float x0 = 250;
|
|
float y0 = 30;
|
|
float width = 200;
|
|
float height = 90;
|
|
float currenty = y0 + 5 ;
|
|
|
|
if (mode == ACTIONSTACK_STANDARD){
|
|
if (!askIfWishesToInterrupt || !askIfWishesToInterrupt->displayStack()) return;
|
|
|
|
|
|
for (int i=0;i<mCount ;i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if (current->state==NOT_RESOLVED) height += current->mHeight;
|
|
}
|
|
|
|
WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetBase(0);
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
mFont->SetColor(ARGB(255,255,255,255));
|
|
JRenderer * renderer = JRenderer::GetInstance();
|
|
|
|
//JQuad * back = resources.GetQuad("interrupt");
|
|
//float xScale = width / back->mWidth;
|
|
//float yScale = height / back->mHeight;
|
|
renderer->FillRoundRect(x0 + 16 ,y0 + 16 ,width +2 ,height +2 , 10, ARGB(128,0,0,0));
|
|
renderer->FillRoundRect(x0 - 5 ,y0 - 5 ,width + 2,height +2 , 10, ARGB(200,0,0,0));
|
|
//renderer->RenderQuad(back,x0,y0,0,xScale, yScale);
|
|
renderer->DrawRoundRect(x0 - 5 ,y0 - 5 ,width + 2,height +2 , 10, ARGB(255,255,255,255));
|
|
|
|
|
|
for (int i=0;i<mCount ;i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if (current && current->state==NOT_RESOLVED){
|
|
current->x = x0 + 5;
|
|
if (i != mCount -1){
|
|
current->y = currenty;
|
|
currenty += current->mHeight;
|
|
}else{
|
|
current->y = currenty + 40 ;
|
|
currenty += current->mHeight + 40;
|
|
}
|
|
current->Render();
|
|
}
|
|
}
|
|
|
|
char buffer[200];
|
|
// WALDORF - changed "interrupt ?" to "Interrupt?". Don't display count down
|
|
// seconds if the user disables auto progressing interrupts by setting the seconds
|
|
// value to zero in Options.
|
|
if (options[Options::INTERRUPT_SECONDS].number == 0)
|
|
sprintf(buffer, "%s", _("Interrupt?").c_str());
|
|
else
|
|
sprintf(buffer, "%s %i", _("Interrupt?").c_str(),static_cast<int>(timer));
|
|
|
|
//WALDORF - removed all the unnecessary math. just display the prompt at the
|
|
// top of the box.
|
|
mFont->DrawString(buffer, x0 + 5, y0);
|
|
|
|
|
|
if (mCount > 1) sprintf(buffer, "%s", _("X Interrupt - 0 No - [] No to All").c_str());
|
|
else sprintf(buffer, "%s", _("X Interrupt - 0 No").c_str());
|
|
|
|
// WALDORF - puts the button legend right under the prompt. the stack
|
|
// will be displayed below it now. no more need to do wierd currY math.
|
|
mFont->DrawString(buffer, x0 + 5, y0 + 14);
|
|
}else if (mode == ACTIONSTACK_TARGET && modal){
|
|
for (int i=0;i<mCount ;i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if (current->display) height += current->mHeight;
|
|
}
|
|
|
|
WFont * mFont = resources.GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
mFont->SetColor(ARGB(255,255,255,255));
|
|
|
|
JRenderer * renderer = JRenderer::GetInstance();
|
|
renderer->FillRect(x0 ,y0 , width ,height , ARGB(200,0,0,0));
|
|
renderer->DrawRect(x0 - 1 ,y0 - 1 ,width + 2 ,height +2 , ARGB(255,255,255,255));
|
|
|
|
|
|
for (int i=0;i<mCount ;i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
if (mObjects[i]!=NULL && current->display){
|
|
((Interruptible *)mObjects[i])->x = x0 + 5;
|
|
if (i != mCount -1){
|
|
((Interruptible *)mObjects[i])->y = currenty;
|
|
currenty += ((Interruptible *)mObjects[i])->mHeight;
|
|
}else{
|
|
((Interruptible *)mObjects[i])->y = currenty + 40 ;
|
|
currenty += ((Interruptible *)mObjects[i])->mHeight + 40;
|
|
}
|
|
mObjects[i]->Render();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined (WIN32) || defined (LINUX)
|
|
|
|
void Interruptible::Dump(){
|
|
string stype, sstate, sdisplay = "";
|
|
switch (type){
|
|
case ACTION_SPELL:
|
|
stype = "spell";
|
|
break;
|
|
case ACTION_DAMAGE:
|
|
stype = "damage";
|
|
break;
|
|
case ACTION_DAMAGES:
|
|
stype = "damages";
|
|
break;
|
|
case ACTION_NEXTGAMEPHASE:
|
|
stype = "next phase";
|
|
break;
|
|
case ACTION_DRAW:
|
|
stype = "draw";
|
|
break;
|
|
case ACTION_PUTINGRAVEYARD:
|
|
stype = "put in graveyard";
|
|
break;
|
|
case ACTION_ABILITY:
|
|
stype = "ability";
|
|
break;
|
|
default:
|
|
stype = "unknown";
|
|
break;
|
|
}
|
|
|
|
switch(state){
|
|
case NOT_RESOLVED:
|
|
sstate = "not resolved";
|
|
break;
|
|
case RESOLVED_OK:
|
|
sstate = "resolved";
|
|
break;
|
|
case RESOLVED_NOK:
|
|
sstate = "fizzled";
|
|
break;
|
|
default:
|
|
sstate = "unknown";
|
|
break;
|
|
}
|
|
DebugTrace("type: " << stype << " " << type << " - state: " << sstate << " " << state << " - display: " << display);
|
|
}
|
|
|
|
void ActionStack::Dump(){
|
|
DebugTrace("=====\nDumping Action Stack=====");
|
|
for (int i=0;i<mCount ;i++){
|
|
Interruptible * current = (Interruptible *)mObjects[i];
|
|
current->Dump();
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|