Fixed Muxus, Goblin Grandee and fixed Liliana, the Last Hope, added conjure keyword for J21 set, added perpetual counters and abilities for J21 set, improved imprint keyword, improved moverandom keyword for J21 set.

This commit is contained in:
Vittorio Alfieri
2021-08-04 12:33:28 +02:00
parent e6ffd056b5
commit 2ca03bb1f0
11 changed files with 178 additions and 19 deletions
+2 -4
View File
@@ -149,8 +149,7 @@ int OrderedAIAction::getEfficiency()
break;
if ((target->defenser || target->blockers.size()) && target->preventable < nextOpponent->power)
NeedPreventing = true;
if (p == target->controller() && target->controller()->isAI() && NeedPreventing && !(nextOpponent->has(Constants::DEATHTOUCH)
||nextOpponent->has(Constants::WITHER)))
if (p == target->controller() && target->controller()->isAI() && NeedPreventing && !(nextOpponent->has(Constants::DEATHTOUCH) || nextOpponent->has(Constants::PERPETUALDEATHTOUCH) || nextOpponent->has(Constants::WITHER)))
{
efficiency = 20 * (target->DangerRanking());//increase this chance to be used in combat if the creature blocking/blocked could kill the creature this chance is taking into consideration how good the creature is, best creature will always be the first "saved"..
if (target->toughness == 1 && nextOpponent->power == 1)
@@ -824,8 +823,7 @@ int OrderedAIAction::getRevealedEfficiency(MTGAbility * ability2)
break;
if ((target->defenser || target->blockers.size()) && target->preventable < nextOpponent->power)
NeedPreventing = true;
if (p == target->controller() && target->controller()->isAI() && NeedPreventing && !(nextOpponent->has(Constants::DEATHTOUCH)
||nextOpponent->has(Constants::WITHER)))
if (p == target->controller() && target->controller()->isAI() && NeedPreventing && !(nextOpponent->has(Constants::DEATHTOUCH) || nextOpponent->has(Constants::PERPETUALDEATHTOUCH) || nextOpponent->has(Constants::WITHER)))
{
eff2 = 20 * (target->DangerRanking());//increase this chance to be used in combat if the creature blocking/blocked could kill the creature this chance is taking into consideration how good the creature is, best creature will always be the first "saved"..
if (target->toughness == 1 && nextOpponent->power == 1)
+102 -2
View File
@@ -2270,6 +2270,82 @@ AAImprint * AAImprint::clone() const
return NEW AAImprint(*this);
}
AAImprint::~AAImprint()
{
SAFE_DELETE(andAbility);
}
//AAConjure
AAConjure::AAConjure(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, string _namedCard, string _cardZone) :
ActivatedAbility(observer, _id, _source, _cost, 0),cardNamed(_namedCard),cardZone(_cardZone)
{
target = _target;
andAbility = NULL;
theNamedCard = NULL;
}
MTGCardInstance * AAConjure::makeCard()
{
string newName = cardNamed;
if(newName.find(";")){//if it's a list of cards we choose one randomly (e.g. Tome of the Infinite)
vector<string> names = split(newName, ';');
newName = names.at(std::rand() % names.size());
}
MTGCardInstance * card = NULL;
MTGCard * cardData = MTGCollection()->getCardByName(newName);
if(!cardData) return NULL;
card = NEW MTGCardInstance(cardData, source->controller()->game);
card->owner = source->controller();
card->lastController = source->controller();
source->controller()->game->sideboard->addCard(card);
return card;
}
int AAConjure::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
if(_target->mutation && _target->parentCards.size() > 0) return 0; // Mutated down cards cannot be conjured, they will follow the fate of top-card
theNamedCard = makeCard();
if(theNamedCard){
MTGCardInstance * copy = source->controller()->game->putInZone(theNamedCard, theNamedCard->currentZone, MTGGameZone::stringToZone(game, cardZone, theNamedCard, NULL));
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = copy;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
}
this->forceDestroy = true;
return 1;
}
return 0;
}
const string AAConjure::getMenuText()
{
return "Conjure";
}
AAConjure * AAConjure::clone() const
{
return NEW AAConjure(*this);
}
AAConjure::~AAConjure()
{
SAFE_DELETE(andAbility);
}
//AAForetell
AAForetell::AAForetell(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
@@ -5685,7 +5761,6 @@ AInstantCastRestrictionUEOT::~AInstantCastRestrictionUEOT()
SAFE_DELETE(ability);
}
//AAMover
AAMover::AAMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest,string newName, ManaCost * _cost, bool undying, bool persist) :
ActivatedAbility(observer, _id, _source, _cost, 0), destination(dest),named(newName),undying(undying),persist(persist)
@@ -5943,6 +6018,7 @@ AARandomMover::AARandomMover(GameObserver* observer, int _id, MTGCardInstance *
{
if (_target)
target = _target;
andAbility = NULL;
}
MTGGameZone * AARandomMover::destinationZone(Targetable * target,string zone)
@@ -5996,7 +6072,24 @@ int AARandomMover::resolve()
return 1;
}
}
p->game->putInZone(toMove, fromDest, toDest);
MTGCardInstance *newTarget = p->game->putInZone(toMove, fromDest, toDest);
if(newTarget)
{
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = newTarget;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
}
return 1;
}
}
@@ -6016,6 +6109,7 @@ AARandomMover * AARandomMover::clone() const
AARandomMover::~AARandomMover()
{
SAFE_DELETE(andAbility);
}
//Random Discard
@@ -9401,6 +9495,12 @@ void AACastCard::Update(float dt)
return;
if(cardNamed.size() && !theNamedCard)
{
if(cardNamed.find("randomcard") != string::npos){ //cast a random card from collection.
MTGCard *rndCard = NULL;
while(!rndCard || rndCard->data->isLand())
rndCard = MTGCollection()->getCardById(MTGCollection()->ids.at(std::rand() % (MTGCollection()->ids).size()));
cardNamed = rndCard->data->name;
}
if (cardNamed.find("imprintedcard") != string::npos)
{
if (source && source->currentimprintName.size())
+3 -3
View File
@@ -131,7 +131,7 @@ void GuiCombat::autoaffectDamage(AttackerDamaged* attacker, CombatStep step)
{
(*it)->clearDamage();
unsigned actual_damage = MIN(damage, (unsigned)MAX((*it)->card->toughness, 0));
if (attacker->card->has(Constants::DEATHTOUCH) && actual_damage > 1)
if ((attacker->card->has(Constants::DEATHTOUCH) || attacker->card->has(Constants::PERPETUALDEATHTOUCH)) && actual_damage > 1)
actual_damage = 1;
(*it)->addDamage(actual_damage, attacker);
attacker->addDamage((*it)->card->stepPower(step), *it);
@@ -159,7 +159,7 @@ void GuiCombat::removeOne(DefenserDamaged* blocker, CombatStep)
{
blocker->addDamage(-1, activeAtk);
for (vector<DamagerDamaged*>::iterator it = activeAtk->blockers.begin(); it != activeAtk->blockers.end(); ++it)
if (activeAtk->card->has(Constants::DEATHTOUCH) ? ((*it)->sumDamages() < 1) : (!(*it)->hasLethalDamage()))
if ((activeAtk->card->has(Constants::DEATHTOUCH) || activeAtk->card->has(Constants::PERPETUALDEATHTOUCH)) ? ((*it)->sumDamages() < 1) : (!(*it)->hasLethalDamage()))
{
(*it)->addDamage(1, activeAtk);
return;
@@ -363,7 +363,7 @@ bool GuiCombat::CheckUserInput(JButton key)
damage -= now;
if (damage > 0)
addOne(active, step);
else if (activeAtk->card->has(Constants::DEATHTOUCH))
else if (activeAtk->card->has(Constants::DEATHTOUCH) || activeAtk->card->has(Constants::PERPETUALDEATHTOUCH))
for (; now >= 1; --now)
removeOne(active, step);
else
+36
View File
@@ -3269,6 +3269,12 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return NULL;
MTGAbility * a = NEW AARandomMover(observer, id, card, target, splitRandomMove[1],splitfrom[1],splitto[1]);
a->oneShot = true;
if(storedAndAbility.size())
{
string stored = storedAndAbility;
storedAndAbility.clear();
((AARandomMover*)a)->andAbility = parseMagicLine(stored, id, spell, card);
}
return a;
}
@@ -3349,6 +3355,36 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
}
}
//Conjure a card
found = s.find("conjure");
if (found != string::npos)
{
replace(s.begin(), s.end(), '^', ','); // To allow the usage of ^ instead of , char (e.g. using conjure keyword inside transforms)
string cardName = "";
vector<string> splitCard = parseBetween(s, "cards(", ")");
if (splitCard.size())
{
cardName = splitCard[1];
}
string cardZone = "";
vector<string> splitZone = parseBetween(s, "zone(", ")");
if (splitZone.size())
{
cardZone = splitZone[1];
}
MTGAbility * a = NEW AAConjure(observer, id, card, target, NULL, cardName, cardZone);
a->oneShot = 1;
a->canBeInterrupted = false;
//andability
if(storedAndAbility.size())
{
string stored = storedAndAbility;
storedAndAbility.clear();
((AAConjure*)a)->andAbility = parseMagicLine(stored, id, spell, card);
}
return a;
}
//foretell
found = s.find("doforetell");
if (found != string::npos)
+3 -1
View File
@@ -220,7 +220,9 @@ const char* Constants::MTGBasicAbilities[] = {
"nodngplr", //Controller can't venture
"canplayauraequiplibrarytop", //auras and equipment
"counterdeath", //It gains a 1/1 counter when it returns from graveyard (to use with inplaydeath and inplaytapdeath)"
"dungeoncompleted" //This dungeon has been completed
"dungeoncompleted", //This dungeon has been completed
"perpetuallifelink", //It gains lifelink perpetually
"perpetualdeathtouch" //It gains deathtouch perpetually
};
map<string,int> Constants::MTGBasicAbilitiesMap;
+7 -3
View File
@@ -726,12 +726,14 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone
WEvent * e = NEW WEventZoneChange(copy, from, to);
g->receiveEvent(e);
// Erasing counters from copy after the event has been triggered (no counter can survive to a zone changing)
// Erasing counters from copy after the event has been triggered (no counter can survive to a zone changing except the perpetual ones)
if(doCopy && copy->counters && copy->counters->mCount > 0){
for (unsigned int i = 0; i < copy->counters->counters.size(); i++){
Counter * counter = copy->counters->counters[i];
for(int j = counter->nb; j > 0; j--)
copy->counters->removeCounter(counter->name.c_str(), counter->power, counter->toughness, true);
for(int j = counter->nb; j > 0; j--){
if(counter->name.find("perpetual") == string::npos)
copy->counters->removeCounter(counter->name.c_str(), counter->power, counter->toughness, true);
}
}
}
}
@@ -863,6 +865,8 @@ MTGCardInstance * MTGGameZone::removeCard(MTGCardInstance * card, int createCopy
copy->basicAbilities[Constants::GAINEDHANDDEATH] = card->basicAbilities[Constants::GAINEDHANDDEATH];
copy->basicAbilities[Constants::GAINEDDOUBLEFACEDEATH] = card->basicAbilities[Constants::GAINEDDOUBLEFACEDEATH];
copy->basicAbilities[Constants::DUNGEONCOMPLETED] = card->basicAbilities[Constants::DUNGEONCOMPLETED];
copy->basicAbilities[Constants::PERPETUALDEATHTOUCH] = card->basicAbilities[Constants::PERPETUALDEATHTOUCH];
copy->basicAbilities[Constants::PERPETUALLIFELINK] = card->basicAbilities[Constants::PERPETUALLIFELINK];
copy->damageInflictedAsCommander = card->damageInflictedAsCommander;
copy->numofcastfromcommandzone = card->numofcastfromcommandzone;
for (int i = 0; i < ManaCost::MANA_PAID_WITH_BESTOW +1; i++)
+2 -2
View File
@@ -3982,7 +3982,7 @@ int MTGLifelinkRule::receiveEvent(WEvent * event)
WEventDamage * e = (WEventDamage *) event;
Damage * d = e->damage;
MTGCardInstance * card = d->source;
if (d->damage > 0 && card && (card->basicAbilities[(int)Constants::LIFELINK]||card->LKIbasicAbilities[(int)Constants::LIFELINK]))
if (d->damage > 0 && card && (card->basicAbilities[(int)Constants::LIFELINK]||card->basicAbilities[(int)Constants::PERPETUALLIFELINK]||card->LKIbasicAbilities[(int)Constants::LIFELINK]||card->LKIbasicAbilities[(int)Constants::PERPETUALLIFELINK]))
{
card->controller()->gainLife(d->damage, card);
return 1;
@@ -4026,7 +4026,7 @@ int MTGDeathtouchRule::receiveEvent(WEvent * event)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) (d->target);
if (card->basicAbilities[(int)Constants::DEATHTOUCH]||card->LKIbasicAbilities[(int)Constants::DEATHTOUCH])
if (card->basicAbilities[(int)Constants::DEATHTOUCH]||card->basicAbilities[(int)Constants::PERPETUALDEATHTOUCH]||card->LKIbasicAbilities[(int)Constants::DEATHTOUCH]||card->LKIbasicAbilities[(int)Constants::PERPETUALDEATHTOUCH])
{
_target->destroy();
return 1;