mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
WrappedAbility: fix TriggeredRemembered being sacrificed
This commit is contained in:
committed by
Michael Kamensky
parent
35ca083650
commit
584b6bd81e
@@ -716,7 +716,6 @@ public class ComputerUtil {
|
||||
CardCollection remaining = new CardCollection(cardlist);
|
||||
final CardCollection sacrificed = new CardCollection();
|
||||
final Card host = source.getHostCard();
|
||||
final boolean considerSacLogic = "ConsiderSac".equals(source.getParam("AILogic"));
|
||||
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
|
||||
|
||||
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
|
||||
@@ -732,14 +731,14 @@ public class ComputerUtil {
|
||||
if (!ai.canLoseLife() || ai.cantLose()) {
|
||||
return sacrificed; // sacrifice none
|
||||
}
|
||||
} else if (!considerSacLogic) {
|
||||
} else {
|
||||
return sacrificed; // sacrifice none
|
||||
}
|
||||
}
|
||||
boolean exceptSelf = "ExceptSelf".equals(source.getParam("AILogic"));
|
||||
boolean removedSelf = false;
|
||||
|
||||
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit") || considerSacLogic) {
|
||||
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit")) {
|
||||
if (source.hasParam("Exploit")) {
|
||||
for (Trigger t : host.getTriggers()) {
|
||||
if (t.getMode() == TriggerType.Exploited) {
|
||||
@@ -761,17 +760,21 @@ public class ComputerUtil {
|
||||
public boolean apply(final Card c) {
|
||||
int sacThreshold = 190;
|
||||
|
||||
if ("HeartPiercer".equals(source.getParam("SacrificeParam"))) {
|
||||
if (c.getNetPower() == 0) {
|
||||
String logic = source.getParamOrDefault("AILogic", "");
|
||||
if (logic.startsWith("SacForDamage")) {
|
||||
if (c.getNetPower() <= 0) {
|
||||
return false;
|
||||
} else if (c.getNetPower() >= ai.getOpponentsSmallestLifeTotal()) {
|
||||
return true;
|
||||
} else if (logic.endsWith(".GiantX2") && c.getType().hasCreatureType("Giant")
|
||||
&& c.getNetPower() * 2 >= ai.getOpponentsSmallestLifeTotal()) {
|
||||
return true; // TODO: generalize this for any type and actually make the AI prefer giants?
|
||||
}
|
||||
}
|
||||
|
||||
if ("DesecrationDemon".equals(source.getParam("AILogic"))) {
|
||||
sacThreshold = SpecialCardAi.DesecrationDemon.getSacThreshold();
|
||||
} else if (considerSacLogic && considerSacThreshold != -1) {
|
||||
} else if (considerSacThreshold != -1) {
|
||||
sacThreshold = considerSacThreshold;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.GenericChoice, ChooseGenericEffectAi.class)
|
||||
.put(ApiType.Goad, GoadAi.class)
|
||||
.put(ApiType.Haunt, HauntAi.class)
|
||||
.put(ApiType.ImmediateTrigger, AlwaysPlayAi.class)
|
||||
.put(ApiType.ImmediateTrigger, DelayedTriggerAi.class)
|
||||
.put(ApiType.Investigate, InvestigateAi.class)
|
||||
.put(ApiType.LoseLife, LifeLoseAi.class)
|
||||
.put(ApiType.LosesGame, GameLossAi.class)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import forge.ai.*;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class ImmediateTriggerAi extends SpellAbilityAi {
|
||||
// TODO: this class is largely reused from DelayedTriggerAi, consider updating
|
||||
|
||||
@Override
|
||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||
String logic = sa.getParamOrDefault("AILogic", "");
|
||||
if (logic.equals("Always")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SpellAbility trigsa = null;
|
||||
if (sa.hasAdditionalAbility("Execute")) {
|
||||
trigsa = sa.getAdditionalAbility("Execute");
|
||||
} else {
|
||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
||||
}
|
||||
trigsa.setActivatingPlayer(ai);
|
||||
|
||||
if (trigsa instanceof AbilitySub) {
|
||||
return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
|
||||
} else {
|
||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
SpellAbility trigsa = null;
|
||||
if (sa.hasAdditionalAbility("Execute")) {
|
||||
trigsa = sa.getAdditionalAbility("Execute");
|
||||
} else {
|
||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
||||
}
|
||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
trigsa.setActivatingPlayer(ai);
|
||||
|
||||
if (!sa.hasParam("OptionalDecider")) {
|
||||
return aic.doTrigger(trigsa, true);
|
||||
} else {
|
||||
return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
String logic = sa.getParamOrDefault("AILogic", "");
|
||||
if (logic.equals("Always")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SpellAbility trigsa = null;
|
||||
if (sa.hasAdditionalAbility("Execute")) {
|
||||
trigsa = sa.getAdditionalAbility("Execute");
|
||||
} else {
|
||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
||||
}
|
||||
trigsa.setActivatingPlayer(ai);
|
||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
// Wrapper ability that checks the requirements again just before
|
||||
@@ -487,18 +486,6 @@ public class WrappedAbility extends Ability {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check timestamps of triggered objects
|
||||
final List<Object> original = Lists.newArrayList(sa.getTriggerRemembered());
|
||||
for (Object o : original) {
|
||||
if (o instanceof Card) {
|
||||
Card card = (Card) o;
|
||||
Card current = game.getCardState(card);
|
||||
if (current.getTimestamp() != card.getTimestamp()) {
|
||||
// TODO: figure out if NoTimestampCheck should be the default for ChangesZone triggers
|
||||
sa.getTriggerRemembered().remove(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
final Map<AbilityKey, Object> triggerMap = AbilityKey.newMap(sa.getTriggeringObjects());
|
||||
for (Entry<AbilityKey, Object> ev : triggerMap.entrySet()) {
|
||||
if (ev.getValue() instanceof Card) {
|
||||
|
||||
@@ -2,13 +2,11 @@ Name:Heart-Piercer Manticore
|
||||
ManaCost:2 R R
|
||||
Types:Creature Manticore
|
||||
PT:4/3
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigSacrifice | TriggerDescription$ When CARDNAME enters the battlefield, you may sacrifice another creature. When you do, CARDNAME deals damage equal to that creature's power to target creature or player.
|
||||
SVar:TrigSacrifice:DB$ Sacrifice | Optional$ True | SacValid$ Creature.Other | SacMessage$ another Creature | Amount$ 1 | AILogic$ ConsiderSac | RememberSacrificed$ True | SubAbility$ DBTrigger
|
||||
SVar:DBTrigger:DB$ ImmediateTrigger | Execute$ TrigDamage | RememberObjects$ RememberedCard | ConditionDefined$ Remembered | ConditionPresent$ Card | SubAbility$ DBCleanup | TriggerDescription$ When you do, CARDNAME deals damage equal to that creature's power to any target.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBTrigger | TriggerDescription$ When CARDNAME enters the battlefield, you may sacrifice another creature. When you do, CARDNAME deals damage equal to that creature's power to target creature or player.
|
||||
SVar:DBTrigger:AB$ ImmediateTrigger | Cost$ Sac<1/Creature.Other/another creature> | Execute$ TrigDamage | AILogic$ SacForDamage | RememberObjects$ Sacrificed | TriggerDescription$ When you do, CARDNAME deals damage equal to that creature's power to any target.
|
||||
SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ XPower | References$ XPower
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
K:Embalm:5 R
|
||||
SVar:XPower:TriggerRemembered$CardPower
|
||||
SVar:AIPreferenceParams:CreatureEvalThreshold$ 200
|
||||
SVar:DeckHas:Ability$Token
|
||||
DeckHas:Ability$Token & Ability$Sacrifice
|
||||
Oracle:When Heart-Piercer Manticore enters the battlefield, you may sacrifice another creature. When you do, Heart-Piercer Manticore deals damage equal to that creature's power to any target.\nEmbalm {5} {R} ({5} {R}, Exile this card from your graveyard: Create a token that's a copy of it, except it's a white Zombie Manticore with no mana cost. Embalm only as a sorcery.)
|
||||
|
||||
15
forge-gui/res/cardsfolder/upcoming/surtland_flinger.txt
Normal file
15
forge-gui/res/cardsfolder/upcoming/surtland_flinger.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
Name:Surtland Flinger
|
||||
ManaCost:3 R R
|
||||
Types:Creature Giant Berserker
|
||||
PT:4/6
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigImmediate | TriggerDescription$ Whenever CARDNAME attacks, you may sacrifice another creature. When you do, CARDNAME deals damage equal to the sacrificed creature's power to any target. If the sacrificed creature was a Giant, CARDNAME deals twice that much damage instead.
|
||||
SVar:TrigImmediate:AB$ ImmediateTrigger | Cost$ Sac<1/Creature.Other/another creature> | RememberObjects$ Sacrificed | Execute$ TrigDamage | AILogic$ SacForDamage.GiantX2 | TriggerDescription$ When you do, CARDNAME deals damage equal to the sacrificed creature's power to any target. If the sacrificed creature was a Giant, CARDNAME deals twice that much damage instead.
|
||||
SVar:TrigDamage:DB$ DealDamage | ConditionDefined$ DelayTriggerRememberedLKI | ConditionPresent$ Giant | ConditionCompare$ EQ0 | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | References$ X | SubAbility$ DBGiantDamage
|
||||
SVar:DBGiantDamage:DB$ DealDamage | ConditionDefined$ DelayTriggerRememberedLKI | ConditionPresent$ Giant | ConditionCompare$ GE1 | Defined$ Targeted | NumDmg$ Y | References$ Y
|
||||
SVar:X:TriggerRemembered$CardPower
|
||||
SVar:Y:TriggerRemembered$CardPower/Times.2
|
||||
SVar:AIPreferenceParams:CreatureEvalThreshold$ 200
|
||||
SVar:HasAttackEffect:TRUE
|
||||
DeckHas:Ability$Sacrifice
|
||||
DeckHints:Type$Giant
|
||||
Oracle:Whenever Surtland Flinger attacks, you may sacrifice another creature. When you do, Surtland Flinger deals damage equal to the sacrificed creature’s power to any target. If the sacrificed creature was a Giant, Surtland Flinger deals twice that much damage instead.
|
||||
Reference in New Issue
Block a user