[ZNR] Improved modal double faces cards in GUI (#7012)

This commit is contained in:
Oleg Agafonov 2020-11-01 12:49:27 +04:00
parent 02e19f0a3f
commit 4893c5b1ac
11 changed files with 156 additions and 87 deletions

View file

@ -204,15 +204,32 @@ public class TestCardRenderDialog extends MageDialog {
game.addPlayer(playerOpponent, deck);
List<CardView> cardViews = new ArrayList<>();
///*
/* // test morphed
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false)); // mountain
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true)); // Judith, the Scourge Diva
//*/
cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card)
cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure card)
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, false, false)); // face down
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", true, false, true)); // morphed
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested
//*/
/* //test emblems
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle
cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon
cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani
cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum
//*/
//* //test split, transform and mdf in hands
cardViews.add(createHandCard(game, playerYou.getId(), "SOI", "97")); // Accursed Witch
cardViews.add(createHandCard(game, playerYou.getId(), "UMA", "225")); // Fire // Ice
cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "14")); // Giant Killer
cardViews.add(createHandCard(game, playerYou.getId(), "ZNR", "134")); // Akoum Warrior
//*/
// duplicate cards
if (checkBoxGenerateManyCards.isSelected()) {
@ -226,17 +243,6 @@ public class TestCardRenderDialog extends MageDialog {
}
}
/*
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle
cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon
cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani
cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum
//*/
BigCard big = new BigCard();
CardsView view = new CardsView(cardViews);
cardsPanel.loadCards(view, big, game.getId());

View file

