mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 02:52:02 -08:00
* Choose an ability - fixed not working cards like Argivian Avenger, Atraxas Skitterfang, Steel Seraph (#10115)
This commit is contained in:
parent
ea10b025e0
commit
6b05562336
9 changed files with 68 additions and 23 deletions
|
|
@ -24,6 +24,7 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -89,7 +90,7 @@ class ArgivianAvengerEffect extends OneShotEffect {
|
||||||
game.addEffect(new BoostSourceEffect(-1, -1, Duration.EndOfTurn), source);
|
game.addEffect(new BoostSourceEffect(-1, -1, Duration.EndOfTurn), source);
|
||||||
Choice choice = new ChoiceImpl(true);
|
Choice choice = new ChoiceImpl(true);
|
||||||
choice.setMessage("Choose an ability");
|
choice.setMessage("Choose an ability");
|
||||||
choice.setChoices(abilityMap.keySet());
|
choice.setChoices(new HashSet<>(abilityMap.keySet()));
|
||||||
player.choose(outcome, choice, game);
|
player.choose(outcome, choice, game);
|
||||||
Ability ability = abilityMap.getOrDefault(choice.getChoice(), null);
|
Ability ability = abilityMap.getOrDefault(choice.getChoice(), null);
|
||||||
if (ability != null) {
|
if (ability != null) {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import mage.players.Player;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -103,7 +104,7 @@ class AtraxasSkitterfangEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
Choice choice = new ChoiceImpl(true);
|
Choice choice = new ChoiceImpl(true);
|
||||||
choice.setMessage("Choose an ability");
|
choice.setMessage("Choose an ability");
|
||||||
choice.setChoices(abilityMap.keySet());
|
choice.setChoices(new HashSet<>(abilityMap.keySet()));
|
||||||
player.choose(outcome, choice, game);
|
player.choose(outcome, choice, game);
|
||||||
Ability ability = abilityMap.getOrDefault(choice.getChoice(), null);
|
Ability ability = abilityMap.getOrDefault(choice.getChoice(), null);
|
||||||
if (ability != null) {
|
if (ability != null) {
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,7 @@ import mage.game.permanent.token.SpiritGreenToken;
|
||||||
import mage.game.permanent.token.Token;
|
import mage.game.permanent.token.Token;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,9 +43,9 @@ public final class InvokeTheAncients extends CardImpl {
|
||||||
class InvokeTheAncientsEffect extends OneShotEffect {
|
class InvokeTheAncientsEffect extends OneShotEffect {
|
||||||
|
|
||||||
private static final Token token = new SpiritGreenToken();
|
private static final Token token = new SpiritGreenToken();
|
||||||
private static final Set<String> choices = Arrays.asList(
|
private static final Set<String> choices = new HashSet<>(Arrays.asList(
|
||||||
"Vigilance", "Reach", "Trample"
|
"Vigilance", "Reach", "Trample"
|
||||||
).stream().collect(Collectors.toSet());
|
));
|
||||||
|
|
||||||
InvokeTheAncientsEffect() {
|
InvokeTheAncientsEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import mage.players.Player;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -93,7 +94,7 @@ class SteelSeraphEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
Choice choice = new ChoiceImpl(true);
|
Choice choice = new ChoiceImpl(true);
|
||||||
choice.setMessage("Choose an ability");
|
choice.setMessage("Choose an ability");
|
||||||
choice.setChoices(map.keySet());
|
choice.setChoices(new HashSet<>(map.keySet()));
|
||||||
player.choose(outcome, choice, game);
|
player.choose(outcome, choice, game);
|
||||||
Ability ability = map.getOrDefault(choice.getChoice(), null);
|
Ability ability = map.getOrDefault(choice.getChoice(), null);
|
||||||
if (ability == null) {
|
if (ability == null) {
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,7 @@ import mage.game.stack.Spell;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetCardInLibrary;
|
import mage.target.common.TargetCardInLibrary;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,8 +74,9 @@ public final class VivienMonstersAdvocate extends CardImpl {
|
||||||
class VivienMonstersAdvocateTokenEffect extends OneShotEffect {
|
class VivienMonstersAdvocateTokenEffect extends OneShotEffect {
|
||||||
|
|
||||||
private static final Token token = new BeastToken();
|
private static final Token token = new BeastToken();
|
||||||
private static final Set<String> choices
|
private static final Set<String> choices = new HashSet<>(Arrays.asList(
|
||||||
= Arrays.asList("Vigilance", "Reach", "Trample").stream().collect(Collectors.toSet());
|
"Vigilance", "Reach", "Trample"
|
||||||
|
));
|
||||||
|
|
||||||
VivienMonstersAdvocateTokenEffect() {
|
VivienMonstersAdvocateTokenEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package org.mage.test.serverside.performance;
|
package org.mage.test.serverside.performance;
|
||||||
|
|
||||||
import mage.abilities.keyword.InfectAbility;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.keyword.*;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.ExpansionSet;
|
import mage.cards.ExpansionSet;
|
||||||
import mage.cards.Sets;
|
import mage.cards.Sets;
|
||||||
import mage.cards.repository.CardInfo;
|
import mage.cards.repository.CardInfo;
|
||||||
import mage.cards.repository.CardRepository;
|
import mage.cards.repository.CardRepository;
|
||||||
|
import mage.choices.Choice;
|
||||||
|
import mage.choices.ChoiceImpl;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
|
|
@ -21,8 +24,7 @@ import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -73,18 +75,18 @@ public class SerializationTest extends CardTestPlayerBase {
|
||||||
CardUtil.getObjectPartsAsObjects(newCard).stream()
|
CardUtil.getObjectPartsAsObjects(newCard).stream()
|
||||||
.map(Card.class::cast)
|
.map(Card.class::cast)
|
||||||
.forEach(card -> {
|
.forEach(card -> {
|
||||||
Card testCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, newCard);
|
|
||||||
Card testPermanent = null;
|
Card testPermanent = null;
|
||||||
if (!testCard.isInstantOrSorcery()) {
|
if (!card.isInstantOrSorcery()) {
|
||||||
testPermanent = new PermanentCard(testCard, playerA.getId(), currentGame);
|
Card battlefieldCard = CardUtil.getDefaultCardSideForBattlefield(currentGame, card);
|
||||||
|
testPermanent = new PermanentCard(battlefieldCard, playerA.getId(), currentGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// card
|
// card
|
||||||
{
|
{
|
||||||
Object compressed = CompressUtil.compress(testCard);
|
Object compressed = CompressUtil.compress(card);
|
||||||
Assert.assertTrue("Must be zip", compressed instanceof ZippedObjectImpl);
|
Assert.assertTrue("Must be zip", compressed instanceof ZippedObjectImpl);
|
||||||
Card uncompressed = (Card) CompressUtil.decompress(compressed);
|
Card uncompressed = (Card) CompressUtil.decompress(compressed);
|
||||||
Assert.assertEquals("Must be same", testCard.getName(), uncompressed.getName());
|
Assert.assertEquals("Must be same", card.getName(), uncompressed.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// permanent
|
// permanent
|
||||||
|
|
@ -166,4 +168,35 @@ public class SerializationTest extends CardTestPlayerBase {
|
||||||
GameView uncompressed = (GameView) CompressUtil.decompress(compressed);
|
GameView uncompressed = (GameView) CompressUtil.decompress(compressed);
|
||||||
Assert.assertEquals("Must be same", 1, uncompressed.getPlayers().get(0).getBattlefield().size());
|
Assert.assertEquals("Must be same", 1, uncompressed.getPlayers().get(0).getBattlefield().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void test_Choices_MustHaveProtectionFromKeySetUsage() {
|
||||||
|
Map<String, Ability> abilityMap = new HashMap<>();
|
||||||
|
abilityMap.put("Flying", FlyingAbility.getInstance());
|
||||||
|
Choice choice = new ChoiceImpl(true);
|
||||||
|
choice.setMessage("Choose an ability");
|
||||||
|
|
||||||
|
choice.setChoices(abilityMap.keySet());
|
||||||
|
Assert.fail("Can't be here");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Choices_MustSupportSerialization() {
|
||||||
|
// related issue: https://github.com/magefree/mage/issues/10115
|
||||||
|
// copy-paste structure for the test (example from Atraxas Skitter)
|
||||||
|
Map<String, Ability> abilityMap = new HashMap<>();
|
||||||
|
abilityMap.put("Flying", FlyingAbility.getInstance());
|
||||||
|
abilityMap.put("Vigilance", VigilanceAbility.getInstance());
|
||||||
|
abilityMap.put("Deathtouch", DeathtouchAbility.getInstance());
|
||||||
|
abilityMap.put("Lifelink", LifelinkAbility.getInstance());
|
||||||
|
Choice choice = new ChoiceImpl(true);
|
||||||
|
choice.setMessage("Choose an ability");
|
||||||
|
|
||||||
|
choice.setChoices(new HashSet<>(abilityMap.keySet()));
|
||||||
|
|
||||||
|
Object compressed = CompressUtil.compress(choice);
|
||||||
|
Assert.assertTrue("Must be zip", compressed instanceof ZippedObjectImpl);
|
||||||
|
Choice uncompressed = (Choice) CompressUtil.decompress(compressed);
|
||||||
|
Assert.assertEquals("Must be same", choice.getChoices().size(), uncompressed.getChoices().size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package mage.choices;
|
package mage.choices;
|
||||||
|
|
||||||
|
import mage.util.CardUtil;
|
||||||
import mage.util.RandomUtil;
|
import mage.util.RandomUtil;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
|
@ -111,6 +112,7 @@ public class ChoiceImpl implements Choice {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setChoices(Set<String> choices) {
|
public void setChoices(Set<String> choices) {
|
||||||
|
CardUtil.checkSetParamForSerializationCompatibility(choices);
|
||||||
this.choices = choices;
|
this.choices = choices;
|
||||||
protectFromEmptyChoices();
|
protectFromEmptyChoices();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import mage.cards.Card;
|
||||||
import mage.cards.Cards;
|
import mage.cards.Cards;
|
||||||
import mage.choices.Choice;
|
import mage.choices.Choice;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -65,6 +66,9 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
||||||
Set<UUID> targets, Cards cards, QueryType queryType, int min, int max, boolean required,
|
Set<UUID> targets, Cards cards, QueryType queryType, int min, int max, boolean required,
|
||||||
Map<String, Serializable> options, List<String> messages) {
|
Map<String, Serializable> options, List<String> messages) {
|
||||||
super(playerId);
|
super(playerId);
|
||||||
|
|
||||||
|
CardUtil.checkSetParamForSerializationCompatibility(choices);
|
||||||
|
|
||||||
this.queryType = queryType;
|
this.queryType = queryType;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
|
|
|
||||||
|
|
@ -1733,4 +1733,12 @@ public final class CardUtil {
|
||||||
return "" + startingLoyalty;
|
return "" + startingLoyalty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkSetParamForSerializationCompatibility(Set<String> data) {
|
||||||
|
// HashMap uses inner class for Keys without serialization support,
|
||||||
|
// so you can't use it for client-server data
|
||||||
|
if (data != null && data.getClass().getName().endsWith("$KeySet")) {
|
||||||
|
throw new IllegalArgumentException("Can't use KeySet as param, use new HashSet<>(data.keySet()) instead");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue