Merge branch 'master' into feature/momirFFA

This commit is contained in:
Faxn 2017-10-19 13:08:13 -04:00
commit 51ea9972e0
10500 changed files with 119688 additions and 52835 deletions

1
.gitignore vendored
View file

@ -132,4 +132,3 @@ Mage.Client/serverlist.txt
client_secrets.json client_secrets.json
dependency-reduced-pom.xml dependency-reduced-pom.xml
/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/target/

View file

@ -0,0 +1,89 @@
1 [C17:106] Corpse Augur
1 [C17:10] Galecaster Colossus
1 [C17:111] Decree of Pain
1 [C17:114] Go for the Throat
1 [C17:115] Magus of the Abyss
1 [C17:117] Necromantic Selection
1 [C17:121] Puppeteer Clique
1 [C17:12] Magus of the Mind
1 [C17:131] Chaos Warp
1 [C17:132] Comet Storm
1 [C17:13] Portal Mage
1 [C17:14] Bloodline Necromancer
1 [C17:166] Cauldron Dance
1 [C17:169] Crosis's Charm
1 [C17:16] Curse of Disturbance
1 [C17:171] Etherium-Horn Sorcerer
1 [C17:173] Havengul Lich
1 [C17:175] Izzet Chronarch
1 [C17:177] Marchesa, the Black Rose
1 [C17:178] Memory Plunder
1 [C17:180] Mercurial Chemister
1 [C17:183] Nin, the Pain Artist
1 [C17:185] Niv-Mizzet, the Firemind
1 [C17:186] Nivix Guildmage
1 [C17:18] Kindred Dominance
1 [C17:190] Rakdos Charm
1 [C17:193] Shadowmage Infiltrator
1 [C17:195] Silumgar's Command
1 [C17:199] Terminate
1 [C17:201] Vela the Night-Clad
1 [C17:206] Commander's Sphere
1 [C17:207] Darksteel Ingot
1 [C17:210] Fellwar Stone
1 [C17:217] Nevinyrral's Disk
1 [C17:21] Vindictive Lich
1 [C17:223] Sol Ring
1 [C17:229] Unstable Obelisk
1 [C17:232] Worn Powerstone
1 [C17:242] Command Tower
1 [C17:244] Crumbling Necropolis
1 [C17:245] Dimir Aqueduct
1 [C17:246] Dismal Backwater
1 [C17:248] Evolving Wilds
1 [C17:249] Exotic Orchard
1 [C17:24] Curse of Opulence
1 [C17:254] Grixis Panorama
1 [C17:256] Izzet Boilerworks
1 [C17:258] Jwar Isle Refuge
1 [C17:264] Mystifying Maze
1 [C17:26] Izzet Chemister
1 [C17:270] Rakdos Carnarium
1 [C17:283] Swiftwater Cliffs
1 [C17:284] Temple of the False God
1 [C17:285] Terramorphic Expanse
1 [C17:289] Vivid Crag
1 [C17:28] Shifting Shadow
1 [C17:290] Vivid Creek
1 [C17:292] Vivid Marsh
1 [C17:305] Mountain
1 [C17:306] Mountain
1 [C17:39] Kess, Dissident Mage
1 [C17:41] Mairsil, the Pretender
1 [C17:47] Taigam, Sidisi's Hand
1 [C17:54] Mirror of the Forebears
1 [C17:56] Path of Ancestry
1 [C17:80] Arcanis the Omnipotent
1 [C17:81] Archaeomancer
1 [C17:82] Azami, Lady of Scrolls
1 [C17:83] Body Double
1 [C17:84] Clone Legion
1 [C17:85] Harbinger of the Tides
1 [C17:86] Into the Roil
1 [C17:87] Merchant of Secrets
1 [C17:89] Opportunity
1 [C17:90] Polymorphist's Jest
1 [C17:91] Reality Shift
1 [C17:92] Sea Gate Oracle
1 [C17:93] Serendib Sorcerer
1 [C17:94] Spelltwine
1 [C17:97] Apprentice Necromancer
1 [C17:9] Curse of Verbosity
2 [C17:301] Swamp
2 [C17:302] Swamp
2 [C17:303] Swamp
2 [C17:304] Mountain
3 [C17:298] Island
3 [C17:300] Island
4 [C17:299] Island
SB: 1 [C17:38] Inalla, Archmage Ritualist

View file

@ -0,0 +1,97 @@
1 [C17:234] Arcane Sanctum
1 [C17:203] Armillary Sphere
1 [C17:161] Atarka, World Render
1 [C17:163] Bladewing the Risen
1 [C17:15] Boneyard Scourge
1 [C17:165] Broodmate Dragon
1 [C17:242] Command Tower
1 [C17:206] Commander's Sphere
1 [C17:168] Crosis, the Purger
1 [C17:133] Crucible of Fire
1 [C17:243] Crucible of the Spirit Dragon
1 [C17:244] Crumbling Necropolis
1 [C17:107] Crux of Fate
1 [C17:147] Cultivate
1 [C17:30] Curse of Bounty
1 [C17:24] Curse of Opulence
1 [C17:9] Curse of Verbosity
1 [C17:207] Darksteel Ingot
1 [C17:110] Deathbringer Regent
1 [C17:134] Dragon Tempest
1 [C17:135] Dragonlord's Servant
1 [C17:136] Dragonspeaker Shaman
1 [C17:209] Dreamstone Hedron
1 [C17:170] Dromoka, the Eternal
1 [C17:137] Earthquake
1 [C17:148] Elemental Bond
1 [C17:149] Farseek
1 [C17:211] Fist of Suns
1 [C17:307] Forest
1 [C17:308] Forest
1 [C17:309] Forest
1 [C17:4] Fortunate Few
1 [C17:37] Fractured Identity
1 [C17:251] Frontier Bivouac
1 [C17:150] Frontier Siege
1 [C17:255] Haven of the Spirit Dragon
1 [C17:138] Hellkite Charger
1 [C17:53] Herald's Horn
1 [C17:174] Intet, the Dreamer
1 [C17:298] Island
1 [C17:299] Island
1 [C17:300] Island
1 [C17:257] Jungle Shrine
1 [C17:11] Kindred Discovery
1 [C17:154] Kodama's Reach
1 [C17:176] Kolaghan, the Storm's Fury
1 [C17:215] Lightning Greaves
1 [C17:54] Mirror of the Forebears
1 [C17:88] Monastery Siege
1 [C17:263] Mystic Monastery
1 [C17:218] Nihil Spellbomb
1 [C17:184] Niv-Mizzet, Dracogenius
1 [C17:265] Nomad Outpost
1 [C17:45] O-Kagachi, Vengeful Kami
1 [C17:187] Ojutai, Soul of Winter
1 [C17:267] Opulent Palace
1 [C17:67] Orator of Ojutai
1 [C17:118] Painful Truths
1 [C17:119] Palace Siege
1 [C17:56] Path of Ancestry
1 [C17:295] Plains
1 [C17:296] Plains
1 [C17:297] Plains
1 [C17:156] Rain of Thorns
1 [C17:55] Ramos, Dragon Engine
1 [C17:141] Ryusei, the Falling Star
1 [C17:274] Sandsteppe Citadel
1 [C17:275] Savage Lands
1 [C17:191] Savage Ventmaw
1 [C17:6] Scalelord Reckoner
1 [C17:192] Scion of the Ur-Dragon
1 [C17:142] Scourge of Valkas
1 [C17:277] Seaside Citadel
1 [C17:194] Silumgar, the Drifting Death
1 [C17:223] Sol Ring
1 [C17:196] Spellbound Dragon
1 [C17:225] Steel Hellkite
1 [C17:74] Sunscorch Regent
1 [C17:301] Swamp
1 [C17:302] Swamp
1 [C17:303] Swamp
1 [C17:46] Taigam, Ojutai Master
1 [C17:198] Teneb, the Harvester
1 [C17:29] Territorial Hellkite
1 [C17:143] Tyrant's Familiar
1 [C17:144] Utvara Hellkite
1 [C17:289] Vivid Crag
1 [C17:290] Vivid Creek
1 [C17:291] Vivid Grove
1 [C17:292] Vivid Marsh
1 [C17:293] Vivid Meadow
1 [C17:49] Wasitora, Nekoru Queen
1 [C17:230] Wayfarer's Bauble
2 [C17:304] Mountain
2 [C17:305] Mountain
2 [C17:306] Mountain
SB: 1 [C17:48] The Ur-Dragon

View file

@ -0,0 +1,92 @@
1 [C17:145] Abundance
1 [C17:1] Alms Collector
1 [C17:202] Argentum Armor
1 [C17:2] Balan, Wandering Knight
1 [C17:162] Behemoth Sledge
1 [C17:235] Blighted Woodland
1 [C17:50] Bloodforged Battle-Axe
1 [C17:237] Blossoming Sands
1 [C17:242] Command Tower
1 [C17:58] Condemn
1 [C17:146] Crushing Vines
1 [C17:147] Cultivate
1 [C17:30] Curse of Bounty
1 [C17:3] Curse of Vitality
1 [C17:59] Divine Reckoning
1 [C17:209] Dreamstone Hedron
1 [C17:247] Elfhame Palace
1 [C17:248] Evolving Wilds
1 [C17:172] Fleecemane Lion
1 [C17:212] Grappling Hook
1 [C17:252] Grasslands
1 [C17:253] Graypelt Refuge
1 [C17:51] Hammer of Nazahn
1 [C17:151] Harmonize
1 [C17:213] Hedron Archive
1 [C17:52] Heirloom Blade
1 [C17:53] Herald's Horn
1 [C17:214] Hero's Blade
1 [C17:31] Hungry Lynx
1 [C17:152] Hunter's Prowess
1 [C17:61] Jareth, Leonine Titan
1 [C17:62] Jazal Goldmane
1 [C17:153] Jedit Ojanen of Efrava
1 [C17:63] Kemba, Kha Regent
1 [C17:32] Kindred Summons
1 [C17:260] Krosan Verge
1 [C17:64] Leonin Arbiter
1 [C17:65] Leonin Relic-Warder
1 [C17:66] Leonin Shikari
1 [C17:215] Lightning Greaves
1 [C17:216] Loxodon Warhammer
1 [C17:181] Mirari's Wake
1 [C17:43] Mirri, Weatherlight Duelist
1 [C17:261] Mosswort Bridge
1 [C17:262] Myriad Landscape
1 [C17:44] Nazahn, Revered Bladesmith
1 [C17:155] Nissa's Pilgrimage
1 [C17:266] Opal Palace
1 [C17:68] Oreskos Explorer
1 [C17:56] Path of Ancestry
1 [C17:188] Phantom Nishoba
1 [C17:189] Qasali Pridemage
1 [C17:33] Qasali Slingers
1 [C17:220] Quietus Spike
1 [C17:69] Raksha Golden Cub
1 [C17:157] Relic Crush
1 [C17:272] Rogue's Passage
1 [C17:71] Rout
1 [C17:273] Saltcrusted Steppe
1 [C17:278] Secluded Steppe
1 [C17:72] Seht's Tiger
1 [C17:279] Selesnya Guildgate
1 [C17:280] Selesnya Sanctuary
1 [C17:222] Skullclamp
1 [C17:223] Sol Ring
1 [C17:158] Soul's Majesty
1 [C17:73] Spirit of the Hearth
1 [C17:224] Staff of Nin
1 [C17:281] Stirring Wildwood
1 [C17:75] Sunspear Shikari
1 [C17:226] Swiftfoot Boots
1 [C17:228] Sword of Vengeance
1 [C17:227] Sword of the Animist
1 [C17:77] Taj-Nar Swordsmith
1 [C17:284] Temple of the False God
1 [C17:159] Temur Sabertooth
1 [C17:285] Terramorphic Expanse
1 [C17:286] Tranquil Expanse
1 [C17:287] Tranquil Thicket
1 [C17:34] Traverse the Outlands
1 [C17:291] Vivid Grove
1 [C17:293] Vivid Meadow
1 [C17:78] White Sun's Zenith
1 [C17:79] Wing Shards
1 [C17:160] Zendikar Resurgent
2 [C17:307] Forest
2 [C17:308] Forest
2 [C17:309] Forest
2 [C17:295] Plains
2 [C17:296] Plains
3 [C17:297] Plains
SB: 1 [C17:35] Arahbo, Roar of the World

View file

@ -0,0 +1,94 @@
1 [C17:100] Blood Tribute
1 [C17:101] Bloodhusk Ritualist
1 [C17:102] Bloodlord of Vaasgoth
1 [C17:103] Butcher of Malakir
1 [C17:104] Captivating Vampire
1 [C17:105] Consuming Vapors
1 [C17:108] Damnable Pact
1 [C17:112] Drana, Kalastria Bloodchief
1 [C17:113] Falkenrath Noble
1 [C17:114] Go for the Throat
1 [C17:116] Malakir Bloodwitch
1 [C17:120] Pawn of Ulamog
1 [C17:122] Read the Bones
1 [C17:123] Sangromancer
1 [C17:124] Sanguine Bond
1 [C17:125] Skeletal Scrying
1 [C17:126] Skeletal Vampire
1 [C17:127] Syphon Mind
1 [C17:128] Underworld Connections
1 [C17:129] Vampire Nighthawk
1 [C17:130] Vein Drinker
1 [C17:139] Outpost Siege
1 [C17:140] Rakish Heir
1 [C17:14] Bloodline Necromancer
1 [C17:164] Blood Baron of Vizkopa
1 [C17:167] Crackling Doom
1 [C17:16] Curse of Disturbance
1 [C17:179] Merciless Eviction
1 [C17:17] Kheru Mind-Eater
1 [C17:182] Mortify
1 [C17:197] Stromkirk Captain
1 [C17:200] Tithe Drinker
1 [C17:204] Blade of the Bloodchief
1 [C17:205] Boros Signet
1 [C17:207] Darksteel Ingot
1 [C17:208] Door of Destinies
1 [C17:20] Patron of the Vein
1 [C17:218] Nihil Spellbomb
1 [C17:219] Orzhov Signet
1 [C17:221] Rakdos Signet
1 [C17:222] Skullclamp
1 [C17:223] Sol Ring
1 [C17:22] Bloodsworn Steward
1 [C17:231] Well of Lost Dreams
1 [C17:232] Worn Powerstone
1 [C17:233] Akoum Refuge
1 [C17:236] Bloodfell Caves
1 [C17:238] Bojuka Bog
1 [C17:239] Boros Garrison
1 [C17:23] Crimson Honor Guard
1 [C17:240] Boros Guildgate
1 [C17:241] Cinder Barrens
1 [C17:242] Command Tower
1 [C17:248] Evolving Wilds
1 [C17:250] Forsaken Sanctuary
1 [C17:259] Kabira Crossroads
1 [C17:25] Disrupt Decorum
1 [C17:265] Nomad Outpost
1 [C17:266] Opal Palace
1 [C17:268] Orzhov Basilica
1 [C17:269] Orzhov Guildgate
1 [C17:270] Rakdos Carnarium
1 [C17:271] Rakdos Guildgate
1 [C17:276] Scoured Barrens
1 [C17:27] Kindred Charge
1 [C17:282] Stone Quarry
1 [C17:285] Terramorphic Expanse
1 [C17:288] Urborg Volcano
1 [C17:294] Wind-Scarred Crag
1 [C17:295] Plains
1 [C17:296] Plains
1 [C17:297] Plains
1 [C17:305] Mountain
1 [C17:306] Mountain
1 [C17:3] Curse of Vitality
1 [C17:40] Licia, Sanguine Tribune
1 [C17:42] Mathas, Fiend Seeker
1 [C17:52] Heirloom Blade
1 [C17:56] Path of Ancestry
1 [C17:57] Blind Obedience
1 [C17:5] Kindred Boon
1 [C17:60] Fell the Mighty
1 [C17:70] Return to Dust
1 [C17:76] Swords to Plowshares
1 [C17:8] Teferi's Protection
1 [C17:95] Ambition's Cost
1 [C17:96] Anowon, the Ruin Sage
1 [C17:98] Black Market
1 [C17:99] Blood Artist
2 [C17:304] Mountain
3 [C17:301] Swamp
2 [C17:303] Swamp
3 [C17:302] Swamp
SB: 1 [C17:36] Edgar Markov

Binary file not shown.

View file

@ -44,8 +44,6 @@ import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener; import javax.swing.event.PopupMenuListener;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository; import mage.cards.repository.CardRepository;
import mage.client.cards.BigCard; import mage.client.cards.BigCard;
import mage.client.chat.ChatPanelBasic; import mage.client.chat.ChatPanelBasic;
@ -134,8 +132,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
private final BalloonTip balloonTip; private final BalloonTip balloonTip;
private java.util.List<CardInfo> missingCards;
/** /**
* @return the session * @return the session
*/ */
@ -513,18 +509,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
} }
private void checkForNewImages() { private void checkForNewImages() {
long beforeCall = System.currentTimeMillis(); // Removed TODO: Remove related pref code
missingCards = CardRepository.instance.findCards(new CardCriteria());
LOGGER.info("Card pool load time: " + ((System.currentTimeMillis() - beforeCall) / 1000 + " seconds"));
beforeCall = System.currentTimeMillis();
if (DownloadPictures.checkForMissingCardImages(missingCards)) {
LOGGER.info("Card images checking time: " + ((System.currentTimeMillis() - beforeCall) / 1000 + " seconds"));
UserRequestMessage message = new UserRequestMessage("New images available", "Card images are missing (" + missingCards.size() + "). Do you want to download the images?"
+ "<br><br><i>You can deactivate the image download check on application start in the preferences.</i>");
message.setButton1("No", null);
message.setButton2("Yes", PlayerAction.CLIENT_DOWNLOAD_CARD_IMAGES);
showUserRequestDialog(message);
}
} }
public static void setActive(MagePane frame) { public static void setActive(MagePane frame) {
@ -978,8 +963,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
}//GEN-LAST:event_btnImagesActionPerformed }//GEN-LAST:event_btnImagesActionPerformed
public void downloadImages() { public void downloadImages() {
java.util.List<CardInfo> cards = CardRepository.instance.findCards(new CardCriteria()); DownloadPictures.startDownload();
DownloadPictures.startDownload(null, cards);
} }
public void exitApp() { public void exitApp() {
@ -1317,7 +1301,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
Plugins.instance.downloadSymbols(); Plugins.instance.downloadSymbols();
break; break;
case CLIENT_DOWNLOAD_CARD_IMAGES: case CLIENT_DOWNLOAD_CARD_IMAGES:
DownloadPictures.startDownload(null, missingCards); DownloadPictures.startDownload();
break; break;
case CLIENT_DISCONNECT: case CLIENT_DISCONNECT:
if (SessionHandler.isConnected()) { if (SessionHandler.isConnected()) {

View file

@ -27,20 +27,9 @@
*/ */
package mage.client.cards; package mage.client.cards;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.List;
import java.util.UUID;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import mage.cards.MageCard; import mage.cards.MageCard;
import mage.client.plugins.impl.Plugins; import mage.client.plugins.impl.Plugins;
import mage.client.util.ClientEventType;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.GUISizeHelper; import mage.client.util.GUISizeHelper;
import mage.client.util.Listener; import mage.client.util.Listener;
@ -50,6 +39,13 @@ import mage.view.CardsView;
import mage.view.SimpleCardView; import mage.view.SimpleCardView;
import org.mage.card.arcane.CardPanel; import org.mage.card.arcane.CardPanel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.List;
import java.util.UUID;
public class CardArea extends JPanel implements MouseListener { public class CardArea extends JPanel implements MouseListener {
protected final CardEventSource cardEventSource = new CardEventSource(); protected final CardEventSource cardEventSource = new CardEventSource();
@ -240,15 +236,15 @@ public class CardArea extends JPanel implements MouseListener {
e.consume(); e.consume();
if (obj instanceof Card) { if (obj instanceof Card) {
if (e.isAltDown()) { if (e.isAltDown()) {
cardEventSource.altDoubleClick(((Card) obj).getOriginal(), "alt-double-click"); cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
cardEventSource.doubleClick(((Card) obj).getOriginal(), "double-click"); cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK);
} }
} else if (obj instanceof MageCard) { } else if (obj instanceof MageCard) {
if (e.isAltDown()) { if (e.isAltDown()) {
cardEventSource.altDoubleClick(((MageCard) obj).getOriginal(), "alt-double-click"); cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
cardEventSource.doubleClick(((MageCard) obj).getOriginal(), "double-click"); cardEventSource.fireEvent(((MageCard) obj).getOriginal(),ClientEventType.DOUBLE_CLICK);
} }
} }
} }
@ -270,14 +266,14 @@ public class CardArea extends JPanel implements MouseListener {
checkMenu(e, null); checkMenu(e, null);
} }
} else { } else {
cardEventSource.actionConsumedEvent("action-consumed"); cardEventSource.fireEvent(ClientEventType.ACTION_CONSUMED);
} }
} }
private void checkMenu(MouseEvent Me, SimpleCardView card) { private void checkMenu(MouseEvent Me, SimpleCardView card) {
if (Me.isPopupTrigger()) { if (Me.isPopupTrigger()) {
Me.consume(); Me.consume();
cardEventSource.showPopupMenuEvent(card, Me.getComponent(), Me.getX(), Me.getY(), "show-popup-menu"); cardEventSource.fireEvent(card, Me.getComponent(), Me.getX(), Me.getY(), ClientEventType.SHOW_POP_UP_MENU);
} }
} }

View file

@ -27,14 +27,13 @@
*/ */
package mage.client.cards; package mage.client.cards;
import java.awt.Component; import mage.client.util.*;
import java.io.Serializable;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.EventDispatcher;
import mage.client.util.EventSource;
import mage.client.util.Listener;
import mage.view.SimpleCardView; import mage.view.SimpleCardView;
import java.awt.*;
import java.io.Serializable;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -49,42 +48,22 @@ public class CardEventSource implements EventSource<Event>, Serializable {
dispatcher.addListener(listener); dispatcher.addListener(listener);
} }
public void setNumber(SimpleCardView card, String message, int number) { public void fireEvent(SimpleCardView card, ClientEventType eventType, int number){
dispatcher.fireEvent(new Event(card, message, number)); dispatcher.fireEvent(new Event(card, eventType, number));
} }
public void removeSpecificCard(SimpleCardView card, String message) { public void fireEvent(ClientEventType eventType){
dispatcher.fireEvent(new Event(card, message)); dispatcher.fireEvent(new Event(null, eventType));
} }
public void addSpecificCard(SimpleCardView card, String message) { public void fireEvent(SimpleCardView card, ClientEventType eventType){
dispatcher.fireEvent(new Event(card, message)); dispatcher.fireEvent(new Event(card, eventType));
} }
public void doubleClick(SimpleCardView card, String message) { public void fireEvent(SimpleCardView card, Component component, int x, int y, ClientEventType message) {
dispatcher.fireEvent(new Event(card, message));
}
public void altDoubleClick(SimpleCardView card, String message) {
dispatcher.fireEvent(new Event(card, message));
}
public void removeFromMainEvent(String message) {
dispatcher.fireEvent(new Event(null, message));
}
public void removeFromSideboardEvent(String message) {
dispatcher.fireEvent(new Event(null, message));
}
public void showPopupMenuEvent(SimpleCardView card, Component component, int x, int y, String message) {
dispatcher.fireEvent(new Event(card, message, x, y, component)); dispatcher.fireEvent(new Event(card, message, x, y, component));
} }
public void actionConsumedEvent(String message) {
dispatcher.fireEvent(new Event(null, message));
}
@Override @Override
public void clearListeners() { public void clearListeners() {
dispatcher.clearListeners(); dispatcher.clearListeners();

View file

@ -33,22 +33,10 @@
*/ */
package mage.client.cards; package mage.client.cards;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import mage.cards.MageCard; import mage.cards.MageCard;
import mage.client.deckeditor.SortSetting; import mage.client.deckeditor.SortSetting;
import mage.client.plugins.impl.Plugins; import mage.client.plugins.impl.Plugins;
import mage.client.util.ClientEventType;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.GUISizeHelper; import mage.client.util.GUISizeHelper;
import mage.client.util.Listener; import mage.client.util.Listener;
@ -57,6 +45,13 @@ import mage.view.CardView;
import mage.view.CardsView; import mage.view.CardsView;
import org.mage.card.arcane.CardPanel; import org.mage.card.arcane.CardPanel;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -321,15 +316,15 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
Object obj = e.getSource(); Object obj = e.getSource();
if (obj instanceof Card) { if (obj instanceof Card) {
if (e.isAltDown()) { if (e.isAltDown()) {
cardEventSource.altDoubleClick(((Card) obj).getOriginal(), "alt-double-click"); cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
cardEventSource.doubleClick(((Card) obj).getOriginal(), "double-click"); cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK);
} }
} else if (obj instanceof MageCard) { } else if (obj instanceof MageCard) {
if (e.isAltDown()) { if (e.isAltDown()) {
cardEventSource.altDoubleClick(((MageCard) obj).getOriginal(), "alt-double-click"); cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
cardEventSource.doubleClick(((MageCard) obj).getOriginal(), "double-click"); cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK);
} }
} }
} }

View file

@ -62,7 +62,6 @@ import java.util.*;
import java.util.List; import java.util.List;
/** /**
*
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public class CardsList extends javax.swing.JPanel implements MouseListener, ICardGrid { public class CardsList extends javax.swing.JPanel implements MouseListener, ICardGrid {
@ -475,9 +474,9 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
setMinimumSize(new java.awt.Dimension(30, 30)); setMinimumSize(new java.awt.Dimension(30, 30));
setPreferredSize((!Beans.isDesignTime())? setPreferredSize((!Beans.isDesignTime()) ?
(GUISizeHelper.editorCardDimension) (GUISizeHelper.editorCardDimension)
:(new Dimension(600, 600))); : (new Dimension(600, 600)));
setRequestFocusEnabled(false); setRequestFocusEnabled(false);
panelControl.setMaximumSize(new java.awt.Dimension(32767, 23)); panelControl.setMaximumSize(new java.awt.Dimension(32767, 23));
@ -522,7 +521,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
chkPiles.setMargin(new java.awt.Insets(3, 2, 2, 2)); chkPiles.setMargin(new java.awt.Insets(3, 2, 2, 2));
chkPiles.addActionListener(evt -> chkPilesActionPerformed(evt)); chkPiles.addActionListener(evt -> chkPilesActionPerformed(evt));
cbSortBy.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "SortBy" })); cbSortBy.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"SortBy"}));
cbSortBy.setToolTipText("Sort the cards if card view is active."); cbSortBy.setToolTipText("Sort the cards if card view is active.");
cbSortBy.setMaximumSize(new java.awt.Dimension(120, 20)); cbSortBy.setMaximumSize(new java.awt.Dimension(120, 20));
cbSortBy.setMinimumSize(new java.awt.Dimension(120, 20)); cbSortBy.setMinimumSize(new java.awt.Dimension(120, 20));
@ -553,36 +552,36 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
javax.swing.GroupLayout panelControlLayout = new javax.swing.GroupLayout(panelControl); javax.swing.GroupLayout panelControlLayout = new javax.swing.GroupLayout(panelControl);
panelControl.setLayout(panelControlLayout); panelControl.setLayout(panelControlLayout);
panelControlLayout.setHorizontalGroup( panelControlLayout.setHorizontalGroup(
panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelControlLayout.createSequentialGroup() .addGroup(panelControlLayout.createSequentialGroup()
.addComponent(lblCount) .addComponent(lblCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblLandCount) .addComponent(lblLandCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblCreatureCount) .addComponent(lblCreatureCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkPiles) .addComponent(chkPiles)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
panelControlLayout.setVerticalGroup( panelControlLayout.setVerticalGroup(
panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelControlLayout.createSequentialGroup() .addGroup(panelControlLayout.createSequentialGroup()
.addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblCount) .addComponent(lblCount)
.addComponent(lblLandCount) .addComponent(lblLandCount)
.addComponent(lblCreatureCount) .addComponent(lblCreatureCount)
.addComponent(chkPiles)) .addComponent(chkPiles))
.addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, 0)) .addGap(0, 0, 0))
); );
jToggleListView.getAccessibleContext().setAccessibleDescription("Switch between image and table view."); jToggleListView.getAccessibleContext().setAccessibleDescription("Switch between image and table view.");
@ -593,16 +592,16 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, 467, Short.MAX_VALUE) .addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, 467, Short.MAX_VALUE)
.addComponent(panelCardArea) .addComponent(panelCardArea)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(2, 2, 2) .addGap(2, 2, 2)
.addComponent(panelCardArea, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE)) .addComponent(panelCardArea, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -660,15 +659,15 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
e.consume(); e.consume();
if (obj instanceof Card) { if (obj instanceof Card) {
if (e.isAltDown()) { if (e.isAltDown()) {
cardEventSource.altDoubleClick(((Card) obj).getOriginal(), "alt-double-click"); cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
cardEventSource.doubleClick(((Card) obj).getOriginal(), "double-click"); cardEventSource.fireEvent(((Card) obj).getOriginal(), ClientEventType.DOUBLE_CLICK);
} }
} else if (obj instanceof MageCard) { } else if (obj instanceof MageCard) {
if (e.isAltDown()) { if (e.isAltDown()) {
cardEventSource.altDoubleClick(((MageCard) obj).getOriginal(), "alt-double-click"); cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
cardEventSource.doubleClick(((MageCard) obj).getOriginal(), "double-click"); cardEventSource.fireEvent(((MageCard) obj).getOriginal(), ClientEventType.DOUBLE_CLICK);
} }
} }
} }
@ -695,7 +694,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
private void checkMenu(MouseEvent Me, SimpleCardView card) { private void checkMenu(MouseEvent Me, SimpleCardView card) {
if (Me.isPopupTrigger()) { if (Me.isPopupTrigger()) {
Me.consume(); Me.consume();
cardEventSource.showPopupMenuEvent(card, Me.getComponent(), Me.getX(), Me.getY(), "show-popup-menu"); cardEventSource.fireEvent(card, Me.getComponent(), Me.getX(), Me.getY(), ClientEventType.SHOW_POP_UP_MENU);
} }
} }

View file

@ -34,17 +34,11 @@
package mage.client.cards; package mage.client.cards;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import mage.cards.CardDimensions; import mage.cards.CardDimensions;
import mage.cards.MageCard; import mage.cards.MageCard;
import mage.client.plugins.impl.Plugins; import mage.client.plugins.impl.Plugins;
import mage.client.util.CardViewRarityComparator; import mage.client.util.CardViewRarityComparator;
import mage.client.util.ClientEventType;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.Listener; import mage.client.util.Listener;
import mage.client.util.audio.AudioManager; import mage.client.util.audio.AudioManager;
@ -53,6 +47,12 @@ import mage.view.CardView;
import mage.view.CardsView; import mage.view.CardsView;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -186,7 +186,7 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener {
if (e.getButton() == MouseEvent.BUTTON1) { if (e.getButton() == MouseEvent.BUTTON1) {
Object obj = e.getSource(); Object obj = e.getSource();
if (obj instanceof MageCard) { if (obj instanceof MageCard) {
this.cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "pick-a-card"); this.cardEventSource.fireEvent(((MageCard)obj).getOriginal(), ClientEventType.PICK_A_CARD);
this.hidePopup(); this.hidePopup();
AudioManager.playOnDraftSelect(); AudioManager.playOnDraftSelect();
} }
@ -203,7 +203,7 @@ public class DraftGrid extends javax.swing.JPanel implements MouseListener {
if (this.markedCard != null) { if (this.markedCard != null) {
markedCard.setSelected(false); markedCard.setSelected(false);
} }
this.cardEventSource.doubleClick(((MageCard)obj).getOriginal(), "mark-a-card"); this.cardEventSource.fireEvent(((MageCard)obj).getOriginal(), ClientEventType.MARK_A_CARD);
markedCard = ((MageCard)obj); markedCard = ((MageCard)obj);
markedCard.setSelected(true); markedCard.setSelected(true);
repaint(); repaint();

View file

@ -70,7 +70,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
if (card.isSelected()) { if (card.isSelected()) {
stack.set(i, null); stack.set(i, null);
removeCardView(card); removeCardView(card);
eventSource.removeSpecificCard(card, "remove-specific-card"); eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD);
} }
} }
} }
@ -326,7 +326,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
for (CardView card : cards) { for (CardView card : cards) {
card.setSelected(true); card.setSelected(true);
addCardView(card, false); addCardView(card, false);
eventSource.addSpecificCard(card, "add-specific-card"); eventSource.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD);
} }
layoutGrid(); layoutGrid();
cardContent.repaint(); cardContent.repaint();
@ -381,7 +381,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
for (int i = 0; i < stack.size(); ++i) { for (int i = 0; i < stack.size(); ++i) {
CardView card = stack.get(i); CardView card = stack.get(i);
if (card.isSelected()) { if (card.isSelected()) {
eventSource.removeSpecificCard(card, "remove-specific-card"); eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD);
stack.set(i, null); stack.set(i, null);
removeCardView(card); removeCardView(card);
} }
@ -1497,7 +1497,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
if (acard.getName().equals(card.getName())) { if (acard.getName().equals(card.getName())) {
CardView pimpedCard = new CardView(acard); CardView pimpedCard = new CardView(acard);
addCardView(pimpedCard, false); addCardView(pimpedCard, false);
eventSource.addSpecificCard(pimpedCard, "add-specific-card"); eventSource.fireEvent(pimpedCard, ClientEventType.ADD_SPECIFIC_CARD);
pimpedCards.put(pimpedCard, 1); pimpedCards.put(pimpedCard, 1);
didModify = true; didModify = true;
} }
@ -1748,9 +1748,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
if (e.getClickCount() == 1) { if (e.getClickCount() == 1) {
cardClicked(card, e); cardClicked(card, e);
} else if (e.isAltDown()) { } else if (e.isAltDown()) {
eventSource.altDoubleClick(card, "alt-double-click"); eventSource.fireEvent(card, ClientEventType.ALT_DOUBLE_CLICK);
} else { } else {
eventSource.doubleClick(card, "double-click"); eventSource.fireEvent(card, ClientEventType.DOUBLE_CLICK);
} }
} }
} }
@ -1776,7 +1776,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
if (duplicated) { if (duplicated) {
sortIntoGrid(card); sortIntoGrid(card);
eventSource.addSpecificCard(card, "add-specific-card"); eventSource.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD);
// Update layout // Update layout
layoutGrid(); layoutGrid();
// Update draw // Update draw

View file

@ -32,10 +32,6 @@
*/ */
package mage.client.deckeditor; package mage.client.deckeditor;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLayout; import mage.cards.decks.DeckCardLayout;
@ -43,12 +39,18 @@ import mage.client.cards.BigCard;
import mage.client.cards.CardEventSource; import mage.client.cards.CardEventSource;
import mage.client.cards.DragCardGrid; import mage.client.cards.DragCardGrid;
import mage.client.constants.Constants.DeckEditorMode; import mage.client.constants.Constants.DeckEditorMode;
import mage.client.util.ClientEventType;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.GUISizeHelper; import mage.client.util.GUISizeHelper;
import mage.client.util.Listener; import mage.client.util.Listener;
import mage.view.CardView; import mage.view.CardView;
import mage.view.CardsView; import mage.view.CardsView;
import javax.swing.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -124,8 +126,8 @@ public class DeckArea extends javax.swing.JPanel {
// Add to hidden and move to sideboard // Add to hidden and move to sideboard
for (CardView card : cards) { for (CardView card : cards) {
hiddenCards.add(card.getId()); hiddenCards.add(card.getId());
maindeckVirtualEvent.removeSpecificCard(card, "remove-specific-card"); maindeckVirtualEvent.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD);
sideboardVirtualEvent.addSpecificCard(card, "add-specific-card"); sideboardVirtualEvent.fireEvent(card, ClientEventType.ADD_SPECIFIC_CARD);
} }
loadDeck(lastDeck, lastBigCard); loadDeck(lastDeck, lastBigCard);
} }

View file

@ -27,23 +27,6 @@
*/ */
package mage.client.deckeditor; package mage.client.deckeditor;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.filechooser.FileFilter;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.Sets; import mage.cards.Sets;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
@ -72,6 +55,18 @@ import mage.view.CardView;
import mage.view.SimpleCardView; import mage.view.SimpleCardView;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -280,11 +275,11 @@ public class DeckEditorPanel extends javax.swing.JPanel {
component.clearCardEventListeners(); component.clearCardEventListeners();
component.addCardEventListener( component.addCardEventListener(
(Listener<Event>) event -> { (Listener<Event>) event -> {
switch (event.getEventName()) { switch (event.getEventType()) {
case "double-click": case DOUBLE_CLICK:
moveSelectorCardToDeck(event); moveSelectorCardToDeck(event);
break; break;
case "alt-double-click": case ALT_DOUBLE_CLICK:
if (mode == DeckEditorMode.FREE_BUILDING) { if (mode == DeckEditorMode.FREE_BUILDING) {
moveSelectorCardToSideboard(event); moveSelectorCardToSideboard(event);
} else { } else {
@ -292,10 +287,10 @@ public class DeckEditorPanel extends javax.swing.JPanel {
moveSelectorCardToDeck(event); moveSelectorCardToDeck(event);
} }
break; break;
case "remove-main": case REMOVE_MAIN:
DeckEditorPanel.this.deckArea.getDeckList().removeSelection(); DeckEditorPanel.this.deckArea.getDeckList().removeSelection();
break; break;
case "remove-sideboard": case REMOVE_SIDEBOARD:
DeckEditorPanel.this.deckArea.getSideboardList().removeSelection(); DeckEditorPanel.this.deckArea.getSideboardList().removeSelection();
break; break;
} }
@ -306,8 +301,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
this.deckArea.addDeckEventListener( this.deckArea.addDeckEventListener(
(Listener<Event>) event -> { (Listener<Event>) event -> {
if (mode == DeckEditorMode.FREE_BUILDING) { if (mode == DeckEditorMode.FREE_BUILDING) {
switch (event.getEventName()) { switch (event.getEventType()) {
case "double-click": { case DOUBLE_CLICK: {
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) { for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -319,7 +314,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
refreshDeck(); refreshDeck();
break; break;
} }
case "alt-double-click": { case ALT_DOUBLE_CLICK: {
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) { for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -332,11 +327,11 @@ public class DeckEditorPanel extends javax.swing.JPanel {
refreshDeck(); refreshDeck();
break; break;
} }
case "set-number": { case SET_NUMBER: {
setCardNumberToCardsList(event, deck.getCards()); setCardNumberToCardsList(event, deck.getCards());
break; break;
} }
case "remove-specific-card": { case REMOVE_SPECIFIC_CARD: {
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) { for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -347,7 +342,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
break; break;
} }
case "add-specific-card": { case ADD_SPECIFIC_CARD: {
SimpleCardView cardView = (CardView) event.getSource(); SimpleCardView cardView = (CardView) event.getSource();
deck.getCards().add(retrieveTemporaryCard(cardView)); deck.getCards().add(retrieveTemporaryCard(cardView));
break; break;
@ -355,9 +350,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
} else { } else {
// constructing phase or sideboarding during match -> card goes always to sideboard // constructing phase or sideboarding during match -> card goes always to sideboard
switch (event.getEventName()) { switch (event.getEventType()) {
case "double-click": case DOUBLE_CLICK:
case "alt-double-click": { case ALT_DOUBLE_CLICK: {
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) { for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -371,7 +366,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
refreshDeck(); refreshDeck();
break; break;
} }
case "remove-specific-card": { case REMOVE_SPECIFIC_CARD: {
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getCards()) { for (Card card : deck.getCards()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -382,7 +377,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
break; break;
} }
case "add-specific-card": { case ADD_SPECIFIC_CARD: {
SimpleCardView cardView = (CardView) event.getSource(); SimpleCardView cardView = (CardView) event.getSource();
deck.getCards().add(retrieveTemporaryCard(cardView)); deck.getCards().add(retrieveTemporaryCard(cardView));
break; break;
@ -395,8 +390,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
(Listener<Event>) event -> { (Listener<Event>) event -> {
if (mode == DeckEditorMode.FREE_BUILDING) { if (mode == DeckEditorMode.FREE_BUILDING) {
// normal edit mode // normal edit mode
switch (event.getEventName()) { switch (event.getEventType()) {
case "double-click": case DOUBLE_CLICK:
// remove card from sideboard (don't add it to deck) // remove card from sideboard (don't add it to deck)
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) { for (Card card : deck.getSideboard()) {
@ -408,7 +403,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
hidePopup(); hidePopup();
refreshDeck(); refreshDeck();
break; break;
case "alt-double-click": case ALT_DOUBLE_CLICK:
// remove card from sideboard // remove card from sideboard
cardView = (SimpleCardView) event.getSource(); cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) { for (Card card : deck.getSideboard()) {
@ -421,11 +416,11 @@ public class DeckEditorPanel extends javax.swing.JPanel {
hidePopup(); hidePopup();
refreshDeck(); refreshDeck();
break; break;
case "set-number": { case SET_NUMBER: {
setCardNumberToCardsList(event, deck.getSideboard()); setCardNumberToCardsList(event, deck.getSideboard());
break; break;
} }
case "remove-specific-card": { case REMOVE_SPECIFIC_CARD: {
cardView = (SimpleCardView) event.getSource(); cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) { for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -436,7 +431,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
break; break;
} }
case "add-specific-card": { case ADD_SPECIFIC_CARD: {
cardView = (CardView) event.getSource(); cardView = (CardView) event.getSource();
deck.getSideboard().add(retrieveTemporaryCard(cardView)); deck.getSideboard().add(retrieveTemporaryCard(cardView));
break; break;
@ -444,8 +439,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
} else { } else {
// construct phase or sideboarding during match // construct phase or sideboarding during match
switch (event.getEventName()) { switch (event.getEventType()) {
case "remove-specific-card": { case REMOVE_SPECIFIC_CARD: {
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) { for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {
@ -456,13 +451,13 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} }
break; break;
} }
case "add-specific-card": { case ADD_SPECIFIC_CARD: {
SimpleCardView cardView = (CardView) event.getSource(); SimpleCardView cardView = (CardView) event.getSource();
deck.getSideboard().add(retrieveTemporaryCard(cardView)); deck.getSideboard().add(retrieveTemporaryCard(cardView));
break; break;
} }
case "double-click": case DOUBLE_CLICK:
case "alt-double-click": case ALT_DOUBLE_CLICK:
SimpleCardView cardView = (SimpleCardView) event.getSource(); SimpleCardView cardView = (SimpleCardView) event.getSource();
for (Card card : deck.getSideboard()) { for (Card card : deck.getSideboard()) {
if (card.getId().equals(cardView.getId())) { if (card.getId().equals(cardView.getId())) {

View file

@ -33,6 +33,7 @@ import mage.client.cards.CardEventSource;
import mage.client.cards.ICardGrid; import mage.client.cards.ICardGrid;
import mage.client.deckeditor.SortSetting; import mage.client.deckeditor.SortSetting;
import mage.client.plugins.impl.Plugins; import mage.client.plugins.impl.Plugins;
import mage.client.util.ClientEventType;
import mage.client.util.Config; import mage.client.util.Config;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.Listener; import mage.client.util.Listener;
@ -146,7 +147,7 @@ public class TableModel extends AbstractTableModel implements ICardGrid {
} }
// no easy logic for merge :) // no easy logic for merge :)
for (Iterator<Entry<UUID, CardView>> i = cards.entrySet().iterator(); i.hasNext();) { for (Iterator<Entry<UUID, CardView>> i = cards.entrySet().iterator(); i.hasNext(); ) {
Entry<UUID, CardView> entry = i.next(); Entry<UUID, CardView> entry = i.next();
if (!showCards.containsKey(entry.getKey())) { if (!showCards.containsKey(entry.getKey())) {
i.remove(); i.remove();
@ -306,25 +307,25 @@ public class TableModel extends AbstractTableModel implements ICardGrid {
public void setNumber(int index, int number) { public void setNumber(int index, int number) {
CardView card = view.get(index); CardView card = view.get(index);
cardEventSource.setNumber(card, "set-number", number); cardEventSource.fireEvent(card, ClientEventType.SET_NUMBER, number);
} }
public void doubleClick(int index) { public void doubleClick(int index) {
CardView card = view.get(index); CardView card = view.get(index);
cardEventSource.doubleClick(card, "double-click"); cardEventSource.fireEvent(card, ClientEventType.DOUBLE_CLICK);
} }
public void altDoubleClick(int index) { public void altDoubleClick(int index) {
CardView card = view.get(index); CardView card = view.get(index);
cardEventSource.altDoubleClick(card, "alt-double-click"); cardEventSource.fireEvent(card, ClientEventType.ALT_DOUBLE_CLICK);
} }
public void removeFromMainEvent(int index) { public void removeFromMainEvent(int index) {
cardEventSource.removeFromMainEvent("remove-main"); cardEventSource.fireEvent(ClientEventType.REMOVE_MAIN);
} }
public void removeFromSideEvent(int index) { public void removeFromSideEvent(int index) {
cardEventSource.removeFromSideboardEvent("remove-sideboard"); cardEventSource.fireEvent(ClientEventType.REMOVE_SIDEBOARD);
} }
public void addListeners(final JTable table) { public void addListeners(final JTable table) {

View file

@ -121,10 +121,16 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/> <EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" max="-2" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="tooltipDelayLabel" pref="308" max="32767" attributes="0"/> <Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Component id="showCardName" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="tooltipDelayLabel" pref="308" max="32767" attributes="0"/>
<Component id="tooltipDelay" alignment="1" max="32767" attributes="0"/> <Component id="tooltipDelay" alignment="1" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="showCardName" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="showFullImagePath" min="-2" max="-2" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
</Group> </Group>
@ -133,7 +139,10 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="showCardName" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0">
<Component id="showCardName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="showFullImagePath" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="tooltipDelayLabel" min="-2" max="-2" attributes="0"/> <Component id="tooltipDelayLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -176,6 +185,24 @@
<Property name="value" type="int" value="300"/> <Property name="value" type="int" value="300"/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="showFullImagePath">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="toolTipText" type="java.lang.String" value="Show the path Xmage is expecting for this card&apos;s image (only displays if missing)"/>
<Property name="actionCommand" type="java.lang.String" value=""/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Default Cursor"/>
</Property>
<Property name="label" type="java.lang.String" value="Display image path for missing images"/>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" value="Display image path for missing images"/>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" value="Show the path Xmage is expecting for this card&apos;s image (only displays if missing)"/>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showFullImagePathActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JPanel" name="main_game"> <Container class="javax.swing.JPanel" name="main_game">

View file

@ -91,6 +91,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_SHOW_TOOLTIPS_DELAY = "showTooltipsDelay"; public static final String KEY_SHOW_TOOLTIPS_DELAY = "showTooltipsDelay";
public static final String KEY_SHOW_CARD_NAMES = "showCardNames"; public static final String KEY_SHOW_CARD_NAMES = "showCardNames";
public static final String KEY_SHOW_FULL_IMAGE_PATH = "showFullImagePath";
public static final String KEY_PERMANENTS_IN_ONE_PILE = "nonLandPermanentsInOnePile"; public static final String KEY_PERMANENTS_IN_ONE_PILE = "nonLandPermanentsInOnePile";
public static final String KEY_SHOW_PLAYER_NAMES_PERMANENTLY = "showPlayerNamesPermanently"; public static final String KEY_SHOW_PLAYER_NAMES_PERMANENTLY = "showPlayerNamesPermanently";
public static final String KEY_SHOW_ABILITY_PICKER_FORCED = "showAbilityPicker"; public static final String KEY_SHOW_ABILITY_PICKER_FORCED = "showAbilityPicker";
@ -399,6 +400,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
tabMain = new javax.swing.JPanel(); tabMain = new javax.swing.JPanel();
main_card = new javax.swing.JPanel(); main_card = new javax.swing.JPanel();
showCardName = new javax.swing.JCheckBox(); showCardName = new javax.swing.JCheckBox();
showFullImagePath = new javax.swing.JCheckBox();
tooltipDelayLabel = new javax.swing.JLabel(); tooltipDelayLabel = new javax.swing.JLabel();
tooltipDelay = new javax.swing.JSlider(); tooltipDelay = new javax.swing.JSlider();
main_game = new javax.swing.JPanel(); main_game = new javax.swing.JPanel();
@ -613,28 +615,48 @@ public class PreferencesDialog extends javax.swing.JDialog {
tooltipDelay.setToolTipText("<HTML>The time the appearance of the tooltip window for a card is delayed.<br>\nIf set to zero, the tooltip window won't be shown at all."); tooltipDelay.setToolTipText("<HTML>The time the appearance of the tooltip window for a card is delayed.<br>\nIf set to zero, the tooltip window won't be shown at all.");
tooltipDelay.setValue(300); tooltipDelay.setValue(300);
showFullImagePath.setSelected(false);
showFullImagePath.setToolTipText("Show the path Xmage is expecting for this card's image (only displays if missing)");
showFullImagePath.setActionCommand("");
showFullImagePath.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
showFullImagePath.setLabel("Display image path for missing images");
showFullImagePath.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showFullImagePathActionPerformed(evt);
}
});
org.jdesktop.layout.GroupLayout main_cardLayout = new org.jdesktop.layout.GroupLayout(main_card); org.jdesktop.layout.GroupLayout main_cardLayout = new org.jdesktop.layout.GroupLayout(main_card);
main_card.setLayout(main_cardLayout); main_card.setLayout(main_cardLayout);
main_cardLayout.setHorizontalGroup( main_cardLayout.setHorizontalGroup(
main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(main_cardLayout.createSequentialGroup() .add(main_cardLayout.createSequentialGroup()
.add(6, 6, 6) .add(6, 6, 6)
.add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false) .add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 308, Short.MAX_VALUE) .add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
.add(org.jdesktop.layout.GroupLayout.LEADING, showCardName) .add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 308, Short.MAX_VALUE)
.add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.add(main_cardLayout.createSequentialGroup()
.add(showCardName)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(showFullImagePath)))
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
main_cardLayout.setVerticalGroup( main_cardLayout.setVerticalGroup(
main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(main_cardLayout.createSequentialGroup() .add(main_cardLayout.createSequentialGroup()
.add(showCardName) .add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(showCardName)
.add(showFullImagePath))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(tooltipDelayLabel) .add(tooltipDelayLabel)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
showFullImagePath.getAccessibleContext().setAccessibleName("Display image path for missing images");
showFullImagePath.getAccessibleContext().setAccessibleDescription("Show the path Xmage is expecting for this card's image (only displays if missing)");
main_game.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Game")); main_game.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Game"));
nonLandPermanentsInOnePile.setSelected(true); nonLandPermanentsInOnePile.setSelected(true);
@ -720,7 +742,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(cbAllowRequestToShowHandCards, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbAllowRequestToShowHandCards, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbShowStormCounter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbShowStormCounter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(255, Short.MAX_VALUE))
); );
main_gameLayout.setVerticalGroup( main_gameLayout.setVerticalGroup(
main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
@ -2679,6 +2701,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
// main // main
save(prefs, dialog.tooltipDelay, KEY_SHOW_TOOLTIPS_DELAY, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.tooltipDelay, KEY_SHOW_TOOLTIPS_DELAY, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.showCardName, KEY_SHOW_CARD_NAMES, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.showCardName, KEY_SHOW_CARD_NAMES, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.showFullImagePath, KEY_SHOW_FULL_IMAGE_PATH, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.nonLandPermanentsInOnePile, KEY_PERMANENTS_IN_ONE_PILE, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.nonLandPermanentsInOnePile, KEY_PERMANENTS_IN_ONE_PILE, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.showPlayerNamesPermanently, KEY_SHOW_PLAYER_NAMES_PERMANENTLY, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.showPlayerNamesPermanently, KEY_SHOW_PLAYER_NAMES_PERMANENTLY, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true", "false", UPDATE_CACHE_POLICY);
@ -3119,6 +3142,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
}); });
}//GEN-LAST:event_bttnResetControlsActionPerformed }//GEN-LAST:event_bttnResetControlsActionPerformed
private void showFullImagePathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showFullImagePathActionPerformed
}//GEN-LAST:event_showFullImagePathActionPerformed
private void showProxySettings() { private void showProxySettings() {
Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem();
switch (proxyType) { switch (proxyType) {
@ -3222,6 +3248,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private static void loadPhases(Preferences prefs) { private static void loadPhases(Preferences prefs) {
load(prefs, dialog.tooltipDelay, KEY_SHOW_TOOLTIPS_DELAY, "300"); load(prefs, dialog.tooltipDelay, KEY_SHOW_TOOLTIPS_DELAY, "300");
load(prefs, dialog.showCardName, KEY_SHOW_CARD_NAMES, "true"); load(prefs, dialog.showCardName, KEY_SHOW_CARD_NAMES, "true");
load(prefs, dialog.showFullImagePath, KEY_SHOW_FULL_IMAGE_PATH, "true");
load(prefs, dialog.nonLandPermanentsInOnePile, KEY_PERMANENTS_IN_ONE_PILE, "true"); load(prefs, dialog.nonLandPermanentsInOnePile, KEY_PERMANENTS_IN_ONE_PILE, "true");
load(prefs, dialog.showPlayerNamesPermanently, KEY_SHOW_PLAYER_NAMES_PERMANENTLY, "true"); load(prefs, dialog.showPlayerNamesPermanently, KEY_SHOW_PLAYER_NAMES_PERMANENTLY, "true");
load(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true"); load(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true");
@ -3898,6 +3925,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JButton saveButton; private javax.swing.JButton saveButton;
private javax.swing.JCheckBox showAbilityPickerForced; private javax.swing.JCheckBox showAbilityPickerForced;
private javax.swing.JCheckBox showCardName; private javax.swing.JCheckBox showCardName;
private javax.swing.JCheckBox showFullImagePath;
private javax.swing.JCheckBox showPlayerNamesPermanently; private javax.swing.JCheckBox showPlayerNamesPermanently;
private javax.swing.JSlider sliderCardSizeHand; private javax.swing.JSlider sliderCardSizeHand;
private javax.swing.JSlider sliderCardSizeMaxBattlefield; private javax.swing.JSlider sliderCardSizeMaxBattlefield;

View file

@ -438,7 +438,10 @@ class UpdateSeatsTask extends SwingWorker<Void, TableView> {
int current = getPlayersCount(tableView); int current = getPlayersCount(tableView);
if (current != count) { if (current != count) {
if (count > 0) { if (count > 0) {
if (current > count) { if (current == tableView.getSeats().size()) {
MageTray.instance.displayMessage("The game can start.");
AudioManager.playGameCanStart();
} else if (current > count) {
MageTray.instance.displayMessage("New player joined your game."); MageTray.instance.displayMessage("New player joined your game.");
AudioManager.playPlayerJoinedTable(); AudioManager.playPlayerJoinedTable();
} else { } else {

View file

@ -33,9 +33,25 @@
*/ */
package mage.client.draft; package mage.client.draft;
import java.awt.Component; import mage.cards.repository.CardInfo;
import java.awt.Dimension; import mage.cards.repository.CardRepository;
import java.awt.Image; import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.components.tray.MageTray;
import mage.client.deckeditor.SortSettingDraft;
import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.*;
import mage.client.util.Event;
import mage.client.util.audio.AudioManager;
import mage.client.util.gui.BufferedImageBuilder;
import mage.constants.PlayerAction;
import mage.view.*;
import org.apache.log4j.Logger;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
@ -46,43 +62,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collection; import java.util.*;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.components.tray.MageTray;
import mage.client.deckeditor.SortSettingDraft;
import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.CardsViewUtil;
import mage.client.util.Event;
import mage.client.util.GUISizeHelper;
import mage.client.util.ImageHelper;
import mage.client.util.Listener;
import mage.client.util.audio.AudioManager;
import mage.client.util.gui.BufferedImageBuilder;
import mage.constants.PlayerAction;
import mage.view.CardsView;
import mage.view.DraftPickView;
import mage.view.DraftView;
import mage.view.SimpleCardView;
import mage.view.SimpleCardsView;
import mage.view.UserRequestMessage;
import org.apache.log4j.Logger;
/** /**
* *
@ -304,7 +284,7 @@ public class DraftPanel extends javax.swing.JPanel {
this.draftPicks.clearCardEventListeners(); this.draftPicks.clearCardEventListeners();
this.draftPicks.addCardEventListener((Listener<Event>) event -> { this.draftPicks.addCardEventListener((Listener<Event>) event -> {
if (event.getEventName().equals("show-popup-menu")) { if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) {
if (event.getSource() != null) { if (event.getSource() != null) {
// Popup Menu Card // Popup Menu Card
cardIdPopupMenu = ((SimpleCardView) event.getSource()).getId(); cardIdPopupMenu = ((SimpleCardView) event.getSource()).getId();
@ -322,7 +302,7 @@ public class DraftPanel extends javax.swing.JPanel {
this.draftBooster.clearCardEventListeners(); this.draftBooster.clearCardEventListeners();
this.draftBooster.addCardEventListener( this.draftBooster.addCardEventListener(
(Listener<Event>) event -> { (Listener<Event>) event -> {
if (event.getEventName().equals("pick-a-card")) { if (event.getEventType() == ClientEventType.PICK_A_CARD) {
SimpleCardView source = (SimpleCardView) event.getSource(); SimpleCardView source = (SimpleCardView) event.getSource();
DraftPickView view = SessionHandler.sendCardPick(draftId, source.getId(), cardsHidden); DraftPickView view = SessionHandler.sendCardPick(draftId, source.getId(), cardsHidden);
if (view != null) { if (view != null) {
@ -332,7 +312,7 @@ public class DraftPanel extends javax.swing.JPanel {
setMessage("Waiting for other players"); setMessage("Waiting for other players");
} }
} }
if (event.getEventName().equals("mark-a-card")) { if (event.getEventType() == ClientEventType.MARK_A_CARD) {
SimpleCardView source = (SimpleCardView) event.getSource(); SimpleCardView source = (SimpleCardView) event.getSource();
SessionHandler.sendCardMark(draftId, source.getId()); SessionHandler.sendCardMark(draftId, source.getId());
} }

View file

@ -27,61 +27,6 @@
*/ */
package mage.client.game; package mage.client.game;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import static java.awt.Component.LEFT_ALIGNMENT;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.choices.Choice; import mage.choices.Choice;
@ -95,51 +40,43 @@ import mage.client.components.KeyboundButton;
import mage.client.components.MageComponents; import mage.client.components.MageComponents;
import mage.client.components.ext.dlg.DialogManager; import mage.client.components.ext.dlg.DialogManager;
import mage.client.components.layout.RelativeLayout; import mage.client.components.layout.RelativeLayout;
import mage.client.dialog.CardInfoWindowDialog; import mage.client.dialog.*;
import mage.client.dialog.CardInfoWindowDialog.ShowType; import mage.client.dialog.CardInfoWindowDialog.ShowType;
import mage.client.dialog.PickChoiceDialog;
import mage.client.dialog.PickNumberDialog;
import mage.client.dialog.PickPileDialog;
import mage.client.dialog.PreferencesDialog;
import static mage.client.dialog.PreferencesDialog.*;
import mage.client.dialog.ShowCardsDialog;
import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.game.FeedbackPanel.FeedbackMode;
import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.adapters.MageActionCallback;
import mage.client.plugins.impl.Plugins; import mage.client.plugins.impl.Plugins;
import mage.client.util.CardsViewUtil; import mage.client.util.*;
import mage.client.util.Event; import mage.client.util.Event;
import mage.client.util.GUISizeHelper;
import mage.client.util.GameManager;
import mage.client.util.Listener;
import mage.client.util.audio.AudioManager; import mage.client.util.audio.AudioManager;
import mage.client.util.gui.ArrowBuilder; import mage.client.util.gui.ArrowBuilder;
import mage.client.util.gui.MageDialogState; import mage.client.util.gui.MageDialogState;
import mage.constants.Constants; import mage.constants.*;
import mage.constants.EnlargeMode;
import mage.constants.PhaseStep;
import mage.constants.PlayerAction;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_ABILITY_FIRST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_ABILITY_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_FIRST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.constants.Zone;
import mage.game.events.PlayerQueryEvent; import mage.game.events.PlayerQueryEvent;
import mage.view.AbilityPickerView; import mage.view.*;
import mage.view.CardView;
import mage.view.CardsView;
import mage.view.ExileView;
import mage.view.GameView;
import mage.view.LookedAtView;
import mage.view.MatchView;
import mage.view.PlayerView;
import mage.view.RevealedView;
import mage.view.SimpleCardsView;
import mage.view.UserRequestMessage;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.mage.card.arcane.CardPanel; import org.mage.card.arcane.CardPanel;
import org.mage.plugins.card.utils.impl.ImageManagerImpl; import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.Timer;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import static mage.client.dialog.PreferencesDialog.*;
import static mage.constants.PlayerAction.*;
/** /**
* @author BetaSteward_at_googlemail.com, nantuko8 * @author BetaSteward_at_googlemail.com, nantuko8
*/ */
@ -2251,7 +2188,7 @@ public final class GamePanel extends javax.swing.JPanel {
// Event listener for the ShowCardsDialog // Event listener for the ShowCardsDialog
private Listener<Event> getShowCardsEventListener(final ShowCardsDialog dialog) { private Listener<Event> getShowCardsEventListener(final ShowCardsDialog dialog) {
return (Listener<Event>) event -> { return (Listener<Event>) event -> {
if (event.getEventName().equals("show-popup-menu")) { if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) {
if (event.getComponent() != null && event.getComponent() instanceof CardPanel) { if (event.getComponent() != null && event.getComponent() instanceof CardPanel) {
JPopupMenu menu = ((CardPanel) event.getComponent()).getPopupMenu(); JPopupMenu menu = ((CardPanel) event.getComponent()).getPopupMenu();
if (menu != null) { if (menu != null) {
@ -2260,7 +2197,7 @@ public final class GamePanel extends javax.swing.JPanel {
} }
} }
} }
if (event.getEventName().equals("action-consumed")) { if (event.getEventType() == ClientEventType.ACTION_CONSUMED) {
dialog.removeDialog(); dialog.removeDialog();
} }
}; };

View file

@ -27,6 +27,10 @@
*/ */
package mage.client.remote; package mage.client.remote;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.UUID;
import javax.swing.*;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.client.MageFrame; import mage.client.MageFrame;
import mage.client.SessionHandler; import mage.client.SessionHandler;
@ -48,11 +52,6 @@ import mage.view.*;
import mage.view.ChatMessage.MessageType; import mage.view.ChatMessage.MessageType;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.UUID;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -325,11 +324,11 @@ public class CallbackClientImpl implements CallbackClient {
break; break;
} }
case VIEW_LIMITED_DECK: { case VIEW_LIMITED_DECK: {
TableClientMessage message = (TableClientMessage) callback.getData(); TableClientMessage message = (TableClientMessage) callback.getData();
DeckView deckView = message.getDeck(); DeckView deckView = message.getDeck();
Deck deck = DeckUtil.construct(deckView); Deck deck = DeckUtil.construct(deckView);
viewLimitedDeck(deck, message.getTableId(), message.getTime()); viewLimitedDeck(deck, message.getTableId(), message.getTime());
break; break;
} }
case CONSTRUCT: { case CONSTRUCT: {
TableClientMessage message = (TableClientMessage) callback.getData(); TableClientMessage message = (TableClientMessage) callback.getData();
@ -356,14 +355,6 @@ public class CallbackClientImpl implements CallbackClient {
} }
break; break;
} }
case DRAFT_INFORM: // if (callback.getMessageId() > messageId) {
{
DraftClientMessage message = (DraftClientMessage) callback.getData();
}
// } else {
// logger.warn("message out of sequence - ignoring");
// }
break;
case DRAFT_INIT: { case DRAFT_INIT: {
DraftClientMessage message = (DraftClientMessage) callback.getData(); DraftClientMessage message = (DraftClientMessage) callback.getData();
DraftPanel panel = MageFrame.getDraft(callback.getObjectId()); DraftPanel panel = MageFrame.getDraft(callback.getObjectId());

View file

@ -28,11 +28,9 @@
package mage.client.table; package mage.client.table;
import mage.client.util.*;
import java.io.Serializable; import java.io.Serializable;
import mage.client.util.Event;
import mage.client.util.EventDispatcher;
import mage.client.util.EventSource;
import mage.client.util.Listener;
/** /**
* *
@ -48,7 +46,7 @@ public class PlayerTypeEventSource implements EventSource<Event>, Serializable {
} }
public void playerTypeChanged() { public void playerTypeChanged() {
dispatcher.fireEvent(new Event(null, "playerTypeChanged")); dispatcher.fireEvent(new Event(null, ClientEventType.PLAYER_TYPE_CHANGED));
} }
@Override @Override

View file

@ -0,0 +1,20 @@
package mage.client.util;
public enum ClientEventType {
SET_NUMBER,
ACTION_CONSUMED,
DOUBLE_CLICK,
ALT_DOUBLE_CLICK,
REMOVE_MAIN,
REMOVE_SIDEBOARD,
SHOW_POP_UP_MENU,
REMOVE_SPECIFIC_CARD,
ADD_SPECIFIC_CARD,
PICK_A_CARD,
MARK_A_CARD,
PLAYER_TYPE_CHANGED
}

View file

@ -38,27 +38,27 @@ import java.io.Serializable;
public class Event implements Serializable { public class Event implements Serializable {
private final Object source; private final Object source;
private final Component component; private final Component component;
private final String eventName; private final ClientEventType eventType;
private final int number; private final int number;
private final int xPos; private final int xPos;
private final int yPos; private final int yPos;
public Event(Object source, String eventName) { public Event(Object source, ClientEventType eventType) {
this(source, eventName, 0); this(source, eventType, 0);
} }
public Event(Object source, String eventName, int number) { public Event(Object source, ClientEventType eventType, int number) {
this.source = source; this.source = source;
this.eventName = eventName; this.eventType = eventType;
this.number = number; this.number = number;
this.xPos = 0; this.xPos = 0;
this.yPos = 0; this.yPos = 0;
this.component = null; this.component = null;
} }
public Event(Object source, String eventName, int xPos, int yPos, Component component) { public Event(Object source, ClientEventType eventType, int xPos, int yPos, Component component) {
this.source = source; this.source = source;
this.eventName = eventName; this.eventType = eventType;
this.number =0; this.number =0;
this.xPos = xPos; this.xPos = xPos;
this.yPos = yPos; this.yPos = yPos;
@ -69,8 +69,8 @@ public class Event implements Serializable {
return source; return source;
} }
public String getEventName() { public ClientEventType getEventType() {
return eventName; return eventType;
} }
public int getNumber() { public int getNumber() {

View file

@ -40,6 +40,7 @@ public class AudioManager {
private MageClip tournamentStarted = null; private MageClip tournamentStarted = null;
private MageClip yourGameStarted = null; private MageClip yourGameStarted = null;
private MageClip playerJoinedTable = null; private MageClip playerJoinedTable = null;
private MageClip gameCanStart = null;
private MageClip playerSubmittedDeck = null; private MageClip playerSubmittedDeck = null;
private MageClip playerWhispered = null; private MageClip playerWhispered = null;
private MageClip playerLeft = null; private MageClip playerLeft = null;
@ -218,6 +219,13 @@ public class AudioManager {
} }
checkAndPlayClip(audioManager.playerJoinedTable); checkAndPlayClip(audioManager.playerJoinedTable);
} }
public static void playGameCanStart() {
if (audioManager.gameCanStart == null) {
audioManager.gameCanStart = new MageClip(Constants.BASE_SOUND_PATH + "GameCanStart.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(audioManager.gameCanStart);
}
public static void playYourGameStarted() { public static void playYourGameStarted() {
if (audioManager.yourGameStarted == null) { if (audioManager.yourGameStarted == null) {

View file

@ -31,7 +31,8 @@ import java.util.UUID;
import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL; import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
/** /**
* Class for drawing the mage card object by using a form based JComponent approach * Class for drawing the mage card object by using a form based JComponent
* approach
* *
* @author arcane, nantuko, noxx, stravant * @author arcane, nantuko, noxx, stravant
*/ */
@ -68,10 +69,13 @@ public class CardPanelComponentImpl extends CardPanel {
private final GlowText titleText; private final GlowText titleText;
private final GlowText ptText; private final GlowText ptText;
private final JLabel fullImageText;
private String fullImagePath = null;
private boolean hasImage = false; private boolean hasImage = false;
private boolean displayTitleAnyway; private boolean displayTitleAnyway;
private boolean displayFullImagePath;
private final static Map<Key, BufferedImage> IMAGE_CACHE; private final static Map<Key, BufferedImage> IMAGE_CACHE;
@ -202,7 +206,7 @@ public class CardPanelComponentImpl extends CardPanel {
counterPanel.setVisible(false); counterPanel.setVisible(false);
} }
// Ability icon // Ability icon
if (newGameCard.isAbility()) { if (newGameCard.isAbility()) {
if (newGameCard.getAbilityType() == AbilityType.TRIGGERED) { if (newGameCard.getAbilityType() == AbilityType.TRIGGERED) {
@ -211,13 +215,14 @@ public class CardPanelComponentImpl extends CardPanel {
setTypeIcon(ImageManagerImpl.instance.getActivatedAbilityImage(), "Activated Ability"); setTypeIcon(ImageManagerImpl.instance.getActivatedAbilityImage(), "Activated Ability");
} }
} }
// Token icon // Token icon
if (this.gameCard.isToken()) { if (this.gameCard.isToken()) {
setTypeIcon(ImageManagerImpl.instance.getTokenIconImage(), "Token Permanent"); setTypeIcon(ImageManagerImpl.instance.getTokenIconImage(), "Token Permanent");
} }
displayTitleAnyway = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_CARD_NAMES, "true").equals("true"); displayTitleAnyway = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_CARD_NAMES, "true").equals("true");
displayFullImagePath = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_FULL_IMAGE_PATH, "false").equals("true");
// Title Text // Title Text
titleText = new GlowText(); titleText = new GlowText();
@ -229,6 +234,12 @@ public class CardPanelComponentImpl extends CardPanel {
titleText.setWrap(true); titleText.setWrap(true);
add(titleText); add(titleText);
// Full path to image text
fullImageText = new JLabel();
fullImageText.setText(fullImagePath);
fullImageText.setForeground(Color.BLACK);
add(fullImageText);
// PT Text // PT Text
ptText = new GlowText(); ptText = new GlowText();
if (gameCard.isCreature()) { if (gameCard.isCreature()) {
@ -300,10 +311,19 @@ public class CardPanelComponentImpl extends CardPanel {
doLayout(); doLayout();
} }
private void setFullPath(String fullImagePath) {
this.fullImagePath = fullImagePath;
this.fullImagePath = this.fullImagePath.replaceAll("\\\\", "\\\\<br>");
this.fullImagePath = this.fullImagePath.replaceAll("/", "/<br>");
this.fullImagePath = "<html>" + this.fullImagePath + "</html>";
fullImageText.setText(!displayFullImagePath ? "" : this.fullImagePath);
doLayout();
}
@Override @Override
public void transferResources(final CardPanel panelAbstract) { public void transferResources(final CardPanel panelAbstract) {
if (panelAbstract instanceof CardPanelComponentImpl) { if (panelAbstract instanceof CardPanelComponentImpl) {
CardPanelComponentImpl panel = (CardPanelComponentImpl)panelAbstract; CardPanelComponentImpl panel = (CardPanelComponentImpl) panelAbstract;
synchronized (panel.imagePanel) { synchronized (panel.imagePanel) {
if (panel.imagePanel.hasImage()) { if (panel.imagePanel.hasImage()) {
setImage(panel.imagePanel.getSrcImage()); setImage(panel.imagePanel.getSrcImage());
@ -321,7 +341,7 @@ public class CardPanelComponentImpl extends CardPanel {
this.titleText.setGlowColor(Color.black); this.titleText.setGlowColor(Color.black);
} }
} }
@Override @Override
protected void paintCard(Graphics2D g2d) { protected void paintCard(Graphics2D g2d) {
float alpha = getAlpha(); float alpha = getAlpha();
@ -333,9 +353,9 @@ public class CardPanelComponentImpl extends CardPanel {
g2d.drawImage( g2d.drawImage(
IMAGE_CACHE.get( IMAGE_CACHE.get(
new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(), new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(),
hasImage, isSelected(), isChoosable(), gameCard.isPlayable(), gameCard.isCanAttack())), hasImage, isSelected(), isChoosable(), gameCard.isPlayable(), gameCard.isCanAttack())),
0, 0, null); 0, 0, null);
g2d.dispose(); g2d.dispose();
} }
private static BufferedImage createImage(Key key) { private static BufferedImage createImage(Key key) {
@ -414,7 +434,7 @@ public class CardPanelComponentImpl extends CardPanel {
@Override @Override
public void doLayout() { public void doLayout() {
super.doLayout(); super.doLayout();
int cardWidth = getCardWidth(); int cardWidth = getCardWidth();
int cardHeight = getCardHeight(); int cardHeight = getCardHeight();
int cardXOffset = getCardXOffset(); int cardXOffset = getCardXOffset();
@ -456,6 +476,7 @@ public class CardPanelComponentImpl extends CardPanel {
boolean showText = (!isAnimationPanel() && fontHeight < 12); boolean showText = (!isAnimationPanel() && fontHeight < 12);
titleText.setVisible(showText); titleText.setVisible(showText);
ptText.setVisible(showText); ptText.setVisible(showText);
fullImageText.setVisible(fullImagePath != null);
if (showText) { if (showText) {
int fontSize = cardHeight / 11; int fontSize = cardHeight / 11;
@ -465,6 +486,9 @@ public class CardPanelComponentImpl extends CardPanel {
int titleY = Math.round(cardHeight * (9f / 680)) + getTextOffset(); int titleY = Math.round(cardHeight * (9f / 680)) + getTextOffset();
titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight - titleY); titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight - titleY);
fullImageText.setFont(getFont().deriveFont(Font.PLAIN, 10));
fullImageText.setBounds(cardXOffset, cardYOffset + titleY, cardWidth, cardHeight - titleY);
ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
Dimension ptSize = ptText.getPreferredSize(); Dimension ptSize = ptText.getPreferredSize();
ptText.setSize(ptSize.width, ptSize.height); ptText.setSize(ptSize.width, ptSize.height);
@ -486,7 +510,7 @@ public class CardPanelComponentImpl extends CardPanel {
public void setCardBounds(int x, int y, int cardWidth, int cardHeight) { public void setCardBounds(int x, int y, int cardWidth, int cardHeight) {
// Call to super // Call to super
super.setCardBounds(x, y, cardWidth, cardHeight); super.setCardBounds(x, y, cardWidth, cardHeight);
// Update image // Update image
if (imagePanel != null && imagePanel.getSrcImage() != null) { if (imagePanel != null && imagePanel.getSrcImage() != null) {
updateArtImage(); updateArtImage();
@ -496,7 +520,7 @@ public class CardPanelComponentImpl extends CardPanel {
@Override @Override
public void setAlpha(float alpha) { public void setAlpha(float alpha) {
super.setAlpha(alpha); super.setAlpha(alpha);
// Update components // Update components
if (alpha == 0) { if (alpha == 0) {
this.ptText.setVisible(false); this.ptText.setVisible(false);
@ -529,6 +553,9 @@ public class CardPanelComponentImpl extends CardPanel {
} else { } else {
srcImage = ImageCache.getThumbnail(gameCard); srcImage = ImageCache.getThumbnail(gameCard);
} }
if (srcImage == null) {
setFullPath(ImageCache.getFilePath(gameCard, getCardWidth()));
}
UI.invokeLater(() -> { UI.invokeLater(() -> {
if (stamp == updateArtImageStamp) { if (stamp == updateArtImageStamp) {
hasImage = srcImage != null; hasImage = srcImage != null;

View file

@ -5,10 +5,7 @@
*/ */
package org.mage.card.arcane; package org.mage.card.arcane;
import java.awt.Color; import java.awt.*;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -19,8 +16,8 @@ import java.util.regex.Pattern;
/** /**
* @author stravant@gmail.com * @author stravant@gmail.com
* * <p>
* Various static utilities for use in the card renderer * Various static utilities for use in the card renderer
*/ */
public final class CardRendererUtils { public final class CardRendererUtils {
@ -124,6 +121,8 @@ public final class CardRendererUtils {
} }
public static String killReminderText(String rule) { public static String killReminderText(String rule) {
return killReminderTextPattern.matcher(rule).replaceAll(""); return killReminderTextPattern.matcher(rule).replaceAll("")
.replaceAll("<i>", "")
.replaceAll("</i>", "");
} }
} }

View file

@ -602,7 +602,7 @@ public class ModernCardRenderer extends CardRenderer {
int partWidth = (int) Math.max(30, 0.20f * cardWidth); int partWidth = (int) Math.max(30, 0.20f * cardWidth);
// Is it a creature? // Is it a creature?
boolean isVehicle = cardView.getSubTypes().contains("Vehicle"); boolean isVehicle = cardView.getSubTypes().contains(SubType.VEHICLE);
if (cardView.isCreature() || isVehicle) { if (cardView.isCreature() || isVehicle) {
int x = cardWidth - borderWidth - partWidth; int x = cardWidth - borderWidth - partWidth;

View file

@ -1,5 +1,6 @@
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import java.util.ArrayList;
import org.mage.plugins.card.images.CardDownloadData; import org.mage.plugins.card.images.CardDownloadData;
/** /**
@ -9,12 +10,38 @@ import org.mage.plugins.card.images.CardDownloadData;
public interface CardImageSource { public interface CardImageSource {
String generateURL(CardDownloadData card) throws Exception; String generateURL(CardDownloadData card) throws Exception;
String generateTokenUrl(CardDownloadData card) throws Exception; String generateTokenUrl(CardDownloadData card) throws Exception;
String getNextHttpImageUrl(); String getNextHttpImageUrl();
String getFileForHttpImage(String httpImageUrl); String getFileForHttpImage(String httpImageUrl);
String getSourceName(); String getSourceName();
float getAverageSize(); float getAverageSize();
int getTotalImages(); int getTotalImages();
boolean isTokenSource();
default int getTokenImages() {
return 0;
}
default boolean isTokenSource() {
return false;
}
void doPause(String httpImageUrl); void doPause(String httpImageUrl);
default ArrayList<String> getSupportedSets() {
return null;
}
default boolean isSetSupportedComplete(String setCode) {
return true;
}
default boolean isImageProvided(String setCode, String cardName) {
return false;
}
} }

View file

@ -1,7 +1,10 @@
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import org.mage.plugins.card.images.CardDownloadData; import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils; import org.mage.plugins.card.utils.CardImageUtils;
@ -13,6 +16,202 @@ import org.mage.plugins.card.utils.CardImageUtils;
public enum MagicCardsImageSource implements CardImageSource { public enum MagicCardsImageSource implements CardImageSource {
instance; instance;
private static final Set<String> supportedSets = new LinkedHashSet<String>() {
{
// add("PTC"); // Prerelease Events
add("LEA");
add("LEB");
add("2ED");
add("ARN");
add("ATQ");
add("3ED");
add("LEG");
add("DRK");
add("FEM");
add("4ED");
add("ICE");
add("CHR");
add("HML");
add("ALL");
add("MIR");
add("VIS");
add("5ED");
add("POR");
add("WTH");
add("TMP");
add("STH");
add("EXO");
add("P02");
add("UGL");
add("USG");
add("DD3DVD");
add("DD3EVG");
add("DD3GVL");
add("DD3JVC");
add("ULG");
add("6ED");
add("UDS");
add("PTK");
add("S99");
add("MMQ");
// add("BRB");Battle Royale Box Set
add("NEM");
add("S00");
add("PCY");
add("INV");
// add("BTD"); // Beatdown Boxset
add("PLS");
add("7ED");
add("APC");
add("ODY");
// add("DKM"); // Deckmasters 2001
add("TOR");
add("JUD");
add("ONS");
add("LGN");
add("SCG");
add("8ED");
add("MRD");
add("DST");
add("5DN");
add("CHK");
add("UNH");
add("BOK");
add("SOK");
add("9ED");
add("RAV");
add("GPT");
add("DIS");
add("CSP");
add("TSP");
add("TSB");
add("PLC");
add("FUT");
add("10E");
add("MED");
add("LRW");
add("EVG");
add("MOR");
add("SHM");
add("EVE");
add("DRB");
add("ME2");
add("ALA");
add("DD2");
add("CON");
add("DDC");
add("ARB");
add("M10");
// add("TD0"); // Magic Online Deck Series
add("V09");
add("HOP");
add("ME3");
add("ZEN");
add("DDD");
add("H09");
add("WWK");
add("DDE");
add("ROE");
add("DPA");
add("ARC");
add("M11");
add("V10");
add("DDF");
add("SOM");
// add("TD0"); // Commander Theme Decks
add("PD2");
add("ME4");
add("MBS");
add("DDG");
add("NPH");
add("CMD");
add("M12");
add("V11");
add("DDH");
add("ISD");
add("PD3");
add("DKA");
add("DDI");
add("AVR");
add("PC2");
add("M13");
add("V12");
add("DDJ");
add("RTR");
add("CM1");
// add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
add("GTC");
add("DDK");
add("DGM");
add("MMA");
add("M14");
add("V13");
add("DDL");
add("THS");
add("C13");
add("BNG");
add("DDM");
add("JOU");
// add("MD1"); // Modern Event Deck
add("CNS");
add("VMA");
add("M15");
add("V14");
add("DDN");
add("KTK");
add("C14");
// add("DD3"); // Duel Decks Anthology
add("FRF");
add("DDO");
add("DTK");
add("TPR");
add("MM2");
add("ORI");
add("V15");
add("DDP");
add("BFZ");
add("EXP");
add("C15");
// add("PZ1"); // Legendary Cube
add("OGW");
add("DDQ");
add("W16");
add("SOI");
add("EMA");
add("EMN");
add("V16");
add("CN2");
add("DDR");
add("KLD");
add("MPS");
// add("PZ2"); // Treasure Chests
add("C16");
add("PCA");
add("AER");
add("MM3");
add("DDS");
add("W17");
add("AKH");
add("MPS");
add("CMA");
add("E01");
add("HOU");
add("C17");
// add("XLN");
// add("DDT");
// add("IMA");
// add("E02");
// add("V17");
// add("UST");
// add("RIX");
// add("A25");
// add("DOM");
// add("M19");
}
};
private static final Map<String, String> setNameTokenReplacement = new HashMap<String, String>() { private static final Map<String, String> setNameTokenReplacement = new HashMap<String, String>() {
{ {
put("10E", "tenth-edition"); put("10E", "tenth-edition");
@ -146,12 +345,11 @@ public enum MagicCardsImageSource implements CardImageSource {
return "magiccards.info"; return "magiccards.info";
} }
@Override @Override
public String getNextHttpImageUrl() { public String getNextHttpImageUrl() {
return null; return null;
} }
@Override @Override
public String getFileForHttpImage(String httpImageUrl) { public String getFileForHttpImage(String httpImageUrl) {
return null; return null;
@ -210,18 +408,26 @@ public enum MagicCardsImageSource implements CardImageSource {
public float getAverageSize() { public float getAverageSize() {
return 70.0f; return 70.0f;
} }
@Override @Override
public int getTotalImages() { public int getTotalImages() {
return -1; return -1;
} }
@Override @Override
public boolean isTokenSource() { public boolean isTokenSource() {
return true; return true;
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
} }

View file

@ -24,20 +24,221 @@
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.mage.plugins.card.images.CardDownloadData;
/** /**
* *
* @author Pete Rossi * @author Pete Rossi
*/ */
public enum MagidexImageSource implements CardImageSource {
instance;
public enum MagidexImageSource implements CardImageSource { private final Set<String> supportedSets;
instance;
MagidexImageSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); // Prerelease Events
// supportedSets.add("FNMP");
supportedSets.add("JR");
supportedSets.add("LEA");
supportedSets.add("LEB");
supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
supportedSets.add("5ED");
supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
supportedSets.add("P02");
supportedSets.add("UGL");
supportedSets.add("USG");
supportedSets.add("DD3DVD");
supportedSets.add("DD3EVG");
supportedSets.add("DD3GVL");
supportedSets.add("DD3JVC");
supportedSets.add("ULG");
supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
supportedSets.add("ME2");
supportedSets.add("ALA");
supportedSets.add("DD2");
supportedSets.add("CON");
supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
supportedSets.add("V09");
supportedSets.add("HOP");
supportedSets.add("ME3");
supportedSets.add("ZEN");
supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
supportedSets.add("V10");
supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
supportedSets.add("PD2");
supportedSets.add("ME4");
supportedSets.add("MBS");
supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
supportedSets.add("DDI");
supportedSets.add("AVR");
supportedSets.add("PC2");
supportedSets.add("M13");
supportedSets.add("V12");
supportedSets.add("DDJ");
supportedSets.add("RTR");
supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
supportedSets.add("DDK");
supportedSets.add("DGM");
supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
supportedSets.add("CNS");
supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
supportedSets.add("DDO");
supportedSets.add("DTK");
supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
supportedSets.add("V15");
supportedSets.add("DDP");
supportedSets.add("BFZ");
supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
supportedSets.add("DDQ");
supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
supportedSets.add("V16");
supportedSets.add("CN2");
supportedSets.add("DDR");
supportedSets.add("KLD");
supportedSets.add("MPS");
// supportedSets.add("PZ2"); // Treasure Chests
supportedSets.add("C16");
supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
supportedSets.add("DDS");
supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
// supportedSets.add("XLN");
// supportedSets.add("DDT");
// supportedSets.add("IMA");
// supportedSets.add("E02");
// supportedSets.add("V17");
// supportedSets.add("UST");
// supportedSets.add("RIX");
// supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");
}
@Override @Override
public String getSourceName() { public String getSourceName() {
@ -68,10 +269,23 @@ public enum MagidexImageSource implements CardImageSource {
} }
// This will properly escape the url // This will properly escape the url
URI uri = new URI("http", "magidex.com", "/extstatic/card/" + cardSet + '/' + cardDownloadName + ".jpg", null, null); URI uri = new URI("http", "magidex.com", "/extstatic/card/" + formatSetName(cardSet) + '/' + cardDownloadName + ".jpg", null, null);
return uri.toASCIIString(); return uri.toASCIIString();
} }
private String formatSetName(String setName) {
if (setNameReplacement.containsKey(setName)) {
setName = setNameReplacement.get(setName);
}
return setName;
}
private static final Map<String, String> setNameReplacement = new HashMap<String, String>() {
{
put("JR", "pJGP");
}
};
@Override @Override
public String generateTokenUrl(CardDownloadData card) { public String generateTokenUrl(CardDownloadData card) {
return null; return null;
@ -95,4 +309,11 @@ public enum MagidexImageSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -27,11 +27,10 @@
*/ */
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
/** /**
* *
@ -368,13 +367,19 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
} }
return -1; return -1;
} }
@Override @Override
public boolean isTokenSource() { public boolean isTokenSource() {
return true; return true;
} }
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public boolean isImageProvided(String setCode, String cardName) {
return true;
}
} }

View file

@ -35,9 +35,11 @@ import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
@ -57,11 +59,12 @@ import org.mage.plugins.card.images.CardDownloadData;
public enum MythicspoilerComSource implements CardImageSource { public enum MythicspoilerComSource implements CardImageSource {
instance; instance;
private Map<String, String> setsAliases; private final Map<String, String> setsAliases;
private Map<String, String> cardNameAliases; private final Map<String, String> cardNameAliases;
private Map<String, Set<String>> cardNameAliasesStart; private final Map<String, Set<String>> cardNameAliasesStart;
private final Map<String, Map<String, String>> sets; private final Map<String, Map<String, String>> sets;
private final Set<String> supportedSets;
private final Map<String, Map<String, String>> manualLinks;
@Override @Override
public String getSourceName() { public String getSourceName() {
@ -69,9 +72,192 @@ public enum MythicspoilerComSource implements CardImageSource {
} }
MythicspoilerComSource() { MythicspoilerComSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("LEA");
supportedSets.add("LEB");
// supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
// supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
// supportedSets.add("5ED");
// supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
// supportedSets.add("P02");
// supportedSets.add("UGL");
supportedSets.add("USG");
// supportedSets.add("DD3DVD");
// supportedSets.add("DD3EVG");
// supportedSets.add("DD3GVL");
// supportedSets.add("DD3JVC");
// supportedSets.add("ULG");
// supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
// supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
// supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
// supportedSets.add("ME2");
supportedSets.add("ALA");
// supportedSets.add("DD2");
supportedSets.add("CON");
// supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
// supportedSets.add("V09");
supportedSets.add("HOP");
// supportedSets.add("ME3");
supportedSets.add("ZEN");
// supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
// supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
// supportedSets.add("V10");
// supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
// supportedSets.add("PD2");
// supportedSets.add("ME4");
supportedSets.add("MBS");
// supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
// supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
// supportedSets.add("DDI");
supportedSets.add("AVR");
// supportedSets.add("PC2");
supportedSets.add("M13");
// supportedSets.add("V12");
// supportedSets.add("DDJ");
supportedSets.add("RTR");
// supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
// supportedSets.add("DDK");
supportedSets.add("DGM");
// supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
// supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
// supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
// supportedSets.add("CNS");
// supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
// supportedSets.add("DDO");
// supportedSets.add("DTK");
// supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
// supportedSets.add("V15");
// supportedSets.add("DDP");
supportedSets.add("BFZ");
// supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
// supportedSets.add("DDQ");
// supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
// supportedSets.add("V16");
supportedSets.add("CN2");
// supportedSets.add("DDR");
supportedSets.add("KLD");
// supportedSets.add("MPS");
// supportedSets.add("PZ2");
supportedSets.add("C16");
// supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
// supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
supportedSets.add("IMA");
supportedSets.add("XLN");
sets = new LinkedHashMap<>(); sets = new LinkedHashMap<>();
setsAliases = new HashMap<>(); setsAliases = new HashMap<>();
setsAliases.put("exp", "bfz"); setsAliases.put("exp", "bfz");
setsAliases.put("xln", "ixa");
cardNameAliases = new HashMap<>(); cardNameAliases = new HashMap<>();
// set+wrong name from web side => correct card name // set+wrong name from web side => correct card name
cardNameAliases.put("MM2-otherwordlyjourney", "otherworldlyjourney"); cardNameAliases.put("MM2-otherwordlyjourney", "otherworldlyjourney");
@ -88,6 +274,25 @@ public enum MythicspoilerComSource implements CardImageSource {
cardNameAliases.put("AKH-reducerumble", "reducerubble"); cardNameAliases.put("AKH-reducerumble", "reducerubble");
cardNameAliases.put("AKH-forsakethewordly", "forsaketheworldly"); cardNameAliases.put("AKH-forsakethewordly", "forsaketheworldly");
cardNameAliases.put("AKH-kefnatsmonument", "kefnetsmonument"); cardNameAliases.put("AKH-kefnatsmonument", "kefnetsmonument");
cardNameAliases.put("XLN-kinjaliscaller", "kinjalliscaller");
cardNameAliases.put("XLN-lookoutsdecision", "lookoutsdispersal");
cardNameAliases.put("XLN-infuriatedgladiodon", "ragingswordtooth");
cardNameAliases.put("XLN-redoubledvolley", "repeatingbarrage");
// <card name, card link>
manualLinks = new HashMap<>();
HashMap<String, String> links = new HashMap<>();
links.put("templeofaclazotz", "templeofaclazotz");
links.put("conquerorsfoothold", "conquerorsfoothold");
links.put("primalwellspring", "primalwellspring");
links.put("azcantathesunkenruin", "azcantathesunkenruin");
links.put("spiresoforazca", "spiresoforazca");
links.put("treasurecove", "treasurecove");
links.put("itlimoccradleofthesun", "itlimoccradleofthesun");
links.put("lostvale", "lostvale");
links.put("adantothefirstfort", "adantothefirstport");
links.put("spitfirebastion", "spitfirebastion");
manualLinks.put("XLN", links);
cardNameAliasesStart = new HashMap<>(); cardNameAliasesStart = new HashMap<>();
HashSet<String> names = new HashSet<>(); HashSet<String> names = new HashSet<>();
@ -184,7 +389,12 @@ public enum MythicspoilerComSource implements CardImageSource {
pageLinks.put(cardName, baseUrl + cardLink); pageLinks.put(cardName, baseUrl + cardLink);
} }
} }
Map<String, String> linksToAdd = manualLinks.get(cardSet);
if (linksToAdd != null) {
for (Map.Entry<String, String> link : linksToAdd.entrySet()) {
pageLinks.put(link.getKey(), baseUrl + "cards/" + link.getValue() + ".jpg");
}
}
return pageLinks; return pageLinks;
} }
@ -243,4 +453,12 @@ public enum MythicspoilerComSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -1,21 +1,223 @@
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.mage.plugins.card.images.CardDownloadData; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.mage.plugins.card.images.CardDownloadData;
/** /**
* @author Quercitron * @author Quercitron
* *
*/ */
public enum ScryfallImageSource implements CardImageSource { public enum ScryfallImageSource implements CardImageSource {
instance; instance;
private final Set<String> supportedSets;
ScryfallImageSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); //
supportedSets.add("LEA");
supportedSets.add("LEB");
supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
supportedSets.add("5ED");
supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
supportedSets.add("P02");
supportedSets.add("UGL");
supportedSets.add("USG");
supportedSets.add("DD3DVD");
supportedSets.add("DD3EVG");
supportedSets.add("DD3GVL");
supportedSets.add("DD3JVC");
supportedSets.add("ULG");
supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
supportedSets.add("ME2");
supportedSets.add("ALA");
supportedSets.add("DD2");
supportedSets.add("CON");
supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
supportedSets.add("V09");
supportedSets.add("HOP");
supportedSets.add("ME3");
supportedSets.add("ZEN");
supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
supportedSets.add("V10");
supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
supportedSets.add("PD2");
supportedSets.add("ME4");
supportedSets.add("MBS");
supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
supportedSets.add("DDI");
supportedSets.add("AVR");
supportedSets.add("PC2");
supportedSets.add("M13");
supportedSets.add("V12");
supportedSets.add("DDJ");
supportedSets.add("RTR");
supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
supportedSets.add("DDK");
supportedSets.add("DGM");
supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
supportedSets.add("CNS");
supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
supportedSets.add("DDO");
supportedSets.add("DTK");
supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
supportedSets.add("V15");
supportedSets.add("DDP");
supportedSets.add("BFZ");
supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
supportedSets.add("DDQ");
supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
supportedSets.add("V16");
supportedSets.add("CN2");
supportedSets.add("DDR");
supportedSets.add("KLD");
supportedSets.add("MPS");
// supportedSets.add("PZ2");
supportedSets.add("C16");
supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
supportedSets.add("DDS");
supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
supportedSets.add("XLN");
// supportedSets.add("DDT");
supportedSets.add("IMA");
// supportedSets.add("E02");
// supportedSets.add("V17");
// supportedSets.add("UST");
// supportedSets.add("RIX");
// supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");
}
@Override @Override
public String generateURL(CardDownloadData card) throws Exception { public String generateURL(CardDownloadData card) throws Exception {
return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/" + card.getCollectorId() + "?format=image"; return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ card.getCollectorId()
+ (card.isSecondSide() ? "b" : "")
+ "?format=image";
} }
@Override @Override
@ -40,7 +242,7 @@ public enum ScryfallImageSource implements CardImageSource {
@Override @Override
public float getAverageSize() { public float getAverageSize() {
return 240; return 90;
} }
@Override @Override
@ -75,4 +277,11 @@ public enum ScryfallImageSource implements CardImageSource {
put("MBP", "pmei"); put("MBP", "pmei");
} }
}; };
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -27,9 +27,6 @@
*/ */
package org.mage.plugins.card.dl.sources; package org.mage.plugins.card.dl.sources;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -37,8 +34,14 @@ import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.images.DownloadPictures;
/** /**
* *
@ -46,11 +49,12 @@ import java.util.Map;
*/ */
public enum TokensMtgImageSource implements CardImageSource { public enum TokensMtgImageSource implements CardImageSource {
instance; instance;
private static final Logger logger = Logger.getLogger(TokensMtgImageSource.class); private static final Logger logger = Logger.getLogger(TokensMtgImageSource.class);
// [[EXP/Name, TokenData>
private List<TokenData> tokensData; private HashMap<String, ArrayList<TokenData>> tokensData;
private static final Set<String> supportedSets = new LinkedHashSet<String>();
private final Object tokensDataSync = new Object(); private final Object tokensDataSync = new Object();
@ -100,7 +104,12 @@ public enum TokensMtgImageSource implements CardImageSource {
"Sorin", "Sorin",
"Tamiyo", "Tamiyo",
"Teferi", "Teferi",
"Venser",}; "Venser",
// Custom Emblems
"Yoda",
"Obi-Wan Kenobi",
"Aurra Sing"
};
private static final Map<String, String> SET_NAMES_REPLACEMENT = new HashMap<String, String>() { private static final Map<String, String> SET_NAMES_REPLACEMENT = new HashMap<String, String>() {
{ {
@ -134,36 +143,31 @@ public enum TokensMtgImageSource implements CardImageSource {
// e.g. http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010 // e.g. http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010
// We don't know these numbers, but we can take them from a file // We don't know these numbers, but we can take them from a file
// with tokens information that can be downloaded from the site. // with tokens information that can be downloaded from the site.
List<TokenData> newTokensData = getTokensData(); if (tokensData.isEmpty()) {
logger.info("Source " + getSourceName() + " provides no token data.");
if (newTokensData.isEmpty()) {
return null; return null;
} }
List<TokenData> matchedTokens = new ArrayList<>(); String key = set + "/" + name;
for (TokenData token : newTokensData) { List<TokenData> list = tokensData.get(key);
if (name.equalsIgnoreCase(token.getName()) && set.equalsIgnoreCase(token.getExpansionSetCode())) { if (list == null) {
matchedTokens.add(token); logger.info("Could not find data for token " + name + ", set " + set + ".");
} return null;
} }
//
// if (matchedTokens.isEmpty()) {
// logger.info("Could not find data for token " + name + ", set " + set + ".");
// return null;
// }
TokenData tokenData; TokenData tokenData;
if (type == 0) { if (type == 0) {
if (matchedTokens.size() > 1) { if (list.size() > 1) {
logger.info("Multiple images were found for token " + name + ", set " + set + '.'); logger.info("Multiple images were found for token " + name + ", set " + set + '.');
} }
tokenData = matchedTokens.get(0); logger.info("Token found: " + name + ", set " + set + '.');
tokenData = list.get(0);
} else { } else {
if (type > matchedTokens.size()) { if (type > list.size()) {
logger.warn("Not enough images for token with type " + type + ", name " + name + ", set " + set + '.'); logger.warn("Not enough images for token with type " + type + ", name " + name + ", set " + set + '.');
return null; return null;
} }
tokenData = matchedTokens.get(card.getType() - 1); tokenData = list.get(card.getType() - 1);
} }
String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_' String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
@ -172,15 +176,26 @@ public enum TokensMtgImageSource implements CardImageSource {
return url; return url;
} }
private List<TokenData> getTokensData() throws IOException { private HashMap<String, ArrayList<TokenData>> getTokensData() throws IOException {
synchronized (tokensDataSync) { synchronized (tokensDataSync) {
if (tokensData == null) { if (tokensData == null) {
tokensData = new ArrayList<>(); DownloadPictures.getInstance().updateAndViewMessage("Creating token data...");
tokensData = new HashMap<>();
// get tokens data from resource file // get tokens data from resource file
try(InputStream inputStream = this.getClass().getResourceAsStream("/tokens-mtg-onl-list.csv")) { try (InputStream inputStream = this.getClass().getResourceAsStream("/tokens-mtg-onl-list.csv")) {
List<TokenData> fileTokensData = parseTokensData(inputStream); List<TokenData> fileTokensData = parseTokensData(inputStream);
tokensData.addAll(fileTokensData); for (TokenData tokenData : fileTokensData) {
String key = tokenData.getExpansionSetCode() + "/" + tokenData.getName();
ArrayList<TokenData> list = tokensData.get(key);
if (list == null) {
list = new ArrayList<>();
tokensData.put(key, list);
supportedSets.add(tokenData.getExpansionSetCode());
logger.info("Added key: " + key);
}
list.add(tokenData);
}
} catch (Exception exception) { } catch (Exception exception) {
logger.warn("Failed to get tokens description from resource file tokens-mtg-onl-list.csv", exception); logger.warn("Failed to get tokens description from resource file tokens-mtg-onl-list.csv", exception);
} }
@ -188,27 +203,33 @@ public enum TokensMtgImageSource implements CardImageSource {
// description on site may contain new information // description on site may contain new information
// try to add it // try to add it
URL url = new URL("http://tokens.mtg.onl/data/SetsWithTokens.csv"); URL url = new URL("http://tokens.mtg.onl/data/SetsWithTokens.csv");
try(InputStream inputStream = url.openStream()) { try (InputStream inputStream = url.openStream()) {
List<TokenData> siteTokensData = parseTokensData(inputStream); List<TokenData> siteTokensData = parseTokensData(inputStream);
List<TokenData> newTokensData = new ArrayList<>();
for (TokenData siteData : siteTokensData) { for (TokenData siteData : siteTokensData) {
boolean isNew = true; String key = siteData.getExpansionSetCode() + "/" + siteData.getName();
for (TokenData fileData : tokensData) { supportedSets.add(siteData.getExpansionSetCode());
if (siteData.getName().equalsIgnoreCase(fileData.getName()) ArrayList<TokenData> list = tokensData.get(key);
&& siteData.getNumber().equalsIgnoreCase(fileData.getNumber()) if (list == null) {
&& siteData.getExpansionSetCode().equalsIgnoreCase(fileData.getExpansionSetCode())) { list = new ArrayList<>();
isNew = false; tokensData.put(key, list);
break; list.add(siteData);
} else {
boolean newToken = true;
for (TokenData tokenData : list) {
if (siteData.getNumber().equals(tokenData.number)) {
newToken = false;
break;
}
}
if (newToken) {
list.add(siteData);
} }
} }
if (isNew) {
newTokensData.add(siteData);
}
} }
DownloadPictures.getInstance().updateAndViewMessage("");
tokensData.addAll(newTokensData); } catch (Exception ex) {
} catch (Exception exception) { logger.warn("Failed to get tokens description from tokens.mtg.onl", ex);
logger.warn("Failed to get tokens description from tokens.mtg.onl", exception); DownloadPictures.getInstance().updateAndViewMessage(ex.getMessage());
} }
} }
} }
@ -219,8 +240,8 @@ public enum TokensMtgImageSource implements CardImageSource {
private List<TokenData> parseTokensData(InputStream inputStream) throws IOException { private List<TokenData> parseTokensData(InputStream inputStream) throws IOException {
List<TokenData> newTokensData = new ArrayList<>(); List<TokenData> newTokensData = new ArrayList<>();
try(InputStreamReader inputReader = new InputStreamReader(inputStream, "Cp1252"); try (InputStreamReader inputReader = new InputStreamReader(inputStream, "Cp1252");
BufferedReader reader = new BufferedReader(inputReader)) { BufferedReader reader = new BufferedReader(inputReader)) {
// we have to specify encoding to read special comma // we have to specify encoding to read special comma
reader.readLine(); // skip header reader.readLine(); // skip header
@ -285,7 +306,17 @@ public enum TokensMtgImageSource implements CardImageSource {
@Override @Override
public int getTotalImages() { public int getTotalImages() {
return -1; return getTokenImages();
}
@Override
public int getTokenImages() {
try {
getTokensData();
} catch (IOException ex) {
logger.error(getSourceName() + ": Loading available data failed. " + ex.getMessage());
}
return tokensData.size();
} }
@Override @Override
@ -296,4 +327,28 @@ public enum TokensMtgImageSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
@Override
public boolean isImageProvided(String setCode, String cardName) {
try {
getTokensData();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(TokensMtgImageSource.class.getName()).log(Level.SEVERE, null, ex);
}
String key = setCode + "/" + cardName;
return (tokensData.containsKey(key));
}
@Override
public boolean isSetSupportedComplete(String setCode) {
return false;
}
} }

View file

@ -34,10 +34,13 @@ import java.net.HttpURLConnection;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -46,6 +49,7 @@ import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection; import mage.remote.Connection;
import mage.remote.Connection.ProxyType; import mage.remote.Connection.ProxyType;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
@ -58,10 +62,13 @@ import org.mage.plugins.card.images.CardDownloadData;
public enum WizardCardsImageSource implements CardImageSource { public enum WizardCardsImageSource implements CardImageSource {
instance; instance;
private Map<String, String> setsAliases;
private Map<String, String> languageAliases;
private final Map<String, Map<String, String>> sets;
private static final Logger logger = Logger.getLogger(WizardCardsImageSource.class);
private final Map<String, String> setsAliases;
private final Map<String, String> languageAliases;
private final Map<String, Map<String, String>> sets;
private final Set<String> supportedSets;
@Override @Override
public String getSourceName() { public String getSourceName() {
@ -69,6 +76,198 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
WizardCardsImageSource() { WizardCardsImageSource() {
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); // Prerelease Events
supportedSets.add("LEA");
supportedSets.add("LEB");
supportedSets.add("2ED");
supportedSets.add("ARN");
supportedSets.add("ATQ");
supportedSets.add("3ED");
supportedSets.add("LEG");
supportedSets.add("DRK");
supportedSets.add("FEM");
supportedSets.add("4ED");
supportedSets.add("ICE");
supportedSets.add("CHR");
supportedSets.add("HML");
supportedSets.add("ALL");
supportedSets.add("MIR");
supportedSets.add("VIS");
supportedSets.add("5ED");
supportedSets.add("POR");
supportedSets.add("WTH");
supportedSets.add("TMP");
supportedSets.add("STH");
supportedSets.add("EXO");
supportedSets.add("P02");
supportedSets.add("UGL");
supportedSets.add("USG");
supportedSets.add("DD3DVD");
supportedSets.add("DD3EVG");
supportedSets.add("DD3GVL");
supportedSets.add("DD3JVC");
supportedSets.add("ULG");
supportedSets.add("6ED");
supportedSets.add("UDS");
supportedSets.add("PTK");
supportedSets.add("S99");
supportedSets.add("MMQ");
// supportedSets.add("BRB");Battle Royale Box Set
supportedSets.add("NEM");
supportedSets.add("S00");
supportedSets.add("PCY");
supportedSets.add("INV");
// supportedSets.add("BTD"); // Beatdown Boxset
supportedSets.add("PLS");
supportedSets.add("7ED");
supportedSets.add("APC");
supportedSets.add("ODY");
// supportedSets.add("DKM"); // Deckmasters 2001
supportedSets.add("TOR");
supportedSets.add("JUD");
supportedSets.add("ONS");
supportedSets.add("LGN");
supportedSets.add("SCG");
supportedSets.add("8ED");
supportedSets.add("MRD");
supportedSets.add("DST");
supportedSets.add("5DN");
supportedSets.add("CHK");
supportedSets.add("UNH");
supportedSets.add("BOK");
supportedSets.add("SOK");
supportedSets.add("9ED");
supportedSets.add("RAV");
supportedSets.add("GPT");
supportedSets.add("DIS");
supportedSets.add("CSP");
supportedSets.add("TSP");
supportedSets.add("TSB");
supportedSets.add("PLC");
supportedSets.add("FUT");
supportedSets.add("10E");
supportedSets.add("MED");
supportedSets.add("LRW");
supportedSets.add("EVG");
supportedSets.add("MOR");
supportedSets.add("SHM");
supportedSets.add("EVE");
supportedSets.add("DRB");
supportedSets.add("ME2");
supportedSets.add("ALA");
supportedSets.add("DD2");
supportedSets.add("CON");
supportedSets.add("DDC");
supportedSets.add("ARB");
supportedSets.add("M10");
// supportedSets.add("TD0"); // Magic Online Deck Series
supportedSets.add("V09");
supportedSets.add("HOP");
supportedSets.add("ME3");
supportedSets.add("ZEN");
supportedSets.add("DDD");
supportedSets.add("H09");
supportedSets.add("WWK");
supportedSets.add("DDE");
supportedSets.add("ROE");
supportedSets.add("DPA");
supportedSets.add("ARC");
supportedSets.add("M11");
supportedSets.add("V10");
supportedSets.add("DDF");
supportedSets.add("SOM");
// supportedSets.add("TD0"); // Commander Theme Decks
supportedSets.add("PD2");
supportedSets.add("ME4");
supportedSets.add("MBS");
supportedSets.add("DDG");
supportedSets.add("NPH");
supportedSets.add("CMD");
supportedSets.add("M12");
supportedSets.add("V11");
supportedSets.add("DDH");
supportedSets.add("ISD");
supportedSets.add("PD3");
supportedSets.add("DKA");
supportedSets.add("DDI");
supportedSets.add("AVR");
supportedSets.add("PC2");
supportedSets.add("M13");
supportedSets.add("V12");
supportedSets.add("DDJ");
supportedSets.add("RTR");
supportedSets.add("CM1");
// supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
supportedSets.add("GTC");
supportedSets.add("DDK");
supportedSets.add("DGM");
supportedSets.add("MMA");
supportedSets.add("M14");
supportedSets.add("V13");
supportedSets.add("DDL");
supportedSets.add("THS");
supportedSets.add("C13");
supportedSets.add("BNG");
supportedSets.add("DDM");
supportedSets.add("JOU");
// supportedSets.add("MD1"); // Modern Event Deck
supportedSets.add("CNS");
supportedSets.add("VMA");
supportedSets.add("M15");
supportedSets.add("V14");
supportedSets.add("DDN");
supportedSets.add("KTK");
supportedSets.add("C14");
// supportedSets.add("DD3"); // Duel Decks Anthology
supportedSets.add("FRF");
supportedSets.add("DDO");
supportedSets.add("DTK");
supportedSets.add("TPR");
supportedSets.add("MM2");
supportedSets.add("ORI");
supportedSets.add("V15");
supportedSets.add("DDP");
supportedSets.add("BFZ");
supportedSets.add("EXP");
supportedSets.add("C15");
// supportedSets.add("PZ1"); // Legendary Cube
supportedSets.add("OGW");
supportedSets.add("DDQ");
supportedSets.add("W16");
supportedSets.add("SOI");
supportedSets.add("EMA");
supportedSets.add("EMN");
supportedSets.add("V16");
supportedSets.add("CN2");
supportedSets.add("DDR");
supportedSets.add("KLD");
supportedSets.add("MPS");
// supportedSets.add("PZ2"); // Treasure Chests
supportedSets.add("C16");
supportedSets.add("PCA");
supportedSets.add("AER");
supportedSets.add("MM3");
supportedSets.add("DDS");
supportedSets.add("W17");
supportedSets.add("AKH");
supportedSets.add("MPS");
supportedSets.add("CMA");
supportedSets.add("E01");
supportedSets.add("HOU");
supportedSets.add("C17");
supportedSets.add("XLN");
// supportedSets.add("DDT"); // Duel Decks: Merfolk vs. Goblins
// supportedSets.add("IMA"); // Iconic Msters
// supportedSets.add("E02"); // Explorers of Ixalan
// supportedSets.add("V17"); // From the Vault: Transform
// supportedSets.add("UST"); // Unstable
// supportedSets.add("RIX"); // Rivals of Ixalan
// supportedSets.add("A25"); // Masters 25
// supportedSets.add("DOM"); // Dominaria
// supportedSets.add("M19"); // Core 2019
sets = new HashMap<>(); sets = new HashMap<>();
setsAliases = new HashMap<>(); setsAliases = new HashMap<>();
setsAliases.put("2ED", "Unlimited Edition"); setsAliases.put("2ED", "Unlimited Edition");
@ -102,6 +301,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("C14", "Commander 2014"); setsAliases.put("C14", "Commander 2014");
setsAliases.put("C15", "Commander 2015"); setsAliases.put("C15", "Commander 2015");
setsAliases.put("C16", "Commander 2016"); setsAliases.put("C16", "Commander 2016");
setsAliases.put("C17", "Commander 2017");
setsAliases.put("CMA", "Commander Anthology"); setsAliases.put("CMA", "Commander Anthology");
setsAliases.put("CHK", "Champions of Kamigawa"); setsAliases.put("CHK", "Champions of Kamigawa");
setsAliases.put("CHR", "Chronicles"); setsAliases.put("CHR", "Chronicles");
@ -140,6 +340,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("DRK", "The Dark"); setsAliases.put("DRK", "The Dark");
setsAliases.put("DST", "Darksteel"); setsAliases.put("DST", "Darksteel");
setsAliases.put("DTK", "Dragons of Tarkir"); setsAliases.put("DTK", "Dragons of Tarkir");
setsAliases.put("E01", "Archenemy: Nicol Bolas");
setsAliases.put("EMN", "Eldritch Moon"); setsAliases.put("EMN", "Eldritch Moon");
setsAliases.put("EMA", "Eternal Masters"); setsAliases.put("EMA", "Eternal Masters");
setsAliases.put("EVE", "Eventide"); setsAliases.put("EVE", "Eventide");
@ -272,18 +473,20 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
private Map<String, String> getSetLinks(String cardSet) { private Map<String, String> getSetLinks(String cardSet) {
ConcurrentHashMap<String, String> setLinks = new ConcurrentHashMap<>(); LinkedHashMap<String, String> setLinks = new LinkedHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(10); ExecutorService executor = Executors.newFixedThreadPool(10);
try { try {
String setNames = setsAliases.get(cardSet); String setNames = setsAliases.get(cardSet);
String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en"); String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
for (String setName : setNames.split("\\^")) { for (String setName : setNames.split("\\^")) {
String URLSetName = URLEncoder.encode(setName, "UTF-8"); // String URLSetName = URLEncoder.encode(setName, "UTF-8");
String URLSetName = setName.replaceAll(" ", "%20");
int page = 0; int page = 0;
int firstMultiverseIdLastPage = 0; int firstMultiverseIdLastPage = 0;
Pages: Pages:
while (page < 999) { while (page < 999) {
String searchUrl = "http://gatherer.wizards.com/Pages/Search/Default.aspx?page=" + page + "&output=spoiler&method=visual&action=advanced&set=+[%22" + URLSetName + "%22]"; String searchUrl = "http://gatherer.wizards.com/Pages/Search/Default.aspx?sort=cn+&page=" + page + "&action=advanced&output=spoiler&method=visual&set=+%5B%22" + URLSetName + "%22%5D";
logger.debug("URL: " + searchUrl);
Document doc = getDocument(searchUrl); Document doc = getDocument(searchUrl);
Elements cardsImages = doc.select("img[src^=../../Handlers/]"); Elements cardsImages = doc.select("img[src^=../../Handlers/]");
if (cardsImages.isEmpty()) { if (cardsImages.isEmpty()) {
@ -307,7 +510,7 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
System.out.println("Exception when parsing the wizards page: " + ex.getMessage()); logger.error("Exception when parsing the wizards page: " + ex.getMessage());
} }
executor.shutdown(); executor.shutdown();
@ -327,14 +530,15 @@ public enum WizardCardsImageSource implements CardImageSource {
Connection.ProxyType proxyType = Connection.ProxyType.valueByText(prefs.get("proxyType", "None")); Connection.ProxyType proxyType = Connection.ProxyType.valueByText(prefs.get("proxyType", "None"));
Document doc; Document doc;
if (proxyType == ProxyType.NONE) { if (proxyType == ProxyType.NONE) {
doc = Jsoup.connect(urlString).get(); doc = Jsoup.connect(urlString).timeout(60 * 1000).get();
} else { } else {
String proxyServer = prefs.get("proxyAddress", ""); String proxyServer = prefs.get("proxyAddress", "");
int proxyPort = Integer.parseInt(prefs.get("proxyPort", "0")); int proxyPort = Integer.parseInt(prefs.get("proxyPort", "0"));
URL url = new URL(urlString); URL url = new URL(urlString);
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort)); Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort));
HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy); HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy);
uc.setConnectTimeout(10000);
uc.setReadTimeout(60000);
uc.connect(); uc.connect();
String line; String line;
@ -356,7 +560,7 @@ public enum WizardCardsImageSource implements CardImageSource {
if (!variations.isEmpty()) { if (!variations.isEmpty()) {
int landNumber = 1; int landNumber = 1;
for (Element variation : variations) { for (Element variation : variations) {
Integer landMultiverseId = Integer.parseInt(variation.attr("onclick").replaceAll("[^\\d]", "")); Integer landMultiverseId = Integer.parseInt(variation.attr("href").replaceAll("[^\\d]", ""));
links.put((cardName + landNumber).toLowerCase(), generateLink(landMultiverseId)); links.put((cardName + landNumber).toLowerCase(), generateLink(landMultiverseId));
landNumber++; landNumber++;
} }
@ -435,6 +639,9 @@ public enum WizardCardsImageSource implements CardImageSource {
String setNames = setsAliases.get(cardSet); String setNames = setsAliases.get(cardSet);
if (setNames != null) { if (setNames != null) {
Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet)); Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet));
if (setLinks == null || setLinks.isEmpty()) {
return null;
}
String link = setLinks.get(card.getDownloadName().toLowerCase()); String link = setLinks.get(card.getDownloadName().toLowerCase());
if (link == null) { if (link == null) {
int length = collectorId.length(); int length = collectorId.length();
@ -444,11 +651,11 @@ public enum WizardCardsImageSource implements CardImageSource {
} }
int number = Integer.parseInt(collectorId.substring(0, length)); int number = Integer.parseInt(collectorId.substring(0, length));
List<String> l = new ArrayList<>(setLinks.values());
if (setLinks.size() >= number) { if (l.size() >= number) {
link = setLinks.get(Integer.toString(number - 1)); link = l.get(number - 1);
} else { } else {;
link = setLinks.get(Integer.toString(number - 21)); link = l.get(number - 21);
if (link != null) { if (link != null) {
link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a')); link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a'));
} }
@ -460,6 +667,7 @@ public enum WizardCardsImageSource implements CardImageSource {
return link; return link;
} }
return null; return null;
} }
@Override @Override
@ -474,16 +682,24 @@ public enum WizardCardsImageSource implements CardImageSource {
private final class GetImageLinkTask implements Runnable { private final class GetImageLinkTask implements Runnable {
private final int multiverseId; private int multiverseId;
private final String cardName; private String cardName;
private final String preferedLanguage; private String preferedLanguage;
private final ConcurrentHashMap setLinks; private LinkedHashMap setLinks;
public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, ConcurrentHashMap setLinks) { public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, LinkedHashMap setLinks) {
this.multiverseId = multiverseId; try {
this.cardName = cardName; this.multiverseId = multiverseId;
this.preferedLanguage = preferedLanguage; this.cardName = cardName;
this.setLinks = setLinks; this.preferedLanguage = preferedLanguage;
this.setLinks = setLinks;
} catch (Exception ex) {
logger.error(ex.getMessage());
logger.error("multiverseId: " + multiverseId);
logger.error("cardName: " + cardName);
logger.error("preferedLanguage: " + preferedLanguage);
logger.error("setLinks: " + setLinks.toString());
}
} }
@Override @Override
@ -496,7 +712,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId)); setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
} }
} catch (IOException | NumberFormatException ex) { } catch (IOException | NumberFormatException ex) {
System.out.println("Exception when parsing the wizards page: " + ex.getMessage()); logger.error("Exception when parsing the wizards page: " + ex.getMessage());
} }
} }
@ -515,4 +731,12 @@ public enum WizardCardsImageSource implements CardImageSource {
@Override @Override
public void doPause(String httpImageUrl) { public void doPause(String httpImageUrl) {
} }
@Override
public ArrayList<String> getSupportedSets() {
ArrayList<String> supportedSetsCopy = new ArrayList<>();
supportedSetsCopy.addAll(supportedSets);
return supportedSetsCopy;
}
} }

View file

@ -40,7 +40,7 @@ public class CardDownloadData {
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide) { public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, false, false, ""); this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, twoFacedCard, secondSide, "");
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) { public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) {
@ -72,7 +72,7 @@ public class CardDownloadData {
this.usesVariousArt = card.usesVariousArt; this.usesVariousArt = card.usesVariousArt;
this.tokenSetCode = card.tokenSetCode; this.tokenSetCode = card.tokenSetCode;
this.tokenDescriptor = card.tokenDescriptor; this.tokenDescriptor = card.tokenDescriptor;
this.tokenClassName = tokenClassName; this.tokenClassName = card.tokenClassName;
this.fileName = card.fileName; this.fileName = card.fileName;
} }
@ -163,7 +163,7 @@ public class CardDownloadData {
public String getTokenDescriptor() { public String getTokenDescriptor() {
return tokenDescriptor; return tokenDescriptor;
} }
public void setTokenClassName(String tokenClassName) { public void setTokenClassName(String tokenClassName) {
this.tokenClassName = tokenClassName; this.tokenClassName = tokenClassName;
} }

View file

@ -1,6 +1,7 @@
package org.mage.plugins.card.images; package org.mage.plugins.card.images;
import java.awt.*; import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
@ -14,18 +15,25 @@ import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.IIOImage; import javax.imageio.IIOImage;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream; import javax.imageio.stream.FileImageOutputStream;
import javax.swing.*; import javax.swing.*;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo; import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.constants.Constants; import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.client.util.sets.ConstructedFormats; import mage.client.util.sets.ConstructedFormats;
import mage.remote.Connection; import mage.remote.Connection;
import static mage.remote.Connection.ProxyType.HTTP;
import static mage.remote.Connection.ProxyType.NONE;
import static mage.remote.Connection.ProxyType.SOCKS;
import net.java.truevfs.access.TFile; import net.java.truevfs.access.TFile;
import net.java.truevfs.access.TFileOutputStream; import net.java.truevfs.access.TFileOutputStream;
import net.java.truevfs.access.TVFS; import net.java.truevfs.access.TVFS;
@ -37,51 +45,97 @@ import org.mage.plugins.card.utils.CardImageUtils;
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable { public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
private static DownloadPictures instance;
private static final Logger logger = Logger.getLogger(DownloadPictures.class); private static final Logger logger = Logger.getLogger(DownloadPictures.class);
public static final String ALL_IMAGES = "- All images from that source";
public static final String ALL_STANDARD_IMAGES = "- All images from standard from that source";
public static final String ALL_TOKENS = "- Only all token images from that source";
private JDialog dialog;
private final JProgressBar bar; private final JProgressBar bar;
private final JOptionPane dlg; private final JOptionPane dlg;
private boolean cancel; private boolean cancel;
private final JButton closeButton; private final JButton closeButton;
private final JButton startDownloadButton; private final JButton startDownloadButton;
private int cardIndex; private int cardIndex;
private List<CardDownloadData> cards; private List<CardDownloadData> allCardsMissingImage;
private List<CardDownloadData> type2cards; private List<CardDownloadData> cardsToDownload;
private final JComboBox jComboBox1;
private final JLabel jLabel1; private int missingCards = 0;
private static boolean offlineMode = false; private int missingTokens = 0;
private JCheckBox checkBox;
List<String> selectedSetCodes = new ArrayList<>();
private final JComboBox jComboBoxServer;
private final JLabel jLabelMessage;
private final JLabel jLabelAllMissing;
private final JLabel jLabelServer;
private final JComboBox jComboBoxSet;
private final JLabel jLabelSet;
private final Object sync = new Object(); private final Object sync = new Object();
private static CardImageSource cardImageSource; private static CardImageSource cardImageSource;
private Proxy p = Proxy.NO_PROXY; private Proxy p = Proxy.NO_PROXY;
// private ExecutorService executor = Executors.newFixedThreadPool(10); enum DownloadSources {
public static void main(String[] args) { WIZARDS("wizards.com", WizardCardsImageSource.instance),
startDownload(null, null); MYTHICSPOILER("mythicspoiler.com", MythicspoilerComSource.instance),
TOKENS("tokens.mtg.onl", TokensMtgImageSource.instance),
// MTG_ONL("mtg.onl", MtgOnlTokensImageSource.instance), Not working correctly yet
ALTERNATIVE("alternative.mtg.onl", AltMtgOnlTokensImageSource.instance),
GRAB_BAG("GrabBag", GrabbagImageSource.instance),
MAGIDEX("magidex.com", MagidexImageSource.instance),
SCRYFALL("scryfall.com", ScryfallImageSource.instance);
// MAGICCARDS("magiccards.info", MagicCardsImageSource.instance)
private final String text;
private final CardImageSource source;
DownloadSources(String text, CardImageSource sourceInstance) {
this.text = text;
this.source = sourceInstance;
}
public CardImageSource getSource() {
return source;
}
@Override
public String toString() {
return text;
}
} }
public static void startDownload(JFrame frame, List<CardInfo> allCards) { public static DownloadPictures getInstance() {
List<CardDownloadData> cards = getNeededCards(allCards); return instance;
}
public static void main(String[] args) {
startDownload();
}
public static void startDownload() {
/* /*
* if (cards == null || cards.isEmpty()) { * if (cards == null || cards.isEmpty()) {
* JOptionPane.showMessageDialog(null, * JOptionPane.showMessageDialog(null,
* "All card pictures have been downloaded."); return; } * "All card pictures have been downloaded."); return; }
*/ */
DownloadPictures download = new DownloadPictures(cards); instance = new DownloadPictures(MageFrame.getInstance());
JDialog dlg = download.getDlg(frame); Thread t1 = new Thread(new LoadMissingCardData(instance));
dlg.setVisible(true); t1.start();
dlg.dispose(); instance.getDlg().setVisible(true);
download.cancel = true; instance.getDlg().dispose();
instance.cancel = true;
} }
public JDialog getDlg(JFrame frame) { public JDialog getDlg() {
String title = "Downloading";
final JDialog dialog = this.dlg.createDialog(frame, title);
closeButton.addActionListener(e -> dialog.setVisible(false));
return dialog; return dialog;
} }
@ -89,75 +143,77 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
this.cancel = cancel; this.cancel = cancel;
} }
public DownloadPictures(List<CardDownloadData> cards) { static int WIDTH = 400;
this.cards = cards;
bar = new JProgressBar(this); public DownloadPictures(JFrame frame) {
cardsToDownload = new ArrayList<>();
JPanel p0 = new JPanel(); JPanel p0 = new JPanel();
p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS)); p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
jLabel1 = new JLabel();
jLabel1.setText("Please select server:");
jLabel1.setAlignmentX(Component.LEFT_ALIGNMENT); jLabelMessage = new JLabel();
jLabelMessage.setAlignmentX(Component.CENTER_ALIGNMENT);
p0.add(jLabel1); jLabelMessage.setText("Initializing image download...");
p0.add(jLabelMessage);
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
ComboBoxModel jComboBox1Model = new DefaultComboBoxModel(new String[]{
// "magiccards.info",
"wizards.com",
"mythicspoiler.com",
"tokens.mtg.onl", //"mtgimage.com (HQ)",
"mtg.onl",
"alternative.mtg.onl",
"GrabBag",
"magidex.com",
"scryfall.com", //"mtgathering.ru HQ",
//"mtgathering.ru MQ",
//"mtgathering.ru LQ",
});
jComboBox1 = new JComboBox();
cardImageSource = MagicCardsImageSource.instance; jLabelAllMissing = new JLabel();
jComboBox1.setModel(jComboBox1Model); jLabelAllMissing.setAlignmentX(Component.LEFT_ALIGNMENT);
jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT); // jLabelAllMissing.setText("Computing number of missing images...");
jComboBox1.addActionListener(e -> { p0.add(jLabelAllMissing);
JComboBox cb = (JComboBox) e.getSource(); p0.add(Box.createVerticalStrut(5));
switch (cb.getSelectedIndex() + 1) {
case 0: jLabelServer = new JLabel();
cardImageSource = MagicCardsImageSource.instance; jLabelServer.setText("Please select image source:");
break; jLabelServer.setAlignmentX(Component.LEFT_ALIGNMENT);
case 1: jLabelServer.setVisible(false);
cardImageSource = WizardCardsImageSource.instance; p0.add(jLabelServer);
break;
case 2: p0.add(Box.createVerticalStrut(5));
cardImageSource = MythicspoilerComSource.instance;
break; jComboBoxServer = new JComboBox();
case 3: jComboBoxServer.setModel(new DefaultComboBoxModel(DownloadSources.values()));
cardImageSource = TokensMtgImageSource.instance; jComboBoxServer.setAlignmentX(Component.LEFT_ALIGNMENT);
break; jComboBoxServer.setAlignmentY(Component.LEFT_ALIGNMENT);
case 4: jComboBoxServer.addItemListener((ItemEvent event) -> {
cardImageSource = MtgOnlTokensImageSource.instance; if (event.getStateChange() == ItemEvent.SELECTED) {
break; comboBoxServerItemSelected(event);
case 5:
cardImageSource = AltMtgOnlTokensImageSource.instance;
break;
case 6:
cardImageSource = GrabbagImageSource.instance;
break;
case 7:
cardImageSource = MagidexImageSource.instance;
break;
case 8:
cardImageSource = ScryfallImageSource.instance;
break;
} }
updateCardsToDownload();
}); });
p0.add(jComboBox1); Dimension d = jComboBoxServer.getPreferredSize();
d.width = WIDTH;
jComboBoxServer.setPreferredSize(d);
p0.add(jComboBoxServer);
jComboBoxServer.setVisible(false);
// set the first source as default
cardImageSource = WizardCardsImageSource.instance;
p0.add(Box.createVerticalStrut(5));
// Set selection ---------------------------------
jLabelSet = new JLabel();
jLabelSet.setText("Please select sets to download the images for:");
jLabelSet.setAlignmentX(Component.LEFT_ALIGNMENT);
jLabelSet.setVisible(false);
p0.add(jLabelSet);
jComboBoxSet = new JComboBox();
// jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
jComboBoxSet.setAlignmentX(Component.LEFT_ALIGNMENT);
jComboBoxSet.addItemListener((ItemEvent event) -> {
if (event.getStateChange() == ItemEvent.SELECTED) {
comboBoxSetItemSelected(event);
}
});
p0.add(jComboBoxSet);
jComboBoxSet.setVisible(false);
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
// Start // Start
@ -165,78 +221,168 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
startDownloadButton.addActionListener(e -> { startDownloadButton.addActionListener(e -> {
new Thread(DownloadPictures.this).start(); new Thread(DownloadPictures.this).start();
startDownloadButton.setEnabled(false); startDownloadButton.setEnabled(false);
checkBox.setEnabled(false);
}); });
p0.add(Box.createVerticalStrut(5)); p0.add(Box.createVerticalStrut(5));
// Progress // Progress
bar = new JProgressBar(this);
p0.add(bar); p0.add(bar);
bar.setStringPainted(true); bar.setStringPainted(true);
int count = cards.size();
float mb = (count * cardImageSource.getAverageSize()) / 1024; d = bar.getPreferredSize();
bar.setString(String.format(cardIndex == cards.size() ? "%d of %d cards finished! Please close!" d.width = WIDTH;
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cards.size(), mb));
Dimension d = bar.getPreferredSize();
d.width = 300;
bar.setPreferredSize(d); bar.setPreferredSize(d);
bar.setVisible(false);
p0.add(Box.createVerticalStrut(5));
checkBox = new JCheckBox("Download images for Standard (Type2) only");
p0.add(checkBox);
p0.add(Box.createVerticalStrut(5));
checkBox.addActionListener(e -> updateCardsToDownload());
// JOptionPane // JOptionPane
Object[] options = {startDownloadButton, closeButton = new JButton("Cancel")}; Object[] options = {startDownloadButton, closeButton = new JButton("Cancel")};
startDownloadButton.setVisible(false);
closeButton.addActionListener(e -> dialog.setVisible(false));
closeButton.setVisible(false);
dlg = new JOptionPane(p0, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]); dlg = new JOptionPane(p0, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]);
dialog = this.dlg.createDialog(frame, "Downloading images");
} }
public static boolean checkForMissingCardImages(List<CardInfo> allCards) { public void setAllMissingCards() {
AtomicBoolean missedCardTFiles = new AtomicBoolean(); updateAndViewMessage("Get all available cards from the repository...");
allCards.parallelStream().forEach(card -> { List<CardInfo> cards = CardRepository.instance.findCards(new CardCriteria());
if (!missedCardTFiles.get()) { updateAndViewMessage("Check which images are missing ...");
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) { this.allCardsMissingImage = getNeededCards(cards);
CardDownloadData url = new CardDownloadData(card.getName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), updateAndViewMessage("Check which images the current source is providing ...");
0, "", "", false, card.isDoubleFaced(), card.isNightCard()); jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
TFile file = new TFile(CardImageUtils.generateImagePath(url));
if (!file.exists()) { updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
missedCardTFiles.set(true);
jComboBoxServer.setVisible(true);
jLabelServer.setVisible(true);
jComboBoxSet.setVisible(true);
jLabelSet.setVisible(true);
bar.setVisible(true);
startDownloadButton.setVisible(true);
closeButton.setVisible(true);
updateAndViewMessage("");
}
private void comboBoxServerItemSelected(ItemEvent evt) {
if (jComboBoxServer.isEnabled()) {
cardImageSource = ((DownloadSources) evt.getItem()).getSource();
// update the available sets / token comboBox
jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
}
}
public void updateAndViewMessage(String text) {
jLabelMessage.setText(text);
if (dialog != null) {
dialog.pack();
dialog.validate();
dialog.repaint();
}
}
private Object[] getSetsForCurrentImageSource() {
// Set the available sets to the combo box
ArrayList<String> supportedSets = cardImageSource.getSupportedSets();
List<String> setNames = new ArrayList<>();
if (supportedSets != null) {
setNames.add(ALL_IMAGES);
setNames.add(ALL_STANDARD_IMAGES);
}
if (cardImageSource.isTokenSource()) {
setNames.add(ALL_TOKENS);
}
if (supportedSets != null) {
for (String setCode : supportedSets) {
ExpansionSet expansionSet = Sets.findSet(setCode);
if (expansionSet != null) {
setNames.add(expansionSet.getName());
} else {
logger.error(cardImageSource.getSourceName() + ": Expansion set for code " + setCode + " not found!");
}
}
}
if (setNames.isEmpty()) {
logger.error("Source " + cardImageSource.getSourceName() + " creates no selectable items.");
setNames.add("not avalable");
}
return setNames.toArray(new String[0]);
}
private void updateCardsToDownload(String itemText) {
selectedSetCodes.clear();
switch (itemText) {
case ALL_IMAGES:
if (cardImageSource.getSupportedSets() == null) {
selectedSetCodes = cardImageSource.getSupportedSets();
} else {
selectedSetCodes.addAll(cardImageSource.getSupportedSets());
}
break;
case ALL_STANDARD_IMAGES:
List<String> standardSets = ConstructedFormats.getSetsByFormat(ConstructedFormats.STANDARD);
for (String setCode : cardImageSource.getSupportedSets()) {
if (standardSets.contains(setCode)) {
selectedSetCodes.add(setCode);
} else {
logger.debug("Set code " + setCode + " from download source " + cardImageSource.getSourceName());
}
}
break;
case ALL_TOKENS:
break;
default:
int nonSetEntries = 0;
if (cardImageSource.getSupportedSets() != null) {
nonSetEntries = 2;
}
if (cardImageSource.isTokenSource()) {
nonSetEntries++;
}
selectedSetCodes.add(cardImageSource.getSupportedSets().get(jComboBoxSet.getSelectedIndex() - nonSetEntries));
}
cardsToDownload.clear();
int numberTokenImagesAvailable = 0;
int numberCardImagesAvailable = 0;
for (CardDownloadData data : allCardsMissingImage) {
if (data.isToken()) {
if (cardImageSource.isTokenSource() && cardImageSource.isImageProvided(data.getSet(), data.getName())) {
numberTokenImagesAvailable++;
cardsToDownload.add(data);
}
} else {
if (selectedSetCodes != null && selectedSetCodes.contains(data.getSet())) {
if (cardImageSource.isSetSupportedComplete(data.getSet()) || cardImageSource.isImageProvided(data.getSet(), data.getName())) {
numberCardImagesAvailable++;
cardsToDownload.add(data);
} }
} }
} }
});
return missedCardTFiles.get();
}
private void updateCardsToDownload() {
List<CardDownloadData> cardsToDownload = cards;
if (type2cardsOnly()) {
selectType2andTokenCardsIfNotYetDone();
cardsToDownload = type2cards;
} }
updateProgressText(cardsToDownload.size()); updateProgressText(numberCardImagesAvailable, numberTokenImagesAvailable);
} }
private boolean type2cardsOnly() { private void comboBoxSetItemSelected(ItemEvent event) {
return checkBox.isSelected(); // Update the cards to download related to the selected set
updateCardsToDownload(event.getItem().toString());
} }
private void selectType2andTokenCardsIfNotYetDone() { private void updateProgressText(int cardCount, int tokenCount) {
if (type2cards == null) { missingTokens = 0;
type2cards = new ArrayList<>(); for (CardDownloadData card : allCardsMissingImage) {
for (CardDownloadData data : cards) { if (card.isToken()) {
if (data.isType2() || data.isToken()) { missingTokens++;
type2cards.add(data);
}
} }
} }
} missingCards = allCardsMissingImage.size() - missingTokens;
jLabelAllMissing.setText("Missing: " + missingCards + " card images / " + missingTokens + " token images");
private void updateProgressText(int cardCount) { int imageSum = cardCount + tokenCount;
float mb = (cardCount * cardImageSource.getAverageSize()) / 1024; float mb = (imageSum * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format(cardIndex == cardCount ? "%d of %d cards finished! Please close!" bar.setString(String.format(cardIndex == imageSum ? "%d of %d (%d cards/%d tokens) image downloads finished! Please close!"
: "%d of %d cards finished! Please wait! [%.1f Mb]", 0, cardCount, mb)); : "%d of %d (%d cards/%d tokens) image downloads finished! Please wait! [%.1f Mb]", 0, imageSum, cardCount, tokenCount, mb));
} }
private static String createDownloadName(CardInfo card) { private static String createDownloadName(CardInfo card) {
@ -262,11 +408,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
logger.warn("No formats defined. Try connecting to a server first!"); logger.warn("No formats defined. Try connecting to a server first!");
} }
int numberCardImages = allCards.size();
int numberWithoutTokens = 0;
List<CardDownloadData> allCardsUrls = Collections.synchronizedList(new ArrayList<>()); List<CardDownloadData> allCardsUrls = Collections.synchronizedList(new ArrayList<>());
try { try {
offlineMode = true;
allCards.parallelStream().forEach(card -> { allCards.parallelStream().forEach(card -> {
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty() if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()
&& !ignoreUrls.contains(card.getSetCode())) { && !ignoreUrls.contains(card.getSetCode())) {
@ -286,6 +429,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) { if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) {
throw new IllegalStateException("Second side card can't have empty name."); throw new IllegalStateException("Second side card can't have empty name.");
} }
url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), true); url = new CardDownloadData(card.getSecondSideName(), card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), true);
url.setType2(isType2); url.setType2(isType2);
allCardsUrls.add(url); allCardsUrls.add(url);
@ -301,21 +445,18 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
allCardsUrls.add(cardDownloadData); allCardsUrls.add(cardDownloadData);
} }
} else if (card.getCardNumber().isEmpty() || "0".equals(card.getCardNumber())) { } else if (card.getCardNumber().isEmpty() || "0".equals(card.getCardNumber())) {
System.err.println("There was a critical error!");
logger.error("Card has no collector ID and won't be sent to client: " + card.getName()); logger.error("Card has no collector ID and won't be sent to client: " + card.getName());
} else if (card.getSetCode().isEmpty()) { } else if (card.getSetCode().isEmpty()) {
System.err.println("There was a critical error!");
logger.error("Card has no set name and won't be sent to client:" + card.getName()); logger.error("Card has no set name and won't be sent to client:" + card.getName());
} else {
logger.info("Card was not selected: " + card.getName());
} }
}); });
numberWithoutTokens = allCards.size();
allCardsUrls.addAll(getTokenCardUrls()); allCardsUrls.addAll(getTokenCardUrls());
} catch (Exception e) { } catch (Exception e) {
logger.error(e); logger.error(e);
} }
int numberAllTokenImages = allCardsUrls.size() - numberWithoutTokens;
/** /**
* check to see which cards we already have * check to see which cards we already have
*/ */
@ -329,22 +470,13 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} }
}); });
int tokenImages = 0;
for (CardDownloadData card : cardsToDownload) {
logger.debug((card.isToken() ? "Token" : "Card") + " image to download: " + card.getName() + " (" + card.getSet() + ')');
if (card.isToken()) {
tokenImages++;
}
}
logger.info("Check download images (total card images: " + numberCardImages + ", total token images: " + numberAllTokenImages + ')');
logger.info(" => Missing card images: " + (cardsToDownload.size() - tokenImages));
logger.info(" => Missing token images: " + tokenImages);
return new ArrayList<>(cardsToDownload); return new ArrayList<>(cardsToDownload);
} }
public static ArrayList<CardDownloadData> getTokenCardUrls() throws RuntimeException { public static ArrayList<CardDownloadData> getTokenCardUrls() throws RuntimeException {
ArrayList<CardDownloadData> list = new ArrayList<>(); ArrayList<CardDownloadData> list = new ArrayList<>();
InputStream in = DownloadPictures.class.getClassLoader().getResourceAsStream("card-pictures-tok.txt"); InputStream in = DownloadPictures.class
.getClassLoader().getResourceAsStream("card-pictures-tok.txt");
if (in == null) { if (in == null) {
logger.error("resources input stream is null"); logger.error("resources input stream is null");
@ -446,9 +578,8 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (p != null) { if (p != null) {
HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls(); HashSet<String> ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
List<CardDownloadData> cardsToDownload = this.checkBox.isSelected() ? type2cards : cards;
update(0, cardsToDownload.size()); update(0, cardsToDownload.size());
logger.info("Started download of " + cardsToDownload.size() + " images from source: " + cardImageSource.getSourceName());
int numberOfThreads = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_THREADS, "10")); int numberOfThreads = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_THREADS, "10"));
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
@ -457,7 +588,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
CardDownloadData card = cardsToDownload.get(i); CardDownloadData card = cardsToDownload.get(i);
logger.debug("Downloading card: " + card.getName() + " (" + card.getSet() + ')'); logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
String url; String url;
if (ignoreUrls.contains(card.getSet()) || card.isToken()) { if (ignoreUrls.contains(card.getSet()) || card.isToken()) {
@ -482,7 +613,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} catch (Exception ex) { } catch (Exception ex) {
} }
} else if (cardImageSource.getTotalImages() == -1) { } else if (cardImageSource.getTotalImages() == -1) {
logger.info("Card not available on " + cardImageSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')'); logger.info("Image not available on " + cardImageSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')');
synchronized (sync) { synchronized (sync) {
update(cardIndex + 1, cardsToDownload.size()); update(cardIndex + 1, cardsToDownload.size());
} }
@ -513,6 +644,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
System.gc(); System.gc();
} }
closeButton.setText("Close"); closeButton.setText("Close");
updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
} }
static String convertStreamToString(java.io.InputStream is) { static String convertStreamToString(java.io.InputStream is) {
@ -604,6 +736,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (temporaryFile != null && temporaryFile.length() > 100) { if (temporaryFile != null && temporaryFile.length() > 100) {
useTempFile = true; useTempFile = true;
} else { } else {
cardImageSource.doPause(url.getPath()); cardImageSource.doPause(url.getPath());
httpConn = url.openConnection(p); httpConn = url.openConnection(p);
httpConn.connect(); httpConn.connect();
@ -708,29 +841,52 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (cardIndex < count) { if (cardIndex < count) {
float mb = ((count - card) * cardImageSource.getAverageSize()) / 1024; float mb = ((count - card) * cardImageSource.getAverageSize()) / 1024;
bar.setString(String.format("%d of %d cards finished! Please wait! [%.1f Mb]", bar.setString(String.format("%d of %d image downloads finished! Please wait! [%.1f Mb]",
card, count, mb)); card, count, mb));
} else { } else {
List<CardDownloadData> remainingCards = Collections.synchronizedList(new ArrayList<>()); List<CardDownloadData> remainingCards = Collections.synchronizedList(new ArrayList<>());
DownloadPictures.this.cards.parallelStream().forEach(cardDownloadData -> { DownloadPictures.this.allCardsMissingImage.parallelStream().forEach(cardDownloadData -> {
TFile file = new TFile(CardImageUtils.generateImagePath(cardDownloadData)); TFile file = new TFile(CardImageUtils.generateImagePath(cardDownloadData));
if (!file.exists()) { if (!file.exists()) {
remainingCards.add(cardDownloadData); remainingCards.add(cardDownloadData);
} }
}); });
DownloadPictures.this.cards = new ArrayList<>(remainingCards); // remove the cards not downloaded to get the siccessfull downloaded cards
DownloadPictures.this.cardsToDownload.removeAll(remainingCards);
DownloadPictures.this.allCardsMissingImage.removeAll(DownloadPictures.this.cardsToDownload);
count = DownloadPictures.this.cards.size(); count = remainingCards.size();
if (count == 0) { if (count == 0) {
bar.setString("0 cards remaining! Please close!"); bar.setString("0 images remaining! Please close!");
} else { } else {
bar.setString(String.format("%d cards remaining! Please choose another source!", count)); // bar.setString(String.format("%d cards remaining! Please choose another source!", count));
startDownloadButton.setEnabled(true); startDownloadButton.setEnabled(true);
} }
} }
} }
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
}
class LoadMissingCardData implements Runnable {
private static DownloadPictures downloadPictures;
public LoadMissingCardData(DownloadPictures downloadPictures) {
LoadMissingCardData.downloadPictures = downloadPictures;
}
@Override
public void run() {
downloadPictures.setAllMissingCards();
}
public static void main() {
(new Thread(new LoadMissingCardData(downloadPictures))).start();
}
} }

View file

@ -157,6 +157,53 @@ public final class ImageCache {
}); });
} }
public static String getFilePath(CardView card, int width) {
String key = getKey(card, card.getName(), Integer.toString(width));
boolean usesVariousArt = false;
if (key.matches(".*#usesVariousArt.*")) {
usesVariousArt = true;
key = key.replace("#usesVariousArt", "");
}
boolean thumbnail = false;
if (key.matches(".*#thumb.*")) {
thumbnail = true;
key = key.replace("#thumb", "");
}
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String set = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
}
String tokenSetCode = m.group(5);
String tokenDescriptor = m.group(6);
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
String path;
if (collectorId.isEmpty() || "0".equals(collectorId)) {
info.setToken(true);
path = CardImageUtils.generateFullTokenImagePath(info);
if (path == null) {
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename;
}
} else {
path = CardImageUtils.generateImagePath(info);
}
if (thumbnail && path.endsWith(".jpg")) {
return buildThumbnailPath(path);
}
return path;
}
return "";
}
private ImageCache() { private ImageCache() {
} }

View file

@ -48,6 +48,19 @@ public final class CardImageUtils {
log.warn("Token image file not found: " + card.getSet() + " - " + card.getTokenSetCode() + " - " + card.getName()); log.warn("Token image file not found: " + card.getSet() + " - " + card.getTokenSetCode() + " - " + card.getName());
return null; return null;
} }
/**
*
* @param card
* @return String regardless of whether image exists
*/
public static String generateFullTokenImagePath(CardDownloadData card) {
if (card.isToken()) {
String filePath = getTokenImagePath(card);
return filePath;
}
return "";
}
private static String getTokenImagePath(CardDownloadData card) { private static String getTokenImagePath(CardDownloadData card) {
String filename = generateImagePath(card); String filename = generateImagePath(card);

View file

@ -1096,6 +1096,16 @@
|Generate|TOK:WWK|Snake|||SnakeToken| |Generate|TOK:WWK|Snake|||SnakeToken|
|Generate|TOK:WWK|Soldier Ally|||JoinTheRanksSoldierToken| |Generate|TOK:WWK|Soldier Ally|||JoinTheRanksSoldierToken|
|Generate|TOK:WWK|Wolf|||WolfToken| |Generate|TOK:WWK|Wolf|||WolfToken|
|Generate|TOK:XLN|Dinosaur|||DinosaurToken|
|Generate|TOK:XLN|Illusion|||JaceCunningCastawayIllusionToken|
|Generate|TOK:XLN|Merfolk|||MerfolkHexproofToken|
|Generate|TOK:XLN|Pirate|||PirateToken|
|Generate|TOK:XLN|Plant|||DefenderPlantToken|
|Generate|TOK:XLN|Treasure|1||TreasureToken|
|Generate|TOK:XLN|Treasure|2||TreasureToken|
|Generate|TOK:XLN|Treasure|3||TreasureToken|
|Generate|TOK:XLN|Treasure|4||TreasureToken|
|Generate|TOK:XLN|Vampire|||IxalanVampireToken|
|Generate|TOK:ZEN|Angel|||AngelToken| |Generate|TOK:ZEN|Angel|||AngelToken|
|Generate|TOK:ZEN|Beast|||BeastToken2| |Generate|TOK:ZEN|Beast|||BeastToken2|
|Generate|TOK:ZEN|Bird|||BirdToken| |Generate|TOK:ZEN|Bird|||BirdToken|

View file

@ -74,6 +74,6 @@ dd3evg=ddaevg
dd3gvl=ddagvl dd3gvl=ddagvl
dd3jvc=ddajvc dd3jvc=ddajvc
# Remove setname as soon as the images can be downloaded # Remove setname as soon as the images can be downloaded
ignore.urls=TOK,DDT,V17,IMA,XLN,RIX,,E02,M19,M25,DOM,UST,H17 ignore.urls=TOK,DDT,V17,RIX,E02,M19,M25,DOM,UST,H17
# sets ordered by release time (newest goes first) # sets ordered by release time (newest goes first)
token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC

View file

@ -222,7 +222,7 @@ Eldrazi Spawn, 1a, -, -, -, Creature - Eldrazi Spawn, Aleksi Briclot, Sacrifice
Eldrazi Spawn, 1b, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 1b, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool.
Eldrazi Spawn, 1c, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 1c, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool.
Elemental, 2, R, *|*, -, Creature - Elemental, Jung Park, - Elemental, 2, R, *|*, -, Creature - Elemental, Jung Park, -
Hellion, 3, R, 4|4, -, Creature - Hellion, Anthony Francisco, - Hellion, 1, R, 4|4, -, Creature - Hellion, Anthony Francisco, -
Ooze, 4, G, *|*, -, Creature - Ooze, Daniel Ljunggren, - Ooze, 4, G, *|*, -, Creature - Ooze, Daniel Ljunggren, -
Tuktuk The Returned, 5, -, 5|5, -, Legendary Artifact Creature - Goblin Golem, Franz Vohwinkel, - Tuktuk The Returned, 5, -, 5|5, -, Legendary Artifact Creature - Goblin Golem, Franz Vohwinkel, -
@ -344,7 +344,7 @@ Soldier, 3, W, 1|1, -, Creature - Soldier, Greg Staples, -
Drake, 4, U, 2|2, -, Creature - Drake, Svetlin Velinov, Flying Drake, 4, U, 2|2, -, Creature - Drake, Svetlin Velinov, Flying
Zombie, 5, B, 2|2, -, Creature - Zombie, Lucas Graciano, - Zombie, 5, B, 2|2, -, Creature - Zombie, Lucas Graciano, -
Goblin, 6, R, 1|1, -, Creature - Goblin, Karl Kopinski, - Goblin, 6, R, 1|1, -, Creature - Goblin, Karl Kopinski, -
Hellion, 7, R, 4|4, -, Creature - Hellion, Anthony Francisco, - Hellion, 1, R, 4|4, -, Creature - Hellion, Anthony Francisco, -
Beast, 8, G, 3|3, -, Creature - Beast, John Donahue, - Beast, 8, G, 3|3, -, Creature - Beast, John Donahue, -
Saproling, 9, G, 1|1, -, Creature - Saproling, Brad Rigney, - Saproling, 9, G, 1|1, -, Creature - Saproling, Brad Rigney, -
Wurm, 10, G, 6|6, -, Creature - Wurm, Anthony Francisco, - Wurm, 10, G, 6|6, -, Creature - Wurm, Anthony Francisco, -
@ -664,7 +664,7 @@ DDP - Duel Decks: Zendikar vs. Eldrazi (2015-08-28)
Eldrazi Spawn, 076, -, -, -, Creature - Eldrazi Spawn, Aleksi Briclot, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 076, -, -, -, Creature - Eldrazi Spawn, Aleksi Briclot, Sacrifice this creature: Add {1} to your mana pool.
Eldrazi Spawn, 077, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 077, -, -, -, Creature - Eldrazi Spawn, Veronique Meignaud, Sacrifice this creature: Add {1} to your mana pool.
Eldrazi Spawn, 078, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool. Eldrazi Spawn, 078, -, -, -, Creature - Eldrazi Spawn, Mark Tedin, Sacrifice this creature: Add {1} to your mana pool.
Hellion, 079, R, 4|4, -, Creature - Hellion, Anthony Francisco, - Hellion, 1, R, 4|4, -, Creature - Hellion, Anthony Francisco, -
Plant, 080, G, -, -, Creature - Plant, Daren Bader, - Plant, 080, G, -, -, Creature - Plant, Daren Bader, -
BFZ - Battle for Zendikar (2015-10-09) BFZ - Battle for Zendikar (2015-10-09)
@ -745,3 +745,20 @@ Clue, 015, -, -, -, Artifact - Clue, James Paick, {2} Sacrifice this Artifact
Clue, 016, -, -, -, Artifact - Clue, Franz Vohwinkel, {2} Sacrifice this Artifact: Draw a card. Clue, 016, -, -, -, Artifact - Clue, Franz Vohwinkel, {2} Sacrifice this Artifact: Draw a card.
Jace Emblem, 017, -, -, -, Emblem - Jace, Tyler Jacobson, Whenever an opponent casts his or her first spell each turn<72> counter that spell. Jace Emblem, 017, -, -, -, Emblem - Jace, Tyler Jacobson, Whenever an opponent casts his or her first spell each turn<72> counter that spell.
Arlinn Emblem, 018, -, -, -, Emblem - Arlinn, Winona Nelson, Creatures you control have haste and '{T}: This creature deals damage equal to its power to target creature or player.' Arlinn Emblem, 018, -, -, -, Emblem - Arlinn, Winona Nelson, Creatures you control have haste and '{T}: This creature deals damage equal to its power to target creature or player.'
SWS - Star Wars Custom set
Rebel, 001, W, 1|1, -, Creature - Rebel, Alex Konstad, -
Trooper, 002, W, 1|1, -, Creature - Trooper, Darren Tan, -
Tusken Raider, 003, W, 1|1, -, Creature - Tusken Raider, William O'Connor, -
Ewok, 004, G, 1|1, -, Creature - Ewok, Chris NG, -
Hunter, 005, R, 4|4, -, Creature - Hunter, Steve Argyle, -
Royal Guard, 006, R, 2|2, -, Creature - Soldier, Aldo Katayanagi, First Strike
AT-AT, 007, -, 5|5, -, Artifact Creature - AT-AT, Prokhoda, When this creature dies<65> create two 1/1 white Trooper creature tokens.
B-Wing, 008, -, 1|1, -, Artifact Creature - Rebel Starship, Anthony Devine, Spaceflight
Droid, 009, -, 1|1, -, Artifact Creature - Droid, PeetuGee, -
TIE Fighter, 010, -, 1|1, -, Artifact Creature - Starship, Darren Tan, Spaceflight
Yoda Emblem, 011, -, -, -, Emblem - Yoda, Jerry Vanderstelt, Hexproof<6F> you and your creatures have.
Obi-Wan Kenobi Emblem, 012, -, -, -, Emblem - Obi-Wan Kenobi, Jerry Vanderstelt, Creatures you control get +1/+1 and have vigilance<63> first strike<6B> and lifelink.
Aurra Sing Emblem, 013, -, -, -, Emblem - Aurra Sing, Willman1701, Whenever a nontoken creature you control leaves the battlefield<6C> discard a card.

Can't render this file because it contains an unexpected character in line 549 and column 140.

View file

@ -30,18 +30,27 @@ public enum ClientCallbackMethod {
GAME_UPDATE("gameUpdate"), GAME_UPDATE("gameUpdate"),
DRAFT_OVER("draftOver"), DRAFT_OVER("draftOver"),
REPLAY_DONE("replayDone"), REPLAY_DONE("replayDone"),
USER_REQUEST_DIALOG("userRequestDialog"), USER_REQUEST_DIALOG("userRequestDialog"),
REPLAY_UPDATE("replayUpdate"), REPLAY_UPDATE("replayUpdate"),
REPLAY_INIT("replayInit"), REPLAY_INIT("replayInit"),
END_GAME_INFO("endGameInfo"), END_GAME_INFO("endGameInfo"),
GAME_TARGET("gameTarget"), GAME_TARGET("gameTarget"),
GAME_CHOOSE_ABILITY("gameChooseAbility"), GAME_CHOOSE_ABILITY("gameChooseAbility"),
GAME_CHOOSE_PILE("gameChoosePile"), GAME_CHOOSE_PILE("gameChoosePile"),
GAME_CHOOSE_CHOICE("gameChooseChoice"), GAME_ASK("gameAsk"), GAME_SELECT("gameSelect"), GAME_PLAY_MANA("gamePlayMana"), GAME_PLAY_XMANA("gamePlayXMana"), GAME_GET_AMOUNT("gameSelectAmount"), DRAFT_INIT("draftInit"), DRAFT_INFORM("draftInform"), DRAFT_PICK("draftPick"), DRAFT_UPDATE("draftUpdate"); GAME_CHOOSE_CHOICE("gameChooseChoice"),
GAME_ASK("gameAsk"),
GAME_SELECT("gameSelect"),
GAME_PLAY_MANA("gamePlayMana"),
GAME_PLAY_XMANA("gamePlayXMana"),
GAME_GET_AMOUNT("gameSelectAmount"),
DRAFT_INIT("draftInit"),
// DRAFT_INFORM("draftInform"),
DRAFT_PICK("draftPick"),
DRAFT_UPDATE("draftUpdate");
String value; String value;
ClientCallbackMethod(String value){ ClientCallbackMethod(String value) {
this.value = value; this.value = value;
} }
} }

View file

@ -35,7 +35,6 @@ import java.util.concurrent.TimeUnit;
import javax.swing.*; import javax.swing.*;
import mage.MageException; import mage.MageException;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.cards.decks.InvalidDeckException;
import mage.cards.repository.CardInfo; import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository; import mage.cards.repository.CardRepository;
import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionInfo;
@ -691,8 +690,6 @@ public class SessionImpl implements Session {
} }
return server.joinTable(sessionId, roomId, tableId, playerName, playerType, skill, deckList, password); return server.joinTable(sessionId, roomId, tableId, playerName, playerType, skill, deckList, password);
} }
} catch (InvalidDeckException iex) {
handleInvalidDeckException(iex);
} catch (GameException ex) { } catch (GameException ex) {
handleGameException(ex); handleGameException(ex);
} catch (MageException ex) { } catch (MageException ex) {
@ -1547,11 +1544,6 @@ public class SessionImpl implements Session {
client.showError(ex.getMessage()); client.showError(ex.getMessage());
} }
private void handleInvalidDeckException(InvalidDeckException iex) {
logger.warn(iex.getMessage() + '\n' + iex.getInvalid());
client.showError(iex.getMessage() + '\n' + iex.getInvalid());
}
private void handleGameException(GameException ex) { private void handleGameException(GameException ex) {
logger.warn(ex.getMessage()); logger.warn(ex.getMessage());
client.showError(ex.getMessage()); client.showError(ex.getMessage());

View file

@ -8,6 +8,7 @@ import mage.interfaces.rate.RateCallback;
import mage.util.RandomUtil; import mage.util.RandomUtil;
import java.util.*; import java.util.*;
import mage.constants.SubType;
/** /**
* Builds deck from provided card pool. * Builds deck from provided card pool.
@ -114,7 +115,7 @@ public final class DeckBuilder {
* @param count * @param count
*/ */
private static void addCardsToDeck(final Collection<MageScoredCard> remainingCards, final int minCost, final int maxCost, private static void addCardsToDeck(final Collection<MageScoredCard> remainingCards, final int minCost, final int maxCost,
final int count) { final int count) {
for (int c = count; c > 0; c--) { for (int c = count; c > 0; c--) {
@ -140,7 +141,8 @@ public final class DeckBuilder {
} }
/** /**
* Adds lands from non basic land (if provided), adds basic lands getting them from provided {@link RateCallback}}. * Adds lands from non basic land (if provided), adds basic lands getting
* them from provided {@link RateCallback}}.
* *
* @param allowedColors * @param allowedColors
* @param landCardPool * @param landCardPool
@ -241,9 +243,9 @@ public final class DeckBuilder {
int type; int type;
if (card.isCreature()) { if (card.isCreature()) {
type = 10; type = 10;
} else if (card.getSubtype(null).contains("Equipment")) { } else if (card.getSubtype(null).contains(SubType.EQUIPMENT)) {
type = 8; type = 8;
} else if (card.getSubtype(null).contains("Aura")) { } else if (card.getSubtype(null).contains(SubType.AURA)) {
type = 5; type = 5;
} else if (card.isInstant()) { } else if (card.isInstant()) {
type = 7; type = 7;
@ -251,11 +253,11 @@ public final class DeckBuilder {
type = 6; type = 6;
} }
this.score = this.score
// 5*card.getValue() + // not possible now = // 5*card.getValue() + // not possible now
3 * cardRater.rateCard(card) + 3 * cardRater.rateCard(card)
// 3*card.getRemoval() + // not possible now + // 3*card.getRemoval() + // not possible now
type + getManaCostScore(card, allowedColors); type + getManaCostScore(card, allowedColors);
} }
private int getManaCostScore(Card card, List<ColoredManaSymbol> allowedColors) { private int getManaCostScore(Card card, List<ColoredManaSymbol> allowedColors) {

View file

@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MAJOR = 1;
public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_MINOR = 4;
public final static int MAGE_VERSION_PATCH = 26; public final static int MAGE_VERSION_PATCH = 26;
public final static String MAGE_VERSION_MINOR_PATCH = "V2"; public final static String MAGE_VERSION_MINOR_PATCH = "V7";
public final static String MAGE_VERSION_INFO = ""; public final static String MAGE_VERSION_INFO = "";
private final int major; private final int major;

View file

@ -41,8 +41,10 @@ public class DuelCommander extends Commander {
banned.add("Channel"); banned.add("Channel");
banned.add("Chrome Mox"); banned.add("Chrome Mox");
banned.add("Dig Through Time"); banned.add("Dig Through Time");
banned.add("Eidolon of the Great Revel");
banned.add("Emrakul, the Aeons Torn"); banned.add("Emrakul, the Aeons Torn");
banned.add("Entomb"); banned.add("Entomb");
banned.add("Fireblast");
banned.add("Food Chain"); banned.add("Food Chain");
banned.add("Gaea's Cradle"); banned.add("Gaea's Cradle");
banned.add("Gifts Ungiven"); banned.add("Gifts Ungiven");
@ -68,11 +70,13 @@ public class DuelCommander extends Commander {
banned.add("Necrotic Ooze"); banned.add("Necrotic Ooze");
banned.add("Oath of Druids"); banned.add("Oath of Druids");
banned.add("Polymorph"); banned.add("Polymorph");
banned.add("Price of Progress");
banned.add("Protean Hulk"); banned.add("Protean Hulk");
banned.add("Sensei's Divining Top"); banned.add("Sensei's Divining Top");
banned.add("Shahrazad"); banned.add("Shahrazad");
banned.add("Sol Ring"); banned.add("Sol Ring");
banned.add("Strip Mine"); banned.add("Strip Mine");
banned.add("Sulfuric Vortex");
banned.add("The Tabernacle at Pendrell Vale"); banned.add("The Tabernacle at Pendrell Vale");
banned.add("Time Vault"); banned.add("Time Vault");
banned.add("Time Walk"); banned.add("Time Walk");
@ -84,6 +88,7 @@ public class DuelCommander extends Commander {
bannedCommander.add("Breya, Etherium Shaper"); bannedCommander.add("Breya, Etherium Shaper");
bannedCommander.add("Bruse Tarl, Boorish Herder"); bannedCommander.add("Bruse Tarl, Boorish Herder");
bannedCommander.add("Derevi, Empyrial Tactician"); bannedCommander.add("Derevi, Empyrial Tactician");
bannedCommander.add("Edgar Markov");
bannedCommander.add("Edric, Spymaster of Trest"); bannedCommander.add("Edric, Spymaster of Trest");
bannedCommander.add("Erayo, Soratami Ascendant"); bannedCommander.add("Erayo, Soratami Ascendant");
bannedCommander.add("Geist of Saint Traft"); bannedCommander.add("Geist of Saint Traft");

View file

@ -109,6 +109,15 @@ public class HistoricalType2 extends Constructed {
@Override @Override
public boolean validate(Deck deck) { public boolean validate(Deck deck) {
// debug code for finding set info
// System.out.println();
// for (ExpansionSet set : Sets.getInstance().values()) {
// if (set.getSetType() == SetType.CORE || set.getSetType() == SetType.EXPANSION) {
// System.out.println("Set:\t" + set.getCode() + "\t" + set.getReleaseDate() + "\t" + set.getName() + "\t" + set.getBlockName());
// }
// }
Map<String, String> leastInvalid = null; Map<String, String> leastInvalid = null;
boolean valid = false; boolean valid = false;

View file

@ -36,17 +36,20 @@ public class MTGO1v1Commander extends Commander {
public MTGO1v1Commander() { public MTGO1v1Commander() {
super("MTGO 1v1 Commander"); super("MTGO 1v1 Commander");
banned.add("Ancestral Recall"); banned.add("Ancestral Recall");
banned.add("Arcum Dagsson");
banned.add("Back to Basics"); banned.add("Back to Basics");
banned.add("Balance"); banned.add("Balance");
banned.add("Baral, Chief of Compliance");
banned.add("Bazaar of Baghdad"); banned.add("Bazaar of Baghdad");
banned.add("Black Lotus"); banned.add("Black Lotus");
banned.add("Braids, Cabal Minion"); banned.add("Braids, Cabal Minion");
banned.add("Brainstorm"); banned.add("Brainstorm");
banned.add("Channel"); banned.add("Channel");
banned.add("Derevi, Empyrial Tactician"); banned.add("Derevi, Empyrial Tactician");
banned.add("Demonic Tutor");
banned.add("Dig Through Time"); banned.add("Dig Through Time");
banned.add("Edric, Spymaster of Trest"); banned.add("Edric, Spymaster of Trest");
banned.add("Emrakul, the Aeons Torn");
banned.add("Enlightened Tutor");
banned.add("Entomb"); banned.add("Entomb");
banned.add("Fastbond"); banned.add("Fastbond");
banned.add("Food Chain"); banned.add("Food Chain");
@ -55,6 +58,7 @@ public class MTGO1v1Commander extends Commander {
banned.add("Griselbrand"); banned.add("Griselbrand");
banned.add("Hermit Druid"); banned.add("Hermit Druid");
banned.add("Humility"); banned.add("Humility");
banned.add("Imperial Seal");
banned.add("Karakas"); banned.add("Karakas");
banned.add("Library of Alexandria"); banned.add("Library of Alexandria");
banned.add("Mana Crypt"); banned.add("Mana Crypt");
@ -68,6 +72,7 @@ public class MTGO1v1Commander extends Commander {
banned.add("Mox Pearl"); banned.add("Mox Pearl");
banned.add("Mox Ruby"); banned.add("Mox Ruby");
banned.add("Mox Sapphire"); banned.add("Mox Sapphire");
banned.add("Mystical Tutor");
banned.add("Natural Order"); banned.add("Natural Order");
banned.add("Necropotence"); banned.add("Necropotence");
banned.add("Oath of Druids"); banned.add("Oath of Druids");
@ -88,8 +93,8 @@ public class MTGO1v1Commander extends Commander {
banned.add("Treachery"); banned.add("Treachery");
banned.add("Treasure Cruise"); banned.add("Treasure Cruise");
banned.add("Vial Smasher the Fierce"); banned.add("Vial Smasher the Fierce");
banned.add("Vampiric Tutor");
banned.add("Yamgmoth's Bargain"); banned.add("Yamgmoth's Bargain");
banned.add("Yisan, the Wanderer Bard");
banned.add("Zur the Enchanter"); banned.add("Zur the Enchanter");
} }
} }

View file

@ -73,9 +73,7 @@ public class Standard extends Constructed {
} }
} }
banned.add("Aetherworks Marvel"); banned.add("Aetherworks Marvel");
banned.add("Emrakul, the Promised End");
banned.add("Felidar Guardian"); banned.add("Felidar Guardian");
banned.add("Reflector Mage");
banned.add("Smuggler's Copter"); banned.add("Smuggler's Copter");
} }
@ -83,6 +81,6 @@ public class Standard extends Constructed {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(set.getReleaseDate()); cal.setTime(set.getReleaseDate());
// Sets from fall block are normally released in September and January // Sets from fall block are normally released in September and January
return cal.get(Calendar.MONTH) > 8 || cal.get(Calendar.MONTH) < 2; return cal.get(Calendar.MONTH) > 7 || cal.get(Calendar.MONTH) < 2;
} }
} }

View file

@ -94,6 +94,7 @@ public class Vintage extends Constructed {
restricted.add("Memory Jar"); restricted.add("Memory Jar");
restricted.add("Merchant Scroll"); restricted.add("Merchant Scroll");
restricted.add("Mind's Desire"); restricted.add("Mind's Desire");
restricted.add("Monastery Mentory");
restricted.add("Mox Emerald"); restricted.add("Mox Emerald");
restricted.add("Mox Jet"); restricted.add("Mox Jet");
restricted.add("Mox Pearl"); restricted.add("Mox Pearl");
@ -104,6 +105,7 @@ public class Vintage extends Constructed {
restricted.add("Ponder"); restricted.add("Ponder");
restricted.add("Sol Ring"); restricted.add("Sol Ring");
restricted.add("Strip Mine"); restricted.add("Strip Mine");
restricted.add("Thorn of Amethyst");
restricted.add("Time Vault"); restricted.add("Time Vault");
restricted.add("Time Walk"); restricted.add("Time Walk");
restricted.add("Timetwister"); restricted.add("Timetwister");
@ -114,7 +116,6 @@ public class Vintage extends Constructed {
restricted.add("Vampiric Tutor"); restricted.add("Vampiric Tutor");
restricted.add("Wheel of Fortune"); restricted.add("Wheel of Fortune");
restricted.add("Windfall"); restricted.add("Windfall");
restricted.add("Yawgmoth's Bargain");
restricted.add("Yawgmoth's Will"); restricted.add("Yawgmoth's Will");
} }

View file

@ -1,5 +1,5 @@
#Generated by Maven #Generated by Maven
#Mon Aug 28 09:53:46 CEST 2017 #Fri Sep 15 22:14:29 CEST 2017
version=1.4.26 version=1.4.26
groupId=org.mage groupId=org.mage
artifactId=mage-game-pennydreadfulcommanderfreeforall artifactId=mage-game-pennydreadfulcommanderfreeforall

View file

@ -1,17 +1,17 @@
package mage.player.ai.ma; package mage.player.ai.ma;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.cards.Card; import mage.cards.Card;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import java.util.UUID;
/** /**
* @author ubeefx, nantuko * @author ubeefx, nantuko
*/ */
@ -56,7 +56,7 @@ public final class ArtificialScoringSystem {
//score + =cardDefinition.getActivations().size()*50; //score + =cardDefinition.getActivations().size()*50;
//score += cardDefinition.getManaActivations().size()*80; //score += cardDefinition.getManaActivations().size()*80;
} else { } else {
if (permanent.getSubtype(game).contains("Equipment")) { if (permanent.getSubtype(game).contains(SubType.EQUIPMENT)) {
score += 100; score += 100;
} }
} }
@ -103,7 +103,7 @@ public final class ArtificialScoringSystem {
} }
score += equipments * 50 + enchantments * 100; score += equipments * 50 + enchantments * 100;
if (!permanent.canAttack(game)) { if (!permanent.canAttack(null, game)) {
score -= 100; score -= 100;
} }

View file

@ -1,16 +1,16 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: * permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this list of * 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list * 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials * of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. * provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,24 +20,22 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.player.ai; package mage.player.ai;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.DoubleStrikeAbility;
import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FirstStrikeAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -50,15 +48,16 @@ public class CombatEvaluator {
public int evaluate(Permanent creature, Game game) { public int evaluate(Permanent creature, Game game) {
if (!values.containsKey(creature.getId())) { if (!values.containsKey(creature.getId())) {
int value = 0; int value = 0;
if (creature.canAttack(game)) if (creature.canAttack(null, game)) {
value += 2; value += 2;
}
value += creature.getPower().getValue(); value += creature.getPower().getValue();
value += creature.getToughness().getValue(); value += creature.getToughness().getValue();
value += creature.getAbilities().getEvasionAbilities().size(); value += creature.getAbilities().getEvasionAbilities().size();
value += creature.getAbilities().getProtectionAbilities().size(); value += creature.getAbilities().getProtectionAbilities().size();
value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())?1:0; value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId()) ? 1 : 0;
value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())?2:0; value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId()) ? 2 : 0;
value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId())?1:0; value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId()) ? 1 : 0;
values.put(creature.getId(), value); values.put(creature.getId(), value);
} }
return values.get(creature.getId()); return values.get(creature.getId());

View file

@ -1514,7 +1514,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (object != null) { if (object != null) {
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(object, game.getState().getZone(object.getId()), game); LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(object, game.getState().getZone(object.getId()), game);
if (useableAbilities != null && !useableAbilities.isEmpty()) { if (useableAbilities != null && !useableAbilities.isEmpty()) {
game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values())); // game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values()));
// TODO: Improve this // TODO: Improve this
return (SpellAbility) useableAbilities.values().iterator().next(); return (SpellAbility) useableAbilities.values().iterator().next();
} }
@ -1939,7 +1939,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
protected int combatPotential(Permanent creature, Game game) { protected int combatPotential(Permanent creature, Game game) {
log.debug("combatPotential"); log.debug("combatPotential");
if (!creature.canAttack(game)) { if (!creature.canAttack(null, game)) {
return 0; return 0;
} }
int potential = creature.getPower().getValue(); int potential = creature.getPower().getValue();

View file

@ -13,6 +13,7 @@ import org.apache.log4j.Logger;
import java.io.InputStream; import java.io.InputStream;
import java.util.*; import java.util.*;
import mage.constants.SubType;
/** /**
* Class responsible for reading ratings from resources and rating given cards. * Class responsible for reading ratings from resources and rating given cards.
@ -60,9 +61,9 @@ public final class RateCard {
type = 15; type = 15;
} else if (card.isCreature()) { } else if (card.isCreature()) {
type = 10; type = 10;
} else if (card.getSubtype(null).contains("Equipment")) { } else if (card.getSubtype(null).contains(SubType.EQUIPMENT)) {
type = 8; type = 8;
} else if (card.getSubtype(null).contains("Aura")) { } else if (card.getSubtype(null).contains(SubType.AURA)) {
type = 5; type = 5;
} else if (card.isInstant()) { } else if (card.isInstant()) {
type = 7; type = 7;
@ -77,7 +78,7 @@ public final class RateCard {
} }
private static int isRemoval(Card card) { private static int isRemoval(Card card) {
if (card.getSubtype(null).contains("Aura") || card.isInstant() || card.isSorcery()) { if (card.getSubtype(null).contains(SubType.AURA) || card.isInstant() || card.isSorcery()) {
for (Ability ability : card.getAbilities()) { for (Ability ability : card.getAbilities()) {
for (Effect effect : ability.getEffects()) { for (Effect effect : ability.getEffects()) {

View file

@ -46,6 +46,7 @@ import mage.choices.ChoiceImpl;
import mage.constants.*; import mage.constants.*;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL; import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL; import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.filter.StaticFilters;
import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterBlockingCreature; import mage.filter.common.FilterBlockingCreature;
import mage.filter.common.FilterCreatureForCombat; import mage.filter.common.FilterCreatureForCombat;
@ -726,7 +727,10 @@ public class HumanPlayer extends PlayerImpl {
// It's end of turn phase // It's end of turn phase
if (!skippedAtLeastOnce if (!skippedAtLeastOnce
|| (playerId.equals(game.getActivePlayerId()) || (playerId.equals(game.getActivePlayerId())
&& !controllingPlayer.getUserData().getUserSkipPrioritySteps().isStopOnAllEndPhases())) { && !controllingPlayer
.getUserData()
.getUserSkipPrioritySteps()
.isStopOnAllEndPhases())) {
skippedAtLeastOnce = true; skippedAtLeastOnce = true;
if (passWithManaPoolCheck(game)) { if (passWithManaPoolCheck(game)) {
return false; return false;
@ -834,10 +838,23 @@ public class HumanPlayer extends PlayerImpl {
return !controllingPlayer.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType()); return !controllingPlayer.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType());
} }
} catch (NullPointerException ex) { } catch (NullPointerException ex) {
String isNull = controllingPlayer.getUserData() == null ? "null" : "not null"; if (controllingPlayer.getUserData() != null) {
logger.error("null pointer exception UserData = " + isNull); if (controllingPlayer.getUserData().getUserSkipPrioritySteps() != null) {
if (game.getStep() != null) {
if (game.getStep().getType() == null) {
logger.error("game.getStep().getType() == null");
}
} else {
logger.error("game.getStep() == null");
}
} else {
logger.error("UserData.getUserSkipPrioritySteps == null");
}
} else {
logger.error("UserData == null");
}
} }
return true; return false;
} }
@Override @Override
@ -1042,21 +1059,26 @@ public class HumanPlayer extends PlayerImpl {
updateGameStatePriority("selectAttackers", game); updateGameStatePriority("selectAttackers", game);
FilterCreatureForCombat filter = filterCreatureForCombat.copy(); FilterCreatureForCombat filter = filterCreatureForCombat.copy();
filter.add(new ControllerIdPredicate(attackingPlayerId)); filter.add(new ControllerIdPredicate(attackingPlayerId));
while (!abort) { while (!abort) {
if (passedAllTurns if (passedAllTurns
|| passedUntilEndStepBeforeMyTurn || passedUntilEndStepBeforeMyTurn
|| (!getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareAttackersDuringSkipAction() || (!getControllingPlayersUserData(game)
.getUserSkipPrioritySteps()
.isStopOnDeclareAttackersDuringSkipAction()
&& (passedTurn && (passedTurn
|| passedTurnSkipStack || passedTurnSkipStack
|| passedUntilEndOfTurn || passedUntilEndOfTurn
|| passedUntilNextMain))) { || passedUntilNextMain))) {
return; if (checkIfAttackersValid(game)) {
return;
}
} }
Map<String, Serializable> options = new HashMap<>(); Map<String, Serializable> options = new HashMap<>();
List<UUID> possibleAttackers = new ArrayList<>(); List<UUID> possibleAttackers = new ArrayList<>();
for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) { for (Permanent possibleAttacker : game.getBattlefield().getActivePermanents(filter, attackingPlayerId, game)) {
if (possibleAttacker.canAttack(game)) { if (possibleAttacker.canAttack(null, game)) {
possibleAttackers.add(possibleAttacker.getId()); possibleAttackers.add(possibleAttacker.getId());
} }
} }
@ -1095,46 +1117,14 @@ public class HumanPlayer extends PlayerImpl {
// attack selected default defender // attack selected default defender
declareAttacker(attacker.getId(), attackedDefender, game, false); declareAttacker(attacker.getId(), attackedDefender, game, false);
} }
} else if (response.getBoolean() != null) { } else if (response.getInteger() != null) { // F-Key
// check if enough attackers are declared if (checkIfAttackersValid(game)) {
if (!game.getCombat().getCreaturesForcedToAttack().isEmpty()) { return;
if (!game.getCombat().getAttackers().containsAll(game.getCombat().getCreaturesForcedToAttack().keySet())) { }
int forcedAttackers = 0; } else if (response.getBoolean() != null) { // ok button
StringBuilder sb = new StringBuilder(); if (checkIfAttackersValid(game)) {
for (UUID creatureId : game.getCombat().getCreaturesForcedToAttack().keySet()) { return;
boolean validForcedAttacker = false;
if (game.getCombat().getAttackers().contains(creatureId)) {
Set<UUID> possibleDefender = game.getCombat().getCreaturesForcedToAttack().get(creatureId);
if (possibleDefender.isEmpty()
|| possibleDefender.contains(game.getCombat().getDefenderId(creatureId))) {
validForcedAttacker = true;
}
}
if (validForcedAttacker) {
forcedAttackers++;
} else {
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
sb.append(creature.getIdName()).append(' ');
}
}
}
if (game.getCombat().getMaxAttackers() > forcedAttackers) {
int requireToAttack = Math.min(game.getCombat().getMaxAttackers() - forcedAttackers, game.getCombat().getCreaturesForcedToAttack().size() - forcedAttackers);
String message = (requireToAttack == 1 ? " more attacker that is " : " more attackers that are ")
+ "forced to attack.\nCreature"
+ (requireToAttack == 1 ? "" : "s") + " forced to attack: ";
game.informPlayer(this, sb.insert(0, message)
.insert(0, requireToAttack)
.insert(0, "You have to attack with ").toString());
continue;
}
}
} }
return;
} else if (response.getInteger() != null) {
return;
} else if (response.getUUID() != null) { } else if (response.getUUID() != null) {
Permanent attacker = game.getPermanent(response.getUUID()); Permanent attacker = game.getPermanent(response.getUUID());
if (attacker != null) { if (attacker != null) {
@ -1148,8 +1138,95 @@ public class HumanPlayer extends PlayerImpl {
} }
} }
private boolean checkIfAttackersValid(Game game) {
if (!game.getCombat().getCreaturesForcedToAttack().isEmpty()) {
if (!game.getCombat().getAttackers().containsAll(game.getCombat().getCreaturesForcedToAttack().keySet())) {
int forcedAttackers = 0;
StringBuilder sb = new StringBuilder();
for (UUID creatureId : game.getCombat().getCreaturesForcedToAttack().keySet()) {
boolean validForcedAttacker = false;
if (game.getCombat().getAttackers().contains(creatureId)) {
Set<UUID> possibleDefender = game.getCombat().getCreaturesForcedToAttack().get(creatureId);
if (possibleDefender.isEmpty()
|| possibleDefender.contains(game.getCombat().getDefenderId(creatureId))) {
validForcedAttacker = true;
}
}
if (validForcedAttacker) {
forcedAttackers++;
} else {
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
sb.append(creature.getIdName()).append(' ');
}
}
}
if (game.getCombat().getMaxAttackers() > forcedAttackers) {
int requireToAttack = Math.min(game.getCombat().getMaxAttackers() - forcedAttackers, game.getCombat().getCreaturesForcedToAttack().size() - forcedAttackers);
String message = (requireToAttack == 1 ? " more attacker that is " : " more attackers that are ")
+ "forced to attack.\nCreature"
+ (requireToAttack == 1 ? "" : "s") + " forced to attack: ";
game.informPlayer(this, sb.insert(0, message)
.insert(0, requireToAttack)
.insert(0, "You have to attack with ").toString());
return false;
}
}
}
// check if enough attackers are declared
// check if players have to be attacked
Set<UUID> playersToAttackIfAble = new HashSet<>();
for (Map.Entry<RequirementEffect, Set<Ability>> entry : game.getContinuousEffects().getApplicableRequirementEffects(null, true, game).entrySet()) {
RequirementEffect effect = entry.getKey();
for (Ability ability : entry.getValue()) {
UUID playerToAttack = effect.playerMustBeAttackedIfAble(ability, game);
if (playerToAttack != null) {
playersToAttackIfAble.add(playerToAttack);
}
}
}
if (!playersToAttackIfAble.isEmpty()) {
Set<UUID> checkPlayersToAttackIfAble = new HashSet<>(playersToAttackIfAble);
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
checkPlayersToAttackIfAble.remove(combatGroup.getDefendingPlayerId());
}
for (UUID forcedToAttackId : checkPlayersToAttackIfAble) {
Player forcedToAttack = game.getPlayer(forcedToAttackId);
for (Permanent attacker : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, getId(), game)) {
if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects(
GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER,
forcedToAttackId, attacker.getId(), attacker.getControllerId()), game)) {
continue;
}
// if attacker needs a specific defender to attack so select that one instead
if (game.getCombat().getCreaturesForcedToAttack().containsKey(attacker.getId())) {
Set<UUID> possibleDefenders = game.getCombat().getCreaturesForcedToAttack().get(attacker.getId());
if (!possibleDefenders.isEmpty() && !possibleDefenders.contains(forcedToAttackId)) {
continue;
}
}
UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(attacker.getId(), game);
if (playersToAttackIfAble.contains(defendingPlayerId)) {
// already attacks other player taht has to be attacked
continue;
}
if (defendingPlayerId != null || attacker.canAttackInPrinciple(forcedToAttackId, game)) {
game.informPlayer(this, "You are forced to attack " + forcedToAttack.getName() + " or a controlled planeswalker e.g. with " + attacker.getIdName() + ".");
return false;
}
}
}
}
return true;
}
private void removeAttackerIfPossible(Game game, Permanent attacker) { private void removeAttackerIfPossible(Game game, Permanent attacker) {
for (Map.Entry entry : game.getContinuousEffects().getApplicableRequirementEffects(attacker, game).entrySet()) { for (Map.Entry entry : game.getContinuousEffects().getApplicableRequirementEffects(attacker, false, game).entrySet()) {
RequirementEffect effect = (RequirementEffect) entry.getKey(); RequirementEffect effect = (RequirementEffect) entry.getKey();
if (effect.mustAttack(game)) { if (effect.mustAttack(game)) {
if (game.getCombat().getMaxAttackers() >= game.getCombat().getCreaturesForcedToAttack().size() if (game.getCombat().getMaxAttackers() >= game.getCombat().getCreaturesForcedToAttack().size()
@ -1223,7 +1300,9 @@ public class HumanPlayer extends PlayerImpl {
FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy(); FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy();
filter.add(new ControllerIdPredicate(defendingPlayerId)); filter.add(new ControllerIdPredicate(defendingPlayerId));
if (game.getBattlefield().count(filter, null, playerId, game) == 0 if (game.getBattlefield().count(filter, null, playerId, game) == 0
&& !getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockerIfNoneAvailable()) { && !getControllingPlayersUserData(game)
.getUserSkipPrioritySteps()
.isStopOnDeclareBlockerIfNoneAvailable()) {
return; return;
} }
while (!abort) { while (!abort) {

View file

@ -548,8 +548,6 @@ public class MageServerImpl implements MageServer {
UUID userId = session.get().getUserId(); UUID userId = session.get().getUserId();
ChatManager.instance.leaveChat(chatId, userId); ChatManager.instance.leaveChat(chatId, userId);
} }
} else {
logger.warn("The chatId is null. sessionId = " + sessionId);
} }
}); });
} }

View file

@ -374,11 +374,9 @@ public class Session {
call.setMessageId(messageId++); call.setMessageId(messageId++);
callbackHandler.handleCallbackOneway(new Callback(call)); callbackHandler.handleCallbackOneway(new Callback(call));
} catch (HandleCallbackException ex) { } catch (HandleCallbackException ex) {
// ex.printStackTrace();
UserManager.instance.getUser(userId).ifPresent(user -> { UserManager.instance.getUser(userId).ifPresent(user -> {
logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId); user.setUserState(User.UserState.Disconnected);
logger.warn(" - method: " + call.getMethod()); logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId + " - cause: " + getBasicCause(ex).toString());
logger.warn(" - cause: " + getBasicCause(ex).toString());
logger.trace("Stack trace:", ex); logger.trace("Stack trace:", ex);
SessionManager.instance.disconnect(sessionId, LostConnection); SessionManager.instance.disconnect(sessionId, LostConnection);
}); });

View file

@ -27,8 +27,6 @@
*/ */
package mage.server; package mage.server;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -41,7 +39,9 @@ import org.jboss.remoting.callback.InvokerCallbackHandler;
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public enum SessionManager { public enum SessionManager {
instance; instance;
private static final Logger logger = Logger.getLogger(SessionManager.class); private static final Logger logger = Logger.getLogger(SessionManager.class);
private final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
@ -153,14 +153,6 @@ public enum SessionManager {
} }
public Map<String, Session> getSessions() {
Map<String, Session> map = new HashMap<>();
for (Map.Entry<String, Session> entry : sessions.entrySet()) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
/** /**
* Admin requested the disconnect of a user * Admin requested the disconnect of a user
* *

View file

@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import mage.MageException; import mage.MageException;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.cards.decks.InvalidDeckException;
import mage.constants.RangeOfInfluence; import mage.constants.RangeOfInfluence;
import mage.constants.TableState; import mage.constants.TableState;
import mage.game.*; import mage.game.*;
@ -414,7 +413,17 @@ public class TableController {
} }
} }
if (!Main.isTestMode() && !table.getValidator().validate(deck)) { if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
throw new InvalidDeckException("Invalid deck for this format", table.getValidator().getInvalid()); Optional<User> _user = UserManager.instance.getUser(userId);
if (!_user.isPresent()) {
return false;
}
StringBuilder sb = new StringBuilder("Invalid deck for the selected ").append(table.getValidator().getName()).append(" format. \n\n");
for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
sb.append("\n\nAdd enough cards and try again!");
_user.get().showUserMessage("Submit deck", sb.toString());
return false;
} }
submitDeck(userId, playerId, deck); submitDeck(userId, playerId, deck);
return true; return true;

View file

@ -35,6 +35,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import mage.MageException; import mage.MageException;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.constants.TableState; import mage.constants.TableState;
@ -66,7 +69,10 @@ public enum TableManager {
private static final DateFormat formatter = new SimpleDateFormat("HH:mm:ss"); private static final DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<>();
private final ReadWriteLock controllersLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>();
private final ReadWriteLock tablesLock = new ReentrantReadWriteLock();
/** /**
* Defines how often checking process should be run on server. * Defines how often checking process should be run on server.
@ -88,25 +94,45 @@ public enum TableManager {
public Table createTable(UUID roomId, UUID userId, MatchOptions options) { public Table createTable(UUID roomId, UUID userId, MatchOptions options) {
TableController tableController = new TableController(roomId, userId, options); TableController tableController = new TableController(roomId, userId, options);
controllers.put(tableController.getTable().getId(), tableController); putControllers(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable()); putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable(); return tableController.getTable();
} }
public Table createTable(UUID roomId, MatchOptions options) { public Table createTable(UUID roomId, MatchOptions options) {
TableController tableController = new TableController(roomId, null, options); TableController tableController = new TableController(roomId, null, options);
controllers.put(tableController.getTable().getId(), tableController); putControllers(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable()); putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable(); return tableController.getTable();
} }
public Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options) { public Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options) {
TableController tableController = new TableController(roomId, userId, options); TableController tableController = new TableController(roomId, userId, options);
controllers.put(tableController.getTable().getId(), tableController); putControllers(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable()); putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable(); return tableController.getTable();
} }
private void putTables(UUID tableId, Table table) {
final Lock w = tablesLock.writeLock();
w.lock();
try {
tables.put(tableId, table);
} finally {
w.unlock();
}
}
private void putControllers(UUID controllerId, TableController tableController) {
final Lock w = controllersLock.writeLock();
w.lock();
try {
controllers.put(controllerId, tableController);
} finally {
w.unlock();
}
}
public Table getTable(UUID tableId) { public Table getTable(UUID tableId) {
return tables.get(tableId); return tables.get(tableId);
} }
@ -119,7 +145,27 @@ public enum TableManager {
} }
public Collection<Table> getTables() { public Collection<Table> getTables() {
return tables.values(); Collection<Table> newTables = new ArrayList<>();
final Lock r = tablesLock.readLock();
r.lock();
try {
newTables.addAll(tables.values());
} finally {
r.unlock();
}
return newTables;
}
public Collection<TableController> getControllers() {
Collection<TableController> newControllers = new ArrayList<>();
final Lock r = controllersLock.readLock();
r.lock();
try {
newControllers.addAll(controllers.values());
} finally {
r.unlock();
}
return newControllers;
} }
public Optional<TableController> getController(UUID tableId) { public Optional<TableController> getController(UUID tableId) {
@ -164,7 +210,7 @@ public enum TableManager {
// removeUserFromAllTablesAndChat user from all tournament sub tables // removeUserFromAllTablesAndChat user from all tournament sub tables
public void userQuitTournamentSubTables(UUID userId) { public void userQuitTournamentSubTables(UUID userId) {
for (TableController controller : controllers.values()) { for (TableController controller : getControllers()) {
if (controller.getTable() != null) { if (controller.getTable() != null) {
if (controller.getTable().isTournamentSubTable()) { if (controller.getTable().isTournamentSubTable()) {
controller.leaveTable(userId); controller.leaveTable(userId);
@ -177,7 +223,7 @@ public enum TableManager {
// removeUserFromAllTablesAndChat user from all sub tables of a tournament // removeUserFromAllTablesAndChat user from all sub tables of a tournament
public void userQuitTournamentSubTables(UUID tournamentId, UUID userId) { public void userQuitTournamentSubTables(UUID tournamentId, UUID userId) {
for (TableController controller : controllers.values()) { for (TableController controller : getControllers()) {
if (controller.getTable().isTournamentSubTable() && controller.getTable().getTournament().getId().equals(tournamentId)) { if (controller.getTable().isTournamentSubTable() && controller.getTable().getTournament().getId().equals(tournamentId)) {
if (controller.hasPlayer(userId)) { if (controller.hasPlayer(userId)) {
controller.leaveTable(userId); controller.leaveTable(userId);
@ -268,12 +314,6 @@ public enum TableManager {
return false; return false;
} }
// public boolean replayTable(UUID userId, UUID tableId) {
// if (controllers.containsKey(tableId)) {
// return controllers.get(tableId).replayTable(userId);
// }
// return false;
// }
public void endGame(UUID tableId) { public void endGame(UUID tableId) {
if (controllers.containsKey(tableId)) { if (controllers.containsKey(tableId)) {
if (controllers.get(tableId).endGameAndStartNextGame()) { if (controllers.get(tableId).endGameAndStartNextGame()) {
@ -321,11 +361,24 @@ public enum TableManager {
public void removeTable(UUID tableId) { public void removeTable(UUID tableId) {
TableController tableController = controllers.get(tableId); TableController tableController = controllers.get(tableId);
if (tableController != null) { if (tableController != null) {
controllers.remove(tableId); Lock w = controllersLock.writeLock();
w.lock();
try {
controllers.remove(tableId);
} finally {
w.unlock();
}
tableController.cleanUp(); // deletes the table chat and references to users tableController.cleanUp(); // deletes the table chat and references to users
Table table = tables.get(tableId); Table table = tables.get(tableId);
tables.remove(tableId); w = tablesLock.writeLock();
w.lock();
try {
tables.remove(tableId);
} finally {
w.unlock();
}
Match match = table.getMatch(); Match match = table.getMatch();
Game game = null; Game game = null;
if (match != null) { if (match != null) {
@ -383,8 +436,7 @@ public enum TableManager {
debugServerState(); debugServerState();
} }
logger.debug("TABLE HEALTH CHECK"); logger.debug("TABLE HEALTH CHECK");
List<Table> tableCopy = new ArrayList<>(tables.values()); for (Table table : getTables()) {
for (Table table : tableCopy) {
try { try {
if (table.getState() != TableState.FINISHED if (table.getState() != TableState.FINISHED
&& ((System.currentTimeMillis() - table.getStartTime().getTime()) / 1000) > 30) { // removeUserFromAllTablesAndChat only if table started longer than 30 seconds ago && ((System.currentTimeMillis() - table.getStartTime().getTime()) / 1000) > 30) { // removeUserFromAllTablesAndChat only if table started longer than 30 seconds ago

View file

@ -159,15 +159,15 @@ public class User {
public void setSessionId(String sessionId) { public void setSessionId(String sessionId) {
this.sessionId = sessionId; this.sessionId = sessionId;
if (sessionId.isEmpty()) { if (sessionId.isEmpty()) {
userState = UserState.Disconnected; setUserState(UserState.Disconnected);
lostConnection(); lostConnection();
logger.trace("USER - lost connection: " + userName + " id: " + userId); logger.trace("USER - lost connection: " + userName + " id: " + userId);
} else if (userState == UserState.Created) { } else if (userState == UserState.Created) {
userState = UserState.Connected; setUserState(UserState.Connected);
logger.trace("USER - created: " + userName + " id: " + userId); logger.trace("USER - created: " + userName + " id: " + userId);
} else { } else {
userState = UserState.Connected; setUserState(UserState.Connected);
reconnect(); reconnect();
logger.trace("USER - reconnected: " + userName + " id: " + userId); logger.trace("USER - reconnected: " + userName + " id: " + userId);
} }
@ -339,7 +339,7 @@ public class User {
} }
lastActivity = new Date(); lastActivity = new Date();
if (userState == UserState.Disconnected) { // this can happen if user reconnects very fast after disconnect if (userState == UserState.Disconnected) { // this can happen if user reconnects very fast after disconnect
userState = UserState.Connected; setUserState(UserState.Connected);
} }
} }
@ -388,7 +388,7 @@ public class User {
} else { } else {
// Table is missing after connection was lost during sideboard. // Table is missing after connection was lost during sideboard.
// Means other players were removed or conceded the game? // Means other players were removed or conceded the game?
logger.error("sideboarding id not found : " + entry.getKey()); logger.debug(getName() + " reconnects during sideboarding but tableId not found: " + entry.getKey());
} }
} }
ServerMessagesUtil.instance.incReconnects(); ServerMessagesUtil.instance.incReconnects();
@ -450,12 +450,14 @@ public class User {
TournamentManager.instance.quit(tournamentId, userId); TournamentManager.instance.quit(tournamentId, userId);
} }
userTournaments.clear(); userTournaments.clear();
constructing.clear();
logger.trace("REMOVE " + userName + " Tables " + tables.size()); logger.trace("REMOVE " + userName + " Tables " + tables.size());
for (Entry<UUID, Table> entry : tables.entrySet()) { for (Entry<UUID, Table> entry : tables.entrySet()) {
logger.debug("-- leave tableId: " + entry.getValue().getId()); logger.debug("-- leave tableId: " + entry.getValue().getId());
TableManager.instance.leaveTable(userId, entry.getValue().getId()); TableManager.instance.leaveTable(userId, entry.getValue().getId());
} }
tables.clear(); tables.clear();
sideboarding.clear();
logger.trace("REMOVE " + userName + " Game sessions: " + gameSessions.size()); logger.trace("REMOVE " + userName + " Game sessions: " + gameSessions.size());
for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) { for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) {
logger.debug("-- kill game session of gameId: " + gameSessionPlayer.getGameId()); logger.debug("-- kill game session of gameId: " + gameSessionPlayer.getGameId());

View file

@ -196,7 +196,7 @@ public enum UserManager {
Calendar calendarRemove = Calendar.getInstance(); Calendar calendarRemove = Calendar.getInstance();
calendarRemove.add(Calendar.MINUTE, -8); calendarRemove.add(Calendar.MINUTE, -8);
List<User> toRemove = new ArrayList<>(); List<User> toRemove = new ArrayList<>();
logger.info("Start Check Expired"); logger.debug("Start Check Expired");
ArrayList<User> userList = new ArrayList<>(); ArrayList<User> userList = new ArrayList<>();
final Lock r = lock.readLock(); final Lock r = lock.readLock();
r.lock(); r.lock();
@ -227,7 +227,7 @@ public enum UserManager {
handleException(ex); handleException(ex);
} }
} }
logger.info("Users to remove " + toRemove.size()); logger.debug("Users to remove " + toRemove.size());
final Lock w = lock.readLock(); final Lock w = lock.readLock();
w.lock(); w.lock();
try { try {
@ -237,7 +237,7 @@ public enum UserManager {
} finally { } finally {
w.unlock(); w.unlock();
} }
logger.info("End Check Expired"); logger.debug("End Check Expired");
} catch (Exception ex) { } catch (Exception ex) {
handleException(ex); handleException(ex);
} }

View file

@ -24,10 +24,16 @@
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.server.draft; package mage.server.draft;
import java.rmi.RemoteException;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import mage.game.draft.Draft; import mage.game.draft.Draft;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod; import mage.interfaces.callback.ClientCallbackMethod;
@ -39,14 +45,6 @@ import mage.view.DraftPickView;
import mage.view.DraftView; import mage.view.DraftView;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.rmi.RemoteException;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -89,21 +87,10 @@ public class DraftSession {
UserManager.instance UserManager.instance
.getUser(userId). .getUser(userId).
ifPresent(user -> user.fireCallback( ifPresent(user -> user.fireCallback(
new ClientCallback(ClientCallbackMethod.DRAFT_UPDATE, draft.getId(), getDraftView()))); new ClientCallback(ClientCallbackMethod.DRAFT_UPDATE, draft.getId(), getDraftView())));
} }
} }
// not used
//
public void inform(final String message) {
if (!killed) {
UserManager.instance
.getUser(userId)
.ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.DRAFT_INFORM, draft.getId(), new DraftClientMessage(getDraftView(), message))));
}
}
public void draftOver() { public void draftOver() {
if (!killed) { if (!killed) {
UserManager.instance UserManager.instance

View file

@ -31,6 +31,9 @@ import java.io.*;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import mage.MageException; import mage.MageException;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -80,7 +83,11 @@ public class GameController implements GameCallback {
protected static final ScheduledExecutorService timeoutIdleExecutor = ThreadExecutor.instance.getTimeoutIdleExecutor(); protected static final ScheduledExecutorService timeoutIdleExecutor = ThreadExecutor.instance.getTimeoutIdleExecutor();
private final ConcurrentHashMap<UUID, GameSessionPlayer> gameSessions = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, GameSessionPlayer> gameSessions = new ConcurrentHashMap<>();
private final ReadWriteLock gameSessionsLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, GameSessionWatcher> watchers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, GameSessionWatcher> watchers = new ConcurrentHashMap<>();
private final ReadWriteLock gameWatchersLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, PriorityTimer> timers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, PriorityTimer> timers = new ConcurrentHashMap<>();
private final ConcurrentHashMap<UUID, UUID> userPlayerMap; private final ConcurrentHashMap<UUID, UUID> userPlayerMap;
@ -114,7 +121,7 @@ public class GameController implements GameCallback {
public void cleanUp() { public void cleanUp() {
cancelTimeout(); cancelTimeout();
for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) { for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
gameSessionPlayer.cleanUp(); gameSessionPlayer.cleanUp();
} }
ChatManager.instance.destroyChatSession(chatId); ChatManager.instance.destroyChatSession(chatId);
@ -301,7 +308,13 @@ public class GameController implements GameCallback {
String joinType; String joinType;
if (gameSession == null) { if (gameSession == null) {
gameSession = new GameSessionPlayer(game, userId, playerId); gameSession = new GameSessionPlayer(game, userId, playerId);
gameSessions.put(playerId, gameSession); final Lock w = gameSessionsLock.writeLock();
w.lock();
try {
gameSessions.put(playerId, gameSession);
} finally {
w.unlock();
}
joinType = "joined"; joinType = "joined";
} else { } else {
joinType = "rejoined"; joinType = "rejoined";
@ -314,8 +327,8 @@ public class GameController implements GameCallback {
private synchronized void startGame() { private synchronized void startGame() {
if (gameFuture == null) { if (gameFuture == null) {
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
entry.getValue().init(); gameSessionPlayer.init();
} }
GameWorker worker = new GameWorker(game, choosingPlayerId, this); GameWorker worker = new GameWorker(game, choosingPlayerId, this);
@ -413,7 +426,13 @@ public class GameController implements GameCallback {
} }
UserManager.instance.getUser(userId).ifPresent(user -> { UserManager.instance.getUser(userId).ifPresent(user -> {
GameSessionWatcher gameWatcher = new GameSessionWatcher(userId, game, false); GameSessionWatcher gameWatcher = new GameSessionWatcher(userId, game, false);
watchers.put(userId, gameWatcher); final Lock w = gameWatchersLock.writeLock();
w.lock();
try {
watchers.put(userId, gameWatcher);
} finally {
w.unlock();
}
gameWatcher.init(); gameWatcher.init();
user.addGameWatchInfo(game.getId()); user.addGameWatchInfo(game.getId());
ChatManager.instance.broadcast(chatId, user.getName(), " has started watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null); ChatManager.instance.broadcast(chatId, user.getName(), " has started watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null);
@ -422,7 +441,13 @@ public class GameController implements GameCallback {
} }
public void stopWatching(UUID userId) { public void stopWatching(UUID userId) {
watchers.remove(userId); final Lock w = gameWatchersLock.writeLock();
w.lock();
try {
watchers.remove(userId);
} finally {
w.unlock();
}
UserManager.instance.getUser(userId).ifPresent(user -> { UserManager.instance.getUser(userId).ifPresent(user -> {
ChatManager.instance.broadcast(chatId, user.getName(), " has stopped watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null); ChatManager.instance.broadcast(chatId, user.getName(), " has stopped watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null);
}); });
@ -673,11 +698,11 @@ public class GameController implements GameCallback {
} }
public void endGame(final String message) throws MageException { public void endGame(final String message) throws MageException {
for (final GameSessionPlayer gameSession : gameSessions.values()) { for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.gameOver(message); gameSession.gameOver(message);
gameSession.removeGame(); gameSession.removeGame();
} }
for (final GameSessionWatcher gameWatcher : watchers.values()) { for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.gameOver(message); gameWatcher.gameOver(message);
} }
TableManager.instance.endGame(tableId); TableManager.instance.endGame(tableId);
@ -722,10 +747,10 @@ public class GameController implements GameCallback {
} }
} }
} }
for (final GameSessionPlayer gameSession : gameSessions.values()) { for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.update(); gameSession.update();
} }
for (final GameSessionWatcher gameWatcher : watchers.values()) { for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.update(); gameWatcher.update();
} }
} }
@ -734,12 +759,12 @@ public class GameController implements GameCallback {
Table table = TableManager.instance.getTable(tableId); Table table = TableManager.instance.getTable(tableId);
if (table != null) { if (table != null) {
if (table.getMatch() != null) { if (table.getMatch() != null) {
for (final GameSessionPlayer gameSession : gameSessions.values()) { for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.endGameInfo(table); gameSession.endGameInfo(table);
} }
// TODO: inform watchers about game end and who won
} }
} }
// TODO: inform watchers about game end and who won
} }
private synchronized void ask(UUID playerId, final String question, final Map<String, Serializable> options) throws MageException { private synchronized void ask(UUID playerId, final String question, final Map<String, Serializable> options) throws MageException {
@ -814,12 +839,12 @@ public class GameController implements GameCallback {
message.append(game.getStep().getType().toString()).append(" - "); message.append(game.getStep().getType().toString()).append(" - ");
} }
message.append("Waiting for ").append(game.getPlayer(playerId).getLogName()); message.append("Waiting for ").append(game.getPlayer(playerId).getLogName());
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
if (!entry.getKey().equals(playerId)) { if (!entry.getKey().equals(playerId)) {
entry.getValue().inform(message.toString()); entry.getValue().inform(message.toString());
} }
} }
for (final GameSessionWatcher watcher : watchers.values()) { for (final GameSessionWatcher watcher : getGameSessionWatchers()) {
watcher.inform(message.toString()); watcher.inform(message.toString());
} }
} }
@ -834,14 +859,13 @@ public class GameController implements GameCallback {
return; return;
} }
final String message = new StringBuilder(game.getStep().getType().toString()).append(" - Waiting for ").append(controller.getName()).toString(); final String message = new StringBuilder(game.getStep().getType().toString()).append(" - Waiting for ").append(controller.getName()).toString();
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
boolean skip = players.stream().anyMatch(playerId -> entry.getKey().equals(playerId)); boolean skip = players.stream().anyMatch(playerId -> entry.getKey().equals(playerId));
if (!skip) { if (!skip) {
entry.getValue().inform(message); entry.getValue().inform(message);
} }
} }
for (final GameSessionWatcher watcher : watchers.values()) { for (final GameSessionWatcher watcher : getGameSessionWatchers()) {
watcher.inform(message); watcher.inform(message);
} }
} }
@ -858,7 +882,7 @@ public class GameController implements GameCallback {
for (StackTraceElement e : ex.getStackTrace()) { for (StackTraceElement e : ex.getStackTrace()) {
sb.append(e.toString()).append('\n'); sb.append(e.toString()).append('\n');
} }
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
entry.getValue().gameError(sb.toString()); entry.getValue().gameError(sb.toString());
} }
} }
@ -995,6 +1019,42 @@ public class GameController implements GameCallback {
void execute(UUID player); void execute(UUID player);
} }
private Map<UUID, GameSessionPlayer> getGameSessionsMap() {
Map<UUID, GameSessionPlayer> newGameSessionsMap = new HashMap<>();
final Lock r = gameSessionsLock.readLock();
r.lock();
try {
newGameSessionsMap.putAll(gameSessions);
} finally {
r.unlock();
}
return newGameSessionsMap;
}
private List<GameSessionPlayer> getGameSessions() {
List<GameSessionPlayer> newGameSessions = new ArrayList<>();
final Lock r = gameSessionsLock.readLock();
r.lock();
try {
newGameSessions.addAll(gameSessions.values());
} finally {
r.unlock();
}
return newGameSessions;
}
private List<GameSessionWatcher> getGameSessionWatchers() {
List<GameSessionWatcher> newGameSessionWatchers = new ArrayList<>();
final Lock r = gameSessionsLock.readLock();
r.lock();
try {
newGameSessionWatchers.addAll(watchers.values());
} finally {
r.unlock();
}
return newGameSessionWatchers;
}
private GameSessionPlayer getGameSession(UUID playerId) { private GameSessionPlayer getGameSession(UUID playerId) {
if (!timers.isEmpty()) { if (!timers.isEmpty()) {
Player player = game.getState().getPlayer(playerId); Player player = game.getState().getPlayer(playerId);

View file

@ -24,13 +24,17 @@
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.server.game; package mage.server.game;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.constants.ManaType; import mage.constants.ManaType;
import mage.constants.PlayerAction; import mage.constants.PlayerAction;
@ -46,10 +50,17 @@ public enum GameManager {
instance; instance;
private final ConcurrentHashMap<UUID, GameController> gameControllers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, GameController> gameControllers = new ConcurrentHashMap<>();
private final ReadWriteLock gameControllersLock = new ReentrantReadWriteLock();
public UUID createGameSession(Game game, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) { public UUID createGameSession(Game game, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) {
GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions); GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions);
gameControllers.put(game.getId(), gameController); final Lock w = gameControllersLock.writeLock();
w.lock();
try {
gameControllers.put(game.getId(), gameController);
} finally {
w.unlock();
}
return gameController.getSessionId(); return gameController.getSessionId();
} }
@ -109,10 +120,10 @@ public enum GameManager {
gameController.quitMatch(userId); gameController.quitMatch(userId);
} }
} }
public void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data) { public void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data) {
GameController gameController = gameControllers.get(gameId); GameController gameController = gameControllers.get(gameId);
if (gameController != null) { if (gameController != null) {
gameController.sendPlayerAction(playerAction, userId, data); gameController.sendPlayerAction(playerAction, userId, data);
} }
} }
@ -151,7 +162,13 @@ public enum GameManager {
GameController gameController = gameControllers.get(gameId); GameController gameController = gameControllers.get(gameId);
if (gameController != null) { if (gameController != null) {
gameController.cleanUp(); gameController.cleanUp();
gameControllers.remove(gameId); final Lock w = gameControllersLock.writeLock();
w.lock();
try {
gameControllers.remove(gameId);
} finally {
w.unlock();
}
} }
} }
@ -174,8 +191,16 @@ public enum GameManager {
public int getNumberActiveGames() { public int getNumberActiveGames() {
return gameControllers.size(); return gameControllers.size();
} }
public ConcurrentHashMap<UUID, GameController> getGameController() { public Map<UUID, GameController> getGameController() {
return gameControllers; Map<UUID, GameController> newControllers = new HashMap<>();
final Lock r = gameControllersLock.readLock();
r.lock();
try {
newControllers.putAll(gameControllers);
} finally {
r.unlock();
}
return newControllers;
} }
} }

View file

@ -170,9 +170,11 @@ public class TournamentSession {
} }
private void removeTournamentForUser() { private void removeTournamentForUser() {
UserManager.instance.getUser(userId).ifPresent(user Optional<User> user = UserManager.instance.getUser(userId);
-> user.removeTournament(playerId)); if (user.isPresent()) {
user.get().removeTable(playerId);
user.get().removeTournament(playerId);
}
} }
} }

View file

@ -51,7 +51,7 @@ public class AcrobaticManeuver extends CardImpl {
Effect effect = new ExileTargetForSourceEffect(); Effect effect = new ExileTargetForSourceEffect();
effect.setApplyEffectsAfter(); effect.setApplyEffectsAfter();
this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, true)); this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect());
// Draw a card. // Draw a card.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));

View file

@ -54,12 +54,11 @@ public class ActOfAggression extends CardImpl {
} }
public ActOfAggression(UUID ownerId, CardSetInfo setInfo) { public ActOfAggression(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R/P}{R/P}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R/P}{R/P}");
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn));
this.getSpellAbility().addEffect(new UntapTargetEffect()); this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature"));
this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn."));
} }
public ActOfAggression(final ActOfAggression card) { public ActOfAggression(final ActOfAggression card) {

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID; import java.util.UUID;
@ -46,16 +45,14 @@ import mage.target.common.TargetCreaturePermanent;
public class ActOfTreason extends CardImpl { public class ActOfTreason extends CardImpl {
public ActOfTreason(UUID ownerId, CardSetInfo setInfo) { public ActOfTreason(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
// Gain control of target creature until end of turn. Untap that creature. // Gain control of target creature until end of turn. Untap that creature.
// It gains haste until end of turn. (It can attack and {T} this turn.) // It gains haste until end of turn. (It can attack and {T} this turn.)
this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn));
this.getSpellAbility().addEffect(new UntapTargetEffect()); this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature"));
this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn."));
} }
public ActOfTreason(final ActOfTreason card) { public ActOfTreason(final ActOfTreason card) {

View file

@ -0,0 +1,74 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.mana.WhiteManaAbility;
import mage.constants.SuperType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.permanent.token.IxalanVampireToken;
/**
*
* @author TheElk801
*/
public class AdantoTheFirstFort extends CardImpl {
public AdantoTheFirstFort(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
this.addSuperType(SuperType.LEGENDARY);
this.nightCard = true;
// T: Add W to your mana pool.
this.addAbility(new WhiteManaAbility());
// 2W, T: Create a 1/1 white Vampire creature token with lifelink.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new IxalanVampireToken()), new ManaCostsImpl("{2}{W}"));
ability.addCost(new TapSourceCost());
this.addAbility(ability);
}
public AdantoTheFirstFort(final AdantoTheFirstFort card) {
super(card);
}
@Override
public AdantoTheFirstFort copy() {
return new AdantoTheFirstFort(this);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.SourceAttackingCondition;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.IndestructibleAbility;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
/**
*
* @author TheElk801
*/
public class AdantoVanguard extends CardImpl {
public AdantoVanguard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// As long as Adanto Vanguard is attacking, it gets +2/+0.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostSourceEffect(2, 0, Duration.WhileOnBattlefield),
SourceAttackingCondition.instance,
"As long as {this} is attacking, it gets +2/+0"
)));
// Pay 4 life: Adanto Vanguard gains indestructible until end of turn.
this.addAbility(new SimpleActivatedAbility(
Zone.BATTLEFIELD,
new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn),
new PayLifeCost(4)
));
}
public AdantoVanguard(final AdantoVanguard card) {
super(card);
}
@Override
public AdantoVanguard copy() {
return new AdantoVanguard(this);
}
}

View file

@ -100,7 +100,7 @@ class AdaptiveAutomatonAddSubtypeEffect extends ContinuousEffectImpl {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) { if (permanent != null) {
SubType subtype = (SubType) game.getState().getValue(permanent.getId() + "_type"); SubType subtype = (SubType) game.getState().getValue(permanent.getId() + "_type");
if (subtype != null && !permanent.getSubtype(game).contains(subtype)) { if (subtype != null && !permanent.hasSubtype(subtype, game)) {
permanent.getSubtype(game).add(subtype); permanent.getSubtype(game).add(subtype);
} }
} }

View file

@ -0,0 +1,160 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetNonlandPermanent;
import mage.watchers.Watcher;
import java.util.*;
/**
*
* @author TheElk801
*/
public class AdmiralBeckettBrass extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Pirates you control");
private static final FilterNonlandPermanent filter2 = new FilterNonlandPermanent("nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn");
static {
filter.add(new SubtypePredicate(SubType.PIRATE));
filter.add(new ControllerPredicate(TargetController.YOU));
filter2.add(new ControllerDealtDamageByPiratesPredicate());
}
public AdmiralBeckettBrass(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}{R}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.PIRATE);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Other Pirates you control get +1/+1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, true)));
// At the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.
Ability ability = new BeginningOfEndStepTriggeredAbility(new GainControlTargetEffect(Duration.Custom, true), TargetController.YOU, false);
ability.addTarget(new TargetNonlandPermanent(filter2));
this.addAbility(ability, new DamagedByPiratesWatcher());
}
public AdmiralBeckettBrass(final AdmiralBeckettBrass card) {
super(card);
}
@Override
public AdmiralBeckettBrass copy() {
return new AdmiralBeckettBrass(this);
}
}
class DamagedByPiratesWatcher extends Watcher {
private final Map<UUID, Set<UUID>> damageSourceIds = new HashMap<>();
public DamagedByPiratesWatcher() {
super(DamagedByPiratesWatcher.class.getSimpleName(), WatcherScope.GAME);
}
public DamagedByPiratesWatcher(final DamagedByPiratesWatcher watcher) {
super(watcher);
for (UUID playerId : watcher.damageSourceIds.keySet()) {
Set<UUID> creatures = new HashSet<>();
creatures.addAll(watcher.damageSourceIds.get(playerId));
this.damageSourceIds.put(playerId, creatures);
}
}
@Override
public DamagedByPiratesWatcher copy() {
return new DamagedByPiratesWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
if (((DamagedPlayerEvent) event).isCombatDamage()) {
Permanent creature = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (creature != null && creature.hasSubtype(SubType.PIRATE, game)) {
if (damageSourceIds.keySet().contains(event.getTargetId())) {
damageSourceIds.get(event.getTargetId()).add(creature.getId());
} else {
Set<UUID> creatureSet = new HashSet<>();
creatureSet.add(creature.getId());
damageSourceIds.put(event.getTargetId(), creatureSet);
}
}
}
}
}
public boolean damagedByEnoughPirates(UUID sourceId, Game game) {
return damageSourceIds.keySet().contains(sourceId) && damageSourceIds.get(sourceId).size() > 2;
}
@Override
public void reset() {
super.reset();
damageSourceIds.clear();
}
}
class ControllerDealtDamageByPiratesPredicate implements Predicate<Permanent> {
public ControllerDealtDamageByPiratesPredicate() {
}
@Override
public boolean apply(Permanent input, Game game) {
DamagedByPiratesWatcher watcher = (DamagedByPiratesWatcher) game.getState().getWatchers().get(DamagedByPiratesWatcher.class.getSimpleName());
if (watcher != null) {
return watcher.damagedByEnoughPirates(input.getControllerId(), game);
}
return false;
}
}

View file

@ -0,0 +1,146 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Library;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author TheElk801
*/
public class AerialCaravan extends CardImpl {
public AerialCaravan(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(4);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// {1}{U}{U}: Exile the top card of your library. Until end of turn, you may play that card.
this.addAbility(new SimpleActivatedAbility(new AerialCaravanExileEffect(), new ManaCostsImpl("{1}{U}{U}")));
}
public AerialCaravan(final AerialCaravan card) {
super(card);
}
@Override
public AerialCaravan copy() {
return new AerialCaravan(this);
}
}
class AerialCaravanExileEffect extends OneShotEffect {
public AerialCaravanExileEffect() {
super(Outcome.Detriment);
this.staticText = "Exile the top card of your library. Until end of turn, you may play that card";
}
public AerialCaravanExileEffect(final AerialCaravanExileEffect effect) {
super(effect);
}
@Override
public AerialCaravanExileEffect copy() {
return new AerialCaravanExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (sourcePermanent != null && controller != null && controller.getLibrary().hasCards()) {
Library library = controller.getLibrary();
Card card = library.removeFromTop(game);
if (card != null) {
String exileName = sourcePermanent.getIdName() + " <this card may be played the turn it was exiled>";
controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName);
ContinuousEffect effect = new AerialCaravanCastFromExileEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game)));
game.addEffect(effect, source);
}
return true;
}
return false;
}
}
class AerialCaravanCastFromExileEffect extends AsThoughEffectImpl {
public AerialCaravanCastFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
staticText = "You may play the card from exile";
}
public AerialCaravanCastFromExileEffect(final AerialCaravanCastFromExileEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public AerialCaravanCastFromExileEffect copy() {
return new AerialCaravanCastFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return source.getControllerId().equals(affectedControllerId)
&& objectId.equals(getTargetPointer().getFirst(game, source));
}
}

View file

@ -27,7 +27,6 @@
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect;
@ -35,13 +34,16 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES;
import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import java.util.UUID;
import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURES;
/** /**
* *
* @author magenoxx_at_gmail.com * @author magenoxx_at_gmail.com
@ -76,7 +78,7 @@ public class AetherBurst extends CardImpl {
} }
} }
} }
((DynamicTargetCreaturePermanent) target).setMaxNumberOfTargets(amount + 1); target.setMaxNumberOfTargets(amount + 1);
} }
} }

View file

@ -33,14 +33,17 @@ import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.costs.common.PayEnergyCost;
import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlSourceEffect;
import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
/** /**
* *
@ -60,9 +63,7 @@ public class AethergeodeMiner extends CardImpl {
this.addAbility(new AttacksTriggeredAbility(new GetEnergyCountersControllerEffect(2), false)); this.addAbility(new AttacksTriggeredAbility(new GetEnergyCountersControllerEffect(2), false));
// Pay {E}{E}: Exile Aethergeode Miner, then return it to the battlefield under its owner's control. // Pay {E}{E}: Exile Aethergeode Miner, then return it to the battlefield under its owner's control.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileSourceEffect(true), new PayEnergyCost(2)); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AethergeodeMinerEffect(), new PayEnergyCost(2)));
ability.addEffect(new ReturnToBattlefieldUnderOwnerControlSourceEffect());
this.addAbility(ability);
} }
public AethergeodeMiner(final AethergeodeMiner card) { public AethergeodeMiner(final AethergeodeMiner card) {
@ -74,3 +75,34 @@ public class AethergeodeMiner extends CardImpl {
return new AethergeodeMiner(this); return new AethergeodeMiner(this);
} }
} }
class AethergeodeMinerEffect extends OneShotEffect {
public AethergeodeMinerEffect() {
super(Outcome.Neutral);
this.staticText = "Exile {this}, then return it to the battlefield under its owner's control";
}
public AethergeodeMinerEffect(final AethergeodeMinerEffect effect) {
super(effect);
}
@Override
public AethergeodeMinerEffect copy() {
return new AethergeodeMinerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
if (permanent.moveToExile(source.getSourceId(), "Aethergeode Miner", source.getSourceId(), game)) {
Card card = game.getExile().getCard(source.getSourceId(), game);
if (card != null) {
return card.moveToZone(Zone.BATTLEFIELD, source.getSourceId(), game, false);
}
}
}
return false;
}
}

View file

@ -0,0 +1,129 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BlocksTriggeredAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.LoseAbilitySourceEffect;
import mage.constants.SubType;
import mage.abilities.keyword.DefenderAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author TheElk801
*/
public class AgelessSentinels extends CardImpl {
public AgelessSentinels(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
this.subtype.add(SubType.WALL);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Defender
this.addAbility(DefenderAbility.getInstance());
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Ageless Sentinels blocks, it becomes a Bird Giant, and it loses defender.
Ability ability = new BlocksTriggeredAbility(new AgelessSentinelsEffect(), false, false, true);
Effect effect = new LoseAbilitySourceEffect(DefenderAbility.getInstance(), Duration.WhileOnBattlefield);
effect.setText("and it loses defender");
ability.addEffect(effect);
this.addAbility(ability);
}
public AgelessSentinels(final AgelessSentinels card) {
super(card);
}
@Override
public AgelessSentinels copy() {
return new AgelessSentinels(this);
}
private class AgelessSentinelsEffect extends ContinuousEffectImpl {
public AgelessSentinelsEffect() {
super(Duration.WhileOnBattlefield, Outcome.BecomeCreature);
staticText = "it becomes a Bird Giant,";
}
public AgelessSentinelsEffect(final AgelessSentinelsEffect effect) {
super(effect);
}
@Override
public AgelessSentinelsEffect copy() {
return new AgelessSentinelsEffect(this);
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
return false;
}
switch (layer) {
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
permanent.getSubtype(game).clear();
permanent.getSubtype(game).add(SubType.BIRD, SubType.GIANT);
}
break;
}
return true;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.TypeChangingEffects_4;
}
}
}

View file

@ -27,7 +27,6 @@
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
@ -42,6 +41,8 @@ import mage.game.permanent.token.CatToken;
import mage.game.permanent.token.Token; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
import java.util.UUID;
/** /**
* *
* @author Plopman * @author Plopman
@ -95,7 +96,7 @@ class AjanisChosenEffect extends OneShotEffect {
Token token = new CatToken(); Token token = new CatToken();
if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) { if (token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId())) {
Permanent enchantment = game.getPermanent(this.getTargetPointer().getFirst(game, source)); Permanent enchantment = game.getPermanent(this.getTargetPointer().getFirst(game, source));
if (enchantment != null && enchantment.getSubtype(game).contains("Aura")) { if (enchantment != null && enchantment.hasSubtype(SubType.AURA, game)) {
for (UUID tokenId : token.getLastAddedTokenIds()) { for (UUID tokenId : token.getLastAddedTokenIds()) {
Permanent tokenPermanent = game.getPermanent(tokenId); Permanent tokenPermanent = game.getPermanent(tokenId);
if (tokenPermanent != null) { if (tokenPermanent != null) {

View file

@ -30,7 +30,7 @@ package mage.cards.a;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.AttacksEachTurnStaticAbility; import mage.abilities.common.AttacksEachCombatStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DoIfCostPaid;
@ -65,7 +65,7 @@ public class AkoumFirebird extends CardImpl {
this.addAbility(HasteAbility.getInstance()); this.addAbility(HasteAbility.getInstance());
// Akoum Firebird attacks each turn if able. // Akoum Firebird attacks each turn if able.
this.addAbility(new AttacksEachTurnStaticAbility()); this.addAbility(new AttacksEachCombatStaticAbility());
// <i>Landfall</i>-Whenever a land enters the battlefield under your control, you may pay {4}{R}{R}. // <i>Landfall</i>-Whenever a land enters the battlefield under your control, you may pay {4}{R}{R}.
// If you do, return Akoum Firebird from your graveyard to the battlefield. // If you do, return Akoum Firebird from your graveyard to the battlefield.

View file

@ -0,0 +1,78 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.common.counter.AddCountersAllEffect;
import mage.constants.SubType;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
/**
*
* @author TheElk801
*/
public class AkuDjinn extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature each opponent controls");
static {
filter.add(new ControllerPredicate(TargetController.OPPONENT));
}
public AkuDjinn(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
this.subtype.add(SubType.DJINN);
this.power = new MageInt(5);
this.toughness = new MageInt(6);
// Trample
this.addAbility(TrampleAbility.getInstance());
// At the beginning of your upkeep, put a +1/+1 counter on each creature each opponent controls.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), TargetController.YOU, false));
}
public AkuDjinn(final AkuDjinn card) {
super(card);
}
@Override
public AkuDjinn copy() {
return new AkuDjinn(this);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.effects.common.GainLifeTargetEffect;
import mage.abilities.effects.common.PreventDamageToTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.target.TargetPlayer;
import mage.target.common.TargetCreatureOrPlayer;
/**
*
* @author TheElk801
*/
public class AlabasterPotion extends CardImpl {
public AlabasterPotion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{W}{W}");
// Choose one - Target player gains X life; or prevent the next X damage that would be dealt to target creature or player this turn.
this.getSpellAbility().addEffect(new GainLifeTargetEffect(new ManacostVariableValue()));
this.getSpellAbility().addTarget(new TargetPlayer());
Mode mode = new Mode();
mode.getEffects().add(new PreventDamageToTargetEffect(Duration.EndOfTurn, false, true, new ManacostVariableValue()));
mode.getTargets().add(new TargetCreatureOrPlayer());
this.getSpellAbility().addMode(mode);
}
public AlabasterPotion(final AlabasterPotion card) {
super(card);
}
@Override
public AlabasterPotion copy() {
return new AlabasterPotion(this);
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BlocksTriggeredAbility;
import mage.abilities.effects.common.DestroySourceEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author TheElk801
*/
public class AlabornZealot extends CardImpl {
public AlabornZealot(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// When Alaborn Zealot blocks a creature, destroy that creature and Alaborn Zealot.
Ability ability = new BlocksTriggeredAbility(new DestroyTargetEffect().setText("destroy that creature"), false, true, true);
ability.addEffect(new DestroySourceEffect().setText("and {this}"));
this.addAbility(ability);
}
public AlabornZealot(final AlabornZealot card) {
super(card);
}
@Override
public AlabornZealot copy() {
return new AlabornZealot(this);
}
}

View file

@ -47,23 +47,24 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
/** /**
* *
* @author noxx * @author noxx
*
*/ */
public class AlchemistsRefuge extends CardImpl { public class AlchemistsRefuge extends CardImpl {
private static final FilterCard filter = new FilterCard("nonland cards"); private static final FilterCard filter = new FilterCard("spells");
static { static {
filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); filter.add(Predicates.not(new CardTypePredicate(CardType.LAND)));
} }
public AlchemistsRefuge(UUID ownerId, CardSetInfo setInfo) { public AlchemistsRefuge(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.LAND},""); super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// {tap}: Add {C} to your mana pool. // {tap}: Add {C} to your mana pool.
this.addAbility(new ColorlessManaAbility()); this.addAbility(new ColorlessManaAbility());
// {G}{U}, {tap}: You may cast nonland cards this turn as though they had flash. // {G}{U}, {tap}: You may cast spells this turn as though they had flash.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
new AddContinuousEffectToGame(new CastAsThoughItHadFlashAllEffect(Duration.EndOfTurn, filter)), new AddContinuousEffectToGame(new CastAsThoughItHadFlashAllEffect(Duration.EndOfTurn, filter)),
new CompositeCost(new ManaCostsImpl("{G}{U}"), new TapSourceCost(), "{G}{U}, {T}"))); new CompositeCost(new ManaCostsImpl("{G}{U}"), new TapSourceCost(), "{G}{U}, {T}")));
} }

View file

@ -42,9 +42,7 @@ import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.FilterPermanent; import mage.filter.StaticFilters;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInHand;
@ -53,17 +51,9 @@ import mage.target.common.TargetCardInHand;
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/ */
public class AlexiZephyrMage extends CardImpl { public class AlexiZephyrMage extends CardImpl {
private final UUID originalId;
private static final FilterPermanent filter = new FilterPermanent("Target creatures");
static {
filter.add(new CardTypePredicate(CardType.CREATURE));
}
public AlexiZephyrMage(UUID ownerId, CardSetInfo setInfo) { public AlexiZephyrMage(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SPELLSHAPER); this.subtype.add(SubType.SPELLSHAPER);
@ -74,22 +64,12 @@ public class AlexiZephyrMage extends CardImpl {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{X}{U}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{X}{U}"));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards")))); ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards"))));
this.addAbility(ability); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURES));
this.addAbility(ability);
originalId = ability.getOriginalId();
}
@Override
public void adjustTargets(Ability ability, Game game) {
if (ability.getOriginalId().equals(originalId)) {
ability.getTargets().clear();
ability.addTarget(new TargetPermanent(ability.getManaCostsToPay().getX(), filter));
}
} }
public AlexiZephyrMage(final AlexiZephyrMage card) { public AlexiZephyrMage(final AlexiZephyrMage card) {
super(card); super(card);
this.originalId = card.originalId;
} }
@Override @Override

View file

@ -32,6 +32,7 @@ import mage.abilities.dynamicvalue.common.DomainValue;
import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.effects.common.DrawCardTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.AbilityWord;
import mage.constants.CardType; import mage.constants.CardType;
import mage.target.TargetPlayer; import mage.target.TargetPlayer;
@ -42,11 +43,13 @@ import mage.target.TargetPlayer;
public class AlliedStrategies extends CardImpl { public class AlliedStrategies extends CardImpl {
public AlliedStrategies(UUID ownerId, CardSetInfo setInfo) { public AlliedStrategies(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}");
// Domain - Target player draws a card for each basic land type among lands he or she controls. // Domain - Target player draws a card for each basic land type among lands he or she controls.
this.getSpellAbility().addEffect(new DrawCardTargetEffect(new DomainValue(true))); this.getSpellAbility().addEffect(new DrawCardTargetEffect(new DomainValue(true)));
this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addTarget(new TargetPlayer());
this.getSpellAbility().setAbilityWord(AbilityWord.DOMAIN);
} }
public AlliedStrategies(final AlliedStrategies card) { public AlliedStrategies(final AlliedStrategies card) {

View file

@ -109,6 +109,6 @@ class AlphaStatusDynamicValue implements DynamicValue {
@Override @Override
public String getMessage() { public String getMessage() {
return "each other creature on the battlefield that shares a creature type with it"; return "other creature on the battlefield that shares a creature type with it";
} }
} }

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID; import java.util.UUID;
@ -41,15 +40,16 @@ import mage.constants.SubType;
*/ */
public class AlphaTyrranax extends CardImpl { public class AlphaTyrranax extends CardImpl {
public AlphaTyrranax (UUID ownerId, CardSetInfo setInfo) { public AlphaTyrranax(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
this.subtype.add(SubType.DINOSAUR);
this.subtype.add(SubType.BEAST); this.subtype.add(SubType.BEAST);
this.power = new MageInt(6); this.power = new MageInt(6);
this.toughness = new MageInt(5); this.toughness = new MageInt(5);
} }
public AlphaTyrranax (final AlphaTyrranax card) { public AlphaTyrranax(final AlphaTyrranax card) {
super(card); super(card);
} }

View file

@ -53,18 +53,18 @@ import mage.target.common.TargetControlledCreaturePermanent;
/** /**
* *
* @author jeffwadsworth * @author jeffwadsworth
*
*/ */
public class AltarGolem extends CardImpl { public class AltarGolem extends CardImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control"); private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control");
static { static {
filter.add(Predicates.not(new TappedPredicate())); filter.add(Predicates.not(new TappedPredicate()));
} }
public AltarGolem(UUID ownerId, CardSetInfo setInfo) { public AltarGolem(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}");
this.subtype.add(SubType.GOLEM); this.subtype.add(SubType.GOLEM);
this.power = new MageInt(0); this.power = new MageInt(0);
@ -72,17 +72,17 @@ public class AltarGolem extends CardImpl {
// Trample // Trample
this.addAbility(TrampleAbility.getInstance()); this.addAbility(TrampleAbility.getInstance());
// Altar Golem's power and toughness are each equal to the number of creatures on the battlefield. // Altar Golem's power and toughness are each equal to the number of creatures on the battlefield.
DynamicValue amount = new PermanentsOnBattlefieldCount(new FilterCreaturePermanent("creatures in play")); DynamicValue amount = new PermanentsOnBattlefieldCount(new FilterCreaturePermanent("creatures on the battlefield"));
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(amount, Duration.EndOfGame))); this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(amount, Duration.EndOfGame)));
// Altar Golem doesn't untap during your untap step. // Altar Golem doesn't untap during your untap step.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect()));
// Tap five untapped creatures you control: Untap Altar Golem. // Tap five untapped creatures you control: Untap Altar Golem.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(5, 5, filter, true)))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(5, 5, filter, true))));
} }
public AltarGolem(final AltarGolem card) { public AltarGolem(final AltarGolem card) {

View file

@ -1,81 +1,81 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: * permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this list of * 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list * 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials * of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. * provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* The views and conclusions contained in the software and documentation are those of the * The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID; import java.util.UUID;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageWithPowerTargetEffect; import mage.abilities.effects.common.DamageWithPowerTargetEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.TargetController; import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate; import mage.filter.predicate.permanent.ControllerPredicate;
import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
/** /**
* *
* @author ciaccona007 * @author ciaccona007
*/ */
public class Ambuscade extends CardImpl { public class Ambuscade extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control");
static { static {
filter.add(new ControllerPredicate(TargetController.NOT_YOU)); filter.add(new ControllerPredicate(TargetController.NOT_YOU));
} }
public Ambuscade(UUID ownerId, CardSetInfo setInfo) { public Ambuscade(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
// Target creature you control gets +1/+0 until end of turn. // Target creature you control gets +1/+0 until end of turn.
Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn); Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn);
effect.setApplyEffectsAfter(); // needed to count the boost for the second effect effect.setApplyEffectsAfter(); // needed to count the boost for the second effect
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(effect);
// It deals damage equal to its power to target creature you don't control. // It deals damage equal to its power to target creature you don't control.
effect = new DamageWithPowerTargetEffect(); effect = new DamageWithPowerTargetEffect();
effect.setText("It deals damage equal to its power to target creature you don't control"); effect.setText("It deals damage equal to its power to target creature you don't control");
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(effect);
} }
public Ambuscade(final Ambuscade card) { public Ambuscade(final Ambuscade card) {
super(card); super(card);
} }
@Override @Override
public Ambuscade copy() { public Ambuscade copy() {
return new Ambuscade(this); return new Ambuscade(this);
} }
} }

View file

@ -0,0 +1,106 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author TheElk801
*/
public class AnZerrinRuins extends CardImpl {
public AnZerrinRuins(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}{R}");
// As An-Zerrin Ruins enters the battlefield, choose a creature type.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.UnboostCreature)));
// Creatures of the chosen type don't untap during their controllers' untap steps.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AnZerrinRuinsDontUntapEffect()));
}
public AnZerrinRuins(final AnZerrinRuins card) {
super(card);
}
@Override
public AnZerrinRuins copy() {
return new AnZerrinRuins(this);
}
}
class AnZerrinRuinsDontUntapEffect extends DontUntapInControllersUntapStepAllEffect {
public AnZerrinRuinsDontUntapEffect() {
super(Duration.WhileOnBattlefield, TargetController.ANY, new FilterCreaturePermanent());
}
public AnZerrinRuinsDontUntapEffect(final AnZerrinRuinsDontUntapEffect effect) {
super(effect);
}
@Override
public AnZerrinRuinsDontUntapEffect copy() {
return new AnZerrinRuinsDontUntapEffect(this);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (super.applies(event, source, game)) {
Permanent sourcePerm = game.getPermanent(source.getSourceId());
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && sourcePerm != null) {
SubType subtype = (SubType) game.getState().getValue(sourcePerm.getId() + "_type");
if (permanent.hasSubtype(subtype, game)) {
return true;
}
}
}
return false;
}
@Override
public String getText(Mode mode) {
return "Creatures of the chosen type don't untap during their controllers' untap steps.";
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SanctuaryTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author TheElk801
*/
public class AnaSanctuary extends CardImpl {
public AnaSanctuary(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
// At the beginning of your upkeep, if you control a blue or black permanent, target creature gets +1/+1 until end of turn. If you control a blue permanent and a black permanent, that creature gets +5/+5 until end of turn instead.
Ability ability = new SanctuaryTriggeredAbility(
new BoostEffect(1), new BoostEffect(5), ObjectColor.BLACK, ObjectColor.BLUE,
"At the beginning of your upkeep, if you control a blue or black permanent, "
+ "target creature gets +1/+1 until end of turn. If you control a blue permanent and a black permanent, that creature gets +5/+5 until end of turn instead."
);
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
}
public AnaSanctuary(final AnaSanctuary card) {
super(card);
}
@Override
public AnaSanctuary copy() {
return new AnaSanctuary(this);
}
}
class BoostEffect extends OneShotEffect {
private final int amount;
BoostEffect(int amount) {
super(Outcome.Benefit);
this.amount = amount;
}
BoostEffect(final BoostEffect effect) {
super(effect);
this.amount = effect.amount;
}
@Override
public BoostEffect copy() {
return new BoostEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
ContinuousEffect effect = new BoostTargetEffect(amount, amount, Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(source.getFirstTarget()));
game.addEffect(effect, source);
return true;
}
}

View file

@ -27,8 +27,6 @@
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.*; import mage.cards.*;
@ -40,6 +38,8 @@ import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetCard; import mage.target.TargetCard;
import java.util.UUID;
/** /**
* *
* @author Quercitron * @author Quercitron
@ -99,7 +99,7 @@ class AncestralMemoriesEffect extends OneShotEffect {
TargetCard target = new TargetCard(Math.min(2, cards.size()), Zone.LIBRARY, new FilterCard("two cards to put in your hand")); TargetCard target = new TargetCard(Math.min(2, cards.size()), Zone.LIBRARY, new FilterCard("two cards to put in your hand"));
if (player.choose(Outcome.Benefit, cards, target, game)) { if (player.choose(Outcome.Benefit, cards, target, game)) {
for (UUID targetId : (List<UUID>)target.getTargets()) { for (UUID targetId : target.getTargets()) {
Card card = cards.get(targetId, game); Card card = cards.get(targetId, game);
if (card != null) { if (card != null) {
card.moveToZone(Zone.HAND, source.getSourceId(), game, false); card.moveToZone(Zone.HAND, source.getSourceId(), game, false);

View file

@ -0,0 +1,59 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
/**
*
* @author caldover
*/
public class AncientBrontodon extends CardImpl {
public AncientBrontodon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}{G}");
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(9);
this.toughness = new MageInt(9);
}
public AncientBrontodon(final AncientBrontodon card) {
super(card);
}
@Override
public AncientBrontodon copy() {
return new AncientBrontodon(this);
}
}

View file

@ -25,29 +25,25 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.a; package mage.cards.a;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl; import mage.abilities.condition.common.SourceAttackingCondition;
import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.CostImpl; import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game; import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate;
import mage.game.permanent.Permanent; import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
/** /**
* *
@ -55,15 +51,24 @@ import mage.target.common.TargetCreaturePermanent;
*/ */
public class AncientHellkite extends CardImpl { public class AncientHellkite extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("creature defending player controls");
static {
filter.add(new CardTypePredicate(CardType.CREATURE));
filter.add(new DefendingPlayerControlsPredicate());
}
public AncientHellkite(UUID ownerId, CardSetInfo setInfo) { public AncientHellkite(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}{R}");
this.subtype.add(SubType.DRAGON); this.subtype.add(SubType.DRAGON);
this.power = new MageInt(6); this.power = new MageInt(6);
this.toughness = new MageInt(6); this.toughness = new MageInt(6);
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
this.addAbility(new AncientHellkiteAbility()); Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{R}"), SourceAttackingCondition.instance);
ability.addTarget(new TargetPermanent(filter));
this.addAbility(ability);
} }
public AncientHellkite(final AncientHellkite card) { public AncientHellkite(final AncientHellkite card) {
@ -76,71 +81,3 @@ public class AncientHellkite extends CardImpl {
} }
} }
class AncientHellkiteAbility extends ActivatedAbilityImpl {
private static final FilterCreaturePermanent filterTemplate = new FilterCreaturePermanent("creature defending player controls");
public AncientHellkiteAbility() {
super(Zone.BATTLEFIELD, new DamageTargetEffect(1));
addCost(new AncientHellkiteCost());
addManaCost(new ColoredManaCost(ColoredManaSymbol.R));
addTarget(new TargetCreaturePermanent(filterTemplate));
}
public AncientHellkiteAbility(final AncientHellkiteAbility ability) {
super(ability);
}
@Override
public AncientHellkiteAbility copy() {
return new AncientHellkiteAbility(this);
}
@Override
public boolean activate(Game game, boolean noMana) {
UUID defenderId = game.getCombat().getDefenderId(sourceId);
if (defenderId != null) {
FilterCreaturePermanent filter = filterTemplate.copy();
filter.add(new ControllerIdPredicate(defenderId));
this.getTargets().clear();
TargetCreaturePermanent target = new TargetCreaturePermanent(filter);
this.addTarget(target);
return super.activate(game, noMana);
}
return false;
}
}
class AncientHellkiteCost extends CostImpl {
public AncientHellkiteCost() {
this.text = "Activate this ability only if Ancient Hellkite is attacking";
}
public AncientHellkiteCost(final AncientHellkiteCost cost) {
super(cost);
}
@Override
public AncientHellkiteCost copy() {
return new AncientHellkiteCost(this);
}
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isAttacking()) {
return true;
}
return false;
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
this.paid = true;
return paid;
}
}

View file

@ -52,15 +52,17 @@ import mage.game.permanent.Permanent;
public class AncientOoze extends CardImpl { public class AncientOoze extends CardImpl {
public AncientOoze(UUID ownerId, CardSetInfo setInfo) { public AncientOoze(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{G}{G}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}");
this.subtype.add(SubType.OOZE); this.subtype.add(SubType.OOZE);
this.color.setGreen(true); this.color.setGreen(true);
this.power = new MageInt(0); this.power = new MageInt(0);
this.toughness = new MageInt(0); this.toughness = new MageInt(0);
// Ancient Ooze's power and toughness are each equal to the total converted mana cost of other creatures you control. // Ancient Ooze's power and toughness are each equal to the total converted mana cost of other creatures you control.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new AncientOozePowerToughnessValue(), Duration.EndOfGame))); this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new AncientOozePowerToughnessValue(), Duration.EndOfGame)
.setText("{this}'s power and toughness are each equal to the total converted mana cost of other creatures you control.")
));
} }
public AncientOoze(final AncientOoze card) { public AncientOoze(final AncientOoze card) {
@ -74,12 +76,12 @@ public class AncientOoze extends CardImpl {
} }
class AncientOozePowerToughnessValue implements DynamicValue { class AncientOozePowerToughnessValue implements DynamicValue {
@Override @Override
public int calculate(Game game, Ability sourceAbility, Effect effect) { public int calculate(Game game, Ability sourceAbility, Effect effect) {
int value = 0; int value = 0;
for(Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)){ for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)) {
if(creature != null && !sourceAbility.getSourceId().equals(creature.getId())){ if (creature != null && !sourceAbility.getSourceId().equals(creature.getId())) {
value += creature.getConvertedManaCost(); value += creature.getConvertedManaCost();
} }
} }

Some files were not shown because too many files have changed in this diff Show more