From 5d639e7f1ad6cd6b8bbce4bff9a1858e4f6265d2 Mon Sep 17 00:00:00 2001 From: Hanmac Date: Mon, 4 Nov 2019 07:48:44 +0100 Subject: [PATCH] Untap: cant untap turns Effect --- .../main/java/forge/game/ability/ApiType.java | 1 + .../ability/effects/CantUntapTurnEffect.java | 33 ++++++ .../src/main/java/forge/game/card/Card.java | 51 ++++++++- .../main/java/forge/game/card/CardView.java | 105 +++++++++++++++--- .../src/main/java/forge/game/phase/Untap.java | 11 +- .../forge/trackable/TrackableProperty.java | 3 + 6 files changed, 178 insertions(+), 26 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/ability/effects/CantUntapTurnEffect.java 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 e058a41c2c0..360838ac4b0 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -30,6 +30,7 @@ public enum ApiType { Block (BlockEffect.class), Bond (BondEffect.class), Branch (BranchEffect.class), + CantUntapTurn (CantUntapTurnEffect.class), ChangeCombatants (ChangeCombatantsEffect.class), ChangeTargets (ChangeTargetsEffect.class), ChangeText (ChangeTextEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/CantUntapTurnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CantUntapTurnEffect.java new file mode 100644 index 00000000000..95197cefad1 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/CantUntapTurnEffect.java @@ -0,0 +1,33 @@ +package forge.game.ability.effects; + +import java.util.List; + +import forge.game.Game; +import forge.game.ability.AbilityUtils; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +public class CantUntapTurnEffect extends SpellAbilityEffect { + + @Override + public void resolve(SpellAbility sa) { + final Card host = sa.getHostCard(); + final Game game = host.getGame(); + final long timestamp = game.getNextTimestamp(); + + final int n = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Turns", "1"), sa); + + List cards = getTargetCards(sa); + + for (final Card tgtC : cards) { + if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) { + continue; + } + + tgtC.addCantUntapTurn(timestamp, n); + } + + } + +} 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 33f51d07943..ff6578f8178 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -3634,20 +3634,67 @@ public class Card extends GameEntity implements Comparable { return !isExertedBy(p); } + public boolean isCantUntap() { + return !cantUntap.isEmpty(); + } + public final boolean addCantUntap(final long timestamp) { - return cantUntap.add(timestamp); + boolean result = cantUntap.add(timestamp); + getView().updateCantUntap(this); + return result; } public final boolean removeCantUntap(final long timestamp) { - return cantUntap.remove(timestamp); + boolean result = cantUntap.remove(timestamp); + getView().updateCantUntap(this); + return result; + } + + public final void addCantUntapTurn(final long timestamp, final int value) { + cantUntapTurns.put(timestamp, value); + getView().updateCantUntap(this); + } + + public final void removeCantUntapTurn(final long timestamp) { + cantUntapTurns.remove(timestamp); + getView().updateCantUntap(this); + } + + public final void removeCantUntapTurn() { + // reduce by one each turn + + List toRemove = Lists.newArrayList(); + for (final Map.Entry e : cantUntapTurns.entrySet()) { + e.setValue(e.getValue() - 1); + if (e.getValue() <= 0) { + toRemove.add(e.getKey()); + } + } + for (final long l : toRemove) { + cantUntapTurns.remove(l); + } + getView().updateCantUntap(this); + } + + public final int getCantUntapTurnValue() { + if (cantUntapTurns.isEmpty()) { + return 0; + } + return Collections.max(cantUntapTurns.values()); } public final void addCantUntapPlayer(final Player p, final long timestamp) { cantUntapPlayer.put(timestamp, p); + getView().updateCantUntap(this); } public final void removeCantUntapPlayer(final long timestamp) { cantUntapPlayer.remove(timestamp); + getView().updateCantUntap(this); + } + + public final Collection getCantUntapPlayer() { + return cantUntapPlayer.values(); } public final void untap() { 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 bc2c4c9ac2c..4464bc5011e 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -2,6 +2,8 @@ package forge.game.card; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + import forge.ImageKeys; import forge.card.*; import forge.card.mana.ManaCost; @@ -22,6 +24,7 @@ import forge.util.Lang; import forge.util.collect.FCollectionView; import org.apache.commons.lang3.StringUtils; +import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -604,7 +607,7 @@ public class CardView extends GameEntityView { sb.append("\r\n\r\n").append("(").append(taltname).append(") ").append(taltoracle); } - String nonAbilityText = get(TrackableProperty.NonAbilityText); + String nonAbilityText = getNonAbilityText(); if (!nonAbilityText.isEmpty()) { sb.append("\r\n \r\nNon ability features: \r\n"); sb.append(nonAbilityText.replaceAll("CARDNAME", getName())); @@ -632,22 +635,6 @@ public class CardView extends GameEntityView { sb.append("\r\n"); } - if (getCanBlockAny()) { - sb.append("\r\n\r\n"); - sb.append("CARDNAME can block any number of creatures.".replaceAll("CARDNAME", getName())); - sb.append("\r\n"); - } else { - int i = getBlockAdditional(); - if (i > 0) { - sb.append("\r\n\r\n"); - sb.append("CARDNAME can block an additional ".replaceAll("CARDNAME", getName())); - sb.append(i == 1 ? "creature" : Lang.nounWithNumeral(i, "creature")); - sb.append(" each combat."); - sb.append("\r\n"); - } - - } - String cloner = get(TrackableProperty.Cloner); if (!cloner.isEmpty()) { sb.append("\r\nCloned by: ").append(cloner); @@ -656,6 +643,57 @@ public class CardView extends GameEntityView { return sb.toString().trim(); } + public String getNonAbilityText() { + StringBuilder sb = new StringBuilder(get(TrackableProperty.NonAbilityText)); + + if (getCanBlockAny()) { + sb.append("\r\n"); + sb.append("CARDNAME can block any number of creatures."); + sb.append("\r\n"); + } else { + int i = getBlockAdditional(); + if (i > 0) { + sb.append("\r\n"); + sb.append("CARDNAME can block an additional "); + sb.append(i == 1 ? "creature" : Lang.nounWithNumeral(i, "creature")); + sb.append(" each combat."); + sb.append("\r\n"); + } + } + + if (getCantUntap()) { + String msg = "CARDNAME doesn’t untap during its controller’s untap step."; + sb.append("\r\n"); + sb.append(msg); + sb.append("\r\n"); + } else { + int i = this.getCantUntapTurn(); + if (i == 1) { + String msg = "CARDNAME doesn’t untap during its controller’s next untap step."; + sb.append("\r\n"); + sb.append(msg); + sb.append("\r\n"); + } else if (i > 1) { + String str = Lang.nounWithNumeral(i, "untap step"); + String msg = ("CARDNAME doesn’t untap during its controller’s next " + str + "."); + sb.append("\r\n"); + sb.append(msg); + sb.append("\r\n"); + } + } + + FCollectionView untapList = getCantUntapPlayer(); + if (untapList != null && !untapList.isEmpty()) { + String p = Lang.joinHomogenous(Lists.newArrayList(untapList), null, "or"); + String msg = "CARDNAME doesn’t untap during " + p + " untap step."; + sb.append("\r\n"); + sb.append(msg); + sb.append("\r\n"); + } + + return sb.toString(); + } + public CardStateView getCurrentState() { return get(TrackableProperty.CurrentState); } @@ -767,6 +805,39 @@ public class CardView extends GameEntityView { set(TrackableProperty.BlockAny, c.canBlockAny()); } + boolean getCantUntap() { + return get(TrackableProperty.CantUntapAny); + } + int getCantUntapTurn() { + return get(TrackableProperty.CantUntapTurns); + } + + FCollectionView getCantUntapPlayer() { + return get(TrackableProperty.CantUntapPlayer); + } + + void updateCantUntap(Card c) { + set(TrackableProperty.CantUntapAny, c.isCantUntap()); + set(TrackableProperty.CantUntapTurns, c.getCantUntapTurnValue()); + + Collection list = c.getCantUntapPlayer(); + if (list.isEmpty()) { + set(TrackableProperty.CantUntapPlayer, null); + } else { + TrackableCollection prop = get(TrackableProperty.CantUntapPlayer); + if (prop == null) { + prop = new TrackableCollection<>(); + } else { + prop.clear(); + } + for (Player p : list) { + prop.add(p.getView()); + } + + set(TrackableProperty.CantUntapPlayer, prop); + } + } + @Override public String toString() { String name = getName(); diff --git a/forge-game/src/main/java/forge/game/phase/Untap.java b/forge-game/src/main/java/forge/game/phase/Untap.java index 38553a6d391..f6cbfd1e089 100644 --- a/forge-game/src/main/java/forge/game/phase/Untap.java +++ b/forge-game/src/main/java/forge/game/phase/Untap.java @@ -65,7 +65,7 @@ public class Untap extends Phase { */ @Override public void executeAt() { - this.execute(this.at); + super.executeAt(); final Player turn = game.getPhaseHandler().getPlayerTurn(); Untap.doPhasing(turn); @@ -84,8 +84,7 @@ public class Untap extends Phase { */ public static boolean canUntap(final Card c) { - if (c.hasKeyword("This card doesn't untap during your next untap step.") - || c.hasKeyword("This card doesn't untap during your next two untap steps.")) { + if (c.hasKeyword("This card doesn't untap during your next untap step.")) { return false; } @@ -199,11 +198,9 @@ public class Untap extends Phase { // Remove temporary keywords for (final Card c : player.getCardsIn(ZoneType.Battlefield)) { + c.removeCantUntapTurn(); + c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next untap step."); - if (c.hasKeyword("This card doesn't untap during your next two untap steps.")) { - c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next two untap steps."); - c.addHiddenExtrinsicKeyword("This card doesn't untap during your next untap step."); - } } // remove exerted flags from all things in play diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index b19902edcaa..479d1fe001b 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -113,6 +113,9 @@ public enum TrackableProperty { OpponentMayLook(TrackableTypes.BooleanType), BlockAdditional(TrackableTypes.IntegerType), BlockAny(TrackableTypes.BooleanType), + CantUntapTurns(TrackableTypes.IntegerType), + CantUntapPlayer(TrackableTypes.PlayerViewCollectionType), + CantUntapAny(TrackableTypes.BooleanType), AbilityText(TrackableTypes.StringType), NonAbilityText(TrackableTypes.StringType), FoilIndex(TrackableTypes.IntegerType),