This commit is contained in:
Goudt 2017-01-20 01:09:37 +01:00
commit 016ed3adc1
13 changed files with 168 additions and 146 deletions

View file

@ -14,7 +14,6 @@ import java.awt.Paint;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import mage.client.dialog.PreferencesDialog;
import mage.constants.AbilityType;
import mage.constants.CardType;
@ -171,11 +170,12 @@ public abstract class CardRenderer {
}
/**
* How far does a card have to be spaced down from
* a rendered card to show it's entire name line?
* This function is a bit of a hack, as different card faces need
* slightly different spacing, but we need it in a static context
* so that spacing is consistent in GY / deck views etc.
* How far does a card have to be spaced down from a rendered card to show
* it's entire name line? This function is a bit of a hack, as different
* card faces need slightly different spacing, but we need it in a static
* context so that spacing is consistent in GY / deck views etc.
*
* @param cardWidth
* @return
*/
public static int getCardTopHeight(int cardWidth) {
@ -186,7 +186,7 @@ public abstract class CardRenderer {
BOX_HEIGHT_MIN,
BOX_HEIGHT_FRAC * cardWidth * 1.4f);
int borderWidth = getBorderWidth(cardWidth);
return 2*borderWidth + boxHeight;
return 2 * borderWidth + boxHeight;
}
// The Draw Method

View file

