mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 01:38:13 +00:00
Compare commits
1 Commits
cantTarget
...
precalc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f3a9cd22e |
2
.github/workflows/test-build.yaml
vendored
2
.github/workflows/test-build.yaml
vendored
@@ -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
3
.gitignore
vendored
@@ -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
|
||||
|
||||
33
.gitlab/issue_templates/Bug.md
Normal file
33
.gitlab/issue_templates/Bug.md
Normal 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
|
||||
15
.gitlab/issue_templates/Feature.md
Normal file
15
.gitlab/issue_templates/Feature.md
Normal 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
|
||||
@@ -15,7 +15,7 @@ public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
GuiBase.setInterface(new GuiMobile(Files.exists(Paths.get("./res"))?"./":"../forge-gui/"));
|
||||
GuiBase.setDeviceInfo(null, 0, 0, System.getProperty("user.home") + "/Downloads/");
|
||||
GuiBase.setDeviceInfo(null, 0, 0);
|
||||
new EditorMainWindow(Config.instance());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -974,13 +974,17 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pBonus = 0;
|
||||
if (ability.getApi() == ApiType.Pump) {
|
||||
if (!ability.hasParam("NumAtt")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
}
|
||||
}
|
||||
} else if (ability.getApi() == ApiType.PutCounter) {
|
||||
if (!ability.hasParam("CounterType") || !ability.getParam("CounterType").equals("P1P1")) {
|
||||
continue;
|
||||
@@ -994,11 +998,12 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
}
|
||||
|
||||
if (pBonus > 0 && ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
power += pBonus;
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1102,13 +1107,17 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
int tBonus = 0;
|
||||
if (ability.getApi() == ApiType.Pump) {
|
||||
if (!ability.hasParam("NumDef")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability);
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability);
|
||||
if (tBonus > 0) {
|
||||
toughness += tBonus;
|
||||
}
|
||||
}
|
||||
} else if (ability.getApi() == ApiType.PutCounter) {
|
||||
if (!ability.hasParam("CounterType") || !ability.getParam("CounterType").equals("P1P1")) {
|
||||
continue;
|
||||
@@ -1122,11 +1131,12 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
}
|
||||
|
||||
if (tBonus > 0 && ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
toughness += tBonus;
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController(), false)) {
|
||||
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
if (tBonus > 0) {
|
||||
toughness += tBonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return toughness;
|
||||
@@ -1295,7 +1305,6 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pBonus = 0;
|
||||
if (ability.getApi() == ApiType.Pump) {
|
||||
if (!ability.hasParam("NumAtt")) {
|
||||
continue;
|
||||
@@ -1305,8 +1314,11 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.getPayCosts().hasTapCost()) {
|
||||
pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
|
||||
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
}
|
||||
}
|
||||
} else if (ability.getApi() == ApiType.PutCounter) {
|
||||
if (!ability.hasParam("CounterType") || !ability.getParam("CounterType").equals("P1P1")) {
|
||||
@@ -1321,14 +1333,13 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.getPayCosts().hasTapCost()) {
|
||||
pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
if (pBonus > 0) {
|
||||
power += pBonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pBonus > 0 && ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
power += pBonus;
|
||||
}
|
||||
}
|
||||
return power;
|
||||
}
|
||||
@@ -1519,14 +1530,16 @@ public class ComputerUtilCombat {
|
||||
if (ability.getPayCosts().hasTapCost() && !attacker.hasKeyword(Keyword.VIGILANCE)) {
|
||||
continue;
|
||||
}
|
||||
if (!ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int tBonus = 0;
|
||||
if (ability.getApi() == ApiType.Pump) {
|
||||
if (!ability.hasParam("NumDef")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability, true);
|
||||
toughness += AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability, true);
|
||||
} else if (ability.getApi() == ApiType.PutCounter) {
|
||||
if (!ability.hasParam("CounterType") || !ability.getParam("CounterType").equals("P1P1")) {
|
||||
continue;
|
||||
@@ -1540,11 +1553,10 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
}
|
||||
|
||||
if (tBonus > 0 && ComputerUtilCost.canPayCost(ability, attacker.getController(), false)) {
|
||||
toughness += tBonus;
|
||||
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParamOrDefault("CounterNum", "1"), ability);
|
||||
if (tBonus > 0) {
|
||||
toughness += tBonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
return toughness;
|
||||
|
||||
@@ -291,12 +291,6 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
|
||||
int amount = ma.hasParam("Amount") ? AbilityUtils.calculateAmount(ma.getHostCard(), ma.getParam("Amount"), ma) : 1;
|
||||
if (amount <= 0) {
|
||||
// wrong gamestate for variable amount
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
// For abilities like Genju of the Cedars, make sure that we're not activating the aura ability by tapping the enchanted card for mana
|
||||
if (saHost.isAura() && "Enchanted".equals(sa.getParam("Defined"))
|
||||
@@ -449,6 +443,7 @@ public class ComputerUtilMana {
|
||||
manaProduced = manaProduced.replace(s, color);
|
||||
}
|
||||
} else if (saMana.hasParam("ReplaceColor")) {
|
||||
// replace color
|
||||
String color = saMana.getParam("ReplaceColor");
|
||||
if ("Chosen".equals(color)) {
|
||||
if (card.hasChosenColor()) {
|
||||
@@ -740,8 +735,7 @@ public class ComputerUtilMana {
|
||||
|
||||
if (saPayment != null && ComputerUtilCost.isSacrificeSelfCost(saPayment.getPayCosts())) {
|
||||
if (sa.getTargets() != null && sa.getTargets().contains(saPayment.getHostCard())) {
|
||||
// not a good idea to sac a card that you're targeting with the SA you're paying for
|
||||
saExcludeList.add(saPayment);
|
||||
saExcludeList.add(saPayment); // not a good idea to sac a card that you're targeting with the SA you're paying for
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1502,7 +1496,7 @@ public class ComputerUtilMana {
|
||||
}
|
||||
|
||||
if (!cost.isReusuableResource()) {
|
||||
for (CostPart part : cost.getCostParts()) {
|
||||
for(CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostSacrifice && !part.payCostFromSource()) {
|
||||
unpreferredCost = true;
|
||||
}
|
||||
@@ -1593,8 +1587,10 @@ public class ComputerUtilMana {
|
||||
|
||||
// don't use abilities with dangerous drawbacks
|
||||
AbilitySub sub = m.getSubAbility();
|
||||
if (sub != null && !SpellApiToAi.Converter.get(sub).chkDrawbackWithSubs(ai, sub).willingToPlay()) {
|
||||
continue;
|
||||
if (sub != null) {
|
||||
if (!SpellApiToAi.Converter.get(sub).chkDrawbackWithSubs(ai, sub).willingToPlay()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
manaMap.get(ManaAtom.GENERIC).add(m); // add to generic source list
|
||||
|
||||
@@ -1347,11 +1347,6 @@ public class PlayerControllerAi extends PlayerController {
|
||||
// Ai won't understand that anyway
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revealUnsupported(Map<Player, List<PaperCard>> unsupported) {
|
||||
// Ai won't understand that anyway
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<DeckSection, List<? extends PaperCard>> complainCardsCantPlayWell(Deck myDeck) {
|
||||
// TODO check if profile detection set to Auto
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,21 +608,7 @@ public class CountersPutAi extends CountersAi {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.MissingNeededCards);
|
||||
}
|
||||
|
||||
final int currCounters = cards.get(0).getCounters(CounterType.getType(type));
|
||||
|
||||
// adding counters would cause counter amount to overflow
|
||||
if (Integer.MAX_VALUE - currCounters <= amount) {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
if (type.equals("P1P1")) {
|
||||
if (Integer.MAX_VALUE - cards.get(0).getNetPower() <= amount) {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
if (Integer.MAX_VALUE - cards.get(0).getNetToughness() <= amount) {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -637,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);
|
||||
}
|
||||
}
|
||||
@@ -975,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) {
|
||||
@@ -1097,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)) {
|
||||
@@ -1115,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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(). ??? )
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class PermanentCreatureAi extends PermanentAi {
|
||||
|| ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||
&& ai.getManaPool().totalMana() <= 0
|
||||
&& (ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
|
||||
&& !card.hasETBTrigger(true) && !card.hasSVar("AmbushAI")
|
||||
&& (!card.hasETBTrigger(true) && !card.hasSVar("AmbushAI"))
|
||||
&& game.getStack().isEmpty()
|
||||
&& !ComputerUtil.castPermanentInMain1(ai, sa)) {
|
||||
// AiPlayDecision.AnotherTime;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,8 +31,6 @@ public final class ImageKeys {
|
||||
public static final String MONARCH_IMAGE = "monarch";
|
||||
public static final String THE_RING_IMAGE = "the_ring";
|
||||
public static final String RADIATION_IMAGE = "radiation";
|
||||
public static final String SPEED_IMAGE = "speed";
|
||||
public static final String MAX_SPEED_IMAGE = "max_speed";
|
||||
|
||||
public static final String BACKFACE_POSTFIX = "$alt";
|
||||
public static final String SPECFACE_W = "$wspec";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,6 +45,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
public final static char NameSetSeparator = '|';
|
||||
public final static String FlagPrefix = "#";
|
||||
public static final String FlagSeparator = "\t";
|
||||
private final String exlcudedCardName = "Concentrate";
|
||||
private final String exlcudedCardSet = "DS0";
|
||||
|
||||
// need this to obtain cardReference by name+set+artindex
|
||||
private final ListMultimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<>(String.CASE_INSENSITIVE_ORDER), Lists::newArrayList);
|
||||
@@ -301,7 +303,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
|
||||
// create faces list from rules
|
||||
for (final CardRules rule : rules.values()) {
|
||||
if (filteredCards.contains(rule.getName()))
|
||||
if (filteredCards.contains(rule.getName()) && !exlcudedCardName.equalsIgnoreCase(rule.getName()))
|
||||
continue;
|
||||
for (ICardFace face : rule.getAllFaces()) {
|
||||
addFaceToDbNames(face);
|
||||
@@ -499,9 +501,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
|
||||
public void addCard(PaperCard paperCard) {
|
||||
if (filtered.contains(paperCard.getName())) {
|
||||
if (excludeCard(paperCard.getName(), paperCard.getEdition()))
|
||||
return;
|
||||
}
|
||||
|
||||
allCardsByName.put(paperCard.getName(), paperCard);
|
||||
|
||||
@@ -522,6 +523,17 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean excludeCard(String cardName, String cardEdition) {
|
||||
if (filtered.isEmpty())
|
||||
return false;
|
||||
if (filtered.contains(cardName)) {
|
||||
if (exlcudedCardSet.equalsIgnoreCase(cardEdition) && exlcudedCardName.equalsIgnoreCase(cardName))
|
||||
return true;
|
||||
else return !exlcudedCardName.equalsIgnoreCase(cardName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reIndex() {
|
||||
uniqueCardsByName.clear();
|
||||
for (Entry<String, Collection<PaperCard>> kv : allCardsByName.asMap().entrySet()) {
|
||||
|
||||
@@ -52,14 +52,6 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public final class CardEdition implements Comparable<CardEdition> {
|
||||
|
||||
public DraftOptions getDraftOptions() {
|
||||
return draftOptions;
|
||||
}
|
||||
|
||||
public void setDraftOptions(DraftOptions draftOptions) {
|
||||
this.draftOptions = draftOptions;
|
||||
}
|
||||
|
||||
// immutable
|
||||
public enum Type {
|
||||
UNKNOWN,
|
||||
@@ -283,22 +275,18 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
// Booster/draft info
|
||||
private List<BoosterSlot> boosterSlots = null;
|
||||
private boolean smallSetOverride = false;
|
||||
private String additionalUnlockSet = "";
|
||||
private FoilType foilType = FoilType.NOT_SUPPORTED;
|
||||
|
||||
// Replace all of these things with booster slots
|
||||
private boolean foilAlwaysInCommonSlot = false;
|
||||
private FoilType foilType = FoilType.NOT_SUPPORTED;
|
||||
private double foilChanceInBooster = 0;
|
||||
private double chanceReplaceCommonWith = 0;
|
||||
private String slotReplaceCommonWith = "Common";
|
||||
private String additionalSheetForFoils = "";
|
||||
private String additionalUnlockSet = "";
|
||||
private String boosterMustContain = "";
|
||||
private String boosterReplaceSlotFromPrintSheet = "";
|
||||
private String sheetReplaceCardFromSheet = "";
|
||||
private String sheetReplaceCardFromSheet2 = "";
|
||||
|
||||
// Draft options
|
||||
private DraftOptions draftOptions = null;
|
||||
private String doublePickDuringDraft = "";
|
||||
private String[] chaosDraftThemes = new String[0];
|
||||
|
||||
private final ListMultimap<String, EditionEntry> cardMap;
|
||||
@@ -385,6 +373,7 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
public String getSlotReplaceCommonWith() { return slotReplaceCommonWith; }
|
||||
public String getAdditionalSheetForFoils() { return additionalSheetForFoils; }
|
||||
public String getAdditionalUnlockSet() { return additionalUnlockSet; }
|
||||
public String getDoublePickDuringDraft() { return doublePickDuringDraft; }
|
||||
public String getBoosterMustContain() { return boosterMustContain; }
|
||||
public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; }
|
||||
public String getSheetReplaceCardFromSheet() { return sheetReplaceCardFromSheet; }
|
||||
@@ -630,7 +619,7 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
* functional variant name - grouping #9
|
||||
*/
|
||||
// "(^(.?[0-9A-Z]+.?))?(([SCURML]) )?(.*)$"
|
||||
"(^(.?[0-9A-Z-]+\\S*[A-Z]*)\\s)?(([SCURML])\\s)?([^@\\$]*)( @([^\\$]*))?( \\$(.+))?$"
|
||||
"(^(.?[0-9A-Z-]+\\S?[A-Z]*)\\s)?(([SCURML])\\s)?([^@\\$]*)( @([^\\$]*))?( \\$(.+))?$"
|
||||
);
|
||||
|
||||
final Pattern tokenPattern = Pattern.compile(
|
||||
@@ -660,37 +649,31 @@ 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)) {
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
|
||||
// parse sections of the format "<collector number> <rarity> <name>"
|
||||
if (editionSectionsWithCollectorNumbers.contains(sectionName)) {
|
||||
for(String line : contents.get(sectionName)) {
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
|
||||
if (!matcher.matches()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String collectorNumber = matcher.group(2);
|
||||
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
||||
String cardName = matcher.group(5);
|
||||
String artistName = matcher.group(7);
|
||||
String functionalVariantName = matcher.group(9);
|
||||
EditionEntry cis = new EditionEntry(cardName, collectorNumber, r, artistName, functionalVariantName);
|
||||
|
||||
cardMap.put(sectionName, cis);
|
||||
if (!matcher.matches()) {
|
||||
continue;
|
||||
}
|
||||
} else if (boosterSlotsToParse.contains(sectionName)) {
|
||||
// parse booster slots of the format "Base=N\n|Replace=<amount> <sheet>"
|
||||
boosterSlots.add(BoosterSlot.parseSlot(sectionName, contents.get(sectionName)));
|
||||
} else {
|
||||
// save custom print sheets of the format "<amount> <name>|<setcode>|<art index>"
|
||||
// to parse later when printsheets are loaded lazily (and the cardpool is already initialized)
|
||||
customPrintSheetsToParse.put(sectionName, contents.get(sectionName));
|
||||
|
||||
String collectorNumber = matcher.group(2);
|
||||
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
||||
String cardName = matcher.group(5);
|
||||
String artistName = matcher.group(7);
|
||||
String functionalVariantName = matcher.group(9);
|
||||
EditionEntry cis = new EditionEntry(cardName, collectorNumber, r, artistName, functionalVariantName);
|
||||
|
||||
cardMap.put(sectionName, cis);
|
||||
}
|
||||
} else if (boosterSlotsToParse.contains(sectionName)) {
|
||||
// parse booster slots of the format "Base=N\n|Replace=<amount> <sheet>"
|
||||
boosterSlots.add(BoosterSlot.parseSlot(sectionName, contents.get(sectionName)));
|
||||
} else {
|
||||
// save custom print sheets of the format "<amount> <name>|<setcode>|<art index>"
|
||||
// to parse later when printsheets are loaded lazily (and the cardpool is already initialized)
|
||||
customPrintSheetsToParse.put(sectionName, contents.get(sectionName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,6 +802,7 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
res.additionalUnlockSet = metadata.get("AdditionalSetUnlockedInQuest", ""); // e.g. Time Spiral Timeshifted (TSB) for Time Spiral
|
||||
|
||||
res.smallSetOverride = metadata.getBoolean("TreatAsSmallSet", false); // for "small" sets with over 200 cards (e.g. Eldritch Moon)
|
||||
res.doublePickDuringDraft = metadata.get("DoublePick", ""); // "FirstPick" or "Always"
|
||||
|
||||
res.boosterMustContain = metadata.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature
|
||||
res.boosterReplaceSlotFromPrintSheet = metadata.get("BoosterReplaceSlotFromPrintSheet", ""); // e.g. Zendikar Rising guaranteed double-faced card
|
||||
@@ -826,23 +810,6 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
res.sheetReplaceCardFromSheet2 = metadata.get("SheetReplaceCardFromSheet2", "");
|
||||
res.chaosDraftThemes = metadata.get("ChaosDraftThemes", "").split(";"); // semicolon separated list of theme names
|
||||
|
||||
// Draft options
|
||||
String doublePick = metadata.get("DoublePick", "Never");
|
||||
int maxPodSize = metadata.getInt("MaxPodSize", 8);
|
||||
int recommendedPodSize = metadata.getInt("RecommendedPodSize", 8);
|
||||
int maxMatchPlayers = metadata.getInt("MaxMatchPlayers", 2);
|
||||
String deckType = metadata.get("DeckType", "Normal");
|
||||
String freeCommander = metadata.get("FreeCommander", "");
|
||||
|
||||
res.draftOptions = new DraftOptions(
|
||||
doublePick,
|
||||
maxPodSize,
|
||||
recommendedPodSize,
|
||||
maxMatchPlayers,
|
||||
deckType,
|
||||
freeCommander
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -873,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");
|
||||
|
||||
@@ -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) {
|
||||
@@ -221,9 +220,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
}
|
||||
|
||||
public boolean isCustom() { return custom; }
|
||||
public void setCustom() { custom = true; }
|
||||
|
||||
public boolean isUnsupported() { return unsupported; }
|
||||
public void setCustom() { custom = true; }
|
||||
|
||||
@Override
|
||||
public CardType getType() {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package forge.card;
|
||||
|
||||
public class DraftOptions {
|
||||
public enum DoublePick {
|
||||
NEVER,
|
||||
FIRST_PICK, // only first pick each pack
|
||||
WHEN_POD_SIZE_IS_4, // only when pod size is 4, so you can pick two cards each time
|
||||
ALWAYS // each time you receive a pack, you can pick two cards
|
||||
};
|
||||
public enum DeckType {
|
||||
Normal, // Standard deck, usually 40 cards
|
||||
Commander // Special deck type for Commander format. Important for selection/construction
|
||||
}
|
||||
|
||||
private DoublePick doublePick = DoublePick.NEVER;
|
||||
private final int maxPodSize; // Usually 8, but could be smaller for cubes. I guess it could be larger too
|
||||
private final int recommendedPodSize; // Usually 8, but is 4 for new double pick
|
||||
private final int maxMatchPlayers; // Usually 2, but 4 for things like Commander or Conspiracy
|
||||
private final DeckType deckType; // Normal or Commander
|
||||
private final String freeCommander;
|
||||
|
||||
public DraftOptions(String doublePickOption, int maxPodSize, int recommendedPodSize, int maxMatchPlayers, String deckType, String freeCommander) {
|
||||
this.maxPodSize = maxPodSize;
|
||||
this.recommendedPodSize = recommendedPodSize;
|
||||
this.maxMatchPlayers = maxMatchPlayers;
|
||||
this.deckType = DeckType.valueOf(deckType);
|
||||
this.freeCommander = freeCommander;
|
||||
if (doublePickOption != null) {
|
||||
switch (doublePickOption.toLowerCase()) {
|
||||
case "firstpick":
|
||||
doublePick = DoublePick.FIRST_PICK;
|
||||
break;
|
||||
case "always":
|
||||
doublePick = DoublePick.ALWAYS;
|
||||
break;
|
||||
case "whenpodsizeis4":
|
||||
doublePick = DoublePick.WHEN_POD_SIZE_IS_4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public int getMaxPodSize() {
|
||||
return maxPodSize;
|
||||
}
|
||||
public int getRecommendedPodSize() {
|
||||
return recommendedPodSize;
|
||||
}
|
||||
public DoublePick getDoublePick() {
|
||||
return doublePick;
|
||||
}
|
||||
|
||||
public DoublePick isDoublePick(int podSize) {
|
||||
if (doublePick == DoublePick.WHEN_POD_SIZE_IS_4) {
|
||||
if (podSize != 4) {
|
||||
return DoublePick.NEVER;
|
||||
}
|
||||
// only when pod size is 4, so you can pick two cards each time
|
||||
return DoublePick.ALWAYS;
|
||||
}
|
||||
|
||||
return doublePick;
|
||||
}
|
||||
|
||||
|
||||
public int getMaxMatchPlayers() {
|
||||
return maxMatchPlayers;
|
||||
}
|
||||
public DeckType getDeckType() {
|
||||
return deckType;
|
||||
}
|
||||
public String getFreeCommander() {
|
||||
return freeCommander;
|
||||
}
|
||||
}
|
||||
@@ -115,20 +115,6 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
return parts.get(DeckSection.Main);
|
||||
}
|
||||
|
||||
public Pair<Deck, List<PaperCard>> getValid() {
|
||||
List<PaperCard> unsupported = new ArrayList<>();
|
||||
for (Entry<DeckSection, CardPool> kv : parts.entrySet()) {
|
||||
CardPool pool = kv.getValue();
|
||||
for (Entry<PaperCard, Integer> pc : pool) {
|
||||
if (pc.getKey().getRules() != null && pc.getKey().getRules().isUnsupported()) {
|
||||
unsupported.add(pc.getKey());
|
||||
pool.remove(pc.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair.of(this, unsupported);
|
||||
}
|
||||
|
||||
public List<PaperCard> getCommanders() {
|
||||
List<PaperCard> result = Lists.newArrayList();
|
||||
final CardPool cp = get(DeckSection.Commander);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -207,6 +207,8 @@ public class ImageUtil {
|
||||
else
|
||||
editionCode = cp.getEdition().toLowerCase();
|
||||
String cardCollectorNumber = cp.getCollectorNumber();
|
||||
// Hack to account for variations in Arabian Nights
|
||||
cardCollectorNumber = cardCollectorNumber.replace("+", "†");
|
||||
// override old planechase sets from their modified id since scryfall move the planechase cards outside their original setcode
|
||||
if (cardCollectorNumber.startsWith("OHOP")) {
|
||||
editionCode = "ohop";
|
||||
@@ -250,11 +252,6 @@ public class ImageUtil {
|
||||
: "&face=front");
|
||||
}
|
||||
|
||||
if (cardCollectorNumber.endsWith("☇")) {
|
||||
faceParam = "&face=back";
|
||||
cardCollectorNumber = cardCollectorNumber.substring(0, cardCollectorNumber.length() - 1);
|
||||
}
|
||||
|
||||
return String.format("%s/%s/%s?format=image&version=%s%s", editionCode, encodeUtf8(cardCollectorNumber),
|
||||
langCode, versionParam, faceParam);
|
||||
}
|
||||
@@ -264,10 +261,6 @@ public class ImageUtil {
|
||||
if (!faceParam.isEmpty()) {
|
||||
faceParam = (faceParam.equals("back") ? "&face=back" : "&face=front");
|
||||
}
|
||||
if (collectorNumber.endsWith("☇")) {
|
||||
faceParam = "&face=back";
|
||||
collectorNumber = collectorNumber.substring(0, collectorNumber.length() - 1);
|
||||
}
|
||||
return String.format("%s/%s/%s?format=image&version=%s%s", setCode, encodeUtf8(collectorNumber),
|
||||
langCode, versionParam, faceParam);
|
||||
}
|
||||
@@ -288,7 +281,8 @@ public class ImageUtil {
|
||||
char c;
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
c = in.charAt(i);
|
||||
if ((c != '"') && (c != '/') && (c != ':') && (c != '?')) {
|
||||
if ((c == '"') || (c == '/') || (c == ':') || (c == '?')) {
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ public class ForgeScript {
|
||||
return sa.isKeyword(Keyword.SADDLE);
|
||||
} else if (property.equals("Station")) {
|
||||
return sa.isKeyword(Keyword.STATION);
|
||||
} else if (property.equals("Cycling")) {
|
||||
}else if (property.equals("Cycling")) {
|
||||
return sa.isCycling();
|
||||
} else if (property.equals("Dash")) {
|
||||
return sa.isDash();
|
||||
@@ -237,8 +237,6 @@ public class ForgeScript {
|
||||
return sa.isBoast();
|
||||
} else if (property.equals("Exhaust")) {
|
||||
return sa.isExhaust();
|
||||
} else if (property.equals("Mayhem")) {
|
||||
return sa.isMayhem();
|
||||
} else if (property.equals("Mutate")) {
|
||||
return sa.isMutate();
|
||||
} else if (property.equals("Ninjutsu")) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
@@ -2222,13 +2222,6 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
public void revealUnsupported(Map<Player, List<PaperCard>> unsupported) {
|
||||
// Notify players
|
||||
for (Player p : game.getPlayers()) {
|
||||
p.getController().revealUnsupported(unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
/** Delivers a message to all players. (use reveal to show Cards) */
|
||||
public void notifyOfValue(SpellAbility saSource, GameObject relatedTarget, String value, Player playerExcept) {
|
||||
if (saSource != null) {
|
||||
|
||||
@@ -125,22 +125,10 @@ public final class GameActionUtil {
|
||||
|
||||
// need to be done there before static abilities does reset the card
|
||||
// These Keywords depend on the Mana Cost of for Split Cards
|
||||
if (sa.isBasicSpell()) {
|
||||
if (sa.isBasicSpell() && !sa.isLandAbility()) {
|
||||
for (final KeywordInterface inst : source.getKeywords()) {
|
||||
final String keyword = inst.getOriginal();
|
||||
|
||||
if (keyword.startsWith("Mayhem")) {
|
||||
if (!source.isInZone(ZoneType.Graveyard) || !source.wasDiscarded() || !source.enteredThisTurn()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
alternatives.add(getGraveyardSpellByKeyword(inst, sa, activator, AlternativeCost.Mayhem));
|
||||
}
|
||||
|
||||
if (sa.isLandAbility()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyword.startsWith("Escape")) {
|
||||
if (!source.isInZone(ZoneType.Graveyard)) {
|
||||
continue;
|
||||
@@ -178,6 +166,18 @@ public final class GameActionUtil {
|
||||
}
|
||||
|
||||
alternatives.add(getGraveyardSpellByKeyword(inst, sa, activator, AlternativeCost.Flashback));
|
||||
} else if (keyword.startsWith("Mayhem")) {
|
||||
if (!source.isInZone(ZoneType.Graveyard) || !source.wasDiscarded() || !source.enteredThisTurn()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if source has No Mana cost, and Mayhem doesn't have own one,
|
||||
// Mayhem can't work
|
||||
if (keyword.equals("Mayhem") && source.getManaCost().isNoCost()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
alternatives.add(getGraveyardSpellByKeyword(inst, sa, activator, AlternativeCost.Mayhem));
|
||||
} else if (keyword.startsWith("Harmonize")) {
|
||||
if (!source.isInZone(ZoneType.Graveyard)) {
|
||||
continue;
|
||||
@@ -242,7 +242,6 @@ public final class GameActionUtil {
|
||||
}
|
||||
stackCopy.setLastKnownZone(game.getStackZone());
|
||||
stackCopy.setCastFrom(oldZone);
|
||||
stackCopy.setCastSA(sa);
|
||||
lkicheck = true;
|
||||
|
||||
stackCopy.clearStaticChangedCardKeywords(false);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ public class GameRules {
|
||||
private boolean AISideboardingEnabled = false;
|
||||
private boolean sideboardForAI = false;
|
||||
private final Set<GameType> appliedVariants = EnumSet.noneOf(GameType.class);
|
||||
private int simTimeout = 120;
|
||||
|
||||
// it's a preference, not rule... but I could hardly find a better place for it
|
||||
private boolean useGrayText;
|
||||
@@ -125,12 +124,4 @@ public class GameRules {
|
||||
public void setWarnAboutAICards(final boolean warnAboutAICards) {
|
||||
this.warnAboutAICards = warnAboutAICards;
|
||||
}
|
||||
|
||||
public int getSimTimeout() {
|
||||
return this.simTimeout;
|
||||
}
|
||||
|
||||
public void setSimTimeout(final int duration) {
|
||||
this.simTimeout = duration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -23,7 +23,6 @@ import forge.item.PaperCard;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
@@ -225,7 +224,6 @@ public class Match {
|
||||
// friendliness
|
||||
Map<Player, Map<DeckSection, List<? extends PaperCard>>> rAICards = new HashMap<>();
|
||||
Multimap<Player, PaperCard> removedAnteCards = ArrayListMultimap.create();
|
||||
Map<Player, List<PaperCard>> unsupported = new HashMap<>();
|
||||
|
||||
final FCollectionView<Player> players = game.getPlayers();
|
||||
final List<RegisteredPlayer> playersConditions = game.getMatch().getPlayers();
|
||||
@@ -290,32 +288,22 @@ public class Match {
|
||||
}
|
||||
}
|
||||
|
||||
Deck toCheck = psc.getDeck();
|
||||
if (toCheck == null) {
|
||||
try {
|
||||
System.err.println(psc.getPlayer().getName() + " Deck is NULL...");
|
||||
int val = rules.getGameType().getDeckFormat().getMainRange().getMinimum();
|
||||
toCheck = new Deck("NULL");
|
||||
if (val > 0)
|
||||
toCheck.getMain().add("Wastes", val);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
Pair<Deck, List<PaperCard>> myDeck = toCheck.getValid();
|
||||
player.setDraftNotes(myDeck.getLeft().getDraftNotes());
|
||||
Deck myDeck = psc.getDeck();
|
||||
player.setDraftNotes(myDeck.getDraftNotes());
|
||||
|
||||
Set<PaperCard> myRemovedAnteCards = null;
|
||||
if (!rules.useAnte()) {
|
||||
myRemovedAnteCards = getRemovedAnteCards(myDeck.getLeft());
|
||||
myRemovedAnteCards = getRemovedAnteCards(myDeck);
|
||||
for (PaperCard cp: myRemovedAnteCards) {
|
||||
for (Entry<DeckSection, CardPool> ds : myDeck.getLeft()) {
|
||||
for (Entry<DeckSection, CardPool> ds : myDeck) {
|
||||
ds.getValue().removeAll(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preparePlayerZone(player, ZoneType.Library, myDeck.getLeft().getMain(), psc.useRandomFoil());
|
||||
if (myDeck.getLeft().has(DeckSection.Sideboard)) {
|
||||
preparePlayerZone(player, ZoneType.Sideboard, myDeck.getLeft().get(DeckSection.Sideboard), psc.useRandomFoil());
|
||||
preparePlayerZone(player, ZoneType.Library, myDeck.getMain(), psc.useRandomFoil());
|
||||
if (myDeck.has(DeckSection.Sideboard)) {
|
||||
preparePlayerZone(player, ZoneType.Sideboard, myDeck.get(DeckSection.Sideboard), psc.useRandomFoil());
|
||||
|
||||
// Assign Companion
|
||||
Card companion = player.assignCompanion(game, person);
|
||||
@@ -334,7 +322,7 @@ public class Match {
|
||||
player.shuffle(null);
|
||||
|
||||
if (isFirstGame) {
|
||||
Map<DeckSection, List<? extends PaperCard>> cardsComplained = player.getController().complainCardsCantPlayWell(myDeck.getLeft());
|
||||
Map<DeckSection, List<? extends PaperCard>> cardsComplained = player.getController().complainCardsCantPlayWell(myDeck);
|
||||
if (cardsComplained != null && !cardsComplained.isEmpty()) {
|
||||
rAICards.put(player, cardsComplained);
|
||||
}
|
||||
@@ -349,7 +337,6 @@ public class Match {
|
||||
if (myRemovedAnteCards != null && !myRemovedAnteCards.isEmpty()) {
|
||||
removedAnteCards.putAll(player, myRemovedAnteCards);
|
||||
}
|
||||
unsupported.put(player, myDeck.getRight());
|
||||
}
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
@@ -360,10 +347,6 @@ public class Match {
|
||||
if (!removedAnteCards.isEmpty()) {
|
||||
game.getAction().revealAnte(localizer.getMessage("lblAnteCardsRemoved"), removedAnteCards);
|
||||
}
|
||||
|
||||
if (!unsupported.isEmpty()) {
|
||||
game.getAction().revealUnsupported(unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAnte(Game lastGame) {
|
||||
|
||||
@@ -383,9 +383,6 @@ public final class AbilityFactory {
|
||||
if (mapParams.containsKey("TargetsWithDifferentCMC")) {
|
||||
abTgt.setDifferentCMC(true);
|
||||
}
|
||||
if (mapParams.containsKey("TargetsWithDifferentNames")) {
|
||||
abTgt.setDifferentNames(true);
|
||||
}
|
||||
if (mapParams.containsKey("TargetsWithEqualToughness")) {
|
||||
abTgt.setEqualToughness(true);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,6 +39,7 @@ public class AirbendEffect extends SpellAbilityEffect {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card hostCard = sa.getHostCard();
|
||||
|
||||
@@ -46,9 +46,6 @@ public class AlterAttributeEffect extends SpellAbilityEffect {
|
||||
boolean altered = false;
|
||||
|
||||
switch (attr.trim()) {
|
||||
case "Harnessed":
|
||||
altered = gameCard.setHarnessed(activate);
|
||||
break;
|
||||
case "Plotted":
|
||||
altered = gameCard.setPlotted(activate);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -969,10 +969,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
String prompt;
|
||||
if (sa.hasParam("OptionalPrompt")) {
|
||||
prompt = sa.getParam("OptionalPrompt");
|
||||
} else if (defined) {
|
||||
prompt = Localizer.getInstance().getMessage("lblPutThatCardFromPlayerOriginToDestination", "{player's}", Lang.joinHomogenous(origin, ZoneType::getTranslatedName).toLowerCase(), destination.getTranslatedName().toLowerCase());
|
||||
} else {
|
||||
prompt = Localizer.getInstance().getMessage("lblSearchPlayerZoneConfirm", "{player's}", Lang.joinHomogenous(origin, ZoneType::getTranslatedName).toLowerCase());
|
||||
if (defined) {
|
||||
prompt = Localizer.getInstance().getMessage("lblPutThatCardFromPlayerOriginToDestination", "{player's}", Lang.joinHomogenous(origin, ZoneType::getTranslatedName).toLowerCase(), destination.getTranslatedName().toLowerCase());
|
||||
} else {
|
||||
prompt = Localizer.getInstance().getMessage("lblSearchPlayerZoneConfirm", "{player's}", Lang.joinHomogenous(origin, ZoneType::getTranslatedName).toLowerCase());
|
||||
}
|
||||
}
|
||||
String message = MessageUtil.formatMessage(prompt , decider, player);
|
||||
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message, null)) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -34,9 +34,6 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
|
||||
// CR 603.12a if the trigger event or events occur multiple times during the resolution of the spell or ability that created it,
|
||||
// the reflexive triggered ability will trigger once for each of those times
|
||||
int amt = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("TriggerAmount", "1"), sa);
|
||||
if (amt <= 0) {
|
||||
return;
|
||||
|
||||
@@ -54,29 +54,33 @@ public class LifeExchangeEffect extends SpellAbilityEffect {
|
||||
|
||||
final int life1 = p1.getLife();
|
||||
final int life2 = p2.getLife();
|
||||
final int diff = Math.abs(life1 - life2);
|
||||
|
||||
if (life2 > life1) {
|
||||
// swap players
|
||||
Player tmp = p2;
|
||||
p2 = p1;
|
||||
p1 = tmp;
|
||||
if (sa.hasParam("RememberDifference")) {
|
||||
final int diff = life1 - life2;
|
||||
source.addRemembered(diff);
|
||||
}
|
||||
if (diff > 0 && p1.canLoseLife() && p2.canGainLife()) {
|
||||
|
||||
final Map<Player, Integer> lossMap = Maps.newHashMap();
|
||||
if ((life1 > life2) && p1.canLoseLife() && p2.canGainLife()) {
|
||||
final int diff = life1 - life2;
|
||||
final int lost = p1.loseLife(diff, false, false);
|
||||
p2.gainLife(diff, source, sa);
|
||||
if (lost > 0) {
|
||||
final Map<Player, Integer> lossMap = Maps.newHashMap();
|
||||
lossMap.put(p1, lost);
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPIMap(lossMap);
|
||||
source.getGame().getTriggerHandler().runTrigger(TriggerType.LifeLostAll, runParams, false);
|
||||
if (sa.hasParam("RememberOwnLoss") && p1.equals(sa.getActivatingPlayer())) {
|
||||
source.addRemembered(lost);
|
||||
}
|
||||
}
|
||||
} else if ((life2 > life1) && p2.canLoseLife() && p1.canGainLife()) {
|
||||
final int diff = life2 - life1;
|
||||
final int lost = p2.loseLife(diff, false, false);
|
||||
p1.gainLife(diff, source, sa);
|
||||
if (lost > 0) {
|
||||
lossMap.put(p2, lost);
|
||||
}
|
||||
} else {
|
||||
// they are equal or can't be exchanged, so nothing to do
|
||||
}
|
||||
if (sa.hasParam("RememberDifference")) {
|
||||
source.addRemembered(p1.getLife() - p2.getLife());
|
||||
if (!lossMap.isEmpty()) { // Run triggers if any player actually lost life
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPIMap(lossMap);
|
||||
source.getGame().getTriggerHandler().runTrigger(TriggerType.LifeLostAll, runParams, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,11 +271,10 @@ public class ManaEffect extends SpellAbilityEffect {
|
||||
producedMana.append(abMana.produceMana(mana, p, sa));
|
||||
}
|
||||
|
||||
// Only clear express choice after mana has been produced
|
||||
abMana.clearExpressChoice();
|
||||
|
||||
abMana.tapsForMana(sa.getRootAbility(), producedMana.toString());
|
||||
|
||||
// Only clear express choice after mana has been produced
|
||||
abMana.clearExpressChoice();
|
||||
if (sa.isKeyword(Keyword.FIREBENDING)) {
|
||||
activator.triggerElementalBend(TriggerType.Firebend);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,8 @@ public class PermanentCreatureEffect extends PermanentEffect {
|
||||
public String getStackDescription(final SpellAbility sa) {
|
||||
final CardState source = sa.getCardState();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(CardTranslation.getTranslatedName(source.getName())).append(" - ").append(Localizer.getInstance().getMessage("lblCreature")).append(" ");
|
||||
sb.append(sa.getParamOrDefault("SetPower", source.getBasePowerString()));
|
||||
sb.append(" / ").append(sa.getParamOrDefault("SetToughness", source.getBaseToughnessString()));
|
||||
sb.append(CardTranslation.getTranslatedName(source.getName())).append(" - ").append(Localizer.getInstance().getMessage("lblCreature")).append(" ").append(source.getBasePowerString());
|
||||
sb.append(" / ").append(source.getBaseToughnessString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.perpetual.PerpetualKeywords;
|
||||
@@ -281,17 +282,6 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
List<Card> tgtCards = getCardsfromTargets(sa);
|
||||
List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
|
||||
if (sa.hasParam("Optional")) {
|
||||
final String targets = Lang.joinHomogenous(tgtCards);
|
||||
final String message = sa.hasParam("OptionQuestion")
|
||||
? TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets)
|
||||
: Localizer.getInstance().getMessage("lblApplyPumpToTarget", targets);
|
||||
|
||||
if (!activator.getController().confirmAction(sa, null, message, null)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> keywords = Lists.newArrayList();
|
||||
if (sa.hasParam("KW")) {
|
||||
keywords.addAll(Arrays.asList(sa.getParam("KW").split(" & ")));
|
||||
@@ -317,6 +307,8 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, host, sa);
|
||||
}
|
||||
|
||||
final CardCollection untargetedCards = CardUtil.getRadiance(sa);
|
||||
|
||||
if (sa.hasParam("DefinedKW")) {
|
||||
String defined = sa.getParam("DefinedKW");
|
||||
if (defined.equals("ChosenType")) {
|
||||
@@ -402,6 +394,17 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
keywords = choice;
|
||||
}
|
||||
|
||||
if (sa.hasParam("Optional")) {
|
||||
final String targets = Lang.joinHomogenous(tgtCards);
|
||||
final String message = sa.hasParam("OptionQuestion")
|
||||
? TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets)
|
||||
: Localizer.getInstance().getMessage("lblApplyPumpToTarget", targets);
|
||||
|
||||
if (!activator.getController().confirmAction(sa, null, message, null)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("RememberObjects")) {
|
||||
host.addRemembered(AbilityUtils.getDefinedObjects(host, sa.getParam("RememberObjects"), sa));
|
||||
}
|
||||
@@ -491,7 +494,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
||||
registerDelayedTrigger(sa, sa.getParam("AtEOT"), tgtCards);
|
||||
}
|
||||
|
||||
for (final Card tgtC : CardUtil.getRadiance(sa)) {
|
||||
for (final Card tgtC : untargetedCards) {
|
||||
// only pump things in PumpZone
|
||||
if (!tgtC.isInZones(pumpZones)) {
|
||||
continue;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -203,7 +203,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
private boolean unearthed;
|
||||
private boolean ringbearer;
|
||||
private boolean monstrous;
|
||||
private boolean harnessed;
|
||||
private boolean renowned;
|
||||
private boolean solved;
|
||||
private boolean tributed;
|
||||
@@ -2463,8 +2462,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
}
|
||||
sbLong.append("Enchant ").append(desc).append("\r\n");
|
||||
} else if (keyword.startsWith("Ripple")) {
|
||||
sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
|
||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")
|
||||
|| keyword.startsWith("Disguise") || keyword.startsWith("Reflect")
|
||||
|| keyword.startsWith("Disguise")
|
||||
|| keyword.startsWith("Escape") || keyword.startsWith("Foretell:")
|
||||
|| keyword.startsWith("Madness:")|| keyword.startsWith("Recover")
|
||||
|| keyword.startsWith("Reconfigure") || keyword.startsWith("Squad")
|
||||
@@ -2503,15 +2504,17 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
sbLong.append(".");
|
||||
}
|
||||
if (k.length > 3) {
|
||||
sbLong.append(". ").append(k[3]);
|
||||
sbLong.append(". " + k[3]);
|
||||
}
|
||||
}
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
sbLong.append("\r\n");
|
||||
} else if (keyword.equals("Mayhem")) {
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
sbLong.append("\r\n");
|
||||
}
|
||||
} else if (keyword.startsWith("Reflect")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1]));
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
sbLong.append("\r\n");
|
||||
} else if (keyword.startsWith("Echo")) {
|
||||
sbLong.append("Echo ");
|
||||
final String[] upkeepCostParams = keyword.split(":");
|
||||
@@ -2650,7 +2653,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
|| keyword.startsWith("Graft") || keyword.startsWith("Fading") || keyword.startsWith("Vanishing:")
|
||||
|| keyword.startsWith("Afterlife") || keyword.startsWith("Hideaway") || keyword.startsWith("Toxic")
|
||||
|| keyword.startsWith("Afflict") || keyword.startsWith ("Poisonous") || keyword.startsWith("Rampage")
|
||||
|| keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Ripple")) {
|
||||
|| keyword.startsWith("Renown") || keyword.startsWith("Annihilator")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]).append(" ").append(k[1]).append(" (").append(inst.getReminderText()).append(")");
|
||||
} else if (keyword.startsWith("Crew")) {
|
||||
@@ -2966,9 +2969,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
if (monstrous) {
|
||||
sb.append("Monstrous\r\n");
|
||||
}
|
||||
if (harnessed) {
|
||||
sb.append("Harnessed\r\n");
|
||||
}
|
||||
if (renowned) {
|
||||
sb.append("Renowned\r\n");
|
||||
}
|
||||
@@ -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
|
||||
@@ -6693,14 +6692,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
setRingBearer(false);
|
||||
}
|
||||
|
||||
public final boolean isHarnessed() {
|
||||
return harnessed;
|
||||
}
|
||||
public final boolean setHarnessed(final boolean harnessed0) {
|
||||
harnessed = harnessed0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean isMonstrous() {
|
||||
return monstrous;
|
||||
}
|
||||
@@ -6858,10 +6849,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return exiledSA.isKeyword(Keyword.WARP);
|
||||
}
|
||||
|
||||
public boolean isWebSlinged() {
|
||||
return getCastSA() != null & getCastSA().isAlternativeCost(AlternativeCost.WebSlinging);
|
||||
}
|
||||
|
||||
public boolean isSpecialized() {
|
||||
return specialized;
|
||||
}
|
||||
@@ -7157,7 +7144,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StaticAbilityCantTarget.cantTarget(this, sa) != null) {
|
||||
if (StaticAbilityCantTarget.cantTarget(this, sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -376,28 +376,22 @@ public class CardFactory {
|
||||
}
|
||||
}
|
||||
|
||||
// Negative card Id's are for view purposes only
|
||||
// Build English oracle and translated oracle mapping
|
||||
if (c.getId() >= 0) {
|
||||
// Build English oracle and translated oracle mapping
|
||||
CardTranslation.buildOracleMapping(face.getName(), face.getOracleText(), variantName);
|
||||
}
|
||||
|
||||
// Set name for Sentry reports to be identifiable
|
||||
// Name first so Senty has the Card name
|
||||
c.setName(face.getName());
|
||||
|
||||
if (c.getId() >= 0) { // Set Triggers & Abilities if not for view
|
||||
for (Entry<String, String> v : face.getVariables())
|
||||
c.setSVar(v.getKey(), v.getValue());
|
||||
for (String r : face.getReplacements())
|
||||
c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true, c.getCurrentState()));
|
||||
for (String s : face.getStaticAbilities())
|
||||
c.addStaticAbility(s);
|
||||
for (String t : face.getTriggers())
|
||||
c.addTrigger(TriggerHandler.parseTrigger(t, c, true, c.getCurrentState()));
|
||||
for (Entry<String, String> v : face.getVariables()) c.setSVar(v.getKey(), v.getValue());
|
||||
|
||||
// keywords not before variables
|
||||
c.addIntrinsicKeywords(face.getKeywords(), false);
|
||||
}
|
||||
for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true, c.getCurrentState()));
|
||||
for (String s : face.getStaticAbilities()) c.addStaticAbility(s);
|
||||
for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true, c.getCurrentState()));
|
||||
|
||||
// keywords not before variables
|
||||
c.addIntrinsicKeywords(face.getKeywords(), false);
|
||||
if (face.getDraftActions() != null) {
|
||||
face.getDraftActions().forEach(c::addDraftAction);
|
||||
}
|
||||
@@ -426,8 +420,7 @@ public class CardFactory {
|
||||
|
||||
c.setAttractionLights(face.getAttractionLights());
|
||||
|
||||
if (c.getId() > 0) // Set FactoryAbilities if not for view
|
||||
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
||||
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
||||
}
|
||||
|
||||
public static void copySpellAbility(SpellAbility from, SpellAbility to, final Card host, final Player p, final boolean lki, final boolean keepTextChanges) {
|
||||
|
||||
@@ -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:");
|
||||
@@ -2776,9 +2776,10 @@ public class CardFactoryUtil {
|
||||
final String cost = params[1];
|
||||
|
||||
final StringBuilder sbAttach = new StringBuilder();
|
||||
sbAttach.append("SP$ Attach | ValidTgts$ Creature | Cost$ ");
|
||||
sbAttach.append("SP$ Attach | Cost$ ");
|
||||
sbAttach.append(cost);
|
||||
sbAttach.append(" | AILogic$ ").append(params.length > 2 ? params[2] : "Pump");
|
||||
sbAttach.append(" | Bestow$ True | ValidTgts$ Creature");
|
||||
|
||||
final SpellAbility sa = AbilityFactory.getAbility(sbAttach.toString(), card);
|
||||
final StringBuilder sbDesc = new StringBuilder();
|
||||
@@ -4112,7 +4113,7 @@ public class CardFactoryUtil {
|
||||
sbValid.append("| ").append(param).append(k[1]);
|
||||
}
|
||||
|
||||
String effect = "Mode$ CantTarget | ValidTarget$ Card.Self | Secondary$ True"
|
||||
String effect = "Mode$ CantTarget | ValidCard$ Card.Self | Secondary$ True"
|
||||
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
|
||||
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
@@ -4153,7 +4154,7 @@ public class CardFactoryUtil {
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
|
||||
// Target
|
||||
effect = "Mode$ CantTarget | ValidTarget$ Card.Self | Secondary$ True ";
|
||||
effect = "Mode$ CantTarget | Protection$ True | ValidCard$ Card.Self | Secondary$ True ";
|
||||
if (!valid.isEmpty()) {
|
||||
effect += "| ValidSource$ " + valid;
|
||||
}
|
||||
@@ -4161,7 +4162,7 @@ public class CardFactoryUtil {
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
|
||||
// Attach
|
||||
effect = "Mode$ CantAttach | Target$ Card.Self | Secondary$ True ";
|
||||
effect = "Mode$ CantAttach | Protection$ True | Target$ Card.Self | Secondary$ True ";
|
||||
if (!valid.isEmpty()) {
|
||||
effect += "| ValidCard$ " + valid;
|
||||
}
|
||||
@@ -4182,7 +4183,7 @@ public class CardFactoryUtil {
|
||||
" | Description$ Chapter abilities of this Saga can't trigger the turn it entered the battlefield unless it has exactly the number of lore counters on it specified in the chapter symbol of that ability.";
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
} else if (keyword.equals("Shroud")) {
|
||||
String effect = "Mode$ CantTarget | ValidTarget$ Card.Self | Secondary$ True"
|
||||
String effect = "Mode$ CantTarget | ValidCard$ Card.Self | Secondary$ True"
|
||||
+ " | Description$ Shroud (" + inst.getReminderText() + ")";
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
} else if (keyword.equals("Skulk")) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1244,8 +1244,7 @@ public class CardProperty {
|
||||
if (property.contains("ControlledBy")) {
|
||||
FCollectionView<Player> p = AbilityUtils.getDefinedPlayers(source, property.split("ControlledBy")[1], spellAbility);
|
||||
cards = CardLists.filterControlledBy(cards, p);
|
||||
// Kraven the Hunter LTB trigger
|
||||
if (!card.isLKI() && !cards.contains(card)) {
|
||||
if (!cards.contains(card)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1820,10 +1819,6 @@ public class CardProperty {
|
||||
if (!card.isWarped()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("webSlinged")) {
|
||||
if (!card.isWebSlinged()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("CrewedThisTurn")) {
|
||||
if (!hasTimestampMatch(card, source.getCrewedByThisTurn())) return false;
|
||||
} else if (property.equals("CrewedBySourceThisTurn")) {
|
||||
@@ -1832,10 +1827,6 @@ public class CardProperty {
|
||||
if (card.getDevouredCards().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("harnessed")) {
|
||||
if (!card.isHarnessed()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("IsMonstrous")) {
|
||||
if (!card.isMonstrous()) {
|
||||
return false;
|
||||
|
||||
@@ -468,9 +468,6 @@ public class CardState extends GameObject implements IHasSVars, ITranslatable {
|
||||
return Iterables.getFirst(getIntrinsicSpellAbilities(), null);
|
||||
}
|
||||
public final SpellAbility getFirstSpellAbility() {
|
||||
if (this.card.getCastSA() != null) {
|
||||
return this.card.getCastSA();
|
||||
}
|
||||
return Iterables.getFirst(getNonManaAbilities(), null);
|
||||
}
|
||||
|
||||
@@ -608,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);
|
||||
}
|
||||
@@ -636,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) {
|
||||
|
||||
@@ -60,12 +60,13 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
public static TrackableCollection<CardView> getCollection(Iterable<Card> cards) {
|
||||
if (cards == null) {
|
||||
return null;
|
||||
}
|
||||
TrackableCollection<CardView> collection = new TrackableCollection<>();
|
||||
if (cards != null) {
|
||||
for (Card c : cards) {
|
||||
if (c != null && c.getRenderForUI()) { //only add cards that match their card for UI
|
||||
collection.add(c.getView());
|
||||
}
|
||||
for (Card c : cards) {
|
||||
if (c.getRenderForUI()) { //only add cards that match their card for UI
|
||||
collection.add(c.getView());
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
|
||||
@@ -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),
|
||||
@@ -167,8 +167,6 @@ public enum CounterEnumType implements CounterType {
|
||||
|
||||
FILIBUSTER("FLBTR", 255, 179, 119),
|
||||
|
||||
FILM("FILM", 255, 255, 255),
|
||||
|
||||
FINALITY("FINAL", 255, 255, 255),
|
||||
|
||||
FIRE("FIRE", 240, 30, 35),
|
||||
@@ -557,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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(eVal, sVal);
|
||||
}
|
||||
|
||||
public boolean is(CounterEnumType eType);
|
||||
@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();
|
||||
}
|
||||
|
||||
public boolean isKeywordCounter();
|
||||
@Override
|
||||
public String toString() {
|
||||
return eVal != null ? eVal.toString() : sVal;
|
||||
}
|
||||
|
||||
public int getRed();
|
||||
public String getName() {
|
||||
return eVal != null ? eVal.getName() : getKeywordDescription();
|
||||
}
|
||||
|
||||
public int getGreen();
|
||||
public String getCounterOnCardDisplayName() {
|
||||
return eVal != null ? eVal.getCounterOnCardDisplayName() : getKeywordDescription();
|
||||
}
|
||||
|
||||
public int getBlue();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.game.combat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -38,12 +39,10 @@ public class AttackRequirement {
|
||||
|
||||
//MustAttack static check
|
||||
final List<GameEntity> mustAttack = StaticAbilityMustAttack.entitiesMustAttack(attacker);
|
||||
nAttackAnything += Collections.frequency(mustAttack, attacker);
|
||||
for (GameEntity e : mustAttack) {
|
||||
if (e.equals(attacker)) {
|
||||
nAttackAnything++;
|
||||
} else {
|
||||
defenderSpecific.add(e);
|
||||
}
|
||||
if (e.equals(attacker)) continue;
|
||||
defenderSpecific.add(e);
|
||||
}
|
||||
|
||||
for (final GameEntity defender : possibleDefenders) {
|
||||
|
||||
@@ -225,7 +225,7 @@ public class CombatUtil {
|
||||
if (!ge.equals(defender) && ge instanceof Player) {
|
||||
// found a player which does not goad that creature
|
||||
// and creature can attack this player or planeswalker
|
||||
if (!attacker.isGoadedBy((Player) ge) && canAttack(attacker, ge)) {
|
||||
if (!attacker.isGoadedBy((Player) ge) && !ge.hasKeyword("Creatures your opponents control attack a player other than you if able.") && canAttack(attacker, ge)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -233,6 +233,17 @@ public class CombatUtil {
|
||||
}
|
||||
}
|
||||
|
||||
// Quasi-goad logic for "Kardur, Doomscourge" etc. that isn't goad but behaves the same
|
||||
if (defender != null && defender.hasKeyword("Creatures your opponents control attack a player other than you if able.")) {
|
||||
for (GameEntity ge : getAllPossibleDefenders(attacker.getController())) {
|
||||
if (!ge.equals(defender) && ge instanceof Player) {
|
||||
if (!ge.hasKeyword("Creatures your opponents control attack a player other than you if able.") && canAttack(attacker, ge)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CantAttack static abilities
|
||||
if (StaticAbilityCantAttackBlock.cantAttack(attacker, defender)) {
|
||||
return false;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package forge.game.event;
|
||||
|
||||
public interface Event {
|
||||
public abstract class Event {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package forge.game.event;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public enum EventValueChangeType {
|
||||
Added,
|
||||
Removed,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user