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:
wagic.the.homebrew
2011-05-03 11:59:27 +00:00
parent d8147a0156
commit 0b9ff076e6
13 changed files with 206 additions and 41 deletions
+17 -3
View File
@@ -8,6 +8,20 @@ auto=shuffle
auto=draw:7 auto=draw:7
auto=@each my draw:draw:1 auto=@each my draw:draw:1
auto=maxPlay(land)1 auto=maxPlay(land)1
[Player1]
#This is a trick, we put this in player 1's rules so that they most likely won't see that this can be interrupted. Kind of a hack until we can get "noninterruptible" events #Mana Empties from manapool at the end of each phase
auto=@each cleanup:all(*|Battlefield) resetDamage auto=@each untap:removeMana(*)
auto=@each upkeep:removeMana(*)
auto=@each draw:removeMana(*)
auto=@each firstmain:removeMana(*)
auto=@each combatbegins:removeMana(*)
auto=@each attackers:removeMana(*)
auto=@each blockers:removeMana(*)
auto=@each combatdamage:removeMana(*)
auto=@each combatEnds:removeMana(*)
auto=@each secondmain:removeMana(*)
auto=@each end:removeMana(*)
auto=@each cleanup:removeMana(*)
#reset Creature damage at the cleanup phase
auto=@each cleanup:all(*|myBattlefield) resetDamage
+17 -3
View File
@@ -6,6 +6,20 @@ mode=mtg
life:20 life:20
auto=@each my draw:draw:1 auto=@each my draw:draw:1
auto=maxPlay(land)1 auto=maxPlay(land)1
[Player1]
#This is a trick, we put this in player 1's rules so that they most likely won't see that this can be interrupted. Kind of a hack until we can get "noninterruptible" events #Mana Empties from manapool at the end of each phase
auto=@each cleanup:all(*|Battlefield) resetDamage auto=@each untap:removeMana(*)
auto=@each upkeep:removeMana(*)
auto=@each draw:removeMana(*)
auto=@each firstmain:removeMana(*)
auto=@each combatbegins:removeMana(*)
auto=@each attackers:removeMana(*)
auto=@each blockers:removeMana(*)
auto=@each combatdamage:removeMana(*)
auto=@each combatEnds:removeMana(*)
auto=@each secondmain:removeMana(*)
auto=@each end:removeMana(*)
auto=@each cleanup:removeMana(*)
#reset Creature damage at the cleanup phase
auto=@each cleanup:all(*|myBattlefield) resetDamage
+14
View File
@@ -4762,6 +4762,20 @@ public:
AAShuffle * clone() const; AAShuffle * clone() const;
}; };
//Remove Mana From ManaPool
class AARemoveMana: public ActivatedAbilityTP
{
public:
ManaCost * mManaDesc;
bool mRemoveAll;
AARemoveMana(int _id, MTGCardInstance * card, Targetable * _target, string ManaDesc, int who = TargetChooser::UNSET);
int resolve();
const char * getMenuText();
AARemoveMana * clone() const;
~AARemoveMana();
};
//Random Discard //Random Discard
class AARandomDiscarder: public ActivatedAbilityTP class AARandomDiscarder: public ActivatedAbilityTP
+1
View File
@@ -109,6 +109,7 @@ public:
int allowedToAltCast(MTGCardInstance* card, Player* player); int allowedToAltCast(MTGCardInstance* card, Player* player);
int oneShot; int oneShot;
int forceDestroy; int forceDestroy;
bool canBeInterrupted;
ManaCost* cost; ManaCost* cost;
ManaCost* alternative; ManaCost* alternative;
ManaCost* BuyBack; ManaCost* BuyBack;
+2
View File
@@ -52,6 +52,7 @@ public:
string alternativeName; string alternativeName;
static ManaCost * parseManaCost(string value, ManaCost * _manacost = NULL, MTGCardInstance * c = NULL); static ManaCost * parseManaCost(string value, ManaCost * _manacost = NULL, MTGCardInstance * c = NULL);
virtual void init(); virtual void init();
virtual void reinit();
void x(); void x();
int hasX(); int hasX();
ManaCost(int _cost[], int nb_elems = 1); ManaCost(int _cost[], int nb_elems = 1);
@@ -85,6 +86,7 @@ public:
void randomDiffHybrids(ManaCost * _cost, int diff[]); void randomDiffHybrids(ManaCost * _cost, int diff[]);
int add(ManaCost * _cost); int add(ManaCost * _cost);
int remove(ManaCost * _cost); int remove(ManaCost * _cost);
int removeAll(int color);
int pay (ManaCost * _cost); int pay (ManaCost * _cost);
//return 1 if _cost can be paid with current data, 0 otherwise //return 1 if _cost can be paid with current data, 0 otherwise
+3
View File
@@ -59,6 +59,7 @@ protected:
Player * initPlayer(int playerId); Player * initPlayer(int playerId);
MTGDeck * buildDeck(int playerId); MTGDeck * buildDeck(int playerId);
int strToGameMode(string s); int strToGameMode(string s);
bool postUpdateInitDone;
public: public:
enum enum
{ {
@@ -86,6 +87,8 @@ public:
bool canChooseDeck(); //True if the players get to select their decks, false if the decks are automatically generated by the mode bool canChooseDeck(); //True if the players get to select their decks, false if the decks are automatically generated by the mode
void addExtraRules(); void addExtraRules();
void initGame(); void initGame();
//second part of the initialization, needs to happen after the first update call
void postUpdateInit();
void cleanup(); void cleanup();
vector<string> extraRules; vector<string> extraRules;
RulesState initState; RulesState initState;
+75
View File
@@ -1761,6 +1761,81 @@ AAShuffle * AAShuffle::clone() const
return a; 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 //Tapper
AATapper::AATapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) : AATapper::AATapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(id, card, _cost, 0) ActivatedAbility(id, card, _cost, 0)
+2 -14
View File
@@ -122,9 +122,6 @@ void GameObserver::nextGamePhase()
return nextGamePhase(); return nextGamePhase();
} }
for (int i = 0; i < 2; ++i)
players[i]->getManaPool()->init();
if (currentGamePhase == Constants::MTG_PHASE_AFTER_EOT) if (currentGamePhase == Constants::MTG_PHASE_AFTER_EOT)
{ {
//Auto Hand cleaning, in case the player didn't do it himself //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 //check land playability at start; as we want this effect to happen reguardless of unresolved
//effects or menus actions //effects or menus actions
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ players[i]->isPoisoned = (players[i]->poisonCount > 0);
if(players[i]->poisonCount > 0)
{
players[i]->isPoisoned = true;
}
else
{
players[i]->isPoisoned = false;
}
}
if (mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0) if (mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0)
return; return;
if (mLayers->actionLayer()->menuObject) if (mLayers->actionLayer()->menuObject)
@@ -939,7 +927,7 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
{ {
//card played as normal, alternative cost, buyback, flashback, retrace. //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=" // 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. // it would automatically force the play method to putinplayrule...even charge you the original mana cost.
+3
View File
@@ -441,6 +441,9 @@ void GameStateDuel::Update(float dt)
GameApp::playMusic(musictrack); GameApp::playMusic(musictrack);
} }
game->Update(dt); 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->gameOver)
{ {
if (game->players[1]->playMode != Player::MODE_TEST_SUITE) credits->compute(game->players[0], game->players[1], mParent); if (game->players[1]->playMode != Player::MODE_TEST_SUITE) credits->compute(game->players[0], game->players[1], mParent);
+17 -8
View File
@@ -1804,6 +1804,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return a; 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 //Cast/Play Restrictions
for (size_t i = 0; i < kMaxCastKeywordsCount; ++i) 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) int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCardInstance * card, int id, MTGGameZone * dest)
{ {
if (!card && spell) if (!card && spell)
card = spell->source; card = spell->source;
if (!card) if (!card)
@@ -2468,12 +2477,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
string cre = "Creature"; string cre = "Creature";
card->setType(cre.c_str()); card->setType(cre.c_str());
card->basicAbilities.reset(); card->basicAbilities.reset();
card->getManaCost()->remove(0,100); card->getManaCost()->reinit();
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);
} }
else if(card && !card->morphed && card->turningOver) else if(card && !card->morphed && card->turningOver)
{ {
@@ -3241,6 +3245,7 @@ MTGAbility::MTGAbility(int id, MTGCardInstance * card) :
cost = NULL; cost = NULL;
forceDestroy = 0; forceDestroy = 0;
oneShot = 0; oneShot = 0;
canBeInterrupted = true;
} }
MTGAbility::MTGAbility(int id, MTGCardInstance * _source, Targetable * _target) : MTGAbility::MTGAbility(int id, MTGCardInstance * _source, Targetable * _target) :
@@ -3253,6 +3258,7 @@ MTGAbility::MTGAbility(int id, MTGCardInstance * _source, Targetable * _target)
cost = NULL; cost = NULL;
forceDestroy = 0; forceDestroy = 0;
oneShot = 0; oneShot = 0;
canBeInterrupted = true;
} }
int MTGAbility::stillInUse(MTGCardInstance * card) int MTGAbility::stillInUse(MTGCardInstance * card)
@@ -3307,7 +3313,10 @@ GameObserver * g=g->GetInstance();
int MTGAbility::fireAbility() int MTGAbility::fireAbility()
{ {
game->mLayers->stackLayer()->addAbility(this); if (canBeInterrupted)
game->mLayers->stackLayer()->addAbility(this);
else
resolve();
return 1; return 1;
} }
+29 -10
View File
@@ -365,6 +365,23 @@ void ManaCost::init()
suspend = NULL; 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) void ManaCost::copy(ManaCost * _manaCost)
{ {
if (!_manaCost) if (!_manaCost)
@@ -475,11 +492,9 @@ int ManaCost::getConvertedCost()
int ManaCost::remove(int color, int value) int ManaCost::remove(int color, int value)
{ {
cost[color] -= value; assert (value >= 0);
if (cost[color] < 0) int toRemove = min(cost[color], value);
{ cost[color] -= toRemove;
cost[color] = 0;
}
return 1; return 1;
} }
@@ -511,15 +526,19 @@ int ManaCost::remove(ManaCost * _cost)
return 0; return 0;
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++) for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
{ {
for(int c = 0;c < _cost->getCost(i);c++) int toRemove = min(cost[i], _cost->getCost(i)); //we don't want to be negative
{ cost[i] -= toRemove;
if(cost[i])//remove 1 at a time to avoid dipping into negitive cost. assert(cost[i] >= 0);
cost[i] -= 1;
}
} }
return 1; return 1;
} }
int ManaCost::removeAll(int color)
{
cost[color] = 0;
return 1;
}
int ManaCost::addHybrid(int c1, int v1, int c2, int v2) int ManaCost::addHybrid(int c1, int v1, int c2, int v2)
{ {
hybrids.push_back(ManaCostHybrid(c1, v1, c2, v2)); hybrids.push_back(ManaCostHybrid(c1, v1, c2, v2));
+17 -1
View File
@@ -255,6 +255,8 @@ void Rules::addExtraRules()
if (a) 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 (a->oneShot)
{ {
if (((p->isAI() && p->playMode if (((p->isAI() && p->playMode
@@ -463,7 +465,6 @@ void Rules::initGame()
p->poisonCount = initState.playerData[i].poisonCount; p->poisonCount = initState.playerData[i].poisonCount;
p->damageCount = initState.playerData[i].damageCount; p->damageCount = initState.playerData[i].damageCount;
p->preventable = initState.playerData[i].preventable; p->preventable = initState.playerData[i].preventable;
p->getManaPool()->copy(initState.playerData[i].manapool);
if (initState.playerData[i].avatar.size()) if (initState.playerData[i].avatar.size())
{ {
p->loadAvatar(initState.playerData[i].avatar); p->loadAvatar(initState.playerData[i].avatar);
@@ -504,9 +505,23 @@ void Rules::initGame()
} }
} }
addExtraRules(); addExtraRules();
postUpdateInitDone = false;
DebugTrace("RULES Init Game Done !\n"); 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() void RulesPlayerZone::cleanup()
{ {
cards.clear(); cards.clear();
@@ -546,6 +561,7 @@ Rules::Rules(string _bg)
unlockOption = INVALID_OPTION; unlockOption = INVALID_OPTION;
hidden = false; hidden = false;
filename = ""; filename = "";
postUpdateInitDone = false;
} }
bool Rules::canChooseDeck() bool Rules::canChooseDeck()
+9 -2
View File
@@ -78,6 +78,14 @@ int TestSuiteAI::Act(float dt)
{ {
GameObserver * g = GameObserver::GetInstance(); GameObserver * g = GameObserver::GetInstance();
g->gameOver = NULL; // Prevent draw rule from losing the game 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) if (playMode == MODE_AI && suite->aiMaxCalls)
{ {
suite->aiMaxCalls--; suite->aiMaxCalls--;
@@ -87,6 +95,7 @@ int TestSuiteAI::Act(float dt)
if (playMode == MODE_HUMAN) if (playMode == MODE_HUMAN)
{ {
g->mLayers->CheckUserInput(0); g->mLayers->CheckUserInput(0);
suite->currentAction++; //hack to avoid repeating the initialization of manapool
return 1; return 1;
} }
@@ -96,7 +105,6 @@ int TestSuiteAI::Act(float dt)
string action = suite->getNextAction(); string action = suite->getNextAction();
g->mLayers->stackLayer()->Dump(); g->mLayers->stackLayer()->Dump();
// DamageResolverLayer * drl = g->mLayers->combatLayer();
DebugTrace("TESTSUITE command: " << action); DebugTrace("TESTSUITE command: " << action);
if (g->mLayers->stackLayer()->askIfWishesToInterrupt == this) if (g->mLayers->stackLayer()->askIfWishesToInterrupt == this)
@@ -386,7 +394,6 @@ void TestSuite::initGame()
AIPlayer * p = (AIPlayer *) (g->players[i]); AIPlayer * p = (AIPlayer *) (g->players[i]);
p->forceBestAbilityUse = forceAbility; p->forceBestAbilityUse = forceAbility;
p->life = initState.playerData[i].life; 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 }; MTGGameZone * playerZones[] = { p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay };
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
{ {