93d9347566
========================================
added a function to return the action element id by mtgcardinstance.
=======================================
refactored all 5 combat triggers to be handled in a single class, this change helps reduce the "bits and pieces" of trigger restrictions which some did support while others didnt, it also allows for easier editing and debugging if future code needs to be added to it.
refactored the parsing for the combat triggers also, so that a combat trigger can be built from a single object if it only contains "or"...amongst other reasons.
the new syntax is auto=@combat(blocking,attacking,turnlimited,once) source(TC) from(TC):effect...all the same words are still used, except they are now all included inside the combat(-----) this includes restrictions for the triggers. if a card still needs multiple triggers from some reason...it is still 100% allowed to have as many as you want, with whatever you want inside them...check updated primitive for examples.
the required sections are auto=@combat(atleast one trigger) source(TC):effect.
=========================================
moved the limitsperturn "limit:" from genericactivatedability up to its parent class activatedability, did this for the following reason
first rules correction, the previous method tracked uses on resolve, which is incorrect, it should count as the ability is fired and placed on the stack.
second, it keeps all the actiavted ability restrictions in the same place, this is the location we are checking summoning sickness, actived restrictions such as "myturnonly" ect. makes sense to handle it in the parent.
third, it allow any and all future activated abilities easy access to "limit:" if needed, it would only require adding a limit string to pass on construction to the child classes instead of recoding the same exact checks as genericactivated.
=======================================================
reworked "name(" so that it no longer is required to be used inside a "&&" ability, also it no longer will create a MTGAbility object with a false return. that was just dirty of me :/
enabled "name(" to replace the menutext of alternative cost.
"other={cost} name(holy cow)"
will display "holy cow" instead of "pay alternative cost", this change is nice because we ended up using "other" cost for WAY more then originally planned...now we can label it exactly what it should say when you click it.
======================================================
added a subtype keyword for powertoughnessmodifier so that you can basically switch on non-static variables for it...lifetotal/lifetotal nonstatic...PT can now except all word variables as i added reparsing of the ints, had to maintain old methods as ai needs them to decide what to do with them.
======================================================
removed the following extremely redundant classes
both ABecomes classes---atransformer has become far superior to it, i kept the "becomes(" parsing tho, and it will act exactly as it use to, except now it has access to most of the subkeywords of transforms...it also now allows word variables. and excepts "forever" as a tag. added "newcolor" subkeyword to transforms to create the "becomes(" adding a color without removing colors effect. "becomes(" now returns a constructor for ATransformer that emulates becomes( exactly as it worked before.
both forevertransformer classes, the only difference between the 2 was one subkeyword, and "forever" did not return a "destroy()"...i recreated this in less then 4 lines and a bool. that was sloppy of me :( i guess we learn and improve.
============================================================================
i removed the parsing of card casting restriction from the first pass when cards load, instead of making it a cardprimitive object, i now simply pass the string through the card and build the restrictions when theyre actually needed and checked.
this change allowed for alot of clean up, removing enum and dependancy on cardprimitive, and combining both allowedAltCast and allowedtocast functions checks through a new function "parseCastRestrictions"...this change keeps all the cast resrictions in a single place, which makes it easier to debug or add if needed, and while i was at it, i enabled it so cards can now contain as many restrictions as you want *of the available ones*
added "turn:" as a restriction to make it so "fourth turn" could actually be ANY turn you list, this allowed me to remove a dirty line of code which was checking if a card was o converted cost but had a suspend...lotus bloom...which can not be cast normally..for cards like this now you can use restriction=turn:200 or otherrestriction=turn:200....
=========================================================================
added Phyrexian Mana...svntax {p(r)} ....this will always be payable with 2 life, but if you have a red mana for this example, it will charge you the red mana instead...you can have any combination of P mana you want in any of the games manacost.
===================================================
reworked a bit of the ai logic i added, it now has true interrupting and can now correctly target for fizzles, Ai will go into full out counterspell wars with you now...
===================================================
added a ingame reward system with fancy flying text animations, they reward the player with actual credits, but are extremely hard to trigger off, theyre not meant to be something you see every 5 secs, but something that when you see them trigger you go "damn that was cool"
the triggers and effects are as follows
first the combo system:
you have to chain cast 5 or more card, without tapping or being interupted, tiny bonus for this..chain is broken as soon as you or your opponent tap something.
next combo level is
Abundant Resources - chain 10 or more spells same condition
then
killer - chain 15 or more same condition
this will include a flying text render once a chain is successfully started (5+ cast)
this bonus can be triggered multiple times in a match
the next bonus is
+ //creatures entering play consecutively will allow you a chance
+ //to gain a bonus for maintaining force sizes, it will trigger every 10th
+ //creature which enters play consecutively.
this is restarted every time a noncreature enters play.
the levels are as follows,
Deadly Force Bonus! -10 creatures enter play consecutively
Extreme Infantry Bonus! - 10 creatures enter play consecutively and you maintain a force size of 20+
Malignant Conqueror Bonus! -10 creatures enter play consecutively and you maintain a force size of 40+
this can only be triggered once per level per match.
next bonus is given for having alot of a specific type come into play under your control during a match, this bonus only triggers once per match.
the levels are:
Toy Collector!
Beast Tamer!
Vampire King!
Lord of Swarms!
Master of Elements!
Zombie Apocalypse!
Sword And Shield!
Medic!
The Promenade!
Heavenly Host!
Teeth And Scales!
its pretty easy to figure out what each requires to trigger, teeth and scales for example is dragons, wurms, drakes, and snakes.
the final bonus is deal 100 damage from a single source in a single damage event, this one triggers only once per match.
============================================================
fixed the following bugs::::
regenerate was not working for non-creature regeneration ie:welding jar
ai would sometime get stuck in a infinate loop tho extremely rarely while deciding what to do, the cause
if (clickstream.empty())
computeActions();
is NOT enough...ai should NEVER be computing actions (finding a card or ability to play) when it is NOT the active player..meaning, it does not have priority.
g->currentlyActing() is the player that has priority.
if (clickstream.empty() && g->currentlyActing() == this)...loop fixed :)
dynamicability had a couple weird results, from sources which dont have an amount to return, i now check this and if non, the amount is 0.
transformer will no longer add the same types a card already has..
removed an unneeded
if (!activated) ....oris bug seems to be corrected with this change.
wrapped limitsperturn checks in a conditional to skip it if there is no limit string.
added safer method to get the target of the combat trigger. the previous could potentially cause a crash if you interrupted the ability while the combat trigger was on the stack, it would do a call to "getnextopponent" and return a null pointer. i now send the opponent with the event.
fixed a crash that would happen when a card did not have a type= line. having a type or subtype line is now optional. and highly recommended to avoid using "type=nothing"
moved a function call in buyback isreactingtoclick so that it checks if its in the hand before parsing the restrictions for it.
removed allowedtocast function calls from the alternative payment types, they are only supposed to check allowedtocast if it is something like "buyback" and kicker...not "you may pay this instead"....
corrected an issue with type: variable where it would add the amount depending on the activeplayer. to correct i added a method to call TargetZoneChooser::targetsZone( with a mSource...so that you can set the source card for the scan.
note: all test pass.
1296 lines
36 KiB
C++
1296 lines
36 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 "Subtypes.h"
|
|
#include "TargetChooser.h"
|
|
#include "Translate.h"
|
|
#include "WResourceManager.h"
|
|
|
|
#include <typeinfo>
|
|
|
|
namespace
|
|
{
|
|
float kGamepadIconSize = 0.5f;
|
|
|
|
std::string kInterruptMessageString("Interrupt?");
|
|
std::string kInterruptString(": Interrupt");
|
|
std::string kNoString(": No");
|
|
std::string kNoToAllString(": No To All");
|
|
}
|
|
|
|
/*
|
|
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 = WResourceManager::Instance()->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();
|
|
}
|
|
|
|
float Interruptible::GetVerticalTextOffset() const
|
|
{
|
|
static const float kTextVerticalOffset = (mHeight - WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT)->GetHeight()) / 2;
|
|
return kTextVerticalOffset;
|
|
}
|
|
|
|
void Interruptible::Render(MTGCardInstance * source, JQuad * targetQuad, string alt1, string alt2, string action,
|
|
bool bigQuad)
|
|
{
|
|
WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetColor(ARGB(255,255,255,255));
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
|
|
mFont->DrawString(_(action).c_str(), x + 35, y + GetVerticalTextOffset(), JGETEXT_LEFT);
|
|
JRenderer * renderer = JRenderer::GetInstance();
|
|
JQuadPtr quad = WResourceManager::Instance()->RetrieveCard(source, CACHE_THUMB);
|
|
if (!quad.get())
|
|
quad = CardGui::AlternateThumbQuad(source);
|
|
if (quad.get())
|
|
{
|
|
quad->SetColor(ARGB(255,255,255,255));
|
|
float scale = mHeight / quad->mHeight;
|
|
renderer->RenderQuad(quad.get(), x + (quad->mWidth * scale / 2), y + (quad->mHeight * scale / 2), 0, scale, scale);
|
|
}
|
|
else if (alt1.size())
|
|
{
|
|
mFont->DrawString(_(alt1).c_str(), x, y + GetVerticalTextOffset());
|
|
}
|
|
|
|
if (bigQuad)
|
|
{
|
|
Pos pos = Pos(CardGui::BigWidth / 2, CardGui::BigHeight / 2 - 10, 1.0, 0.0, 220);
|
|
CardGui::DrawCard(source, pos, CardSelectorSingleton::Instance()->GetDrawMode());
|
|
}
|
|
|
|
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 + ((mHeight - targetQuad->mHeight) / 2) + targetQuad->mHotSpotY, 0, scale, scale);
|
|
targetQuad->SetHotSpot(backupX, backupY);
|
|
}
|
|
else if (alt2.size())
|
|
{
|
|
mFont->DrawString(_(alt2).c_str(), x + 120, y + GetVerticalTextOffset());
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
JQuadPtr quad;
|
|
string alt2 = "";
|
|
if (target)
|
|
{
|
|
quad = target->getIcon();
|
|
if (target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
alt2 = ((MTGCardInstance *) target)->name;
|
|
}
|
|
}
|
|
|
|
Interruptible::Render(source, quad.get(), 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();
|
|
payResult = ManaCost::MANA_UNPAID;
|
|
source->castMethod = Constants::NOT_CAST;
|
|
}
|
|
|
|
Spell::Spell(int id, MTGCardInstance * _source, TargetChooser * tc, ManaCost * _cost, int payResult) :
|
|
Interruptible(id), tc(tc), cost(_cost), payResult(payResult)
|
|
{
|
|
if (!cost) cost = NEW ManaCost();
|
|
source = _source;
|
|
mHeight = 40;
|
|
type = ACTION_SPELL;
|
|
from = _source->getCurrentZone();
|
|
for(int i = 0;i < MAX_TARGETS;i++)
|
|
{
|
|
if(tc && tc->targets[i] != NULL)
|
|
_source->backupTargets[i] = tc->targets[i];
|
|
}
|
|
|
|
// fill information on how the card came into this zone. Right now the quickest way is to do it here, based on how the mana was paid...
|
|
switch(payResult) {
|
|
case ManaCost::MANA_UNPAID:
|
|
source->castMethod = Constants::NOT_CAST;
|
|
break;
|
|
case ManaCost::MANA_PAID:
|
|
case ManaCost::MANA_PAID_WITH_KICKER:
|
|
source->castMethod = Constants::CAST_NORMALLY;
|
|
break;
|
|
default:
|
|
source->castMethod = Constants::CAST_ALTERNATE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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(Subtypes::TYPE_INSTANT) && !source->hasType(Subtypes::TYPE_SORCERY))
|
|
{
|
|
Player * p = source->controller();
|
|
int castMethod = source->castMethod;
|
|
source = p->game->putInZone(source, from, p->game->battlefield);
|
|
|
|
// We need to get the information about the cast method on both the card in the stack AND the card in play,
|
|
//so we copy it from the previous card (in the stack) to the new one (in play).
|
|
source->castMethod = castMethod;
|
|
|
|
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 = "";
|
|
|
|
string alt2 = "";
|
|
Damageable * target = getNextDamageableTarget();
|
|
JQuadPtr quad;
|
|
if (target)
|
|
{
|
|
quad = target->getIcon();
|
|
if (target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
alt2 = ((MTGCardInstance *) target)->name;
|
|
}
|
|
}
|
|
Interruptible::Render(source, quad.get(), 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 = WResourceManager::Instance()->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();
|
|
JQuadPtr quad = WResourceManager::Instance()->RetrieveCard(card, CACHE_THUMB);
|
|
if (quad.get())
|
|
{
|
|
quad->SetColor(ARGB(255,255,255,255));
|
|
float scale = 30 / quad->mHeight;
|
|
renderer->RenderQuad(quad.get(), 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 = WResourceManager::Instance()->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 + 35, y + GetVerticalTextOffset(), JGETEXT_LEFT);
|
|
}
|
|
|
|
ostream& DrawAction::toString(ostream& out) const
|
|
{
|
|
out << "DrawAction ::: nbcards : " << nbcards << " ; player : " << player;
|
|
return out;
|
|
}
|
|
//////
|
|
LifeAction::LifeAction(int id, Damageable * _target, int amount) :
|
|
Interruptible(id), amount(amount),target(_target)
|
|
{
|
|
}
|
|
|
|
int LifeAction::resolve()
|
|
{
|
|
target->life += amount;
|
|
return 1;
|
|
}
|
|
|
|
void LifeAction::Render()
|
|
{
|
|
WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
|
|
mFont->SetBase(0);
|
|
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
|
|
char buffer[200];
|
|
if(amount >= 0)
|
|
sprintf(buffer, _("Player gains %i life").c_str(), amount);
|
|
else if(amount >= 0)
|
|
sprintf(buffer, _("Player loses %i life").c_str(), amount);
|
|
else
|
|
sprintf(buffer, _("Nothing happened").c_str(), amount);
|
|
mFont->DrawString(buffer, x + 20, y, JGETEXT_LEFT);
|
|
}
|
|
|
|
ostream& LifeAction::toString(ostream& out) const
|
|
{
|
|
out << "LifeAction ::: amount : " << amount << " ; target : " << target;
|
|
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();
|
|
for(int i = nb_cards; i > 0;i--)
|
|
{
|
|
WEvent * e = NEW WEventcardDraw(player, nb_cards);
|
|
g->receiveEvent(e);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::addLife(Damageable * _target, int amount)
|
|
{
|
|
LifeAction * life = NEW LifeAction(mCount, _target, amount);
|
|
addAction(life);
|
|
return 1;
|
|
}
|
|
|
|
int ActionStack::addDamage(MTGCardInstance * _source, Damageable * _target, int _damage)
|
|
{
|
|
Damage * damage = NEW Damage(_source, _target, _damage);
|
|
addAction(damage);
|
|
_source->thatmuch = _damage;
|
|
_target->thatmuch = _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;
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
std::ostringstream stream;
|
|
stream << "iconspsp" << i;
|
|
pspIcons[i] = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", (float) i * 32, 0, 32, 32, stream.str(), RETRIEVE_MANAGE);
|
|
pspIcons[i]->SetHotSpot(16, 16);
|
|
}
|
|
|
|
// fix for translation.
|
|
kInterruptMessageString = _(kInterruptMessageString);
|
|
kInterruptString = _(kInterruptString);
|
|
kNoString = _(kNoString);
|
|
kNoToAllString = _(kNoToAllString);
|
|
}
|
|
|
|
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++)
|
|
{
|
|
if (interruptDecision[i] != 2)
|
|
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;
|
|
}
|
|
int ActionStack::getActionElementFromCard(MTGCardInstance * card)
|
|
{
|
|
|
|
for (int i = 0; i < mCount; i++)
|
|
{
|
|
Interruptible * current = (Interruptible *) mObjects[i];
|
|
if (current->source == card)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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))
|
|
{
|
|
Interruptible * currentSpell = (Interruptible *)getLatest(NOT_RESOLVED);
|
|
MTGCardInstance * card = currentSpell->source;
|
|
if(card && card->has(Constants::SPLITSECOND))
|
|
{
|
|
resolve();
|
|
}
|
|
else
|
|
{
|
|
int currentPlayerId = 0;
|
|
int otherPlayerId = 1;
|
|
if (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)
|
|
{
|
|
int extraTime = 0;
|
|
//extraTime is a multiplier, it counts the number of unresolved stack actions
|
|
//then is used to extend the time you have to interupt.
|
|
//this prevents you from "running out of time" while deciding.
|
|
//before this int was added, it was possible to run out of time if you had 10 stack actions
|
|
//and set the timer to 4 secs. BUG FIX //http://code.google.com/p/wagic/issues/detail?id=464
|
|
extraTime = count(0, NOT_RESOLVED, 0);
|
|
if (extraTime == 0)
|
|
extraTime = 1;//we never want this int to be 0.
|
|
if (timer < 0)
|
|
timer = options[Options::INTERRUPT_SECONDS].number * extraTime;
|
|
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()
|
|
{
|
|
static const float kSpacer = 8;
|
|
static const float x0 = 250;
|
|
static const float y0 = 0;
|
|
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 = WResourceManager::Instance()->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 = WResourceManager::Instance()->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, width + 2, height + 2, 10, ARGB(200,0,0,0));
|
|
//renderer->RenderQuad(back,x0,y0,0,xScale, yScale);
|
|
renderer->DrawRoundRect(x0 - 5, y0, width + 2, height + 2, 10, ARGB(255,255,255,255));
|
|
|
|
std::ostringstream stream;
|
|
// 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.
|
|
|
|
// Mootpoint 01/12/2011: draw the interrupt text first, at the top. Offset the rest of the
|
|
// unresolved stack effects down so that they don't collide with the interrupt text.
|
|
if (options[Options::INTERRUPT_SECONDS].number == 0)
|
|
stream << kInterruptMessageString;
|
|
else
|
|
stream << kInterruptMessageString << " " << static_cast<int>(timer);
|
|
|
|
mFont->DrawString(stream.str(), x0 + 5, currenty);
|
|
|
|
static const float kIconVerticalOffset = 24;
|
|
if (mCount > 1)
|
|
{
|
|
renderer->RenderQuad(pspIcons[7].get(), x0 + 10, kIconVerticalOffset, 0, kGamepadIconSize, kGamepadIconSize);
|
|
mFont->DrawString(kInterruptString, x0 + 19, kIconVerticalOffset - 6);
|
|
|
|
renderer->RenderQuad(pspIcons[4].get(), x0 + 97, kIconVerticalOffset, 0, kGamepadIconSize, kGamepadIconSize);
|
|
mFont->DrawString(kNoString, x0 + 106, kIconVerticalOffset - 6);
|
|
|
|
renderer->RenderQuad(pspIcons[6].get(), x0 + 145, kIconVerticalOffset, 0, kGamepadIconSize, kGamepadIconSize);
|
|
mFont->DrawString(kNoToAllString, x0 + 154, kIconVerticalOffset - 6);
|
|
}
|
|
else
|
|
{
|
|
renderer->RenderQuad(pspIcons[7].get(), x0 + 40, kIconVerticalOffset, 0, kGamepadIconSize, kGamepadIconSize);
|
|
mFont->DrawString(kInterruptString, x0 + 49, kIconVerticalOffset - 6);
|
|
|
|
renderer->RenderQuad(pspIcons[4].get(), x0 + 140, kIconVerticalOffset - 6, 0, kGamepadIconSize, kGamepadIconSize);
|
|
mFont->DrawString(kNoString, x0 + 146, kIconVerticalOffset - 6);
|
|
}
|
|
|
|
currenty += kIconVerticalOffset + kSpacer;
|
|
|
|
for (int i = 0; i < mCount; i++)
|
|
{
|
|
Interruptible * current = (Interruptible *) mObjects[i];
|
|
if (current && current->state == NOT_RESOLVED)
|
|
{
|
|
current->x = x0;
|
|
current->y = currenty;
|
|
current->Render();
|
|
|
|
currenty += current->mHeight;
|
|
}
|
|
}
|
|
}
|
|
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 = WResourceManager::Instance()->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) || defined (IOS)
|
|
|
|
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
|