Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ public Pair<Map<Card, GameEntity>, Integer> getLegalAttackers() {
) || (
types.contains(AttackRestrictionType.NOT_ALONE) && myMax <= 1
) || (
types.contains(AttackRestrictionType.NEED_BLACK_OR_GREEN) && myMax <= 1
) || (
types.contains(AttackRestrictionType.NEED_GREATER_POWER) && myMax <= 1
)) {
reqs.removeIf(findAll(attacker));
Expand All @@ -106,11 +104,7 @@ public Pair<Map<Card, GameEntity>, Integer> getLegalAttackers() {
// Next, remove creatures with constraints that can't be fulfilled.
for (final Card attacker : myPossibleAttackers) {
final Set<AttackRestrictionType> types = restrictions.get(attacker).getTypes();
if (types.contains(AttackRestrictionType.NEED_BLACK_OR_GREEN)) {
if (!myPossibleAttackers.anyMatch(AttackRestrictionType.NEED_BLACK_OR_GREEN.getPredicate(attacker))) {
attackersToRemove.add(attacker);
}
} else if (types.contains(AttackRestrictionType.NEED_GREATER_POWER)) {
if (types.contains(AttackRestrictionType.NEED_GREATER_POWER)) {
if (!myPossibleAttackers.anyMatch(AttackRestrictionType.NEED_GREATER_POWER.getPredicate(attacker))) {
attackersToRemove.add(attacker);
}
Expand Down
20 changes: 10 additions & 10 deletions forge-game/src/main/java/forge/game/combat/AttackRestriction.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityAttackBlockRestrict;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;

Expand All @@ -28,7 +31,6 @@ public AttackRestriction(final Card attacker, final FCollectionView<GameEntity>

if ((restrictions.contains(AttackRestrictionType.ONLY_ALONE) && (
restrictions.contains(AttackRestrictionType.NEED_GREATER_POWER) ||
restrictions.contains(AttackRestrictionType.NEED_BLACK_OR_GREEN) ||
restrictions.contains(AttackRestrictionType.NOT_ALONE) ||
restrictions.contains(AttackRestrictionType.NEED_TWO_OTHERS))
) || (
Expand All @@ -53,10 +55,6 @@ public Set<AttackRestrictionType> getViolation(final Map<Card, GameEntity> attac
&& attackers.keySet().stream().noneMatch(AttackRestrictionType.NEED_GREATER_POWER.getPredicate(attacker))) {
violations.add(AttackRestrictionType.NEED_GREATER_POWER);
}
if (restrictions.contains(AttackRestrictionType.NEED_BLACK_OR_GREEN)
&& attackers.keySet().stream().noneMatch(AttackRestrictionType.NEED_BLACK_OR_GREEN.getPredicate(attacker))) {
violations.add(AttackRestrictionType.NEED_BLACK_OR_GREEN);
}
if (restrictions.contains(AttackRestrictionType.NOT_ALONE) && nAttackers <= 1) {
violations.add(AttackRestrictionType.NOT_ALONE);
}
Expand All @@ -66,12 +64,18 @@ public Set<AttackRestrictionType> getViolation(final Map<Card, GameEntity> attac
return violations;
}

public List<StaticAbility> getStaticViolations(final Map<Card, GameEntity> attackers) {
CardCollection others = new CardCollection(attackers.keySet());
others.remove(attacker);
return StaticAbilityAttackBlockRestrict.attackRestrict(attacker, others);
}

public boolean canAttack(final GameEntity defender, final Map<Card, GameEntity> attackers) {
if (!canAttack(defender)) {
return false;
}

return getViolation(attackers).isEmpty();
return getViolation(attackers).isEmpty() && getStaticViolations(attackers).isEmpty();
}

public Set<AttackRestrictionType> getTypes() {
Expand All @@ -87,10 +91,6 @@ private void setRestrictions() {
restrictions.add(AttackRestrictionType.NEED_GREATER_POWER);
}

if (attacker.hasKeyword("CARDNAME can't attack unless a black or green creature also attacks.")) {
restrictions.add(AttackRestrictionType.NEED_BLACK_OR_GREEN);
}

if (attacker.hasKeyword("CARDNAME can't attack or block alone.") || attacker.hasKeyword("CARDNAME can't attack alone.")) {
restrictions.add(AttackRestrictionType.NOT_ALONE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package forge.game.combat;

import forge.card.MagicColor;
import forge.game.card.Card;
import forge.game.card.CardPredicates;

Expand All @@ -10,7 +9,6 @@ public enum AttackRestrictionType {

ONLY_ALONE,
NEED_GREATER_POWER,
NEED_BLACK_OR_GREEN,
NOT_ALONE,
NEED_TWO_OTHERS,
NEVER;
Expand All @@ -19,10 +17,6 @@ public Predicate<Card> getPredicate(final Card attacker) {
switch (this) {
case NEED_GREATER_POWER:
return CardPredicates.hasGreaterPowerThan(attacker.getNetPower());
case NEED_BLACK_OR_GREEN:
return CardPredicates.isColor((byte) (MagicColor.BLACK | MagicColor.GREEN))
// may explicitly not be black/green itself
.and(Predicate.not(attacker::equals));
case NOT_ALONE:
return x -> true;
default:
Expand Down
4 changes: 2 additions & 2 deletions forge-game/src/main/java/forge/game/combat/CombatUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityBlockRestrict;
import forge.game.staticability.StaticAbilityAttackBlockRestrict;
import forge.game.staticability.StaticAbilityCantAttackBlock;
import forge.game.staticability.StaticAbilityMustBlock;
import forge.game.trigger.TriggerType;
Expand Down Expand Up @@ -433,7 +433,7 @@ public static boolean canBlock(final Card blocker, final Combat combat) {
CardCollection allOtherBlockers = combat.getAllBlockers();
allOtherBlockers.remove(blocker);
final int blockersFromOnePlayer = CardLists.count(allOtherBlockers, CardPredicates.isController(blocker.getController()));
if (blockersFromOnePlayer >= StaticAbilityBlockRestrict.blockRestrictNum(blocker.getController())) {
if (blockersFromOnePlayer >= StaticAbilityAttackBlockRestrict.blockRestrictNum(blocker.getController())) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.staticability.StaticAbilityAttackRestrict;
import forge.game.staticability.StaticAbilityAttackBlockRestrict;
import forge.util.collect.FCollectionView;

public class GlobalAttackRestrictions {
Expand Down Expand Up @@ -59,10 +59,10 @@ public static GlobalAttackRestrictions getGlobalRestrictions(final Player attack
final Map<GameEntity, Integer> defenderMax = Maps.newHashMapWithExpectedSize(possibleDefenders.size());
final Game game = attackingPlayer.getGame();

Integer max = StaticAbilityAttackRestrict.globalAttackRestrict(game);
Integer max = StaticAbilityAttackBlockRestrict.globalAttackRestrictNum(game);

for (final GameEntity defender : possibleDefenders) {
final Integer defMax = StaticAbilityAttackRestrict.attackRestrictNum(defender);
final Integer defMax = StaticAbilityAttackBlockRestrict.attackRestrictNum(defender);
if (defMax != null) {
defenderMax.put(defender, defMax);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package forge.game.staticability;

import java.util.Collection;
import java.util.List;

import com.google.common.collect.Lists;

import forge.game.Game;
import forge.game.GameEntity;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardPredicates;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.Expressions;

public class StaticAbilityAttackBlockRestrict {

static public Integer globalAttackRestrictNum(Game game) {
Integer max = null;
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.checkConditions(StaticAbilityMode.AttackRestrictNum)
|| stAb.hasParam("ValidDefender")) {
continue;
}
int stMax = AbilityUtils.calculateAmount(stAb.getHostCard(),
stAb.getParamOrDefault("MaxAttackers", "1"), stAb);
if (null == max || stMax < max) {
max = stMax;
}
}
}
return max;
}

static public Integer attackRestrictNum(GameEntity defender) {
final Game game = defender.getGame();
Integer num = null;
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.checkConditions(StaticAbilityMode.AttackRestrictNum)
|| !stAb.hasParam("ValidDefender")) {
continue;
}
if (validRestrictNum(stAb, defender)) {
int stNum = AbilityUtils.calculateAmount(stAb.getHostCard(),
stAb.getParamOrDefault("MaxAttackers", "1"), stAb);
if (null == num || stNum < num) {
num = stNum;
}
}
}
}
return num;
}

static public int blockRestrictNum(Player defender) {
final Game game = defender.getGame();
int num = Integer.MAX_VALUE;
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.checkConditions(StaticAbilityMode.BlockRestrictNum)) {
continue;
}
if (validRestrictNum(stAb, defender)) {
int stNum = AbilityUtils.calculateAmount(stAb.getHostCard(),
stAb.getParamOrDefault("MaxBlockers", "1"), stAb);
if (stNum < num) {
num = stNum;
}
}

}
}
return num;
}

static public boolean validRestrictNum(StaticAbility stAb, GameEntity defender) {
if (!stAb.matchesValidParam("ValidDefender", defender)) {
return false;
}
return true;
}

static public List<StaticAbility> attackRestrict(final Card attacker, final Collection<Card> others) {
final Game game = attacker.getGame();
List<StaticAbility> result = Lists.newArrayList();
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final StaticAbility stAb : ca.getStaticAbilities()) {
if (!stAb.checkConditions(StaticAbilityMode.AttackRestrict)) {
continue;
}
if (!stAb.matchesValidParam("ValidCard", attacker)) {
continue;
}
if (validRestrictCommon(stAb, attacker, others)) {
result.add(stAb);
}
}
}
return result;
}

static public boolean validRestrictCommon(StaticAbility stAb, Card card, Collection<Card> others) {
long size;
if (stAb.hasParam("ValidOthers")) {
size = others.stream().filter(CardPredicates.restriction(stAb.getParam("ValidOthers").split(","), stAb.getHostCard().getController(), stAb.getHostCard(), stAb)).count();
} else if (stAb.hasParam("ValidOthersRelative")) {
size = others.stream().filter(CardPredicates.restriction(stAb.getParam("ValidOthers").split(","), card.getController(), card, stAb)).count();
} else {
size = others.size();
}
String compare = stAb.getParamOrDefault("OthersCompare", "GE1");

String operator = compare.substring(0, 2);
String operand = compare.substring(2);

final int operandValue = AbilityUtils.calculateAmount(card, operand, stAb);
return !Expressions.compare((int)size, operator, operandValue);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ public enum StaticAbilityMode {
// StaticAbilityNoCleanupDamage
NoCleanupDamage,

// StaticAbilityBlockRestrict
BlockRestrict,

// StaticAbilityCantGainLosePayLife
CantGainLife,
CantLoseLife,
Expand All @@ -77,8 +74,10 @@ public enum StaticAbilityMode {
IgnoreHexproof,
IgnoreShroud,

// StaticAbilityAttackRestrict
// StaticAbilityAttackBlockRestrict
AttackRestrict,
AttackRestrictNum,
BlockRestrictNum,

// StaticAbilityAssignNoCombatDamage
AssignNoCombatDamage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PT:1/4
K:Defender
R:Event$ GameLoss | ActiveZones$ Battlefield | ValidPlayer$ You | Layer$ CantHappen | Description$ You can't lose the game and your opponents can't win the game.
R:Event$ GameWin | ActiveZones$ Battlefield | ValidPlayer$ Opponent | Layer$ CantHappen | Secondary$ True | Description$ You can't lose the game and your opponents can't win the game.
S:Mode$ AttackRestrict | MaxAttackers$ 6 | ValidDefender$ You | Description$ No more than six creatures can attack you each combat.
S:Mode$ AttackRestrictNum | MaxAttackers$ 6 | ValidDefender$ You | Description$ No more than six creatures can attack you each combat.
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | ValidCard$ Card.Self | ReplaceWith$ Exile | Description$ If CARDNAME would leave the battlefield, instead exile it with three time counters on it. It gains suspend.
SVar:Exile:DB$ ChangeZone | Hidden$ True | WithCountersType$ TIME | WithCountersAmount$ 3 | Origin$ All | Destination$ Exile | Defined$ ReplacedCard | SubAbility$ GiveSuspend
SVar:GiveSuspend:DB$ PumpAll | ValidCards$ Card.withoutSuspend+YouOwn | KW$ Suspend | PumpZone$ Exile | Duration$ Permanent
Expand Down
Loading
Loading