added support for alternative casting cost, evoke, added support for phantom cycle,hydras, added support for exile/bounce as casting cost

This commit is contained in:
omegablast2002@yahoo.com
2010-08-30 18:45:38 +00:00
parent e6769fcfe9
commit 0b4dde558b
20 changed files with 347 additions and 13 deletions

View File

@@ -88,6 +88,7 @@ class Spell: public Interruptible {
int resolve();
void Render();
bool kickerWasPaid();
bool AlternativeWasPaid();
const string getDisplayName() const;
virtual ostream& toString(ostream& out) const;
MTGCardInstance * getNextCardTarget(MTGCardInstance * previous = 0);

View File

@@ -53,6 +53,8 @@ public:
intValue = target->getManaCost()->getConvertedCost();
}else if (s == "lifetotal"){
intValue = target->controller()->life;
}else if (s == "sunburst"){
intValue = target->costAmount;
}else if (s == "odcount"){
intValue = target->controller()->opponent()->damageCount;
}else if (s == "opponentlifetotal"){

View File

@@ -82,6 +82,32 @@ public:
virtual int setSource(MTGCardInstance * _source);
virtual TapTargetCost * clone() const;
};
//exile as cost
class ExileTargetCost: public ExtraCost{
public:
MTGCardInstance * target;
ExileTargetCost(TargetChooser *_tc = NULL);
virtual int setPayment(MTGCardInstance * card);
virtual int isPaymentSet();
virtual int canPay();
virtual int doPay();
virtual void Render();
virtual int setSource(MTGCardInstance * _source);
virtual ExileTargetCost * clone() const;
};
//bounce cost
class BounceTargetCost: public ExtraCost{
public:
MTGCardInstance * target;
BounceTargetCost(TargetChooser *_tc = NULL);
virtual int setPayment(MTGCardInstance * card);
virtual int isPaymentSet();
virtual int canPay();
virtual int doPay();
virtual void Render();
virtual int setSource(MTGCardInstance * _source);
virtual BounceTargetCost * clone() const;
};
class CounterCost: public ExtraCost{
public:

View File

@@ -49,6 +49,8 @@ class MTGAbility: public ActionElement{
int oneShot;
int forceDestroy;
ManaCost * cost;
ManaCost * alternative;
Targetable * target;
int aType;
MTGCardInstance * source;

View File

@@ -44,9 +44,11 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable {
MTGGameZone * currentZone;
Pos* view;
int X;
int costAmount;
int regenerateTokens;
int isToken;
int stillInUse();
int getCostAmount();
Player * lastController;
MTGGameZone * getCurrentZone();
MTGGameZone * previousZone;

View File

@@ -107,8 +107,12 @@ class Constants
POISONDAMAGE = 49,
POISONTWODAMAGE = 50,
POISONTHREEDAMAGE = 51,
PHANTOM = 52,
COUNTERASDAMAGE = 53,
VIGOR = 54,
CHANGELING = 55,
NB_BASIC_ABILITIES = 52,
NB_BASIC_ABILITIES = 56,
RARITY_S = 'S', //Special Rarity

View File

@@ -160,6 +160,8 @@ class MTGPlayerCards {
void resetLibrary();
void initDeck(MTGDeck * deck);
MTGCardInstance * putInGraveyard(MTGCardInstance * card);
MTGCardInstance * putInExile(MTGCardInstance * card);
MTGCardInstance * putInHand(MTGCardInstance * card);
MTGCardInstance * putInZone(MTGCardInstance * card, MTGGameZone * from, MTGGameZone * to);
int isInPlay(MTGCardInstance * card);
};

View File

@@ -22,10 +22,12 @@ class ManaCost{
public:
enum{
MANA_PAID = 1,
MANA_PAID_WITH_KICKER = 2
MANA_PAID_WITH_KICKER = 2,
MANA_PAID_WITH_ALTERNATIVE = 3
};
ExtraCosts * extraCosts;
ManaCost * kicker;
ManaCost * alternative;
static ManaCost * parseManaCost(string value, ManaCost * _manacost = NULL, MTGCardInstance * c = NULL);
virtual void init();
void x();

View File

@@ -166,6 +166,10 @@ bool Spell::kickerWasPaid(){
return (payResult == ManaCost::MANA_PAID_WITH_KICKER);
}
bool Spell::AlternativeWasPaid(){
return (payResult == ManaCost::MANA_PAID_WITH_ALTERNATIVE);
}
const string Spell::getDisplayName() const {
return source->getName();
}

View File

@@ -65,7 +65,28 @@ int Damage::resolve(){
if (target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE){
MTGCardInstance * _target = (MTGCardInstance *)target;
if ((_target)->protectedAgainst(source)) damage = 0;
//rulings = 10/4/2004 The damage prevention ability works even if it has no counters, as long as some effect keeps its toughness above zero.
//these creature are essentially immune to damage. however 0/-1 effects applied through lords or counters can kill them.
if ((_target)->has(Constants::PHANTOM)) {
damage = 0;
(_target)->counters->removeCounter(1,1);
}
if ((_target)->has(Constants::COUNTERASDAMAGE)) {
for (int i = 0; i < damage; i++){
for (int i = damage; i > 0; i--){
(_target)->counters->addCounter(-1,-1);
}
damage = 0;
}
}
if ((_target)->has(Constants::VIGOR)){
for (int i = 0; i < damage; i++){
for (int i = damage; i > 0; i--){
(_target)->counters->addCounter(1,1);
}
damage = 0;
}
}
if (!damage){
state = RESOLVED_NOK;
delete (e);

View File

@@ -140,6 +140,133 @@ void TapTargetCost::Render(){
mFont->DrawString(buffer, 20 ,20, JGETEXT_LEFT);
}
//endtaptargetcost
//exile as cost
ExileTargetCost * ExileTargetCost::clone() const{
ExileTargetCost * ec = NEW ExileTargetCost(*this);
if (tc) ec->tc = tc->clone();
return ec;
}
ExileTargetCost::ExileTargetCost(TargetChooser *_tc):ExtraCost(_tc){
if (tc) tc->targetter = NULL; //tapping targets is not targetting, protections do not apply
target = NULL;
}
int ExileTargetCost::setSource(MTGCardInstance * card){
ExtraCost::setSource(card);
if (tc) tc->targetter = NULL; //Tapping targets is not targetting, protections do not apply
if (!tc) target = card;
return 1;
}
int ExileTargetCost::setPayment(MTGCardInstance * card){
if (tc) {
int result = tc->addTarget(card);
if (result) {
target = card;
return result;
}
}
return 0;
}
int ExileTargetCost::isPaymentSet(){
if (target) return 1;
return 0;
}
int ExileTargetCost::canPay(){
//tap target does not have any additional restrictions.
return 1;
}
int ExileTargetCost::doPay(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target){
target->controller()->game->putInExile(target);
target = NULL;
if (tc) tc->initTargets();
return 1;
}
return 0;
}
void ExileTargetCost::Render(){
//TODO : real stuff
WFont * mFont = resources.GetWFont(Constants::MAIN_FONT);
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
mFont->SetColor(ARGB(255,255,255,255));
char buffer[200];
sprintf(buffer, "%s", _("Exile Target").c_str());
mFont->DrawString(buffer, 20 ,20, JGETEXT_LEFT);
}
//endExiletargetcost
//------------------------------------------------------------
//Bounce as cost
BounceTargetCost * BounceTargetCost::clone() const{
BounceTargetCost * ec = NEW BounceTargetCost(*this);
if (tc) ec->tc = tc->clone();
return ec;
}
BounceTargetCost::BounceTargetCost(TargetChooser *_tc):ExtraCost(_tc){
if (tc) tc->targetter = NULL; //tapping targets is not targetting, protections do not apply
target = NULL;
}
int BounceTargetCost::setSource(MTGCardInstance * card){
ExtraCost::setSource(card);
if (tc) tc->targetter = NULL; //Tapping targets is not targetting, protections do not apply
if (!tc) target = card;
return 1;
}
int BounceTargetCost::setPayment(MTGCardInstance * card){
if (tc) {
int result = tc->addTarget(card);
if (result) {
target = card;
return result;
}
}
return 0;
}
int BounceTargetCost::isPaymentSet(){
if (target) return 1;
return 0;
}
int BounceTargetCost::canPay(){
//tap target does not have any additional restrictions.
return 1;
}
int BounceTargetCost::doPay(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target){
target->controller()->game->putInHand(target);
target = NULL;
if (tc) tc->initTargets();
return 1;
}
return 0;
}
void BounceTargetCost::Render(){
//TODO : real stuff
WFont * mFont = resources.GetWFont(Constants::MAIN_FONT);
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
mFont->SetColor(ARGB(255,255,255,255));
char buffer[200];
sprintf(buffer, "%s", _("Return Target to Hand").c_str());
mFont->DrawString(buffer, 20 ,20, JGETEXT_LEFT);
}
//endbouncetargetcost
//------------------------------------------------------------
SacrificeCost * SacrificeCost::clone() const{
SacrificeCost * ec = NEW SacrificeCost(*this);
if (tc) ec->tc = tc->clone();

View File

@@ -343,6 +343,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
}
return NULL;
}
//alternative cost
found = s.find("alternative ");
if (found == 0){
if (spell && spell->AlternativeWasPaid()){
string s1 = s.substr(found+7);
return parseMagicLine(s1,id,spell, card);
}
return NULL;
}
//When...comes into play, you may...
found = s.find("may ");
if (found == 0){

View File

@@ -109,6 +109,7 @@ void MTGCardInstance::initMTGCI(){
next = NULL;
lastController = NULL;
regenerateTokens = 0;
costAmount = 0;
blocked = false;
currentZone = NULL;
data = this; //an MTGCardInstance point to itself for data, allows to update it without killing the underlying database item
@@ -296,6 +297,10 @@ int MTGCardInstance::hasSummoningSickness(){
return 1;
}
int MTGCardInstance::getCostAmount(){
return 1;
}
MTGCardInstance * MTGCardInstance::changeController(Player * newController){
Player * originalOwner = controller();
if (originalOwner == newController) return this;

View File

@@ -108,6 +108,15 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi
cost->kicker = ManaCost::parseManaCost(value);
}
break;
case 'o': //othercost
if (!primitive) primitive = NEW CardPrimitive();
if (ManaCost * cost = primitive->getManaCost())
{
string value = val;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
cost->alternative = ManaCost::parseManaCost(value);
}
break;
case 'i': //id
if (!card) card = NEW MTGCard();

View File

@@ -60,7 +60,10 @@ const char* Constants::MTGBasicAbilities[] = {
"poisondamage",
"poisontwodamage",
"poisonthreedamage",
"phantom",//prevents damage and remove 1 +1/+1 counter
"counterasdamage",//source takes damage in the form of -1/-1 counters.
"vigor",//instead of taking damage the source gains +1/+1 counters
"changeling",//this card is every creature type at all times
};

View File

@@ -125,6 +125,38 @@ MTGCardInstance * MTGPlayerCards::putInGraveyard(MTGCardInstance * card){
}
MTGCardInstance * MTGPlayerCards::putInExile(MTGCardInstance * card){
MTGCardInstance * copy = NULL;
MTGRemovedFromGame * exile = card->owner->game->exile;
if (inPlay->hasCard(card)){
copy = putInZone(card,inPlay, exile);
}else if (stack->hasCard(card)){
copy = putInZone(card,stack, exile);
}
if(graveyard->hasCard(card)){
copy = putInZone(card,graveyard, exile);
}else{
copy = putInZone(card,hand, exile);
}
return copy;
}
MTGCardInstance * MTGPlayerCards::putInHand(MTGCardInstance * card){
MTGCardInstance * copy = NULL;
MTGHand * hand = card->owner->game->hand;
if (inPlay->hasCard(card)){
copy = putInZone(card,inPlay, hand);
}else if (stack->hasCard(card)){
copy = putInZone(card,stack, hand);
}
if(graveyard->hasCard(card)){
copy = putInZone(card,graveyard, hand);
}else{
copy = putInZone(card,hand, hand);
}
return copy;
}
MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone * from, MTGGameZone * to){
MTGCardInstance * copy = NULL;
GameObserver *g = GameObserver::GetInstance();

View File

@@ -31,12 +31,18 @@ int cardsinhand = game->players[0]->game->hand->nb_cards;
}else if ((card->hasType("instant")) || card->has(Constants::FLASH) || (player == currentPlayer && !game->isInterrupting && (game->currentGamePhase == Constants::MTG_PHASE_FIRSTMAIN || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN))){
ManaCost * playerMana = player->getManaPool();
ManaCost * cost = card->getManaCost();
ManaCost * alternative = card->getManaCost()->alternative;
#ifdef WIN32
cost->Dump();
#endif
if (playerMana->canAfford(cost)){
if(alternative && playerMana->canAfford(alternative)){
return 1;
}
}
if (playerMana->canAfford(cost)){
return 1;
}
}
return 0;
}
@@ -44,6 +50,9 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card){
if (!isReactingToClick(card)) return 0;
Player * player = game->currentlyActing();
ManaCost * cost = card->getManaCost();
ManaCost * alternative = card->getManaCost()->alternative;
ManaCost * playerMana = player->getManaPool();
//this handles extra cost payments at the moment a card is played.
if (cost->isExtraPaymentSet()){
if (!game->targetListIsSet(card)){
return 0;
@@ -53,10 +62,34 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card){
game->waitForExtraPayment = cost->extraCosts;
return 0;
}
//as long as you dont have enough mana to pay the real cost, and the alternative contains extra cost, add extras
if(!playerMana->canAfford(cost) && playerMana->canAfford(alternative)){
if (cost->alternative->isExtraPaymentSet()){
if (!game->targetListIsSet(card)){
return 0;
}
}else{
cost->alternative->setExtraCostsAction(this, card);
game->waitForExtraPayment = cost->alternative->extraCosts;
return 0;
}
}
//------------------------------------------------------------------------
ManaCost * previousManaPool = NEW ManaCost(player->getManaPool());
int payResult = player->getManaPool()->pay(card->getManaCost());
card->getManaCost()->doPayExtra();
//if alternative has a extra payment thats set, this code pays it.the if statement is 100% needed as it would cause a crash on cards that dont have the alternative cost.
if(alternative){
card->getManaCost()->alternative->doPayExtra();}
//---------------------------------------------------------------------------
ManaCost * spellCost = previousManaPool->Diff(player->getManaPool());
delete previousManaPool;
if (card->hasType("land")){
MTGCardInstance * copy = player->game->putInZone(card, player->game->hand, player->game->temp);
@@ -131,7 +164,7 @@ int MTGAttackRule::receiveEvent(WEvent *e){
for (int i= 0; i < z->nb_cards; i++){
MTGCardInstance * card = z->cards[i];
if (!card->isAttacker() && card->has(Constants::MUSTATTACK)) reactToClick(card);
}
}
return 1;
}
}
@@ -216,7 +249,6 @@ int MTGBlockRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){
int MTGBlockRule::reactToClick(MTGCardInstance * card){
if (!isReactingToClick(card)) return 0;
MTGCardInstance * currentOpponent = card->isDefenser();
bool result = false;
int candefend = 0;
while (!result){

View File

@@ -79,7 +79,34 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan
}
manaCost->addExtraCost(NEW TapTargetCost(tc));
//tapcost
//exile cost
}else if (value[0] == 'e'){
//tap
OutputDebugString("Exile\n");
TargetChooserFactory tcf;
TargetChooser * tc = NULL;
size_t target_start = value.find("(");
size_t target_end = value.find(")");
if (target_start!=string::npos && target_end!=string::npos){
string target = value.substr(target_start+1, target_end-1 - target_start);
tc = tcf.createTargetChooser(target,c);
}
manaCost->addExtraCost(NEW ExileTargetCost(tc));
//---------------------------------
//bounce cost
}else if (value[0] == 'h'){
//tap
OutputDebugString("Bounce\n");
TargetChooserFactory tcf;
TargetChooser * tc = NULL;
size_t target_start = value.find("(");
size_t target_end = value.find(")");
if (target_start!=string::npos && target_end!=string::npos){
string target = value.substr(target_start+1, target_end-1 - target_start);
tc = tcf.createTargetChooser(target,c);
}
manaCost->addExtraCost(NEW BounceTargetCost(tc));
//---------------------------------
//life cost
}else if (value[0] == 'l'){
@@ -186,6 +213,8 @@ ManaCost::~ManaCost(){
SAFE_DELETE(extraCosts);
SAFE_DELETE(kicker);
SAFE_DELETE(alternative);
}
void ManaCost::x(){
@@ -205,6 +234,7 @@ void ManaCost::init(){
extraCosts = NULL;
extraCostsIsCopy = 0;
kicker = NULL;
alternative = NULL;
}
@@ -231,6 +261,11 @@ void ManaCost::copy(ManaCost * _manaCost){
kicker = NEW ManaCost();
kicker->copy(_manaCost->kicker);
}
SAFE_DELETE(alternative);
if (_manaCost->alternative){
alternative = NEW ManaCost();
alternative->copy(_manaCost->alternative);
}
}
int ManaCost::getCost(int color){
@@ -334,15 +369,30 @@ int ManaCost::setExtraCostsAction(MTGAbility * action, MTGCardInstance * card){
int ManaCost::pay(ManaCost * _cost){
int result = MANA_PAID;
ManaCost * toPay = NEW ManaCost();
toPay->copy(_cost);
toPay->copy(_cost);
//---if you can pay the cards real cost, it is paided instead and alternative is not used.
if (toPay->alternative && canAfford(toPay->alternative) && !canAfford(_cost)){
//const char * getMenuText(){return "Put into play";}
toPay = toPay->alternative;
if (!canAfford(toPay) || canAfford(_cost)){
toPay->copy(_cost);
}
result = MANA_PAID_WITH_ALTERNATIVE;
}
if (toPay->kicker){
toPay->add(toPay->kicker);
if (!canAfford(toPay)){
toPay->copy(_cost);
}else{
result = MANA_PAID_WITH_KICKER;
}
}
}
ManaCost * diff = Diff(toPay);
for (int i=0; i < Constants::MTG_NB_COLORS; i++){
cost[i] = diff->getCost(i);
@@ -518,6 +568,7 @@ int ManaPool::pay (ManaCost * _cost){
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++){
current[i] = cost[i];
}
int result = ManaCost::pay(_cost);
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++){
int value = current[i] - cost[i];

View File

@@ -99,8 +99,6 @@ int Player::prevented(){
void Player::cleanupPhase(){
game->inPlay->cleanupPhase();
game->graveyard->cleanupPhase();
}
ostream& operator<<(ostream& out, const Player& p)

View File

@@ -519,6 +519,7 @@ bool TypeTargetChooser::canTarget(Targetable * target){
MTGCardInstance * card = (MTGCardInstance *) target;
for (int i= 0; i < nbtypes; i++){
if (card->hasSubtype(types[i])) return true;
if (card->has(Constants::CHANGELING)) return true;
if (Subtypes::subtypesList->find(card->getLCName()) == types[i]) return true;
}
return false;