Please update your rules folder
- "Manapool empties at the end of each step" becomes an ability, and was moved into the external rules file. "removemana(*) to remove all, removemana(*{G}) to remove all green, removemana(*{G}{B}{R}) to remove all green black red, removemana({G}{G}{B}{U}) (no "*") to remove a specific value.
- Added a possibility to make abilities non interruptible. With little work, this could be added to the parser if needed. Please use with care, let's discuss what is an acceptable usage of this now functionality, if needed.
This commit is contained in:
@@ -1761,6 +1761,81 @@ AAShuffle * AAShuffle::clone() const
|
||||
return a;
|
||||
}
|
||||
|
||||
// Remove Mana From ManaPool
|
||||
AARemoveMana::AARemoveMana(int _id, MTGCardInstance * card, Targetable * _target, string manaDesc, int who) :
|
||||
ActivatedAbilityTP(_id, card, _target, NULL, who)
|
||||
{
|
||||
if (!manaDesc.size())
|
||||
{
|
||||
DebugTrace("ALL_ABILITIES: AARemoveMana ctor error");
|
||||
return;
|
||||
}
|
||||
mRemoveAll = (manaDesc[0] == '*');
|
||||
if (mRemoveAll)
|
||||
manaDesc = manaDesc.substr(1);
|
||||
|
||||
mManaDesc = (manaDesc.size()) ? ManaCost::parseManaCost(manaDesc) : NULL;
|
||||
|
||||
}
|
||||
|
||||
int AARemoveMana::resolve()
|
||||
{
|
||||
Targetable * _target = getTarget();
|
||||
Player * player;
|
||||
if (_target)
|
||||
{
|
||||
if (_target->typeAsTarget() == TARGET_CARD)
|
||||
{
|
||||
player = ((MTGCardInstance *) _target)->controller();
|
||||
}
|
||||
else
|
||||
{
|
||||
player = (Player *) _target;
|
||||
}
|
||||
ManaPool * manaPool = player->getManaPool();
|
||||
if (mRemoveAll)
|
||||
{
|
||||
if (mManaDesc) // Remove all mana Matching a description
|
||||
{
|
||||
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
|
||||
{
|
||||
if (mManaDesc->hasColor(i))
|
||||
manaPool->removeAll(i);
|
||||
}
|
||||
}
|
||||
else //Remove all mana
|
||||
{
|
||||
manaPool->init();
|
||||
}
|
||||
}
|
||||
else //remove a "standard" mana Description
|
||||
{
|
||||
((ManaCost *)manaPool)->remove(mManaDesc); //why do I have to cast here?
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char * AARemoveMana::getMenuText()
|
||||
{
|
||||
if (mRemoveAll && !mManaDesc)
|
||||
return "Empty Manapool";
|
||||
return "Remove Mana";
|
||||
}
|
||||
|
||||
AARemoveMana * AARemoveMana::clone() const
|
||||
{
|
||||
AARemoveMana * a = NEW AARemoveMana(*this);
|
||||
a->mManaDesc = mManaDesc ? NEW ManaCost(mManaDesc) : NULL;
|
||||
a->isClone = 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
AARemoveMana::~AARemoveMana()
|
||||
{
|
||||
SAFE_DELETE(mManaDesc);
|
||||
}
|
||||
|
||||
//Tapper
|
||||
AATapper::AATapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
||||
ActivatedAbility(id, card, _cost, 0)
|
||||
|
||||
@@ -122,9 +122,6 @@ void GameObserver::nextGamePhase()
|
||||
return nextGamePhase();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
players[i]->getManaPool()->init();
|
||||
|
||||
if (currentGamePhase == Constants::MTG_PHASE_AFTER_EOT)
|
||||
{
|
||||
//Auto Hand cleaning, in case the player didn't do it himself
|
||||
@@ -371,16 +368,7 @@ void GameObserver::gameStateBasedEffects()
|
||||
//check land playability at start; as we want this effect to happen reguardless of unresolved
|
||||
//effects or menus actions
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if(players[i]->poisonCount > 0)
|
||||
{
|
||||
players[i]->isPoisoned = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
players[i]->isPoisoned = false;
|
||||
}
|
||||
}
|
||||
players[i]->isPoisoned = (players[i]->poisonCount > 0);
|
||||
if (mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0)
|
||||
return;
|
||||
if (mLayers->actionLayer()->menuObject)
|
||||
@@ -939,7 +927,7 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
|
||||
{
|
||||
//card played as normal, alternative cost, buyback, flashback, retrace.
|
||||
|
||||
//the varible "paymenttype = int" only serves one purpose, to tell this bug fix what menu item you clicked on...
|
||||
//the variable "paymenttype = int" only serves one purpose, to tell this bug fix what menu item you clicked on...
|
||||
// all alternative cost or play methods suffered from the fix because if the card contained "target="
|
||||
// it would automatically force the play method to putinplayrule...even charge you the original mana cost.
|
||||
|
||||
|
||||
@@ -441,6 +441,9 @@ void GameStateDuel::Update(float dt)
|
||||
GameApp::playMusic(musictrack);
|
||||
}
|
||||
game->Update(dt);
|
||||
//run a "post update" init call in the rules. This is for things such as Manapool, which gets emptied in the update
|
||||
// That's mostly because of a legacy bug, where we use the update sequence for some things when we should use events (such as phase changes)
|
||||
mParent->rules->postUpdateInit();
|
||||
if (game->gameOver)
|
||||
{
|
||||
if (game->players[1]->playMode != Player::MODE_TEST_SUITE) credits->compute(game->players[0], game->players[1], mParent);
|
||||
|
||||
@@ -1804,6 +1804,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
return a;
|
||||
}
|
||||
|
||||
//Remove Mana from ManaPool
|
||||
vector<string> splitRemove = parseBetween(s, "removemana(", ")");
|
||||
if (splitRemove.size())
|
||||
{
|
||||
Targetable * t = spell? spell->getNextTarget() : NULL;
|
||||
MTGAbility *a = NEW AARemoveMana(id, card, t, splitRemove[1], who);
|
||||
a->oneShot = 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
//Cast/Play Restrictions
|
||||
for (size_t i = 0; i < kMaxCastKeywordsCount; ++i)
|
||||
{
|
||||
@@ -2400,7 +2410,6 @@ int AbilityFactory::computeX(Spell * spell, MTGCardInstance * card)
|
||||
|
||||
int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card, int id, MTGGameZone * dest)
|
||||
{
|
||||
|
||||
if (!card && spell)
|
||||
card = spell->source;
|
||||
if (!card)
|
||||
@@ -2468,12 +2477,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
|
||||
string cre = "Creature";
|
||||
card->setType(cre.c_str());
|
||||
card->basicAbilities.reset();
|
||||
card->getManaCost()->remove(0,100);
|
||||
card->getManaCost()->remove(1,100);
|
||||
card->getManaCost()->remove(2,100);
|
||||
card->getManaCost()->remove(3,100);
|
||||
card->getManaCost()->remove(4,100);
|
||||
card->getManaCost()->remove(5,100);
|
||||
card->getManaCost()->reinit();
|
||||
}
|
||||
else if(card && !card->morphed && card->turningOver)
|
||||
{
|
||||
@@ -3241,6 +3245,7 @@ MTGAbility::MTGAbility(int id, MTGCardInstance * card) :
|
||||
cost = NULL;
|
||||
forceDestroy = 0;
|
||||
oneShot = 0;
|
||||
canBeInterrupted = true;
|
||||
}
|
||||
|
||||
MTGAbility::MTGAbility(int id, MTGCardInstance * _source, Targetable * _target) :
|
||||
@@ -3253,6 +3258,7 @@ MTGAbility::MTGAbility(int id, MTGCardInstance * _source, Targetable * _target)
|
||||
cost = NULL;
|
||||
forceDestroy = 0;
|
||||
oneShot = 0;
|
||||
canBeInterrupted = true;
|
||||
}
|
||||
|
||||
int MTGAbility::stillInUse(MTGCardInstance * card)
|
||||
@@ -3307,7 +3313,10 @@ GameObserver * g=g->GetInstance();
|
||||
|
||||
int MTGAbility::fireAbility()
|
||||
{
|
||||
game->mLayers->stackLayer()->addAbility(this);
|
||||
if (canBeInterrupted)
|
||||
game->mLayers->stackLayer()->addAbility(this);
|
||||
else
|
||||
resolve();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -365,6 +365,23 @@ void ManaCost::init()
|
||||
suspend = NULL;
|
||||
}
|
||||
|
||||
void ManaCost::reinit()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= Constants::MTG_NB_COLORS; i++)
|
||||
{
|
||||
cost[i] = 0;
|
||||
}
|
||||
SAFE_DELETE(extraCosts);
|
||||
SAFE_DELETE(kicker);
|
||||
SAFE_DELETE(alternative);
|
||||
SAFE_DELETE(BuyBack);
|
||||
SAFE_DELETE(FlashBack);
|
||||
SAFE_DELETE(Retrace);
|
||||
SAFE_DELETE(morph);
|
||||
SAFE_DELETE(suspend);
|
||||
}
|
||||
|
||||
void ManaCost::copy(ManaCost * _manaCost)
|
||||
{
|
||||
if (!_manaCost)
|
||||
@@ -475,11 +492,9 @@ int ManaCost::getConvertedCost()
|
||||
|
||||
int ManaCost::remove(int color, int value)
|
||||
{
|
||||
cost[color] -= value;
|
||||
if (cost[color] < 0)
|
||||
{
|
||||
cost[color] = 0;
|
||||
}
|
||||
assert (value >= 0);
|
||||
int toRemove = min(cost[color], value);
|
||||
cost[color] -= toRemove;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -511,15 +526,19 @@ int ManaCost::remove(ManaCost * _cost)
|
||||
return 0;
|
||||
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
|
||||
{
|
||||
for(int c = 0;c < _cost->getCost(i);c++)
|
||||
{
|
||||
if(cost[i])//remove 1 at a time to avoid dipping into negitive cost.
|
||||
cost[i] -= 1;
|
||||
}
|
||||
int toRemove = min(cost[i], _cost->getCost(i)); //we don't want to be negative
|
||||
cost[i] -= toRemove;
|
||||
assert(cost[i] >= 0);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ManaCost::removeAll(int color)
|
||||
{
|
||||
cost[color] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ManaCost::addHybrid(int c1, int v1, int c2, int v2)
|
||||
{
|
||||
hybrids.push_back(ManaCostHybrid(c1, v1, c2, v2));
|
||||
|
||||
@@ -255,6 +255,8 @@ void Rules::addExtraRules()
|
||||
|
||||
if (a)
|
||||
{
|
||||
//We make those non interruptible, so that they don't appear on the player's stack
|
||||
a->canBeInterrupted = false;
|
||||
if (a->oneShot)
|
||||
{
|
||||
if (((p->isAI() && p->playMode
|
||||
@@ -463,7 +465,6 @@ void Rules::initGame()
|
||||
p->poisonCount = initState.playerData[i].poisonCount;
|
||||
p->damageCount = initState.playerData[i].damageCount;
|
||||
p->preventable = initState.playerData[i].preventable;
|
||||
p->getManaPool()->copy(initState.playerData[i].manapool);
|
||||
if (initState.playerData[i].avatar.size())
|
||||
{
|
||||
p->loadAvatar(initState.playerData[i].avatar);
|
||||
@@ -504,9 +505,23 @@ void Rules::initGame()
|
||||
}
|
||||
}
|
||||
addExtraRules();
|
||||
|
||||
postUpdateInitDone = false;
|
||||
DebugTrace("RULES Init Game Done !\n");
|
||||
}
|
||||
|
||||
//This function has all iitialization that can't be done in the "real" init function,
|
||||
// because the first update call messes things up.
|
||||
//It's a hack, ideally, the first update call shouldn't mess the init parameters...
|
||||
void Rules::postUpdateInit()
|
||||
{
|
||||
if (postUpdateInitDone)
|
||||
return;
|
||||
for (int i = 0; i < 2; ++ i)
|
||||
GameObserver::GetInstance()->players[i]->getManaPool()->copy(initState.playerData[i].manapool);
|
||||
postUpdateInitDone = true;
|
||||
}
|
||||
|
||||
void RulesPlayerZone::cleanup()
|
||||
{
|
||||
cards.clear();
|
||||
@@ -546,6 +561,7 @@ Rules::Rules(string _bg)
|
||||
unlockOption = INVALID_OPTION;
|
||||
hidden = false;
|
||||
filename = "";
|
||||
postUpdateInitDone = false;
|
||||
}
|
||||
|
||||
bool Rules::canChooseDeck()
|
||||
|
||||
@@ -78,6 +78,14 @@ int TestSuiteAI::Act(float dt)
|
||||
{
|
||||
GameObserver * g = GameObserver::GetInstance();
|
||||
g->gameOver = NULL; // Prevent draw rule from losing the game
|
||||
|
||||
//Last bits of initialization require to be done here, after the first "update" call of the game
|
||||
if (suite->currentAction == 0)
|
||||
{
|
||||
for (int i = 0; i < 2; ++ i)
|
||||
g->players[i]->getManaPool()->copy(suite->initState.playerData[i].manapool);
|
||||
}
|
||||
|
||||
if (playMode == MODE_AI && suite->aiMaxCalls)
|
||||
{
|
||||
suite->aiMaxCalls--;
|
||||
@@ -87,6 +95,7 @@ int TestSuiteAI::Act(float dt)
|
||||
if (playMode == MODE_HUMAN)
|
||||
{
|
||||
g->mLayers->CheckUserInput(0);
|
||||
suite->currentAction++; //hack to avoid repeating the initialization of manapool
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -96,7 +105,6 @@ int TestSuiteAI::Act(float dt)
|
||||
|
||||
string action = suite->getNextAction();
|
||||
g->mLayers->stackLayer()->Dump();
|
||||
// DamageResolverLayer * drl = g->mLayers->combatLayer();
|
||||
DebugTrace("TESTSUITE command: " << action);
|
||||
|
||||
if (g->mLayers->stackLayer()->askIfWishesToInterrupt == this)
|
||||
@@ -386,7 +394,6 @@ void TestSuite::initGame()
|
||||
AIPlayer * p = (AIPlayer *) (g->players[i]);
|
||||
p->forceBestAbilityUse = forceAbility;
|
||||
p->life = initState.playerData[i].life;
|
||||
p->getManaPool()->copy(initState.playerData[i].manapool);
|
||||
MTGGameZone * playerZones[] = { p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay };
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user