From a280a46ffd872d809002aaffc02c889dbadeb856 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Fri, 18 Mar 2022 10:43:06 +0000 Subject: [PATCH] Y22/J21: Implement Intensity mechanic --- .../src/main/java/forge/game/GameAction.java | 5 +++ .../java/forge/game/ability/AbilityUtils.java | 4 ++ .../main/java/forge/game/ability/ApiType.java | 1 + .../game/ability/effects/IntensifyEffect.java | 44 +++++++++++++++++++ .../ability/effects/RestartGameEffect.java | 3 ++ .../src/main/java/forge/game/card/Card.java | 21 +++++++++ .../main/java/forge/game/card/CardView.java | 11 +++++ .../main/java/forge/game/keyword/Keyword.java | 1 + .../forge/trackable/TrackableProperty.java | 1 + .../res/cardsfolder/s/static_discharge.txt | 8 ++++ .../upcoming/bellowsbreath_ogre.txt | 11 +++++ .../cardsfolder/upcoming/runaway_growth.txt | 11 +++++ .../java/forge/gui/card/CardDetailUtil.java | 8 ++++ 13 files changed, 129 insertions(+) create mode 100644 forge-game/src/main/java/forge/game/ability/effects/IntensifyEffect.java create mode 100644 forge-gui/res/cardsfolder/s/static_discharge.txt create mode 100644 forge-gui/res/cardsfolder/upcoming/bellowsbreath_ogre.txt create mode 100644 forge-gui/res/cardsfolder/upcoming/runaway_growth.txt diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 3b4a9d86998..47f0335df5b 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -565,6 +565,11 @@ public class GameAction { copied.clearEtbCounters(); } + // intensity is perpetual + if (c.hasIntensity()) { + copied.setIntensity(c.getIntensity(false)); + } + // update state for view copied.updateStateForView(); diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 68cd7ffde04..6a8bf99c4de 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -2066,6 +2066,10 @@ public class AbilityUtils { return doXMath(c.getDamageHistory().getCreatureAttacksThisTurn(), expr, c, ctb); } + if (sq[0].contains("Intensity")) { + return doXMath(c.getIntensity(true), expr, c, ctb); + } + if (sq[0].contains("CardCounters")) { // CardCounters.ALL to be used for Kinsbaile Borderguard and anything that cares about all counters int count = 0; diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index 0e2bb95c076..f900b4b4555 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -96,6 +96,7 @@ public enum ApiType { Goad (GoadEffect.class), Haunt (HauntEffect.class), Investigate (InvestigateEffect.class), + Intensify (IntensifyEffect.class), ImmediateTrigger (ImmediateTriggerEffect.class), Learn (LearnEffect.class), LookAt (LookAtEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/IntensifyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/IntensifyEffect.java new file mode 100644 index 00000000000..f1185faea78 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/IntensifyEffect.java @@ -0,0 +1,44 @@ +package forge.game.ability.effects; + +import forge.game.ability.AbilityUtils; +import forge.game.card.Card; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.CardCollectionView; +import forge.game.card.CardLists; +import forge.game.spellability.SpellAbility; +import forge.util.Lang; + +public class IntensifyEffect extends SpellAbilityEffect { + @Override + protected String getStackDescription(SpellAbility sa) { + final StringBuilder sb = new StringBuilder(); + String these = sa.hasParam("DefinedDesc") ? sa.getParam("DefinedDesc") : + Lang.joinHomogenous(getDefinedCardsOrTargeted(sa)); + final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), + sa.getParamOrDefault("Amount", "1"), sa); + + sb.append(sa.getActivatingPlayer()).append(" perpetually increases the intensity of ").append(these); + sb.append(" by ").append(amount).append("."); + + return sb.toString(); + } + + @Override + public void resolve(SpellAbility sa) { + final Card host = sa.getHostCard(); + final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), + sa.getParamOrDefault("Amount", "1"), sa); + + CardCollectionView toIntensify; + if (sa.hasParam("AllDefined")) { + toIntensify = CardLists.getValidCards(host.getGame().getCardsInGame(), sa.getParam("AllDefined"), + sa.getActivatingPlayer(), host, sa); + } else { + toIntensify = getDefinedCardsOrTargeted(sa); + } + + for (final Card tgtC : toIntensify) { + tgtC.addIntensity(amount); + } + } +} diff --git a/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java index 54d4cfdd914..e988f073e19 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java @@ -91,6 +91,9 @@ public class RestartGameEffect extends SpellAbilityEffect { p.getZone(ZoneType.Command).removeAllCards(true); for (Card c : newLibrary) { + if (c.getIntensity(false) > 0) { + c.setIntensity(0); + } action.moveToLibrary(c, 0, sa); } p.initVariantsZones(p.getRegisteredPlayer()); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f3c737daa23..8e30e5b32c5 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2090,6 +2090,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { || keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour")) { final String[] k = keyword.split(":"); sbLong.append(k[0]).append(" ").append(k[1]).append(" (").append(inst.getReminderText()).append(")"); + } else if (keyword.startsWith("Starting intensity")) { + sbLong.append(TextUtil.fastReplace(keyword, ":", " ")); } else if (keyword.contains("Haunt")) { sb.append("\r\nHaunt ("); if (isCreature()) { @@ -2541,6 +2543,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } else if (keyword.startsWith("Dredge")) { sbAfter.append(TextUtil.fastReplace(keyword, ":", " ")).append(" (").append(inst.getReminderText()).append(")"); sbAfter.append("\r\n"); + } else if (keyword.startsWith("Starting intensity")) { + sbAfter.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n"); } else if (keyword.startsWith("Escalate") || keyword.startsWith("Buyback") || keyword.startsWith("Prowl")) { final String[] k = keyword.split(":"); @@ -4081,6 +4085,23 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return getNetCombatDamageBreakdown().getTotal(); } + private int intensity = 0; + public final void addIntensity(final int n) { + intensity = intensity + n; + view.updateIntensity(this); + } + public final int getIntensity(boolean total) { + if (total && hasKeyword(Keyword.STARTING_INTENSITY)) { + return getKeywordMagnitude(Keyword.STARTING_INTENSITY) + intensity; + } else { + return intensity; + } + } + public final void setIntensity(final int n) { intensity = n; } + public final boolean hasIntensity() { + return intensity > 0; + } + private int multiKickerMagnitude = 0; public final void addMultiKickerMagnitude(final int n) { multiKickerMagnitude += n; } public final void setKickerMagnitude(final int n) { multiKickerMagnitude = n; } diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 86ae1bd1953..cd2bf8124e2 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -415,6 +415,13 @@ public class CardView extends GameEntityView { set(TrackableProperty.CurrentRoom, c.getCurrentRoom()); } + public int getIntensity() { + return get (TrackableProperty.Intensity); + } + void updateIntensity(Card c) { + set(TrackableProperty.Intensity, c.getIntensity(true)); + } + public boolean wasDestroyed() { if (get(TrackableProperty.WasDestroyed) == null) return false; @@ -867,6 +874,10 @@ public class CardView extends GameEntityView { updateZoneText(c); updateDamage(c); + if (c.getIntensity(false) > 0) { + updateIntensity(c); + } + if (getBackup() == null && !c.isFaceDown() && (c.hasBackSide()||c.isFlipCard()||c.isAdventureCard())) { set(TrackableProperty.PaperCardBackup, c.getPaperCard()); } diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index 4595f92ed42..9912796aac4 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -153,6 +153,7 @@ public enum Keyword { SPECTACLE("Spectacle", KeywordWithCost.class, false, "You may cast this spell for its spectacle cost rather than its mana cost if an opponent lost life this turn."), SPLICE("Splice", KeywordWithCostAndType.class, false, "As you cast an %2$s spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell."), SPLIT_SECOND("Split second", SimpleKeyword.class, true, "As long as this spell is on the stack, players can't cast other spells or activate abilities that aren't mana abilities."), + STARTING_INTENSITY("Starting intensity", KeywordWithAmount.class, true, null), STORM("Storm", SimpleKeyword.class, false, "When you cast this spell, copy it for each other spell that was cast before it this turn. You may choose new targets for the copies."), STRIVE("Strive", KeywordWithCost.class, false, "CARDNAME costs %s more to cast for each target beyond the first."), SUNBURST("Sunburst", SimpleKeyword.class, false, "This enters the battlefield with either a +1/+1 or charge counter on it for each color of mana spent to cast it based on whether it's a creature."), diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 4fad645e8b6..59d71f7a4b4 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -68,6 +68,7 @@ public enum TrackableProperty { ChosenMode(TrackableTypes.StringType), ClassLevel(TrackableTypes.IntegerType), CurrentRoom(TrackableTypes.StringType), + Intensity(TrackableTypes.IntegerType), Remembered(TrackableTypes.StringType), NamedCard(TrackableTypes.StringType), NamedCard2(TrackableTypes.StringType), diff --git a/forge-gui/res/cardsfolder/s/static_discharge.txt b/forge-gui/res/cardsfolder/s/static_discharge.txt new file mode 100644 index 00000000000..fde9f52396f --- /dev/null +++ b/forge-gui/res/cardsfolder/s/static_discharge.txt @@ -0,0 +1,8 @@ +Name:Static Discharge +ManaCost:1 R +Types:Sorcery +K:Starting intensity:3 +A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DBIntensify | SpellDescription$ CARDNAME deals damage equal to its intensity to any target, then perpetually increase the intensity of all cards you own named Static Discharge by 1. +SVar:DBIntensify:DB$ Intensify | AllDefined$ Card.YouOwn+namedStatic Discharge | DefinedDesc$ all cards they own named Static Discharge +SVar:X:Count$Intensity +Oracle:Starting intensity 3\nStatic Discharge deals damage equal to its intensity to any target, then perpetually increase the intensity of all cards you own named Static Discharge by 1. diff --git a/forge-gui/res/cardsfolder/upcoming/bellowsbreath_ogre.txt b/forge-gui/res/cardsfolder/upcoming/bellowsbreath_ogre.txt new file mode 100644 index 00000000000..466733fb188 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/bellowsbreath_ogre.txt @@ -0,0 +1,11 @@ +Name:Bellowsbreath Ogre +ManaCost:2 R +Types:Artifact Creature Ogre Shaman +PT:3/3 +K:Starting intensity:1 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerDescription$ Whenever CARDNAME attacks, it deals damage equal to its intensity to any target, then perpetually increase its intensity by 1. +SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DBIntensify +SVar:DBIntensify:DB$ Intensify +SVar:X:Count$Intensity +SVar:HasAttackEffect:TRUE +Oracle:Starting intensity 1\nWhenever Bellowsbreath Ogre attacks, it deals damage equal to its intensity to any target, then perpetually increase its intensity by 1. diff --git a/forge-gui/res/cardsfolder/upcoming/runaway_growth.txt b/forge-gui/res/cardsfolder/upcoming/runaway_growth.txt new file mode 100644 index 00000000000..9fa62daf0f3 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/runaway_growth.txt @@ -0,0 +1,11 @@ +Name:Runaway Growth +ManaCost:3 G +Types:Enchantment Aura +K:Starting intensity:1 +K:Enchant land +A:SP$ Attach | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Pump +T:Mode$ TapsForMana | ValidCard$ Card.EnchantedBy | TriggerZones$ Battlefield | Execute$ TrigMana | TriggerDescription$ Whenever enchanted land is tapped for mana, its controller adds an additional amount of {G} equal to CARDNAME's intensity, then perpetually increase its intensity by 1. +SVar:TrigMana:DB$ Mana | Produced$ G | Amount$ X | Defined$ TriggeredCardController | SubAbility$ DBIntensify +SVar:DBIntensify:DB$ Intensify +SVar:X:Count$Intensity +Oracle:Starting intensity 1\nEnchant land\nWhenever enchanted land is tapped for mana, its controller adds an additional amount of {G} equal to Runaway Growth's intensity, then perpetually increase its intensity by 1. diff --git a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java index f19ef89d0be..a129b11f179 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java @@ -349,6 +349,14 @@ public class CardDetailUtil { } } + final int intensity = card.getIntensity(); + if (intensity > 0) { + if (area.length() != 0) { + area.append("\n"); + } + area.append("Intensity: ").append(intensity); + } + // counter text if (card.getCounters() != null) { for (final Entry c : card.getCounters().entrySet()) {