Compare commits

..

1 Commits

Author SHA1 Message Date
Hans Mackowiak
0f3a9cd22e AbilityUtils: add Precalc Option 2025-08-23 16:28:57 +02:00
299 changed files with 4021 additions and 5356 deletions

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: ['17', '21']
java: [ '17' ]
name: Test with Java ${{ matrix.Java }}
steps:
- uses: actions/checkout@v3

3
.gitignore vendored
View File

@@ -66,9 +66,6 @@ forge-gui-mobile-dev/testAssets
forge-gui/res/cardsfolder/*.bat
# Generated changelog file
forge-gui/release-files/CHANGES.txt
forge-gui/res/PerSetTrackingResults
forge-gui/res/decks
forge-gui/res/layouts

View File

@@ -0,0 +1,33 @@
Summary
(Summarize the bug encountered concisely)
Steps to reproduce
(How one can reproduce the issue - this is very important. Specific cards and specific actions especially)
Which version of Forge are you on (Release, Snapshot? Desktop, Android?)
What is the current bug behavior?
(What actually happens)
What is the expected correct behavior?
(What you should see instead)
Relevant logs and/or screenshots
(Paste/Attach your game.log from the crash - please use code blocks (```)) Also, provide screenshots of the current state.
Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
/label ~needs-investigation

View File

@@ -0,0 +1,15 @@
Summary
(Summarize the feature you wish concisely)
Example screenshots
(If this is a UI change, please provide an example screenshot of how this feature might work)
Feature type
(Where in Forge does this belong? e.g. Quest Mode, Deck Editor, Limited, Constructed, etc.)
/label ~feature request

View File

@@ -563,7 +563,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int thisRemove = Math.min(prefCard.getCounters(cType), stillToRemove);
if (thisRemove > 0) {
removed += thisRemove;
table.put(null, prefCard, cType, thisRemove);
table.put(null, prefCard, CounterType.get(cType), thisRemove);
}
}
}
@@ -573,7 +573,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public PaymentDecision visit(CostRemoveAnyCounter cost) {
final int c = cost.getAbilityAmount(ability);
final Card originalHost = ObjectUtils.getIfNull(ability.getOriginalHost(), source);
final Card originalHost = ObjectUtils.defaultIfNull(ability.getOriginalHost(), source);
if (c <= 0) {
return null;
@@ -716,7 +716,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int over = Math.min(crd.getCounters(CounterEnumType.QUEST) - e, c - toRemove);
if (over > 0) {
toRemove += over;
table.put(null, crd, CounterEnumType.QUEST, over);
table.put(null, crd, CounterType.get(CounterEnumType.QUEST), over);
}
}
}

View File

@@ -767,7 +767,7 @@ public class ComputerUtil {
public static CardCollection chooseUntapType(final Player ai, final String type, final Card activate, final boolean untap, final int amount, SpellAbility sa) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, sa);
typeList = CardLists.filter(typeList, CardPredicates.TAPPED, c -> c.getCounters(CounterEnumType.STUN) == 0 || c.canRemoveCounters(CounterEnumType.STUN));
typeList = CardLists.filter(typeList, CardPredicates.TAPPED, c -> c.getCounters(CounterEnumType.STUN) == 0 || c.canRemoveCounters(CounterType.get(CounterEnumType.STUN)));
if (untap) {
typeList.remove(activate);
@@ -2542,7 +2542,7 @@ public class ComputerUtil {
boolean opponent = controller.isOpponentOf(ai);
final CounterType p1p1Type = CounterEnumType.P1P1;
final CounterType p1p1Type = CounterType.get(CounterEnumType.P1P1);
if (!sa.hasParam("AILogic")) {
return Aggregates.random(options);

View File

@@ -171,7 +171,7 @@ public class SpecialAiLogic {
final boolean isInfect = source.hasKeyword(Keyword.INFECT); // Flesh-Eater Imp
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterEnumType.POISON)) {
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
}
@@ -277,7 +277,7 @@ public class SpecialAiLogic {
final boolean isInfect = source.hasKeyword(Keyword.INFECT);
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterEnumType.POISON)) {
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
}

View File

@@ -102,7 +102,7 @@ public abstract class CountersAi extends SpellAbilityAi {
} else if (type.equals("DIVINITY")) {
final CardCollection boon = CardLists.filter(list, c -> c.getCounters(CounterEnumType.DIVINITY) == 0);
choice = ComputerUtilCard.getMostExpensivePermanentAI(boon);
} else if (CounterType.getType(type).isKeywordCounter()) {
} else if (CounterType.get(type).isKeywordCounter()) {
choice = ComputerUtilCard.getBestCreatureAI(CardLists.getNotKeyword(list, type));
} else {
// The AI really should put counters on cards that can use it.

View File

@@ -154,7 +154,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
}
if (counterType == null || counterType.is(type)) {
addTargetsByCounterType(ai, sa, aiList, type);
addTargetsByCounterType(ai, sa, aiList, CounterType.get(type));
}
}
}
@@ -163,7 +163,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (!oppList.isEmpty()) {
// not enough targets
if (sa.canAddMoreTarget()) {
final CounterType type = CounterEnumType.M1M1;
final CounterType type = CounterType.get(CounterEnumType.M1M1);
if (counterType == null || counterType == type) {
addTargetsByCounterType(ai, sa, oppList, type);
}

View File

@@ -110,7 +110,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Proliferate is always optional for all, no need to select best
final CounterType poison = CounterEnumType.POISON;
final CounterType poison = CounterType.get(CounterEnumType.POISON);
boolean aggroAI = (((PlayerControllerAi) ai.getController()).getAi()).getBooleanProperty(AiProps.PLAY_AGGRO);
// because countertype can't be chosen anymore, only look for poison counters

View File

@@ -170,7 +170,7 @@ public class CountersPutAi extends CountersAi {
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1));
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
oppCreatM1 = CardLists.filter(oppCreatM1, input -> input.getNetToughness() <= 1 && input.canReceiveCounters(CounterEnumType.M1M1));
oppCreatM1 = CardLists.filter(oppCreatM1, input -> input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.get(CounterEnumType.M1M1)));
Card best = ComputerUtilCard.getBestAI(oppCreatM1);
if (best != null) {
@@ -336,7 +336,7 @@ public class CountersPutAi extends CountersAi {
Game game = ai.getGame();
Combat combat = game.getCombat();
if (!source.canReceiveCounters(CounterEnumType.P1P1) || source.getCounters(CounterEnumType.P1P1) > 0) {
if (!source.canReceiveCounters(CounterType.get(CounterEnumType.P1P1)) || source.getCounters(CounterEnumType.P1P1) > 0) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
} else if (combat != null && ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
return doCombatAdaptLogic(source, amount, combat);
@@ -608,7 +608,7 @@ public class CountersPutAi extends CountersAi {
return new AiAbilityDecision(0, AiPlayDecision.MissingNeededCards);
}
final int currCounters = cards.get(0).getCounters(CounterType.getType(type));
final int currCounters = cards.get(0).getCounters(CounterType.get(type));
// each non +1/+1 counter on the card is a 10% chance of not
// activating this ability.
@@ -623,7 +623,7 @@ public class CountersPutAi extends CountersAi {
}
// Useless since the card already has the keyword (or for another reason)
if (ComputerUtil.isUselessCounter(CounterType.getType(type), cards.get(0))) {
if (ComputerUtil.isUselessCounter(CounterType.get(type), cards.get(0))) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
}
}
@@ -961,8 +961,8 @@ public class CountersPutAi extends CountersAi {
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Bolster does use this
// TODO need more or less logic there?
final CounterType m1m1 = CounterEnumType.M1M1;
final CounterType p1p1 = CounterEnumType.P1P1;
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
// no logic if there is no options or no to choice
if (!isOptional && Iterables.size(options) <= 1) {
@@ -1083,8 +1083,8 @@ public class CountersPutAi extends CountersAi {
if (e instanceof Card) {
Card c = (Card) e;
if (c.getController().isOpponentOf(ai)) {
if (options.contains(CounterEnumType.M1M1) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterEnumType.M1M1;
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterType.get(CounterEnumType.M1M1);
}
for (CounterType type : options) {
if (ComputerUtil.isNegativeCounter(type, c)) {
@@ -1101,12 +1101,12 @@ public class CountersPutAi extends CountersAi {
} else if (e instanceof Player) {
Player p = (Player) e;
if (p.isOpponentOf(ai)) {
if (options.contains(CounterEnumType.POISON)) {
return CounterEnumType.POISON;
if (options.contains(CounterType.get(CounterEnumType.POISON))) {
return CounterType.get(CounterEnumType.POISON);
}
} else {
if (options.contains(CounterEnumType.EXPERIENCE)) {
return CounterEnumType.EXPERIENCE;
if (options.contains(CounterType.get(CounterEnumType.EXPERIENCE))) {
return CounterType.get(CounterEnumType.EXPERIENCE);
}
}

View File

@@ -218,18 +218,18 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
Card tgt = (Card) params.get("Target");
// planeswalker has high priority for loyalty counters
if (tgt.isPlaneswalker() && options.contains(CounterEnumType.LOYALTY)) {
return CounterEnumType.LOYALTY;
if (tgt.isPlaneswalker() && options.contains(CounterType.get(CounterEnumType.LOYALTY))) {
return CounterType.get(CounterEnumType.LOYALTY);
}
if (tgt.getController().isOpponentOf(ai)) {
// creatures with BaseToughness below or equal zero might be
// killed if their counters are removed
if (tgt.isCreature() && tgt.getBaseToughness() <= 0) {
if (options.contains(CounterEnumType.P1P1)) {
return CounterEnumType.P1P1;
} else if (options.contains(CounterEnumType.M1M1)) {
return CounterEnumType.M1M1;
if (options.contains(CounterType.get(CounterEnumType.P1P1))) {
return CounterType.get(CounterEnumType.P1P1);
} else if (options.contains(CounterType.get(CounterEnumType.M1M1))) {
return CounterType.get(CounterEnumType.M1M1);
}
}
@@ -241,17 +241,17 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
}
} else {
// this counters are treat first to be removed
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterEnumType.ICE)) {
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.get(CounterEnumType.ICE))) {
CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage");
boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, (Predicate<Card>) Card::ignoreLegendRule);
if (maritEmpty) {
return CounterEnumType.ICE;
return CounterType.get(CounterEnumType.ICE);
}
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterEnumType.P1P1)) {
return CounterEnumType.P1P1;
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterEnumType.M1M1)) {
return CounterEnumType.M1M1;
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.get(CounterEnumType.P1P1))) {
return CounterType.get(CounterEnumType.P1P1);
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.get(CounterEnumType.M1M1))) {
return CounterType.get(CounterEnumType.M1M1);
}
// fallback logic, select positive counter to add more

View File

@@ -384,7 +384,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (targetCard.getController().isOpponentOf(ai)) {
// if its a Planeswalker try to remove Loyality first
if (targetCard.isPlaneswalker()) {
return CounterEnumType.LOYALTY;
return CounterType.get(CounterEnumType.LOYALTY);
}
for (CounterType type : options) {
if (!ComputerUtil.isNegativeCounter(type, targetCard)) {
@@ -392,10 +392,10 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
}
} else {
if (options.contains(CounterEnumType.M1M1) && targetCard.hasKeyword(Keyword.PERSIST)) {
return CounterEnumType.M1M1;
} else if (options.contains(CounterEnumType.P1P1) && targetCard.hasKeyword(Keyword.UNDYING)) {
return CounterEnumType.P1P1;
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && targetCard.hasKeyword(Keyword.PERSIST)) {
return CounterType.get(CounterEnumType.M1M1);
} else if (options.contains(CounterType.get(CounterEnumType.P1P1)) && targetCard.hasKeyword(Keyword.UNDYING)) {
return CounterType.get(CounterEnumType.P1P1);
}
for (CounterType type : options) {
if (ComputerUtil.isNegativeCounter(type, targetCard)) {

View File

@@ -133,9 +133,9 @@ public class DamageAllAi extends SpellAbilityAi {
if (ComputerUtilCombat.predictDamageTo(opp, dmg, source, false) > 0) {
// When using Pestilence to hurt players, do it at
// the end of the opponent's turn only
if (!"DmgAllCreaturesAndPlayers".equals(sa.getParam("AILogic"))
|| (ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)
&& !ai.getGame().getPhaseHandler().isPlayerTurn(ai)))
if ((!"DmgAllCreaturesAndPlayers".equals(sa.getParam("AILogic")))
|| ((ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)
&& (ai.getGame().getNonactivePlayers().contains(ai)))))
// Need further improvement : if able to kill immediately with repeated activations, do not wait
// for phases! Will also need to implement considering repeated activations for killed creatures!
// || (ai.sa.getPayCosts(). ??? )

View File

@@ -26,6 +26,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -369,7 +370,7 @@ public class DrawAi extends SpellAbilityAi {
// try to make opponent lose to poison
// currently only Caress of Phyrexia
if (getPoison != null && oppA.canReceiveCounters(CounterEnumType.POISON)) {
if (getPoison != null && oppA.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (oppA.getPoisonCounters() + numCards > 9) {
sa.getTargets().add(oppA);
return true;
@@ -413,7 +414,7 @@ public class DrawAi extends SpellAbilityAi {
}
}
if (getPoison != null && ai.canReceiveCounters(CounterEnumType.POISON)) {
if (getPoison != null && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (numCards + ai.getPoisonCounters() >= 8) {
aiTarget = false;
}
@@ -471,7 +472,7 @@ public class DrawAi extends SpellAbilityAi {
}
// ally would lose because of poison
if (getPoison != null && ally.canReceiveCounters(CounterEnumType.POISON) && ally.getPoisonCounters() + numCards > 9) {
if (getPoison != null && ally.canReceiveCounters(CounterType.get(CounterEnumType.POISON)) && ally.getPoisonCounters() + numCards > 9) {
continue;
}

View File

@@ -159,7 +159,7 @@ public class ManaAi extends SpellAbilityAi {
int numCounters = 0;
int manaSurplus = 0;
if ("Count$xPaid".equals(host.getSVar("X")) && sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
CounterType ctrType = CounterEnumType.KI; // Petalmane Baku
CounterType ctrType = CounterType.get(CounterEnumType.KI); // Petalmane Baku
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;

View File

@@ -6,6 +6,7 @@ import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.GameLossReason;
@@ -64,7 +65,7 @@ public class PoisonAi extends SpellAbilityAi {
boolean result;
if (sa.usesTargeting()) {
result = tgtPlayer(ai, sa, mandatory);
} else if (mandatory || !ai.canReceiveCounters(CounterEnumType.POISON)) {
} else if (mandatory || !ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
// mandatory or ai is uneffected
result = true;
} else {
@@ -89,7 +90,7 @@ public class PoisonAi extends SpellAbilityAi {
PlayerCollection betterTgts = tgts.filter(input -> {
if (input.cantLoseCheck(GameLossReason.Poisoned)) {
return false;
} else if (!input.canReceiveCounters(CounterEnumType.POISON)) {
} else if (!input.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
return false;
}
return true;
@@ -108,7 +109,7 @@ public class PoisonAi extends SpellAbilityAi {
if (tgts.isEmpty()) {
if (mandatory) {
// AI is uneffected
if (ai.canBeTargetedBy(sa) && !ai.canReceiveCounters(CounterEnumType.POISON)) {
if (ai.canBeTargetedBy(sa) && !ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
sa.getTargets().add(ai);
return true;
}
@@ -120,7 +121,7 @@ public class PoisonAi extends SpellAbilityAi {
if (input.cantLoseCheck(GameLossReason.Poisoned)) {
return true;
}
return !input.canReceiveCounters(CounterEnumType.POISON);
return !input.canReceiveCounters(CounterType.get(CounterEnumType.POISON));
});
if (!betterAllies.isEmpty()) {
allies = betterAllies;

View File

@@ -8,6 +8,7 @@ import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerController;
@@ -39,7 +40,7 @@ public class TimeTravelAi extends SpellAbilityAi {
// so removing them is good; stuff on the battlefield is usually stuff like Vanishing or As Foretold, which favors adding Time
// counters for better effect, but exceptions should be added here).
Card target = (Card)params.get("Target");
return !ComputerUtil.isNegativeCounter(CounterEnumType.TIME, target);
return !ComputerUtil.isNegativeCounter(CounterType.get(CounterEnumType.TIME), target);
}
@Override

View File

@@ -29,6 +29,8 @@ import java.util.stream.Collectors;
public class StaticData {
private final CardStorageReader cardReader;
private final CardStorageReader tokenReader;
private final CardStorageReader customCardReader;
private final String blockDataFolder;
private final CardDb commonCards;
private final CardDb variantCards;
@@ -77,6 +79,7 @@ public class StaticData {
this.tokenReader = tokenReader;
this.editions = new CardEdition.Collection(new CardEdition.Reader(new File(editionFolder)));
this.blockDataFolder = blockDataFolder;
this.customCardReader = customCardReader;
this.allowCustomCardsInDecksConformance = allowCustomCardsInDecksConformance;
this.enableSmartCardArtSelection = enableSmartCardArtSelection;
this.loadNonLegalCards = loadNonLegalCards;

View File

@@ -649,11 +649,6 @@ public final class CardEdition implements Comparable<CardEdition> {
continue;
}
if (sectionName.endsWith("Types")) {
CardType.Helper.parseTypes(sectionName, contents.get(sectionName));
} else {
// Parse cards
// parse sections of the format "<collector number> <rarity> <name>"
if (editionSectionsWithCollectorNumbers.contains(sectionName)) {
for(String line : contents.get(sectionName)) {
@@ -681,7 +676,6 @@ public final class CardEdition implements Comparable<CardEdition> {
customPrintSheetsToParse.put(sectionName, contents.get(sectionName));
}
}
}
ListMultimap<String, EditionEntry> tokenMap = ArrayListMultimap.create();
ListMultimap<String, EditionEntry> otherMap = ArrayListMultimap.create();
@@ -846,7 +840,7 @@ public final class CardEdition implements Comparable<CardEdition> {
@Override
public void add(CardEdition item) { //Even though we want it to be read only, make an exception for custom content.
if(lock) throw new UnsupportedOperationException("This is a read-only storage");
else map.put(item.getCode(), item);
else map.put(item.getName(), item);
}
public void append(CardEdition.Collection C){ //Append custom editions
if (lock) throw new UnsupportedOperationException("This is a read-only storage");

View File

@@ -53,7 +53,6 @@ public final class CardRules implements ICardCharacteristics {
private boolean addsWildCardColor;
private int setColorID;
private boolean custom;
private boolean unsupported;
private String path;
public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
@@ -223,8 +222,6 @@ public final class CardRules implements ICardCharacteristics {
public boolean isCustom() { return custom; }
public void setCustom() { custom = true; }
public boolean isUnsupported() { return unsupported; }
@Override
public CardType getType() {
switch (splitType.getAggregationMethod()) {
@@ -376,9 +373,6 @@ public final class CardRules implements ICardCharacteristics {
public boolean canBeOathbreaker() {
CardType type = mainPart.getType();
if (mainPart.getOracleText().contains("can be your commander")) {
return true;
}
return type.isPlaneswalker();
}
@@ -831,8 +825,6 @@ public final class CardRules implements ICardCharacteristics {
faces[0].assignMissingFields();
final CardRules result = new CardRules(faces, CardSplitType.None, cah);
result.unsupported = true;
return result;
}

View File

@@ -196,31 +196,6 @@ public final class CardRulesPredicates {
return card -> card.getSplitType().equals(type);
}
/**
* @return a Predicate that matches cards that are vanilla.
*/
public static Predicate<CardRules> isVanilla() {
return card -> {
if (!(card.getType().isCreature() || card.getType().isLand()) ||
card.getSplitType() != CardSplitType.None ||
card.hasFunctionalVariants()) {
return false;
}
ICardFace mainPart = card.getMainPart();
boolean hasAny =
mainPart.getKeywords().iterator().hasNext() ||
mainPart.getAbilities().iterator().hasNext() ||
mainPart.getStaticAbilities().iterator().hasNext() ||
mainPart.getTriggers().iterator().hasNext() ||
(mainPart.getDraftActions() != null && mainPart.getDraftActions().iterator().hasNext()) ||
mainPart.getReplacements().iterator().hasNext();
return !hasAny;
};
}
/**
* Checks for color.
*

View File

@@ -1066,74 +1066,4 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
return type;
}
public static class Helper {
public static final void parseTypes(String sectionName, List<String> content) {
Set<String> addToSection = null;
switch (sectionName) {
case "BasicTypes":
addToSection = CardType.Constant.BASIC_TYPES;
break;
case "LandTypes":
addToSection = CardType.Constant.LAND_TYPES;
break;
case "CreatureTypes":
addToSection = CardType.Constant.CREATURE_TYPES;
break;
case "SpellTypes":
addToSection = CardType.Constant.SPELL_TYPES;
break;
case "EnchantmentTypes":
addToSection = CardType.Constant.ENCHANTMENT_TYPES;
break;
case "ArtifactTypes":
addToSection = CardType.Constant.ARTIFACT_TYPES;
break;
case "WalkerTypes":
addToSection = CardType.Constant.WALKER_TYPES;
break;
case "DungeonTypes":
addToSection = CardType.Constant.DUNGEON_TYPES;
break;
case "BattleTypes":
addToSection = CardType.Constant.BATTLE_TYPES;
break;
case "PlanarTypes":
addToSection = CardType.Constant.PLANAR_TYPES;
break;
}
if (addToSection == null) {
return;
}
for(String line : content) {
if (line.length() == 0) continue;
if (line.contains(":")) {
String[] k = line.split(":");
if (addToSection.contains(k[0])) {
continue;
}
addToSection.add(k[0]);
CardType.Constant.pluralTypes.put(k[0], k[1]);
if (k[0].contains(" ")) {
CardType.Constant.MultiwordTypes.add(k[0]);
}
} else {
if (addToSection.contains(line)) {
continue;
}
addToSection.add(line);
if (line.contains(" ")) {
CardType.Constant.MultiwordTypes.add(line);
}
}
}
}
}
}

View File

@@ -250,7 +250,7 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
this.artIndex = Math.max(artIndex, IPaperCard.DEFAULT_ART_INDEX);
this.foil = foil;
this.rarity = rarity;
this.artist = artist;
this.artist = TextUtil.normalizeText(artist);
this.collectorNumber = (collectorNumber != null && !collectorNumber.isEmpty()) ? collectorNumber : IPaperCard.NO_COLLECTOR_NUMBER;
// If the user changes the language this will make cards sort by the old language until they restart the game.
// This is a good tradeoff
@@ -375,8 +375,7 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
System.out.println("PaperCard: " + name + " not found with set and index " + edition + ", " + artIndex);
pc = readObjectAlternate(name, edition);
if (pc == null) {
pc = StaticData.instance().getCommonCards().createUnsupportedCard(name);
//throw new IOException(TextUtil.concatWithSpace("Card", name, "not found with set and index", edition, Integer.toString(artIndex)));
throw new IOException(TextUtil.concatWithSpace("Card", name, "not found with set and index", edition, Integer.toString(artIndex)));
}
System.out.println("Alternate object found: " + pc.getName() + ", " + pc.getEdition() + ", " + pc.getArtIndex());
}

View File

@@ -414,6 +414,19 @@ public class Game {
return players;
}
/**
* Gets the nonactive players who are still fighting to win, in turn order.
*/
public final PlayerCollection getNonactivePlayers() {
// Don't use getPlayersInTurnOrder to prevent copying the player collection twice
final PlayerCollection players = new PlayerCollection(ingamePlayers);
players.remove(phaseHandler.getPlayerTurn());
if (!getTurnOrder().isDefaultDirection()) {
Collections.reverse(players);
}
return players;
}
/**
* Gets the players who participated in match (regardless of outcome).
* <i>Use this in UI and after match calculations</i>

View File

@@ -1822,8 +1822,8 @@ public class GameAction {
private boolean stateBasedAction704_5q(Card c) {
boolean checkAgain = false;
final CounterType p1p1 = CounterEnumType.P1P1;
final CounterType m1m1 = CounterEnumType.M1M1;
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
int plusOneCounters = c.getCounters(p1p1);
int minusOneCounters = c.getCounters(m1m1);
if (plusOneCounters > 0 && minusOneCounters > 0) {
@@ -1843,7 +1843,7 @@ public class GameAction {
return checkAgain;
}
private boolean stateBasedAction704_5r(Card c) {
final CounterType dreamType = CounterEnumType.DREAM;
final CounterType dreamType = CounterType.get(CounterEnumType.DREAM);
int old = c.getCounters(dreamType);
if (old <= 0) {

View File

@@ -33,6 +33,7 @@ import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.event.GameEventCardAttachment;
import forge.game.keyword.Keyword;
@@ -304,6 +305,9 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
Integer value = counters.get(counterName);
return value == null ? 0 : value;
}
public final int getCounters(final CounterEnumType counterType) {
return getCounters(CounterType.get(counterType));
}
public void setCounters(final CounterType counterType, final Integer num) {
if (num <= 0) {
@@ -312,6 +316,9 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
counters.put(counterType, num);
}
}
public void setCounters(final CounterEnumType counterType, final Integer num) {
setCounters(CounterType.get(counterType), num);
}
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
@@ -321,6 +328,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
abstract public int subtractCounter(final CounterType counterName, final int n, final Player remover);
abstract public void clearCounters();
public boolean canReceiveCounters(final CounterEnumType type) {
return canReceiveCounters(CounterType.get(type));
}
public final void addCounter(final CounterType counterType, int n, final Player source, GameEntityCounterTable table) {
if (n <= 0 || !canReceiveCounters(counterType)) {
// As per rule 107.1b
@@ -340,7 +351,18 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
table.put(source, this, counterType, n);
}
public final void addCounter(final CounterEnumType counterType, final int n, final Player source, GameEntityCounterTable table) {
addCounter(CounterType.get(counterType), n, source, table);
}
public int subtractCounter(final CounterEnumType counterName, final int n, final Player remover) {
return subtractCounter(CounterType.get(counterName), n, remover);
}
abstract public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params);
public void addCounterInternal(final CounterEnumType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params) {
addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table, params);
}
public Integer getCounterMax(final CounterType counterType) {
return null;
}

View File

@@ -29,25 +29,25 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(GameEventGameOutcome ev) {
// Turn number counted from the starting player
int lastTurn = (int)Math.ceil((float)ev.result().getLastTurnNumber() / 2.0);
int lastTurn = (int)Math.ceil((float)ev.result.getLastTurnNumber() / 2.0);
log.add(GameLogEntryType.GAME_OUTCOME, localizer.getMessage("lblTurn") + " " + lastTurn);
for (String outcome : ev.result().getOutcomeStrings()) {
for (String outcome : ev.result.getOutcomeStrings()) {
log.add(GameLogEntryType.GAME_OUTCOME, outcome);
}
return generateSummary(ev.history());
return generateSummary(ev.history);
}
@Override
public GameLogEntry visit(GameEventScry ev) {
String scryOutcome = "";
if (ev.toTop() > 0 && ev.toBottom() > 0) {
scryOutcome = localizer.getMessage("lblLogScryTopBottomLibrary").replace("%s", ev.player().toString()).replace("%top", String.valueOf(ev.toTop())).replace("%bottom", String.valueOf(ev.toBottom()));
} else if (ev.toBottom() == 0) {
scryOutcome = localizer.getMessage("lblLogScryTopLibrary").replace("%s", ev.player().toString()).replace("%top", String.valueOf(ev.toTop()));
if (ev.toTop > 0 && ev.toBottom > 0) {
scryOutcome = localizer.getMessage("lblLogScryTopBottomLibrary").replace("%s", ev.player.toString()).replace("%top", String.valueOf(ev.toTop)).replace("%bottom", String.valueOf(ev.toBottom));
} else if (ev.toBottom == 0) {
scryOutcome = localizer.getMessage("lblLogScryTopLibrary").replace("%s", ev.player.toString()).replace("%top", String.valueOf(ev.toTop));
} else {
scryOutcome = localizer.getMessage("lblLogScryBottomLibrary").replace("%s", ev.player().toString()).replace("%bottom", String.valueOf(ev.toBottom()));
scryOutcome = localizer.getMessage("lblLogScryBottomLibrary").replace("%s", ev.player.toString()).replace("%bottom", String.valueOf(ev.toBottom));
}
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, scryOutcome);
@@ -57,12 +57,12 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
public GameLogEntry visit(GameEventSurveil ev) {
String surveilOutcome = "";
if (ev.toLibrary() > 0 && ev.toGraveyard() > 0) {
surveilOutcome = localizer.getMessage("lblLogSurveiledToLibraryGraveyard", ev.player().toString(), String.valueOf(ev.toLibrary()), String.valueOf(ev.toGraveyard()));
} else if (ev.toGraveyard() == 0) {
surveilOutcome = localizer.getMessage("lblLogSurveiledToLibrary", ev.player().toString(), String.valueOf(ev.toLibrary()));
if (ev.toLibrary > 0 && ev.toGraveyard > 0) {
surveilOutcome = localizer.getMessage("lblLogSurveiledToLibraryGraveyard", ev.player.toString(), String.valueOf(ev.toLibrary), String.valueOf(ev.toGraveyard));
} else if (ev.toGraveyard == 0) {
surveilOutcome = localizer.getMessage("lblLogSurveiledToLibrary", ev.player.toString(), String.valueOf(ev.toLibrary));
} else {
surveilOutcome = localizer.getMessage("lblLogSurveiledToGraveyard", ev.player().toString(), String.valueOf(ev.toGraveyard()));
surveilOutcome = localizer.getMessage("lblLogSurveiledToGraveyard", ev.player.toString(), String.valueOf(ev.toGraveyard));
}
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, surveilOutcome);
@@ -70,26 +70,26 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(GameEventSpellResolved ev) {
String messageForLog = ev.hasFizzled() ? localizer.getMessage("lblLogCardAbilityFizzles", ev.spell().getHostCard().toString()) : ev.spell().getStackDescription();
String messageForLog = ev.hasFizzled ? localizer.getMessage("lblLogCardAbilityFizzles", ev.spell.getHostCard().toString()) : ev.spell.getStackDescription();
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, messageForLog);
}
@Override
public GameLogEntry visit(GameEventSpellAbilityCast event) {
String player = event.sa().getActivatingPlayer().getName();
String action = event.sa().isSpell() ? localizer.getMessage("lblCast")
: event.sa().isTrigger() ? localizer.getMessage("lblTriggered")
String player = event.sa.getActivatingPlayer().getName();
String action = event.sa.isSpell() ? localizer.getMessage("lblCast")
: event.sa.isTrigger() ? localizer.getMessage("lblTriggered")
: localizer.getMessage("lblActivated");
String object = event.si().getStackDescription().startsWith("Morph ")
String object = event.si.getStackDescription().startsWith("Morph ")
? localizer.getMessage("lblMorph")
: event.sa().getHostCard().toString();
: event.sa.getHostCard().toString();
String messageForLog = "";
if (event.sa().getTargetRestrictions() != null) {
if (event.sa.getTargetRestrictions() != null) {
StringBuilder sb = new StringBuilder();
for (TargetChoices ch : event.sa().getAllTargetChoices()) {
for (TargetChoices ch : event.sa.getAllTargetChoices()) {
if (null != ch) {
sb.append(ch);
}
@@ -104,18 +104,18 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(GameEventCardModeChosen ev) {
if (!ev.log()) {
if (!ev.log) {
return null;
}
String modeChoiceOutcome;
if (ev.random()) {
modeChoiceOutcome = localizer.getMessage("lblLogRandomMode", ev.cardName(), ev.mode());
if (ev.random) {
modeChoiceOutcome = localizer.getMessage("lblLogRandomMode", ev.cardName, ev.mode);
} else {
modeChoiceOutcome = localizer.getMessage("lblLogPlayerChosenModeForCard",
ev.player().toString(), ev.mode(), ev.cardName());
ev.player.toString(), ev.mode, ev.cardName);
}
String name = CardTranslation.getTranslatedName(ev.cardName());
String name = CardTranslation.getTranslatedName(ev.cardName);
modeChoiceOutcome = TextUtil.fastReplace(modeChoiceOutcome, "CARDNAME", name);
modeChoiceOutcome = TextUtil.fastReplace(modeChoiceOutcome, "NICKNAME",
Lang.getInstance().getNickName(name));
@@ -124,7 +124,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(GameEventRandomLog ev) {
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, ev.message());
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, ev.message);
}
private static GameLogEntry generateSummary(final Collection<GameOutcome> gamesPlayed) {
@@ -152,8 +152,8 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(final GameEventPlayerControl event) {
final LobbyPlayer newLobbyPlayer = event.newLobbyPlayer();
final Player p = event.player();
final LobbyPlayer newLobbyPlayer = event.newLobbyPlayer;
final Player p = event.player;
final String message;
if (newLobbyPlayer == null) {
@@ -166,23 +166,23 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(GameEventTurnPhase ev) {
Player p = ev.playerTurn();
return new GameLogEntry(GameLogEntryType.PHASE, ev.phaseDesc() + Lang.getInstance().getPossessedObject(p.getName(), ev.phase().nameForUi));
Player p = ev.playerTurn;
return new GameLogEntry(GameLogEntryType.PHASE, ev.phaseDesc + Lang.getInstance().getPossessedObject(p.getName(), ev.phase.nameForUi));
}
@Override
public GameLogEntry visit(GameEventCardDamaged event) {
String additionalLog = "";
if (event.type() == DamageType.Deathtouch) {
if (event.type == DamageType.Deathtouch) {
additionalLog = localizer.getMessage("lblDeathtouch");
}
if (event.type() == DamageType.M1M1Counters) {
if (event.type == DamageType.M1M1Counters) {
additionalLog = localizer.getMessage("lblAsM1M1Counters");
}
if (event.type() == DamageType.LoyaltyLoss) {
additionalLog = localizer.getMessage("lblRemovingNLoyaltyCounter", String.valueOf(event.amount()));
if (event.type == DamageType.LoyaltyLoss) {
additionalLog = localizer.getMessage("lblRemovingNLoyaltyCounter", String.valueOf(event.amount));
}
String message = localizer.getMessage("lblSourceDealsNDamageToDest", event.source().toString(), String.valueOf(event.amount()), additionalLog, event.card().toString());
String message = localizer.getMessage("lblSourceDealsNDamageToDest", event.source.toString(), String.valueOf(event.amount), additionalLog, event.card.toString());
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
}
@@ -191,43 +191,43 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
*/
@Override
public GameLogEntry visit(GameEventLandPlayed ev) {
String message = localizer.getMessage("lblLogPlayerPlayedLand", ev.player().toString(), ev.land().toString());
String message = localizer.getMessage("lblLogPlayerPlayedLand", ev.player.toString(), ev.land.toString());
return new GameLogEntry(GameLogEntryType.LAND, message);
}
@Override
public GameLogEntry visit(GameEventTurnBegan event) {
String message = localizer.getMessage("lblLogTurnNOwnerByPlayer", String.valueOf(event.turnNumber()), event.turnOwner().toString());
String message = localizer.getMessage("lblLogTurnNOwnerByPlayer", String.valueOf(event.turnNumber), event.turnOwner.toString());
return new GameLogEntry(GameLogEntryType.TURN, message);
}
@Override
public GameLogEntry visit(GameEventPlayerDamaged ev) {
String extra = ev.infect() ? localizer.getMessage("lblLogAsPoisonCounters") : "";
String damageType = ev.combat() ? localizer.getMessage("lblCombat") : localizer.getMessage("lblNonCombat");
String message = localizer.getMessage("lblLogSourceDealsNDamageOfTypeToDest", ev.source().toString(),
String.valueOf(ev.amount()), damageType, ev.target().toString(), extra);
String extra = ev.infect ? localizer.getMessage("lblLogAsPoisonCounters") : "";
String damageType = ev.combat ? localizer.getMessage("lblCombat") : localizer.getMessage("lblNonCombat");
String message = localizer.getMessage("lblLogSourceDealsNDamageOfTypeToDest", ev.source.toString(),
String.valueOf(ev.amount), damageType, ev.target.toString(), extra);
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
}
@Override
public GameLogEntry visit(GameEventPlayerPoisoned ev) {
String message = localizer.getMessage("lblLogPlayerReceivesNPosionCounterFrom",
ev.receiver().toString(), String.valueOf(ev.amount()), ev.source().toString());
ev.receiver.toString(), String.valueOf(ev.amount), ev.source.toString());
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
}
@Override
public GameLogEntry visit(GameEventPlayerRadiation ev) {
String message;
final int change = ev.change();
final int change = ev.change;
String radCtr = CounterEnumType.RAD.getName().toLowerCase() + " " +
Localizer.getInstance().getMessage("lblCounter").toLowerCase();
if (change >= 0) message = localizer.getMessage("lblLogPlayerRadiation",
ev.receiver().toString(), Lang.nounWithNumeralExceptOne(String.valueOf(change), radCtr),
ev.source().toString());
ev.receiver.toString(), Lang.nounWithNumeralExceptOne(String.valueOf(change), radCtr),
ev.source.toString());
else message = localizer.getMessage("lblLogPlayerRadRemove",
ev.receiver().toString(), Lang.nounWithNumeralExceptOne(String.valueOf(Math.abs(change)), radCtr));
ev.receiver.toString(), Lang.nounWithNumeralExceptOne(String.valueOf(Math.abs(change)), radCtr));
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
}
@@ -239,16 +239,16 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
// Append Defending Player/Planeswalker
// Not a big fan of the triple nested loop here
for (GameEntity k : ev.attackersMap().keySet()) {
Collection<Card> attackers = ev.attackersMap().get(k);
for (GameEntity k : ev.attackersMap.keySet()) {
Collection<Card> attackers = ev.attackersMap.get(k);
if (attackers == null || attackers.isEmpty()) {
continue;
}
if (sb.length() > 0) sb.append("\n");
sb.append(localizer.getMessage("lblLogPlayerAssignedAttackerToAttackTarget", ev.player(), Lang.joinHomogenous(attackers), k));
sb.append(localizer.getMessage("lblLogPlayerAssignedAttackerToAttackTarget", ev.player, Lang.joinHomogenous(attackers), k));
}
if (sb.length() == 0) {
sb.append(localizer.getMessage("lblPlayerDidntAttackThisTurn").replace("%s", ev.player().toString()));
sb.append(localizer.getMessage("lblPlayerDidntAttackThisTurn").replace("%s", ev.player.toString()));
}
return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString());
}
@@ -262,7 +262,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
Collection<Card> blockers = null;
for (Entry<GameEntity, MapOfLists<Card, Card>> kv : ev.blockers().entrySet()) {
for (Entry<GameEntity, MapOfLists<Card, Card>> kv : ev.blockers.entrySet()) {
GameEntity defender = kv.getKey();
MapOfLists<Card, Card> attackers = kv.getValue();
if (attackers == null || attackers.isEmpty()) {
@@ -298,7 +298,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(GameEventMulligan ev) {
String message = localizer.getMessage("lblPlayerHasMulliganedDownToNCards").replace("%d", String.valueOf(ev.player().getZone(ZoneType.Hand).size())).replace("%s", ev.player().toString());
String message = localizer.getMessage("lblPlayerHasMulliganedDownToNCards").replace("%d", String.valueOf(ev.player.getZone(ZoneType.Hand).size())).replace("%s", ev.player.toString());
return new GameLogEntry(GameLogEntryType.MULLIGAN, message);
}

View File

@@ -215,7 +215,6 @@ public class GameView extends TrackableObject {
}
public void setDependencies(Table<StaticAbility, StaticAbility, Set<StaticAbilityLayer>> dependencies) {
if (dependencies.isEmpty()) {
set(TrackableProperty.Dependencies, "");
return;
}
StringBuilder sb = new StringBuilder();

View File

@@ -1328,6 +1328,12 @@ public class AbilityUtils {
game.getTriggerHandler().resetActiveTriggers();
}
if (sa.hasParam("Precalc")) {
for (String s : sa.getParam("Precalc").split(",")) {
sa.setSVar(s, String.valueOf(calculateAmount(sa.getHostCard(), s, sa)));
}
}
resolvePreAbilities(sa, game);
// count times ability resolves this turn

View File

@@ -39,6 +39,7 @@ public class AirbendEffect extends SpellAbilityEffect {
return sb.toString();
}
@Override
public void resolve(SpellAbility sa) {
final Card hostCard = sa.getHostCard();

View File

@@ -17,6 +17,7 @@ import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardZoneTable;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.card.token.TokenInfo;
import forge.game.event.GameEventCombatChanged;
import forge.game.event.GameEventTokenCreated;
@@ -85,7 +86,7 @@ public class AmassEffect extends TokenEffectBase {
}
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", CounterEnumType.P1P1);
params.put("CounterType", CounterType.get(CounterEnumType.P1P1));
params.put("Amount", amount);
Card tgt = activator.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), false, params);

View File

@@ -90,16 +90,6 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
c.addPerpetual(p);
p.applyEffect(c);
}
if (sa.hasParam("ManaCost")) {
final ManaCost manaCost = new ManaCost(new ManaCostParser(sa.getParam("ManaCost")));
if (perpetual) {
PerpetualManaCost p = new PerpetualManaCost(timestamp, manaCost);
c.addPerpetual(p);
p.applyEffect(c);
} else {
c.addChangedManaCost(manaCost, timestamp, (long) 0);
}
}
if (!addType.isEmpty() || !removeType.isEmpty() || addAllCreatureTypes || !remove.isEmpty()) {
if (perpetual) {

View File

@@ -102,29 +102,24 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
int totalRemoved = 0;
CardCollectionView srcCards;
if (sa.hasParam("Choices")) {
ZoneType choiceZone = sa.hasParam("ChoiceZone") ? ZoneType.smartValueOf(sa.getParam("ChoiceZone"))
: ZoneType.Battlefield;
srcCards = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"),
activator, source, sa);
} else {
srcCards = getTargetCards(sa);
}
if (sa.isReplacementAbility()) {
srcCards = new CardCollection(srcCards).filter(c -> !c.isInPlay() || sa.getLastStateBattlefield().contains(c));
}
if (sa.hasParam("Choices")) {
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"),
activator, source, sa);
int min = 1;
int max = 1;
if (sa.hasParam("ChoiceOptional")) {
min = 0;
max = srcCards.size();
max = choices.size();
}
if (sa.hasParam("ChoiceNum")) {
min = max = AbilityUtils.calculateAmount(source, sa.getParam("ChoiceNum"), sa);
}
if (srcCards.size() < min) {
if (choices.size() < min) {
return;
}
@@ -133,12 +128,13 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
title = title.replace(" ", " ");
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
srcCards = pc.chooseCardsForEffect(srcCards, sa, title, min, max, min == 0, params);
srcCards = pc.chooseCardsForEffect(choices, sa, title, min, max, min == 0, params);
} else {
for (final Player tgtPlayer : getTargetPlayers(sa)) {
if (!tgtPlayer.isInGame()) {
continue;
}
// Removing energy
if (type.equals("All")) {
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(tgtPlayer.getCounters().entrySet())) {
totalRemoved += tgtPlayer.subtractCounter(e.getKey(), e.getValue(), activator);
@@ -154,6 +150,8 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
}
}
}
srcCards = getTargetCards(sa);
}
for (final Card tgtCard : srcCards) {

View File

@@ -40,7 +40,6 @@ public class EarthbendEffect extends SpellAbilityEffect {
TargetRestrictions abTgt = new TargetRestrictions("Select target land you control", "Land.YouCtrl".split(","), "1", "1");
sa.setTargetRestrictions(abTgt);
}
@Override
public void resolve(SpellAbility sa) {
final Card source = sa.getHostCard();

View File

@@ -369,8 +369,8 @@ public class RollDiceEffect extends SpellAbilityEffect {
List<Card> canIncrementDice = new ArrayList<>();
for (Card c : xenosquirrels) {
// Xenosquirrels must have a P1P1 counter on it to remove in order to modify
Integer P1P1Counters = c.getCounters().get(CounterEnumType.P1P1);
if (P1P1Counters != null && P1P1Counters > 0 && c.canRemoveCounters(CounterEnumType.P1P1)) {
Integer P1P1Counters = c.getCounters().get(CounterType.get(CounterEnumType.P1P1));
if (P1P1Counters != null && P1P1Counters > 0 && c.canRemoveCounters(CounterType.get(CounterEnumType.P1P1))) {
canIncrementDice.add(c);
}
}
@@ -399,7 +399,6 @@ public class RollDiceEffect extends SpellAbilityEffect {
* @param repParams replacement effect parameters
* @return list of final roll results after applying ignores and replacements, sorted in ascending order
*/
@SuppressWarnings("unchecked")
private static List<Integer> rollAction(int amount, int sides, int ignore, List<Integer> rollsResult, List<Integer> ignored, Map<Player, Integer> ignoreChosenMap, Set<Card> dicePTExchanges, Player player, Map<AbilityKey, Object> repParams) {
repParams.put(AbilityKey.Sides, sides);
@@ -417,8 +416,6 @@ public class RollDiceEffect extends SpellAbilityEffect {
ignoreChosenMap = (Map<Player, Integer>) repParams.get(AbilityKey.IgnoreChosen);
break;
}
default:
break;
}
List<Integer> naturalRolls = (rollsResult == null ? new ArrayList<>() : rollsResult);

View File

@@ -11,7 +11,6 @@ import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.player.Player;
import forge.game.player.PlayerController;
@@ -38,7 +37,7 @@ public class TimeTravelEffect extends SpellAbilityEffect {
PlayerController pc = activator.getController();
final CounterType counterType = CounterEnumType.TIME;
final CounterEnumType counterType = CounterEnumType.TIME;
for (int i = 0; i < num; i++) {
FCollection<Card> list = new FCollection<>();

View File

@@ -3561,8 +3561,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
if (!getStaticAbilities().isEmpty()) {
return false;
}
if (!getReplacementEffects().isEmpty()
&& (getReplacementEffects().size() > 1 || !isSaga() || hasKeyword(Keyword.READ_AHEAD))) {
if (!getReplacementEffects().isEmpty()) {
return false;
}
if (!getTriggers().isEmpty()) {
@@ -6442,10 +6441,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
DamageType damageType = DamageType.Normal;
if (isPlaneswalker()) { // 120.3c
subtractCounter(CounterEnumType.LOYALTY, damageIn, null, true);
subtractCounter(CounterType.get(CounterEnumType.LOYALTY), damageIn, null, true);
}
if (isBattle()) {
subtractCounter(CounterEnumType.DEFENSE, damageIn, null, true);
subtractCounter(CounterType.get(CounterEnumType.DEFENSE), damageIn, null, true);
}
if (isCreature()) {
if (source.isWitherDamage()) { // 120.3d

View File

@@ -2568,7 +2568,7 @@ public class CardFactoryUtil {
} else if (keyword.equals("Sunburst")) {
// Rule 702.43a If this object is entering the battlefield as a creature,
// ignoring any type-changing effects that would affect it
CounterType t = host.isCreature() ? CounterEnumType.P1P1 : CounterEnumType.CHARGE;
CounterType t = CounterType.get(host.isCreature() ? CounterEnumType.P1P1 : CounterEnumType.CHARGE);
StringBuilder sb = new StringBuilder("etbCounter:");
sb.append(t).append(":Sunburst:no Condition:");

View File

@@ -213,10 +213,16 @@ public final class CardPredicates {
public static Predicate<Card> hasCounter(final CounterType type) {
return hasCounter(type, 1);
}
public static Predicate<Card> hasCounter(final CounterEnumType type) {
return hasCounter(type, 1);
}
public static Predicate<Card> hasCounter(final CounterType type, final int n) {
return c -> c.getCounters(type) >= n;
}
public static Predicate<Card> hasCounter(final CounterEnumType type, final int n) {
return hasCounter(CounterType.get(type), n);
}
public static Predicate<Card> hasLessCounter(final CounterType type, final int n) {
return c -> {
@@ -224,10 +230,16 @@ public final class CardPredicates {
return x > 0 && x <= n;
};
}
public static Predicate<Card> hasLessCounter(final CounterEnumType type, final int n) {
return hasLessCounter(CounterType.get(type), n);
}
public static Predicate<Card> canReceiveCounters(final CounterType counter) {
return c -> c.canReceiveCounters(counter);
}
public static Predicate<Card> canReceiveCounters(final CounterEnumType counter) {
return canReceiveCounters(CounterType.get(counter));
}
public static Predicate<Card> hasGreaterPowerThan(final int minPower) {
return c -> c.getNetPower() > minPower;
@@ -236,6 +248,9 @@ public final class CardPredicates {
public static Comparator<Card> compareByCounterType(final CounterType type) {
return Comparator.comparingInt(arg0 -> arg0.getCounters(type));
}
public static Comparator<Card> compareByCounterType(final CounterEnumType type) {
return compareByCounterType(CounterType.get(type));
}
public static Predicate<Card> hasSVar(final String name) {
return c -> c.hasSVar(name);

View File

@@ -605,18 +605,18 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
result.add(loyaltyRep);
}
if (type.isBattle()) {
// TODO This is currently breaking for Battle/Defense
// Going to script the cards to work but ideally it would happen here
if (defenseRep == null) {
defenseRep = CardFactoryUtil.makeEtbCounter("etbCounter:DEFENSE:" + this.baseDefense, this, true);
}
result.add(defenseRep);
// TODO add Siege "Choose a player to protect it"
}
card.updateReplacementEffects(result, this);
// below are global rules
if (type.hasSubtype("Saga") && !hasKeyword(Keyword.READ_AHEAD)) {
if (sagaRep == null) {
sagaRep = CardFactoryUtil.makeEtbCounter("etbCounter:LORE:1", this, false);
sagaRep = CardFactoryUtil.makeEtbCounter("etbCounter:LORE:1", this, true);
}
result.add(sagaRep);
}
@@ -633,6 +633,7 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
result.add(omenRep);
}
card.updateReplacementEffects(result, this);
return result;
}
public boolean addReplacementEffect(final ReplacementEffect replacementEffect) {

View File

@@ -1,5 +1,6 @@
package forge.game.card;
import com.google.common.collect.Lists;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
@@ -7,45 +8,74 @@ import forge.game.trigger.Trigger;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public record CardTraitChanges(Collection<SpellAbility> abilities, Collection<SpellAbility> removedAbilities,
Collection<Trigger> triggers, Collection<ReplacementEffect> replacements, Collection<StaticAbility> staticAbilities,
public class CardTraitChanges implements Cloneable {
private List<Trigger> triggers = Lists.newArrayList();
private List<ReplacementEffect> replacements = Lists.newArrayList();
private List<SpellAbility> abilities = Lists.newArrayList();
private List<StaticAbility> staticAbilities = Lists.newArrayList();
private List<SpellAbility> removedAbilities = Lists.newArrayList();
private boolean removeAll = false;
private boolean removeNonMana = false;
public CardTraitChanges(Collection<SpellAbility> spells, Collection<SpellAbility> removedAbilities,
Collection<Trigger> trigger, Collection<ReplacementEffect> res, Collection<StaticAbility> st,
boolean removeAll, boolean removeNonMana) {
if (spells != null) {
this.abilities.addAll(spells);
}
if (removedAbilities != null) {
this.removedAbilities.addAll(removedAbilities);
}
if (trigger != null) {
this.triggers.addAll(trigger);
}
if (res != null) {
this.replacements.addAll(res);
}
if (st != null) {
this.staticAbilities.addAll(st);
}
this.removeAll |= removeAll;
this.removeNonMana |= removeNonMana;
}
/**
* @return the triggers
*/
public Collection<Trigger> getTriggers() {
return Objects.requireNonNullElse(triggers, List.of());
return triggers;
}
/**
* @return the replacements
*/
public Collection<ReplacementEffect> getReplacements() {
return Objects.requireNonNullElse(replacements, List.of());
return replacements;
}
/**
* @return the abilities
*/
public Collection<SpellAbility> getAbilities() {
return Objects.requireNonNullElse(abilities, List.of());
return abilities;
}
/**
* @return the abilities
*/
public Collection<SpellAbility> getRemovedAbilities() {
return Objects.requireNonNullElse(removedAbilities, List.of());
return removedAbilities;
}
/**
* @return the staticAbilities
*/
public Collection<StaticAbility> getStaticAbilities() {
return Objects.requireNonNullElse(staticAbilities, List.of());
return staticAbilities;
}
public boolean isRemoveAll() {
@@ -57,30 +87,53 @@ public record CardTraitChanges(Collection<SpellAbility> abilities, Collection<Sp
}
public CardTraitChanges copy(Card host, boolean lki) {
return new CardTraitChanges(
this.getAbilities().stream().map(sa -> sa.copy(host, lki)).collect(Collectors.toList()),
this.getRemovedAbilities().stream().map(sa -> sa.copy(host, lki)).collect(Collectors.toList()),
this.getTriggers().stream().map(tr -> tr.copy(host, lki)).collect(Collectors.toList()),
this.getReplacements().stream().map(tr -> tr.copy(host, lki)).collect(Collectors.toList()),
this.getStaticAbilities().stream().map(st -> st.copy(host, lki)).collect(Collectors.toList()),
removeAll, removeNonMana
);
try {
CardTraitChanges result = (CardTraitChanges) super.clone();
result.abilities = Lists.newArrayList();
for (SpellAbility sa : this.abilities) {
result.abilities.add(sa.copy(host, lki));
}
result.removedAbilities = Lists.newArrayList();
for (SpellAbility sa : this.removedAbilities) {
result.removedAbilities.add(sa.copy(host, lki));
}
result.triggers = Lists.newArrayList();
for (Trigger tr : this.triggers) {
result.triggers.add(tr.copy(host, lki));
}
result.replacements = Lists.newArrayList();
for (ReplacementEffect re : this.replacements) {
result.replacements.add(re.copy(host, lki));
}
result.staticAbilities = Lists.newArrayList();
for (StaticAbility sa : this.staticAbilities) {
result.staticAbilities.add(sa.copy(host, lki));
}
return result;
} catch (final Exception ex) {
throw new RuntimeException("CardTraitChanges : clone() error", ex);
}
}
public void changeText() {
for (SpellAbility sa : this.getAbilities()) {
for (SpellAbility sa : this.abilities) {
sa.changeText();
}
for (Trigger tr : this.getTriggers()) {
for (Trigger tr : this.triggers) {
tr.changeText();
}
for (ReplacementEffect re : this.getReplacements()) {
for (ReplacementEffect re : this.replacements) {
re.changeText();
}
for (StaticAbility sa : this.getStaticAbilities()) {
for (StaticAbility sa : this.staticAbilities) {
sa.changeText();
}
}

View File

@@ -28,7 +28,7 @@ import java.util.Locale;
* @author Clemens Koza
* @version V0.0 17.02.2010
*/
public enum CounterEnumType implements CounterType {
public enum CounterEnumType {
M1M1("-1/-1", "-1/-1", 255, 110, 106),
P1P1("+1/+1", "+1/+1", 96, 226, 23),
@@ -555,14 +555,4 @@ public enum CounterEnumType implements CounterType {
public static final ImmutableList<CounterEnumType> values = ImmutableList.copyOf(values());
@Override
public boolean is(CounterEnumType eType) {
return this == eType;
}
@Override
public boolean isKeywordCounter() {
return false;
}
}

View File

@@ -1,74 +0,0 @@
package forge.game.card;
import java.util.Map;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
public record CounterKeywordType(String keyword) implements CounterType {
// Rule 122.1b
static ImmutableList<String> keywordCounter = ImmutableList.of(
"Flying", "First Strike", "Double Strike", "Deathtouch", "Decayed", "Exalted", "Haste", "Hexproof",
"Indestructible", "Lifelink", "Menace", "Reach", "Shadow", "Trample", "Vigilance");
private static Map<String, CounterKeywordType> sMap = Maps.newHashMap();
public static CounterKeywordType get(String s) {
if (!sMap.containsKey(s)) {
sMap.put(s, new CounterKeywordType(s));
}
return sMap.get(s);
}
@Override
public String toString() {
return keyword;
}
public String getName() {
return getKeywordDescription();
}
public String getCounterOnCardDisplayName() {
return getKeywordDescription();
}
private String getKeywordDescription() {
if (keyword.startsWith("Hexproof:")) {
final String[] k = keyword.split(":");
return "Hexproof from " + k[2];
}
if (keyword.startsWith("Trample:")) {
return "Trample over Planeswalkers";
}
return keyword;
}
public boolean is(CounterEnumType eType) {
return false;
}
public boolean isKeywordCounter() {
if (keyword.startsWith("Hexproof:")) {
return true;
}
if (keyword.startsWith("Trample:")) {
return true;
}
return keywordCounter.contains(keyword);
}
public int getRed() {
return 255;
}
public int getGreen() {
return 255;
}
public int getBlue() {
return 255;
}
}

View File

@@ -1,31 +1,141 @@
package forge.game.card;
import java.io.Serializable;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import org.apache.commons.lang3.builder.EqualsBuilder;
public interface CounterType extends Serializable {
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
public class CounterType implements Comparable<CounterType>, Serializable {
private static final long serialVersionUID = -7575835723159144478L;
private CounterEnumType eVal = null;
private String sVal = null;
// Rule 122.1b
static ImmutableList<String> keywordCounter = ImmutableList.of(
"Flying", "First Strike", "Double Strike", "Deathtouch", "Decayed", "Exalted", "Haste", "Hexproof",
"Indestructible", "Lifelink", "Menace", "Reach", "Shadow", "Trample", "Vigilance");
private static Map<CounterEnumType, CounterType> eMap = Maps.newEnumMap(CounterEnumType.class);
private static Map<String, CounterType> sMap = Maps.newHashMap();
private CounterType(CounterEnumType e, String s) {
this.eVal = e;
this.sVal = s;
}
public static CounterType get(CounterEnumType e) {
if (!eMap.containsKey(e)) {
eMap.put(e, new CounterType(e, null));
}
return eMap.get(e);
}
public static CounterType get(String s) {
if (!sMap.containsKey(s)) {
sMap.put(s, new CounterType(null, s));
}
return sMap.get(s);
}
public static CounterType getType(String name) {
if ("Any".equalsIgnoreCase(name)) {
return null;
}
try {
return CounterEnumType.getType(name);
return get(CounterEnumType.getType(name));
} catch (final IllegalArgumentException ex) {
return CounterKeywordType.get(name);
return get(name);
}
}
public String getName();
public String getCounterOnCardDisplayName();
public boolean is(CounterEnumType eType);
public boolean isKeywordCounter();
public int getRed();
public int getGreen();
public int getBlue();
@Override
public int hashCode() {
return Objects.hash(eVal, sVal);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
CounterType rhs = (CounterType) obj;
return new EqualsBuilder()
.append(eVal, rhs.eVal)
.append(sVal, rhs.sVal)
.isEquals();
}
@Override
public String toString() {
return eVal != null ? eVal.toString() : sVal;
}
public String getName() {
return eVal != null ? eVal.getName() : getKeywordDescription();
}
public String getCounterOnCardDisplayName() {
return eVal != null ? eVal.getCounterOnCardDisplayName() : getKeywordDescription();
}
private String getKeywordDescription() {
if (sVal.startsWith("Hexproof:")) {
final String[] k = sVal.split(":");
return "Hexproof from " + k[2];
}
if (sVal.startsWith("Trample:")) {
return "Trample over Planeswalkers";
}
return sVal;
}
@Override
public int compareTo(CounterType o) {
return ComparisonChain.start()
.compare(eVal, o.eVal, Ordering.natural().nullsLast())
.compare(sVal, o.sVal, Ordering.natural().nullsLast())
.result();
}
public boolean is(CounterEnumType eType) {
return eVal == eType;
}
public boolean isKeywordCounter() {
if (eVal != null) {
return false;
}
if (sVal.startsWith("Hexproof:")) {
return true;
}
if (sVal.startsWith("Trample:")) {
return true;
}
return keywordCounter.contains(sVal);
}
public int getRed() {
return eVal != null ? eVal.getRed() : 255;
}
public int getGreen() {
return eVal != null ? eVal.getGreen() : 255;
}
public int getBlue() {
return eVal != null ? eVal.getBlue() : 255;
}
}

View File

@@ -1,26 +0,0 @@
package forge.game.card.perpetual;
import forge.card.mana.ManaCost;
import forge.game.card.Card;
import forge.game.cost.Cost;
public record PerpetualManaCost(long timestamp, ManaCost manaCost) implements PerpetualInterface {
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void applyEffect(Card c) {
c.addChangedManaCost(manaCost, timestamp, (long) 0);
c.updateManaCostForView();
if (c.getFirstSpellAbility() != null) {
Cost cost = c.getFirstSpellAbility().getPayCosts().copyWithDefinedMana(manaCost);
c.getFirstSpellAbility().setPayCosts(cost);
}
}
}

View File

@@ -621,11 +621,8 @@ public class Cost implements Serializable {
}
public final Cost copyWithDefinedMana(String manaCost) {
return copyWithDefinedMana(new ManaCost(new ManaCostParser(manaCost)));
}
public final Cost copyWithDefinedMana(ManaCost manaCost) {
Cost toRet = copyWithNoMana();
toRet.costParts.add(new CostPartMana(manaCost, null));
toRet.costParts.add(new CostPartMana(new ManaCost(new ManaCostParser(manaCost)), null));
toRet.cacheTapCost();
return toRet;
}
@@ -997,9 +994,9 @@ public class Cost implements Serializable {
Integer counters = otherAmount - part.convertAmount();
// the cost can turn positive if multiple Carth raise it
if (counters < 0) {
costParts.add(new CostPutCounter(String.valueOf(counters *-1), CounterEnumType.LOYALTY, part.getType(), part.getTypeDescription()));
costParts.add(new CostPutCounter(String.valueOf(counters *-1), CounterType.get(CounterEnumType.LOYALTY), part.getType(), part.getTypeDescription()));
} else {
costParts.add(new CostRemoveCounter(String.valueOf(counters), CounterEnumType.LOYALTY, part.getType(), part.getTypeDescription(), Lists.newArrayList(ZoneType.Battlefield) , false));
costParts.add(new CostRemoveCounter(String.valueOf(counters), CounterType.get(CounterEnumType.LOYALTY), part.getType(), part.getTypeDescription(), Lists.newArrayList(ZoneType.Battlefield) , false));
}
} else {
continue;

View File

@@ -22,6 +22,7 @@ import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
@@ -80,7 +81,7 @@ public class CostUntap extends CostPart {
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
final Card source = ability.getHostCard();
return source.isTapped() && !source.isAbilitySick() &&
(source.getCounters(CounterEnumType.STUN) == 0 || source.canRemoveCounters(CounterEnumType.STUN));
(source.getCounters(CounterEnumType.STUN) == 0 || source.canRemoveCounters(CounterType.get(CounterEnumType.STUN)));
}
@Override

View File

@@ -86,7 +86,7 @@ public class CostUntapType extends CostPartWithList {
if (!canUntapSource) {
typeList.remove(source);
}
typeList = CardLists.filter(typeList, CardPredicates.TAPPED, c -> c.getCounters(CounterEnumType.STUN) == 0 || c.canRemoveCounters(CounterEnumType.STUN));
typeList = CardLists.filter(typeList, CardPredicates.TAPPED, c -> c.getCounters(CounterEnumType.STUN) == 0 || c.canRemoveCounters(CounterType.get(CounterEnumType.STUN)));
final int amount = this.getAbilityAmount(ability);
return (typeList.size() != 0) && (typeList.size() >= amount);

View File

@@ -1,5 +1,5 @@
package forge.game.event;
public interface Event {
public abstract class Event {
}

View File

@@ -1,5 +1,9 @@
package forge.game.event;
/**
* TODO: Write javadoc for this type.
*
*/
public enum EventValueChangeType {
Added,
Removed,

View File

@@ -1,6 +1,6 @@
package forge.game.event;
public interface GameEvent extends Event {
public abstract class GameEvent extends Event {
public abstract <T> T visit(IGameEventVisitor<T> visitor);
}

View File

@@ -5,7 +5,11 @@ import com.google.common.collect.Multimap;
import forge.game.card.Card;
import forge.game.player.Player;
public record GameEventAnteCardsSelected(Multimap<Player, Card> cards) implements GameEvent {
public class GameEventAnteCardsSelected extends GameEvent {
public final Multimap<Player, Card> cards;
public GameEventAnteCardsSelected(Multimap<Player, Card> list) {
cards = list;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -6,21 +6,27 @@ import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.player.Player;
public record GameEventAttackersDeclared(Player player, Multimap<GameEntity, Card> attackersMap) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventAttackersDeclared extends GameEvent {
public final Player player;
public final Multimap<GameEntity, Card> attackersMap;
public GameEventAttackersDeclared(Player playerTurn, Multimap<GameEntity, Card> attackersMap) {
this.player = playerTurn;
this.attackersMap = attackersMap;
}
/* (non-Javadoc)
* @see forge.game.event.GameEvent#visit(forge.game.event.IGameEventVisitor)
*/
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
// TODO Auto-generated method stub
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + player + " declared attackers: " + attackersMap;
}
}

View File

@@ -12,10 +12,23 @@ import forge.util.Lang;
import forge.util.TextUtil;
import forge.util.maps.MapOfLists;
public record GameEventBlockersDeclared(Player defendingPlayer, Map<GameEntity, MapOfLists<Card, Card>> blockers) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventBlockersDeclared extends GameEvent {
public final Map<GameEntity, MapOfLists<Card, Card>> blockers;
public final Player defendingPlayer;
public GameEventBlockersDeclared(Player who, Map<GameEntity, MapOfLists<Card, Card>> blockers) {
this.blockers = blockers;
defendingPlayer = who;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
// TODO Auto-generated method stub
return visitor.visit(this);
}

View File

@@ -3,18 +3,20 @@ package forge.game.event;
import forge.game.GameEntity;
import forge.game.card.Card;
public record GameEventCardAttachment(Card equipment, GameEntity newTarget, GameEntity oldEntity) implements GameEvent {
public class GameEventCardAttachment extends GameEvent {
public final Card equipment;
public final GameEntity newTarget; // can enchant player, I'm ssaving a class to enchants - it could be incorrect.
public final GameEntity oldEntiy;
public GameEventCardAttachment(Card attachment, GameEntity formerEntity, GameEntity newEntity) {
this.equipment = attachment;
this.newTarget = newEntity;
this.oldEntiy = formerEntity;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return newTarget == null ? "Detached " + equipment + " from " + oldEntity : "Attached " + equipment + (oldEntity == null ? "" : " from " + oldEntity) + " to " + newTarget;
}
}

View File

@@ -4,7 +4,17 @@ import forge.game.card.Card;
import forge.game.zone.Zone;
import forge.util.TextUtil;
public record GameEventCardChangeZone(Card card, Zone from, Zone to) implements GameEvent {
public class GameEventCardChangeZone extends GameEvent {
public final Card card;
public final Zone from;
public final Zone to;
public GameEventCardChangeZone(Card c, Zone zoneFrom, Zone zoneTo) {
card = c;
from = zoneFrom;
to = zoneTo;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -3,17 +3,21 @@ package forge.game.event;
import forge.game.card.Card;
import forge.game.card.CounterType;
public record GameEventCardCounters(Card card, CounterType type, int oldValue, int newValue) implements GameEvent {
public class GameEventCardCounters extends GameEvent {
public final Card card;
public final CounterType type;
public final int oldValue;
public final int newValue;
public GameEventCardCounters(Card card, CounterType counterType, int old, int newValue) {
this.card = card;
type = counterType;
this.oldValue = old;
this.newValue = newValue;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + card + " " + type + " counters: " + oldValue + " -> " + newValue;
}
}

View File

@@ -2,7 +2,7 @@ package forge.game.event;
import forge.game.card.Card;
public record GameEventCardDamaged(Card card, Card source, int amount, DamageType type) implements GameEvent {
public class GameEventCardDamaged extends GameEvent {
public enum DamageType {
Normal,
@@ -11,16 +11,21 @@ public record GameEventCardDamaged(Card card, Card source, int amount, DamageTyp
LoyaltyLoss
}
public final Card card;
public final Card source;
public final int amount;
public final DamageType type;
public GameEventCardDamaged(Card card, Card src, int damageToAdd, DamageType damageType) {
this.card = card;
source = src;
amount = damageToAdd;
type = damageType;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + source + " dealt " + amount + " " + type + " damage to " + card;
}
}

View File

@@ -1,17 +1,10 @@
package forge.game.event;
public record GameEventCardDestroyed() implements GameEvent {
public class GameEventCardDestroyed extends GameEvent {
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Card destroyed";
}
}

View File

@@ -2,7 +2,12 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventCardForetold(Player activatingPlayer) implements GameEvent {
public class GameEventCardForetold extends GameEvent {
public final Player activatingPlayer;
public GameEventCardForetold(Player player) {
activatingPlayer = player;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -2,7 +2,21 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventCardModeChosen(Player player, String cardName, String mode, boolean log, boolean random) implements GameEvent {
public class GameEventCardModeChosen extends GameEvent {
public final Player player;
public final String cardName;
public final String mode;
public final boolean log;
public final boolean random;
public GameEventCardModeChosen(Player player, String cardName, String mode, boolean log, boolean random) {
this.player = player;
this.cardName = cardName;
this.mode = mode;
this.log = log;
this.random = random;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -2,7 +2,19 @@ package forge.game.event;
import forge.game.card.Card;
public record GameEventCardPhased(Card card, boolean phaseState) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventCardPhased extends GameEvent {
public final Card card;
public final boolean phaseState;
public GameEventCardPhased(Card card, boolean state) {
this.card = card;
phaseState = state;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -3,7 +3,16 @@ package forge.game.event;
import forge.game.card.Card;
import forge.game.player.Player;
public record GameEventCardPlotted(Card card, Player activatingPlayer) implements GameEvent {
public class GameEventCardPlotted extends GameEvent {
public final Card card;
public final Player activatingPlayer;
public GameEventCardPlotted(Card card, Player player) {
this.card = card;
activatingPlayer = player;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -5,9 +5,11 @@ import forge.game.card.Card;
import java.util.Arrays;
import java.util.Collection;
public record GameEventCardRegenerated(Collection<Card> cards) implements GameEvent {
public class GameEventCardRegenerated extends GameEvent {
public final Collection<Card> cards;
public GameEventCardRegenerated(Card affected) {
this(Arrays.asList(affected));
cards = Arrays.asList(affected);
}
@Override

View File

@@ -1,19 +1,9 @@
package forge.game.event;
import forge.game.card.Card;
public record GameEventCardSacrificed(Card card) implements GameEvent {
public class GameEventCardSacrificed extends GameEvent {
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + card.getController() + " sacrificed " + card;
}
}

View File

@@ -11,20 +11,23 @@ import forge.game.card.Card;
/**
* This means card's characteristics have changed on server, clients must re-request them
*/
public record GameEventCardStatsChanged(Collection<Card> cards, boolean transform) implements GameEvent {
public class GameEventCardStatsChanged extends GameEvent {
public final Collection<Card> cards;
public boolean transform = false;
public GameEventCardStatsChanged(Card affected) {
this(affected, false);
}
public GameEventCardStatsChanged(Card affected, boolean isTransform) {
this(Arrays.asList(affected), false);
cards = Arrays.asList(affected);
//the transform should only fire once so the flip effect sound will trigger once every transformation...
// disable for now
transform = false;
}
public GameEventCardStatsChanged(Collection<Card> affected) {
this(affected, false);
cards = affected;
}
/* (non-Javadoc)
@@ -32,6 +35,7 @@ public record GameEventCardStatsChanged(Collection<Card> cards, boolean transfor
*/
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
// TODO Auto-generated method stub
return visitor.visit(this);
}

View File

@@ -2,18 +2,17 @@ package forge.game.event;
import forge.game.card.Card;
public record GameEventCardTapped(Card card, boolean tapped) implements GameEvent {
public class GameEventCardTapped extends GameEvent {
public final boolean tapped;
public final Card card;
public GameEventCardTapped(final Card card, final boolean tapped) {
this.tapped = tapped;
this.card = card;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + card.getController() + (tapped ? " tapped " : " untapped ") + card;
}
}

View File

@@ -1,17 +1,13 @@
package forge.game.event;
public record GameEventCombatChanged() implements GameEvent {
public class GameEventCombatChanged extends GameEvent {
public GameEventCombatChanged() {
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Combat changed";
}
}

View File

@@ -4,18 +4,19 @@ import java.util.List;
import forge.game.card.Card;
public record GameEventCombatEnded(List<Card> attackers, List<Card> blockers) implements GameEvent {
public class GameEventCombatEnded extends GameEvent {
public final List<Card> attackers;
public final List<Card> blockers;
public GameEventCombatEnded(List<Card> attackers, List<Card> blockers) {
this.attackers = attackers;
this.blockers = blockers;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Combat ended. Attackers: " + attackers + " Blockers: " + blockers;
}
}

View File

@@ -4,7 +4,15 @@ import java.util.List;
import forge.game.card.Card;
public record GameEventCombatUpdate(List<Card> attackers, List<Card> blockers) implements GameEvent {
public class GameEventCombatUpdate extends GameEvent {
public final List<Card> attackers;
public final List<Card> blockers;
public GameEventCombatUpdate(List<Card> attackers, List<Card> blockers) {
this.attackers = attackers;
this.blockers = blockers;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -1,6 +1,11 @@
package forge.game.event;
public record GameEventDayTimeChanged(boolean daytime) implements GameEvent {
public class GameEventDayTimeChanged extends GameEvent {
public final boolean daytime;
public GameEventDayTimeChanged(final boolean daytime) {
this.daytime = daytime;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -6,7 +6,18 @@ import forge.game.player.Player;
import forge.util.CardTranslation;
import forge.util.Lang;
public record GameEventDoorChanged(Player activatingPlayer, Card card, CardStateName state, boolean unlock) implements GameEvent {
public class GameEventDoorChanged extends GameEvent {
public final Player activatingPlayer;
public final Card card;
public final CardStateName state;
public boolean unlock;
public GameEventDoorChanged(Player player, Card c, CardStateName state, boolean unlock) {
activatingPlayer = player;
card = c;
this.state = state;
this.unlock = unlock;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -1,17 +1,9 @@
package forge.game.event;
public record GameEventFlipCoin() implements GameEvent {
public class GameEventFlipCoin extends GameEvent {
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Flipped coin";
}
}

View File

@@ -1,17 +1,8 @@
package forge.game.event;
public record GameEventGameFinished() implements GameEvent {
public class GameEventGameFinished extends GameEvent {
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Game finished";
}
} // need this class to launch after log was built via previous event

View File

@@ -4,18 +4,17 @@ import java.util.Collection;
import forge.game.GameOutcome;
public record GameEventGameOutcome(GameOutcome result, Collection<GameOutcome> history) implements GameEvent {
public class GameEventGameOutcome extends GameEvent {
public final GameOutcome result;
public final Collection<GameOutcome> history;
public GameEventGameOutcome(GameOutcome lastOne, Collection<GameOutcome> history) {
this.result = lastOne;
this.history = history;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Game Outcome: " + result.getOutcomeStrings();
}
}

View File

@@ -2,7 +2,17 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventGameRestarted(Player whoRestarted) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventGameRestarted extends GameEvent {
public final Player whoRestarted;
public GameEventGameRestarted(Player playerTurn) {
whoRestarted = playerTurn;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -5,7 +5,22 @@ import forge.game.player.Player;
import forge.util.Lang;
import forge.util.TextUtil;
public record GameEventGameStarted(GameType gameType, Player firstTurn, Iterable<Player> players) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventGameStarted extends GameEvent {
public final Player firstTurn;
public final Iterable<Player> players;
public final GameType gameType;
public GameEventGameStarted(GameType type, Player firstTurn, Iterable<Player> players) {
super();
this.gameType = type;
this.firstTurn = firstTurn;
this.players = players;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -3,17 +3,19 @@ package forge.game.event;
import forge.game.card.Card;
import forge.game.player.Player;
public record GameEventLandPlayed(Player player, Card land) implements GameEvent {
public class GameEventLandPlayed extends GameEvent {
public final Player player;
public final Card land;
public GameEventLandPlayed(Player player, Card land) {
this.player = player;
this.land = land;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + player + " played " + land;
}
}

View File

@@ -3,7 +3,22 @@ package forge.game.event;
import forge.game.player.Player;
// This special event denotes loss of mana due to phase end
public record GameEventManaBurn(Player player, boolean causedLifeLoss, int amount) implements GameEvent {
public class GameEventManaBurn extends GameEvent {
public final Player player;
public final boolean causedLifeLoss;
public final int amount;
/**
* TODO: Write javadoc for Constructor.
* @param dealDamage
* @param burn
*/
public GameEventManaBurn(Player who, int burn, boolean dealDamage) {
player = who;
amount = burn;
causedLifeLoss = dealDamage;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -4,7 +4,20 @@ import forge.game.mana.Mana;
import forge.game.player.Player;
import forge.util.Lang;
public record GameEventManaPool(Player player, EventValueChangeType mode, Mana mana) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventManaPool extends GameEvent {
public final Player player;
public final EventValueChangeType mode;
public final Mana mana;
public GameEventManaPool(Player owner, EventValueChangeType changeMode, Mana mana) {
this.mana = mana;
player = owner;
mode = changeMode;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -2,18 +2,19 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventMulligan(Player player) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventMulligan extends GameEvent {
public final Player player;
public GameEventMulligan(Player p) {
player = p;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + player + " mulligans";
}
}

View File

@@ -4,18 +4,23 @@ import forge.LobbyPlayer;
import forge.game.player.Player;
import forge.game.player.PlayerController;
public record GameEventPlayerControl(Player player, LobbyPlayer oldLobbyPlayer, PlayerController oldController, LobbyPlayer newLobbyPlayer, PlayerController newController) implements GameEvent {
public class GameEventPlayerControl extends GameEvent {
public final Player player;
public final LobbyPlayer oldLobbyPlayer;
public final PlayerController oldController;
public final LobbyPlayer newLobbyPlayer;
public final PlayerController newController;
public GameEventPlayerControl(final Player p, final LobbyPlayer oldLobbyPlayer, final PlayerController oldController, final LobbyPlayer newLobbyPlayer, final PlayerController newController) {
this.player = p;
this.oldLobbyPlayer = oldLobbyPlayer;
this.oldController = oldController;
this.newLobbyPlayer = newLobbyPlayer;
this.newController = newController;
}
@Override
public <T> T visit(final IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + player + " controlled by " + player.getControllingPlayer();
}
}

View File

@@ -3,18 +3,21 @@ package forge.game.event;
import forge.game.card.CounterType;
import forge.game.player.Player;
public record GameEventPlayerCounters(Player receiver, CounterType type, int oldValue, int amount) implements GameEvent {
public class GameEventPlayerCounters extends GameEvent {
public final Player receiver;
public final CounterType type;
public final int oldValue;
public final int amount;
public GameEventPlayerCounters(Player recv, CounterType t, int old, int num) {
receiver = recv;
type = t;
oldValue = old;
amount = num;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + receiver + " got " + oldValue + " plus " + amount + " " + type;
}
}

View File

@@ -3,7 +3,33 @@ package forge.game.event;
import forge.game.card.Card;
import forge.game.player.Player;
public record GameEventPlayerDamaged(Player target, Card source, int amount, boolean combat, boolean infect) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventPlayerDamaged extends GameEvent {
public final Player target;
public final Card source;
public final int amount;
final public boolean infect;
public final boolean combat;
/**
* TODO: Write javadoc for Constructor.
* @param player
* @param source
* @param amount
* @param isCombat
* @param infect
*/
public GameEventPlayerDamaged(Player player, Card source, int amount, boolean isCombat, boolean infect) {
target = player;
this.source = source;
this.amount = amount;
combat = isCombat;
this.infect = infect;
}
/* (non-Javadoc)
* @see forge.game.event.GameEvent#visit(forge.game.event.IGameEventVisitor)
@@ -13,11 +39,4 @@ public record GameEventPlayerDamaged(Player target, Card source, int amount, boo
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + target + " took " + amount + (infect ? " infect" : combat ? " combat" : "") + " damage from " + source;
}
}

View File

@@ -4,7 +4,16 @@ import forge.game.player.Player;
import forge.util.Lang;
import forge.util.TextUtil;
public record GameEventPlayerLivesChanged(Player player, int oldLives, int newLives) implements GameEvent {
public class GameEventPlayerLivesChanged extends GameEvent {
public final Player player;
public final int oldLives;
public final int newLives;
public GameEventPlayerLivesChanged(Player who, int oldValue, int newValue) {
player = who;
oldLives = oldValue;
newLives = newValue;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -6,7 +6,18 @@ import forge.game.player.Player;
*
*
*/
public record GameEventPlayerPoisoned(Player receiver, Player source, int oldValue, int amount) implements GameEvent {
public class GameEventPlayerPoisoned extends GameEvent {
public final Player receiver;
public final Player source;
public final int oldValue;
public final int amount;
public GameEventPlayerPoisoned(Player recv, Player src, int old, int num) {
receiver = recv;
source = src;
oldValue = old;
amount = num;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -4,7 +4,21 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.util.TextUtil;
public record GameEventPlayerPriority(Player turn, PhaseType phase, Player priority) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventPlayerPriority extends GameEvent {
public final Player turn;
public final PhaseType phase;
public final Player priority;
public GameEventPlayerPriority(Player playerTurn, PhaseType phase, Player priorityPlayer) {
turn = playerTurn;
this.phase = phase;
priority = priorityPlayer;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -2,7 +2,16 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventPlayerRadiation(Player receiver, Player source, int change) implements GameEvent {
public class GameEventPlayerRadiation extends GameEvent {
public final Player receiver;
public final Player source;
public final int change;
public GameEventPlayerRadiation(Player recv, Player src, int chng) {
receiver = recv;
source = src;
change = chng;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -4,7 +4,16 @@ import forge.game.player.Player;
import forge.util.Lang;
import forge.util.TextUtil;
public record GameEventPlayerShardsChanged(Player player, int oldShards, int newShards) implements GameEvent {
public class GameEventPlayerShardsChanged extends GameEvent {
public final Player player;
public final int oldShards;
public final int newShards;
public GameEventPlayerShardsChanged(Player who, int oldValue, int newValue) {
player = who;
oldShards = oldValue;
newShards = newValue;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -10,10 +10,18 @@ import forge.util.TextUtil;
/**
* This means card's characteristics have changed on server, clients must re-request them
*/
public record GameEventPlayerStatsChanged(Collection<Player> players, boolean updateCards) implements GameEvent {
public class GameEventPlayerStatsChanged extends GameEvent {
public final Collection<Player> players;
public final boolean updateCards;
public GameEventPlayerStatsChanged(Player affected, boolean updateCards) {
this(Arrays.asList(affected), updateCards);
players = Arrays.asList(affected);
this.updateCards = updateCards;
}
public GameEventPlayerStatsChanged(Collection<Player> affected, boolean updateCards) {
players = affected;
this.updateCards = updateCards;
}
/* (non-Javadoc)
@@ -21,6 +29,7 @@ public record GameEventPlayerStatsChanged(Collection<Player> players, boolean up
*/
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
// TODO Auto-generated method stub
return visitor.visit(this);
}

View File

@@ -1,6 +1,12 @@
package forge.game.event;
public record GameEventRandomLog(String message) implements GameEvent {
public class GameEventRandomLog extends GameEvent {
public final String message;
public GameEventRandomLog(String message) {
this.message = message;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -1,6 +1,6 @@
package forge.game.event;
public record GameEventRollDie() implements GameEvent {
public class GameEventRollDie extends GameEvent {
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

View File

@@ -2,18 +2,19 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventScry(Player player, int toTop, int toBottom) implements GameEvent {
public class GameEventScry extends GameEvent {
public final Player player;
public final int toTop, toBottom;
public GameEventScry(Player player, int toTop, int toBottom) {
this.player = player;
this.toTop = toTop;
this.toBottom = toBottom;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + player + " scried " + toTop + " to top, " + toBottom + " to bottom";
}
}

View File

@@ -4,7 +4,13 @@ import forge.game.player.Player;
import forge.util.Lang;
import forge.util.TextUtil;
public record GameEventShuffle(Player player) implements GameEvent {
public class GameEventShuffle extends GameEvent {
public final Player player;
public GameEventShuffle(Player player) {
this.player = player;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
@@ -16,6 +22,6 @@ public record GameEventShuffle(Player player) implements GameEvent {
*/
@Override
public String toString() {
return TextUtil.concatWithSpace(player.toString(), Lang.joinVerb(player.getName(), "shuffle"), "their library");
return TextUtil.concatWithSpace(player.toString(), Lang.joinVerb(player.getName(), "shuffle"),"his/her/its library");
}
}

View File

@@ -2,7 +2,18 @@ package forge.game.event;
import forge.game.player.Player;
public record GameEventSpeedChanged(Player player, int oldValue, int newValue) implements GameEvent {
public class GameEventSpeedChanged extends GameEvent {
public final Player player;
public final int oldValue;
public final int newValue;
public GameEventSpeedChanged(Player affected, int oldValue, int newValue) {
player = affected;
this.oldValue = oldValue;
this.newValue = newValue;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);

View File

@@ -3,7 +3,21 @@ package forge.game.event;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
public record GameEventSpellAbilityCast(SpellAbility sa, SpellAbilityStackInstance si, int stackIndex) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventSpellAbilityCast extends GameEvent {
public final SpellAbility sa;
public final SpellAbilityStackInstance si;
public final int stackIndex;
public GameEventSpellAbilityCast(SpellAbility sp, SpellAbilityStackInstance si, int stackIndex) {
sa = sp;
this.si = si;
this.stackIndex = stackIndex;
}
/* (non-Javadoc)
* @see forge.game.event.GameEvent#visit(forge.game.event.IGameEventVisitor)
@@ -13,11 +27,4 @@ public record GameEventSpellAbilityCast(SpellAbility sa, SpellAbilityStackInstan
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "" + sa.getActivatingPlayer() + (sa.isSpell() ? " cast " : sa.isActivatedAbility() ? " activated " : " triggered ") + sa;
}
}

View File

@@ -2,18 +2,21 @@ package forge.game.event;
import forge.game.spellability.SpellAbility;
public record GameEventSpellRemovedFromStack(SpellAbility sa) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventSpellRemovedFromStack extends GameEvent {
public final SpellAbility sa;
public GameEventSpellRemovedFromStack(SpellAbility spellAbility) {
sa = spellAbility;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
// TODO Auto-generated method stub
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Stack removed " + sa;
}
}

View File

@@ -2,18 +2,29 @@ package forge.game.event;
import forge.game.spellability.SpellAbility;
public record GameEventSpellResolved(SpellAbility spell, boolean hasFizzled) implements GameEvent {
/**
* TODO: Write javadoc for this type.
*
*/
public class GameEventSpellResolved extends GameEvent {
public final SpellAbility spell;
public final boolean hasFizzled;
/**
* TODO: Write javadoc for Constructor.
* @param source
* @param sa
* @param hasFizzled
*/
public GameEventSpellResolved(SpellAbility sa, boolean hasFizzled) {
// TODO Auto-generated constructor stub
this.spell = sa;
this.hasFizzled = hasFizzled;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Stack resolved " + spell + (hasFizzled ? " (fizzled)" : "");
}
}

View File

@@ -2,7 +2,18 @@ package forge.game.event;
import forge.game.card.Card;
public record GameEventSprocketUpdate(Card contraption, int oldSprocket, int sprocket) implements GameEvent {
public class GameEventSprocketUpdate extends GameEvent {
public final Card contraption;
public final int oldSprocket;
public final int sprocket;
public GameEventSprocketUpdate(Card contraption, int oldSprocket, int sprocket) {
this.contraption = contraption;
this.oldSprocket = oldSprocket;
this.sprocket = sprocket;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {

Some files were not shown because too many files have changed in this diff Show More