[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); game.addPlayer(playerOpponent, deck);
List<CardView> cardViews = new ArrayList<>(); 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", "263", 0, 0, 0, false)); // mountain
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true)); // Judith, the Scourge Diva 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(), "DIS", "153")); // Odds // Ends (split card)
cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure 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", 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", true, false, true)); // morphed
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested 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 // duplicate cards
if (checkBoxGenerateManyCards.isSelected()) { 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(); BigCard big = new BigCard();
CardsView view = new CardsView(cardViews); CardsView view = new CardsView(cardViews);
cardsPanel.loadCards(view, big, game.getId()); cardsPanel.loadCards(view, big, game.getId());

View file

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

View file

@ -1,13 +1,10 @@
package mage.cards; package mage.cards;
import com.google.common.collect.ImmutableList;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectImpl; import mage.MageObjectImpl;
import mage.Mana; import mage.Mana;
import mage.ObjectColor; import mage.ObjectColor;
import mage.abilities.*; import mage.abilities.*;
import mage.abilities.hint.Hint;
import mage.abilities.hint.HintUtils;
import mage.abilities.keyword.FlashbackAbility; import mage.abilities.keyword.FlashbackAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.repository.PluginClassloaderRegistery; import mage.cards.repository.PluginClassloaderRegistery;
@ -22,6 +19,7 @@ import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.game.stack.StackObject; import mage.game.stack.StackObject;
import mage.util.CardUtil;
import mage.util.GameLog; import mage.util.GameLog;
import mage.util.SubTypeList; import mage.util.SubTypeList;
import mage.watchers.Watcher; import mage.watchers.Watcher;
@ -218,52 +216,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
game.getState().getCardState(objectId).addInfo(key, value); game.getState().getCardState(objectId).addInfo(key, value);
} }
protected static final List<String> rulesError = ImmutableList.of("Exception occurred in rules generation");
@Override @Override
public List<String> getRules() { public List<String> getRules() {
try { Abilities<Ability> sourceAbilities = this.getAbilities();
return getAbilities().getRules(this.getName()); return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities);
} catch (Exception e) {
logger.info("Exception in rules generation for card: " + this.getName(), e);
}
return rulesError;
} }
@Override @Override
public List<String> getRules(Game game) { public List<String> getRules(Game game) {
try { Abilities<Ability> sourceAbilities = this.getAbilities(game);
List<String> rules = getAbilities(game).getRules(getName()); return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities);
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;
} }
/** /**

View file

@ -12,6 +12,7 @@ import mage.abilities.costs.mana.ManaCosts;
import mage.constants.*; import mage.constants.*;
import mage.game.Game; import mage.game.Game;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.util.CardUtil;
import mage.util.SubTypeList; import mage.util.SubTypeList;
import java.util.ArrayList; import java.util.ArrayList;
@ -157,20 +158,7 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
@Override @Override
public Abilities<Ability> getAbilities() { public Abilities<Ability> getAbilities() {
Abilities<Ability> allAbilites = new AbilitiesImpl<>(); return getInnerAbilities(false);
// 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;
} }
public Abilities<Ability> getSharedAbilities(Game game) { public Abilities<Ability> getSharedAbilities(Game game) {
@ -180,6 +168,10 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
@Override @Override
public Abilities<Ability> getAbilities(Game game) { public Abilities<Ability> getAbilities(Game game) {
return getInnerAbilities(game, false);
}
private Abilities<Ability> getInnerAbilities(Game game, boolean showOnlyMainSide) {
Abilities<Ability> allAbilites = new AbilitiesImpl<>(); Abilities<Ability> allAbilites = new AbilitiesImpl<>();
// ignore default spell ability from main card (only halfes are actual) // 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(leftHalfCard.getAbilities(game));
allAbilites.addAll(rightHalfCard.getAbilities(game)); if (!showOnlyMainSide) {
allAbilites.addAll(rightHalfCard.getAbilities(game));
}
return allAbilites; 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 @Override
public boolean hasAbility(Ability ability, Game game) { public boolean hasAbility(Ability ability, Game game) {
return super.hasAbility(ability, game); return super.hasAbility(ability, game);

View file

@ -100,4 +100,10 @@ public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubl
this.power = power; this.power = power;
this.toughness = toughness; 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()) { if (this.isPlaneswalker()) {
String startingLoyaltyString = card.getStartingLoyalty(); String startingLoyaltyString = card.getStartingLoyalty();
if (startingLoyaltyString.isEmpty()) { if (startingLoyaltyString.isEmpty()) {
//Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty.");
} else { } else {
try { try {
this.startingLoyalty = Integer.parseInt(startingLoyaltyString); this.startingLoyalty = Integer.parseInt(startingLoyaltyString);

View file

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

View file

@ -264,7 +264,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
// ability hints // ability hints
List<String> abilityHints = new ArrayList<>(); List<String> abilityHints = new ArrayList<>();
if (HintUtils.ABILITY_HINTS_ENABLE) { if (HintUtils.ABILITY_HINTS_ENABLE) {
for (Ability ability : abilities) { for (Ability ability : getAbilities(game)) {
for (Hint hint : ability.getHints()) { for (Hint hint : ability.getHints()) {
String s = hint.getText(game, ability); String s = hint.getText(game, ability);
if (s != null && !s.isEmpty()) { if (s != null && !s.isEmpty()) {
@ -341,7 +341,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return rules; return rules;
} catch (Exception e) { } 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); + " 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()); 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()); card.getId(), card.getId(), playerId, activationStatus.getApprovingObject());
landEventAfter.setZone(cardZoneBefore); landEventAfter.setZone(cardZoneBefore);
game.fireEvent(landEventAfter); 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); // game.removeBookmark(bookmark);
resetStoredBookmark(game); // prevent undo after playing a land resetStoredBookmark(game); // prevent undo after playing a land
return true; return true;

View file

@ -1,5 +1,6 @@
package mage.util; package mage.util;
import com.google.common.collect.ImmutableList;
import mage.MageObject; import mage.MageObject;
import mage.Mana; import mage.Mana;
import mage.abilities.Abilities; import mage.abilities.Abilities;
@ -9,6 +10,8 @@ import mage.abilities.SpellAbility;
import mage.abilities.costs.VariableCost; import mage.abilities.costs.VariableCost;
import mage.abilities.costs.mana.*; import mage.abilities.costs.mana.*;
import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.HintUtils;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.MeldCard; import mage.cards.MeldCard;
import mage.cards.ModalDoubleFacesCard; import mage.cards.ModalDoubleFacesCard;
@ -26,6 +29,7 @@ import mage.game.stack.Spell;
import mage.players.Player; import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.util.functions.CopyTokenFunction; import mage.util.functions.CopyTokenFunction;
import org.apache.log4j.Logger;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
@ -39,6 +43,10 @@ import java.util.stream.Collectors;
*/ */
public final class CardUtil { 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"; private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", 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(); 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;
}
} }