forked from External/mage
Face down images and cards rework (#11873)
Face down changes: * GUI: added visible face down type and real card name for controller/owner (opponent can see it after game ends); * GUI: added day/night button to view real card for controller/owner (opponent can see it after game ends); * game: fixed that faced-down card can render symbols, abilities and other hidden data from a real card; * images: added image support for normal faced-down cards; * images: added image support for morph and megamorph faced-down cards; * images: added image support for foretell faced-down cards; Other changes: * images: fixed missing tokens from DDD set; * images: no more client restart to apply newly downloaded images or render settings; * images: improved backface image quality (use main menu -> symbols to download it);
This commit is contained in:
parent
4901de12c1
commit
e38a79f231
104 changed files with 2178 additions and 1495 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package mage.game;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.*;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -27,7 +26,7 @@ public final class ZonesHandler {
|
|||
|
||||
public static boolean cast(ZoneChangeInfo info, Ability source, Game game) {
|
||||
if (maybeRemoveFromSourceZone(info, game, source)) {
|
||||
placeInDestinationZone(info,0, source, game);
|
||||
placeInDestinationZone(info, 0, source, game);
|
||||
// create a group zone change event if a card is moved to stack for casting (it's always only one card, but some effects check for group events (one or more xxx))
|
||||
Set<Card> cards = new HashSet<>();
|
||||
Set<PermanentToken> tokens = new HashSet<>();
|
||||
|
|
@ -38,12 +37,12 @@ public final class ZonesHandler {
|
|||
cards.add(targetCard);
|
||||
}
|
||||
game.fireEvent(new ZoneChangeGroupEvent(
|
||||
cards,
|
||||
tokens,
|
||||
info.event.getSourceId(),
|
||||
info.event.getSource(),
|
||||
info.event.getPlayerId(),
|
||||
info.event.getFromZone(),
|
||||
cards,
|
||||
tokens,
|
||||
info.event.getSourceId(),
|
||||
info.event.getSource(),
|
||||
info.event.getPlayerId(),
|
||||
info.event.getFromZone(),
|
||||
info.event.getToZone()));
|
||||
// normal movement
|
||||
game.fireEvent(info.event);
|
||||
|
|
@ -325,33 +324,50 @@ public final class ZonesHandler {
|
|||
// Handle all normal cases
|
||||
Card card = getTargetCard(game, event.getTargetId());
|
||||
if (card == null) {
|
||||
// If we can't find the card we can't remove it.
|
||||
// if we can't find the card we can't remove it.
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
boolean isGoodToMove = false;
|
||||
if (info.faceDown) {
|
||||
card.setFaceDown(true, game);
|
||||
// any card can be moved as face down (doubled faced cards also support face down)
|
||||
isGoodToMove = true;
|
||||
} else if (event.getToZone().equals(Zone.BATTLEFIELD)) {
|
||||
if (!card.isPermanent(game)
|
||||
&& (!card.isTransformable() || Boolean.FALSE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())))) {
|
||||
// Non permanents (Instants, Sorceries, ... stay in the zone they are if an abilty/effect tries to move it to the battlefield
|
||||
return false;
|
||||
}
|
||||
// non-permanents can't move to battlefield
|
||||
// "return to battlefield transformed" abilities uses game state value instead "info.transformed", so check it too
|
||||
// TODO: possible bug with non permanent on second side like Life // Death, see https://github.com/magefree/mage/issues/11573
|
||||
// need to check second side here, not status only
|
||||
// TODO: possible bug with Nightbound, search all usage of getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED and insert additional check Ability.checkCard
|
||||
boolean wantToPutTransformed = card.isTransformable()
|
||||
&& Boolean.TRUE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId()));
|
||||
isGoodToMove = card.isPermanent(game) || wantToPutTransformed;
|
||||
} else {
|
||||
// other zones allows to move
|
||||
isGoodToMove = true;
|
||||
}
|
||||
if (!isGoodToMove) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: is it buggy? Card characteristics are global - if you change face down then it will be
|
||||
// changed in original card too, not in blueprint only
|
||||
card.setFaceDown(info.faceDown, game);
|
||||
|
||||
boolean success = false;
|
||||
if (!game.replaceEvent(event)) {
|
||||
Zone fromZone = event.getFromZone();
|
||||
if (event.getToZone() == Zone.BATTLEFIELD) {
|
||||
// prepare card and permanent
|
||||
// If needed take attributes from the spell (e.g. color of spell was changed)
|
||||
card = takeAttributesFromSpell(card, event, game);
|
||||
// PUT TO BATTLEFIELD AS PERMANENT
|
||||
// prepare card and permanent (card must contain full data, even for face down)
|
||||
// if needed to take attributes from the spell (e.g. color of spell was changed)
|
||||
card = prepareBlueprintCardFromSpell(card, event, game);
|
||||
|
||||
// controlling player can be replaced so use event player now
|
||||
Permanent permanent;
|
||||
if (card instanceof MeldCard) {
|
||||
permanent = new PermanentMeld(card, event.getPlayerId(), game);
|
||||
} else if (card instanceof ModalDoubleFacedCard) {
|
||||
// main mdf card must be processed before that call (e.g. only halfes can be moved to battlefield)
|
||||
// main mdf card must be processed before that call (e.g. only halves can be moved to battlefield)
|
||||
throw new IllegalStateException("Unexpected trying of move mdf card to battlefield instead half");
|
||||
} else if (card instanceof Permanent) {
|
||||
throw new IllegalStateException("Unexpected trying of move permanent to battlefield instead card");
|
||||
|
|
@ -361,11 +377,12 @@ public final class ZonesHandler {
|
|||
|
||||
// put onto battlefield with possible counters
|
||||
game.getPermanentsEntering().put(permanent.getId(), permanent);
|
||||
card.checkForCountersToAdd(permanent, source, game);
|
||||
card.applyEnterWithCounters(permanent, source, game);
|
||||
|
||||
permanent.setTapped(info instanceof ZoneChangeInfo.Battlefield
|
||||
&& ((ZoneChangeInfo.Battlefield) info).tapped);
|
||||
|
||||
|
||||
// if need prototyped version
|
||||
if (Zone.STACK == event.getFromZone()) {
|
||||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||
if (spell != null) {
|
||||
|
|
@ -375,29 +392,34 @@ public final class ZonesHandler {
|
|||
|
||||
permanent.setFaceDown(info.faceDown, game);
|
||||
if (info.faceDown) {
|
||||
card.setFaceDown(false, game);
|
||||
// TODO: need research cards with "setFaceDown(false"
|
||||
// TODO: delete after new release and new face down bugs (old code remove face down status from a card for unknown reason), 2024-02-20
|
||||
//card.setFaceDown(false, game);
|
||||
}
|
||||
|
||||
// make sure the controller of all continuous effects of this card are switched to the current controller
|
||||
game.setScopeRelevant(true);
|
||||
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
|
||||
if (permanent.entersBattlefield(source, game, fromZone, true)
|
||||
&& card.removeFromZone(game, fromZone, source)) {
|
||||
success = true;
|
||||
event.setTarget(permanent);
|
||||
} else {
|
||||
// revert controller to owner if permanent does not enter
|
||||
game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId());
|
||||
game.getPermanentsEntering().remove(permanent.getId());
|
||||
try {
|
||||
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
|
||||
if (permanent.entersBattlefield(source, game, fromZone, true)
|
||||
&& card.removeFromZone(game, fromZone, source)) {
|
||||
success = true;
|
||||
event.setTarget(permanent);
|
||||
} else {
|
||||
// revert controller to owner if permanent does not enter
|
||||
game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId());
|
||||
game.getPermanentsEntering().remove(permanent.getId());
|
||||
}
|
||||
} finally {
|
||||
game.setScopeRelevant(false);
|
||||
}
|
||||
game.setScopeRelevant(false);
|
||||
} else if (event.getTarget() != null) {
|
||||
card.setFaceDown(info.faceDown, game);
|
||||
// PUT PERMANENT TO OTHER ZONE (e.g. remove only)
|
||||
Permanent target = event.getTarget();
|
||||
success = target.removeFromZone(game, fromZone, source)
|
||||
&& game.getPlayer(target.getControllerId()).removeFromBattlefield(target, source, game);
|
||||
} else {
|
||||
card.setFaceDown(info.faceDown, game);
|
||||
// PUT CARD TO OTHER ZONE
|
||||
success = card.removeFromZone(game, fromZone, source);
|
||||
}
|
||||
}
|
||||
|
|
@ -434,17 +456,30 @@ public final class ZonesHandler {
|
|||
return order;
|
||||
}
|
||||
|
||||
private static Card takeAttributesFromSpell(Card card, ZoneChangeEvent event, Game game) {
|
||||
private static Card prepareBlueprintCardFromSpell(Card card, ZoneChangeEvent event, Game game) {
|
||||
card = card.copy();
|
||||
if (Zone.STACK == event.getFromZone()) {
|
||||
// TODO: wtf, why only colors!? Must research and remove colors workaround or add all other data like types too
|
||||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||
if (spell != null && !spell.isFaceDown(game)) {
|
||||
// TODO: wtf, why only colors!? Must research and remove colors workaround
|
||||
|
||||
// old version
|
||||
if (false && spell != null && !spell.isFaceDown(game)) {
|
||||
if (!card.getColor(game).equals(spell.getColor(game))) {
|
||||
// the card that is referenced to in the permanent is copied and the spell attributes are set to this copied card
|
||||
card.getColor(game).setColor(spell.getColor(game));
|
||||
}
|
||||
}
|
||||
|
||||
// new version
|
||||
if (true && spell != null && spell.getSpellAbility() != null) {
|
||||
Card characteristics = spell.getSpellAbility().getCharacteristics(game);
|
||||
if (!characteristics.isFaceDown(game)) {
|
||||
if (!card.getColor(game).equals(characteristics.getColor(game))) {
|
||||
// TODO: don't work with prototyped spells (setColor can't set colorless color)
|
||||
card.getColor(game).setColor(characteristics.getColor(game));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return card;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue