mirror of
https://github.com/correl/mage.git
synced 2025-01-11 19:13:02 +00:00
* GUI: face down cards improved:
* now it show face up cards in game logs on game end; * now it show face up cards in battlefield on game end (#4635); * fixed that real card face was visible in network data;
This commit is contained in:
parent
6017f35517
commit
4bf3e43da6
4 changed files with 131 additions and 22 deletions
|
@ -26,7 +26,7 @@ public class PermanentView extends CardView {
|
|||
private final boolean summoningSickness;
|
||||
private final int damage;
|
||||
private List<UUID> attachments;
|
||||
private final CardView original; // original card before transforms and modifications
|
||||
private final CardView original; // original card before transforms and modifications (null for opponents face down cards)
|
||||
private final boolean copy;
|
||||
private final String nameOwner; // only filled if != controller
|
||||
private final boolean controlled;
|
||||
|
@ -51,13 +51,17 @@ public class PermanentView extends CardView {
|
|||
attachments.addAll(permanent.getAttachments());
|
||||
}
|
||||
this.attachedTo = permanent.getAttachedTo();
|
||||
|
||||
// show face down cards to all players at the game end
|
||||
boolean showFaceDownInfo = controlled || game.hasEnded();
|
||||
|
||||
if (isToken()) {
|
||||
original = new CardView(((PermanentToken) permanent).getToken().copy(), (Game) null);
|
||||
original.expansionSetCode = permanent.getExpansionSetCode();
|
||||
expansionSetCode = permanent.getExpansionSetCode();
|
||||
} else {
|
||||
if (card != null) {
|
||||
// original may not be face down
|
||||
if (card != null && showFaceDownInfo) {
|
||||
// face down card must be hidden from opponent, but shown on game end for all
|
||||
original = new CardView(card.copy(), (Game) null);
|
||||
} else {
|
||||
original = null;
|
||||
|
@ -71,10 +75,7 @@ public class PermanentView extends CardView {
|
|||
if (permanent.isCopy() && permanent.isFlipCard()) {
|
||||
this.alternateName = permanent.getFlipCardName();
|
||||
} else {
|
||||
if (controlled // controller may always know
|
||||
|| (!morphed && !manifested)) { // others don't know for morph or transformed cards
|
||||
this.alternateName = original.getName();
|
||||
}
|
||||
this.alternateName = original.getName();
|
||||
}
|
||||
}
|
||||
if (permanent.getOwnerId() != null && !permanent.getOwnerId().equals(permanent.getControllerId())) {
|
||||
|
@ -88,8 +89,9 @@ public class PermanentView extends CardView {
|
|||
this.nameOwner = "";
|
||||
}
|
||||
|
||||
// add info for face down permanents
|
||||
if (permanent.isFaceDown(game) && card != null) {
|
||||
if (controlled) {
|
||||
if (showFaceDownInfo) {
|
||||
// must be a morphed or manifested card
|
||||
for (Ability permanentAbility : permanent.getAbilities(game)) {
|
||||
if (permanentAbility.getWorksFaceDown()) {
|
||||
|
|
|
@ -4,13 +4,19 @@ import mage.cards.Card;
|
|||
import mage.constants.EmptyNames;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.view.CardView;
|
||||
import mage.view.GameView;
|
||||
import mage.view.PermanentView;
|
||||
import mage.view.PlayerView;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
public class ManifestTest extends CardTestPlayerBase {
|
||||
|
||||
|
@ -391,7 +397,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
@Test
|
||||
public void testWhisperwoodElemental() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
// Seismic Rupture deals 2 damage to each creature without flying.
|
||||
addCard(Zone.HAND, playerA, "Seismic Rupture", 1);
|
||||
|
@ -474,7 +480,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test
|
||||
public void test_ManifestSorceryAndBlinkIt() {
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1);
|
||||
|
@ -483,11 +489,11 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
// {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||
|
||||
|
||||
// Exile target creature you control, then return that card to the battlefield under your control.
|
||||
addCard(Zone.HAND, playerB, "Cloudshift", 1); //Instant {W}
|
||||
|
||||
|
||||
|
||||
// Devoid
|
||||
// Flying
|
||||
// At the beginning of your upkeep, sacrifice a creature
|
||||
|
@ -502,14 +508,14 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
setChoice(playerB, "Silvercoat Lion");
|
||||
|
||||
waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
// no life gain
|
||||
// no life gain
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
|
@ -517,11 +523,91 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
assertGraveyardCount(playerB, "Cloudshift", 1);
|
||||
|
||||
assertPermanentCount(playerB, "Lightning Bolt", 0);
|
||||
assertExileCount(playerB, "Lightning Bolt", 1);
|
||||
|
||||
assertPermanentCount(playerB, "Lightning Bolt", 0);
|
||||
assertExileCount(playerB, "Lightning Bolt", 1);
|
||||
|
||||
assertHandCount(playerB, "Mountain", 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private PermanentView findFaceDownPermanent(Game game, TestPlayer viewFromPlayer, TestPlayer searchInPlayer) {
|
||||
Permanent perm = game.getBattlefield().getAllPermanents()
|
||||
.stream()
|
||||
.filter(permanent -> permanent.isFaceDown(game))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
Assert.assertNotNull(perm);
|
||||
GameView gameView = new GameView(game.getState(), game, viewFromPlayer.getId(), null);
|
||||
PlayerView playerView = gameView.getPlayers()
|
||||
.stream()
|
||||
.filter(view -> view.getPlayerId().equals(searchInPlayer.getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
Assert.assertNotNull(playerView);
|
||||
PermanentView permanentView = playerView.getBattlefield().values()
|
||||
.stream()
|
||||
.filter(CardView::isFaceDown)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
Assert.assertNotNull(permanentView);
|
||||
return permanentView;
|
||||
}
|
||||
|
||||
private void assertFaceDown(String info, PermanentView faceDownPermanent, String realPermanentName, boolean realInfoMustBeVisible) {
|
||||
if (realInfoMustBeVisible) {
|
||||
// show all info
|
||||
Assert.assertEquals(realPermanentName, faceDownPermanent.getName()); // show real name
|
||||
Assert.assertEquals("2", faceDownPermanent.getPower());
|
||||
Assert.assertEquals("2", faceDownPermanent.getToughness());
|
||||
//
|
||||
Assert.assertNotNull(faceDownPermanent.getOriginal());
|
||||
Assert.assertEquals(realPermanentName, faceDownPermanent.getOriginal().getName());
|
||||
} else {
|
||||
// hide original info
|
||||
Assert.assertEquals(info, "", faceDownPermanent.getName());
|
||||
Assert.assertEquals(info, "2", faceDownPermanent.getPower());
|
||||
Assert.assertEquals(info, "2", faceDownPermanent.getToughness());
|
||||
Assert.assertNull(info, faceDownPermanent.getOriginal());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_FaceDownCardsMustBeVisibleOnGameEnd() {
|
||||
// Exile target creature. Its controller manifests the top card of their library {1}{U}
|
||||
addCard(Zone.HAND, playerA, "Reality Shift");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reality Shift", "Silvercoat Lion");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
runCode("on active game", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
// hide from opponent
|
||||
PermanentView permanent = findFaceDownPermanent(game, playerA, playerB);
|
||||
assertFaceDown("in game: must hide from opponent", permanent, "Mountain", false);
|
||||
|
||||
// show for yourself
|
||||
permanent = findFaceDownPermanent(game, playerB, playerB);
|
||||
assertFaceDown("in game: must show for yourself", permanent, "Mountain", true);
|
||||
});
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
|
||||
// workaround to force end game (can't use other test commands after that)
|
||||
playerA.won(currentGame);
|
||||
Assert.assertTrue(currentGame.hasEnded());
|
||||
|
||||
// show all after game end
|
||||
PermanentView permanent = findFaceDownPermanent(currentGame, playerA, playerB);
|
||||
assertFaceDown("end game: must show for opponent", permanent, "Mountain", true);
|
||||
//
|
||||
permanent = findFaceDownPermanent(currentGame, playerB, playerB);
|
||||
assertFaceDown("end game: must show for yourself", permanent, "Mountain", true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public class AuratouchedMageTest extends CardTestPlayerBase {
|
|||
* made the Mage an artifact, for example, you could search for an Aura with
|
||||
* “enchant artifact.” (2005-10-01)
|
||||
*/
|
||||
@Test
|
||||
@Test // TODO: fix very rare random fails (16 of 1000)
|
||||
public void testAuratouchedMageEffectHasMadeIntoTypeArtifact() {
|
||||
//Expected result: An effect has made Auratouched Mage into an artifact upon entering the battlefield. An aura that only works on artifacts should work.
|
||||
setStrictChooseMode(true);
|
||||
|
|
|
@ -675,8 +675,8 @@ public abstract class GameImpl implements Game {
|
|||
spell = (Spell) obj;
|
||||
} else if (obj != null) {
|
||||
logger.error(String.format(
|
||||
"getSpellOrLKIStack got non-spell id %s correlating to non-spell object %s.",
|
||||
obj.getClass().getName(), obj.getName()),
|
||||
"getSpellOrLKIStack got non-spell id %s correlating to non-spell object %s.",
|
||||
obj.getClass().getName(), obj.getName()),
|
||||
new Throwable()
|
||||
);
|
||||
}
|
||||
|
@ -1398,6 +1398,27 @@ public abstract class GameImpl implements Game {
|
|||
logger.debug("END of gameId: " + this.getId());
|
||||
endTime = new Date();
|
||||
state.endGame();
|
||||
|
||||
// inform players about face down cards
|
||||
state.getBattlefield().getAllPermanents()
|
||||
.stream()
|
||||
.filter(permanent -> permanent.isFaceDown(this))
|
||||
.map(permanent -> {
|
||||
Player player = this.getPlayer(permanent.getControllerId());
|
||||
Card card = permanent.getMainCard();
|
||||
if (card != null) {
|
||||
return String.format("Face down card reveal: %s had %s",
|
||||
(player == null ? "Unknown" : player.getLogName()),
|
||||
permanent.getLogName());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted()
|
||||
.forEach(this::informPlayers);
|
||||
|
||||
// cancel all player dialogs/feedbacks
|
||||
for (Player player : state.getPlayers().values()) {
|
||||
player.abort();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue