mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-11 16:26:22 +00:00
- [XLN] Added Trove of Temptation. This card is ugly, so certain side effects in corner cases are probably still possible. Currently implemented as a keyword-like ability similar to "no more than X creatures can attack each turn" etc., but maybe is better as a global rule, I'm not sure (and not sure how to properly convert it to a global rule either...). Assistance and improvements are welcome.
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -17335,6 +17335,7 @@ forge-gui/res/cardsfolder/upcoming/tishana_voice_of_thunder.txt -text
|
|||||||
forge-gui/res/cardsfolder/upcoming/tishanas_wayfinder.txt -text
|
forge-gui/res/cardsfolder/upcoming/tishanas_wayfinder.txt -text
|
||||||
forge-gui/res/cardsfolder/upcoming/tocatli_honor_guard.txt -text
|
forge-gui/res/cardsfolder/upcoming/tocatli_honor_guard.txt -text
|
||||||
forge-gui/res/cardsfolder/upcoming/treasure_map_treasure_cove.txt -text
|
forge-gui/res/cardsfolder/upcoming/treasure_map_treasure_cove.txt -text
|
||||||
|
forge-gui/res/cardsfolder/upcoming/trove_of_temptation.txt -text
|
||||||
forge-gui/res/cardsfolder/upcoming/unclaimed_territory.txt -text
|
forge-gui/res/cardsfolder/upcoming/unclaimed_territory.txt -text
|
||||||
forge-gui/res/cardsfolder/upcoming/unfriendly_fire.txt -text
|
forge-gui/res/cardsfolder/upcoming/unfriendly_fire.txt -text
|
||||||
forge-gui/res/cardsfolder/upcoming/vanquishers_banner.txt -text
|
forge-gui/res/cardsfolder/upcoming/vanquishers_banner.txt -text
|
||||||
|
|||||||
@@ -1,41 +1,24 @@
|
|||||||
package forge.game.combat;
|
package forge.game.combat;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.*;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardCollection;
|
|
||||||
import forge.game.card.CardCollectionView;
|
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.card.CardPredicates;
|
|
||||||
import forge.game.card.CounterType;
|
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
import forge.util.maps.MapToAmountUtil;
|
|
||||||
import forge.util.maps.LinkedHashMapToAmount;
|
import forge.util.maps.LinkedHashMapToAmount;
|
||||||
import forge.util.maps.MapToAmount;
|
import forge.util.maps.MapToAmount;
|
||||||
|
import forge.util.maps.MapToAmountUtil;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
public class AttackConstraints {
|
public class AttackConstraints {
|
||||||
|
|
||||||
@@ -180,7 +163,9 @@ public class AttackConstraints {
|
|||||||
// Now try all others (plus empty attack) and count their violations
|
// Now try all others (plus empty attack) and count their violations
|
||||||
final FCollection<Map<Card, GameEntity>> legalAttackers = collectLegalAttackers(reqs, myMax);
|
final FCollection<Map<Card, GameEntity>> legalAttackers = collectLegalAttackers(reqs, myMax);
|
||||||
possible.putAll(Maps.asMap(legalAttackers.asSet(), FN_COUNT_VIOLATIONS));
|
possible.putAll(Maps.asMap(legalAttackers.asSet(), FN_COUNT_VIOLATIONS));
|
||||||
possible.put(Collections.<Card, GameEntity>emptyMap(), countViolations(Collections.<Card, GameEntity>emptyMap()));
|
if (countViolations(Collections.<Card, GameEntity>emptyMap()) != -1) {
|
||||||
|
possible.put(Collections.<Card, GameEntity>emptyMap(), countViolations(Collections.<Card, GameEntity>emptyMap()));
|
||||||
|
}
|
||||||
|
|
||||||
// take the case with the fewest violations
|
// take the case with the fewest violations
|
||||||
return MapToAmountUtil.min(possible);
|
return MapToAmountUtil.min(possible);
|
||||||
@@ -385,7 +370,7 @@ public class AttackConstraints {
|
|||||||
* restriction is violated.
|
* restriction is violated.
|
||||||
*/
|
*/
|
||||||
public final int countViolations(final Map<Card, GameEntity> attackers) {
|
public final int countViolations(final Map<Card, GameEntity> attackers) {
|
||||||
if (!globalRestrictions.isLegal(attackers)) {
|
if (!globalRestrictions.isLegal(attackers, possibleAttackers)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (final Entry<Card, GameEntity> attacker : attackers.entrySet()) {
|
for (final Entry<Card, GameEntity> attacker : attackers.entrySet()) {
|
||||||
|
|||||||
@@ -1,31 +1,37 @@
|
|||||||
package forge.game.combat;
|
package forge.game.combat;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardLists;
|
||||||
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
import forge.util.maps.MapToAmountUtil;
|
|
||||||
import forge.util.maps.LinkedHashMapToAmount;
|
import forge.util.maps.LinkedHashMapToAmount;
|
||||||
import forge.util.maps.MapToAmount;
|
import forge.util.maps.MapToAmount;
|
||||||
|
import forge.util.maps.MapToAmountUtil;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class AttackRequirement {
|
public class AttackRequirement {
|
||||||
|
|
||||||
private final MapToAmount<GameEntity> defenderSpecific;
|
private final MapToAmount<GameEntity> defenderSpecific;
|
||||||
|
private final MapToAmount<GameEntity> defenderOrPWSpecific;
|
||||||
|
private final Map<GameEntity, List<GameEntity>> defenderSpecificAlternatives;
|
||||||
private final MapToAmount<Card> causesToAttack;
|
private final MapToAmount<Card> causesToAttack;
|
||||||
|
|
||||||
public AttackRequirement(final Card attacker, final MapToAmount<Card> causesToAttack, final FCollectionView<GameEntity> possibleDefenders) {
|
public AttackRequirement(final Card attacker, final MapToAmount<Card> causesToAttack, final FCollectionView<GameEntity> possibleDefenders) {
|
||||||
this.defenderSpecific = new LinkedHashMapToAmount<GameEntity>();
|
this.defenderSpecific = new LinkedHashMapToAmount<GameEntity>();
|
||||||
|
this.defenderOrPWSpecific = new LinkedHashMapToAmount<GameEntity>();
|
||||||
|
this.defenderSpecificAlternatives = new HashMap<GameEntity, List<GameEntity>>();
|
||||||
|
|
||||||
this.causesToAttack = causesToAttack;
|
this.causesToAttack = causesToAttack;
|
||||||
|
|
||||||
final GameEntity mustAttack = attacker.getController().getMustAttackEntity();
|
final GameEntity mustAttack = attacker.getController().getMustAttackEntity();
|
||||||
@@ -64,19 +70,43 @@ public class AttackRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Game game = attacker.getGame();
|
final Game game = attacker.getGame();
|
||||||
|
|
||||||
|
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||||
|
if (c.hasKeyword("Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.")) {
|
||||||
|
if (attacker.getController().isOpponentOf(c.getController()) && !defenderOrPWSpecific.containsKey(c.getController())) {
|
||||||
|
defenderOrPWSpecific.put(c.getController(), 1);
|
||||||
|
for (Card pw : CardLists.filter(c.getController().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS)) {
|
||||||
|
// Add the attack alternatives that suffice (planeswalkers that can be attacked instead of the player)
|
||||||
|
if (!defenderSpecificAlternatives.containsKey(c.getController())) {
|
||||||
|
defenderSpecificAlternatives.put(c.getController(), Lists.<GameEntity>newArrayList());
|
||||||
|
}
|
||||||
|
defenderSpecificAlternatives.get(c.getController()).add(pw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final GameEntity defender : possibleDefenders) {
|
for (final GameEntity defender : possibleDefenders) {
|
||||||
if (CombatUtil.getAttackCost(game, attacker, defender) == null) {
|
if (CombatUtil.getAttackCost(game, attacker, defender) == null) {
|
||||||
// use put here because we want to always put it, even if the value is 0
|
// use put here because we want to always put it, even if the value is 0
|
||||||
defenderSpecific.put(defender, Integer.valueOf(defenderSpecific.count(defender) + nAttackAnything));
|
defenderSpecific.put(defender, Integer.valueOf(defenderSpecific.count(defender) + nAttackAnything));
|
||||||
|
if (defenderOrPWSpecific.containsKey(defender)) {
|
||||||
|
defenderOrPWSpecific.put(defender, Integer.valueOf(defenderOrPWSpecific.count(defender) + nAttackAnything));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defenderSpecific.remove(defender);
|
defenderSpecific.remove(defender);
|
||||||
|
defenderOrPWSpecific.remove(defender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove GameEntities that are no longer on the battlefield or are
|
// Remove GameEntities that are no longer on the battlefield or are
|
||||||
// related to Players who have lost the game
|
// related to Players who have lost the game
|
||||||
final List<GameEntity> toRemove = Lists.newArrayListWithCapacity(defenderSpecific.size());
|
final MapToAmount<GameEntity> combinedDefMap = new LinkedHashMapToAmount<>();
|
||||||
for (final GameEntity entity : defenderSpecific.keySet()) {
|
combinedDefMap.putAll(defenderSpecific);
|
||||||
|
combinedDefMap.putAll(defenderOrPWSpecific);
|
||||||
|
|
||||||
|
final List<GameEntity> toRemove = Lists.newArrayListWithCapacity(combinedDefMap.size());
|
||||||
|
for (final GameEntity entity : combinedDefMap.keySet()) {
|
||||||
boolean removeThis = false;
|
boolean removeThis = false;
|
||||||
if (entity instanceof Player) {
|
if (entity instanceof Player) {
|
||||||
if (((Player) entity).hasLost()) {
|
if (((Player) entity).hasLost()) {
|
||||||
@@ -94,11 +124,12 @@ public class AttackRequirement {
|
|||||||
}
|
}
|
||||||
for (final GameEntity entity : toRemove) {
|
for (final GameEntity entity : toRemove) {
|
||||||
defenderSpecific.remove(entity);
|
defenderSpecific.remove(entity);
|
||||||
|
defenderOrPWSpecific.remove(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRequirement() {
|
public boolean hasRequirement() {
|
||||||
return !defenderSpecific.isEmpty() || !causesToAttack.isEmpty();
|
return !defenderSpecific.isEmpty() || !causesToAttack.isEmpty() || !defenderOrPWSpecific.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final MapToAmount<Card> getCausesToAttack() {
|
public final MapToAmount<Card> getCausesToAttack() {
|
||||||
@@ -111,7 +142,36 @@ public class AttackRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final boolean isAttacking = defender != null;
|
final boolean isAttacking = defender != null;
|
||||||
int violations = defenderSpecific.countAll() - (isAttacking ? defenderSpecific.count(defender) : 0);
|
int violations = 0;
|
||||||
|
|
||||||
|
// first. check to see if "must attack X or Y with at least one creature" requirements are satisfied
|
||||||
|
List<GameEntity> toRemoveFromDefSpecific = Lists.<GameEntity>newArrayList();
|
||||||
|
if (!defenderOrPWSpecific.isEmpty()) {
|
||||||
|
for (GameEntity def : defenderOrPWSpecific.keySet()) {
|
||||||
|
if (defenderSpecificAlternatives.containsKey(def)) {
|
||||||
|
boolean isAttackingDefender = false;
|
||||||
|
outer: for (Card atk : attackers.keySet()) {
|
||||||
|
// is anyone attacking this defender or any of the alternative defenders?
|
||||||
|
if (attackers.get(atk).equals(def)) {
|
||||||
|
isAttackingDefender = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (GameEntity altDef : defenderSpecificAlternatives.get(def)) {
|
||||||
|
if (attackers.get(atk).equals(altDef)) {
|
||||||
|
isAttackingDefender = true;
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isAttackingDefender) {
|
||||||
|
violations++; // no one is attacking that defender or any of his PWs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now, count everything else
|
||||||
|
violations += defenderSpecific.countAll() - (isAttacking ? (defenderSpecific.count(defender)) : 0);
|
||||||
if (isAttacking) {
|
if (isAttacking) {
|
||||||
for (final Map.Entry<Card, Integer> mustAttack : causesToAttack.entrySet()) {
|
for (final Map.Entry<Card, Integer> mustAttack : causesToAttack.entrySet()) {
|
||||||
// only count violations if the forced creature can actually attack and has no cost incurred for doing so
|
// only count violations if the forced creature can actually attack and has no cost incurred for doing so
|
||||||
@@ -126,6 +186,7 @@ public class AttackRequirement {
|
|||||||
public List<Pair<GameEntity, Integer>> getSortedRequirements() {
|
public List<Pair<GameEntity, Integer>> getSortedRequirements() {
|
||||||
final List<Pair<GameEntity, Integer>> result = Lists.newArrayListWithExpectedSize(defenderSpecific.size());
|
final List<Pair<GameEntity, Integer>> result = Lists.newArrayListWithExpectedSize(defenderSpecific.size());
|
||||||
result.addAll(MapToAmountUtil.sort(defenderSpecific));
|
result.addAll(MapToAmountUtil.sort(defenderSpecific));
|
||||||
|
result.addAll(MapToAmountUtil.sort(defenderOrPWSpecific));
|
||||||
|
|
||||||
for (int i = 0; i < result.size(); i++) {
|
for (int i = 0; i < result.size(); i++) {
|
||||||
final Pair<GameEntity, Integer> def = result.get(i);
|
final Pair<GameEntity, Integer> def = result.get(i);
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
package forge.game.combat;
|
package forge.game.combat;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
import forge.util.maps.MapToAmountUtil;
|
|
||||||
import forge.util.maps.LinkedHashMapToAmount;
|
import forge.util.maps.LinkedHashMapToAmount;
|
||||||
import forge.util.maps.MapToAmount;
|
import forge.util.maps.MapToAmount;
|
||||||
|
import forge.util.maps.MapToAmountUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
public class GlobalAttackRestrictions {
|
public class GlobalAttackRestrictions {
|
||||||
|
|
||||||
private final int max;
|
private final int max;
|
||||||
private final MapToAmount<GameEntity> defenderMax;
|
private final MapToAmount<GameEntity> defenderMax;
|
||||||
private GlobalAttackRestrictions(final int max, final MapToAmount<GameEntity> defenderMax) {
|
private final PlayerCollection mustBeAttackedByEachOpp;
|
||||||
|
private GlobalAttackRestrictions(final int max, final MapToAmount<GameEntity> defenderMax, PlayerCollection mustBeAttackedByEachOpp) {
|
||||||
this.max = max;
|
this.max = max;
|
||||||
this.defenderMax = defenderMax;
|
this.defenderMax = defenderMax;
|
||||||
|
this.mustBeAttackedByEachOpp = mustBeAttackedByEachOpp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMax() {
|
public int getMax() {
|
||||||
@@ -32,17 +35,17 @@ public class GlobalAttackRestrictions {
|
|||||||
return defenderMax;
|
return defenderMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLegal(final Map<Card, GameEntity> attackers) {
|
public boolean isLegal(final Map<Card, GameEntity> attackers, final CardCollection possibleAttackers) {
|
||||||
return !getViolations(attackers, true).isViolated();
|
return !getViolations(attackers, possibleAttackers,true).isViolated();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalAttackRestrictionViolations getViolations(final Map<Card, GameEntity> attackers) {
|
public GlobalAttackRestrictionViolations getViolations(final Map<Card, GameEntity> attackers, final CardCollection possibleAttackers) {
|
||||||
return getViolations(attackers, false);
|
return getViolations(attackers, possibleAttackers,false);
|
||||||
}
|
}
|
||||||
private GlobalAttackRestrictionViolations getViolations(final Map<Card, GameEntity> attackers, final boolean returnQuickly) {
|
private GlobalAttackRestrictionViolations getViolations(final Map<Card, GameEntity> attackers, final CardCollection possibleAttackers, final boolean returnQuickly) {
|
||||||
final int nTooMany = max < 0 ? 0 : attackers.size() - max;
|
final int nTooMany = max < 0 ? 0 : attackers.size() - max;
|
||||||
if (returnQuickly && nTooMany > 0) {
|
if (returnQuickly && nTooMany > 0) {
|
||||||
return new GlobalAttackRestrictionViolations(nTooMany, MapToAmountUtil.<GameEntity>emptyMap());
|
return new GlobalAttackRestrictionViolations(nTooMany, MapToAmountUtil.<GameEntity>emptyMap(), MapToAmountUtil.<GameEntity>emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
final MapToAmount<GameEntity> defenderTooMany = new LinkedHashMapToAmount<GameEntity>(defenderMax.size());
|
final MapToAmount<GameEntity> defenderTooMany = new LinkedHashMapToAmount<GameEntity>(defenderMax.size());
|
||||||
@@ -73,18 +76,49 @@ public class GlobalAttackRestrictions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GlobalAttackRestrictionViolations(nTooMany, defenderTooMany);
|
final MapToAmount<GameEntity> defenderTooFew = new LinkedHashMapToAmount<GameEntity>(defenderMax.size());
|
||||||
|
for (final GameEntity mandatoryDef : mustBeAttackedByEachOpp) {
|
||||||
|
// check to ensure that this defender can even legally be attacked in the first place
|
||||||
|
boolean canAttackThisDef = false;
|
||||||
|
for (Card c : possibleAttackers) {
|
||||||
|
if (CombatUtil.canAttack(c, mandatoryDef) && null == CombatUtil.getAttackCost(c.getGame(), c, mandatoryDef)) {
|
||||||
|
canAttackThisDef = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!canAttackThisDef) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAttacked = false;
|
||||||
|
for (final GameEntity defender : attackers.values()) {
|
||||||
|
if (defender.equals(mandatoryDef)) {
|
||||||
|
isAttacked = true;
|
||||||
|
break;
|
||||||
|
} else if (defender instanceof Card && ((Card)defender).getController().equals(mandatoryDef)) {
|
||||||
|
isAttacked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isAttacked) {
|
||||||
|
defenderTooFew.add(mandatoryDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GlobalAttackRestrictionViolations(nTooMany, defenderTooMany, defenderTooFew);
|
||||||
}
|
}
|
||||||
|
|
||||||
final class GlobalAttackRestrictionViolations {
|
final class GlobalAttackRestrictionViolations {
|
||||||
private final boolean isViolated;
|
private final boolean isViolated;
|
||||||
private final int globalTooMany;
|
private final int globalTooMany;
|
||||||
private final MapToAmount<GameEntity> defenderTooMany;
|
private final MapToAmount<GameEntity> defenderTooMany;
|
||||||
|
private final MapToAmount<GameEntity> defenderTooFew;
|
||||||
|
|
||||||
public GlobalAttackRestrictionViolations(final int globalTooMany, final MapToAmount<GameEntity> defenderTooMany) {
|
public GlobalAttackRestrictionViolations(final int globalTooMany, final MapToAmount<GameEntity> defenderTooMany, final MapToAmount<GameEntity> defenderTooFew) {
|
||||||
this.isViolated = globalTooMany > 0 || !defenderTooMany.isEmpty();
|
this.isViolated = globalTooMany > 0 || !defenderTooMany.isEmpty() || !defenderTooFew.isEmpty();
|
||||||
this.globalTooMany = globalTooMany;
|
this.globalTooMany = globalTooMany;
|
||||||
this.defenderTooMany = defenderTooMany;
|
this.defenderTooMany = defenderTooMany;
|
||||||
|
this.defenderTooFew = defenderTooFew;
|
||||||
}
|
}
|
||||||
public boolean isViolated() {
|
public boolean isViolated() {
|
||||||
return isViolated;
|
return isViolated;
|
||||||
@@ -92,6 +126,9 @@ public class GlobalAttackRestrictions {
|
|||||||
public int getGlobalTooMany() {
|
public int getGlobalTooMany() {
|
||||||
return globalTooMany;
|
return globalTooMany;
|
||||||
}
|
}
|
||||||
|
public MapToAmount<GameEntity> getDefenderTooFew() {
|
||||||
|
return defenderTooFew;
|
||||||
|
}
|
||||||
public MapToAmount<GameEntity> getDefenderTooMany() {
|
public MapToAmount<GameEntity> getDefenderTooMany() {
|
||||||
return defenderTooMany;
|
return defenderTooMany;
|
||||||
}
|
}
|
||||||
@@ -102,13 +139,14 @@ public class GlobalAttackRestrictions {
|
|||||||
* Get all global restrictions (applying to all creatures).
|
* Get all global restrictions (applying to all creatures).
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param player
|
* @param attackingPlayer
|
||||||
* the {@link Player} declaring attack.
|
* the {@link Player} declaring attack.
|
||||||
* @return a {@link GlobalAttackRestrictions} object.
|
* @return a {@link GlobalAttackRestrictions} object.
|
||||||
*/
|
*/
|
||||||
public static GlobalAttackRestrictions getGlobalRestrictions(final Player attackingPlayer, final FCollectionView<GameEntity> possibleDefenders) {
|
public static GlobalAttackRestrictions getGlobalRestrictions(final Player attackingPlayer, final FCollectionView<GameEntity> possibleDefenders) {
|
||||||
int max = -1;
|
int max = -1;
|
||||||
final MapToAmount<GameEntity> defenderMax = new LinkedHashMapToAmount<GameEntity>(possibleDefenders.size());
|
final MapToAmount<GameEntity> defenderMax = new LinkedHashMapToAmount<GameEntity>(possibleDefenders.size());
|
||||||
|
final PlayerCollection mustBeAttacked = new PlayerCollection();
|
||||||
final Game game = attackingPlayer.getGame();
|
final Game game = attackingPlayer.getGame();
|
||||||
|
|
||||||
if (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.onlyOneAttackerATurn)) {
|
if (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.onlyOneAttackerATurn)) {
|
||||||
@@ -132,6 +170,14 @@ public class GlobalAttackRestrictions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
||||||
|
if (card.hasKeyword("Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.")) {
|
||||||
|
if (attackingPlayer.isOpponentOf(card.getController())) {
|
||||||
|
mustBeAttacked.add(card.getController());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final GameEntity defender : possibleDefenders) {
|
for (final GameEntity defender : possibleDefenders) {
|
||||||
final int defMax = getMaxAttackTo(defender);
|
final int defMax = getMaxAttackTo(defender);
|
||||||
if (defMax != -1) {
|
if (defMax != -1) {
|
||||||
@@ -142,7 +188,8 @@ public class GlobalAttackRestrictions {
|
|||||||
// maximum on each defender, global maximum is sum of these
|
// maximum on each defender, global maximum is sum of these
|
||||||
max = Ints.min(max, defenderMax.countAll());
|
max = Ints.min(max, defenderMax.countAll());
|
||||||
}
|
}
|
||||||
return new GlobalAttackRestrictions(max, defenderMax);
|
|
||||||
|
return new GlobalAttackRestrictions(max, defenderMax, mustBeAttacked);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
11
forge-gui/res/cardsfolder/upcoming/trove_of_temptation.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/trove_of_temptation.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Name:Trove of Temptation
|
||||||
|
ManaCost:3 R
|
||||||
|
Types:Enchantment
|
||||||
|
K:Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.
|
||||||
|
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ DBTreasureToken | TriggerDescription$ At the beginning of your end step, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool.
|
||||||
|
# TODO: How many Treasure tokens with different art are there?
|
||||||
|
SVar:DBTreasureToken:DB$ Token | TokenAmount$ 1 | TokenName$ Treasure | TokenTypes$ Artifact,Treasure | TokenOwner$ You | TokenColors$ Colorless | TokenImage$ c treasure | TokenAbilities$ ABTreasureMana | TokenAltImages$ c_treasure2,c_treasure3
|
||||||
|
SVar:ABTreasureMana:AB$ Mana | Cost$ T Sac<1/CARDNAME> | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool.
|
||||||
|
DeckHas:Ability$Token
|
||||||
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/trove_of_temptation.jpg
|
||||||
|
Oracle:Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.\nAt the beginning of your end step, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color to your mana pool.
|
||||||
Reference in New Issue
Block a user