@ -19,14 +19,16 @@ import java.util.regex.Pattern;
/**
* @author stravant@gmail.com
*
*
* Various static utilities for use in the card renderer
*/
public class CardRendererUtils {
/**
* Convert an abstract image, whose underlying implementation may or may
* not be a BufferedImage into a BufferedImage by creating one and coping
* the contents if it is not, and simply up-casting if it is.
* Convert an abstract image, whose underlying implementation may or may not
* be a BufferedImage into a BufferedImage by creating one and coping the
* contents if it is not, and simply up-casting if it is.
*
* @param img The image to convert
* @return The converted image
*/
@ -35,7 +37,7 @@ public class CardRendererUtils {
if (img == null) {
return null;
}
// Already a buffered image?
if (img instanceof BufferedImage) {
return (BufferedImage) img;
@ -52,25 +54,25 @@ public class CardRendererUtils {
// Return the buffered image
return bimage;
}
// Draw a rounded box with a 2-pixel border
// Used on various card parts.
public static void drawRoundedBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Color fill) {
g.setColor(new Color(0, 0, 0, 150));
g.drawOval(x-1, y-1, bevel*2, h);
g.drawOval(x - 1, y - 1, bevel * 2, h);
g.setPaint(border);
g.drawOval(x, y, bevel*2-1, h-1);
g.drawOval(x + w - bevel*2, y, bevel*2-1, h-1);
g.drawOval(x+1, y+1, bevel*2-3, h-3);
g.drawOval(x+1 + w - bevel*2, y+1, bevel*2-3, h-3);
g.drawRect(x + bevel, y, w - 2*bevel, h-1);
g.drawRect(x+1 + bevel, y+1, w - 2*bevel-2, h-3);
g.drawOval(x, y, bevel * 2 - 1, h - 1);
g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1);
g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3);
g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3);
g.drawRect(x + bevel, y, w - 2 * bevel, h - 1);
g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3);
g.setColor(fill);
g.fillOval(x+2, y+2, bevel*2-4, h-4);
g.fillOval(x+2 + w - bevel*2, y+2, bevel*2-4, h-4);
g.fillRect(x + bevel, y+2, w - 2*bevel, h-4);
g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4);
g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4);
g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4);
}
// Get the width of a mana cost rendered with ManaSymbols.draw
public static int getManaCostWidth(String manaCost, int symbolSize) {
int width = 0;
@ -82,18 +84,19 @@ public class CardRendererUtils {
}
return width;
}
// Abbreviate a piece of rules text, making substitutions to decrease its
// length. Also abbreviate reminder text.
private static final Pattern abbreviationPattern;
private static final Map<String, String> abbreviations = new HashMap<String, String>();
private static final Map<String, String> abbreviations = new HashMap<>();
private static final Pattern killReminderTextPattern;
static {
// Available abbreviations
abbreviations.put("enters the battlefield", "ETB");
abbreviations.put("less than", "<");
abbreviations.put("greater than", ">");
// Compile into regex
String patternString = "(";
Iterator<String> it = abbreviations.keySet().iterator();
@ -105,10 +108,11 @@ public class CardRendererUtils {
}
patternString += ")";
abbreviationPattern = Pattern.compile(patternString);
// Reminder text killing
killReminderTextPattern = Pattern.compile("\\([^\\)]*\\)");
}
public static String abbreviateRule(String rule) {
StringBuffer build = new StringBuffer();
Matcher match = abbreviationPattern.matcher(rule);
@ -118,6 +122,7 @@ public class CardRendererUtils {
match.appendTail(build);
return build.toString();
}
public static String killReminderText(String rule) {
return killReminderTextPattern.matcher(rule).replaceAll("");
}

View file

@ -311,7 +311,7 @@ public class ModernCardRenderer extends CardRenderer {
Rectangle2D rect;
if (useInventionFrame()) {
rect = new Rectangle2D.Float(0, 0, 1, 1);
} else if (cardView.getFrameStyle().isFullArt() || cardView.isToken()) {
} else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) {
rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f);
} else {
rect = new Rectangle2D.Float(.079f, .11f, .84f, .42f);
@ -320,7 +320,7 @@ public class ModernCardRenderer extends CardRenderer {
}
private float getTypeLineYFrac() {
if (cardView.isToken()) {
if (cardView.isToken() && cardView.getCardNumber() == null) {
return TYPE_LINE_Y_FRAC_TOKEN;
} else if (cardView.getFrameStyle().isFullArt()) {
return TYPE_LINE_Y_FRAC_FULL_ART;

View file

@ -39,6 +39,8 @@ import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.InfoEffect;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
@ -47,6 +49,7 @@ import mage.constants.MultiplayerAttackOption;
import mage.constants.Outcome;
import mage.constants.PhaseStep;
import mage.constants.RangeOfInfluence;
import mage.constants.SetType;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.game.command.Emblem;
@ -156,14 +159,26 @@ class MomirEffect extends OneShotEffect {
// should this be random across card names, or card printings?
CardCriteria criteria = new CardCriteria().types(CardType.CREATURE).convertedManaCost(value);
List<CardInfo> options = CardRepository.instance.findCards(criteria);
if (options != null && !options.isEmpty()) {
Card card = options.get(RandomUtil.nextInt(options.size())).getCard();
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(card);
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), false, false);
} else {
if (options == null || options.isEmpty()) {
game.informPlayers("No random creature card with converted mana cost of " + value + " was found.");
return false;
}
EmptyToken token = new EmptyToken(); // search for a non custom set creature
while (token.getName().isEmpty() && !options.isEmpty()) {
int index = RandomUtil.nextInt(options.size());
ExpansionSet expansionSet = Sets.findSet(options.get(index).getSetCode());
if (expansionSet == null || expansionSet.getSetType().equals(SetType.CUSTOM_SET)) {
options.remove(index);
} else {
Card card = options.get(index).getCard();
if (card != null) {
CardUtil.copyTo(token).from(card);
} else {
options.remove(index);
}
}
}
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), false, false);
return true;
}
}

View file

