From 4bae4c7491ee1018d13e940ca571859a0e1b32c6 Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 18 Apr 2018 09:25:58 +0300 Subject: [PATCH 1/4] - Basic AI for Torgaar (currently won't sac anything as a part of cost payment) - Fixed references in the script for Torgaar. --- forge-ai/src/main/java/forge/ai/AiCostDecision.java | 9 +++++++++ forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java | 10 ++++++++-- .../res/cardsfolder/t/torgaar_famine_incarnate.txt | 7 +++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 2d93d12cf52..1d7179b017f 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -510,6 +510,15 @@ public class AiCostDecision extends CostDecisionMakerBase { Integer c = cost.convertAmount(); if (c == null) { if (ability.getSVar(cost.getAmount()).equals("XChoice")) { + if ("SacToReduceCost".equals(ability.getParam("AILogic"))) { + // e.g. Torgaar, Famine Incarnate + // TODO: currently returns an empty list, so the AI doesn't sacrifice anything. Trying to make + // the AI decide on creatures to sac makes the AI sacrifice them, but the cost is not reduced and the + // AI pays the full mana cost anyway (despite sacrificing creatures). + return PaymentDecision.card(new CardCollection()); + } + + // Other cards are assumed to be flagged RemAIDeck for now return null; } diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java index eb849eb92c9..6f4a821870c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java @@ -124,6 +124,13 @@ public class LifeSetAi extends SpellAbilityAi { amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa); } + // special cases when amount can't be calculated without targeting first + if (amount == 0 && "TargetedPlayer$StartingLife/HalfDown".equals(source.getSVar(amountStr))) { + // TODO: this assumes equal starting life for all players, which is not true e.g. for Archenemy + amount = ai.getStartingLife() / 2; + } + + if (sourceName.equals("Eternity Vessel") && (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) { return false; @@ -131,8 +138,7 @@ public class LifeSetAi extends SpellAbilityAi { // If the Target is gaining life, target self. // if the Target is modifying how much life is gained, this needs to - // be - // handled better + // be handled better final TargetRestrictions tgt = sa.getTargetRestrictions(); if (tgt != null) { sa.resetTargets(); diff --git a/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt b/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt index f87381c9654..78d418b99ee 100644 --- a/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt +++ b/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt @@ -2,12 +2,11 @@ Name:Torgaar, Famine Incarnate ManaCost:6 B B Types:Legendary Creature Avatar PT:7/6 -A:SP$ PermanentCreature | Cost$ 6 B B Sac | Announce$ X -S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ Y | EffectZone$ All | Description$ As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost. +A:SP$ PermanentCreature | Cost$ 6 B B Sac | Announce$ X | References$ X,Y | AILogic$ SacToReduceCost +S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ Y | EffectZone$ All | References$ Y | Description$ As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost. SVar:X:XChoice SVar:Y:SVar$X/Times.2 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSetLife | TriggerDescription$ When CARDNAME enters the battlefield, up to one target player’s life total becomes half their starting life total, rounded down. -SVar:TrigSetLife:DB$ SetLife | ValidTgts$ Player | LifeAmount$ HalfLife | TargetMin$ 0 | TargetMax$ 1 +SVar:TrigSetLife:DB$ SetLife | ValidTgts$ Player | LifeAmount$ HalfLife | TargetMin$ 0 | TargetMax$ 1 | References$ HalfLife SVar:HalfLife:TargetedPlayer$StartingLife/HalfDown -SVar:RemAIDeck:True Oracle:As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost.\nWhen Torgaar, Famine Incarnate enters the battlefield, up to one target player’s life total becomes half their starting life total, rounded down. \ No newline at end of file From 6c0112262e47764c2e839240a51933a03c45e87f Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 18 Apr 2018 09:45:00 +0300 Subject: [PATCH 2/4] - Somewhat better LifeSetAi logic for Torgaar which accounts for possible different starting life. --- .../main/java/forge/ai/ability/LifeSetAi.java | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java index 6f4a821870c..20a77ad08f9 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java @@ -126,11 +126,10 @@ public class LifeSetAi extends SpellAbilityAi { // special cases when amount can't be calculated without targeting first if (amount == 0 && "TargetedPlayer$StartingLife/HalfDown".equals(source.getSVar(amountStr))) { - // TODO: this assumes equal starting life for all players, which is not true e.g. for Archenemy - amount = ai.getStartingLife() / 2; + // e.g. Torgaar, Famine Incarnate + return doHalfStartingLifeLogic(ai, opponent, sa); } - if (sourceName.equals("Eternity Vessel") && (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) { return false; @@ -160,4 +159,36 @@ public class LifeSetAi extends SpellAbilityAi { return true; } + private boolean doHalfStartingLifeLogic(Player ai, Player opponent, SpellAbility sa) { + int aiAmount = ai.getStartingLife() / 2; + int oppAmount = opponent.getStartingLife() / 2; + int aiLife = ai.getLife(); + int oppLife = opponent.getLife(); + + sa.resetTargets(); + + final TargetRestrictions tgt = sa.getTargetRestrictions(); + if (tgt != null) { + if (tgt.canOnlyTgtOpponent()) { + if (oppLife > oppAmount) { + sa.getTargets().add(opponent); + } else { + return false; + } + } else { + if (aiAmount > ai.getLife() && aiLife <= 10) { + sa.getTargets().add(ai); + } else if (oppLife > oppAmount) { + sa.getTargets().add(opponent); + } else if (aiAmount > aiLife) { + sa.getTargets().add(ai); + } else { + return false; + } + } + } + + return true; + } + } From 694ac39e897eb05390d4b7305a763d8c5a2e9358 Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 18 Apr 2018 09:25:58 +0300 Subject: [PATCH 3/4] - Basic AI for Torgaar (currently won't sac anything as a part of cost payment) - Fixed references in the script for Torgaar. --- forge-ai/src/main/java/forge/ai/AiCostDecision.java | 9 +++++++++ forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java | 10 ++++++++-- .../res/cardsfolder/t/torgaar_famine_incarnate.txt | 3 +-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 2d93d12cf52..1d7179b017f 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -510,6 +510,15 @@ public class AiCostDecision extends CostDecisionMakerBase { Integer c = cost.convertAmount(); if (c == null) { if (ability.getSVar(cost.getAmount()).equals("XChoice")) { + if ("SacToReduceCost".equals(ability.getParam("AILogic"))) { + // e.g. Torgaar, Famine Incarnate + // TODO: currently returns an empty list, so the AI doesn't sacrifice anything. Trying to make + // the AI decide on creatures to sac makes the AI sacrifice them, but the cost is not reduced and the + // AI pays the full mana cost anyway (despite sacrificing creatures). + return PaymentDecision.card(new CardCollection()); + } + + // Other cards are assumed to be flagged RemAIDeck for now return null; } diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java index eb849eb92c9..6f4a821870c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java @@ -124,6 +124,13 @@ public class LifeSetAi extends SpellAbilityAi { amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa); } + // special cases when amount can't be calculated without targeting first + if (amount == 0 && "TargetedPlayer$StartingLife/HalfDown".equals(source.getSVar(amountStr))) { + // TODO: this assumes equal starting life for all players, which is not true e.g. for Archenemy + amount = ai.getStartingLife() / 2; + } + + if (sourceName.equals("Eternity Vessel") && (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) { return false; @@ -131,8 +138,7 @@ public class LifeSetAi extends SpellAbilityAi { // If the Target is gaining life, target self. // if the Target is modifying how much life is gained, this needs to - // be - // handled better + // be handled better final TargetRestrictions tgt = sa.getTargetRestrictions(); if (tgt != null) { sa.resetTargets(); diff --git a/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt b/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt index 1493069b325..78d418b99ee 100644 --- a/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt +++ b/forge-gui/res/cardsfolder/t/torgaar_famine_incarnate.txt @@ -2,12 +2,11 @@ Name:Torgaar, Famine Incarnate ManaCost:6 B B Types:Legendary Creature Avatar PT:7/6 -A:SP$ PermanentCreature | Cost$ 6 B B Sac | Announce$ X | References$ X,Y +A:SP$ PermanentCreature | Cost$ 6 B B Sac | Announce$ X | References$ X,Y | AILogic$ SacToReduceCost S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ Y | EffectZone$ All | References$ Y | Description$ As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost. SVar:X:XChoice SVar:Y:SVar$X/Times.2 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSetLife | TriggerDescription$ When CARDNAME enters the battlefield, up to one target player’s life total becomes half their starting life total, rounded down. SVar:TrigSetLife:DB$ SetLife | ValidTgts$ Player | LifeAmount$ HalfLife | TargetMin$ 0 | TargetMax$ 1 | References$ HalfLife SVar:HalfLife:TargetedPlayer$StartingLife/HalfDown -SVar:RemAIDeck:True Oracle:As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed as an additional cost.\nWhen Torgaar, Famine Incarnate enters the battlefield, up to one target player’s life total becomes half their starting life total, rounded down. \ No newline at end of file From 6a47d10d7bed479928d432f3942328212bcd6f8b Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 18 Apr 2018 09:45:00 +0300 Subject: [PATCH 4/4] - Somewhat better LifeSetAi logic for Torgaar which accounts for possible different starting life. --- .../main/java/forge/ai/ability/LifeSetAi.java | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java index 6f4a821870c..20a77ad08f9 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java @@ -126,11 +126,10 @@ public class LifeSetAi extends SpellAbilityAi { // special cases when amount can't be calculated without targeting first if (amount == 0 && "TargetedPlayer$StartingLife/HalfDown".equals(source.getSVar(amountStr))) { - // TODO: this assumes equal starting life for all players, which is not true e.g. for Archenemy - amount = ai.getStartingLife() / 2; + // e.g. Torgaar, Famine Incarnate + return doHalfStartingLifeLogic(ai, opponent, sa); } - if (sourceName.equals("Eternity Vessel") && (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) { return false; @@ -160,4 +159,36 @@ public class LifeSetAi extends SpellAbilityAi { return true; } + private boolean doHalfStartingLifeLogic(Player ai, Player opponent, SpellAbility sa) { + int aiAmount = ai.getStartingLife() / 2; + int oppAmount = opponent.getStartingLife() / 2; + int aiLife = ai.getLife(); + int oppLife = opponent.getLife(); + + sa.resetTargets(); + + final TargetRestrictions tgt = sa.getTargetRestrictions(); + if (tgt != null) { + if (tgt.canOnlyTgtOpponent()) { + if (oppLife > oppAmount) { + sa.getTargets().add(opponent); + } else { + return false; + } + } else { + if (aiAmount > ai.getLife() && aiLife <= 10) { + sa.getTargets().add(ai); + } else if (oppLife > oppAmount) { + sa.getTargets().add(opponent); + } else if (aiAmount > aiLife) { + sa.getTargets().add(ai); + } else { + return false; + } + } + } + + return true; + } + }