@ -417,7 +417,6 @@ public class CardView extends SimpleCardView {
this.subTypes = card.getSubtype(game);
this.superTypes = card.getSuperType();
this.color = card.getColor(game);
this.transformable = card.isTransformable();
this.flipCard = card.isFlipCard();
this.faceDown = !showFaceUp;
@ -436,7 +435,7 @@ public class CardView extends SimpleCardView {
this.tokenDescriptor = card.getTokenDescriptor();
}
//
// set code und card number for token copies to get the image
// set code and card number for token copies to get the image
this.rules = card.getRules(game);
this.type = ((PermanentToken) card).getToken().getTokenType();
} else {
@ -445,6 +444,8 @@ public class CardView extends SimpleCardView {
}
// transformable, double faces cards
this.transformable = card.isTransformable();
Card secondSideCard = card.getSecondCardFace();
if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard);
@ -458,14 +459,13 @@ public class CardView extends SimpleCardView {
this.originalName = card.getName();
}
/*
if (card instanceof ModalDoubleFacesCard) {
this.transformable = true; // enable GUI day/night button
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard());
this.alternateName = mdfCard.getRightHalfCard().getName();
this.originalName = card.getName();
}
*/
if (card instanceof Spell) {
this.mageObjectType = MageObjectType.SPELL;

View file

@ -1,13 +1,10 @@
package mage.cards;
import com.google.common.collect.ImmutableList;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
import mage.ObjectColor;
import mage.abilities.*;
import mage.abilities.hint.Hint;
import mage.abilities.hint.HintUtils;
import mage.abilities.keyword.FlashbackAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.repository.PluginClassloaderRegistery;
@ -22,6 +19,7 @@ import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.util.CardUtil;
import mage.util.GameLog;
import mage.util.SubTypeList;
import mage.watchers.Watcher;
@ -218,52 +216,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
game.getState().getCardState(objectId).addInfo(key, value);
}
protected static final List<String> rulesError = ImmutableList.of("Exception occurred in rules generation");
@Override
public List<String> getRules() {
try {
return getAbilities().getRules(this.getName());
} catch (Exception e) {
logger.info("Exception in rules generation for card: " + this.getName(), e);
}
return rulesError;
Abilities<Ability> sourceAbilities = this.getAbilities();
return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities);
}
@Override
public List<String> getRules(Game game) {
try {
List<String> rules = getAbilities(game).getRules(getName());
if (game != null) {
// debug state
for (String data : game.getState().getCardState(objectId).getInfo().values()) {
rules.add(data);
}
// ability hints
List<String> abilityHints = new ArrayList<>();
if (HintUtils.ABILITY_HINTS_ENABLE) {
for (Ability ability : abilities) {
for (Hint hint : ability.getHints()) {
String s = hint.getText(game, ability);
if (s != null && !s.isEmpty()) {
abilityHints.add(s);
}
}
}
}
// restrict hints only for permanents, not cards
// total hints
if (!abilityHints.isEmpty()) {
rules.add(HintUtils.HINT_START_MARK);
HintUtils.appendHints(rules, abilityHints);
}
}
return rules;
} catch (Exception e) {
logger.error("Exception in rules generation for card: " + this.getName(), e);
}
return rulesError;
Abilities<Ability> sourceAbilities = this.getAbilities(game);
return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities);
}
/**

View file

@ -12,6 +12,7 @@ import mage.abilities.costs.mana.ManaCosts;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.util.CardUtil;
import mage.util.SubTypeList;
import java.util.ArrayList;
@ -157,20 +158,7 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
@Override
public Abilities<Ability> getAbilities() {
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
// ignore default spell ability from main card (only halfes are actual)
for (Ability ability : super.getAbilities()) {
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) {
continue;
}
allAbilites.add(ability);
}
allAbilites.addAll(super.getAbilities());
allAbilites.addAll(leftHalfCard.getAbilities());
allAbilites.addAll(rightHalfCard.getAbilities());
return allAbilites;
return getInnerAbilities(false);
}
public Abilities<Ability> getSharedAbilities(Game game) {
@ -180,6 +168,10 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
@Override
public Abilities<Ability> getAbilities(Game game) {
return getInnerAbilities(game, false);
}
private Abilities<Ability> getInnerAbilities(Game game, boolean showOnlyMainSide) {
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
// ignore default spell ability from main card (only halfes are actual)
@ -191,10 +183,50 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
}
allAbilites.addAll(leftHalfCard.getAbilities(game));
allAbilites.addAll(rightHalfCard.getAbilities(game));
if (!showOnlyMainSide) {
allAbilites.addAll(rightHalfCard.getAbilities(game));
}
return allAbilites;
}
private Abilities<Ability> getInnerAbilities(boolean showOnlyMainSide) {
Abilities<Ability> allAbilites = new AbilitiesImpl<>();
// ignore default spell ability from main card (only halfes are actual)
for (Ability ability : super.getAbilities()) {
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) {
continue;
}
allAbilites.add(ability);
}
allAbilites.addAll(leftHalfCard.getAbilities());
if (!showOnlyMainSide) {
allAbilites.addAll(rightHalfCard.getAbilities());
}
return allAbilites;
}
@Override
public List<String> getRules() {
// rules must show only main side (another side visible by toggle/transform button in GUI)
// card hints from both sides
return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(),
this.getInnerAbilities(true), this.getInnerAbilities(false)
);
}
@Override
public List<String> getRules(Game game) {
// rules must show only main side (another side visible by toggle/transform button in GUI)
// card hints from both sides
return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(),
this.getInnerAbilities(game, true), this.getInnerAbilities(game, false)
);
}
@Override
public boolean hasAbility(Ability ability, Game game) {
return super.hasAbility(ability, game);

View file

@ -100,4 +100,10 @@ public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubl
this.power = power;
this.toughness = toughness;
}
@Override
public String getIdName() {
// id must send to main card (popup card hint in game logs)
return getName() + " [" + parentCard.getId().toString().substring(0, 3) + ']';
}
}

View file

@ -78,7 +78,6 @@ public class MockCard extends CardImpl {
if (this.isPlaneswalker()) {
String startingLoyaltyString = card.getStartingLoyalty();
if (startingLoyaltyString.isEmpty()) {
//Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty.");
} else {
try {
this.startingLoyalty = Integer.parseInt(startingLoyaltyString);

View file

@ -198,14 +198,7 @@ public class CardInfo {
rulesList.add(rule);
}
} else if (card instanceof ModalDoubleFacesCard) {
for (String rule : ((ModalDoubleFacesCard) card).getLeftHalfCard().getRules()) {
length += rule.length();
rulesList.add(rule);
}
for (String rule : ((ModalDoubleFacesCard) card).getRightHalfCard().getRules()) {
length += rule.length();
rulesList.add(rule);
}
// mdf card return main side's rules only (GUI can toggle it to another side)
for (String rule : card.getRules()) {
length += rule.length();
rulesList.add(rule);

View file

@ -264,7 +264,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
// ability hints
List<String> abilityHints = new ArrayList<>();
if (HintUtils.ABILITY_HINTS_ENABLE) {
for (Ability ability : abilities) {
for (Ability ability : getAbilities(game)) {
for (Hint hint : ability.getHints()) {
String s = hint.getText(game, ability);
if (s != null && !s.isEmpty()) {
@ -341,7 +341,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return rules;
} catch (Exception e) {
return rulesError;
return CardUtil.RULES_ERROR_INFO;
}
}

View file

@ -178,6 +178,12 @@ public class Spell extends StackObjImpl implements Card {
+ " as Adventure spell of " + GameLog.getColoredObjectIdName(adventureCard);
}
if (card instanceof ModalDoubleFacesCardHalf) {
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard();
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString(), mdfCard)
+ " as mdf side of " + GameLog.getColoredObjectIdName(mdfCard);
}
return GameLog.replaceNameByColoredName(card, getSpellAbility().toString());
}

View file

@ -1309,7 +1309,14 @@ public abstract class PlayerImpl implements Player, Serializable {
card.getId(), card.getId(), playerId, activationStatus.getApprovingObject());
landEventAfter.setZone(cardZoneBefore);
game.fireEvent(landEventAfter);
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
String playText = getLogName() + " plays " + card.getLogName();
if (card instanceof ModalDoubleFacesCardHalf) {
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard();
playText = getLogName() + " plays " + GameLog.replaceNameByColoredName(card, card.getName(), mdfCard)
+ " as MDF side of " + GameLog.getColoredObjectIdName(mdfCard);
}
game.fireInformEvent(playText);
// game.removeBookmark(bookmark);
resetStoredBookmark(game); // prevent undo after playing a land
return true;

View file

@ -1,5 +1,6 @@
package mage.util;
import com.google.common.collect.ImmutableList;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Abilities;
@ -9,6 +10,8 @@ import mage.abilities.SpellAbility;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.*;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.HintUtils;
import mage.cards.Card;
import mage.cards.MeldCard;
import mage.cards.ModalDoubleFacesCard;
@ -26,6 +29,7 @@ import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.Target;
import mage.util.functions.CopyTokenFunction;
import org.apache.log4j.Logger;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
@ -39,6 +43,10 @@ import java.util.stream.Collectors;
*/
public final class CardUtil {
private static final Logger logger = Logger.getLogger(CardUtil.class);
public static final List<String> RULES_ERROR_INFO = ImmutableList.of("Exception occurred in rules generation");
private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
@ -897,4 +905,54 @@ public final class CardUtil {
return card.getName();
}
}
public static List<String> getCardRulesWithAdditionalInfo(UUID cardId, String cardName,
Abilities<Ability> rulesSource, Abilities<Ability> hintAbilities) {
return getCardRulesWithAdditionalInfo(null, cardId, cardName, rulesSource, hintAbilities);
}
/**
* Prepare rules list from abilities
*
* @param rulesSource abilities list to show as rules
* @param hintsSource abilities list to show as card hints only (you can add additional hints here; exameple: from second or transformed side)
*/
public static List<String> getCardRulesWithAdditionalInfo(Game game, UUID cardId, String cardName,
Abilities<Ability> rulesSource, Abilities<Ability> hintsSource) {
try {
List<String> rules = rulesSource.getRules(cardName);
if (game != null) {
// debug state
for (String data : game.getState().getCardState(cardId).getInfo().values()) {
rules.add(data);
}
// ability hints
List<String> abilityHints = new ArrayList<>();
if (HintUtils.ABILITY_HINTS_ENABLE) {
for (Ability ability : hintsSource) {
for (Hint hint : ability.getHints()) {
String s = hint.getText(game, ability);
if (s != null && !s.isEmpty()) {
abilityHints.add(s);
}
}
}
}
// restrict hints only for permanents, not cards
// total hints
if (!abilityHints.isEmpty()) {
rules.add(HintUtils.HINT_START_MARK);
HintUtils.appendHints(rules, abilityHints);
}
}
return rules;
} catch (Exception e) {
logger.error("Exception in rules generation for card: " + cardName, e);
}
return RULES_ERROR_INFO;
}
}