@ -55,7 +55,7 @@ public class CogworkAssembler extends CardImpl {
public CogworkAssembler(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
this.subtype.add("Assembly-Worker");
this.power = new MageInt(2);
this.toughness = new MageInt(3);

View file

@ -124,6 +124,7 @@ class GontisMachinationsFirstLostLifeThisTurnWatcher extends Watcher {
public GontisMachinationsFirstLostLifeThisTurnWatcher(final GontisMachinationsFirstLostLifeThisTurnWatcher watcher) {
super(watcher);
this.playersLostLife.putAll(watcher.playersLostLife);
}
@Override

View file

@ -46,7 +46,6 @@ import mage.game.stack.StackObject;
import mage.players.Player;
import mage.watchers.common.FirstSpellCastThisTurnWatcher;
/**
*
* @author jeffwadsworth
@ -54,7 +53,7 @@ import mage.watchers.common.FirstSpellCastThisTurnWatcher;
public class MaelstromNexus extends CardImpl {
public MaelstromNexus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{W}{U}{B}{R}{G}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{U}{B}{R}{G}");
// The first spell you cast each turn has cascade.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MaelstromNexusGainCascadeFirstSpellEffect()), new FirstSpellCastThisTurnWatcher());
@ -72,7 +71,7 @@ public class MaelstromNexus extends CardImpl {
class MaelstromNexusGainCascadeFirstSpellEffect extends ContinuousEffectImpl {
private Ability cascadeAbility = new CascadeAbility();
private final Ability cascadeAbility = new CascadeAbility();
public MaelstromNexusGainCascadeFirstSpellEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
@ -106,4 +105,4 @@ class MaelstromNexusGainCascadeFirstSpellEffect extends ContinuousEffectImpl {
}
return false;
}
}
}

View file

@ -28,7 +28,7 @@
package mage.cards.q;
import java.util.UUID;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.DealsDamageToACreatureAllTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.costs.common.SacrificeSourceCost;
@ -37,13 +37,10 @@ import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.filter.StaticFilters;
import mage.target.common.TargetCreaturePermanent;
/**
@ -57,7 +54,11 @@ public class QuestForTheGemblades extends CardImpl {
// Whenever a creature you control deals combat damage to a creature, you may put a quest counter on Quest for the Gemblades.
this.addAbility(new QuestForTheGembladesTriggeredAbility());
this.addAbility(new DealsDamageToACreatureAllTriggeredAbility(
new AddCountersSourceEffect(CounterType.QUEST.createInstance()), false,
StaticFilters.FILTER_CONTROLLED_A_CREATURE,
SetTargetPointer.PERMANENT, true));
// Remove a quest counter from Quest for the Gemblades and sacrifice it: Put four +1/+1 counters on target creature.
SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
new AddCountersTargetEffect(CounterType.P1P1.createInstance(4)),
@ -76,43 +77,3 @@ public class QuestForTheGemblades extends CardImpl {
return new QuestForTheGemblades(this);
}
}
class QuestForTheGembladesTriggeredAbility extends TriggeredAbilityImpl {
public QuestForTheGembladesTriggeredAbility() {
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.QUEST.createInstance()), true);
}
public QuestForTheGembladesTriggeredAbility(final QuestForTheGembladesTriggeredAbility ability) {
super(ability);
}
@Override
public QuestForTheGembladesTriggeredAbility copy() {
return new QuestForTheGembladesTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGED_CREATURE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (((DamagedCreatureEvent) event).isCombatDamage()) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent == null) {
permanent = (Permanent) game.getLastKnownInformation(event.getSourceId(), Zone.BATTLEFIELD);
}
if (permanent != null && permanent.getCardType().contains(CardType.CREATURE) && permanent.getControllerId().equals(this.getControllerId())) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever a creature you control deals combat damage to a creature, " + super.getRule();
}
}

View file

@ -29,18 +29,13 @@ package mage.cards.t;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.DealsDamageToACreatureTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.common.DealsDamageToACreatureAllTriggeredAbility;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.target.targetpointer.FixedTarget;
import mage.constants.SetTargetPointer;
import mage.filter.common.FilterCreaturePermanent;
/**
*
@ -56,7 +51,10 @@ public class ToxinSliver extends CardImpl {
this.toughness = new MageInt(3);
// Whenever a Sliver deals combat damage to a creature, destroy that creature. It can't be regenerated.
this.addAbility(new DealsDamageToACreatureTriggeredAbility(new DestroyTargetEffect(true), true, false, true));
this.addAbility(new DealsDamageToACreatureAllTriggeredAbility(
new DestroyTargetEffect(true), false,
new FilterCreaturePermanent("Sliver","a Sliver"),
SetTargetPointer.PERMANENT, true));
}
@ -69,48 +67,3 @@ public class ToxinSliver extends CardImpl {
return new ToxinSliver(this);
}
}
class DealsDamageTriggeredAbility extends TriggeredAbilityImpl {
private boolean setTargetPointer;
public DealsDamageTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) {
super(Zone.BATTLEFIELD, effect, optional);
this.setTargetPointer = setTargetPointer;
}
public DealsDamageTriggeredAbility(final DealsDamageTriggeredAbility ability) {
super(ability);
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public DealsDamageTriggeredAbility copy() {
return new DealsDamageTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGED_CREATURE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.getPermanent(event.getSourceId()).hasSubtype("Sliver", game)) {
if (setTargetPointer) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(game.getControllerId(event.getTargetId())));
effect.setValue("damage", event.getAmount());
}
}
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever a Sliver deals damage to a creature" + super.getRule();
}
}

View file

@ -0,0 +1,83 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author Ludwig.Hirth
*/
public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityImpl {
private final boolean combatDamageOnly;
private final FilterPermanent filterPermanent;
private final SetTargetPointer setTargetPointer;
public DealsDamageToACreatureAllTriggeredAbility(Effect effect, boolean optional, FilterPermanent filterPermanent, SetTargetPointer setTargetPointer, boolean combatDamageOnly) {
super(Zone.BATTLEFIELD, effect, optional);
this.combatDamageOnly = combatDamageOnly;
this.setTargetPointer = setTargetPointer;
this.filterPermanent = filterPermanent;
}
public DealsDamageToACreatureAllTriggeredAbility(final DealsDamageToACreatureAllTriggeredAbility ability) {
super(ability);
this.combatDamageOnly = ability.combatDamageOnly;
this.filterPermanent = ability.filterPermanent;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public DealsDamageToACreatureAllTriggeredAbility copy() {
return new DealsDamageToACreatureAllTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGED_CREATURE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!combatDamageOnly || ((DamagedCreatureEvent) event).isCombatDamage()) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (permanent != null && filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) {
for (Effect effect : this.getEffects()) {
effect.setValue("damage", event.getAmount());
effect.setValue("sourceId", event.getSourceId());
switch (setTargetPointer) {
case PLAYER:
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game)));
break;
}
}
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever " + filterPermanent.getMessage() + " deals "
+ (combatDamageOnly ? "combat ":"") + "damage to a creature, " + super.getRule();
}
}

View file

@ -53,7 +53,7 @@ public class SacrificeTargetCost extends CostImpl {
target.setNotTarget(true); // sacrifice is never targeted
this.text = "sacrifice "
+ ((target.getNumberOfTargets() != 1 || (target.getTargetName().startsWith("an") || target.getTargetName().startsWith("a ")))
? "" : "a ") + target.getTargetName();
? "" : (target.getTargetName().startsWith("artifact") ? "an " : "a ")) + target.getTargetName();
target.setTargetName(target.getTargetName() + " (to sacrifice)");
}
@ -123,4 +123,4 @@ public class SacrificeTargetCost extends CostImpl {
public List<Permanent> getPermanents() {
return permanents;
}
}
}

