mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Untap: cant untap turns Effect
This commit is contained in:
@@ -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),
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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 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<PlayerView> 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() {
|
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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user