mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 21:42:07 -08:00
* GUI: new reworked GUI and card render engine, card icons and dozens of other fixes (see full list in related PR);
This commit is contained in:
parent
df98cc3e62
commit
a1da5ef437
304 changed files with 7266 additions and 5093 deletions
|
|
@ -9,6 +9,7 @@ import mage.abilities.costs.mana.ManaCosts;
|
|||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.constants.*;
|
||||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
|
|
@ -523,6 +524,10 @@ public interface Ability extends Controllable, Serializable {
|
|||
|
||||
Ability addHint(Hint hint);
|
||||
|
||||
List<CardIcon> getIcons();
|
||||
|
||||
Ability addIcon(CardIcon cardIcon);
|
||||
|
||||
Ability addCustomOutcome(Outcome customOutcome);
|
||||
|
||||
Outcome getCustomOutcome();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mage.abilities.effects.Effects;
|
|||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.mana.ManaEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.SplitCard;
|
||||
|
|
@ -70,6 +71,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
protected TargetAdjuster targetAdjuster = null;
|
||||
protected CostAdjuster costAdjuster = null;
|
||||
protected List<Hint> hints = new ArrayList<>();
|
||||
protected List<CardIcon> icons = new ArrayList<>();
|
||||
protected Outcome customOutcome = null; // uses for AI decisions instead effects
|
||||
protected MageIdentifier identifier; // used to identify specific ability (e.g. to match with corresponding watcher)
|
||||
protected String appendToRule = null;
|
||||
|
|
@ -120,6 +122,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
for (Hint hint : ability.getHints()) {
|
||||
this.hints.add(hint.copy());
|
||||
}
|
||||
for (CardIcon icon : ability.getIcons()) {
|
||||
this.icons.add(icon.copy());
|
||||
}
|
||||
this.customOutcome = ability.customOutcome;
|
||||
this.identifier = ability.identifier;
|
||||
this.activated = ability.activated;
|
||||
|
|
@ -1294,6 +1299,17 @@ public abstract class AbilityImpl implements Ability {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CardIcon> getIcons() {
|
||||
return this.icons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ability addIcon(CardIcon cardIcon) {
|
||||
this.icons.add(cardIcon);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ability addCustomOutcome(Outcome customOutcome) {
|
||||
this.customOutcome = customOutcome;
|
||||
|
|
|
|||
|
|
@ -8,12 +8,15 @@ import mage.constants.Zone;
|
|||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author varaghar
|
||||
*/
|
||||
public class CrewWithToughnessAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final CrewWithToughnessAbility instance = new CrewWithToughnessAbility();
|
||||
private static final CrewWithToughnessAbility instance;
|
||||
|
||||
static {
|
||||
instance = new CrewWithToughnessAbility();
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.PlayLandAbility;
|
||||
import mage.constants.Zone;
|
||||
|
||||
public class PlayLandFromGraveyardAbility extends PlayLandAbility {
|
||||
public PlayLandFromGraveyardAbility(String name){
|
||||
super(name);
|
||||
zone = Zone.GRAVEYARD;
|
||||
}
|
||||
}
|
||||
|
|
@ -47,8 +47,7 @@ public class BecomeBlockedTargetEffect extends OneShotEffect {
|
|||
continue;
|
||||
}
|
||||
boolean alreadyBlocked = combatGroup.getBlocked();
|
||||
combatGroup.setBlocked(true); // non-banded creatures
|
||||
combatGroup.setBlocked(true, game); // this only works for banded creatures and needs to be checked out
|
||||
combatGroup.setBlocked(true, game);
|
||||
if (alreadyBlocked) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.abilities.effects.common;
|
|||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.icon.abilities.CrewAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
|
@ -11,6 +12,7 @@ public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
public CrewsVehicleSourceTriggeredAbility(Effect effect) {
|
||||
super(Zone.BATTLEFIELD, effect, false);
|
||||
this.addIcon(CrewAbilityIcon.instance);
|
||||
}
|
||||
|
||||
public CrewsVehicleSourceTriggeredAbility(final CrewsVehicleSourceTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class HintUtils {
|
|||
|
||||
// text
|
||||
if (text != null && color != null) {
|
||||
String hex = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
|
||||
String hex = colorToHtml(color);
|
||||
res = String.format("<font color=%s>%s</font>", hex, text);
|
||||
} else {
|
||||
res = text;
|
||||
|
|
@ -55,4 +55,14 @@ public class HintUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Conver color to html hex format like #7FFFD4
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
public static String colorToHtml(Color color) {
|
||||
return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
Mage/src/main/java/mage/abilities/icon/CardIcon.java
Normal file
34
Mage/src/main/java/mage/abilities/icon/CardIcon.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
import mage.util.Copyable;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public interface CardIcon extends Copyable<CardIcon> {
|
||||
|
||||
CardIconType getIconType();
|
||||
|
||||
String getText();
|
||||
|
||||
/**
|
||||
* Card hint on popup, support html and mana/restrict symbols
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getHint();
|
||||
|
||||
/**
|
||||
* Combined info (text + hint)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default String getCombinedInfo() {
|
||||
String res = getText();
|
||||
if (!getHint().isEmpty()) {
|
||||
res += res.isEmpty() ? "" : " - ";
|
||||
res += getHint();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
11
Mage/src/main/java/mage/abilities/icon/CardIconCategory.java
Normal file
11
Mage/src/main/java/mage/abilities/icon/CardIconCategory.java
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum CardIconCategory {
|
||||
|
||||
ABILITY, // example: flying
|
||||
PLAYABLE_COUNT,
|
||||
SYSTEM
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum CardIconComparator implements Comparator<CardIcon> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int compare(CardIcon a, CardIcon b) {
|
||||
// by icon type
|
||||
int res = Integer.compare(a.getIconType().getSortOrder(), b.getIconType().getSortOrder());
|
||||
|
||||
// by text
|
||||
if (res == 0) {
|
||||
res = String.CASE_INSENSITIVE_ORDER.compare(a.getCombinedInfo(), b.getCombinedInfo());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
47
Mage/src/main/java/mage/abilities/icon/CardIconImpl.java
Normal file
47
Mage/src/main/java/mage/abilities/icon/CardIconImpl.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class CardIconImpl implements CardIcon {
|
||||
|
||||
private final CardIconType cardIconType;
|
||||
private final String text;
|
||||
private final String hint;
|
||||
|
||||
public CardIconImpl(CardIconType cardIconType, String hint) {
|
||||
this(cardIconType, hint, "");
|
||||
}
|
||||
|
||||
public CardIconImpl(CardIconType cardIconType, String hint, String text) {
|
||||
this.text = text;
|
||||
this.hint = hint;
|
||||
this.cardIconType = cardIconType;
|
||||
}
|
||||
|
||||
public CardIconImpl(final CardIconImpl icon) {
|
||||
this.cardIconType = icon.cardIconType;
|
||||
this.text = icon.text;
|
||||
this.hint = icon.hint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return cardIconType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return new CardIconImpl(this);
|
||||
}
|
||||
}
|
||||
22
Mage/src/main/java/mage/abilities/icon/CardIconOrder.java
Normal file
22
Mage/src/main/java/mage/abilities/icon/CardIconOrder.java
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
/**
|
||||
* Card icons order on the card's side
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum CardIconOrder {
|
||||
|
||||
START,
|
||||
CENTER,
|
||||
END;
|
||||
|
||||
public static CardIconOrder fromString(String value) {
|
||||
for (CardIconOrder item : CardIconOrder.values()) {
|
||||
if (item.toString().equals(value)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
37
Mage/src/main/java/mage/abilities/icon/CardIconPosition.java
Normal file
37
Mage/src/main/java/mage/abilities/icon/CardIconPosition.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
/**
|
||||
* Card icons position on the card
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum CardIconPosition {
|
||||
|
||||
TOP(7),
|
||||
LEFT(7),
|
||||
BOTTOM(7),
|
||||
RIGHT(7),
|
||||
CORNER_TOP_LEFT(1),
|
||||
CORNER_TOP_RIGHT(1),
|
||||
CORNER_BOTTOM_LEFT(1),
|
||||
CORNER_BOTTOM_RIGHT(1);
|
||||
|
||||
private final int maxIconsAmount; // max icons for the panel
|
||||
|
||||
CardIconPosition(int maxIconsAmount) {
|
||||
this.maxIconsAmount = maxIconsAmount;
|
||||
}
|
||||
|
||||
public int getMaxIconsAmount() {
|
||||
return this.maxIconsAmount;
|
||||
}
|
||||
|
||||
public static CardIconPosition fromString(String value) {
|
||||
for (CardIconPosition item : CardIconPosition.values()) {
|
||||
if (item.toString().equals(value)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
/**
|
||||
* Card icons drawing settings for MageCard
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class CardIconRenderSettings {
|
||||
|
||||
// custom settings for test render dialog
|
||||
CardIconPosition customPosition = null;
|
||||
CardIconOrder customOrder = null;
|
||||
int customMaxVisibleCount = 0;
|
||||
int customIconSizePercent = 0;
|
||||
boolean debugMode = false;
|
||||
|
||||
public CardIconRenderSettings() {
|
||||
}
|
||||
|
||||
public CardIconRenderSettings withCustomPosition(CardIconPosition customPosition) {
|
||||
this.customPosition = customPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CardIconRenderSettings withCustomOrder(CardIconOrder customOrder) {
|
||||
this.customOrder = customOrder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CardIconRenderSettings withCustomMaxVisibleCount(int customMaxVisibleCount) {
|
||||
this.customMaxVisibleCount = customMaxVisibleCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CardIconRenderSettings withCustomIconSizePercent(int customIconSizePercent) {
|
||||
this.customIconSizePercent = customIconSizePercent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CardIconRenderSettings withDebugMode(boolean debugMode) {
|
||||
this.debugMode = debugMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CardIconOrder getCustomOrder() {
|
||||
return customOrder;
|
||||
}
|
||||
|
||||
public CardIconPosition getCustomPosition() {
|
||||
return customPosition;
|
||||
}
|
||||
|
||||
public int getCustomIconSizePercent() {
|
||||
return customIconSizePercent;
|
||||
}
|
||||
|
||||
public int getCustomMaxVisibleCount() {
|
||||
return customMaxVisibleCount;
|
||||
}
|
||||
|
||||
public boolean isDebugMode() {
|
||||
return debugMode;
|
||||
}
|
||||
}
|
||||
62
Mage/src/main/java/mage/abilities/icon/CardIconType.java
Normal file
62
Mage/src/main/java/mage/abilities/icon/CardIconType.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package mage.abilities.icon;
|
||||
|
||||
/**
|
||||
* Icons for GUI card panel
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum CardIconType {
|
||||
|
||||
/*
|
||||
- Use svg icon file name from folder ..\Mage.Client\src\main\resources\card\icons
|
||||
- Look at readme-icons.txt file for more info about files format
|
||||
- Sort order can be same for icons (it will be sorted by text)
|
||||
- Sort order goes from 0 to 1000 (from first to last icons in normal order like START,
|
||||
but CENTER and END order can be different)
|
||||
*/
|
||||
|
||||
PLAYABLE_COUNT("prepared/cog.svg", CardIconCategory.PLAYABLE_COUNT, 100),
|
||||
//
|
||||
ABILITY_FLYING("prepared/feather-alt.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_DEFENDER("prepared/chess-rook.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_DEATHTOUCH("prepared/skull-crossbones.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_LIFELINK("prepared/link.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_DOUBLE_STRIKE("prepared/mars-double.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_FIRST_STRIKE("prepared/mars.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_CREW("prepared/truck-monster.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_TRAMPLE("prepared/grimace.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_HEXPROOF("prepared/expand-arrows-alt.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_INFECT("prepared/flask.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_INDESTRUCTIBLE("prepared/ankh.svg", CardIconCategory.ABILITY, 100),
|
||||
ABILITY_VIGILANCE("prepared/khanda.svg", CardIconCategory.ABILITY, 100),
|
||||
//
|
||||
SYSTEM_COMBINED("prepared/square-fill.svg", CardIconCategory.SYSTEM, 1000), // inner usage, must use last order
|
||||
SYSTEM_DEBUG("prepared/link.svg", CardIconCategory.SYSTEM, 1000); // used for test render dialog
|
||||
|
||||
private final String resourceName;
|
||||
private final CardIconCategory category;
|
||||
private final int sortOrder;
|
||||
|
||||
CardIconType(String resourceName, CardIconCategory category, int sortOrder) {
|
||||
this.resourceName = resourceName;
|
||||
this.category = category;
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public CardIconCategory getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
public int getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return resourceName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum CrewAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_CREW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Crew ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum DeathtouchAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_DEATHTOUCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Deathtouch ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum DefenderAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_DEFENDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Defender ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum DoubleStrikeAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_DOUBLE_STRIKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Double strike ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum FirstStrikeAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_FIRST_STRIKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "First strike ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum FlyingAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_FLYING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Flying ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum HexproofAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_HEXPROOF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Hexproof ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum IndestructibleAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_INDESTRUCTIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Indestructible ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum InfectAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_INFECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Infect ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum LifelinkAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_LIFELINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Lifelink ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum TrampleAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_TRAMPLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Trumple ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package mage.abilities.icon.abilities;
|
||||
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum VigilanceAbilityIcon implements CardIcon {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public CardIconType getIconType() {
|
||||
return CardIconType.ABILITY_VIGILANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHint() {
|
||||
return "Vigilance ability";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardIcon copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package mage.abilities.icon.system;
|
||||
|
||||
import mage.abilities.icon.CardIconImpl;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class CombinedCountIcon extends CardIconImpl {
|
||||
|
||||
public CombinedCountIcon(int hiddenCount, String hint) {
|
||||
super(CardIconType.SYSTEM_COMBINED, hint, "+" + hiddenCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package mage.abilities.icon.system;
|
||||
|
||||
import mage.abilities.icon.CardIconImpl;
|
||||
import mage.abilities.icon.CardIconType;
|
||||
import mage.players.PlayableObjectStats;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* GUI: playable icon, shown if card have an improtant playable abilities
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public final class PlayableCountIcon extends CardIconImpl {
|
||||
|
||||
public PlayableCountIcon(PlayableObjectStats objectStats) {
|
||||
// show full stats, so users can see normal and important abilities
|
||||
super(CardIconType.PLAYABLE_COUNT, getHint(objectStats), getAmount(objectStats));
|
||||
}
|
||||
|
||||
static private String getAmount(PlayableObjectStats objectStats) {
|
||||
return String.valueOf(objectStats.getPlayableAmount());
|
||||
}
|
||||
|
||||
static private String getHint(PlayableObjectStats objectStats) {
|
||||
String res = "Playable abilities: " + objectStats.getPlayableAmount();
|
||||
// abilities list already sorted
|
||||
List<String> list = objectStats.getPlayableAbilities();
|
||||
final int[] counter = {0};
|
||||
if (list.size() > 0) {
|
||||
res += "<br>" + list
|
||||
.stream()
|
||||
.map(s -> {
|
||||
counter[0]++;
|
||||
return " " + counter[0] + ". " + s;
|
||||
})
|
||||
.collect(Collectors.joining("<br>")) + "";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageInt;
|
||||
|
|
@ -9,6 +8,7 @@ import mage.abilities.costs.Cost;
|
|||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect;
|
||||
import mage.abilities.hint.HintUtils;
|
||||
import mage.abilities.icon.abilities.CrewAbilityIcon;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -36,6 +36,7 @@ public class CrewAbility extends SimpleActivatedAbility {
|
|||
public CrewAbility(int value) {
|
||||
super(Zone.BATTLEFIELD, new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT), new CrewCost(value));
|
||||
this.addEffect(new AddCardTypeSourceEffect(Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE));
|
||||
this.addIcon(CrewAbilityIcon.instance);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.DeathtouchAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class DeathtouchAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final DeathtouchAbility instance = new DeathtouchAbility();
|
||||
private static final DeathtouchAbility instance;
|
||||
|
||||
static {
|
||||
instance = new DeathtouchAbility();
|
||||
instance.addIcon(DeathtouchAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
|
||||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.DefenderAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class DefenderAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final DefenderAbility instance = new DefenderAbility();
|
||||
private static final DefenderAbility instance;
|
||||
|
||||
static {
|
||||
instance = new DefenderAbility();
|
||||
instance.addIcon(DefenderAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.DoubleStrikeAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class DoubleStrikeAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final DoubleStrikeAbility instance = new DoubleStrikeAbility();
|
||||
private static final DoubleStrikeAbility instance;
|
||||
|
||||
static {
|
||||
instance = new DoubleStrikeAbility();
|
||||
instance.addIcon(DoubleStrikeAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.FirstStrikeAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class FirstStrikeAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final FirstStrikeAbility instance = new FirstStrikeAbility();
|
||||
private static final FirstStrikeAbility instance;
|
||||
|
||||
static {
|
||||
instance = new FirstStrikeAbility();
|
||||
instance.addIcon(FirstStrikeAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.EvasionAbility;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.abilities.icon.abilities.FlyingAbilityIcon;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
|
|
@ -17,7 +18,12 @@ import java.io.ObjectStreamException;
|
|||
*/
|
||||
public class FlyingAbility extends EvasionAbility implements MageSingleton {
|
||||
|
||||
private static final FlyingAbility instance = new FlyingAbility();
|
||||
private static final FlyingAbility instance;
|
||||
|
||||
static {
|
||||
instance = new FlyingAbility();
|
||||
instance.addIcon(FlyingAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.abilities.keyword;
|
|||
import mage.MageObject;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.icon.abilities.HexproofAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ public abstract class HexproofBaseAbility extends SimpleStaticAbility implements
|
|||
|
||||
HexproofBaseAbility() {
|
||||
super(Zone.BATTLEFIELD, null);
|
||||
this.addIcon(HexproofAbilityIcon.instance);
|
||||
}
|
||||
|
||||
public abstract boolean checkObject(MageObject source, Game game);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.IndestructibleAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
* OLD RULES: 700.4. If a permanent is indestructible, rules and effects can't
|
||||
|
|
@ -11,18 +12,13 @@ import mage.abilities.StaticAbility;
|
|||
* lethal damage, and they ignore the lethal-damage state-based action (see rule
|
||||
* 704.5g). Rules or effects may cause an indestructible permanent to be
|
||||
* sacrificed, put into a graveyard, or exiled. #
|
||||
*
|
||||
* <p>
|
||||
* 700.4a Although the text "[This permanent] is indestructible" is an ability,
|
||||
* actually being indestructible is neither an ability nor a characteristic.
|
||||
* It's just something that's true about a permanent.
|
||||
*
|
||||
* <p>
|
||||
* NEW RULES
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class IndestructibleAbility extends StaticAbility {
|
||||
|
|
@ -31,6 +27,7 @@ public class IndestructibleAbility extends StaticAbility {
|
|||
|
||||
static {
|
||||
instance = new IndestructibleAbility();
|
||||
instance.addIcon(IndestructibleAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
|
|
|
|||
|
|
@ -1,39 +1,43 @@
|
|||
|
||||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.InfectAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
* 702.87. Infect
|
||||
* 702.87. Infect
|
||||
* <p>
|
||||
* 702.87a. Infect is a static ability.
|
||||
* <p>
|
||||
* 702.87b. Damage dealt to a player by a source with infect doesn't cause that player to lose life. Rather, it causes the player to get that many poison counters. See rule 119.3.
|
||||
* <p>
|
||||
* 702.87c. Damage dealt to a creature by a source with infect isn't marked on that creature. Rather, it causes that many -1/-1 counters to be put on that creature. See rule 119.3.
|
||||
* <p>
|
||||
* 702.87d. If a permanent leaves the battlefield before an effect causes it to deal damage, its last known information
|
||||
* (Last Known Information: Information about an object that's no longer in the zone it's expected to be in, or information about a player that's no longer in the game. This information captures that object's last existence in that zone or that player's last existence in the game....)
|
||||
* 112.7a. Once activated or triggered, an ability exists on the stack independently of its source. Destruction or removal of the source after that time won't affect the ability. Note that some abilities cause a source to do something (for example, "Prodigal Sorcerer deals 1 damage...
|
||||
* 608.2b. If the spell or ability specifies targets, it checks whether the targets are still legal. A target that's no longer in the zone it was in when it was targeted is illegal. Other changes to the game state may cause a target to no longer be legal; for example, its...
|
||||
* 608.2g. If an effect requires information from the game (such as the number of creatures on the battlefield), the answer is determined only once, when the effect is applied. If the effect requires information from a specific object, including the source of the ability itself or a...
|
||||
* 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if they are still in the game; otherwise, the effect uses the last known information about that player before they left the game.
|
||||
* is used to determine whether it had infect.
|
||||
* <p>
|
||||
* 702.87e. The infect rules function no matter what zone an object with infect deals damage from.
|
||||
* <p>
|
||||
* 702.87f. Multiple instances of infect on the same object are redundant.
|
||||
*
|
||||
* 702.87a. Infect is a static ability.
|
||||
*
|
||||
* 702.87b. Damage dealt to a player by a source with infect doesn't cause that player to lose life. Rather, it causes the player to get that many poison counters. See rule 119.3.
|
||||
*
|
||||
* 702.87c. Damage dealt to a creature by a source with infect isn't marked on that creature. Rather, it causes that many -1/-1 counters to be put on that creature. See rule 119.3.
|
||||
*
|
||||
* 702.87d. If a permanent leaves the battlefield before an effect causes it to deal damage, its last known information
|
||||
* (Last Known Information: Information about an object that's no longer in the zone it's expected to be in, or information about a player that's no longer in the game. This information captures that object's last existence in that zone or that player's last existence in the game....)
|
||||
* 112.7a. Once activated or triggered, an ability exists on the stack independently of its source. Destruction or removal of the source after that time won't affect the ability. Note that some abilities cause a source to do something (for example, "Prodigal Sorcerer deals 1 damage...
|
||||
* 608.2b. If the spell or ability specifies targets, it checks whether the targets are still legal. A target that's no longer in the zone it was in when it was targeted is illegal. Other changes to the game state may cause a target to no longer be legal; for example, its...
|
||||
* 608.2g. If an effect requires information from the game (such as the number of creatures on the battlefield), the answer is determined only once, when the effect is applied. If the effect requires information from a specific object, including the source of the ability itself or a...
|
||||
* 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if they are still in the game; otherwise, the effect uses the last known information about that player before they left the game.
|
||||
* is used to determine whether it had infect.
|
||||
*
|
||||
* 702.87e. The infect rules function no matter what zone an object with infect deals damage from.
|
||||
*
|
||||
* 702.87f. Multiple instances of infect on the same object are redundant.
|
||||
*
|
||||
* @author nantuko
|
||||
* @author nantuko
|
||||
*/
|
||||
public class InfectAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final InfectAbility instance = new InfectAbility();
|
||||
private static final InfectAbility instance;
|
||||
|
||||
static {
|
||||
instance = new InfectAbility();
|
||||
instance.addIcon(InfectAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
|
||||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.LifelinkAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class LifelinkAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final LifelinkAbility instance = new LifelinkAbility();
|
||||
private static final LifelinkAbility instance;
|
||||
|
||||
static {
|
||||
instance = new LifelinkAbility();
|
||||
instance.addIcon(LifelinkAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.TrampleAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class TrampleAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final TrampleAbility instance = new TrampleAbility();
|
||||
private static final TrampleAbility instance;
|
||||
|
||||
static {
|
||||
instance = new TrampleAbility();
|
||||
instance.addIcon(TrampleAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.MageSingleton;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.icon.abilities.VigilanceAbilityIcon;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class VigilanceAbility extends StaticAbility implements MageSingleton {
|
||||
|
||||
private static final VigilanceAbility instance = new VigilanceAbility();
|
||||
private static final VigilanceAbility instance;
|
||||
|
||||
static {
|
||||
instance = new VigilanceAbility();
|
||||
instance.addIcon(VigilanceAbilityIcon.instance);
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return instance;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import mage.game.stack.Spell;
|
|||
import mage.game.stack.StackObject;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.ManaUtil;
|
||||
import mage.watchers.Watcher;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -34,12 +35,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(CardImpl.class);
|
||||
|
||||
private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*";
|
||||
private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*";
|
||||
private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*";
|
||||
private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*";
|
||||
private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*";
|
||||
|
||||
protected UUID ownerId;
|
||||
protected String cardNumber;
|
||||
protected String expansionSetCode;
|
||||
|
|
@ -807,60 +802,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
@Override
|
||||
public FilterMana getColorIdentity() {
|
||||
FilterMana mana = new FilterMana();
|
||||
mana.setBlack(getManaCost().getText().matches(regexBlack));
|
||||
mana.setBlue(getManaCost().getText().matches(regexBlue));
|
||||
mana.setGreen(getManaCost().getText().matches(regexGreen));
|
||||
mana.setRed(getManaCost().getText().matches(regexRed));
|
||||
mana.setWhite(getManaCost().getText().matches(regexWhite));
|
||||
|
||||
for (String rule : getRules()) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!mana.isBlack() && (rule.matches(regexBlack) || this.color.isBlack())) {
|
||||
mana.setBlack(true);
|
||||
}
|
||||
if (!mana.isBlue() && (rule.matches(regexBlue) || this.color.isBlue())) {
|
||||
mana.setBlue(true);
|
||||
}
|
||||
if (!mana.isGreen() && (rule.matches(regexGreen) || this.color.isGreen())) {
|
||||
mana.setGreen(true);
|
||||
}
|
||||
if (!mana.isRed() && (rule.matches(regexRed) || this.color.isRed())) {
|
||||
mana.setRed(true);
|
||||
}
|
||||
if (!mana.isWhite() && (rule.matches(regexWhite) || this.color.isWhite())) {
|
||||
mana.setWhite(true);
|
||||
}
|
||||
}
|
||||
if (isTransformable()) {
|
||||
Card secondCard = getSecondCardFace();
|
||||
ObjectColor color = secondCard.getColor(null);
|
||||
mana.setBlack(mana.isBlack() || color.isBlack());
|
||||
mana.setGreen(mana.isGreen() || color.isGreen());
|
||||
mana.setRed(mana.isRed() || color.isRed());
|
||||
mana.setBlue(mana.isBlue() || color.isBlue());
|
||||
mana.setWhite(mana.isWhite() || color.isWhite());
|
||||
for (String rule : secondCard.getRules()) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!mana.isBlack() && rule.matches(regexBlack)) {
|
||||
mana.setBlack(true);
|
||||
}
|
||||
if (!mana.isBlue() && rule.matches(regexBlue)) {
|
||||
mana.setBlue(true);
|
||||
}
|
||||
if (!mana.isGreen() && rule.matches(regexGreen)) {
|
||||
mana.setGreen(true);
|
||||
}
|
||||
if (!mana.isRed() && rule.matches(regexRed)) {
|
||||
mana.setRed(true);
|
||||
}
|
||||
if (!mana.isWhite() && rule.matches(regexWhite)) {
|
||||
mana.setWhite(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mana;
|
||||
return ManaUtil.getColorIdentity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
|
||||
package mage.cards.decks;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
|
|
@ -14,6 +7,9 @@ import mage.game.GameException;
|
|||
import mage.util.DeckUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
public class Deck implements Serializable {
|
||||
|
||||
private String name;
|
||||
|
|
@ -59,8 +55,8 @@ public class Deck implements Serializable {
|
|||
* Warning, AI can't play Mock cards, so call it with extra params in real games or tests
|
||||
*
|
||||
* @param deckCardLists cards to load
|
||||
* @param ignoreErrors - do not raise exception error on wrong deck
|
||||
* @param mockCards - use it for GUI only code, real game cards must be real
|
||||
* @param ignoreErrors - do not raise exception error on wrong deck
|
||||
* @param mockCards - use it for GUI only code, real game cards must be real
|
||||
* @return
|
||||
* @throws GameException
|
||||
*/
|
||||
|
|
@ -182,6 +178,14 @@ public class Deck implements Serializable {
|
|||
return cards;
|
||||
}
|
||||
|
||||
public Card findCard(UUID cardId) {
|
||||
return cards
|
||||
.stream()
|
||||
.filter(card -> card.getId().equals(cardId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public DeckCardLayout getCardsLayout() {
|
||||
return cardsLayout;
|
||||
}
|
||||
|
|
@ -190,6 +194,14 @@ public class Deck implements Serializable {
|
|||
return sideboard;
|
||||
}
|
||||
|
||||
public Card findSideboardCard(UUID cardId) {
|
||||
return sideboard
|
||||
.stream()
|
||||
.filter(card -> card.getId().equals(cardId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public DeckCardLayout getSideboardLayout() {
|
||||
return sideboardLayout;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -781,20 +781,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There are effects, that set an attacker to be blocked. Therefore this
|
||||
* setter can be used.
|
||||
* <p>
|
||||
* This method lacks a band check, use setBlocked(blocked, game) instead.
|
||||
*
|
||||
* @param blocked
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked, Game game) {
|
||||
this.blocked = blocked;
|
||||
for (UUID attackerId : attackers) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.game.draft;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,20 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
|
||||
public enum QueryType {
|
||||
|
||||
ASK, CHOOSE_CHOICE, CHOOSE_ABILITY, CHOOSE_MODE, PICK_TARGET, PICK_ABILITY, SELECT, PLAY_MANA, PLAY_X_MANA, AMOUNT, PICK_CARD, CONSTRUCT, CHOOSE_PILE, PERSONAL_MESSAGE
|
||||
ASK,
|
||||
CHOOSE_CHOICE,
|
||||
CHOOSE_ABILITY,
|
||||
CHOOSE_MODE,
|
||||
PICK_TARGET,
|
||||
PICK_ABILITY,
|
||||
SELECT,
|
||||
PLAY_MANA,
|
||||
PLAY_X_MANA,
|
||||
AMOUNT,
|
||||
PICK_CARD,
|
||||
CONSTRUCT,
|
||||
CHOOSE_PILE,
|
||||
PERSONAL_MESSAGE
|
||||
}
|
||||
|
||||
private String message;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import mage.game.permanent.token.EmptyToken;
|
|||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.GameLog;
|
||||
import mage.util.ManaUtil;
|
||||
import mage.util.SubTypes;
|
||||
|
||||
import java.util.*;
|
||||
|
|
@ -45,12 +46,6 @@ public class Spell extends StackObjImpl implements Card {
|
|||
private final List<SpellAbility> spellAbilities = new ArrayList<>();
|
||||
private final List<Card> spellCards = new ArrayList<>();
|
||||
|
||||
private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*";
|
||||
private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*";
|
||||
private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*";
|
||||
private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*";
|
||||
private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*";
|
||||
|
||||
private final Card card;
|
||||
private final ObjectColor color;
|
||||
private final ObjectColor frameColor;
|
||||
|
|
@ -1000,60 +995,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
|
||||
@Override
|
||||
public FilterMana getColorIdentity() {
|
||||
FilterMana mana = new FilterMana();
|
||||
mana.setBlack(getManaCost().getText().matches(regexBlack));
|
||||
mana.setBlue(getManaCost().getText().matches(regexBlue));
|
||||
mana.setGreen(getManaCost().getText().matches(regexGreen));
|
||||
mana.setRed(getManaCost().getText().matches(regexRed));
|
||||
mana.setWhite(getManaCost().getText().matches(regexWhite));
|
||||
|
||||
for (String rule : getRules()) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!mana.isBlack() && (rule.matches(regexBlack) || this.color.isBlack())) {
|
||||
mana.setBlack(true);
|
||||
}
|
||||
if (!mana.isBlue() && (rule.matches(regexBlue) || this.color.isBlue())) {
|
||||
mana.setBlue(true);
|
||||
}
|
||||
if (!mana.isGreen() && (rule.matches(regexGreen) || this.color.isGreen())) {
|
||||
mana.setGreen(true);
|
||||
}
|
||||
if (!mana.isRed() && (rule.matches(regexRed) || this.color.isRed())) {
|
||||
mana.setRed(true);
|
||||
}
|
||||
if (!mana.isWhite() && (rule.matches(regexWhite) || this.color.isWhite())) {
|
||||
mana.setWhite(true);
|
||||
}
|
||||
}
|
||||
if (isTransformable()) {
|
||||
Card secondCard = getSecondCardFace();
|
||||
ObjectColor objectColor = secondCard.getColor(null);
|
||||
mana.setBlack(mana.isBlack() || objectColor.isBlack());
|
||||
mana.setGreen(mana.isGreen() || objectColor.isGreen());
|
||||
mana.setRed(mana.isRed() || objectColor.isRed());
|
||||
mana.setBlue(mana.isBlue() || objectColor.isBlue());
|
||||
mana.setWhite(mana.isWhite() || objectColor.isWhite());
|
||||
for (String rule : secondCard.getRules()) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!mana.isBlack() && rule.matches(regexBlack)) {
|
||||
mana.setBlack(true);
|
||||
}
|
||||
if (!mana.isBlue() && rule.matches(regexBlue)) {
|
||||
mana.setBlue(true);
|
||||
}
|
||||
if (!mana.isGreen() && rule.matches(regexGreen)) {
|
||||
mana.setGreen(true);
|
||||
}
|
||||
if (!mana.isRed() && rule.matches(regexRed)) {
|
||||
mana.setRed(true);
|
||||
}
|
||||
if (!mana.isWhite() && rule.matches(regexWhite)) {
|
||||
mana.setWhite(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mana;
|
||||
return ManaUtil.getColorIdentity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
|
|||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.icon.CardIcon;
|
||||
import mage.abilities.text.TextPart;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.FrameStyle;
|
||||
|
|
@ -680,6 +681,16 @@ public class StackAbility extends StackObjImpl implements Ability {
|
|||
throw new IllegalArgumentException("Stack ability is not supports hint adding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CardIcon> getIcons() {
|
||||
return this.ability.getIcons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ability addIcon(CardIcon cardIcon) {
|
||||
throw new IllegalArgumentException("Stack ability is not supports icon adding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ability addCustomOutcome(Outcome customOutcome) {
|
||||
throw new IllegalArgumentException("Stack ability is not supports custom outcome adding");
|
||||
|
|
|
|||
136
Mage/src/main/java/mage/players/PlayableObjectStats.java
Normal file
136
Mage/src/main/java/mage/players/PlayableObjectStats.java
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package mage.players;
|
||||
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.PlayLandAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.mana.BasicManaAbility;
|
||||
import mage.constants.SpellAbilityType;
|
||||
import mage.util.Copyable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Contains stats with playable abilities for one object
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayableObjectStats implements Serializable, Copyable<PlayableObjectStats> {
|
||||
|
||||
// map: ability.id -> ability.rules
|
||||
List<PlayableObjectRecord> basicManaAbilities = new ArrayList<>();
|
||||
List<PlayableObjectRecord> basicPlayAbilities = new ArrayList<>();
|
||||
List<PlayableObjectRecord> basicCastAbilities = new ArrayList<>();
|
||||
List<PlayableObjectRecord> other = new ArrayList<>();
|
||||
|
||||
public PlayableObjectStats() {
|
||||
}
|
||||
|
||||
public PlayableObjectStats(List<ActivatedAbility> activatedAbilities) {
|
||||
load(activatedAbilities);
|
||||
}
|
||||
|
||||
public void load(List<ActivatedAbility> activatedAbilities) {
|
||||
this.basicManaAbilities.clear();
|
||||
this.basicPlayAbilities.clear();
|
||||
this.basicCastAbilities.clear();
|
||||
this.other.clear();
|
||||
|
||||
// split abilities to types (it allows to enable or disable playable icon)
|
||||
for (ActivatedAbility ability : activatedAbilities) {
|
||||
List<PlayableObjectRecord> dest;
|
||||
if (ability instanceof BasicManaAbility) {
|
||||
dest = this.basicManaAbilities;
|
||||
} else if (ability instanceof PlayLandAbility) {
|
||||
dest = this.basicPlayAbilities;
|
||||
} else if (ability instanceof SpellAbility && (((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.BASE)) {
|
||||
dest = this.basicCastAbilities;
|
||||
} else {
|
||||
dest = this.other;
|
||||
}
|
||||
|
||||
// collect info about abilities for card icons popup, must be simple online text (html symbols are possible)
|
||||
// some long html tags can be miss (example: ability extra hint) -- that's ok
|
||||
String shortInfo = ability.toString();
|
||||
if (shortInfo.length() > 50) {
|
||||
shortInfo = shortInfo.substring(0, 50 - 1) + "...";
|
||||
}
|
||||
shortInfo = shortInfo.replace("<br>", " ");
|
||||
shortInfo = shortInfo.replace("\n", " ");
|
||||
dest.add(new PlayableObjectRecord(ability.getId(), shortInfo));
|
||||
}
|
||||
}
|
||||
|
||||
public PlayableObjectStats(final PlayableObjectStats source) {
|
||||
for (PlayableObjectRecord rec : source.basicManaAbilities) {
|
||||
this.basicManaAbilities.add(rec.copy());
|
||||
}
|
||||
for (PlayableObjectRecord rec : source.basicPlayAbilities) {
|
||||
this.basicPlayAbilities.add(rec.copy());
|
||||
}
|
||||
for (PlayableObjectRecord rec : source.basicCastAbilities) {
|
||||
this.basicCastAbilities.add(rec.copy());
|
||||
}
|
||||
for (PlayableObjectRecord rec : source.other) {
|
||||
this.other.add(rec.copy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayableObjectStats copy() {
|
||||
return new PlayableObjectStats(this);
|
||||
}
|
||||
|
||||
public int getPlayableAmount() {
|
||||
return this.basicManaAbilities.size()
|
||||
+ this.basicPlayAbilities.size()
|
||||
+ this.basicCastAbilities.size()
|
||||
+ this.other.size();
|
||||
}
|
||||
|
||||
public List<String> getPlayableAbilities() {
|
||||
List<String> all = new ArrayList<>();
|
||||
all.addAll(this.basicManaAbilities.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList()));
|
||||
all.addAll(this.basicPlayAbilities.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList()));
|
||||
all.addAll(this.basicCastAbilities.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList()));
|
||||
all.addAll(this.other.stream().map(PlayableObjectRecord::getValue).sorted().collect(Collectors.toList()));
|
||||
return all;
|
||||
}
|
||||
|
||||
public int getPlayableImportantAmount() {
|
||||
// return only important abilities (e.g. show it as card icons)
|
||||
return this.other.size();
|
||||
}
|
||||
}
|
||||
|
||||
class PlayableObjectRecord implements Serializable, Copyable<PlayableObjectRecord> {
|
||||
|
||||
UUID id;
|
||||
String value;
|
||||
|
||||
public PlayableObjectRecord(UUID id, String value) {
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private PlayableObjectRecord(final PlayableObjectRecord record) {
|
||||
this.id = record.id;
|
||||
this.value = record.value;
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayableObjectRecord copy() {
|
||||
return new PlayableObjectRecord(this);
|
||||
}
|
||||
}
|
||||
69
Mage/src/main/java/mage/players/PlayableObjectsList.java
Normal file
69
Mage/src/main/java/mage/players/PlayableObjectsList.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package mage.players;
|
||||
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.util.Copyable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Contains stats with all playable cards for the player
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayableObjectsList implements Serializable, Copyable<PlayableObjectsList> {
|
||||
|
||||
Map<UUID, PlayableObjectStats> objects = new HashMap<>();
|
||||
|
||||
public PlayableObjectsList() {
|
||||
}
|
||||
|
||||
public PlayableObjectsList(Map<UUID, List<ActivatedAbility>> playableObjects) {
|
||||
load(playableObjects);
|
||||
}
|
||||
|
||||
public PlayableObjectsList(final PlayableObjectsList source) {
|
||||
source.objects.entrySet().forEach(entry -> {
|
||||
this.objects.put(entry.getKey(), entry.getValue().copy());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayableObjectsList copy() {
|
||||
return new PlayableObjectsList(this);
|
||||
}
|
||||
|
||||
public void load(Map<UUID, List<ActivatedAbility>> playableObjects) {
|
||||
objects.clear();
|
||||
playableObjects.forEach((objectId, list) -> {
|
||||
objects.put(objectId, new PlayableObjectStats(list));
|
||||
});
|
||||
}
|
||||
|
||||
public boolean containsObject(UUID objectId) {
|
||||
return objects.containsKey(objectId);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return objects.isEmpty();
|
||||
}
|
||||
|
||||
public PlayableObjectStats getStats(UUID objectId) {
|
||||
if (objects.containsKey(objectId)) {
|
||||
return objects.get(objectId).copy();
|
||||
} else {
|
||||
return new PlayableObjectStats();
|
||||
}
|
||||
}
|
||||
|
||||
public int getPlayableAmount(UUID objectId) {
|
||||
if (objects.containsKey(objectId)) {
|
||||
return objects.get(objectId).getPlayableAmount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,10 +82,10 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
void setLife(int life, Game game, Ability source);
|
||||
|
||||
/**
|
||||
* @param amount amount of life loss
|
||||
* @param amount amount of life loss
|
||||
* @param game
|
||||
* @param source can be null for default game events like mana burn
|
||||
* @param atCombat was the source combat damage
|
||||
* @param source can be null for default game events like mana burn
|
||||
* @param atCombat was the source combat damage
|
||||
* @param attackerId id of the attacker for combat events (can be null)
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -94,7 +94,6 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
int loseLife(int amount, Game game, Ability source, boolean atCombat);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param amount
|
||||
* @param game
|
||||
* @param source can be null for default game events life lifelink damage
|
||||
|
|
@ -338,7 +337,6 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
void reset();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param source can be null for game default shuffle (non effects, example: mulligans)
|
||||
* @param game
|
||||
*/
|
||||
|
|
@ -360,7 +358,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param num
|
||||
* @param source can be null for game default draws (non effects, example: start of the turn)
|
||||
* @param game
|
||||
* @param event original draw event in replacement code
|
||||
* @param event original draw event in replacement code
|
||||
* @return
|
||||
*/
|
||||
int drawCards(int num, Ability source, Game game, GameEvent event);
|
||||
|
|
@ -609,7 +607,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param game
|
||||
* @param source
|
||||
* @param xFromTheTop
|
||||
* @param withName - show card name in game logs for all players
|
||||
* @param withName - show card name in game logs for all players
|
||||
* @return
|
||||
*/
|
||||
boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop, boolean withName);
|
||||
|
|
@ -705,7 +703,7 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
List<Ability> getPlayableOptions(Ability ability, Game game);
|
||||
|
||||
Map<UUID, Integer> getPlayableObjects(Game game, Zone zone);
|
||||
PlayableObjectsList getPlayableObjects(Game game, Zone zone);
|
||||
|
||||
LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game);
|
||||
|
||||
|
|
@ -846,7 +844,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param withName
|
||||
* @return
|
||||
*/
|
||||
@Deprecated // if you want to use it in replaceEvent, then use ((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
@Deprecated
|
||||
// if you want to use it in replaceEvent, then use ((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, Ability source, Game game, Zone fromZone, boolean withName);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3710,32 +3710,45 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
* abilities
|
||||
*/
|
||||
@Override
|
||||
public Map<UUID, Integer> getPlayableObjects(Game game, Zone zone) {
|
||||
public PlayableObjectsList getPlayableObjects(Game game, Zone zone) {
|
||||
// collect abilities per object
|
||||
List<ActivatedAbility> playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards
|
||||
Map<UUID, Integer> playableObjects = new HashMap<>();
|
||||
for (Ability ability : playableAbilities) {
|
||||
Map<UUID, List<ActivatedAbility>> playableObjects = new HashMap<>();
|
||||
for (ActivatedAbility ability : playableAbilities) {
|
||||
if (ability.getSourceId() != null) {
|
||||
playableObjects.put(ability.getSourceId(), playableObjects.getOrDefault(ability.getSourceId(), 0) + 1);
|
||||
|
||||
// main card must be marked playable in GUI
|
||||
// normal card
|
||||
putToPlayableObjects(playableObjects, ability.getSourceId(), ability);
|
||||
|
||||
// main card - must be marked playable in GUI
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
if (card != null && card.getMainCard().getId() != card.getId()) {
|
||||
UUID mainCardId = card.getMainCard().getId();
|
||||
playableObjects.put(mainCardId, playableObjects.getOrDefault(mainCardId, 0) + 1);
|
||||
putToPlayableObjects(playableObjects, card.getMainCard().getId(), ability);
|
||||
}
|
||||
|
||||
// spell on stack can have activated abilities,
|
||||
// spell on stack - can have activated abilities,
|
||||
// so mark it as playable too (users must able to clicks on stack objects)
|
||||
// example: Lightning Storm
|
||||
Spell spell = game.getSpell(ability.getSourceId());
|
||||
if (spell != null) {
|
||||
playableObjects.put(spell.getId(), playableObjects.getOrDefault(spell.getId(), 0) + 1);
|
||||
putToPlayableObjects(playableObjects, spell.getId(), ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
return playableObjects;
|
||||
|
||||
// collect stats
|
||||
PlayableObjectsList playableObjectsList = new PlayableObjectsList(playableObjects);
|
||||
return playableObjectsList;
|
||||
}
|
||||
|
||||
private void putToPlayableObjects(Map<UUID, List<ActivatedAbility>> playableObjects, UUID objectId, ActivatedAbility ability) {
|
||||
if (!playableObjects.containsKey(objectId)) {
|
||||
playableObjects.put(objectId, new ArrayList<>());
|
||||
}
|
||||
playableObjects.get(objectId).add(ability);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Skip "silent" phase step when players are not allowed to cast anything.
|
||||
* E.g. players can't play or cast anything during declaring attackers.
|
||||
|
|
|
|||
31
Mage/src/main/java/mage/util/DebugUtil.java
Normal file
31
Mage/src/main/java/mage/util/DebugUtil.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package mage.util;
|
||||
|
||||
/**
|
||||
* Devs only: enable or disable debug features
|
||||
* <p>
|
||||
* WARNING, don't forget to disable it before release/commit
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class DebugUtil {
|
||||
|
||||
// cards basic (card panels)
|
||||
public static boolean GUI_CARD_DRAW_OUTER_BORDER = false;
|
||||
public static boolean GUI_CARD_DRAW_INNER_BORDER = false;
|
||||
public static boolean GUI_CARD_ICONS_DRAW_PANEL_BORDER = false;
|
||||
public static boolean GUI_CARD_ICONS_DRAW_ICON_BORDER = false;
|
||||
public static boolean GUI_CARD_DRAW_MOUSE_CONTAINS_BOUNDS = false;
|
||||
|
||||
// cards renders (inner card panel)
|
||||
public static boolean GUI_RENDER_IMAGE_DRAW_IMAGE_BORDER = false;
|
||||
public static boolean GUI_RENDER_CENTERED_TEXT_DRAW_DEBUG_LINES = false;
|
||||
|
||||
// deck editor
|
||||
public static boolean GUI_DECK_EDITOR_DRAW_DRAGGING_PANE_BORDER = false;
|
||||
public static boolean GUI_DECK_EDITOR_DRAW_COUNT_LABEL_BORDER = false;
|
||||
|
||||
// game
|
||||
public static boolean GUI_GAME_DRAW_BATTLEFIELD_BORDER = false;
|
||||
public static boolean GUI_GAME_DRAW_HAND_AND_STACK_BORDER = false;
|
||||
public static boolean GUI_GAME_DIALOGS_DRAW_CARDS_AREA_BORDER = false;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package mage.util;
|
|||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.ManaSymbol;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
|
|
@ -21,10 +22,16 @@ import mage.players.Player;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author noxx
|
||||
* @author noxx, JayDi85
|
||||
*/
|
||||
public final class ManaUtil {
|
||||
|
||||
private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*";
|
||||
private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*";
|
||||
private static final String regexRed = ".*\\x7b.{0,2}R.{0,2}\\x7d.*";
|
||||
private static final String regexGreen = ".*\\x7b.{0,2}G.{0,2}\\x7d.*";
|
||||
private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*";
|
||||
|
||||
private ManaUtil() {
|
||||
}
|
||||
|
||||
|
|
@ -504,6 +511,121 @@ public final class ManaUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find full card's color identity (from mana cost and rules)
|
||||
*
|
||||
* @param cardColor color indicator
|
||||
* @param cardManaSymbols mana cost
|
||||
* @param cardRules rules list
|
||||
* @param secondSideCard second side of double faces card
|
||||
* @return
|
||||
*/
|
||||
public static FilterMana getColorIdentity(ObjectColor cardColor, List<String> cardManaSymbols, List<String> cardRules, Card secondSideCard) {
|
||||
// 20210121
|
||||
// 903.4
|
||||
// The Commander variant uses color identity to determine what cards can be in a deck with a certain
|
||||
// commander. The color identity of a card is the color or colors of any mana symbols in that card’s mana
|
||||
// cost or rules text, plus any colors defined by its characteristic-defining abilities (see rule 604.3)
|
||||
// or color indicator (see rule 204).
|
||||
// 903.4d
|
||||
// The back face of a double-faced card (see rule 711) is included when determining a card’s color identity.
|
||||
// This is an exception to rule 711.4a.
|
||||
FilterMana res = new FilterMana();
|
||||
|
||||
// from object (color indicator)
|
||||
ObjectColor color = (cardColor != null ? new ObjectColor(cardColor) : new ObjectColor());
|
||||
|
||||
// from mana
|
||||
res.setWhite(color.isWhite() || containsManaSymbol(cardManaSymbols, "W"));
|
||||
res.setBlue(color.isBlue() || containsManaSymbol(cardManaSymbols, "U"));
|
||||
res.setBlack(color.isBlack() || containsManaSymbol(cardManaSymbols, "B"));
|
||||
res.setRed(color.isRed() || containsManaSymbol(cardManaSymbols, "R"));
|
||||
res.setGreen(color.isGreen() || containsManaSymbol(cardManaSymbols, "G"));
|
||||
|
||||
// from rules
|
||||
for (String rule : cardRules) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!res.isWhite() && (rule.matches(regexWhite))) {
|
||||
res.setWhite(true);
|
||||
}
|
||||
if (!res.isBlue() && (rule.matches(regexBlue))) {
|
||||
res.setBlue(true);
|
||||
}
|
||||
if (!res.isBlack() && rule.matches(regexBlack)) {
|
||||
res.setBlack(true);
|
||||
}
|
||||
if (!res.isRed() && (rule.matches(regexRed))) {
|
||||
res.setRed(true);
|
||||
}
|
||||
if (!res.isGreen() && (rule.matches(regexGreen))) {
|
||||
res.setGreen(true);
|
||||
}
|
||||
}
|
||||
|
||||
// SECOND SIDE CARD
|
||||
if (secondSideCard != null) {
|
||||
// from object (color indicator)
|
||||
ObjectColor secondColor = secondSideCard.getColor(null);
|
||||
res.setBlack(res.isBlack() || secondColor.isBlack());
|
||||
res.setGreen(res.isGreen() || secondColor.isGreen());
|
||||
res.setRed(res.isRed() || secondColor.isRed());
|
||||
res.setBlue(res.isBlue() || secondColor.isBlue());
|
||||
res.setWhite(res.isWhite() || secondColor.isWhite());
|
||||
|
||||
// from mana
|
||||
List<String> secondManaSymbols = secondSideCard.getManaCost().getSymbols();
|
||||
res.setWhite(res.isWhite() || containsManaSymbol(secondManaSymbols, "W"));
|
||||
res.setBlue(res.isBlue() || containsManaSymbol(secondManaSymbols, "U"));
|
||||
res.setBlack(res.isBlack() || containsManaSymbol(secondManaSymbols, "B"));
|
||||
res.setRed(res.isRed() || containsManaSymbol(secondManaSymbols, "R"));
|
||||
res.setGreen(res.isGreen() || containsManaSymbol(secondManaSymbols, "G"));
|
||||
|
||||
// from rules
|
||||
for (String rule : secondSideCard.getRules()) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!res.isWhite() && rule.matches(regexWhite)) {
|
||||
res.setWhite(true);
|
||||
}
|
||||
if (!res.isBlue() && rule.matches(regexBlue)) {
|
||||
res.setBlue(true);
|
||||
}
|
||||
if (!res.isBlack() && rule.matches(regexBlack)) {
|
||||
res.setBlack(true);
|
||||
}
|
||||
if (!res.isRed() && rule.matches(regexRed)) {
|
||||
res.setRed(true);
|
||||
}
|
||||
if (!res.isGreen() && rule.matches(regexGreen)) {
|
||||
res.setGreen(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static boolean containsManaSymbol(List<String> cardManaSymbols, String needSymbol) {
|
||||
// search R in {R/B}
|
||||
return cardManaSymbols.stream().anyMatch(s -> s.contains(needSymbol));
|
||||
}
|
||||
|
||||
public static FilterMana getColorIdentity(Card card) {
|
||||
// TODO: is it support mdf cards?
|
||||
// TODO: is it support adventure cards?
|
||||
Card secondSide = card.getSecondCardFace();
|
||||
return getColorIdentity(card.getColor(null), card.getManaCost().getSymbols(), card.getRules(), secondSide);
|
||||
}
|
||||
|
||||
public static int getColorIdentityHash(FilterMana colorIdentity) {
|
||||
int hash = 3;
|
||||
hash = 23 * hash + (colorIdentity.isWhite() ? 1 : 0);
|
||||
hash = 23 * hash + (colorIdentity.isBlue() ? 1 : 0);
|
||||
hash = 23 * hash + (colorIdentity.isBlack() ? 1 : 0);
|
||||
hash = 23 * hash + (colorIdentity.isRed() ? 1 : 0);
|
||||
hash = 23 * hash + (colorIdentity.isGreen() ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* all ability/effect code with "= new GenericManaCost" must be replaced by
|
||||
* createManaCost call
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue