mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Kicker Linked Lite (#2401)
* Kicker Linked Lite * Fix incomplete LKI for Metallic Mimic and possibly others
This commit is contained in:
@@ -232,7 +232,7 @@ public class AiController {
|
||||
if (!validCard.contains("Self")) {
|
||||
continue;
|
||||
}
|
||||
if (validCard.contains("notkicked")) {
|
||||
if (validCard.contains("!kicked")) {
|
||||
if (sa.isKicked()) {
|
||||
continue;
|
||||
}
|
||||
@@ -288,7 +288,7 @@ public class AiController {
|
||||
if (!validCard.contains("Self")) {
|
||||
continue;
|
||||
}
|
||||
if (validCard.contains("notkicked")) {
|
||||
if (validCard.contains("!kicked")) {
|
||||
if (sa.isKicked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1766,7 +1766,7 @@ public class AbilityUtils {
|
||||
|
||||
// Count$Kicked.<numHB>.<numNotHB>
|
||||
if (sq[0].startsWith("Kicked")) {
|
||||
boolean kicked = sa.isKicked() || c.getKickerMagnitude() > 0;
|
||||
boolean kicked = sa.isKicked() || (!isUnlinkedFromCastSA(ctb, c) && c.getKickerMagnitude() > 0);
|
||||
return doXMath(Integer.parseInt(kicked ? sq[1] : sq[2]), expr, c, ctb);
|
||||
}
|
||||
|
||||
@@ -2004,7 +2004,7 @@ public class AbilityUtils {
|
||||
}
|
||||
|
||||
if (sq[0].startsWith("Kicked")) { // fallback for not spellAbility
|
||||
return doXMath(calculateAmount(c, sq[c.getKickerMagnitude() > 0 ? 1 : 2], ctb), expr, c, ctb);
|
||||
return doXMath(calculateAmount(c, sq[!isUnlinkedFromCastSA(ctb, c) && c.getKickerMagnitude() > 0 ? 1 : 2], ctb), expr, c, ctb);
|
||||
}
|
||||
if (sq[0].startsWith("Escaped")) {
|
||||
return doXMath(calculateAmount(c, sq[c.getCastSA() != null && c.getCastSA().isEscape() ? 1 : 2], ctb), expr, c, ctb);
|
||||
@@ -2073,7 +2073,7 @@ public class AbilityUtils {
|
||||
return doXMath(c.getKeywordMagnitude(Keyword.smartValueOf(l[0].split(" ")[1])), expr, c, ctb);
|
||||
}
|
||||
if (sq[0].contains("TimesKicked")) {
|
||||
return doXMath(c.getKickerMagnitude(), expr, c, ctb);
|
||||
return doXMath(isUnlinkedFromCastSA(ctb, c) ? 0 : c.getKickerMagnitude(), expr, c, ctb);
|
||||
}
|
||||
if (sq[0].contains("TimesPseudokicked")) {
|
||||
return doXMath(c.getPseudoKickerMagnitude(), expr, c, ctb);
|
||||
@@ -3894,4 +3894,35 @@ public class AbilityUtils {
|
||||
}
|
||||
return types.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an ability source can be considered a "broken link" on a specific host
|
||||
* (which usually means it won't have its normal effect).
|
||||
* <br>
|
||||
* Because castSA gets used to compare it can only make a safe conclusion for
|
||||
* links that depend on stack decisions and can't be gained by other means
|
||||
* e.g. Kicker costs.
|
||||
*
|
||||
* @param ctb the source of the ability
|
||||
* @param card the host that it should be linked to
|
||||
* @return true if the ability can't be linked
|
||||
*/
|
||||
public static boolean isUnlinkedFromCastSA(final CardTraitBase ctb, final Card card) {
|
||||
// check if it should come from same host
|
||||
if (ctb != null && ctb.isIntrinsic() && ctb.getHostCard().equals(card)) {
|
||||
Card host = ctb.getOriginalHost();
|
||||
SpellAbility castSA = card.getCastSA();
|
||||
if (host != null && castSA != null) {
|
||||
Card castHost = castSA.getOriginalHost();
|
||||
if (castHost == null) {
|
||||
castHost = castSA.getHostCard();
|
||||
}
|
||||
// impossible to match with the other part when not even from same host
|
||||
if (!host.equals(castHost)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1708,6 +1708,10 @@ public class CardProperty {
|
||||
if (!game.getPhaseHandler().isPlayerTurn(controller)) return false;
|
||||
return CombatUtil.couldAttackButNotAttacking(combat, card);
|
||||
} else if (property.startsWith("kicked")) {
|
||||
// CR 607.2i check cost is linked
|
||||
if (AbilityUtils.isUnlinkedFromCastSA(spellAbility, card)) {
|
||||
return false;
|
||||
}
|
||||
if (property.equals("kicked")) {
|
||||
if (card.getKickerMagnitude() == 0) {
|
||||
return false;
|
||||
@@ -1717,10 +1721,6 @@ public class CardProperty {
|
||||
if ("1".equals(s) && !card.isOptionalCostPaid(OptionalCost.Kicker1)) return false;
|
||||
if ("2".equals(s) && !card.isOptionalCostPaid(OptionalCost.Kicker2)) return false;
|
||||
}
|
||||
} else if (property.startsWith("notkicked")) {
|
||||
if (card.getKickerMagnitude() > 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.startsWith("pseudokicked")) {
|
||||
if (property.equals("pseudokicked")) {
|
||||
if (!card.isOptionalCostPaid(OptionalCost.Generic)) return false;
|
||||
|
||||
@@ -238,6 +238,10 @@ public final class CardUtil {
|
||||
|
||||
newCopy.setCounters(Maps.newHashMap(in.getCounters()));
|
||||
|
||||
newCopy.setTributed(in.isTributed());
|
||||
newCopy.setMonstrous(in.isMonstrous());
|
||||
newCopy.setRenowned(in.isRenowned());
|
||||
|
||||
newCopy.setColor(in.getColor().getColor());
|
||||
newCopy.setPhasedOut(in.getPhasedOut());
|
||||
|
||||
@@ -266,6 +270,15 @@ public final class CardUtil {
|
||||
newCopy.addImprintedCards(in.getImprintedCards());
|
||||
newCopy.setChosenCards(new CardCollection(in.getChosenCards()));
|
||||
|
||||
newCopy.setChosenType(in.getChosenType());
|
||||
newCopy.setChosenType2(in.getChosenType2());
|
||||
newCopy.setChosenName(in.getChosenName());
|
||||
newCopy.setChosenName2(in.getChosenName2());
|
||||
newCopy.setChosenColors(Lists.newArrayList(in.getChosenColors()));
|
||||
if (in.hasChosenNumber()) {
|
||||
newCopy.setChosenNumber(in.getChosenNumber());
|
||||
}
|
||||
|
||||
for (Table.Cell<Player, CounterType, Integer> cl : in.getEtbCounters()) {
|
||||
newCopy.addEtbCounter(cl.getColumnKey(), cl.getValue(), cl.getRowKey());
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ Types:Creature Angel
|
||||
PT:5/4
|
||||
K:Flying
|
||||
K:Kicker:W W
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+notkicked | Execute$ TrigDestroyYourLand | TriggerDescription$ When CARDNAME enters the battlefield, destroy all lands you control. If it was kicked, destroy all lands instead.
|
||||
SVar:TrigDestroyYourLand:DB$ DestroyAll | ValidCards$ Land.YouCtrl | SpellDescription$ CARDNAME destroys all land you control.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Secondary$ True | Execute$ TrigKicker | TriggerDescription$ Kicker: If you paid the kicker cost, destroy all lands instead.
|
||||
SVar:TrigKicker:DB$ DestroyAll | ValidCards$ Land | SpellDescription$ CARDNAME destroys all land.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroyYourLand | TriggerDescription$ When CARDNAME enters the battlefield, destroy all lands you control. If it was kicked, destroy all lands instead.
|
||||
SVar:TrigDestroyYourLand:DB$ DestroyAll | ValidCards$ Land.YouCtrl | ConditionDefined$ Self | ConditionPresent$ Card.!kicked | SubAbility$ TrigKicker
|
||||
SVar:TrigKicker:DB$ DestroyAll | ValidCards$ Land | Condition$ Kicked
|
||||
AI:RemoveDeck:Random
|
||||
DeckNeeds:Color$White
|
||||
Oracle:Kicker {W}{W} (You may pay an additional {W}{W} as you cast this spell.)\nFlying\nWhen Desolation Angel enters the battlefield, destroy all lands you control. If it was kicked, destroy all lands instead.
|
||||
|
||||
@@ -3,10 +3,9 @@ ManaCost:2 R R
|
||||
Types:Creature Giant
|
||||
PT:3/3
|
||||
K:Kicker:W W
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+notkicked | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy all other creatures you control. If it was kicked, destroy all other creatures instead.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Secondary$ True | Execute$ TrigKicker | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, destroy all other creatures.
|
||||
SVar:TrigDestroy:DB$ DestroyAll | ValidCards$ Creature.Other+YouCtrl
|
||||
SVar:TrigKicker:DB$ DestroyAll | ValidCards$ Creature.Other
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ When CARDNAME enters the battlefield, destroy all other creatures you control. If it was kicked, destroy all other creatures instead.
|
||||
SVar:TrigDestroy:DB$ DestroyAll | ValidCards$ Creature.Other+YouCtrl | ConditionDefined$ Self | ConditionPresent$ Card.!kicked | SubAbility$ TrigKicker
|
||||
SVar:TrigKicker:DB$ DestroyAll | ValidCards$ Creature.Other | Condition$ Kicked
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
DeckNeeds:Color$White
|
||||
|
||||
@@ -4,7 +4,7 @@ Types:Creature Elemental
|
||||
PT:0/0
|
||||
K:etbCounter:P1P1:X
|
||||
SVar:X:Count$xPaid
|
||||
R:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ Card.Self+counters_GE1_P1P1 | ReplaceWith$ Counters | PreventionEffect$ True | AlwaysReplace$ True | Description$ If damage would be dealt to CARDNAME while it has a +1/+1 counter on it, prevent that damage and remove that many +1/+1 counters from it. When one or more counters are removed from Magma Pummeler this way, it deals that much damage to any target.
|
||||
R:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ Card.Self+counters_GE1_P1P1 | ReplaceWith$ Counters | PreventionEffect$ True | AlwaysReplace$ True | Description$ If damage would be dealt to CARDNAME while it has a +1/+1 counter on it, prevent that damage and remove that many +1/+1 counters from it. When one or more counters are removed from CARDNAME this way, it deals that much damage to any target.
|
||||
SVar:Counters:DB$ RemoveCounter | Defined$ ReplacedTarget | CounterType$ P1P1 | CounterNum$ Y | RememberRemoved$ True | SubAbility$ DBImmediateTrigger
|
||||
SVar:Y:ReplaceCount$DamageAmount
|
||||
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigDamage | RememberSVarAmount$ Z | SubAbility$ DBCleanup | TriggerDescription$ When one or more counters are removed from CARDNAME this way, it deals that much damage to any target.
|
||||
|
||||
@@ -5,10 +5,10 @@ K:MayFlashSac
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ RaiseDead | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it becomes an Aura with "enchant creature put onto the battlefield with CARDNAME." Put target creature card from a graveyard onto the battlefield under your control and attach CARDNAME to it. When CARDNAME leaves the battlefield, that creature's controller sacrifices it. When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
||||
SVar:RaiseDead:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | TgtPrompt$ Select target creature card in a graveyard | ValidTgts$ Creature | ChangeNum$ 1 | SubAbility$ Aurify
|
||||
SVar:Aurify:DB$ Animate | IsPresent$ Card.Self | Types$ Aura | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | Duration$ Permanent | SubAbility$ NecromAttach
|
||||
SVar:NewAttach:SP$ Attach | Cost$ 2 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump | SubAbility$ DBDelay
|
||||
SVar:NewAttach:SP$ Attach | Cost$ 2 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
|
||||
SVar:DBDelay:DB$ DelayedTrigger | Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Execute$ TrigSacrifice | RememberObjects$ RememberedLKI | TriggerDescription$ When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
|
||||
SVar:NecromAttach:DB$ Attach | Defined$ Remembered
|
||||
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ DirectRemembered
|
||||
SVar:NecromAttach:DB$ Attach | Defined$ Remembered | SubAbility$ DBDelay
|
||||
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ DelayTriggerRememberedLKI
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ DBCleanup | Static$ True
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:NeedsToPlayVar:Y GE1
|
||||
|
||||
@@ -5,7 +5,7 @@ PT:5/3
|
||||
K:Haste
|
||||
K:Trample
|
||||
K:Kicker:R
|
||||
T:Mode$ Phase | Phase$ End of Turn | IsPresent$ Card.Self+notkicked | Execute$ TrigNotKicked | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of the end step, if CARDNAME wasn't kicked, sacrifice it.
|
||||
T:Mode$ Phase | Phase$ End of Turn | IsPresent$ Card.Self+!kicked | Execute$ TrigNotKicked | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of the end step, if CARDNAME wasn't kicked, sacrifice it.
|
||||
SVar:TrigNotKicked:DB$ Sacrifice
|
||||
# The following construct specifies that the AI always plays the spell kicked, and plays it unkicked on its own turn only if it will attack
|
||||
SVar:NeedsToPlay:WillAttack
|
||||
|
||||
@@ -4,9 +4,7 @@ Types:Creature Sphinx
|
||||
PT:3/5
|
||||
K:Flying
|
||||
K:Kicker:1 U
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+notkicked | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw three cards. Then if it wasn't kicked, discard three cards.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Secondary$ True | Execute$ TrigKicker | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, draw three cards.
|
||||
SVar:TrigKicker:DB$ Draw | NumCards$ 3
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw three cards. Then if it wasn't kicked, discard three cards.
|
||||
SVar:TrigDraw:DB$ Draw | NumCards$ 3 | SubAbility$ DBDiscard
|
||||
SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 3
|
||||
SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 3 | ConditionDefined$ Self | ConditionPresent$ Card.!kicked
|
||||
Oracle:Kicker {1}{U} (You may pay an additional {1}{U} as you cast this spell.)\nFlying\nWhen Sphinx of Lost Truths enters the battlefield, draw three cards. Then if it wasn't kicked, discard three cards.
|
||||
|
||||
@@ -489,7 +489,7 @@ public final class CardScriptParser {
|
||||
"blockedBySourceThisTurn", "blockedSource",
|
||||
"isBlockedByRemembered", "blockedRemembered",
|
||||
"blockedByRemembered", "unblocked", "attackersBandedWith",
|
||||
"kicked", "kicked1", "kicked2", "notkicked", "evoked",
|
||||
"kicked", "kicked1", "kicked2", "evoked",
|
||||
"HasDevoured", "HasNotDevoured", "IsMonstrous", "IsNotMonstrous",
|
||||
"CostsPhyrexianMana", "IsRemembered", "IsNotRemembered",
|
||||
"IsImprinted", "IsNotImprinted", "hasActivatedAbilityWithTapCost",
|
||||
|
||||
Reference in New Issue
Block a user