View file

@ -9,6 +9,7 @@ import mage.constants.CardType;
import mage.filter.common.FilterArtifactCard;
import mage.filter.common.FilterArtifactCreaturePermanent;
import mage.filter.common.FilterControlledArtifactPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterCreatureSpell;
@ -25,6 +26,7 @@ public class StaticFilters {
public static final FilterCreaturePermanent FILTER_ARTIFACT_CREATURE_PERMANENT = new FilterArtifactCreaturePermanent();
public static final FilterPermanent FILTER_PERMANENT_ARTIFACT_OR_CREATURE = new FilterPermanent("artifact or creature");
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT_OR_CREATURE = new FilterControlledPermanent("artifact or creature you control");
public static final FilterControlledPermanent FILTER_CONTROLLED_A_CREATURE = new FilterControlledCreaturePermanent("a creature you control");
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT = new FilterControlledArtifactPermanent();
public static final FilterArtifactCard FILTER_CARD_ARTIFACT = new FilterArtifactCard();
public static final FilterNonlandCard FILTER_CARD_NON_LAND = new FilterNonlandCard();

View file

@ -12,7 +12,8 @@ import mage.watchers.Watcher;
/**
* @author jeffwadsworth
**/
*
*/
public class FirstSpellCastThisTurnWatcher extends Watcher {
private final Map<UUID, UUID> playerFirstSpellCast = new HashMap<>();
@ -24,6 +25,8 @@ public class FirstSpellCastThisTurnWatcher extends Watcher {
public FirstSpellCastThisTurnWatcher(final FirstSpellCastThisTurnWatcher watcher) {
super(watcher);
playerFirstSpellCast.putAll(watcher.playerFirstSpellCast);
playerFirstCastSpell.putAll(watcher.playerFirstSpellCast);
}
@Override
@ -61,4 +64,4 @@ public class FirstSpellCastThisTurnWatcher extends Watcher {
return playerFirstSpellCast.get(playerId);
}
}
}
}