mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
game: improved game logs for faced-down spells and exiled cards - now it support popup hint to view card/permanent (part of #11884, related to #11881, #8781)
This commit is contained in:
parent
4334ac8987
commit
c8a9a1a9db
8 changed files with 77 additions and 26 deletions
|
|
@ -30,7 +30,7 @@ public class ExileTargetEffect extends OneShotEffect {
|
|||
private String exileZone = null;
|
||||
private UUID exileId = null;
|
||||
private boolean toSourceExileZone = false; // exile the targets to a source object specific exile zone (takes care of zone change counter)
|
||||
private boolean withName = true;
|
||||
private boolean withName = true; // for face down - allows to hide card name in game logs before real face down apply
|
||||
|
||||
public ExileTargetEffect(String effectText) {
|
||||
this();
|
||||
|
|
@ -75,6 +75,7 @@ public class ExileTargetEffect extends OneShotEffect {
|
|||
return this;
|
||||
}
|
||||
|
||||
// TODO: replace to withHiddenName and instructions to use
|
||||
public void setWithName(boolean withName) {
|
||||
this.withName = withName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ public enum EmptyNames {
|
|||
// TODO: make names for that cards and enable Assert.assertNotEquals("", permanentName); for assertXXX tests
|
||||
// TODO: replace all getName().equals to haveSameNames and haveEmptyName
|
||||
FACE_DOWN_CREATURE(""), // "Face down creature"
|
||||
FACE_DOWN_TOKEN(""); // "Face down token"
|
||||
FACE_DOWN_TOKEN(""), // "Face down token"
|
||||
FACE_DOWN_CARD(""); // "Face down card"
|
||||
|
||||
public static final String EMPTY_NAME_IN_LOGS = "face down object";
|
||||
|
||||
|
|
@ -23,4 +24,10 @@ public enum EmptyNames {
|
|||
public String toString() {
|
||||
return cardName;
|
||||
}
|
||||
|
||||
public static boolean isEmptyName(String objectName) {
|
||||
return objectName.equals(FACE_DOWN_CREATURE.toString())
|
||||
|| objectName.equals(FACE_DOWN_TOKEN.toString())
|
||||
|| objectName.equals(FACE_DOWN_CARD.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import mage.abilities.costs.mana.ActivationManaAbilityStep;
|
|||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
import mage.abilities.keyword.BestowAbility;
|
||||
import mage.abilities.keyword.MorphAbility;
|
||||
import mage.abilities.keyword.PrototypeAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.*;
|
||||
|
|
@ -83,8 +82,8 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
// simulate another side as new card (another code part in continues effect from disturb ability)
|
||||
affectedCard = TransformAbility.transformCardSpellStatic(card, card.getSecondCardFace(), game);
|
||||
}
|
||||
if (ability instanceof PrototypeAbility){
|
||||
affectedCard = ((PrototypeAbility)ability).prototypeCardSpell(card);
|
||||
if (ability instanceof PrototypeAbility) {
|
||||
affectedCard = ((PrototypeAbility) ability).prototypeCardSpell(card);
|
||||
this.prototyped = true;
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +99,7 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
this.ability = ability;
|
||||
this.ability.setControllerId(controllerId);
|
||||
|
||||
if (ability.getSpellAbilityCastMode().isFaceDown()){
|
||||
if (ability.getSpellAbilityCastMode().isFaceDown()) {
|
||||
// TODO: need research:
|
||||
// - why it use game param for color and subtype (possible bug?)
|
||||
// - is it possible to use BecomesFaceDownCreatureEffect.makeFaceDownObject or like that?
|
||||
|
|
@ -202,8 +201,10 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
}
|
||||
|
||||
public String getSpellCastText(Game game) {
|
||||
if (this.getSpellAbility() instanceof MorphAbility) {
|
||||
return "a card face down";
|
||||
if (this.getSpellAbility().getSpellAbilityCastMode().isFaceDown()) {
|
||||
// add face down name with object link, so user can look at it from logs
|
||||
return "a " + GameLog.getColoredObjectIdName(this.getSpellAbility().getCharacteristics(game))
|
||||
+ " using " + this.getSpellAbility().getSpellAbilityCastMode();
|
||||
}
|
||||
|
||||
if (card instanceof AdventureCardSpell) {
|
||||
|
|
@ -677,7 +678,9 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setManaCost(ManaCosts<ManaCost> costs) { this.manaCost = costs.copy(); }
|
||||
public void setManaCost(ManaCosts<ManaCost> costs) {
|
||||
this.manaCost = costs.copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 202.3b When calculating the converted mana cost of an object with an {X}
|
||||
|
|
|
|||
|
|
@ -988,7 +988,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param source
|
||||
* @param game
|
||||
* @param fromZone
|
||||
* @param withName
|
||||
* @param withName for face down: used to hide card name in game logs before real face down status apply
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
|
|||
|
|
@ -1849,7 +1849,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
int last = cards.size();
|
||||
for (Card card : cards.getCards(game)) {
|
||||
current++;
|
||||
sb.append(GameLog.getColoredObjectName(card));
|
||||
sb.append(GameLog.getColoredObjectName(card)); // TODO: see same usage in OfferingAbility for hide card's id (is it needs for reveal too?!)
|
||||
if (current < last) {
|
||||
sb.append(", ");
|
||||
}
|
||||
|
|
@ -4699,7 +4699,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
Player eventPlayer = game.getPlayer(info.event.getPlayerId());
|
||||
if (eventPlayer != null && fromZone != null) {
|
||||
game.informPlayers(eventPlayer.getLogName() + " puts "
|
||||
+ (info.faceDown ? "a card face down " : permanent.getLogName()) + " from "
|
||||
+ GameLog.getColoredObjectIdName(permanent) + " from "
|
||||
+ fromZone.toString().toLowerCase(Locale.ENGLISH) + " onto the Battlefield"
|
||||
+ CardUtil.getSourceLogName(game, source, permanent.getId()));
|
||||
}
|
||||
|
|
@ -4725,10 +4725,23 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
break;
|
||||
case EXILED:
|
||||
for (Card card : cards) {
|
||||
// 708.9.
|
||||
// If a face-down permanent or a face-down component of a merged permanent moves from the
|
||||
// battlefield to any other zone, its owner must reveal it to all players as they move it.
|
||||
// If a face-down spell moves from the stack to any zone other than the battlefield,
|
||||
// its owner must reveal it to all players as they move it. If a player leaves the game,
|
||||
// all face-down permanents, face-down components of merged permanents, and face-down spells
|
||||
// owned by that player must be revealed to all players. At the end of each game, all
|
||||
// face-down permanents, face-down components of merged permanents, and face-down spells must
|
||||
// be revealed to all players.
|
||||
|
||||
// force to show face down name in logs
|
||||
// TODO: replace it to real reveal code somehow?
|
||||
fromZone = game.getState().getZone(card.getId());
|
||||
boolean withName = (fromZone == Zone.BATTLEFIELD
|
||||
|| fromZone == Zone.STACK)
|
||||
|| !card.isFaceDown(game);
|
||||
|
||||
if (moveCardToExileWithInfo(card, null, "", source, game, fromZone, withName)) {
|
||||
successfulMovedCards.add(card);
|
||||
}
|
||||
|
|
@ -5006,10 +5019,19 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
if (Zone.EXILED.equals(game.getState().getZone(card.getId()))) { // only if target zone was not replaced
|
||||
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
|
||||
+ (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
|
||||
+ (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
|
||||
+ ' ' : "") + "to the exile zone" + CardUtil.getSourceLogName(game, source, card.getId()));
|
||||
String visibleName;
|
||||
if (withName) {
|
||||
// warning, withName param used to forced name show of the face down card (see 708.9.)
|
||||
if (card.getName().isEmpty()) {
|
||||
throw new IllegalStateException("Wrong code usage: method must find real card name, but found nothing", new Throwable());
|
||||
}
|
||||
visibleName = card.getLogName() + (card.isCopy() ? " (Copy)" : "");
|
||||
} else {
|
||||
visibleName = "a " + GameLog.getNeutralObjectIdName(EmptyNames.FACE_DOWN_CARD.toString(), card.getId());
|
||||
}
|
||||
game.informPlayers(this.getLogName() + " moves " + visibleName
|
||||
+ (fromZone != null ? " from " + fromZone.toString().toLowerCase(Locale.ENGLISH) : "")
|
||||
+ " to the exile zone" + CardUtil.getSourceLogName(game, source, card.getId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -757,7 +757,9 @@ public final class CardUtil {
|
|||
}
|
||||
|
||||
public static boolean haveEmptyName(String name) {
|
||||
return name == null || name.isEmpty() || name.equals(EmptyNames.FACE_DOWN_CREATURE.toString()) || name.equals(EmptyNames.FACE_DOWN_TOKEN.toString());
|
||||
return name == null
|
||||
|| name.isEmpty()
|
||||
|| EmptyNames.isEmptyName(name);
|
||||
}
|
||||
|
||||
public static boolean haveEmptyName(MageObject object) {
|
||||
|
|
|
|||
|
|
@ -33,12 +33,15 @@ public final class GameLog {
|
|||
static final String LOG_TT_COLOR_COLORLESS = "#94A4BA";
|
||||
static final String LOG_COLOR_NEUTRAL = "#F0F8FF"; // AliceBlue
|
||||
|
||||
private static String getNameForLogs(MageObject mageObject) {
|
||||
if (mageObject.getName().equals(EmptyNames.FACE_DOWN_CREATURE.toString())
|
||||
|| mageObject.getName().equals(EmptyNames.FACE_DOWN_TOKEN.toString())) {
|
||||
private static String getNameForLogs(MageObject object) {
|
||||
return getNameForLogs(object.getName());
|
||||
}
|
||||
|
||||
private static String getNameForLogs(String objectName) {
|
||||
if (EmptyNames.isEmptyName(objectName)) {
|
||||
return EmptyNames.EMPTY_NAME_IN_LOGS;
|
||||
} else {
|
||||
return mageObject.getName();
|
||||
return objectName;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,14 +77,27 @@ public final class GameLog {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create object "link" in game logs
|
||||
*/
|
||||
public static String getNeutralObjectIdName(String objectName, UUID objectId) {
|
||||
return getColoredObjectIdName(
|
||||
new ObjectColor(),
|
||||
objectId,
|
||||
getNameForLogs(objectName),
|
||||
String.format("[%s]", objectId.toString().substring(0, 3)),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare html text with additional object info (can be used for card popup in GUI)
|
||||
*
|
||||
* @param color text color of the colored part
|
||||
* @param objectID object id
|
||||
* @param visibleColorPart colored part, popup will be work on it
|
||||
* @param color text color of the colored part
|
||||
* @param objectID object id
|
||||
* @param visibleColorPart colored part, popup will be work on it
|
||||
* @param visibleNormalPart additional part with default color
|
||||
* @param alternativeName alternative name, popup will use it on unknown object id or name
|
||||
* @param alternativeName alternative name, popup will use it on unknown object id or name
|
||||
* @return
|
||||
*/
|
||||
public static String getColoredObjectIdName(ObjectColor color,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue