Untap: cant untap turns Effect

This commit is contained in:
Hanmac
2019-11-04 07:48:44 +01:00
parent 7ff7fa67d1
commit 5d639e7f1a
6 changed files with 178 additions and 26 deletions

View File

@@ -30,6 +30,7 @@ public enum ApiType {
Block (BlockEffect.class), Block (BlockEffect.class),
Bond (BondEffect.class), Bond (BondEffect.class),
Branch (BranchEffect.class), Branch (BranchEffect.class),
CantUntapTurn (CantUntapTurnEffect.class),
ChangeCombatants (ChangeCombatantsEffect.class), ChangeCombatants (ChangeCombatantsEffect.class),
ChangeTargets (ChangeTargetsEffect.class), ChangeTargets (ChangeTargetsEffect.class),
ChangeText (ChangeTextEffect.class), ChangeText (ChangeTextEffect.class),

View File

@@ -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<Card> cards = getTargetCards(sa);
for (final Card tgtC : cards) {
if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) {
continue;
}
tgtC.addCantUntapTurn(timestamp, n);
}
}
}

View File

@@ -3634,20 +3634,67 @@ public class Card extends GameEntity implements Comparable<Card> {
return !isExertedBy(p); return !isExertedBy(p);
} }
public boolean isCantUntap() {
return !cantUntap.isEmpty();
}
public final boolean addCantUntap(final long timestamp) { 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) { 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<Long> toRemove = Lists.newArrayList();
for (final Map.Entry<Long, Integer> 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) { public final void addCantUntapPlayer(final Player p, final long timestamp) {
cantUntapPlayer.put(timestamp, p); cantUntapPlayer.put(timestamp, p);
getView().updateCantUntap(this);
} }
public final void removeCantUntapPlayer(final long timestamp) { public final void removeCantUntapPlayer(final long timestamp) {
cantUntapPlayer.remove(timestamp); cantUntapPlayer.remove(timestamp);
getView().updateCantUntap(this);
}
public final Collection<Player> getCantUntapPlayer() {
return cantUntapPlayer.values();
} }
public final void untap() { public final void untap() {

View File

@@ -2,6 +2,8 @@ package forge.game.card;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ImageKeys; import forge.ImageKeys;
import forge.card.*; import forge.card.*;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
@@ -22,6 +24,7 @@ import forge.util.Lang;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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); sb.append("\r\n\r\n").append("(").append(taltname).append(") ").append(taltoracle);
} }
String nonAbilityText = get(TrackableProperty.NonAbilityText); String nonAbilityText = getNonAbilityText();
if (!nonAbilityText.isEmpty()) { if (!nonAbilityText.isEmpty()) {
sb.append("\r\n \r\nNon ability features: \r\n"); sb.append("\r\n \r\nNon ability features: \r\n");
sb.append(nonAbilityText.replaceAll("CARDNAME", getName())); sb.append(nonAbilityText.replaceAll("CARDNAME", getName()));
@@ -632,22 +635,6 @@ public class CardView extends GameEntityView {
sb.append("\r\n"); 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); String cloner = get(TrackableProperty.Cloner);
if (!cloner.isEmpty()) { if (!cloner.isEmpty()) {
sb.append("\r\nCloned by: ").append(cloner); sb.append("\r\nCloned by: ").append(cloner);
@@ -656,6 +643,57 @@ public class CardView extends GameEntityView {
return sb.toString().trim(); 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 doesnt untap during its controllers untap step.";
sb.append("\r\n");
sb.append(msg);
sb.append("\r\n");
} else {
int i = this.getCantUntapTurn();
if (i == 1) {
String msg = "CARDNAME doesnt untap during its controllers 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 doesnt untap during its controllers next " + str + ".");
sb.append("\r\n");
sb.append(msg);
sb.append("\r\n");
}
}
FCollectionView<PlayerView> untapList = getCantUntapPlayer();
if (untapList != null && !untapList.isEmpty()) {
String p = Lang.joinHomogenous(Lists.newArrayList(untapList), null, "or");
String msg = "CARDNAME doesnt untap during " + p + " untap step.";
sb.append("\r\n");
sb.append(msg);
sb.append("\r\n");
}
return sb.toString();
}
public CardStateView getCurrentState() { public CardStateView getCurrentState() {
return get(TrackableProperty.CurrentState); return get(TrackableProperty.CurrentState);
} }
@@ -767,6 +805,39 @@ public class CardView extends GameEntityView {
set(TrackableProperty.BlockAny, c.canBlockAny()); set(TrackableProperty.BlockAny, c.canBlockAny());
} }
boolean getCantUntap() {
return get(TrackableProperty.CantUntapAny);
}
int getCantUntapTurn() {
return get(TrackableProperty.CantUntapTurns);
}
FCollectionView<PlayerView> getCantUntapPlayer() {
return get(TrackableProperty.CantUntapPlayer);
}
void updateCantUntap(Card c) {
set(TrackableProperty.CantUntapAny, c.isCantUntap());
set(TrackableProperty.CantUntapTurns, c.getCantUntapTurnValue());
Collection<Player> list = c.getCantUntapPlayer();
if (list.isEmpty()) {
set(TrackableProperty.CantUntapPlayer, null);
} else {
TrackableCollection<PlayerView> 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 @Override
public String toString() { public String toString() {
String name = getName(); String name = getName();

View File

@@ -65,7 +65,7 @@ public class Untap extends Phase {
*/ */
@Override @Override
public void executeAt() { public void executeAt() {
this.execute(this.at); super.executeAt();
final Player turn = game.getPhaseHandler().getPlayerTurn(); final Player turn = game.getPhaseHandler().getPlayerTurn();
Untap.doPhasing(turn); Untap.doPhasing(turn);
@@ -84,8 +84,7 @@ public class Untap extends Phase {
*/ */
public static boolean canUntap(final Card c) { public static boolean canUntap(final Card c) {
if (c.hasKeyword("This card doesn't untap during your next untap step.") 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.")) {
return false; return false;
} }
@@ -199,11 +198,9 @@ public class Untap extends Phase {
// Remove temporary keywords // Remove temporary keywords
for (final Card c : player.getCardsIn(ZoneType.Battlefield)) { for (final Card c : player.getCardsIn(ZoneType.Battlefield)) {
c.removeCantUntapTurn();
c.removeHiddenExtrinsicKeyword("This card doesn't untap during your next untap step."); 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 // remove exerted flags from all things in play

View File

@@ -113,6 +113,9 @@ public enum TrackableProperty {
OpponentMayLook(TrackableTypes.BooleanType), OpponentMayLook(TrackableTypes.BooleanType),
BlockAdditional(TrackableTypes.IntegerType), BlockAdditional(TrackableTypes.IntegerType),
BlockAny(TrackableTypes.BooleanType), BlockAny(TrackableTypes.BooleanType),
CantUntapTurns(TrackableTypes.IntegerType),
CantUntapPlayer(TrackableTypes.PlayerViewCollectionType),
CantUntapAny(TrackableTypes.BooleanType),
AbilityText(TrackableTypes.StringType), AbilityText(TrackableTypes.StringType),
NonAbilityText(TrackableTypes.StringType), NonAbilityText(TrackableTypes.StringType),
FoilIndex(TrackableTypes.IntegerType), FoilIndex(TrackableTypes.IntegerType),