Bug fix for the biggest crash and fixes to the primitives

I figured a while ago that the cause of the crash was a rendering issue, not an engine issue, since you could copy spells in the stack if you didn't targeted them. The crash was most likely in ActionStack, just had to follow the trace. It doesn't affect anithing else, that line was trying to render something in the stack that's a permanent but it always had bad ptrs.
This commit is contained in:
Eduardo
2019-04-17 20:28:43 -05:00
parent c3937ce517
commit 3dfcc65fa6
5 changed files with 221 additions and 196 deletions

View File

@@ -342,14 +342,14 @@ int OrderedAIAction::getEfficiency()
// Bonus if almost no cards in hand
if (p->game->hand->nb_cards <= 1)
{
efficiency = 60;
efficiency = 50;
}
else efficiency = 30;
}
//nothing huge here, just ensuring that Ai makes his noncreature becomers into creatures during first main, so it can actually use them in combat.
if (coreAbilityCardTarget && !coreAbilityCardTarget->isCreature() && currentPhase == MTG_PHASE_FIRSTMAIN)
if (coreAbilityCardTarget && !coreAbilityCardTarget->isCreature() && !coreAbilityCardTarget->isTapped() && currentPhase == MTG_PHASE_FIRSTMAIN)
{
efficiency = 60;
efficiency = 50;
}
break;
}
@@ -603,8 +603,8 @@ int OrderedAIAction::getEfficiency()
}
else
{
// We don't want to return the ability source cards in play to own hand, save rare combos
// cards like Blinking Spirit use to be auto lose for AI
// We don't want to return the ability source cards that are in play to own hand, save rare combos
// cards like Blinking Spirit used to be auto lose for AI
if(z == p->game->hand || z == p->game->library)
efficiency = 1;
else efficiency = 50;
@@ -662,8 +662,8 @@ int OrderedAIAction::getEfficiency()
SAFE_DELETE(parsedAICC);
}
else
efficiency = 0;
else // this is why the AI never chooses any card at all? reveal is used to get cards so it should be at better value
efficiency = 60;
}
//At this point the "basic" efficiency is computed, we further tweak it depending on general decisions, independent of theAbility type
@@ -708,7 +708,14 @@ int OrderedAIAction::getEfficiency()
if (ability->source)
if(ability->source->hasType(Subtypes::TYPE_PLANESWALKER))
efficiency += 50;
efficiency += 40;
else if(ability->source->hasType(Subtypes::TYPE_LAND))
{ // probably a shockland, don't pay life if hand is empty
if (p->game->hand->nb_cards == 0 || p->life<=2)
// check that's not a manland(like Celestial Colonnade)
if(efficiency < 50)
efficiency = 0;
}
SAFE_DELETE(transAbility);
return efficiency;
@@ -2365,6 +2372,10 @@ int AIPlayerBaka::chooseTarget(TargetChooser * _tc, Player * forceTarget,MTGCard
cardEffect = af.abilityEfficiency(withoutGuessing,this,MODE_TARGET,tc,NULL);
delete withoutGuessing;
}
// Don't really like it but green mana producing auras targeting the player is one of the most reported bugs
if(cardEffect == BAKA_EFFECT_DONTKNOW && tc->source->hasSubtype(Subtypes::TYPE_AURA) && tc->source->hasColor(Constants::MTG_COLOR_GREEN))
cardEffect = BAKA_EFFECT_GOOD;
if (cardEffect != BAKA_EFFECT_GOOD)
{
target = this->opponent();
@@ -2571,164 +2582,164 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if( card->has(Constants::CANPLAYFROMGRAVEYARD) || card->has(Constants::TEMPFLASHBACK) || hasFlashback )
{
if (!CanHandleCost(card->getManaCost(),card))
continue;
if (!CanHandleCost(card->getManaCost(),card))
continue;
if (hasFlashback && !CanHandleCost(card->getManaCost()->getFlashback(),card))
continue;
if (hasFlashback && !CanHandleCost(card->getManaCost()->getFlashback(),card))
continue;
// Case were manacost is equal to flashback cost, if they are different the AI hangs
if (hasFlashback && (card->getManaCost() != card->getManaCost()->getFlashback()) )
continue;
// Case were manacost is equal to flashback cost, if they are different the AI hangs
if (hasFlashback && (card->getManaCost() != card->getManaCost()->getFlashback()) )
continue;
if (card->hasType(Subtypes::TYPE_LAND))
{
if (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY)
continue;
}
else
{
if (game->playRestrictions->canPutIntoZone(card, game->stack) == PlayRestriction::CANT_PLAY)
continue;
}
if (card->hasType(Subtypes::TYPE_LAND))
{
if (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY)
continue;
}
else
{
if (game->playRestrictions->canPutIntoZone(card, game->stack) == PlayRestriction::CANT_PLAY)
continue;
}
if (card->hasType(Subtypes::TYPE_LEGENDARY) && game->inPlay->findByName(card->name))
continue;
//glimmervoid alias to avoid ai stalling the game as the hint combo is stuck
//next card to play was galvanic blast but on activate combo it clashes with glimmervoid...
if ((card->alias == 48132) && (card->controller()->game->inPlay->countByType("artifact") < 1))
continue;
if (card->hasType(Subtypes::TYPE_LEGENDARY) && game->inPlay->findByName(card->name))
continue;
//glimmervoid alias to avoid ai stalling the game as the hint combo is stuck
//next card to play was galvanic blast but on activate combo it clashes with glimmervoid...
if ((card->alias == 48132) && (card->controller()->game->inPlay->countByType("artifact") < 1))
continue;
if (card->has(Constants::TREASON) && observer->getCurrentGamePhase() != MTG_PHASE_FIRSTMAIN)
continue;
if (card->has(Constants::TREASON) && observer->getCurrentGamePhase() != MTG_PHASE_FIRSTMAIN)
continue;
if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1]))
continue;
if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1]))
continue;
if(hints && hints->HintSaysItsForCombo(observer,card))
{
if(hints->canWeCombo(observer,card,this))
{
AbilityFactory af(observer);
int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions());
if(!canPlay)
continue;
nextCardToPlay = card;
gotPayments.clear();
if((!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->getKicker()))
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost());
return activateCombo();
}
else
{
nextCardToPlay = NULL;
continue;
}
}
int currentCost = card->getManaCost()->getConvertedCost();
int hasX = card->getManaCost()->hasX();
gotPayments.clear();
if((!pMana->canAfford(card->getManaCost()) || card->getManaCost()->getKicker()))
gotPayments = canPayMana(card,card->getManaCost());
//for preformence reason we only look for specific mana if the payment couldn't be made with pmana.
if ((currentCost > maxCost || hasX) && (gotPayments.size() || pMana->canAfford(card->getManaCost())))
{
TargetChooserFactory tcf(observer);
TargetChooser * tc = tcf.createTargetChooser(card);
int shouldPlayPercentage = 0;
if (tc)
{
int hasTarget = chooseTarget(tc,NULL,NULL,true);
if(
(tc->maxtargets > hasTarget && tc->maxtargets > 1 && !tc->targetMin && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS) ||//target=<3>creature
(tc->maxtargets == TargetChooser::UNLITMITED_TARGETS && hasTarget < 1)//target=creatures
)
hasTarget = 0;
if (!hasTarget)//single target covered here.
{
SAFE_DELETE(tc);
continue;
}
shouldPlayPercentage = 90;
if(tc->targetMin && hasTarget < tc->maxtargets)
shouldPlayPercentage = 0;
if(tc->maxtargets > 1 && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS && hasTarget <= tc->maxtargets)
{
int maxA = hasTarget-tc->maxtargets;
shouldPlayPercentage += (10*maxA);//reduce the chances of playing multitarget if we are not above max targets.
}
if(tc->maxtargets == TargetChooser::UNLITMITED_TARGETS)
{
shouldPlayPercentage = 40 + (10*hasTarget);
int totalCost = pMana->getConvertedCost()-currentCost;
int totalTargets = hasTarget+hasTarget;
if(hasX && totalCost <= totalTargets)// {x} spell with unlimited targeting tend to divide damage, we want atleast 1 damage per target before casting.
{
shouldPlayPercentage = 0;
}
}
SAFE_DELETE(tc);
}
else
{
// Refactor to not check effect of lands since it always returned BAKA_EFFECT_DONTKNOW
// If it is a land, play it
if (card->isLand())
{
shouldPlayPercentage = 90;
}
else {
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD) {
if(hints && hints->HintSaysItsForCombo(observer,card))
{
if(hints->canWeCombo(observer,card,this))
{
AbilityFactory af(observer);
int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions());
if(!canPlay)
continue;
nextCardToPlay = card;
gotPayments.clear();
if((!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->getKicker()))
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost());
return activateCombo();
}
else
{
nextCardToPlay = NULL;
continue;
}
}
int currentCost = card->getManaCost()->getConvertedCost();
int hasX = card->getManaCost()->hasX();
gotPayments.clear();
if((!pMana->canAfford(card->getManaCost()) || card->getManaCost()->getKicker()))
gotPayments = canPayMana(card,card->getManaCost());
//for preformence reason we only look for specific mana if the payment couldn't be made with pmana.
if ((currentCost > maxCost || hasX) && (gotPayments.size() || pMana->canAfford(card->getManaCost())))
{
TargetChooserFactory tcf(observer);
TargetChooser * tc = tcf.createTargetChooser(card);
int shouldPlayPercentage = 0;
if (tc)
{
int hasTarget = chooseTarget(tc,NULL,NULL,true);
if(
(tc->maxtargets > hasTarget && tc->maxtargets > 1 && !tc->targetMin && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS) ||//target=<3>creature
(tc->maxtargets == TargetChooser::UNLITMITED_TARGETS && hasTarget < 1)//target=creatures
)
hasTarget = 0;
if (!hasTarget)//single target covered here.
{
SAFE_DELETE(tc);
continue;
}
shouldPlayPercentage = 90;
if(tc->targetMin && hasTarget < tc->maxtargets)
shouldPlayPercentage = 0;
if(tc->maxtargets > 1 && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS && hasTarget <= tc->maxtargets)
{
int maxA = hasTarget-tc->maxtargets;
shouldPlayPercentage += (10*maxA);//reduce the chances of playing multitarget if we are not above max targets.
}
if(tc->maxtargets == TargetChooser::UNLITMITED_TARGETS)
{
shouldPlayPercentage = 40 + (10*hasTarget);
int totalCost = pMana->getConvertedCost()-currentCost;
int totalTargets = hasTarget+hasTarget;
if(hasX && totalCost <= totalTargets)// {x} spell with unlimited targeting tend to divide damage, we want atleast 1 damage per target before casting.
{
shouldPlayPercentage = 0;
}
}
SAFE_DELETE(tc);
}
else
{
// Refactor to not check effect of lands since it always returned BAKA_EFFECT_DONTKNOW
// If it is a land, play it
if (card->isLand())
{
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay) {
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else {
// shouldPlay == baka_effect_bad giving it a 10 for odd ball lottery chance.
shouldPlayPercentage = 10;
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD) {
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay) {
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else {
// shouldPlay == baka_effect_bad giving it a 10 for odd ball lottery chance.
shouldPlayPercentage = 10;
}
}
}
}
//Reduce the chances of playing a spell with X cost if available mana is low
if (hasX)
{
int xDiff = pMana->getConvertedCost() - currentCost;
if (xDiff < 0)
xDiff = 0;
shouldPlayPercentage = shouldPlayPercentage - static_cast<int> ((shouldPlayPercentage * 1.9f) / (1 + xDiff));
}
if(card->getManaCost() && card->getManaCost()->getKicker() && card->getManaCost()->getKicker()->isMulti)
{
shouldPlayPercentage = 10* size_t(gotPayments.size())/int(1+(card->getManaCost()->getConvertedCost()+card->getManaCost()->getKicker()->getConvertedCost()));
if(shouldPlayPercentage <= 10)
shouldPlayPercentage = shouldPlayPercentage/3;
}
DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl
<<"shouldPlayPercentage = "<< shouldPlayPercentage);
if(card->getRestrictions().size())
{
AbilityFactory af(observer);
int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions());
if(!canPlay)
continue;
}
int randomChance = randomGenerator.random();
int chance = randomChance % 100;
if (chance > shouldPlayPercentage)
continue;
if(shouldPlayPercentage <= 10)
{
DebugTrace("shouldPlayPercentage was less than 10 this was a lottery roll on RNG");
}
nextCardToPlay = card;
maxCost = currentCost;
if (hasX)
maxCost = pMana->getConvertedCost();
}
//Reduce the chances of playing a spell with X cost if available mana is low
if (hasX)
{
int xDiff = pMana->getConvertedCost() - currentCost;
if (xDiff < 0)
xDiff = 0;
shouldPlayPercentage = shouldPlayPercentage - static_cast<int> ((shouldPlayPercentage * 1.9f) / (1 + xDiff));
}
if(card->getManaCost() && card->getManaCost()->getKicker() && card->getManaCost()->getKicker()->isMulti)
{
shouldPlayPercentage = 10* size_t(gotPayments.size())/int(1+(card->getManaCost()->getConvertedCost()+card->getManaCost()->getKicker()->getConvertedCost()));
if(shouldPlayPercentage <= 10)
shouldPlayPercentage = shouldPlayPercentage/3;
}
DebugTrace("Should I play from grave " << (card ? card->name : "Nothing" ) << "?" << endl
<<"shouldPlayPercentage = "<< shouldPlayPercentage);
if(card->getRestrictions().size())
{
AbilityFactory af(observer);
int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions());
if(!canPlay)
continue;
}
int randomChance = randomGenerator.random();
int chance = randomChance % 100;
if (chance > shouldPlayPercentage)
continue;
if(shouldPlayPercentage <= 10)
{
DebugTrace("shouldPlayPercentage was less than 10 this was a lottery roll on RNG");
}
nextCardToPlay = card;
maxCost = currentCost;
if (hasX)
maxCost = pMana->getConvertedCost();
}
}
}
//canplayfromexile
@@ -2863,7 +2874,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if(shouldPlayPercentage <= 10)
shouldPlayPercentage = shouldPlayPercentage/3;
}
DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl
DebugTrace("Should I play from exile" << (card ? card->name : "Nothing" ) << "?" << endl
<<"shouldPlayPercentage = "<< shouldPlayPercentage);
if(card->getRestrictions().size())
{
@@ -2912,8 +2923,9 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if (card->has(Constants::TREASON) && observer->getCurrentGamePhase() != MTG_PHASE_FIRSTMAIN)
continue;
if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1]))
continue;
//PLaneswalkers are now legendary so this is redundant
//if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1]))
//continue;
if(hints && hints->HintSaysItsForCombo(observer,card))
{
@@ -3017,7 +3029,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if(shouldPlayPercentage <= 10)
shouldPlayPercentage = shouldPlayPercentage/3;
}
DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl
DebugTrace("Should I play from hand" << (card ? card->name : "Nothing" ) << "?" << endl
<<"shouldPlayPercentage = "<< shouldPlayPercentage);
if(card->getRestrictions().size())
{
@@ -3444,12 +3456,17 @@ int AIPlayerBaka::getCreaturesInfo(Player * player, int neededInfo, int untapMod
int AIPlayerBaka::chooseAttackers()
{
int myCreatures = getCreaturesInfo(this, INFO_NBCREATURES, -1, 1);
if (myCreatures < 1)
return 0;
//Attack with all creatures
//How much damage can the other player do during his next Attack ?
int opponentForce = getCreaturesInfo(opponent(), INFO_CREATURESPOWER);
int opponentCreatures = getCreaturesInfo(opponent(), INFO_NBCREATURES);
int myForce = getCreaturesInfo(this, INFO_CREATURESPOWER, -1, 1);
int myCreatures = getCreaturesInfo(this, INFO_NBCREATURES, -1, 1);
if(opponent()->life < 5)
agressivity += 31;
bool attack = ((myCreatures > opponentCreatures) || (myForce > opponentForce) || (myForce > 2 * opponent()->life));
if (agressivity > 80 && !attack && life > opponentForce)
{
@@ -3466,7 +3483,7 @@ int AIPlayerBaka::chooseAttackers()
MTGCardInstance * card = NULL;
while ((card = cd.nextmatch(game->inPlay, card)))
{
if (hints && hints->HintSaysAlwaysAttack(observer, card))
if ((hints && hints->HintSaysAlwaysAttack(observer, card)) || card->has(Constants::UNBLOCKABLE))
{
if (!card->isAttacker())
{