[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

@ -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;
}
}