Compare commits

..

1287 Commits

Author SHA1 Message Date
Chris
3c08718b4b [maven-release-plugin] prepare release forge-1.5.12 2014-02-07 17:25:37 +00:00
Chris
2f033f795f Preparing the changes.txt file for the next beta build and release. 2014-02-07 15:10:33 +00:00
drdev
acd2ca2f23 Add GroupBy and PileBy combo boxes to Image View 2014-02-07 07:42:56 +00:00
drdev
de2731c21f Group sealed pool by color by default 2014-02-07 06:36:26 +00:00
drdev
5e0a8a6331 Add view options panel to ItemManager 2014-02-07 06:26:28 +00:00
drdev
b34ca7b951 Support canceling when selecting block or custom draft format when setting up a new draft or sealed deck
Prevent crash if you select a block that has no sets
2014-02-07 05:20:08 +00:00
drdev
1422748447 Prevent double-click failing after a previous double-click on a control opened a different screen 2014-02-07 04:25:31 +00:00
drdev
186f7f70e4 Fix so hover doesn't get stuck after right-clicking card in deck viewer 2014-02-07 04:11:56 +00:00
drdev
4b1eb98e5b Make Image View the default in all decklist ItemManagers 2014-02-07 04:05:45 +00:00
drdev
f7e185be3b Fix flaw with previous fix 2014-02-07 03:55:10 +00:00
Agetian
ea6da0b7dc - Make the zebra skin color on Marble Blue skin easier on the eyes. 2014-02-07 03:51:25 +00:00
drdev
ae23652c0c Avoid odd display when first opening image view 2014-02-07 03:51:17 +00:00
drdev
54b86b4db2 Update CHANGES.txt 2014-02-07 03:32:25 +00:00
drdev
f45779d2c3 Don't enforce 4-of card limit in deck editor if not enforcing deck legality 2014-02-07 03:28:13 +00:00
drdev
480ebbb121 Include information about ItemManager ImageView in CHANGES.txt 2014-02-07 01:00:12 +00:00
Chris
427e8c7059 Added new card names to changes.txt. 2014-02-06 15:01:20 +00:00
RumbleBBU
86cf1b63f4 Fixed quest starting pool color bias selection. 2014-02-06 08:25:41 +00:00
drdev
27ee03494b Improve restored scroll position after adjusting image view zoom 2014-02-06 05:36:46 +00:00
Maxmtg
2f5799f453 routed all checks like 'can pay for this shard with mana of certain color' through either player's manapool, or a method in ManaCostBeingPaid. Will add a color conversion matrix at these 2 points later. This change is a prerequisite to implement cards like Daxos of Meletis 2014-02-06 04:55:30 +00:00
drdev
7c419d875d Fix so image size goes from 50 to 300 2014-02-06 04:52:17 +00:00
Maxmtg
d7d87b5c5d CardDb is now able to report the unique missing cards (it's not a good idea to keep it all in ctor, should refactor later) 2014-02-06 04:00:51 +00:00
Sol
a11e7bbcb8 - Introduce PhaseIn and PhaseOut triggers
- Added Teferi's Imp (as an example of both)
2014-02-06 03:01:21 +00:00
drdev
028e4bc196 Support image view scaling using Ctrl+MouseWheel 2014-02-06 02:28:54 +00:00
Sloth
2252b9908b - Updated mtg-data.txt. 2014-02-05 17:05:21 +00:00
Sloth
02353704ca - Fixed Seize the Soul and friends. 2014-02-05 16:35:14 +00:00
Sloth
2bde0ac099 - Fixed lands with "May be played by your opponent" being playable by the owner. 2014-02-05 16:11:59 +00:00
Chris
2543aabbd4 Added new card names to changes.txt. 2014-02-05 13:25:59 +00:00
Maxmtg
d01c22b5f9 Change zone - only the choose card left inside if condition 2014-02-05 08:15:56 +00:00
swordshine
08896bdb8e - Converted "When CARDNAME is dealt damage, destroy it." keyword to script 2014-02-05 06:28:17 +00:00
swordshine
46d7491c78 - Update scripts 2014-02-05 06:26:05 +00:00
swordshine
9437ea5d90 - Converted Fastbond and Naya to script
- Missing file for Felhide Spiritbinder
2014-02-05 06:23:45 +00:00
swordshine
184943da83 - Fixed some scripts 2014-02-05 06:21:41 +00:00
swordshine
66e53ad1ed - Added Felhide Spiritbinder, Hunter's Prowess, Lightning Volley 2014-02-05 06:17:49 +00:00
Maxmtg
6595893e48 Add BNG cycle of creatues with "inspired - pay {2}{C} to put some tokens onto battlefield"
Fix: Oracle text for Searing Blood
2014-02-05 06:14:52 +00:00
Maxmtg
14ac5860f8 removed from EXT banlist cards whose editions left the format 2014-02-05 05:40:25 +00:00
drdev
4554d94f32 Prevent crash when Control clicking a selected card 2014-02-05 03:26:01 +00:00
drdev
737b2eb0f7 Prevent FMouseAdapters getting screwed up if popup appears between mouse down and mouse up 2014-02-05 02:03:07 +00:00
drdev
cb318eb8b9 Ensure scrolled to top after loading deck in list view 2014-02-05 01:25:52 +00:00
drdev
557035e340 Focus list when header clicked
Improve behavior of removing items in Image View
2014-02-05 01:21:48 +00:00
drdev
86bd570e2c Lock hovered item while context menu or zoomer open 2014-02-05 00:41:58 +00:00
drdev
d406114118 Add middle (and left+right) click zoom support to Image View 2014-02-05 00:15:24 +00:00
drdev
e4dd0faf5e Code cleanup 2014-02-04 23:02:04 +00:00
drdev
83aa60a77c Code cleanup 2014-02-04 22:59:12 +00:00
Sloth
acfe00d6db - Fixed a bug in ChooseSourceAi. 2014-02-04 19:55:45 +00:00
Maxmtg
d4a2ae07df code formatting 2014-02-04 18:13:56 +00:00
Maxmtg
926bdc1d5a Fix wrong filtering in 2 piles effect 2014-02-04 18:13:45 +00:00
drdev
ffee3af0ac Use same border for hovered and selected items 2014-02-04 18:06:33 +00:00
Maxmtg
42e6dffda2 Fixed problem with antes being drawn before players are assigned 2014-02-04 18:02:06 +00:00
drdev
c9db375d7c Fix multiple selection for image view 2014-02-04 17:53:30 +00:00
drdev
06de741370 Prevent auto-selecting card when first opening deck, changing filters, or expanding/collapsing groups 2014-02-04 17:45:23 +00:00
drdev
a2287aff5b Improve appearance of group headers
Allow deselecting cards in image view
2014-02-04 17:25:28 +00:00
drdev
444aad2034 Wrap piles 2014-02-04 16:10:27 +00:00
Chris
0de852043e Added new card names to changes.txt. 2014-02-04 14:49:52 +00:00
Sloth
9df1102514 - Added a space to message. 2014-02-04 14:24:06 +00:00
Sloth
8eaa535d6b - The AI will no longer kill itself with Mogis, God of Slaughter. 2014-02-04 13:59:44 +00:00
Sloth
b5e7de67a2 - The AI will no longer kill itself with Vendetta. 2014-02-04 13:47:55 +00:00
swordshine
f4db8078b8 - Converted Chains of Mephistopheles to script (it's a normal replacement effect, the original hardcoded script is not correct) 2014-02-04 08:13:50 +00:00
swordshine
21db65e9ab - Update state-based actions (704_5r now fire remove counter triggers, Brother Yamazaki now work correctly with Humility) 2014-02-04 08:09:25 +00:00
swordshine
1d0b60427d - Fixed ETBtapped effect
- Converted Transmute to script
2014-02-04 08:06:01 +00:00
swordshine
1760b10940 - Update scripts 2014-02-04 08:03:20 +00:00
swordshine
ce54805981 - Update banlist 2014-02-04 07:54:52 +00:00
swordshine
6df75248d8 - Added Gild and Satyr Firedancer 2014-02-04 07:52:47 +00:00
drdev
21331d4189 Support sorting cards into piles 2014-02-04 06:36:52 +00:00
drdev
c1e72c380b Prevent unnecessary refresh when setting up Image View groupBy/pileBy 2014-02-04 04:35:42 +00:00
drdev
b38346e00c Prevent changing collapsed state of empty group 2014-02-04 04:29:16 +00:00
drdev
9e5cbbb9d1 Prevent prompting to leave draft when exiting after already leaving a draft 2014-02-04 04:24:59 +00:00
drdev
f767ca34ce Fix so programatically selecting an item in collapsed group auto-expands group 2014-02-04 04:07:16 +00:00
drdev
d4bc720dd0 Fix so selection on Image View not lost on resize 2014-02-04 03:56:04 +00:00
drdev
1d1950d3cb Fix so hovering cards in FDeckViewer shows Details/Picture in dialog as expected 2014-02-04 03:50:00 +00:00
drdev
038418ca0d Fix selection after expand/collapse group 2014-02-04 03:29:34 +00:00
drdev
4aa2dd4471 Prevent auto-expanding groups when filters change
Fix how multi-type cards are grouped
2014-02-04 03:04:41 +00:00
drdev
075a01e3ae Fix display glitch 2014-02-04 02:14:55 +00:00
drdev
35ea4c2a50 Add grouping support for Deck editors 2014-02-04 01:56:46 +00:00
Sloth
bc851d0b51 - Fixed spells cast by AF Play effects (like Isochron Scepter) not triggering cast spell triggers (like storm). 2014-02-03 20:08:25 +00:00
Sloth
c11d353540 - Fixed Searing Blood. 2014-02-03 19:59:50 +00:00
Sloth
32e6da4a78 - Fixed Herald of Torment. 2014-02-03 19:40:26 +00:00
Chris
a96f9be77a Added new card names to changes.txt. 2014-02-03 13:52:35 +00:00
Maxmtg
04a38b79ac Old-format decks will load without exceptions 2014-02-03 08:16:44 +00:00
Maxmtg
a1886d51f8 mana effect - will give all colors to choose from if manaAb.expressChoice was empty 2014-02-03 06:41:14 +00:00
Agetian
9cd2bc595d - Fixed ManaEffect with AnyMana crashing, e.g. for Chromatic Star (temporarily fixed it by reverting the relevant part to the previous version, feel free to revert and commit a more optimal fix) 2014-02-03 06:25:51 +00:00
Maxmtg
e592d3bc56 add god-favoured general (script by moomarc) 2014-02-03 05:33:58 +00:00
Sol
1da213b157 - Improved Card Detail description of Buyback and Entwine 2014-02-03 04:43:37 +00:00
Sol
3f349d0eb3 - Improved Card Detail description of Suspend 2014-02-03 04:33:43 +00:00
Sol
84cac99a59 - Improved Card Detail description of Echo 2014-02-03 04:11:12 +00:00
Sol
de1b198f4c - Adding Mana symbols to Unnerving Assault 2014-02-03 03:22:25 +00:00
Sol
5954e7053c - Adding Mana symbol to Vigor Mortis 2014-02-03 02:46:02 +00:00
Maxmtg
1b4b9ad32b change zone... still didn't dare to change it =) 2014-02-02 18:11:32 +00:00
Agetian
394e96d051 - A little clarification in CHANGES.txt. 2014-02-02 15:42:16 +00:00
Chris
2c6151996e Added a fluff piece to the changes.txt file. 2014-02-02 15:22:36 +00:00
Maxmtg
5c195f3cea moved forge.net.protocol to .net project 2014-02-02 13:31:55 +00:00
Maxmtg
1af6d101e8 fix mana atoms (bug emerged with 24602) 2014-02-02 12:32:46 +00:00
Maxmtg
b6b220496b do not add target folder into svn 2014-02-02 11:17:49 +00:00
Maxmtg
ff92c4cf31 add forge.net project (to hold network protocol, FServer and similar things) 2014-02-02 11:16:39 +00:00
Maxmtg
12ed04a7cc non-supported cards will have a default image 2014-02-02 10:56:44 +00:00
Maxmtg
6575000c28 better loading of unsupported cards 2014-02-02 10:26:22 +00:00
Agetian
43f4e45827 - Some more error message mistype / spelling fixes. 2014-02-02 10:22:49 +00:00
Agetian
4b6e58ddf0 - A little mistype fix. 2014-02-02 10:19:59 +00:00
Maxmtg
0abdf7a3b5 CardDb - unsupported cards detection
CardPool - fix bug in decks save
2014-02-02 10:03:41 +00:00
Maxmtg
53e0a2ad37 createUnsuportedCard -> cardDb 2014-02-02 09:39:22 +00:00
Agetian
7e22527164 - Send unsupported card names to the console so that the user is aware of which cards failed to load when the decks were parsed. 2014-02-02 09:22:49 +00:00
Maxmtg
7e6928d700 Split Deck- storage and serializer parts into their own classes 2014-02-02 09:11:48 +00:00
Agetian
4fa37ac4b9 - Delete the unwanted file that got pulled into the last commit. 2014-02-02 08:51:34 +00:00
Maxmtg
bbe5e3c556 small tweaks 2014-02-02 08:51:03 +00:00
Agetian
4c7f16baf4 - Most random card pools (quest shop, quest starting pool, sealed deck, booster draft) will now generate only one kind of Zendikar lands (full art or standard art) at a time instead of mixed art of both kinds. 2014-02-02 08:44:46 +00:00
Maxmtg
cec3d26493 re-arranged deck serialization code to make it more "symmetric": the what is serialized somewhere tends to be de-serialized in the same class 2014-02-02 08:39:12 +00:00
Maxmtg
86edf9fd66 nothing important 2014-02-02 01:56:26 +00:00
Maxmtg
0adcfe55aa made deck color filters null-tolerant 2014-02-02 01:53:45 +00:00
Maxmtg
5a22e6a15d generated decks will not attempt to calculate their color 2014-02-02 01:49:00 +00:00
Maxmtg
4d833b6f38 getColor method moved to DeckProxy 2014-02-02 01:42:54 +00:00
Maxmtg
d05ecdf840 propper filtering of deck proxies 2014-02-02 01:40:32 +00:00
Maxmtg
1f66ec432d shifted MagicColors 1 bit to the right 2014-02-02 01:08:53 +00:00
drdev
2fcc5943fa Refactor Sections to Groups and create enum for grouping items 2014-02-02 00:53:03 +00:00
Maxmtg
08e909ca00 remove another isHuman check 2014-02-02 00:28:24 +00:00
Maxmtg
4c48e3e089 Reveal empty list won't crash the game 2014-02-02 00:05:41 +00:00
Maxmtg
d9d32f7508 fix hangs when playing spell from devmode that triggers something
remove unneeded code
2014-02-01 23:53:08 +00:00
Maxmtg
691c52fff9 Forge will no longer crash when there are decks with unsupported cards. These cards will appear in decks and games player starts as a dummy without cost, types, abilities, but with explanation in card's text 2014-02-01 21:43:42 +00:00
drdev
c3d7a9f075 Refactor so Image View shares sort order of List View 2014-02-01 21:16:12 +00:00
Maxmtg
da3b2c1dc1 "take priority" changed: now on priority SBA is checked, triggers are ordered, then player is asked for spell they want to play, then spell is played. Until player refuses to choose a spell, that means "I pass" 2014-02-01 20:43:20 +00:00
drdev
2ea69dde48 Show picture and details on hover for image view 2014-02-01 19:31:55 +00:00
Chris
7c66069ee3 Cleared out the changes.txt file, now ready for new material. 2014-02-01 14:36:16 +00:00
Sol
fd9ee6f4e1 - Fix Clean remembered line for Arbiter of the Ideal 2014-02-01 13:59:53 +00:00
Sloth
6314550377 - AI can now target with Tyrant of Discord. 2014-02-01 09:22:15 +00:00
Agetian
738fc7f52d - Fixed the Suspended Sentence precon description. 2014-02-01 07:02:12 +00:00
drdev
4c129ffee4 Add hover effect to ImageView 2014-02-01 04:47:14 +00:00
Sol
c1750989b7 - Fixing Asphyxiate cost 2014-02-01 01:53:21 +00:00
drdev
2b685348ab Add image view to item managers 2014-02-01 01:16:43 +00:00
Maxmtg
070fd7695a phase won't end if players are allowed to have priority and there are sim-stack entries to be added 2014-01-31 23:59:54 +00:00
Maxmtg
7cf1a52405 orderSimultaneousStackEntries only when player gains priority 2014-01-31 21:51:50 +00:00
Maxmtg
096f41172d remove some playertype checks 2014-01-31 19:57:59 +00:00
Chris
59665029d2 [maven-release-plugin] prepare for next development iteration 2014-01-31 16:18:08 +00:00
Chris
3249e844f9 [maven-release-plugin] prepare release forge-1.5.11 2014-01-31 16:17:57 +00:00
Chris
187c90c7df Preparing the changes.txt file for the next beta build and release. 2014-01-31 15:40:38 +00:00
Chris
d779034da8 Added new card names to changes.txt. 2014-01-31 15:36:38 +00:00
moomarc
429d8dbf30 - Added Searing Blood 2014-01-31 07:46:08 +00:00
Maxmtg
c5d35c9596 added a method: magic color form char 2014-01-31 05:55:30 +00:00
Maxmtg
23265d2623 moved a method from ai to effect class (because the code being moved does not take decisions) 2014-01-31 05:12:45 +00:00
drdev
4810518d74 Code cleanup 2014-01-31 00:22:14 +00:00
Sloth
1e389fdef8 - Added oracle texts to BNG cards. 2014-01-30 21:57:20 +00:00
Sloth
f31550732e - Added Forsaken Drifters, Servant of Tymaret and Warchanter of Mogis. 2014-01-30 21:45:54 +00:00
drdev
29876aeb28 Code cleanup 2014-01-30 20:50:23 +00:00
Sloth
a90c34aac4 - Updated some SVars. 2014-01-30 19:34:17 +00:00
moomarc
78456d6333 - Removed duplicate script for Sapphire Drake 2014-01-30 17:47:11 +00:00
moomarc
44edc80abc - BNG: Added Pain Seer 2014-01-30 17:45:55 +00:00
Sloth
3f6e6f54a1 - Updated some SVars. 2014-01-30 16:38:46 +00:00
moomarc
1e576cfde6 - BNG: Added Black Oak of Odunos 2014-01-30 16:15:59 +00:00
moomarc
07c4ab24e7 - BNG: Added Retraction Helix 2014-01-30 16:02:34 +00:00
Sloth
915a6a42c2 - Added Sphinx's Disciple and Stratus Walk. 2014-01-30 13:27:34 +00:00
Chris
5216b35688 Added new card names to changes.txt. 2014-01-30 13:25:04 +00:00
Maxmtg
3c53eec798 fix CostRemoveCounter for X counters 2014-01-30 07:10:13 +00:00
moomarc
db080d8b10 - BTG: Added Acolyte's Reward 2014-01-30 06:46:58 +00:00
drdev
04d129b8d8 Cleanup look of vanguard and planar deck panels 2014-01-30 02:30:11 +00:00
drdev
059e7f7920 Fix so first player's avatar has focus when Forge first opened 2014-01-30 01:58:49 +00:00
drdev
3bbfe87868 Code cleanup 2014-01-30 01:48:57 +00:00
Sloth
8cdc76427e - Added Kraken of the Straits and Meletis Astronomer. 2014-01-29 19:58:07 +00:00
Maxmtg
11976bf964 fix year of BNG set 2014-01-29 17:20:38 +00:00
Maxmtg
a5f4f78018 won't throw when not expected 2014-01-29 17:02:32 +00:00
Maxmtg
0621f2e242 refactor isAPermanentType to be less ugly 2014-01-29 16:59:06 +00:00
Chris
5538480862 minor edit. 2014-01-29 16:10:42 +00:00
Sloth
07f126cb52 - Updated Cloudstone Curio. 2014-01-29 15:47:45 +00:00
Sloth
a5263b1375 - Updated scripts of Blind Fury, Bloodletter Quill, Goblin Warchief and Opal Palace. 2014-01-29 15:11:25 +00:00
Sloth
6a52bdf10d - Merged BNG branch into trunk. 2014-01-29 15:00:25 +00:00
moomarc
ad38be34b7 - updated Changes.txt to reflect the latest state of the new constructed match screen 2014-01-29 11:05:33 +00:00
moomarc
a7785b0d1b - disabled checkboxes for Archenemy and Commander in constructed match setup.
- disabling a variant now updates the deck selector as appropriate
2014-01-29 10:57:16 +00:00
moomarc
26743d0ca0 - uploaded missing precon images and updated download URLs 2014-01-29 10:19:13 +00:00
moomarc
fccc8f129f - formalised/fixed some precon image names/links
- added Sol's python script for checking for missing precon images
2014-01-29 08:43:57 +00:00
Maxmtg
c1a11dd89a natural ordering of cards now includes art idnex 2014-01-29 06:49:48 +00:00
Agetian
0730e2d774 - Changed typeContains so that it can correctly identify Planes apart from Planeswalkers (fixes the card zoomer incorrectly showing planeswalkers rotated 90 degrees). 2014-01-29 06:46:51 +00:00
drdev
a8bf257391 Refactor Spell Shop prompts to only appear once no matter how many items are being bought/sold together 2014-01-28 22:51:56 +00:00
Agetian
86f6a9e3f1 - Attempt to fix image cache visualizing wrong card pictures for cards with multiple art. 2014-01-28 16:24:38 +00:00
Chris
41f3b22237 Added new card names to changes.txt. 2014-01-28 15:17:03 +00:00
Maxmtg
7ad5bfb581 rollback bug 2014-01-28 09:31:20 +00:00
Maxmtg
75e5c8d0ca fix precon loading issues 2014-01-28 07:32:10 +00:00
Maxmtg
1b9437ad32 fix artindex 2014-01-28 07:11:41 +00:00
Maxmtg
d5bde7570a correct picture 2014-01-28 06:58:48 +00:00
Maxmtg
a0d13578f3 artIndex inside the very cards is 1-based as well (no more adjustments of +/-1 needed) 2014-01-28 06:56:42 +00:00
Maxmtg
ed48547f36 very minor tweaks 2014-01-28 03:50:28 +00:00
Agetian
73b97fe151 - Fix for the previous commit. 2014-01-27 18:18:43 +00:00
Agetian
f21e1c3f8a - Make generated quest card pool indices 1-based. 2014-01-27 18:15:54 +00:00
Maxmtg
aa230be806 quest save indices 2014-01-27 17:53:35 +00:00
Maxmtg
26bb8e5009 art index bugfix for decks being loaded 2014-01-27 17:37:54 +00:00
Agetian
5322d5a9c2 - Reapply the previous NPE fix. 2014-01-27 16:06:36 +00:00
Agetian
0f5eb7d5f8 - An alternate NPE fix as suggested by Max. 2014-01-27 15:47:35 +00:00
Agetian
9379f7283c - Added descriptions to new precon decks.
- Fixed a NPE when trying to show the description of a precon in spell shop that lacks the description line in its metadata.
2014-01-27 15:45:24 +00:00
Maxmtg
29025cce61 step 2 on hidden origins 2014-01-27 08:48:28 +00:00
Maxmtg
7fd201d5e3 merge ChangeZoneEffect.java hidden part to make it invariant of player types (step 1) 2014-01-27 07:52:13 +00:00
moomarc
1f947d5781 - selecting a vanguard avatar updates the button label 2014-01-27 07:52:01 +00:00
Agetian
8dea53bcda - A better fix for the custom booster draft NPEing 2014-01-27 07:06:20 +00:00
Agetian
f5d43d0986 - Fix imports. 2014-01-27 06:43:13 +00:00
Agetian
cf67fb456e - Temporarily revert the previous fix, will recommit a better one soon. 2014-01-27 06:42:35 +00:00
moomarc
308814bdca - removed a few unnecessary lines 2014-01-27 05:51:06 +00:00
Agetian
149a9bd7b2 - Fix a crash when choosing Custom Draft.
- Note that this fix gets rid of a seemingly useless/buggy piece of code in GuiChoose, I can't remember why if there was a good reason for it in the first place. Please report if I broke something else by removing it.
2014-01-27 05:39:42 +00:00
Agetian
e16998beb5 - Fixed the ability to choose to use the deck-default Vanguard avatar. 2014-01-27 05:22:19 +00:00
Maxmtg
a9e12e38ef update something on ai/human playertype change 2014-01-26 22:26:25 +00:00
Maxmtg
9c7d25f49d arranged components inside a panel 2014-01-26 22:02:57 +00:00
Maxmtg
a52a1f5820 Threw in an idea for playerPanels in constructed screen 2014-01-26 18:54:10 +00:00
Agetian
e0c3244667 - Compilation fix 2014-01-26 17:17:33 +00:00
moomarc
32bf18781a - Vanguard matches now launched via constructed match screen 2014-01-26 15:51:43 +00:00
Chris
5bd88d468e Added new card names to changes.txt. 2014-01-26 14:59:07 +00:00
Agetian
e758e07658 - Fix a typo in image name. 2014-01-26 05:06:44 +00:00
swordshine
eb0ac0dde9 - BNG: Added Whims of the Fates 2014-01-26 04:56:56 +00:00
Maxmtg
c81a4d50dd auto set assignment - report to CHANGES.txt 2014-01-25 23:20:23 +00:00
Maxmtg
ae28b04615 A couple of intro decks from GPT times 2014-01-25 23:01:40 +00:00
Maxmtg
471623cd10 3 Starters from M11 added 2014-01-25 22:51:45 +00:00
Maxmtg
4378bfdbea release art indices on M14-precons 2014-01-25 22:42:12 +00:00
Maxmtg
8ff6cbd98e better art randomization for loaded decks 2014-01-25 22:36:15 +00:00
drdev
f4ffe22d61 Refactor ItemManager to switching between different views 2014-01-25 22:33:50 +00:00
Maxmtg
4573e30e73 refactor and remove some dependencies 2014-01-25 22:31:32 +00:00
Maxmtg
832b40e235 Add theros intro decks 2014-01-25 22:10:41 +00:00
Maxmtg
aec0bad678 more problems with art indices solved 2014-01-25 21:49:53 +00:00
Maxmtg
aeedde578d exclude schemes, planes etc from xitax-conversion 2014-01-25 21:40:29 +00:00
Maxmtg
dfebb08e1e fix deck loading problem (wrong editions were shown) 2014-01-25 21:30:56 +00:00
Maxmtg
dbf77d8548 M14 intro decks added to precons 2014-01-25 20:04:16 +00:00
Maxmtg
003c252d8a added option to use only core and expansion sets in deck importer, fixed a related bug 2014-01-25 19:54:32 +00:00
Maxmtg
1986df1961 Xitax's deck convertor integrated into Forge - applies on load to decks where no card has explicitly defined set. 2014-01-25 19:10:26 +00:00
Maxmtg
11c205f0c9 fix bugs in minimal set calculation.
added that column to itemmanager
2014-01-25 16:29:56 +00:00
drdev
7d2cc8fac5 Use arrow button scroller for Variant panel 2014-01-25 16:07:04 +00:00
drdev
863e7733e6 Prevent unmaximizing unless mouse moved 5 pixels on title bar 2014-01-25 16:04:37 +00:00
Maxmtg
e657a1b07d Moved ColumnDef to separate file 2014-01-25 15:58:16 +00:00
Maxmtg
2f29cf7755 edition cached in deckproxy 2014-01-25 15:50:56 +00:00
Maxmtg
6d34dbc286 refactored cardDb, added getEdition method to DeckProxy 2014-01-25 15:43:48 +00:00
Sloth
28f7f7baaf - Fixed Myr Reservoir. 2014-01-25 15:26:58 +00:00
Chris
f3b78b1aa3 Added a fluff piece to the changes.txt file.
Added new card names to changes.txt.
2014-01-25 14:31:19 +00:00
moomarc
ce82a93afe - Fixed starting a constructed match with no variants applied 2014-01-25 12:21:28 +00:00
Maxmtg
fe9a35b706 fix crash 2014-01-25 11:19:01 +00:00
moomarc
febba22f45 - Updated booster and precon lists 2014-01-25 09:56:33 +00:00
Maxmtg
2981668172 PreconDecks' folder property will be filled with their set (temporary) 2014-01-25 09:56:14 +00:00
Maxmtg
ed7369c98f store variants in an enumset 2014-01-25 09:45:47 +00:00
Maxmtg
b6409b11c7 fix NPE when you close any dialog prompting for random name options 2014-01-25 09:37:24 +00:00
Sloth
98c51e45b4 - Fixed Night Dealings. 2014-01-25 07:39:51 +00:00
drdev
9e6b82d10a Add "Folder" filter for DeckManagers 2014-01-25 06:39:48 +00:00
drdev
ee49314502 Fix so planar and scheme decks are saved in the right place 2014-01-25 06:14:17 +00:00
Agetian
29e73367a6 - Planes and phenomena will now be shown rotated 90 degrees in card zoomer mode.
- Updated the AI hints for Onakke Catacomb.
2014-01-25 06:04:50 +00:00
drdev
80aa655164 Add "Folder" column to DeckManagers 2014-01-25 05:50:12 +00:00
drdev
f8d7d060b9 Fix so saving a deck multiple time works
Fix so adding/removing decks in All Decks pane updates deck choosers on home screen
Fix so saving a deck in Deck Editor updates All Decks pane right away
Fix so typing in deck title field marks deck as having changes to save
2014-01-25 05:12:09 +00:00
drdev
b198e8a40e Code cleanup 2014-01-25 02:48:36 +00:00
drdev
a3fb6de6b3 Fix so decks inside subfolders support being deleted 2014-01-25 01:02:51 +00:00
moomarc
5252cca5c5 - small update to changes.txt 2014-01-24 20:33:23 +00:00
moomarc
4cab5da076 - Planechase variant now launched from Constructed match setup screen.
- moved random deck checkboxes into deck panel. Only show them when a random deck type is selected. They are still global settings however.
- when player 1 name is changed in preferences, switching back to constructed setup screen will reflect the change
2014-01-24 19:06:31 +00:00
Sloth
2cdf7030f8 - Made some CantBeBlocked keywords HIDDEN. 2014-01-24 15:37:54 +00:00
Chris
e30f8d0fb6 Preparing the changes.txt file for the next beta build and release. 2014-01-24 14:12:10 +00:00
Chris
22fe17048d Added new card names to changes.txt. 2014-01-24 14:07:24 +00:00
swordshine
3d70e2e9f6 - BNG: Added Gorgon's Head, Siren Song Lyre, and Thunderous Might 2014-01-24 07:20:41 +00:00
Maxmtg
f35dc1f30d apply random foil option for quest games 2014-01-24 06:58:52 +00:00
Maxmtg
34b36a8ef2 optimized formats calculation for decks,
added some comments to deckmanagers
2014-01-24 06:55:38 +00:00
Maxmtg
7c105ca398 Deckmanager : "Edit deck" button (icon) now opens decks from nested folders correctly 2014-01-24 06:43:22 +00:00
Maxmtg
2ec4873552 match itself no longer creates a separate thread, it is now caller's responsibility. Some callers (such as tests or simulations) may run their match in the same thread. 2014-01-24 06:32:45 +00:00
Maxmtg
aa4f3de30c done with Dependencies
added a class to hold all game rules (type, gamesToWinMatch, manaburn, etc)
2014-01-24 06:19:09 +00:00
Maxmtg
e4fb939e91 free blocks option moved to PlayerControllerHuman 2014-01-24 05:44:23 +00:00
Maxmtg
68acaf77c5 cut Dependencies on log entry type and AI profiles 2014-01-24 05:21:21 +00:00
Maxmtg
308039739f Random foiling moved to per-player settings, is set up before starting a match 2014-01-24 04:35:22 +00:00
swordshine
7687637783 - BNG: Added Astral Cornucopia, Fated Return, and Pillar of War 2014-01-24 01:06:57 +00:00
drdev
7b799eb531 Restore borders that were accidentally removed from scroll panes 2014-01-23 23:15:08 +00:00
drdev
9c37156906 Remove unnecessary setting of scroll pane border to null 2014-01-23 22:30:54 +00:00
Chris
32d6eb3ed5 Added new card names to changes.txt. 2014-01-23 14:36:05 +00:00
swordshine
faa2cef0bb - BNG: Added Shrike Harpy and Scourge of Skola Vale 2014-01-23 12:52:26 +00:00
Maxmtg
7e999ea4b3 Cut the dependence on images. Game module will store image keys, unaware of real image locations 2014-01-23 09:11:51 +00:00
swordshine
69c569c82b - added missing subtype arcane to Cranial Extraction 2014-01-23 08:34:49 +00:00
Agetian
bcc5f2c526 - A little fix for the fix. 2014-01-23 07:29:06 +00:00
Agetian
35bff6a89b - Use CLR_ZEBRA for the game log window for consistency with the deck editor tables and to make some themes (e.g. Firebloom) easier on the eyes. 2014-01-23 06:24:21 +00:00
Agetian
a26d45793e - Fix the formation of card choice window title for DigEffect. 2014-01-22 19:00:20 +00:00
Chris
d6577d1d72 Added new card names to changes.txt. 2014-01-22 15:00:22 +00:00
swordshine
e8aeec7f87 - BNG: Added Nessian Demolok and Oracle of Bones 2014-01-22 10:58:32 +00:00
Maxmtg
0b75a0ee8e fix duplication of copies brought by "bftb" 2014-01-22 07:31:52 +00:00
Maxmtg
4163c56502 back from the brink now brings back the cards (but created 2 copies for unknown reason) 2014-01-22 07:14:53 +00:00
drdev
37d45ffbeb Use base ZEBRA and tweak skins that would otherwise have unreadable text 2014-01-22 06:57:09 +00:00
drdev
f03a1397f1 Use dark ZEBRA instead of THEME2 as base background color for Item Manager tables 2014-01-22 06:44:32 +00:00
drdev
410b23974e Make alternating row colors more distinct and remove horizontal grid lines 2014-01-22 05:35:10 +00:00
Maxmtg
3366843ca1 added a method to reload a proxied object from storage (call this method after the object has been changed in deck editor) 2014-01-22 05:26:26 +00:00
Maxmtg
9bcc6cbc64 save/delete methods implemented on DeckProxy 2014-01-22 05:06:56 +00:00
drdev
9636e3d2e4 Prevent player column stretching up over variants panel when players added 2014-01-22 04:59:54 +00:00
drdev
140e44a3bd Add title to avatar selector
Return focus to proper player after avatar selector closed
2014-01-22 04:50:24 +00:00
Maxmtg
9f76a78b0a visbility fix 2014-01-22 04:36:36 +00:00
Maxmtg
dd6e8d0884 rename field 2014-01-22 04:35:08 +00:00
drdev
66b1a795c1 Make Quest Duels and Quest Challenges screens use FScrollPanel with arrow buttons
Prevent focusing arrow buttons
2014-01-22 04:29:19 +00:00
Maxmtg
8b8b9f0cf5 add methods to search for nested folders and create them 2014-01-22 04:22:31 +00:00
drdev
d5647c9e98 Move arrow buttons support to FScrollPane to allow better separation of FScrollPane and FScrollPanel 2014-01-22 04:09:38 +00:00
Maxmtg
5d1d77f4df generalize DeckProxy to manage Limited mode decks efficiently 2014-01-22 03:52:06 +00:00
drdev
7a193c0618 Fix so adding cards to sideboard in Quest Deck Editor properly removes them from inventory 2014-01-22 03:43:07 +00:00
drdev
1709ed07c7 Prevent weird diamond shaped scrollbar thumbs 2014-01-22 03:34:58 +00:00
drdev
23586a027e Make FLabel colors static 2014-01-22 03:21:45 +00:00
drdev
97c74ec2b1 Make scroll arrow buttons semi-transparent and more like FLabels 2014-01-22 03:14:23 +00:00
drdev
961461aa19 Dim scrollbars unless mouse over them 2014-01-22 02:28:52 +00:00
drdev
713a9ecb3b Prevent long deck name causing horizontal scroll arrow to appear 2014-01-22 00:29:36 +00:00
drdev
8a46d38914 Fix so previously played deck restored properly again 2014-01-22 00:12:44 +00:00
drdev
423033d2ac Code cleanup 2014-01-21 23:55:17 +00:00
Maxmtg
d01925107f fix label on 'Choose deck' button 2014-01-21 18:54:02 +00:00
Maxmtg
47a719147c load decks from subfolders
display formats, sb/main sizes
2014-01-21 18:38:17 +00:00
Agetian
556bb2caa7 - Mention the new random art option in CHANGES.txt. 2014-01-21 14:39:34 +00:00
Agetian
ec3813fb13 - Fix getCbRandomArtInPools returning the wrong checkbox.
- Some more renaming.
2014-01-21 14:31:53 +00:00
Agetian
926152940e - Better name for the random art in generated card pools option. 2014-01-21 14:23:46 +00:00
Agetian
7cf186952e - Added an option "Randomize card art in generated card pools" (enabled by default). When enabled, generates cards with different art in generated limited mode pools (sealed, booster draft, quest). When disabled, generates a certain predefined amount (30 for draft/sealed, 10 for quest shop) of lands, all with the same art. However, the art index will be randomized at every generation, thus still leaving some room for art randomization.
- Tweaked case of letters in some option names for consistency.
2014-01-21 14:17:01 +00:00
Chris
b2ccb5c689 Added new card names to changes.txt. 2014-01-21 13:51:48 +00:00
Agetian
3da6e688e4 - Fixed the "remove this card if not playing for ante" inversion causing the ante cards to be removed when playing for ante and not removed when not playing for ante. 2014-01-21 11:04:08 +00:00
Maxmtg
567ffe84cd DeckManagers use DeckProxies 2014-01-21 09:32:36 +00:00
swordshine
d97495ed41 - BNG: Added Heroes' Podium 2014-01-21 08:47:10 +00:00
Maxmtg
ceab176d9f improved the codestyle for Dependencies left from gui 2014-01-21 06:59:28 +00:00
Maxmtg
050887c53c fix compile problems 2014-01-21 06:34:03 +00:00
Maxmtg
2bc61a9e68 the great move 2014-01-21 06:27:36 +00:00
Maxmtg
6b1d9356f3 move authorization to see downface of cards to classes that won't move to different module 2014-01-21 06:20:08 +00:00
Maxmtg
1dbc34fe62 move command and constant, set up bridges for dependencies 2014-01-21 06:16:10 +00:00
Maxmtg
d93b041397 moved 2 files to ensure history is saved 2014-01-21 06:11:18 +00:00
Maxmtg
ecd14e1de4 Revert module separation to make another commit that would keep files' history 2014-01-21 06:05:34 +00:00
Agetian
923bddc90d - Generate 10 lands per art in sealed deck and booster draft modes if there is more than one art available (to avoid spamming the sideboarding window with many hundreds of basic lands) or just generate 30 basic lands of each type if there is only one art for the basic land in set (should still be reasonably enough for any sane limited play). 2014-01-21 05:58:56 +00:00
Maxmtg
97297f291f Game now compiles and runs! 2014-01-20 19:01:29 +00:00
Maxmtg
84850c76a9 restore compilation ability - 1 method left 2014-01-20 18:57:00 +00:00
Maxmtg
ca8c8d35d9 commited a workaround for preferences dependency 2014-01-20 18:44:43 +00:00
Maxmtg
18e96a1028 module separation in progress. Project cannot be compiled. Will restore in under an hour 2014-01-20 18:33:47 +00:00
Maxmtg
f27b3e469a last call to gui eliminated in game code, will try to extract module next 2014-01-20 18:01:12 +00:00
Maxmtg
7b47938062 remove method from AbilityUtils that adds reference from Game to Gui 2014-01-20 17:47:00 +00:00
swordshine
d9f3bb8bdd - BNG: Added Spirit of the Labyrinth 2014-01-20 14:32:16 +00:00
swordshine
8919532b84 - Convert Dredge to a replacement effect 2014-01-20 14:13:13 +00:00
Chris
481ac50726 Added new card names to changes.txt. 2014-01-20 12:45:54 +00:00
Maxmtg
731b3c8c36 DeckProxy (work in progress) 2014-01-20 09:13:36 +00:00
Maxmtg
8393bc02e4 remove ItemPoolView class, instead a read-only pool will be an instance of same class, but backed with an unmodifiable map 2014-01-20 09:13:13 +00:00
Maxmtg
85e5b0ff13 removed list or items from CardPool that had to be kept in sync with the main map, since no other class but ItemManagerModel used it (I wanted to do that for too long already!) 2014-01-20 08:49:52 +00:00
Maxmtg
3b8d779b27 reverted another change to core (the ordering of colors for random generation is implemented in a simpler way) 2014-01-20 08:20:28 +00:00
Maxmtg
89dcef5b67 moved convoke cards selection code from ManaCostBeingPaid to dedicated input for human player 2014-01-20 07:32:12 +00:00
moomarc
6ced3d38a4 - ComboBoxWrapper will retain whether its combobox is enabled or not with a skin change 2014-01-20 07:05:47 +00:00
swordshine
7fc97562b2 - BNG: Added Fanati of Xenagos and Flame-Wreathed Phoenix 2014-01-20 06:59:51 +00:00
moomarc
80372f40b5 - Added manual avatar selection to constructed match setup. 2014-01-20 06:23:57 +00:00
Sol
d9a5a27391 - Fixed Antes Panel from updating too early, and not updating when cards are added to the Ante zone. 2014-01-20 04:29:30 +00:00
Sol
8c73aa93c8 - Adding missed bazaar index file from last checking for new Quest item 2014-01-20 04:16:09 +00:00
drdev
59997f5cf2 Skin scrollbars 2014-01-20 03:03:43 +00:00
drdev
3ab7db6061 Change ItemTable border to match filter separators 2014-01-20 00:26:58 +00:00
drdev
3d2ba5a182 Tweak alt row color so it contrast better with inactive selection color 2014-01-20 00:18:56 +00:00
drdev
d7cb5165ef Skin ItemManagers 2014-01-20 00:14:40 +00:00
Agetian
3a89700d95 - The AI will add snow-covered lands to maindeck when building limited decks (sealed, draft). 2014-01-19 19:04:22 +00:00
drdev
f0ce722aa4 Don't retain selection when clicking to change column sort
Support showing color deck options in proper order with no column header
2014-01-19 18:15:02 +00:00
Chris
95709401a3 Added new card names to changes.txt. 2014-01-19 14:54:50 +00:00
swordshine
06f030ab73 - BNG: Added Champion of Stray Souls 2014-01-19 11:36:37 +00:00
Sloth
bd0dc07c43 - Fixed the keyword "At the beginning of your upkeep, sacrifice CARDNAME unless you pay ..." broken by the brackets {}. 2014-01-19 11:16:54 +00:00
swordshine
0d354f679c - BNG: Added Arbiter of the Ideal, Everflame Eidolon, and Mindreaver 2014-01-19 04:10:20 +00:00
Sol
7ed37ec5f8 - Created a new Bazaar item "Cash Stakes" to allow cards lost in ante to be available in the card shop immediately after the match 2014-01-19 03:15:24 +00:00
swordshine
fa40deaa11 - BNG: Added Tromokratis 2014-01-19 01:33:24 +00:00
Sloth
117d25b686 - Prevention for infinite loops in payManaCost. 2014-01-18 20:41:30 +00:00
Sloth
7491eb0076 - Fixed AI using Sphere of the Suns. 2014-01-18 20:28:50 +00:00
drdev
1acb9f8ab1 Fix so restored deck in view when application first opened
Prevent selection going out of view when table height changes
2014-01-18 20:04:23 +00:00
drdev
f87b262194 Fix so selection retained when changing sort
Fix so previously played deck restored on Constructed screen
2014-01-18 19:26:01 +00:00
Sloth
6c82cabe3a - Updated the quest deck Abe Sapien 3. 2014-01-18 18:25:34 +00:00
drdev
7e7969988a Rework player zone message building for ability effects 2014-01-18 17:06:18 +00:00
moomarc
4b51c24b66 - removed a console println 2014-01-18 15:28:40 +00:00
Sol
5e0ad38ffc - Cards lost to Ante in Quest mode will now appear in the Spell Shop for repurchase 2014-01-18 14:53:31 +00:00
Chris
56557101f3 Added new card names to changes.txt. 2014-01-18 13:36:12 +00:00
drdev
1d0cf99e48 Add View Deck button to make FDeckViewer more discoverable 2014-01-18 07:14:30 +00:00
drdev
2f5b534de6 Make quantity column narrower in FDeckViewer 2014-01-18 06:57:34 +00:00
drdev
00ae4d4824 Add card detail and picture panels to FDeckViewer 2014-01-18 06:42:25 +00:00
swordshine
af2a87a207 - BNG: Added Archetype of Courage, Archetype of Endurance, and Archetype of Imagination 2014-01-18 06:15:53 +00:00
drdev
72fe5ad996 Add FDeckViewer to display deck list when double-clicking
Improve support for opening modal dialog on top of another modal dialog
2014-01-18 05:50:11 +00:00
drdev
d00da28a3c Prevent cursor changing to Move every time you click a tab 2014-01-18 04:06:42 +00:00
drdev
81363bc537 Revert my revert of Max's revert (now that I see what I did wrong, sorry Max) 2014-01-18 03:49:45 +00:00
drdev
1e98cd4939 Fix so combo box constraints retained when switching skins 2014-01-18 03:23:36 +00:00
drdev
814a8976fb Show tooltip for truncated caption 2014-01-18 02:55:09 +00:00
drdev
86cea92ca1 Truncate item manager caption if not enough room for it 2014-01-18 02:53:32 +00:00
drdev
f9178dcff2 Fix so Constructed layout is at least usable at minimum resolution
Fix so Archenemy combo box supports skin changes
2014-01-18 02:33:36 +00:00
swordshine
fcc4b92efe - Fix last commit 2014-01-18 02:19:54 +00:00
swordshine
44f79a5db2 - BNG: Added Brimaz, King of Oreskos and Oreskos Sun Guide 2014-01-18 02:15:56 +00:00
drdev
034f235196 Show color and other columns for quest opponent decks 2014-01-18 01:26:59 +00:00
swordshine
a1e16543b2 - BNG: Added Temple of Enlightenment, Temple of Malice, and Temple of Plenty 2014-01-18 01:24:23 +00:00
drdev
8578e2a538 Fix case of random button 2014-01-18 01:21:37 +00:00
drdev
092d3dac40 Fix padding around new random name button 2014-01-18 01:19:15 +00:00
drdev
e9ddf8400b Revert Max's revert 2014-01-18 00:31:45 +00:00
Maxmtg
22b78255bb Storages must not be changed for UI needs 2014-01-17 17:23:50 +00:00
Agetian
2985b48432 - Updated Alara block precons with authentic basic land art [by Diogenes]. 2014-01-17 16:45:46 +00:00
Agetian
b249028b8e - Code simplification related to effective art index calculation. Deprecated art index 0 will now be treated as random art (please update your decks if they use art index 0). 2014-01-17 16:39:21 +00:00
Agetian
4cb2077e1a - The art indexes in .dck files now start at 1, not at 0, and thus match the image file name conventions (e.g. index 1 for a Forest matches Forest1.full.jpg). Note that if you had decks created with different art in mind prior to this beta, the art in your decks will be slightly off (index 0 will be treated as index 1 for the sake of limited backwards compatibility). Please update your decks if necessary. 2014-01-17 15:37:19 +00:00
Agetian
88b6223820 - Fix imports. 2014-01-17 15:05:11 +00:00
Agetian
0b4ef6b1e3 - Experimental: generate 50 basic lands for each art type in limited modes (Sealed Deck and Booster Draft, in particular). While this is not theoretically "unlimited" as the rules for limited modes provide, it guarantees at least 150-200 basic lands of each type for every set with three or four pictures, and at least 50 basic lands of each type even if there's only one art; this probably should be more than necessary for any sane limited play needs). Feedback welcome. 2014-01-17 15:04:12 +00:00
Agetian
8921ce16e6 - Basic NPE prevention in showCard, please review (not sure if there's a better way to prevent this NPE that would be more desirable). 2014-01-17 14:37:35 +00:00
Chris
f4606da30f Added new card names to changes.txt. 2014-01-17 14:14:50 +00:00
moomarc
41a5727a93 * Added a button to game setup to fetch random names 2014-01-17 13:14:14 +00:00
Maxmtg
19cec963d2 moved a gui reference out from costs 2014-01-17 08:32:39 +00:00
swordshine
156bc84c73 - Update more scripts 2014-01-17 07:52:22 +00:00
swordshine
eb98eaf5af - Update more scripts 2014-01-17 07:30:27 +00:00
swordshine
fb6d86af8a - Update scripts 2014-01-17 07:18:54 +00:00
drdev
39aa6bfda1 Add a little more padding between columns 2014-01-17 05:58:46 +00:00
drdev
ba59dfb01c Allow clicking anywhere inside player panel to select it
Distinguish selected player better using semi-transparent overlay for other panels
2014-01-17 05:55:30 +00:00
drdev
dd044c30e6 Prevent scroll arrow button sticking around when switching to other pages on home screen 2014-01-17 05:01:46 +00:00
drdev
f1fcc2c26b Increase gap below variants panel 2014-01-17 04:52:58 +00:00
drdev
c0e9d82ff6 Improve Constructed screen layout 2014-01-17 04:40:35 +00:00
Agetian
89598ab48c - Fixed apostrophes in Vintage format banned list definition. 2014-01-17 02:53:32 +00:00
drdev
586de9321a Remove copy decklist message 2014-01-17 01:16:05 +00:00
drdev
ae7244a10f Fix so toggle button filters appear properly when removed then re-added 2014-01-17 00:54:21 +00:00
drdev
d95fc7352f Cleanup unused references 2014-01-16 23:42:27 +00:00
moomarc
e4b7c92c6a - New constructed match setup improvements:
* Skinned player close button
2014-01-16 18:47:58 +00:00
drdev
a52a88e0cb Fix so DeckManagers list decks in nested folders
Update FDeckChooser to use DeckManager
2014-01-16 15:57:43 +00:00
moomarc
2ec3c08e72 - New constructed match setup improvements:
* changing the avatar in match setup or avatar preferences will now update each other to reflect the change to the preferences
2014-01-16 15:41:54 +00:00
Chris
b57e0a9848 Added new card names to changes.txt. 2014-01-16 13:49:09 +00:00
Chris
eabcc78815 Added new card names to changes.txt. 2014-01-16 13:46:49 +00:00
moomarc
1b7a2730f0 - New constructed match setup improvements:
* When a player is removed, the closest active player gains focus.
2014-01-16 11:17:18 +00:00
moomarc
caac7dc2ed - New player name generator applied 2014-01-16 10:17:38 +00:00
Maxmtg
d8eb3a5c7a DeckChooser will notify of their selection change 2014-01-16 06:40:22 +00:00
Agetian
5973d0d944 - Added some more information about the WIP functionality targeted for the upcoming beta. 2014-01-16 05:16:36 +00:00
Agetian
a512a7ab33 - Added some information about the WIP functionality targeted for the upcoming beta. 2014-01-16 05:11:10 +00:00
Agetian
e44cada50e - [Fixed] only save the information about the card art index to .dck file if the card has more than one possible art in set. 2014-01-16 04:45:17 +00:00
Sloth
49e734b66d - Added code support for Ragemonger. 2014-01-15 22:31:18 +00:00
Agetian
5c4efbcd04 - Minor code style tweak. 2014-01-15 16:01:57 +00:00
Agetian
f131aafa44 - Optimized the generation of basic lands with random art for the quest mode card pool and quest shop.
- Better name for a function to split a value into a predefined number of random groups.
2014-01-15 15:59:57 +00:00
Chris
f4263e943d Added new card names to changes.txt. 2014-01-15 14:07:07 +00:00
moomarc
45cd72f7dc - New constructed match setup improvements:
* avatar randomization now ensures duplicates aren't chosen
  * player names and avatars are now used for the match
2014-01-15 12:07:45 +00:00
Agetian
634289407b - Optimization of adding cards with random art index in CardPool. 2014-01-15 09:34:03 +00:00
moomarc
01ff93375a - New constructed match setup fixes:
* starting a game without preferences set no longer causes issues with avatars or name.
  * avatars are assigned avatars randomly (except for p1 and p2 who uses their preferences)
  * right clicking an avatar assigns a new random avatar
2014-01-15 08:10:55 +00:00
Agetian
c64ef5deed - Fix a crash in the multiple art processing code related to cards not assigned to any edition. 2014-01-15 07:50:27 +00:00
drdev
a5a0c510b5 More code cleanup 2014-01-15 05:48:53 +00:00
drdev
d7f8231717 More code cleanup 2014-01-15 05:20:00 +00:00
drdev
f0f7cfc839 Move remaining storage classes out of GUI 2014-01-15 05:14:05 +00:00
drdev
03870821c5 Code cleanup 2014-01-15 05:07:59 +00:00
Agetian
726084bc06 - Removed a check for variant card art index (all variant cards currently have only one art). 2014-01-15 05:02:50 +00:00
swordshine
cbbc341472 - Remove creature subtypes when gods are not creatures 2014-01-15 04:54:36 +00:00
drdev
664b707330 Fix so multicolor decks shown if only multicolor button checked 2014-01-15 02:21:26 +00:00
drdev
a47bba59d4 Fix so right-click updates table selection even if no context menu is shown 2014-01-15 02:10:36 +00:00
drdev
e84544afe4 Fix deck format default sort 2014-01-15 01:49:07 +00:00
drdev
cd505adc3d Fix padding around deck list buttons 2014-01-15 01:46:11 +00:00
drdev
5b1dd964ae Fix warnings 2014-01-15 01:33:07 +00:00
Sol
86ef4d0820 - Adding support for Gain Ownership cards, utilizing some of the same methods Ante is already using
- Added Darkpact
2014-01-15 01:06:14 +00:00
moomarc
966e593617 - Initial implementation of new constructed game setup 2014-01-14 16:04:10 +00:00
Agetian
eb4a355dc7 - Only write out the card art index to the deck if the art count for the card is greater than 1. 2014-01-14 15:42:47 +00:00
Agetian
d65b645a64 - Added a function to CardDb to get the number of different art available for a given card in a given set (to be used soon). 2014-01-14 15:27:33 +00:00
Chris
f899c6e58c Added new card names to changes.txt. 2014-01-14 14:30:40 +00:00
Agetian
a554599fd7 - Updated the Duel Decks: HvM edition file to correctly recognize 8 Mountains with different art (thanks Diogenes!) 2014-01-14 13:53:47 +00:00
swordshine
6046860e15 - BNG: Added Hero of Iroas
- Tweak Bestow
2014-01-14 12:15:24 +00:00
Maxmtg
1dda7d72a9 restore fields( in case GameFormat is serialized) 2014-01-14 07:35:07 +00:00
Maxmtg
3e2f3ab1c5 small bug 2014-01-14 07:22:39 +00:00
Maxmtg
286d7d043c Restored functionality : deckeditor shows all valid formats for decks in its list. 2014-01-14 07:21:49 +00:00
Maxmtg
563c089b9d now GameFormat supports "Restricted" cards (ex: power 9 in Vintage) 2014-01-14 07:13:48 +00:00
Agetian
ca103655bd - Optimize splitCardName by getting rid of continuous regular expression parsing. 2014-01-14 06:56:06 +00:00
Agetian
666fb6b35c - Cards with no art index specified will now have random art in every match (e.g. the line "20 Forest|7ED" provides twenty 7th Edition Forest cards each with randomized art).
- Some code reogranization related to card art randomization.
2014-01-14 06:02:06 +00:00
swordshine
d234975cde - BNG: Added Nessian Wilds Ravager and Pharagax Giant 2014-01-14 04:40:40 +00:00
drdev
13a6310ac0 Increase height of table rows slightly 2014-01-14 03:55:54 +00:00
drdev
62bfb66e11 Tweak spell shop columns 2014-01-14 03:39:48 +00:00
drdev
b776929fe8 Ignore sideboard when determining deck color (mostly for sake of limited) 2014-01-14 03:16:05 +00:00
drdev
273b7286cf Tweak cost column width 2014-01-14 02:27:45 +00:00
drdev
5bd450e0c9 Fix deck sort logic 2014-01-14 02:19:51 +00:00
drdev
777dff3c4b Adjust column widths 2014-01-14 02:06:15 +00:00
drdev
e3ddb00eee Left align table headers and ColorSetRenderer
Fix default sort for Format column
2014-01-14 01:54:45 +00:00
Maxmtg
870ffe3437 better color sorting, colorless decks icon is properly centered 2014-01-13 19:30:07 +00:00
Maxmtg
b8ea837204 align colors on center 2014-01-13 19:21:39 +00:00
Maxmtg
4a611fcf58 fix problems with deck format detection 2014-01-13 19:01:46 +00:00
Maxmtg
91be7b5a32 ColorSetRenderer for deck list 2014-01-13 18:46:40 +00:00
Maxmtg
42cf0def0f rollback changes to Deck, ManaCost, StorageBase, CardPool 2014-01-13 18:21:29 +00:00
Agetian
10fd05478d - Sealed Deck and Booster Draft limited modes now correctly handle and support cards with different art. 2014-01-13 14:46:12 +00:00
Chris
9fbc7a17c3 Added new card names to changes.txt. 2014-01-13 14:29:06 +00:00
Agetian
3cd105bac0 - Restore the functionality to show all cards from all sets and with different card art in the Constructed deck editor. This is temporarily the default and only behavior because it's the only way to properly support cards from multiple sets and with multiple card art indexes at the same time (given the note below).
- NOTE: The "Show unique cards only" functionality of the Constructed deck editor is currently broken (it was commented out by someone, possibly to be reworked later?)
2014-01-13 10:48:38 +00:00
swordshine
559c96412a - BNG: Added Ephara, God of the Polis 2014-01-13 09:56:21 +00:00
Agetian
ee5187c7e7 - Snow-covered lands have only one art (at least thus far). 2014-01-13 09:47:12 +00:00
Agetian
0cb7573494 - Generate basic land cards with random art in quest mode starting pool and quest mode card shop. 2014-01-13 09:46:35 +00:00
Agetian
0f514be363 - Added a simple Python tool to batch-scan a number of deck files for compatibility with Forge AI (use -h to get help). Right now it requires an unpacked 'cardsfolder' and a 'decks' folder in the same folder as the tool in order to operate.
- The -p option will only list decks that are playable by the AI, the -u option will only list decks that are unplayable by the AI (along with the lists of unplayable cards in each deck), the -d option will physically remove all deck files that are not compatible with the AI.
2014-01-13 09:10:34 +00:00
Agetian
ca07a8103a - Use the single-line 'for' style similar to other code places.
- Fix unused imports.
2014-01-13 08:55:47 +00:00
Agetian
4837bb5740 - Support distinction between cards with different card art indexes in the deck editors (the card index is now saved to .dck file and loaded from there if available).
- Remove a Singleton call from Match that was necessary to randomize card art (will be unsupported later and will not be necessary anyway once the true support is fully implemented).
- (WIP) Automatically randomize card art for cards that do not have the art index specified; for now only works for basic lands that were generated randomly (e.g. in random Constructed decks). Limited mode (sealed decks, draft decks, quest mode generated pool, quest mode spell shop) support for generating cards with different card art will hopefully follow soon.
2014-01-13 08:54:04 +00:00
drdev
fc7cc33111 Allow filtering and sorting decks based on name, color, and format
Refactor table column logic to be easier to implement new columns
Provide set sizes for certain columns so other columns can auto-size nicer
2014-01-13 08:35:47 +00:00
Maxmtg
ce341302a3 renamed createIngamePlayer method to minimize confusion 2014-01-13 06:23:32 +00:00
Maxmtg
4cb27c40ac (doubtful) replace xcantbe0 flag with restriction 2014-01-13 06:20:58 +00:00
swordshine
84e3fc1be9 - Fixed ChangeZoneAllEffect (r23967 broke many cards. Sloth, please review this fix) 2014-01-13 02:41:33 +00:00
Maxmtg
2522450930 Kill all gui 2014-01-12 18:32:49 +00:00
Maxmtg
e3e761ddc0 -1 gui call 2014-01-12 18:27:37 +00:00
Maxmtg
c95ee53137 remove gui calls from RepeatEachEffect.java 2014-01-12 18:22:26 +00:00
Maxmtg
7f9ef96178 wipe gui calls from effects 2014-01-12 18:16:03 +00:00
Maxmtg
95cbb2870c event phased had to be game event, not purely UI one 2014-01-12 17:34:40 +00:00
Maxmtg
17891a62fa remove global object reference from ZoneType 2014-01-12 17:20:45 +00:00
Maxmtg
f1ff893962 Human payment for costs now uses visitor pattern... as a result the concrete Cost classes no longer depend on UI 2014-01-12 16:15:41 +00:00
Chris
ce6d092b16 Cleared out the changes.txt file, now ready for new material. 2014-01-12 15:31:45 +00:00
Maxmtg
f6577217ff remove 'put' method from ItemPool , use 'add' instead 2014-01-12 12:22:12 +00:00
Maxmtg
283af44c48 removed deckbox 2014-01-12 12:16:51 +00:00
Maxmtg
1ea839f5d4 All costs except mana - AI uses decision, HumanPlay generates decision 2014-01-12 11:53:58 +00:00
drdev
4a3e07bbae Fix titles on dig effect prompts
Make reveal dialogs have more consistent titles
2014-01-11 18:26:43 +00:00
Chris
9ba8866b19 The year is now 2014. 2014-01-11 16:10:22 +00:00
Chris
5dbe15e7e0 [maven-release-plugin] prepare for next development iteration 2014-01-11 15:11:47 +00:00
Chris
cbf38aed9c [maven-release-plugin] prepare release forge-1.5.10 2014-01-11 15:11:36 +00:00
Chris
a8eb34d201 Added a fluff piece to the changes.txt file.
Preparing the changes.txt file for the next beta build and release.
2014-01-11 14:54:09 +00:00
drdev
496b12913d Prevent random crash that sometimes occurs when moving mouse over deck tables 2014-01-11 04:15:04 +00:00
drdev
be47a4bad3 Fix crash when clicking a card that can't be played 2014-01-11 01:56:14 +00:00
Sol
9d28e88a24 - Adding mana symbol to trigger description 2014-01-10 23:01:04 +00:00
Chris
d937a397a7 [maven-release-plugin] prepare for next development iteration 2014-01-10 14:01:32 +00:00
Chris
c9daed2056 [maven-release-plugin] prepare release forge-1.5.9 2014-01-10 14:01:20 +00:00
Chris
804c36e8b7 Preparing the changes.txt file for the next beta build and release. 2014-01-10 13:47:11 +00:00
drdev
7274d46865 Increase maximum list box auto size width 2014-01-10 04:36:12 +00:00
drdev
feaec2eb6d Auto size ListChooser dialogs to fit items if possible 2014-01-10 04:30:23 +00:00
drdev
753c0731a3 Fix performance of list boxes
Skin border for list on ListChooser dialogs
2014-01-10 03:01:52 +00:00
drdev
9a6b1822a9 Ensure application icon updated when skin changed 2014-01-10 01:17:04 +00:00
Sloth
db5e8316ec - Added AI logic to Ward Sliver. 2014-01-09 22:54:04 +00:00
drdev
513b9db9b5 Update CHANGES.txt 2014-01-09 19:33:26 +00:00
drdev
04248f0166 Refactor skinning logic to avoid caching components, allowing memory to be cleared and improving performance 2014-01-09 19:05:51 +00:00
Chris
c60c041012 Added new card names to changes.txt. 2014-01-09 15:02:49 +00:00
swordshine
f10ec1751f - Converted Old Man of the Sea
- Added Rootwater Matriarch
2014-01-09 13:46:14 +00:00
drdev
561d4deb32 Move graphics functions to static FSkin class 2014-01-09 03:19:30 +00:00
Sloth
3e552ac655 - Fixed Gilt-Leaf Archdruid. 2014-01-08 15:49:04 +00:00
Chris
12d8b289e5 Added new card names to changes.txt. 2014-01-08 14:54:57 +00:00
swordshine
bcf2abdbbc - Comment out unused complex counting methods 2014-01-08 14:28:56 +00:00
swordshine
8747c163ee - Updated scripts 2014-01-08 14:12:07 +00:00
swordshine
458bd8f964 - Updated scripts 2014-01-08 14:02:12 +00:00
swordshine
19a34ba730 - Updated scripts 2014-01-08 13:59:22 +00:00
swordshine
16294fe54a - Combine GreatestPowerYouControl and GreatestPowerYouDontControl 2014-01-08 13:55:22 +00:00
swordshine
76cdfa9759 - Converted Count$LowestLibrary to playXCount 2014-01-08 12:52:09 +00:00
swordshine
fc508b3a27 - Converted LowestLifeTotal 2014-01-08 12:32:02 +00:00
swordshine
a4c2fa3797 - Converted Psychic Transfer for multiplayer 2014-01-08 12:26:14 +00:00
swordshine
d1575996b4 - Converted Liu Bei, Lord of Shu to script 2014-01-08 11:38:50 +00:00
swordshine
75293299ed - Update scripts for AllM12Empires (should fix some corner cases) 2014-01-08 11:22:27 +00:00
swordshine
e50f31fb5f - Update scripts for Visions of Beyond 2014-01-08 11:05:26 +00:00
drdev
e47cdebf14 Prompt to save changes of previous deck before loading another deck 2014-01-08 04:17:26 +00:00
drdev
85eaf866d9 Avoid unnecessary reloading of deck if closing deck 2014-01-08 04:04:23 +00:00
swordshine
800a9b6854 - Added Storage Matrix 2014-01-08 03:54:03 +00:00
drdev
94eb94508c Reload deck if you choose not to save when leaving screen 2014-01-08 03:49:22 +00:00
drdev
02a49149fa Revert previous change 2014-01-08 03:11:47 +00:00
drdev
80308ab621 Prevent prompting to save deck when switching away from editor 2014-01-08 02:35:39 +00:00
drdev
cb055269d3 Prompt before leaving draft 2014-01-08 02:06:17 +00:00
drdev
1c6ccbb20a Make end turn button automatically pass priority if possible, and otherwise not end turn 2014-01-08 01:33:02 +00:00
drdev
764310e08b Fix issues with Dock button hovering 2014-01-08 00:42:54 +00:00
swordshine
673fd2dae3 - Fixed Sphinx of Jwar Isle 2014-01-07 05:35:58 +00:00
drdev
5fe57fcb11 Improve Sealed Deck button usability and support canceling prompt for number of booster packs 2014-01-07 04:08:31 +00:00
drdev
0a9617b0c4 Improve captions on Deck Editors 2014-01-07 03:29:46 +00:00
drdev
6b06a6f274 Prevent being able to add more cards to deck in quest deck editor than you own
Show inventory when in sideboard mode in quest deck editor
2014-01-07 01:59:53 +00:00
Chris
e5c3d4595c Added new card names to changes.txt. 2014-01-06 15:11:55 +00:00
swordshine
ab0a18cc64 - Added Static Orb 2014-01-06 11:46:41 +00:00
drdev
b534c4bbcd Make Deck Editor add/remove items smarter
Support adding X copies of card at a time using new GuiChoose.getInteger API
2014-01-06 08:51:30 +00:00
Agetian
0ee95fae76 - Randomize Card Art is no longer an option and is the default functionality instead (since there is no true support for distinguishing cards with different art in Forge, the game will randomize the card art that appears on cards with multiple available pictures, most notably basic lands, in each match). 2014-01-06 05:33:26 +00:00
Sol
dcb8c54e4c - Updaating Crypt Sliver description 2014-01-05 23:19:47 +00:00
drdev
802aed63df Refactor context menu building logic for editor tables 2014-01-05 22:26:01 +00:00
Sol
ba7cd330e6 - Updating trigger description for Mana Symbol 2014-01-05 22:23:48 +00:00
drdev
adeec5d6a9 Refactor incremental search (FindAsYouType) into ItemView class 2014-01-05 19:59:07 +00:00
drdev
8b0e20aa9e Create DeckManager 2014-01-05 19:21:15 +00:00
Agetian
a22a3dc849 - Fixed the description of Drain Life's spell ability (now it includes the first part of Oracle text which says to spend only black mana on X). 2014-01-05 19:05:35 +00:00
drdev
f220aef52a Make dialog backdrop appear over top of navigation bar 2014-01-05 17:15:20 +00:00
drdev
0175cc1a26 Prevent Alt key opening Forge menu while dialog open 2014-01-05 17:01:11 +00:00
drdev
59b6e092c9 Use new backdrop panel behind FDialog that's more transparent than overlay panel 2014-01-05 16:46:21 +00:00
drdev
ceb7b13a8e Refactor ComponentSkin.skins to compSkins in FSkin 2014-01-05 15:50:13 +00:00
Agetian
f68f8ba157 - Fix a bug that caused the deck subtitle not to be updated when the quest mode deck editor was turned to sideboard mode.
- Fix a bug that allowed the Import button to be used in quest mode deck editor.
2014-01-05 11:33:26 +00:00
drdev
19a58c309e Update CHANGES.txt 2014-01-05 08:34:57 +00:00
drdev
62479e1de6 Use reveal list dialog when revealing cards 2014-01-05 08:29:41 +00:00
drdev
04dde1c18d Fix crash when closing Spell Shop 2014-01-05 08:23:22 +00:00
Agetian
c7450034f5 - Restored the assign random card edition in randomly generated decks functionality. 2014-01-05 08:08:58 +00:00
drdev
9140dab9f1 Ensure animation card panels are disposed 2014-01-05 08:08:46 +00:00
drdev
8f4d3792c6 Dispose CardPanels as they are removed from CardPanelContainer 2014-01-05 08:00:30 +00:00
drdev
9d73236812 Support disposing of component skins when that component's dialog or screen is closed 2014-01-05 07:33:17 +00:00
swordshine
fbc0404f94 - Fixed CostRemoveCounter 2014-01-05 07:20:46 +00:00
Agetian
a2050ed3e6 - Removing an erroneously committed empty method. 2014-01-05 06:06:23 +00:00
drdev
0d2c13bf80 Clear images from cache when switching screens to reduce memory usage
Dispose of card panels when each game ends to reduce memory usage
2014-01-05 06:05:10 +00:00
Agetian
39061439dc - Restored the "Use Random Card Art" functionality. Implementation may be suboptimal with the latest changes in mind, feel free to reimplement in a better way if you know how (do NOT randomly remove though!). 2014-01-05 06:03:30 +00:00
drdev
d9e5d93265 Add standard API for setting default focus on FDialog 2014-01-05 03:30:24 +00:00
Agetian
b3bec906ef - Fix the bug that causes the sideboard not to work correctly when the sideboard is initially empty. 2014-01-04 18:38:50 +00:00
drdev
6d969373b9 Improve list reveal dialogs to allow Escape key and clicking close button
Improve title for "AI can't play cards well" dialog
2014-01-04 17:42:37 +00:00
drdev
c8ac6b9f2d Prevent ListChooser getting caught in infinite loop of reprompting 2014-01-04 16:27:24 +00:00
drdev
45d7b43aa6 Prevent filter widgets being hidden when opening Workshop 2014-01-04 05:44:33 +00:00
drdev
e954b40d3c Skin ListChooser 2014-01-04 05:27:28 +00:00
drdev
07f94f9d42 Ensure filters shown when Ctrl+F used to focus search field 2014-01-04 03:16:20 +00:00
drdev
18809bc918 Prevent updating item manager view if filters didn't change
Focus item manager when Deck Editor opened and when filters hidden/shown
2014-01-04 03:07:14 +00:00
drdev
85d32d49e2 Update CHANGES.txt 2014-01-04 02:55:55 +00:00
drdev
64459b414e Support hiding filters 2014-01-04 02:52:05 +00:00
drdev
f0ffeefe24 Move filter items to "Add" submenu and add "Reset Filters" option to main Filters menu 2014-01-03 03:47:19 +00:00
drdev
df022d5df7 Fix size of Commander Deck Editor button 2014-01-03 02:52:31 +00:00
drdev
a2b75c2072 Prevent showing File menu in Spell Shop 2014-01-03 02:46:53 +00:00
drdev
e7a9492200 Prevent crash when switching away from then back to Spell Shop 2014-01-03 02:25:42 +00:00
Agetian
3133dbf8b4 - A better fix for the attacker icon not appearing (getting rid of GUI calls in game.Combat and moving them over to gui.InputAttack) 2014-01-02 06:51:30 +00:00
Agetian
8fd2ffc93e - Getting rid of UI event duplication for declaration of blockers. 2014-01-02 06:42:58 +00:00
Agetian
39675d465d - Fixed the attacker icon not appearing automatically when a card with "attacks each turn if able" is declared.
- Added the UI event calls to Combat.addAttacker and Combat.addBlocker (this fixes the bug mentioned above). Not sure if this may lead to a duplication of events when declaring attackers/blockers manually, please review.
2014-01-02 06:40:28 +00:00
drdev
6e969ddaaa Update CHANGES.txt 2014-01-01 23:50:15 +00:00
drdev
a9f4009e43 Prevent marking deck as modified when changing sections
Update item manager caption when changing section
Show full catalog when in sideboard section
Avoid hiding title and save buttons when changing sections
2014-01-01 23:43:14 +00:00
drdev
b28b5e1ec9 Add File menu and keyboard shortcuts to Deck Editor 2013-12-31 21:44:45 +00:00
drdev
f4b9dd2f34 Update buttons on concede, restart, and exit prompts 2013-12-31 20:00:51 +00:00
drdev
40c651cd04 Apply anti-aliasing to FDialog border on Windows where it doesn't cause issues 2013-12-31 19:50:02 +00:00
drdev
b6d6da88e8 Skin more message/option/input dialogs
Prevent prompting twice when switching away from modified card in Workshop
2013-12-31 19:30:26 +00:00
drdev
7f95e5f583 Add prompts to confirm buying and selling items in Spell Shop 2013-12-31 17:16:40 +00:00
Maxmtg
e179aa404c setLife uses no gui 2013-12-31 05:53:06 +00:00
Maxmtg
4235a6906e remove isHuman from dig effect 2013-12-31 05:42:47 +00:00
Maxmtg
618cd6415d visited the quest ante code 2013-12-31 05:19:59 +00:00
Maxmtg
9d458ce8c5 still needed an originalDeck in RegisteredPlayer (to restart a match) 2013-12-31 05:08:02 +00:00
Sol
311306e986 - Fixing Mana symbol for Ogre Savant 2013-12-31 03:57:12 +00:00
Sol
a1688a7404 - Some cleanup with ante fixes. Quest mode midgame ante behavior restored in a more generalized fashion 2013-12-31 03:26:33 +00:00
Maxmtg
da421bc5f8 removed some duplicated code 2013-12-30 21:36:24 +00:00
Maxmtg
db8a71cca2 changed how ante works 2013-12-30 21:17:14 +00:00
swordshine
c8352e4968 - Remove phasedOut cards from combat 2013-12-30 14:30:34 +00:00
Chris
1d8aec2c3b Added new card names to changes.txt. 2013-12-30 13:56:56 +00:00
swordshine
459c2cfae4 - Missing Oracle 2013-12-30 01:41:38 +00:00
swordshine
32c52578e2 - Added Bludgeon Brawl 2013-12-30 00:56:08 +00:00
swordshine
eccadbf6fc - Added Nullstone Gargoyle 2013-12-30 00:39:50 +00:00
swordshine
8d99d2032d - Fixed Avacyn, Angel of Hope 2013-12-30 00:38:55 +00:00
Maxmtg
121db288c1 into single thread 2013-12-29 17:16:24 +00:00
Agetian
b8b3fff684 - Changed the representation of booleans in AI profile config files to match the preference file conventions.
- Sorted the options in AI profile config files in alphabetical order.
2013-12-29 08:17:32 +00:00
Agetian
33a08f67ec - Changed the way AI cheating works. The old option "Smooth AI Land" is now replaced with a global toggle "Enable AI Cheating" which allows the AI personalities (profiles) that have various cheat options set in them to utilize those cheat options to gain card advantage over the human.
- Currently there is only one cheat option (CHEAT_WITH_MANA_ON_SHUFFLE) which works like the old Smooth AI Land option by allowing the AI to cheat-shuffle appropriate lands into appropriate places in the library to minimize mana lock.
- Both default AI personalities (Default.ai and Reckless.ai) currently have CHEAT_WITH_MANA_ON_SHUFFLE set to True, which emulates the old Smooth AI Land functionality: if the "Enable AI Cheating" option is enabled, both profiles will cheat-shuffle. If disabled, neither profile will cheat-shuffle.
- Overall, it is now possible to set AI cheating on a per-profile basis and it's also possible to globally disable AI cheating even for personalities that normally do cheat.
- When adding new AI cheating options, don't forget to check for the value of the global toggle which is FPref.UI_ENABLE_AI_CHEATS.
2013-12-29 07:42:07 +00:00
Agetian
dffc1b2a42 - NetBeans settings loss related to expanding tabs is killing me :\ 2013-12-29 06:19:01 +00:00
Agetian
55aa4350b9 - Fixed the Phasing icon not appearing immediately when the card phases in/out.
- Added support for an additional sound effect that is played when a card phases in/out (phasing.wav is played if available).
- Added the "CHEAT_WITH_MANA_ON_SHUFFLE" option to the stock AI profiles (not used yet, to be implemented soon).
- Organized the AI profile config files a little bit by sorting the options.
2013-12-29 06:13:42 +00:00
drdev
40bfde20fa Cleanup Quest and Deck lister display
Fix Quest renaming
2013-12-28 22:28:59 +00:00
drdev
8ceda2b07b Use skinned dialog for new quest dialogs 2013-12-28 20:57:34 +00:00
drdev
a592a44647 Support input FOptionPane 2013-12-28 20:23:41 +00:00
Chris
2a9b59f935 Added new card names to changes.txt. 2013-12-28 14:50:01 +00:00
swordshine
5ed0549dc4 - Fixed devModePlaneswalkTo 2013-12-28 10:29:02 +00:00
swordshine
158da7f60f - Fixed Phyrexian Dreadnought 2013-12-28 06:56:34 +00:00
swordshine
58e529097f - Added Debt of Loyalty, Matopi Golem, Skeleton Scavengers, and Soldevi Sentry 2013-12-28 05:08:39 +00:00
Maxmtg
2de5beb6d1 something is no longer wrong with 2 piles separation in fact or fiction 2013-12-28 00:17:50 +00:00
Chris
cb5df407fd Cleared out the changes.txt file, now ready for new material. 2013-12-27 22:02:48 +00:00
Chris
8fd0e873c7 [maven-release-plugin] prepare for next development iteration 2013-12-27 17:42:27 +00:00
Chris
fe9ff2f4f9 [maven-release-plugin] prepare release forge-1.5.8 2013-12-27 17:42:15 +00:00
Chris
bf05d850fb Preparing the changes.txt file for the next beta build and release. 2013-12-27 17:29:36 +00:00
Chris
266cbd5dde Added new card names to changes.txt. 2013-12-27 17:26:08 +00:00
swordshine
e222c0f7a9 - Added Pools of Becoming 2013-12-27 12:59:31 +00:00
swordshine
12e80be6d1 - Fix NewGame trigger not working 2013-12-27 12:31:21 +00:00
swordshine
5efb2c2e40 - Fixed DigEffect 2013-12-27 11:34:05 +00:00
swordshine
2e574caac6 - Another fix to avoid overriding 2013-12-27 10:32:07 +00:00
swordshine
74b7a40763 -Fixed Selesnya Loft Gardens 2013-12-27 10:30:32 +00:00
Maxmtg
1ede72b64a ai cost decisions now use visitors 2013-12-27 09:26:39 +00:00
Maxmtg
c45fe42129 prerequisites to pay costs using visitor pattern (to remove AI decision-making and gui-specific inputs from the concrete cost classes) 2013-12-27 08:42:44 +00:00
drdev
ddaffcf7a5 Prevent glitchy dialog on Linux
Transition more message/confirm dialogs to skinned look
2013-12-27 05:59:29 +00:00
swordshine
0d28620ee3 - Fixed a typo in Journey of Discovery 2013-12-27 00:28:23 +00:00
Chris
bbab149d3c Added new card names to changes.txt. 2013-12-26 14:36:00 +00:00
Maxmtg
d4e193f39a ensure that limited games won't pop the AI cannot play cards window 2013-12-26 07:25:16 +00:00
swordshine
bd4dd064be - BNG: Added Kiora, the Crashing Wave 2013-12-26 05:47:19 +00:00
Agetian
2efb70e3b5 - Stupid NetBeans space/tab settings were broken again, fixing. 2013-12-26 05:33:47 +00:00
Agetian
92668c3a2a - Adding an AI profile property for cheat shuffling (to be used soon). 2013-12-26 05:31:31 +00:00
Agetian
3915c3ac2c - Fix the dialog window popping up about unplayable cards in the AI deck in Sealed Deck, Booster Draft, and Quest modes. 2013-12-26 05:22:46 +00:00
Chris
51e521fbb3 Added new card names to changes.txt. 2013-12-25 15:03:12 +00:00
Maxmtg
a81a666b81 move haunt Ai to a separate file
remove isComputer method
2013-12-25 08:21:25 +00:00
swordshine
69a9f0fe43 - Added Plunge into Darkness 2013-12-25 08:14:19 +00:00
Maxmtg
9262ad4d82 make newGame method private, non-static 2013-12-25 08:07:33 +00:00
Maxmtg
b6270a71bb dissolve gameNew, move ai cheating to that very class, improve ante messages
remove lose by milling option
2013-12-25 08:03:20 +00:00
Maxmtg
e31d2ae0d1 fix proliferate not working on creatures 2013-12-25 05:49:00 +00:00
Maxmtg
77510ac6e3 arrange inputs hierarchy 2013-12-25 05:42:49 +00:00
swordshine
0f1e88bb25 - Converted Amplify to replacement effects 2013-12-25 04:50:26 +00:00
swordshine
c5a42f03e6 - Update more scripts 2013-12-25 04:07:37 +00:00
swordshine
de1124c19e - Update 3 scripts 2013-12-25 00:33:58 +00:00
drdev
60ba4bfec6 Add back FDialog custom paint code 2013-12-24 19:06:10 +00:00
drdev
6372242b0b Temporarily comment out FDialog custom paint code 2013-12-24 19:05:17 +00:00
drdev
9a8c4a1c8c Make Left/Right/Home/End keys shift focus as expected between buttons in FOptionPanes 2013-12-24 18:50:09 +00:00
drdev
891db98700 Close FDialogs when Escape key pressed 2013-12-24 18:38:11 +00:00
drdev
f7d1b73a6c Move setShape call from paint to resize listener rather to hopefully fix Linux issue
Make FDialog border non-rounded if setShape isn't supported (Mac)
2013-12-24 18:11:09 +00:00
swordshine
7a906fdc03 - Update more scripts 2013-12-24 13:21:40 +00:00
swordshine
e7046c9bf5 - Update more scripts 2013-12-24 10:54:44 +00:00
swordshine
8d87b0256b - Update more scripts 2013-12-24 10:30:26 +00:00
Maxmtg
802aa167a9 Added input to select both players and cards for tokens created by Hero of Bladehold 2013-12-24 09:23:14 +00:00
swordshine
b83bd41716 - Update more scripts 2013-12-24 07:06:01 +00:00
swordshine
7c8cb3053a - Update more scripts 2013-12-24 06:24:29 +00:00
swordshine
7c9fb9f6b4 - Update some scripts 2013-12-24 05:21:11 +00:00
swordshine
68989d3e31 - Cleanup 2013-12-24 02:08:18 +00:00
swordshine
51a0745348 - Refactored Entwine ability 2013-12-24 02:06:22 +00:00
drdev
3505a2bcd5 Add FOptionPane
Make FDialog title use skinned font
2013-12-23 23:05:17 +00:00
drdev
0581a5e42b Fix Mac FDialog crash properly 2013-12-23 21:16:40 +00:00
drdev
7ea839334c Fix setShape issue without try/catch block 2013-12-23 17:06:39 +00:00
drdev
be7cce4300 Prevent FDialog crashing on Mac. 2013-12-23 16:46:03 +00:00
drdev
c78251a90e Improve appearance of FDialogs
Make CardListViewer use FDialog and display on screen
2013-12-23 10:45:31 +00:00
Maxmtg
c3c8fd7186 improve random choice of N cards
remove gui calls from choosePile effect
2013-12-23 09:05:50 +00:00
drdev
3d97768f71 Code cleanup 2013-12-23 04:52:08 +00:00
swordshine
f2c93cb5dd - Fixed Maelstrom Nexus and similar cards 2013-12-23 04:37:59 +00:00
drdev
689ef0cf8a Make selection after adding/removing cards from deck behave as expected and improve performance of adding multiple cards at once
Support Ctrl+F in Current Deck pane
Prevent losing multi-selection when using Left/Right arrow keys to switch focus between Catalog and Deck
2013-12-23 03:03:33 +00:00
swordshine
77de4abfd1 - Fixed Battalion Triggers check requirements when resolving 2013-12-23 02:58:10 +00:00
Maxmtg
dc16b75c94 minimize need to player type checks 2013-12-22 23:13:07 +00:00
Maxmtg
3a0fdb4d4f remove gui calls from untap 2013-12-22 22:50:01 +00:00
Maxmtg
32b0f8333d remove calls to gui from game code 2013-12-22 22:36:36 +00:00
drdev
446dfd2257 Add +/- buttons to Deck Editor Quantity column 2013-12-22 20:43:11 +00:00
drdev
faa9947974 Fix so tooltips don't always appear for all columns of table 2013-12-22 19:09:33 +00:00
drdev
c75971e8c5 Get favorites feature working again 2013-12-22 18:20:36 +00:00
Maxmtg
2c850cbe53 clash - gui call replaced by gameAction.notifyOfValue
proliferate - single executor for human and AI, chooseProliferation method added to PlayerController
2013-12-22 18:04:00 +00:00
Chris
a060f5f322 Added new card names to changes.txt. 2013-12-22 15:00:26 +00:00
Maxmtg
5367811a33 moved card prefrences to gui 2013-12-22 13:08:28 +00:00
Maxmtg
7d41ae1331 revert unwanted "preferences" from game-core.
remove gui calls
2013-12-22 13:06:27 +00:00
swordshine
f55abfa576 - Added Tariff 2013-12-22 13:03:31 +00:00
drdev
79b8340b80 Fix bug with sort priorities getting messed up 2013-12-22 10:21:53 +00:00
drdev
e0b7034f97 Fix so favorites appear on top by default when column first clicked 2013-12-22 10:03:57 +00:00
drdev
b79876c959 Support hiding Favorite column from Editor Preferences pane 2013-12-22 09:39:00 +00:00
drdev
e159c25f6d Improve appearance of star icons 2013-12-22 09:07:47 +00:00
drdev
d16eedd1b3 Update CHANGES.txt to mention Deck Editor usability improvements 2013-12-22 08:41:08 +00:00
drdev
a062d61c8d Support marking cards as favorites in catalog 2013-12-22 08:39:20 +00:00
drdev
471e5b5399 Support mana icons 13, 14, 17, 18, 19
Reorganize a couple mana symbols in skin
2013-12-21 22:29:27 +00:00
drdev
577f94a67f Fix firebloom and simpsons mana icons 2013-12-21 22:01:29 +00:00
drdev
f7e05491da Prevent 16/17 mana cost icons being cut off 2013-12-21 21:56:09 +00:00
drdev
2771b4fe1f Fix mana cost rendering in table
Start favorites column
2013-12-21 19:43:52 +00:00
Sol
b5830fac6e - Command Tower taps for Controller Commander's CI, not the owners. 2013-12-21 17:34:25 +00:00
drdev
d8511c92c0 Fix so Pack and Multicolor filter buttons work correctly when only they are toggled off 2013-12-21 17:33:30 +00:00
Sloth
1ea41edd6a - Added the hard quest opponent Abe Sapien 3. 2013-12-21 07:28:38 +00:00
swordshine
4f79f92940 - Converted Sphinx of Jwar Isle to script 2013-12-21 03:16:16 +00:00
swordshine
e13865a966 - Converted "You may discard CARDNAME any time you could cast an instant." to script 2013-12-21 01:08:36 +00:00
Maxmtg
05e1161632 this should prevent some NPE 2013-12-20 20:40:33 +00:00
Sloth
feae947742 - Fixed Tsabo's Decree. 2013-12-20 16:13:44 +00:00
Sloth
9ef69149ee - Fixed preventRunAwayActivations to work with temporary abilities.
- AI updates and code cleanup.
2013-12-20 15:45:27 +00:00
Chris
04ca668807 dded new card names to changes.txt. 2013-12-20 14:46:42 +00:00
Sloth
489af75669 - Updated the quest deck Optimus Prime 3. 2013-12-20 14:44:19 +00:00
swordshine
c8e1aff861 - Update mana replacements in ComputerUtilMana 2013-12-20 08:49:00 +00:00
Maxmtg
373bcd0351 commit 24000 FTW! (remove gui calls from untap) 2013-12-20 08:40:08 +00:00
swordshine
c08e984e35 - Added Hall of Gemstone 2013-12-20 08:06:33 +00:00
drdev
5dee59e8e5 Make Pack stat label appear in color and cmc filters too so each toggle button filter has the same button count and works the same way when left clicking other filters. 2013-12-20 04:57:04 +00:00
drdev
350dbeb12f Add back Pack filter button to Spell Shop 2013-12-20 03:35:32 +00:00
swordshine
fdc2eb66aa - Added Scrambleverse 2013-12-20 00:33:16 +00:00
Maxmtg
ef332e4568 move HumanPlay, PlayerControllerHuman to forge.gui.player package 2013-12-20 00:22:20 +00:00
Maxmtg
93c7d28714 moved limited play to a different package (since they don't belong to game module) 2013-12-19 23:17:04 +00:00
Maxmtg
5d8225f041 eliminate gui calls 2013-12-19 23:15:32 +00:00
Maxmtg
b501c76f7c some more gui calls gone 2013-12-19 23:03:28 +00:00
Maxmtg
97526110d0 removing direct references from game to gui 2013-12-19 22:20:28 +00:00
drdev
f8a4c5db92 Fix Statistics tooltips 2013-12-19 06:22:00 +00:00
drdev
2093d23e24 Add Card CMC toggle button filters
Enhance Filters menu to support adding Color and Type filters
2013-12-19 06:14:49 +00:00
drdev
7e32b752a0 Refactor ItemTable into ItemListView and ItemView base class
Add combo box for selecting view
2013-12-19 04:29:59 +00:00
Sloth
4b02708bb0 - Updated the Sherlock Holmes quest decks. 2013-12-18 15:47:50 +00:00
Chris
9684d69bd1 Added new card names to changes.txt. 2013-12-18 15:07:57 +00:00
swordshine
f6d07944d8 - Converted TokenDoubler to script 2013-12-18 11:38:07 +00:00
swordshine
46796041c7 - Added Primal Vigor 2013-12-18 10:33:44 +00:00
swordshine
196a782f8c - Update 3 scripts 2013-12-18 05:25:41 +00:00
swordshine
4839d05b70 - Update two scripts 2013-12-18 04:27:09 +00:00
drdev
d8b6140692 Refactor previous fix 2013-12-17 22:14:17 +00:00
Sloth
040e51f124 - Fixed two scripts missing a nonToken restriction. 2013-12-17 22:11:07 +00:00
drdev
a24161db2b Support search for non-card items in SpellShop 2013-12-17 21:42:21 +00:00
drdev
117501c511 Fix SpellShop item manager to not crash and support filtering 2013-12-17 21:21:35 +00:00
moomarc
1c122b99e9 - Fixed Rotted Ones Lay Siege 2013-12-17 10:24:15 +00:00
drdev
6970c1e7cf Fix ratio in Current Deck item manager
Add captions before ratios in item managers
Fix padding around header in Current Deck pane
2013-12-16 19:28:33 +00:00
drdev
d56cecdba4 Integrate filters into ItemManager 2013-12-16 09:10:40 +00:00
Sloth
8ebf514dab - Fixed possible NPE in ChooseSourceAi. 2013-12-15 22:36:14 +00:00
Sol
1376f0dde2 - Fixing mana symbols for a few devotion cards 2013-12-15 16:50:42 +00:00
Sloth
dbacf597da - Updated some quest decks. 2013-12-15 11:17:58 +00:00
Sloth
87eaf96fa1 - Fixed Reparations triggering on creatures not on the battlefield. 2013-12-14 21:21:09 +00:00
Sloth
13db59aac8 - Fixed possible source for NPE's in RemoveFromCombatEffect. 2013-12-14 20:56:21 +00:00
Sol
9a46768de6 - Starke is already removed from combat by the GainControl code, and whatever he's destroying should be removed by the destruction code. 2013-12-14 19:02:50 +00:00
Chris
3c5010cebf Cleared out the changes.txt file, now ready for new material. 2013-12-14 15:35:37 +00:00
Sloth
d25b197c09 - Fixed Legion's Initiative. 2013-12-14 07:52:54 +00:00
Chris
b892cfa029 [maven-release-plugin] prepare for next development iteration 2013-12-13 15:54:18 +00:00
Chris
23a012364b [maven-release-plugin] prepare release forge-1.5.7 2013-12-13 15:54:07 +00:00
Chris
ed0f3600f5 Preparing the changes.txt file for the next beta build and release. 2013-12-13 15:39:28 +00:00
drdev
dd4e250432 Fix compilation problem 2013-12-13 08:05:42 +00:00
drdev
2ffb71e053 Code cleanup 2013-12-13 08:04:36 +00:00
Maxmtg
be25cf699c removed input play or draw (for being a special case of InputConfirm)
added optional param to chooseCreatureType
2013-12-13 07:51:27 +00:00
drdev
1cc15fe50c Update CHANGES.txt 2013-12-13 07:40:21 +00:00
drdev
a04e5c1da2 Skin FDialog 2013-12-13 07:29:55 +00:00
Maxmtg
5b18ba9058 Cost.chooseXValue moved to member of CostPart 2013-12-13 07:10:23 +00:00
Maxmtg
753f4d989b renamed InputYesOrNo, removed static methods from it, added a method to confirmPayment to PlayerController 2013-12-13 07:01:30 +00:00
Maxmtg
8960fa20c3 added showAndWait method to InputSyncronizedBase - it holds the long chain of calls to reach Control's input queue. 2013-12-13 06:25:55 +00:00
drdev
38344027ca Show cost payment prompts during ability resolve using Prompt pane instead of a dialog 2013-12-13 06:17:03 +00:00
drdev
90ff1abab8 Code cleanup 2013-12-13 05:48:35 +00:00
drdev
7fe3799ea6 Code cleanup 2013-12-13 05:19:09 +00:00
drdev
5ed6fba085 Code cleanup 2013-12-13 05:04:27 +00:00
drdev
6c3573d854 Use Prompt pane instead of dialogs for cost-related questions 2013-12-13 04:48:31 +00:00
drdev
d00aeffbda Add static "ask" API to InputYesOrNo 2013-12-13 03:07:15 +00:00
drdev
e247b8e368 Update CHANGES.txt 2013-12-13 02:40:36 +00:00
drdev
3d54fd3b7b Prompt Yes or No for optional triggered abilities using Prompt pane rather than dialog 2013-12-13 02:37:09 +00:00
drdev
b19b5daf48 Refactor confirmTrigger into controller class 2013-12-13 02:02:54 +00:00
drdev
f48952aa94 Added notes to CHANGES.txt 2013-12-13 01:06:37 +00:00
drdev
e8d4433bb3 Tweak follow up logic after auto payment 2013-12-13 00:59:45 +00:00
drdev
84b0e17572 Fix so you're still prompted for a color when paying cards that care what colors are spent to cast them 2013-12-12 14:53:17 +00:00
drdev
e4ba6f56dd Fix so GUI thread not locked while waiting for Auto payment 2013-12-12 12:57:08 +00:00
drdev
bdfd2e9adf Avoid prompting for color when using "Add one mana of any color" effect to pay for a colorless cost 2013-12-12 12:43:50 +00:00
drdev
849655cb2f Fix so Auto payment occurs in Game thread 2013-12-12 11:49:50 +00:00
swordshine
1c391a7c7e - Fixed Varolz 2013-12-12 11:36:41 +00:00
Sloth
cf1990ddf7 - Fixed Ragged Veins. 2013-12-11 15:04:35 +00:00
drdev
fad3ea497d Code cleanup 2013-12-11 04:05:17 +00:00
drdev
f586525a61 Ensure index in hand zone updated when card drag dropped into new position 2013-12-10 16:27:11 +00:00
Chris
832a290778 Added new card names to changes.txt. 2013-12-10 13:26:11 +00:00
swordshine
ecbba9f1c0 - Added Sheltering Ancient 2013-12-10 13:10:32 +00:00
drdev
afeb4eb815 Fix so canceled spells return to same place in your hand as they were cast from 2013-12-10 02:50:24 +00:00
drdev
d0b5e78f7a Code cleanup 2013-12-10 01:13:34 +00:00
drdev
1837234650 Revert change that prevented a spell moving on the stack until after prerequisites were met 2013-12-10 00:24:21 +00:00
drdev
c65031ea31 Avoid removing source card from lists 2013-12-10 00:17:46 +00:00
Chris
14a7176325 Added a new piece to the Known Issues section. 2013-12-09 19:23:32 +00:00
swordshine
a0c3a1eb05 - Clones are able to copy LevelUp abilities 2013-12-09 04:03:02 +00:00
drdev
f7af3865b7 Allow canceling discarding cards to pay cost 2013-12-09 00:03:18 +00:00
drdev
51a0c08042 Prevent card exiled to pay its own additional cost 2013-12-08 23:47:53 +00:00
drdev
abc2118a77 Code cleanup 2013-12-08 23:18:40 +00:00
drdev
776ac3ee2b Prevent card discarded to pay its own additional cost 2013-12-08 23:12:07 +00:00
drdev
63eaec8348 Code cleanup 2013-12-08 22:50:59 +00:00
drdev
ddb8464c1c Code cleanup 2013-12-08 22:22:44 +00:00
drdev
29ef7a7d3b Code cleanup 2013-12-08 22:14:19 +00:00
drdev
49aa0cc459 Code cleanup 2013-12-08 22:10:03 +00:00
drdev
cdf1b44cb0 Check static abilities when adding card to zone from nowhere (such as using Dev tools) 2013-12-08 18:47:31 +00:00
drdev
61fe9f2282 Code cleanup 2013-12-08 17:49:49 +00:00
Maxmtg
0b9d254e12 fix NPE caused by null predicate 2013-12-07 23:12:37 +00:00
Maxmtg
77b241eb68 remove another toString method from manacost, update references to use better ways 2013-12-07 22:18:52 +00:00
Sloth
b98ae5844c - Fixed type of Stern Judge. 2013-12-07 21:28:45 +00:00
drdev
507ce6f220 Ensure OK button gets initial focus in DualListBox if remainingSources matches source list count 2013-12-07 20:08:03 +00:00
drdev
4d2a27f7e8 Prevent adding cards in DualListBox using spacebar if add button disabled
Prevent selecting cards to discard with Abandon Hope if 0 cards should have been allowed
2013-12-07 19:54:57 +00:00
drdev
a90841a6b6 Fix conditions under which Auto button enabled on DualListBox 2013-12-07 19:39:33 +00:00
drdev
e9bb6e42f6 Prevent adding cards in DualListBox once specific target reached 2013-12-07 19:30:42 +00:00
drdev
ed4b11d9ef Prevent focusing OK button if DualListBox has no specified count needed 2013-12-07 19:10:16 +00:00
drdev
c115f01edf Fix so OK button in DualListBox gets focus when it becomes enabled 2013-12-07 18:52:10 +00:00
drdev
8fd6bbef3b Code cleanup 2013-12-07 18:29:49 +00:00
drdev
8fa68731d1 Fix so mana refunded when using Auto to pay non-X cost then canceling at X prompt 2013-12-07 18:25:29 +00:00
drdev
b6b586a865 Make {X} appear before generic on card overlays 2013-12-07 17:38:52 +00:00
Chris
457ba444f1 Added new card names to changes.txt. 2013-12-07 14:36:10 +00:00
Sloth
31a367c434 - Added Praetor creature type. 2013-12-07 09:43:13 +00:00
swordshine
e83eb92948 - Added Fight or Flight 2013-12-07 01:50:02 +00:00
Sloth
5ee779846d - Fixed 2 more LKI problems. 2013-12-06 17:11:54 +00:00
Sloth
7a3ecf8e38 - Fixed LKI problems with Mercy Killing and similar cards. 2013-12-06 17:06:12 +00:00
Chris
9e2c96a18e Minor change to the changes.txt file. 2013-12-06 15:23:43 +00:00
Sloth
6550fa28ca - Fixed a bug in DamagePreventAi. 2013-12-06 14:47:30 +00:00
drdev
de9ce7ebb9 Mention recent game play usability improvements in CHANGES.txt 2013-12-06 07:26:54 +00:00
drdev
caae78b6ee Add basic Undo support for untapping mana sources that were tapped when not paying an active mana cost 2013-12-06 07:16:04 +00:00
Maxmtg
495701c46c tap-untap effect is indifferent to playertype 2013-12-06 06:46:24 +00:00
Maxmtg
ed99ce1694 remove gui calls from add/remove counters effects 2013-12-06 06:26:49 +00:00
Maxmtg
51a37e896d move counters no longer uses gui 2013-12-06 06:10:51 +00:00
Maxmtg
b431315e88 removed some gui calls 2013-12-06 05:49:11 +00:00
drdev
ea59f120da Prevent cards disappearing from your hand while paying for them or picking targets 2013-12-06 05:11:36 +00:00
drdev
9bd30e8d5d Code cleanup 2013-12-06 04:37:39 +00:00
drdev
39d1ef7e13 Add Escape as shortcut for Cancel in Prompt even if OK button has focus 2013-12-06 04:24:49 +00:00
drdev
83a1b70efe Ensure game ends if you concede while in the middle of playing a spell/ability 2013-12-05 02:26:06 +00:00
drdev
01b4f803a8 Code cleanup 2013-12-05 02:25:19 +00:00
drdev
5c31890066 Code cleanup 2013-12-05 02:02:34 +00:00
drdev
be7f4e5192 Code cleanup 2013-12-05 01:37:35 +00:00
drdev
4124c45875 Code cleanup 2013-12-05 01:18:28 +00:00
drdev
06e785c75a Code cleanup 2013-12-05 01:10:32 +00:00
drdev
2f384c079c Code cleanup 2013-12-05 00:56:57 +00:00
drdev
e4adada542 Code cleanup 2013-12-05 00:50:42 +00:00
drdev
efa86891cc Code cleanup 2013-12-05 00:38:04 +00:00
drdev
bcc20bb774 Return focus to OK button when returning to match screen 2013-12-05 00:31:41 +00:00
drdev
937a095209 Make runAsAi private 2013-12-05 00:14:06 +00:00
Maxmtg
1661228168 fix npe in PlayerControllerHuman 2013-12-04 21:28:11 +00:00
Chris
b17b9eb4e1 Added new card names to changes.txt. 2013-12-04 13:31:37 +00:00
Maxmtg
2ffa1510ed Player belongs to game, it cannot have references to any concrete PlayerController 2013-12-04 07:46:30 +00:00
swordshine
d1ef107b48 - Added Chorus of the Conclave 2013-12-04 07:05:50 +00:00
drdev
aa48e9cacc Fix so Ok button in DualListBox dialog receives focus as soon as it's been enabled so spacebar will activate it 2013-12-04 03:44:32 +00:00
drdev
3c0bfa7843 Remove extra blank line before "Pay Mana Cost" with optional payment prompt 2013-12-04 02:58:54 +00:00
drdev
803b6331f4 Fix so abilities using InputPayManaExecuteCommands work with Auto button 2013-12-04 02:49:26 +00:00
drdev
fe8af8705d Fix crash when determining if mana costs can be paid for SpellAbilities with no source card 2013-12-04 01:51:44 +00:00
drdev
624365cda3 Prevent AI taking over if exception thrown while AI controller temporarily set 2013-12-04 01:33:42 +00:00
Maxmtg
4fb59b6071 give spelldescriptions to card that offer 'generic' choices 2013-12-03 09:04:09 +00:00
Maxmtg
d15948e44b remove refs to gui and playertype in ChooseGenericEffect 2013-12-03 08:48:19 +00:00
Maxmtg
bf629ff518 choose color now uses player controller to decide.
removed calls to gui from ability executing classes
adjust visibility of CardFace methods
2013-12-03 08:27:44 +00:00
Maxmtg
ee9ee7f207 moved evaluator away from core 2013-12-03 06:56:13 +00:00
Maxmtg
311d4091ff remove runAsAi from playercontroller (it's meant to be indifferent to playercontrollers, and PlayerControllers descendants don't know of thier siblings' existance) 2013-12-03 06:34:29 +00:00
Sol
4e79f508c0 - Fixed issue with formatted mana being used for level up cost 2013-12-03 04:19:31 +00:00
swordshine
90828e6eab - Fixed a bug related to Commander games 2013-12-03 00:58:35 +00:00
Chris
7d3afc35cc Added a fluff piece to the changes.txt file.
Added new card names to changes.txt.
2013-12-02 14:58:46 +00:00
swordshine
6f4c046732 - Added Mana braces for three cards 2013-12-02 13:05:56 +00:00
swordshine
503a8da70f - Added Caller of the Hunt 2013-12-02 12:24:43 +00:00
drdev
1cf9a005d9 Fix places that assumed old format of ManaCost.toString() to either call new getCostString() function or assume {G} formatting
This fixes bugs with Rout ("may cast as instance if you pay 2 more" cards)
This also fixes bugs with Rune Snag ("unless player pays X" cards)
2013-12-02 08:52:30 +00:00
drdev
fe04d56568 Code cleanup 2013-12-02 07:31:12 +00:00
drdev
93b6ac15dd Ensure enters the battlefield triggered abilities are visible on stack 2013-12-02 07:06:50 +00:00
drdev
500632515c Mention Auto button and other game usability improvements in CHANGES.txt 2013-12-02 06:15:26 +00:00
drdev
598d2da9d6 Prevent mana not deducting from pool when manually paying mana costs 2013-12-02 05:56:18 +00:00
drdev
05f7a9b30a Prevent being prompted to decide how to pay mana when clicking Auto by temporarily using AI controller 2013-12-02 05:33:44 +00:00
drdev
0b6be04428 Add "Auto" button for automatically paying a mana cost
Code cleanup
2013-12-02 03:07:19 +00:00
Sol
dcb9553807 - Added Mana braces for Level up Ability and Insidious Bookworms 2013-12-02 01:03:38 +00:00
Chris
c9c9be68b1 Added new card names to changes.txt. 2013-12-01 13:50:11 +00:00
swordshine
53e6671143 - Added Call to Arms 2013-12-01 05:34:21 +00:00
drdev
3d063d8188 Code cleanup and refactoring 2013-12-01 03:50:47 +00:00
drdev
8d3633fe63 Code cleanup 2013-12-01 03:27:17 +00:00
drdev
ee7d64c533 Code cleanup 2013-12-01 03:24:22 +00:00
drdev
5009ebe0ab Code cleanup 2013-12-01 03:21:51 +00:00
drdev
9b2f3f3108 Tweak fix for not prompting if can't attack 2013-12-01 03:14:19 +00:00
drdev
9ca9dc797e Don't prompt to declare blockers if no creatures can block 2013-11-30 22:01:04 +00:00
drdev
2ffc18cc89 Skip combat phases if player has no creatures that can attack 2013-11-30 20:57:30 +00:00
drdev
cfeeda3c93 Cleanup whitespace 2013-11-30 18:50:17 +00:00
drdev
a2277bc2d4 Cleanup whitespace 2013-11-30 18:40:40 +00:00
drdev
694e4e7988 Fix misspelling 2013-11-30 18:39:28 +00:00
drdev
1c0e786489 Code cleanup 2013-11-30 18:31:18 +00:00
drdev
7420e94876 Rename CMessage to CPrompt to better match tab caption 2013-11-30 18:19:22 +00:00
drdev
73c921bb95 Increase contrast between Inactive color and background texture in Journeyman theme 2013-11-30 18:00:13 +00:00
Chris
7a22f4f74a - Added a fluff piece to the changes.txt file.
- Cleared out the changes.txt file, now ready for new material.
2013-11-30 15:15:56 +00:00
drdev
cdb76d344e Fix display of 0 loyalty planeswalker abilities 2013-11-30 07:26:10 +00:00
drdev
6cbded92cb if colorless filtered out ensure phyrexian cards don't appear unless at least one of their colors is selected 2013-11-30 07:04:21 +00:00
drdev
bc032a074c Optimize logic for no cost cards 2013-11-30 06:46:20 +00:00
drdev
89a4afdc08 Prevent cards with no mana cost showing up if they're color is filtered 2013-11-30 06:28:36 +00:00
drdev
be6266d633 Improve filter logic to support showing playable hybrid and phyrexian cards even if filtering out colors present in the card's mana cost that aren't needed to cast it 2013-11-30 05:45:30 +00:00
drdev
705264ba9a Ensure multicolor filter selected after right-clicking a color filter 2013-11-30 00:56:05 +00:00
drdev
a0b181e6fe Don't filter out multicolor cards after right-clicking a color filter 2013-11-30 00:51:09 +00:00
drdev
df50a0a38d Support showing only colorless or multicolor cards 2013-11-30 00:27:47 +00:00
Chris
abd182e17f [maven-release-plugin] prepare for next development iteration 2013-11-29 15:06:44 +00:00
Chris
312c15d824 [maven-release-plugin] prepare release forge-1.5.6 2013-11-29 15:06:32 +00:00
Chris
3f0cdc9b19 - Preparing the changes.txt file for the next beta build and release. 2013-11-29 14:52:38 +00:00
drdev
5d5c42c843 Mention Compact Prompt in CHANGES.txt 2013-11-29 06:57:36 +00:00
drdev
1a8fe19c88 Fix so multi-color cards containing a filtered color no longer show up in catalogs 2013-11-29 06:52:51 +00:00
drdev
7e37495b49 Hide checkbox to enable "Stack Card View" setting which isn't ready yet 2013-11-29 06:23:29 +00:00
drdev
3210f06931 Revert so command zone shown for all game types 2013-11-29 05:08:25 +00:00
drdev
a457ff539d Convert hide prompt header setting into compact prompt setting that also controls font size 2013-11-29 04:51:51 +00:00
drdev
afbcd90ffc Don't switch to Combat pane unless an attacker is declared 2013-11-29 04:37:35 +00:00
Maxmtg
8acdc6c458 fix name card effect 2013-11-29 04:37:23 +00:00
drdev
4ed8f69da1 If switched to Combat pane when combat begins, switch back to previous pane when combat ends 2013-11-29 04:28:05 +00:00
Maxmtg
90fe657da6 refactor: move isCommandZoneNeeded to gameType (java enums are far better than c#'s ones :) 2013-11-29 04:03:20 +00:00
drdev
91fe7b516c Show turn number in prompt message 2013-11-29 03:54:04 +00:00
drdev
e3851e3f1d Reduce padding around Prompt buttons 2013-11-29 03:39:34 +00:00
drdev
301014285f Reduce font size of prompt
Add setting to hide prompt header
2013-11-29 03:33:09 +00:00
drdev
e467f44bf2 Fix so Save and Open dialogs appear over main window 2013-11-29 02:59:22 +00:00
drdev
e8e5ac2ca7 Don't show command zone if game type doesn't need it 2013-11-29 02:51:55 +00:00
drdev
ebb0a4b2bc Mention change to show tabs if multiple on pane in CHANGES.txt 2013-11-28 18:01:50 +00:00
drdev
d21e2666ec Don't hide drag tabs if drag cell has multiple tabs 2013-11-28 17:58:46 +00:00
moomarc
43140b2db3 - Small fix for multiplayer tab labels 2013-11-28 09:02:39 +00:00
Maxmtg
c898e1321f cardreader fix 2013-11-27 07:46:21 +00:00
Maxmtg
4f04cbbcc8 fix hangs when both textfiles and zip are present in cardsfolder 2013-11-26 17:40:30 +00:00
asepetci
f17e857710 updated rankings.txt 2013-11-26 09:01:22 +00:00
drdev
2401b9b1ff Fix display issue with switching away from Game screen then back 2013-11-26 03:31:03 +00:00
drdev
e646ff1d1f Add checkbox to control whether list view or card view for Stack 2013-11-26 01:55:46 +00:00
drdev
9bb800ce43 Start adding card view to Stack, with preference to use current list view 2013-11-26 01:36:42 +00:00
ptx
6859ad2ad9 Some initial game simulation tests 2013-11-25 21:33:24 +00:00
drdev
19152790b2 Prevent dividing by zero when laying out cards 2013-11-24 23:03:51 +00:00
drdev
05751dbe1b Break up files in nonsingleton folder into existing controller and view folders 2013-11-24 21:16:23 +00:00
drdev
f35f6b3edd Cleanup whitespace 2013-11-24 20:56:11 +00:00
Maxmtg
8af7fadbf6 another 2 calls to gui removed 2013-11-24 19:21:30 +00:00
Maxmtg
9190ce038f remove imports of forge.gui from where they are not supposed to be (part 2 of many) 2013-11-24 11:14:51 +00:00
Maxmtg
bda47748f2 remove imports of forge.gui from where they are not supposed to be (part 1 of many) 2013-11-24 10:53:58 +00:00
Maxmtg
96084d7b26 disband forge.card package in gui module 2013-11-24 10:34:45 +00:00
Maxmtg
e96ff73b50 modified spells' canPlayAi, doTrigger and playFromEffect methods used by AI to include aiPlayer as 1st parameter 2013-11-24 10:02:58 +00:00
Hellfish
fcb8c4bdd6 *Fixed Charm of Vigor not having to be bought to be available 2013-11-24 09:44:28 +00:00
Maxmtg
c7c1d2d302 arrange Predicates for CardEdition 2013-11-24 09:35:43 +00:00
Maxmtg
21ea8e377c move card/CardCharacteristics.java to forge.game.card 2013-11-24 09:24:50 +00:00
Maxmtg
e9409d01a6 merged cardfactory into forge.game.card package 2013-11-24 07:17:43 +00:00
Maxmtg
a8c70a903f arrange packages 2013-11-24 07:09:23 +00:00
Maxmtg
c66ca97dfc move game Card class to forge.game.card package to ease further translation to game module 2013-11-24 06:59:42 +00:00
Maxmtg
5b5517ad29 replace TreeMapOfLists with Guava Multimap 2013-11-24 06:45:30 +00:00
drdev
c8de886967 Fix Flash of Insight reminder text 2013-11-23 23:13:54 +00:00
drdev
2ef19583a6 Fix displayed equip cost for M13 ring cycle 2013-11-23 23:06:01 +00:00
drdev
9e0ff5e635 Fix Civic Saber descriptions 2013-11-23 22:54:07 +00:00
drdev
427cef758f Fix Card Detail for Equip and Fortify cards 2013-11-23 22:49:54 +00:00
drdev
1cc1516feb Mention preference to hide reminder text in CHANGES.txt 2013-11-23 21:56:11 +00:00
drdev
daafa3f5c2 Add preference to Hide Reminder Text 2013-11-23 21:55:12 +00:00
drdev
9e20af7603 Cleanup whitespace 2013-11-23 21:37:37 +00:00
Chris
306a160df0 - Added a fluff piece to the changes.txt file. 2013-11-23 14:57:58 +00:00
Hellfish
b5686bcd2f *Removed first attempt code Charm of Vigor code. 2013-11-23 09:43:57 +00:00
Maxmtg
33dd245131 commit empty folders (otherwise eclipse refuses to move files) 2013-11-23 08:46:17 +00:00
Maxmtg
25334c27f1 (minor) remove IZone, rename gameAge to gameStage, remove unused imports 2013-11-23 08:34:14 +00:00
Maxmtg
3f20ebb15b prevent NPE for cases when new card is added 2013-11-23 08:21:49 +00:00
Maxmtg
37849b88e2 clean up core classes 2013-11-23 08:12:40 +00:00
drdev
800b3f8272 Tweak logic for formatting reminder text in italics 2013-11-23 06:39:42 +00:00
drdev
b2ecb3cb52 Mention reminder text formatting in CHANGES.txt 2013-11-23 06:25:27 +00:00
drdev
8a794f2e02 Display reminder text in italics 2013-11-23 06:21:26 +00:00
drdev
3c05e1674f Only show Workshop tab if running in Developer mode (for now) 2013-11-23 06:06:19 +00:00
drdev
a15b3c2a69 More card format fixes 2013-11-23 05:54:25 +00:00
drdev
10c138d64e Format mana batteries and a few other cards 2013-11-23 05:39:42 +00:00
drdev
99d1718498 Tweak formatting on Musician 2013-11-23 05:25:00 +00:00
drdev
515985dead Fix formatting of Volvers and Planar cards 2013-11-23 05:12:53 +00:00
drdev
e30c4532b8 Show regular and variant cards in Workshop 2013-11-23 05:03:07 +00:00
drdev
2ab5f19f79 Fix formatting of a couple cumulative upkeep cards 2013-11-23 04:55:17 +00:00
drdev
f5054558c2 Fix formating of Forecast and other cards 2013-11-23 04:50:31 +00:00
drdev
b726053164 Auto-select first card after doing search if no card otherwise selected 2013-11-23 04:38:34 +00:00
drdev
cc433b6708 Fix so PaperCard for card name doesn't change unless card renamed 2013-11-23 04:19:05 +00:00
drdev
e95e251161 Format mana costs for card scripts 2013-11-23 04:15:06 +00:00
drdev
75edc751f3 Format bestow costs 2013-11-23 03:45:47 +00:00
drdev
b4ba84a0d1 Fix Flash of Insight cost formatting and display {X} before generic cost 2013-11-23 03:29:14 +00:00
drdev
38074a1660 Fix so Card Detail updated when card script saved 2013-11-23 03:11:11 +00:00
drdev
ca5e20b1a9 Fix so rules not replaced when updated 2013-11-23 03:05:28 +00:00
drdev
c4db463338 Support saving script changes again 2013-11-23 02:54:52 +00:00
Maxmtg
34c2cb604f carddb editor will detect cases when rules are changed without rename 2013-11-23 00:11:07 +00:00
Maxmtg
f058e38004 CardRules.reinitializeFromScript 2013-11-23 00:03:51 +00:00
drdev
b38a8b5830 Format flashback cost on Firecat Blitz 2013-11-22 23:46:30 +00:00
drdev
8e76829a27 Format mana costs for Madness, Recover, and Miracle on instants/sorceries 2013-11-22 23:42:38 +00:00
Hellfish
f3bf092898 *Added Quest Item - Charm of Vigor: Let's you play best of 5 games rather than 3. 2013-11-22 23:03:49 +00:00
drdev
4dd2b87538 Rename allScripts hash map 2013-11-22 22:47:24 +00:00
Maxmtg
4d6930e903 prioritize text files over whatever found in zip 2013-11-22 18:10:06 +00:00
Maxmtg
3fecd16efd Have cardStorageReader read both zip and the files folder. There remains a task to replace duplicate cards 2013-11-22 17:09:07 +00:00
Maxmtg
29a535c71a moved CardStorageReader to core project 2013-11-22 16:51:38 +00:00
Maxmtg
3fd72f35df use concurrent map 2013-11-22 16:36:07 +00:00
Maxmtg
bb3d637738 adjust dependencies for CardScriptInfo 2013-11-22 16:32:56 +00:00
jendave
02fe620360 update guava 2013-11-22 09:25:24 +00:00
Hellfish
ebe0e350ff *Constructed defaults to 2 players to match previous behaviour 2013-11-22 05:41:52 +00:00
Sol
6dbe638a59 - Merfolk Seer wasn't displaying mana symbols in trigger cost 2013-11-22 04:42:22 +00:00
drdev
31b5f4181a Support displaying scripts in Workshop again 2013-11-22 03:21:37 +00:00
drdev
72d0cfdbbc Whitespace cleanup 2013-11-22 02:23:53 +00:00
drdev
f934764290 Prevent forge.profile.properties being accidentally committed to SVN 2013-11-22 01:38:33 +00:00
drdev
e28f809a58 Remove forge.profile.properties from SVN 2013-11-22 01:34:41 +00:00
Maxmtg
a88dd00a0d interface to edit cards 2013-11-21 23:37:34 +00:00
Hellfish
5d9de47fd5 *Expanded Constructed to up to 8 players, any amount AI or Human, to match up with the Variant update. 2013-11-21 22:54:30 +00:00
Maxmtg
7add28ad7d propper delete 2013-11-21 22:54:15 +00:00
Maxmtg
17b97aca28 hide some readers, collections and simple enums as nested classes, 2013-11-21 22:53:20 +00:00
Maxmtg
86b422230e added module 'game' 2013-11-21 21:32:49 +00:00
Hellfish
0193671453 *Fixed extra Field/Command/Hand views sticking around when for instance playing a game with 2 players after playing a game with 8 players. 2013-11-21 21:27:27 +00:00
Maxmtg
c4be1b8697 inlined type casts 2013-11-21 21:27:27 +00:00
Maxmtg
52f8880627 minor rearrangements in util package 2013-11-21 21:23:04 +00:00
Hellfish
f537e8cf78 *Prettyfied my previous addition. 2013-11-21 21:10:41 +00:00
Maxmtg
482af12b67 deck generation moved to core 2013-11-21 20:59:18 +00:00
Hellfish
2f85d50778 *Added the ability to specify multiple human (hotseat, of course) players or all AI for all variants 2013-11-21 20:25:14 +00:00
Hellfish
b51663c130 *Undoing Subclipse's mess 2013-11-21 19:46:05 +00:00
Hellfish
7ebc54352e Initial import. 2013-11-21 19:40:06 +00:00
jendave
6703e62823 minor doc fixes 2013-11-21 16:54:34 +00:00
Hellfish
e860e9e1af *"Remove All Counters" in a way that interacts other systems. Fixes Chronozoa + AEther Snap 2013-11-21 07:04:22 +00:00
Sloth
de3303abc9 - More use of activateForCost function. 2013-11-20 22:51:22 +00:00
Hellfish
a0695c08cc *Remove Inbound Tokens at appropriate places if the etb of the same token was replaced. Fixes Chronozoa. 2013-11-20 20:05:02 +00:00
Chris
c5aeb0785e - Cleared out the changes.txt file, now ready for new material.
- Added new card names to changes.txt.
2013-11-20 15:05:11 +00:00
Sloth
21b376848f - More use of activateForCost function. 2013-11-20 14:55:33 +00:00
swordshine
f8fe7e41b4 - Added Liar's Pendulum 2013-11-20 13:00:06 +00:00
Maxmtg
22caa0a408 updated references to org.apache.commons.lang version 2.6 to use version 3 ('cause forge used to import both 2.6 and 3.x versions of the named artifact) 2013-11-20 08:47:06 +00:00
Maxmtg
4d3945f5a4 moved all items to core, decks were included too. 2013-11-20 08:34:37 +00:00
Maxmtg
0786012ed8 rm ColorChanger.java (unused)
mv2core BoosterGenerator and UnOpenedProduct
2013-11-19 22:54:04 +00:00
Maxmtg
6e9a316460 remove UI references from CardStorageReader 2013-11-19 21:53:01 +00:00
Maxmtg
fd7d0e1d99 cleaned CardStorageReader of experimental code 2013-11-19 20:56:59 +00:00
Maxmtg
65199c7a81 moved cardFace and rules reader to core 2013-11-19 20:38:51 +00:00
Sloth
851094c523 - Added a doTriggerAINoCost function to RepeatAI. 2013-11-19 13:15:01 +00:00
Maxmtg
9b31032016 Moved cardDb and most of static data to core project. Had to rollback some incompatible changes. Sorry, drdev! 2013-11-19 07:40:58 +00:00
Sloth
a7fa173e52 - Content downloader will now show the number of item skipped. 2013-11-18 22:11:42 +00:00
Sloth
de4d83372c - Added 2 precons by Erazmus. 2013-11-18 20:59:32 +00:00
Sloth
c7f24490f3 - Some more work on the activateForCost function. 2013-11-18 20:57:25 +00:00
drdev
fabc7a3a27 Support updating card rules and display when saving script changes 2013-11-18 14:35:19 +00:00
swordshine
e830527f74 - Fixed other cards with optional decisions in ChangeZone effect 2013-11-18 12:48:02 +00:00
swordshine
5fc5feb636 - Fixed Cloudstone Curio 2013-11-18 12:41:19 +00:00
drdev
384af0001c Fix cursor positioning after undoing delete 2013-11-17 21:44:02 +00:00
drdev
2abc9b3f7c Create FUndoManager to encapsulate making undo/redo logic smarter and lumping typing changes together 2013-11-17 21:38:27 +00:00
drdev
9242f3439c Create FTextEditor to encapsulate a plain text editor with undo/redo support 2013-11-17 21:17:14 +00:00
drdev
b1ba8f0aa2 Add File menu and support Ctrl+S to save card in Workshop 2013-11-17 20:36:44 +00:00
drdev
20b3775280 Prompt to save card when switching to another card or another screen 2013-11-17 17:57:55 +00:00
drdev
c2bb321e17 Optimize determination of card script text being dirty 2013-11-17 08:33:00 +00:00
drdev
b0160287ba Add Workshop screen 2013-11-17 08:02:40 +00:00
drdev
284e2257f3 Fix Cascade 2013-11-16 21:43:57 +00:00
drdev
ec06d07f35 Breakup PrecostDesc$ of certain Entwine cards so mana cost part in CostDesc$
Make "Choose one" syntax consistent
2013-11-16 20:11:41 +00:00
drdev
5eca8807d8 Format mana costs for Echo, Cumulative Upkeep, and Unearth 2013-11-16 19:25:43 +00:00
drdev
5b3ee63299 Ignore forge-*/target directories for SVN 2013-11-16 19:22:41 +00:00
drdev
a6ffa3ae36 Update card scripts with CostDesc$ so its formatted 2013-11-16 19:17:44 +00:00
Sol
e021e66387 - Fixed Spell Description of MorphDown 2013-11-16 16:02:23 +00:00
Sol
eb70985551 - Fix Viridian Joiner description 2013-11-16 01:23:00 +00:00
Chris
cd6dc5158e [maven-release-plugin] prepare for next development iteration 2013-11-15 14:14:27 +00:00
Chris
942f30557c [maven-release-plugin] prepare release forge-1.5.5 2013-11-15 14:14:16 +00:00
Chris
1486d61e65 - Preparing the changes.txt file for the next beta build and release. 2013-11-15 13:57:23 +00:00
drdev
eb082d2f49 Fix abilities that used "tap" 2013-11-15 12:57:47 +00:00
drdev
7e6e691771 Ensure top of card text visible when first viewed in Card Detail 2013-11-15 12:14:03 +00:00
drdev
8e4962ceb1 Eliminate Card Detail flicker 2013-11-15 12:11:31 +00:00
drdev
414d82bd37 Fix The Tabernacle at Pendrell Vale mana format 2013-11-15 11:38:31 +00:00
drdev
4091f1d1d9 Fix Fleshwriter reminder cost format 2013-11-15 11:36:58 +00:00
drdev
fe0f4ddf99 Format Ninjutsu mana cost in reminder text 2013-11-15 11:34:27 +00:00
drdev
b0845c5795 Commit commented out imperfect code used for parsing ability in quotes 2013-11-15 11:24:44 +00:00
drdev
aeddb37d9d Format ability costs in quotes 2013-11-15 11:22:51 +00:00
drdev
1ff1c35486 Remove bullet in CHANGES.txt that no longer applies 2013-11-15 09:48:11 +00:00
drdev
6e585a7afa For now, if card only has 1 activated ability, don't show menu even if cost is automatic and undoable 2013-11-15 09:47:24 +00:00
Sloth
e0df16ee17 - Fixed AI using Exhume. 2013-11-15 09:42:15 +00:00
drdev
ae5cb7a17a Make ability menu items consistent regardless of containing mana symbol icons and make them respect menu item height 2013-11-15 09:40:47 +00:00
Sloth
8d79ed527f - Fixed a bug in ChooseCardEffect. 2013-11-15 09:37:08 +00:00
Sloth
d4bc84c6bd - Fixed Stormfront Riders. 2013-11-15 09:11:52 +00:00
drdev
9743a7d1b7 Mention fixing WUBRG order in CHANGES.txt 2013-11-15 09:03:48 +00:00
drdev
e5cffc623a Support updating colored mana order, phyrexian and hybrid mana format, and mana production abilities with multiple choices 2013-11-15 06:59:23 +00:00
drdev
9c0a559bce Update hybrid costs in abilities 2013-11-15 06:56:52 +00:00
drdev
b794bc5ee0 Update card ability descriptions containing choice of dual mana production 2013-11-15 06:26:33 +00:00
drdev
55c71d77e1 Fix colored mana order for card scripts 2013-11-15 06:04:12 +00:00
drdev
6914e30443 Fix colored mana order for card scripts 2013-11-15 05:48:47 +00:00
drdev
1c52e8e38a Update card ability descriptions containing choice of mana production 2013-11-15 03:00:09 +00:00
drdev
c38f169d1d Add space between ability description mana cost and reminder text 2013-11-15 00:58:24 +00:00
drdev
8f4b8dbf88 Prevent trimming spaces 2013-11-15 00:51:34 +00:00
drdev
b68f28b621 Update earwig_squad.txt prowl cost 2013-11-15 00:51:01 +00:00
drdev
a23eb2f869 Support outputting results from parse utility 2013-11-15 00:45:49 +00:00
drdev
8dfbf2b3b6 Update more card ability descriptions 2013-11-15 00:14:31 +00:00
drdev
70b0fab66a Use try/catch block to avoid crash from bad pattern 2013-11-14 23:27:08 +00:00
drdev
082e2ff2cd Add parse command line argument to allow performing function on all parsed cards 2013-11-14 23:16:28 +00:00
Sloth
12af50d473 - Update more card ability descriptions. 2013-11-14 22:54:50 +00:00
Sloth
b1cde02dd4 - Update more card ability descriptions with {T} symbol. 2013-11-14 22:22:19 +00:00
jendave
2d737ab092 a little progress on the Mac app bundle 2013-11-14 21:59:44 +00:00
jendave
8cd9220f36 update release plugin. Update site urls for new maven structure. 2013-11-14 17:08:54 +00:00
swordshine
838d4a98aa - Update more scripts 2013-11-14 13:50:40 +00:00
swordshine
d32bfea94e - Fixed Braid of Fire 2013-11-14 13:25:28 +00:00
swordshine
181a7f5b9c - Updated basic land abilities to display mana symbol icons 2013-11-14 10:57:51 +00:00
drdev
ee00b544f7 Support displaying Chaos icon in details 2013-11-14 09:20:29 +00:00
drdev
5548c71bda Update more card ability descriptions 2013-11-14 08:46:01 +00:00
drdev
ae7a6b79b7 Update more card ability descriptions 2013-11-14 08:27:50 +00:00
drdev
96d2338bea Update card ability descriptions so mana symbols are displayed as icons 2013-11-14 07:48:31 +00:00
drdev
21217e030b Code cleanup 2013-11-14 04:42:59 +00:00
drdev
1d63f4b4a1 Update CHANGES.txt 2013-11-14 04:19:34 +00:00
drdev
8843e5bfe7 Properly translate only mana costs to icons
Make snow and certain hybrid mana symbols display correctly
Show mana symbols in game prompt
2013-11-14 04:08:24 +00:00
Sloth
48b97f8632 - Fixed Balduvian Frostwaker. 2013-11-13 22:51:29 +00:00
jendave
20184fb4c0 ignore IntelliJ files 2013-11-12 08:46:35 +00:00
drdev
76000a90fb Update CHANGES.txt 2013-11-12 07:08:52 +00:00
drdev
4aebd350ee Support showing mana symbols in ability menu and card detail pane 2013-11-12 07:06:09 +00:00
Sol
13f4a0abb4 - Adding Enchant opponent line to Psychic Posssesion 2013-11-12 02:19:51 +00:00
drdev
35ea297090 Make target arrows appear in correct place in window mode 2013-11-12 02:15:55 +00:00
drdev
f11909d6da Prevent repeating shortcuts for abilities 2013-11-12 01:57:24 +00:00
jendave
5df82652b6 clean ups 2013-11-11 16:07:37 +00:00
Chris
8fde8ebe5f - Added new card names to changes.txt. 2013-11-11 13:43:52 +00:00
drdev
6bc20a8830 Update CHANGES.txt 2013-11-11 12:48:18 +00:00
drdev
0bbd9c103a Make menu items easier to click
Mention context menu ability select in CHANGES.txt
2013-11-11 12:39:18 +00:00
jendave
0b68e2ade6 re-org 2013-11-11 08:27:14 +00:00
jendave
1ca79539b6 update deps and re-org a few things 2013-11-11 08:27:00 +00:00
drdev
1c2c3d6d51 Ensure first ability selected in menu by default 2013-11-11 02:55:18 +00:00
drdev
bdb1243409 Prevent showing menu if no ability can be played 2013-11-11 02:36:44 +00:00
drdev
812d4ca519 Prevent returning ability that can't be played 2013-11-11 02:22:27 +00:00
swordshine
4e7e14cbf3 - Added Invasion Plans 2013-11-11 00:29:43 +00:00
swordshine
2064b6517e - Temporarily removed Arboria
- Fixed Quicksilver Dragon
2013-11-11 00:22:11 +00:00
drdev
c9f491a483 Show unplayable activated abilities disabled unless activator or zone restriction
Prompt for single activated ability unless mana ability
Show mana abilities before other abilities to match card order
2013-11-10 21:48:55 +00:00
Maxmtg
be2ec80f0c redirect dependency between Card and IPaperCard 2013-11-10 21:37:07 +00:00
drdev
1c13e8dc10 Show context menu to select ability instead of dialog 2013-11-10 20:25:25 +00:00
drdev
4748aa2262 Fix typo in function name 2013-11-10 18:56:41 +00:00
swordshine
a3376b0502 - Added Arboria, Premature Burial, and Sacred Ground 2013-11-10 05:14:26 +00:00
swordshine
c073d39cd6 - Updated scripts 2013-11-10 04:51:16 +00:00
Chris
b508f78f04 Moved the CHANGES.txt, LICENSE.txt, forge.profile.properties.example and README.txt files to forge-gui folder. 2013-11-09 15:33:38 +00:00
Chris
8424da8311 - Added new card names to changes.txt. 2013-11-09 15:17:05 +00:00
swordshine
cfd9d55668 - Added Quicksilver Dragon 2013-11-09 10:01:48 +00:00
swordshine
417e3bb104 - Added Reflecting Mirror 2013-11-09 09:36:45 +00:00
swordshine
320d4f62d9 2013-11-09 08:24:47 +00:00
Maxmtg
6c712d86fb moved a few classes to core module 2013-11-09 02:27:38 +00:00
jendave
a0479f9610 Test run of moving files to modules. 2013-11-08 23:21:11 +00:00
Maxmtg
374a744a44 added java nature to forge-gui project, set up classpath (copied it from former project) 2013-11-08 21:57:20 +00:00
Maxmtg
81416d49de added reference from core to guava, set runtime version of ai and core to 1.7 2013-11-08 21:35:14 +00:00
Sloth
31ebd62c83 - Added more precons by Erazmus. 2013-11-08 20:18:39 +00:00
jendave
93dd4af6ed fix eclipse issues 2013-11-08 18:39:59 +00:00
jendave
9f0308dd66 Start of re-org into modules 2013-11-08 10:05:52 +00:00
Sloth
f9298703d4 - Added some precons by Erazmus. 2013-11-08 08:52:31 +00:00
Sloth
3992032e8e - Added Rebound. 2013-11-07 23:08:44 +00:00
Sloth
58dacc0789 - Added Silver Wyvern. 2013-11-07 22:54:17 +00:00
Chris
b3280923d9 - Added new card names to changes.txt. 2013-11-07 14:49:02 +00:00
Sloth
e83da9ff9c - Some safety fixes for Muck Drubb. 2013-11-07 09:17:48 +00:00
Sloth
8b1d0fc759 - Added Muck Drubb. 2013-11-06 22:45:35 +00:00
Sloth
ff869933be - Cleanup. 2013-11-06 22:15:51 +00:00
Sloth
fb0af4dc18 - Start of using isValid function for SpellAbilities. 2013-11-06 19:39:34 +00:00
Sloth
6f5551d78d - Fixed Razia's Purification. 2013-11-05 22:32:40 +00:00
swordshine
a90efe4939 - Fixed Mirror Strike 2013-11-05 02:37:33 +00:00
Sloth
46c37b9d82 - Updated mtg-data.txt.
- Added Oracle texts to C13 cards.
2013-11-04 21:10:55 +00:00
drdev
3e23eb517b Use FMouseAdapter to make catalog and deck tables more responsive 2013-11-04 03:17:28 +00:00
Sloth
9cf75ea844 - Script fixes and updates. 2013-11-03 22:02:28 +00:00
drdev
5565b4a4d0 Retain column sort direction between sessions 2013-11-03 18:44:14 +00:00
drdev
bde5967ef4 Remember column order in Deck Editor 2013-11-03 17:47:57 +00:00
Chris
f3560238fe - Added new card names to changes.txt. 2013-11-03 13:52:06 +00:00
swordshine
f97f489aba - Added Juxtapose 2013-11-03 13:28:12 +00:00
swordshine
454b7715c0 - Added Scheme: Your Inescapable Doom 2013-11-03 11:46:46 +00:00
swordshine
ac1e279235 - Fixed LibraryPosition 2013-11-03 07:50:02 +00:00
swordshine
5950162663 - Added Johan
- Updated mtgdata
2013-11-03 07:32:08 +00:00
Chris
6a52b46192 - Added a fluff piece to the changes.txt file. 2013-11-02 12:42:27 +00:00
Sloth
f5558eec7f - Updated and fixed some C13 cards. 2013-11-02 07:37:33 +00:00
Chris
0c39f596a3 - Cleared out the changes.txt file, now ready for new material. 2013-11-01 23:09:20 +00:00
Sloth
ba3c544484 - Fixed Saltskitter. 2013-11-01 19:42:54 +00:00
jendave
e0063727a9 update to latest deps and plugins. Did not update to latest jetty and guava since they may cause issues 2013-11-01 16:27:50 +00:00
swordshine
7b68821a8c - Merged C13 Branch
- Fixed Curse of Chaos, Curse of the Forsaken, Fell Shepherd
2013-11-01 14:19:28 +00:00
Chris
4a84b408d4 [maven-release-plugin] prepare for next development iteration 2013-11-01 13:06:01 +00:00
Chris
b9aec4ffb8 [maven-release-plugin] prepare release forge-1.5.4 2013-11-01 13:05:50 +00:00
Chris
1a958e758f - Preparing the changes.txt file for the next beta build and release. 2013-11-01 12:55:46 +00:00
RumbleBBU
74527319aa Relabeled the starting pool color distribution options to make them more intuitive. Merged the randomization option to the pulldown menu. 2013-10-31 09:48:35 +00:00
drdev
a66e7ad14f Add click effect to opague and selectable FLabels to make them feel more like buttons 2013-10-30 14:40:36 +00:00
drdev
e4b91416dd Update DeckLister to use FMouseAdapter 2013-10-30 14:00:37 +00:00
drdev
7735c705c0 Fix so right-clicking stat label that's the only selected one in group will re-select all other labels in group 2013-10-30 13:15:56 +00:00
drdev
31ab48d0d4 Let FMouseAdapter raise click if mouse moved outside component then back inside before releasing mouse 2013-10-30 13:02:44 +00:00
drdev
e5cf9da31d Update FLabel to use new FMouseAdapter 2013-10-30 12:39:10 +00:00
drdev
9eac64ab7b Create FMouseAdapter to facilitate more reliable and responsive click handling
Improve responsiveness of table sorting in ItemManagers
Prevent events on editor tables being duplicated, resulting in poor performance and inverting sort sometimes not working
2013-10-30 12:29:30 +00:00
swordshine
7131cebf7d - Fixed last commit 2013-10-30 04:49:55 +00:00
swordshine
bfb4a5ee57 - Commander 2013 edition file 2013-10-30 03:04:38 +00:00
drdev
21e250bf36 Prevent auto-inverting sort direction when switching away from editor and back 2013-10-28 14:04:31 +00:00
swordshine
f5b1ac970b - Fixed Scroll Thief and Stealer of Secrets 2013-10-28 03:04:36 +00:00
drdev
c85f14f27c Fix crash when taking over another player's turn 2013-10-27 19:14:00 +00:00
drdev
c0cf6198a4 Mention addition of concede confirmation in CHANGES.txt 2013-10-27 06:53:39 +00:00
drdev
4d3012fee7 Ensure Match screen active before showing concede prompt 2013-10-27 06:52:17 +00:00
drdev
75726dc272 Make user confirm that they want to concede the current game 2013-10-27 06:47:20 +00:00
drdev
260724af80 Fix so Match screen uses the correct controller (so correct menus built for example) 2013-10-27 06:33:26 +00:00
drdev
2b37cf217f Updated CHANGES.txt for combo box visual tweaks 2013-10-27 06:23:03 +00:00
drdev
669ff579c5 Fix so combo boxes on Constructed home screen update when skin switched 2013-10-27 06:17:23 +00:00
drdev
d0e65fe1bc Use FComboBox in place of JComboBox 2013-10-27 05:54:22 +00:00
drdev
4485e46801 Remove unused FComboBox properties 2013-10-27 05:27:51 +00:00
drdev
2265687c75 Make combo box be all one piece 2013-10-27 05:12:57 +00:00
drdev
f70523bf14 Update CHANGES.txt for Draft fixes 2013-10-27 03:56:13 +00:00
drdev
48013b9339 Fix a couple more issues with drafting 2013-10-27 03:46:20 +00:00
drdev
e9e1caf9d1 Prevent losing draft picks when switching away and back
Prevent canceling naming draft pool which caused a crash
2013-10-27 03:29:20 +00:00
drdev
5b17e2beda Make it so Draft screen closes when draft process finished and Draft Deck Editor immediately opens 2013-10-27 03:17:45 +00:00
drdev
19bc74f88d Fix a few remaining places that would show prompts outside main window 2013-10-27 02:52:14 +00:00
drdev
ae0069652a Fix switching between Editors 2013-10-26 21:49:26 +00:00
Sloth
5ec7f01b7a - Added the new quest deck Robot Santa 3. 2013-10-26 20:55:39 +00:00
Sloth
691f798f90 - Replaced the interface ITargetable with the class GameObject (which reflects the term used in the rules for players, cards and spell/abilities).
- Preparations for extending isValid and hasProperty functions for spell/abilities.
2013-10-25 21:41:38 +00:00
Chris
d5bef0861b - Added new card names to changes.txt. 2013-10-24 13:27:07 +00:00
swordshine
87dd938cde - Added Graxiplon 2013-10-24 08:54:27 +00:00
Sloth
c4e0d3e7ce - Added Glarecaster. 2013-10-23 21:52:06 +00:00
Sloth
c705722bdc - Updated some AI SVars. 2013-10-23 15:53:08 +00:00
Sloth
6424fa151a - Added a hard version of the Leela quest deck. 2013-10-23 14:04:44 +00:00
swordshine
0646e49359 - Added Scheme: Only Blood Ends Your Nightmares
- Added Cyclopean Tomb
2013-10-23 00:24:38 +00:00
Sloth
6ddae9e9da - Updated some Shandalar world quest decks. 2013-10-22 20:34:41 +00:00
Sloth
efc7be1637 - Fixed face-down cards moving to Hand or Library not being turned face up. 2013-10-22 13:41:16 +00:00
Chris
1de80be872 - Added new card names to changes.txt. 2013-10-22 13:02:54 +00:00
swordshine
f8740f1268 - Updated scripts (Psychogenic Probe should trigger only once for these cards) 2013-10-22 12:19:08 +00:00
swordshine
d6f30c9220 - Added Dichotomancy 2013-10-22 12:02:28 +00:00
swordshine
f2d7004b02 - C13: Added From the Ashes 2013-10-22 11:08:14 +00:00
swordshine
6ecf800bab - Added Seeds of Innocence 2013-10-22 10:23:24 +00:00
Sloth
6f92a834da - Some deck updates. 2013-10-21 21:06:56 +00:00
Sloth
a7a85d22d5 - Added some card specific AI for Serene Master and Shape Stealer. 2013-10-21 20:18:05 +00:00
Sloth
178024e248 - Fixed AI freeze caused by Fireball. 2013-10-21 19:53:04 +00:00
swordshine
798e93af46 - Fixed Opal Palace (using an etbCounter replacement now) 2013-10-21 13:40:36 +00:00
swordshine
3cb125ae4a - C13: Added Opal Palace 2013-10-21 12:12:01 +00:00
swordshine
30aed49e87 - C13: Added Serene Master (please improve AiAttackController.declareAttackers and AiBlockController for this card) 2013-10-21 06:27:48 +00:00
swordshine
d0173a2341 - Fixed r23523 LibraryPosition 2013-10-21 02:56:07 +00:00
drdev
fb7a61ab30 Fix crash when purchasing items in the Bazaar 2013-10-20 23:07:37 +00:00
Sloth
13e09c73bc - Fixed text of Force of Nature. 2013-10-20 07:34:25 +00:00
swordshine
6fdaf4fda4 - C13: Added Tempt with Glory, Tempt with Reflections and Unexpectedly Absent 2013-10-20 05:24:56 +00:00
Chris
ba79089bed - Cleared out the changes.txt file, now ready for new material.
- Added new card names to changes.txt.
2013-10-19 16:22:03 +00:00
Sloth
a8670467be - Fixed Tangle Wire. 2013-10-19 11:26:31 +00:00
swordshine
4dd84cdb2e - C13: Added Naya Soulbeast and Tempt with Discovery 2013-10-19 04:09:38 +00:00
Sol
028d6c0a1f - Using CARDNAME instead of Seasinger for ability stealing cards 2013-10-19 03:36:39 +00:00
swordshine
ca2584f598 - C13: Added Sudden Demise, Tempt with Vengeance, Terra Ravager, Toxic Deluge, Widespread Panic and Witch Hunt 2013-10-19 00:49:33 +00:00
Sol
17e96250f7 - Fix issue with a few Avatars that spawn cards outside of the game not knowing what game they are apart of 2013-10-18 21:48:38 +00:00
Chris
79fdd21817 [maven-release-plugin] prepare for next development iteration 2013-10-18 14:33:15 +00:00
Chris
a0daa7c4be [maven-release-plugin] prepare release forge-1.5.3 2013-10-18 14:33:05 +00:00
Chris
000d0d3119 - Preparing the changes.txt file for the next beta build and release. 2013-10-18 14:21:49 +00:00
Chris
3b01b4f3dd - Added new card names to changes.txt. 2013-10-18 12:54:11 +00:00
swordshine
f0613e9660 - C13: Added Act of Authority and Angel of Finality 2013-10-18 05:43:51 +00:00
Sloth
1c51ed37c2 - Fixed an issue with the "May be played by your opponent" keyword. 2013-10-17 21:18:24 +00:00
Chris
2bfcd92da9 - Added new card names to changes.txt. 2013-10-17 12:50:02 +00:00
RumbleBBU
0bf676448b Fixed a bug that caused the Quest starting pool reduction incorrectly affect randomized starting pools. 2013-10-17 12:43:46 +00:00
RumbleBBU
5bceff414e Bias steepness now reduces the total amount of cards in your Quest starting pool. Effect of steepness slightly increased to compensate for this in your chosen color.
Slightly tweaked and relabeled the UI components for color preference selection, to make them slightly more intuitive.
2013-10-17 10:11:31 +00:00
swordshine
1b65231e30 - C13: Added Illusionist's Gambit and True-Name Nemesis 2013-10-17 05:23:38 +00:00
drdev
8b39b69bb7 Move/rename FControl.Screens to FScreen and make that define the Navigation tab data instead of INavigationTabData
Make it so each Deck Editor type appears in its own tab
Make Bazaar screen act more like other screens
2013-10-17 04:23:48 +00:00
Chris
4dfab45544 - Added new card names to changes.txt. 2013-10-16 12:53:43 +00:00
swordshine
f046dbf3cc - C13: Added Surveyor's Scope 2013-10-16 06:34:58 +00:00
swordshine
2e5f9350eb - Replicate is a trigger now 2013-10-16 00:35:36 +00:00
Sloth
938784d6e8 - Fixed Kiki-Jiki, Mirror Breaker not granting haste to transformed cards. 2013-10-15 13:59:43 +00:00
Chris
02c582a695 - Added new card names to changes.txt. 2013-10-15 13:05:01 +00:00
swordshine
2851d49c3b - C13: Added Eye of Doom 2013-10-15 12:15:33 +00:00
swordshine
e88af1f6ba - Converted Madness 2013-10-15 10:48:32 +00:00
swordshine
da3b6bf19e - Converted Persist/Undying to script (these triggers can be stifled and copied now) 2013-10-15 00:21:23 +00:00
Chris
da28e10ac7 - Added new card names to changes.txt. 2013-10-14 13:42:37 +00:00
swordshine
e6b260cc60 - C13: Added Curse of Chaos 2013-10-14 04:12:40 +00:00
Sloth
2cea9debcf - Added Carom. 2013-10-13 19:26:17 +00:00
swordshine
768a26a5d4 - Converted Tangle Wire to script 2013-10-13 08:33:38 +00:00
swordshine
0e251ffb6a - Converted Drop of Honey and Porphyry Nodes to script 2013-10-13 08:00:44 +00:00
swordshine
74c04ea561 - Cleanup 2013-10-13 07:20:08 +00:00
swordshine
e92cc2735b - Converted Braid of Fire to script 2013-10-13 07:15:59 +00:00
swordshine
6a94535b78 - Converted Intruder Alarm, Smoke and Stoic Angel to script 2013-10-13 05:24:17 +00:00
swordshine
17ad490f97 - Converted Damping Field and Imi Statue to script 2013-10-13 05:07:07 +00:00
swordshine
704e4c4ef7 - Converted Winter Orb and Mungha Wurm to script 2013-10-13 04:57:08 +00:00
Chris
36a301275e - Added new card names to changes.txt. 2013-10-12 20:20:15 +00:00
drdev
bb2a01512e Make disabled close buttons appear disabled
Fix contrast skin colors
2013-10-12 20:17:52 +00:00
Sloth
ad73c9391d - Added the WU Heroic deck Bilbo Baggins 2 with help from Nordos. 2013-10-12 17:13:07 +00:00
swordshine
ce33d146d0 - Converted Land Equilibrium to script 2013-10-12 13:06:37 +00:00
Sloth
055695890e - Cleanup. 2013-10-12 13:02:12 +00:00
swordshine
028579eed4 - Added Protective Sphere 2013-10-12 11:20:54 +00:00
swordshine
5c836228f7 - Added Spellweaver Volute 2013-10-12 05:48:07 +00:00
swordshine
0663eee49a - Added Takklemaggot 2013-10-12 02:38:30 +00:00
dripton
4d9205699b Fix typo in comment 2013-10-11 15:12:52 +00:00
drdev
d8a5993a28 Ensure close buttons disabled along with navigation tabs 2013-10-11 06:11:31 +00:00
drdev
6e7e19fd34 Disable navigation tabs while overlay open 2013-10-11 05:56:22 +00:00
drdev
9e55ee9be1 Improve animation of navigation bar reveal and prevent it being stuck open with auto-hiding 2013-10-11 05:41:06 +00:00
drdev
ae93665645 Fix so remaining dialogs show up on top of Forge, such as sideboard 2013-10-11 01:52:50 +00:00
drdev
7eb84bb6a3 Update tooltip for X button based on current screen and close action 2013-10-11 01:18:39 +00:00
drdev
3006cc9431 Ensure previous game ended when continuing Guantlet 2013-10-11 00:32:22 +00:00
Sloth
480629953b - Fixed Ghoulcaller's Chant. 2013-10-10 21:59:30 +00:00
dripton
c54ed09d38 Add try/catch to hiddenOriginCanPlayAi
If a SpellAbility's origin is something like "Graveyard,Library",
ZoneType.smartValueOf throws an IllegalArgumentException.  Catch
this exception and return false.  This prevents the crash, but
means that the AI still can't play cards like Doomsday.

Fixes bug 745
2013-10-10 14:29:53 +00:00
drdev
a098b56339 Fix typos 2013-10-10 03:19:28 +00:00
drdev
7a9f8d9788 Add setting to control behavior of X button in upper right (Close Screen vs. Exit Forge) 2013-10-10 03:06:05 +00:00
Sloth
7bd822b96a - Fixed human player not being able to activate mana abilities as a spell/ability is played when Mana produced replacement effects are around. 2013-10-09 13:57:59 +00:00
swordshine
d20e9d5629 - Fixed Dark Betrayal 2013-10-09 10:34:20 +00:00
RumbleBBU
9b61a99dc3 Added a preference to adjust color bias steepness when using color-biased Quest starting pools. 2013-10-09 09:49:38 +00:00
drdev
4c1f670147 Set dialog default to Yes when exiting Forge without game active
Ensure Deck Editor visible if and when "Save Changes?" dialog would appear
2013-10-09 05:10:59 +00:00
drdev
c05d299fe4 Always prompt user before exiting or restarting Forge 2013-10-09 04:45:44 +00:00
drdev
0ba8b530fe Fix so quitting Quest games works with tabs
Prevent adding multiple game tabs
2013-10-09 04:33:45 +00:00
drdev
0a6f7afeff Add navigation tab for Bazaar
Prevent arrow button remaining visible when switching to Bazaar from Home screen
2013-10-09 01:56:03 +00:00
Sloth
483f274bac - Fixed my commit r23388. 2013-10-08 21:15:03 +00:00
Chris
1fd36a4623 - Added new card names to changes.txt. 2013-10-08 13:55:52 +00:00
drdev
3b1ab80c1c Add navigation tabs for switching between Home, Deck Editor, and Match screens
Remove Deck Editor and Exit Forge buttons home screen
Exit Forge when X button in upper right clicked
Add safety checks before exit or restart
Show warning if you try to Start a game while one is already active (at least for now)
2013-10-08 13:07:33 +00:00
RumbleBBU
abe0cec346 - Added an experimental UI that allows you to choose a preferred color in new Quest games. Also added a "Balanced color distribution" checkbox that can be unchecked to completely randomize the color distribution in new Quest games.
- Minor checkstyle fixes to old issues in VSubmenuQuestData and CSubmenuQuestData.
2013-10-08 12:32:42 +00:00
spr
6e1e22d6cb - Refactor GamePlayerUtil. 2013-10-08 10:04:32 +00:00
drdev
7650ec9323 Minimize if switching from Full Screen Forge to outside application window 2013-10-08 09:20:47 +00:00
spr
fe3fedaf64 - Renamed "Themed ComboBox" setting to "Enable Themes" to better reflect its purpose. 2013-10-08 07:16:52 +00:00
spr
f21f187890 - Removed redundant Visual Themes section from Game Settings -> Preferences. 2013-10-08 07:10:59 +00:00
spr
41e164d131 - Removed redundant Card Overlay Options section from Game Settings -> Preferences. 2013-10-08 07:04:28 +00:00
drdev
adb6a0b7f0 Fix so Deck Importer and other JDialogs display centered over main Forge window 2013-10-08 02:11:50 +00:00
drdev
31618c1611 Mention dialog fix in CHANGES.txt 2013-10-08 01:25:28 +00:00
drdev
6eba60638b Prevent Full Screen Forge being minimized when deactivated (such as by opening JOptionPane dialog) 2013-10-08 01:15:29 +00:00
drdev
e4040d4a47 Fix so JOptionPane dialogs always display at center of main window by default 2013-10-08 00:43:34 +00:00
drdev
c53cd32291 Fix so, when maximized, Forge doesn't extend below top of taskbar if screen has a top inset 2013-10-07 23:07:42 +00:00
drdev
172ca340ae Disable Forge button and Forge menu shortcut keys while overlay open 2013-10-07 22:53:14 +00:00
drdev
a8451abea4 Delay hiding titlebar a half second after becoming full-screen 2013-10-07 22:26:02 +00:00
drdev
cdf67a3865 Mention fix to Mac minimize crash 2013-10-07 22:05:31 +00:00
Sloth
e2f2fc4804 - Added a medium version of the Hugo Drax deck. 2013-10-07 20:41:08 +00:00
spr
db9a451e32 - Added option to replace default "Human" with custom name during gameplay. (http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=11553). 2013-10-07 19:56:00 +00:00
spr
a8cf682f71 - Added very simple About box to display Forge version. 2013-10-07 19:46:05 +00:00
spr
0c721f1776 - FNavBar tweak - Revealspeed = 200, revealDelay = 100. 2013-10-07 19:39:48 +00:00
Chris
f8194aac7a - Added new card names to changes.txt. 2013-10-07 13:14:57 +00:00
swordshine
15dd60ff3e - Update the script for Chained to the Rocks 2013-10-07 12:45:55 +00:00
drdev
d54441a9b2 Update CHANGES.txt for Full Screen support 2013-10-07 06:59:19 +00:00
drdev
4e75bfb4d0 Remove status bar, instead using tooltips for menu hints
Prevent overlays showing on top of titlebar
2013-10-07 06:52:41 +00:00
drdev
3e350d03c6 If titlebar unlocked or window made full-screen, delay hiding titlebar until mouse moves away 2013-10-07 05:42:10 +00:00
drdev
2fa44e1ae2 Always show Full Screen button right of Minimize button 2013-10-07 05:23:43 +00:00
drdev
1e5244cee0 Only allow titlebar being hidden when in Full Screen mode
Ensure Full Screen window appears on correct monitor
Add buttons to toggle Full Screen and lock/unlock titlebar
Make clock appear in titlebar when Full Screen and hiding status bar
Prevent moving window or double-click restore down when Full Screen
2013-10-07 05:17:27 +00:00
swordshine
6043c2738d - Added Snowblind 2013-10-07 05:09:55 +00:00
drdev
013cf5eb13 Disable Direct3D to improve rendering performance 2013-10-07 02:37:03 +00:00
drdev
a413fe2dc1 Ensure full-screen still works after using Set Window Size when in in Full Screen mode 2013-10-06 16:14:02 +00:00
drdev
a2bf02eb82 Support toggling Full Screen with F11 on all platforms
Fix way window state is updated (minimized/normal/maximized/full screen)
2013-10-06 16:08:29 +00:00
swordshine
229485494a - Update scripts 2013-10-06 11:40:05 +00:00
Hellfish
4ea3439667 *Updated Kederekt Parasite for multiplayer 2013-10-06 09:07:17 +00:00
Sloth
4154d31143 - Fixed possible NPE caused by Haunt. 2013-10-06 08:44:18 +00:00
Hellfish
30b660f802 *Missed a vital piece of the AI commander pay fail fix. 2013-10-06 08:08:39 +00:00
drdev
b79cf45778 Prevent Forge covering up taskbar when maximized 2013-10-05 22:53:47 +00:00
drdev
c6e9cbd69b Skin tooltips 2013-10-05 22:12:51 +00:00
drdev
7e493ed129 Change constants to static 2013-10-05 21:53:40 +00:00
drdev
c3ed6ebe79 Make reveal speed easier to tweak 2013-10-05 21:47:32 +00:00
Hellfish
27e33ec1b1 *Fixed AI failing to pay for recasting commander. 2013-10-05 18:02:42 +00:00
Chris
7601d5a2bd - Cleared out the changes.txt file, now ready for new material.
- Added new card names to changes.txt.
2013-10-05 13:24:12 +00:00
swordshine
a9fb42eb15 - Added Excavator 2013-10-05 11:38:53 +00:00
Sloth
cd9cdbf1be - Added AEtherplasm. 2013-10-05 11:38:33 +00:00
swordshine
e68a716587 - Added Elemental Resonance 2013-10-05 06:49:09 +00:00
swordshine
bab5672669 - Added Charmed Pendant 2013-10-05 06:31:54 +00:00
drdev
80f083e141 Increase speed and responsiveness of titlebar reveal 2013-10-05 00:23:08 +00:00
Sloth
91d37b38cc - Fixed Ghazban Ogre. 2013-10-04 19:55:21 +00:00
Chris
9349fa9217 [maven-release-plugin] prepare for next development iteration 2013-10-04 16:25:23 +00:00
Chris
748523f9cf [maven-release-plugin] prepare release forge-1.5.2 2013-10-04 16:25:13 +00:00
Chris
e64336abe9 - Preparing the changes.txt file for the next beta build and release. 2013-10-04 16:14:56 +00:00
RumbleBBU
15450a7fba - Added supporting infrastructure for manipulating the initial card distribution in new Quest games. The user interface components that actually enable this feature will be added after the next beta.
- Lots of checkstyle fixes to old issues.
2013-10-04 11:52:56 +00:00
drdev
ad2f63cd8f Fix so Forge menu items update from skin change properly 2013-10-04 06:43:41 +00:00
drdev
dbd3d956fb Avoid losing hidden title bar setting when switching to window mode and back to full screen 2013-10-04 06:12:15 +00:00
drdev
aabb4399ed Support temporarily revealing hidden title bar by moving mouse to top of screen 2013-10-04 05:27:18 +00:00
drdev
a99386ac1d Show clock in titlebar if maximized and status bar hidden 2013-10-04 02:43:14 +00:00
Sloth
b400f22d15 - Fixed description of Nest Invader. 2013-10-03 18:12:03 +00:00
moomarc
8843054b89 - New skin added 2013-10-03 16:34:14 +00:00
Sloth
8c8bde0799 - Fixed cost lists not using LKI's (once again). 2013-10-03 11:23:21 +00:00
drdev
109a990b28 Dim status bar text color and fix padding right of clock 2013-10-03 09:26:31 +00:00
drdev
8e9ac2e3e7 Make status bar text look better aligned 2013-10-03 09:05:44 +00:00
drdev
c0ad9b1b71 Update CHANGES.txt for Forge button changes 2013-10-03 08:37:11 +00:00
Maxmtg
01609a1f29 adjusted visibility of listInSync, removed unused import, removed test that does not test draft rankings anyway 2013-10-03 08:26:30 +00:00
drdev
2f5219615d Create Forge button which, when clicked, displays popup menu containing items from old menu bar
Support hiding status bar (F12) and saving title bar and status bar visibility between sessions
Changed F1 to be a shortcut for launching the Forge wiki
2013-10-03 08:26:11 +00:00
spr
91cd93783f - Fix: Constructed home screen "Game" menu was not being cleared from MenuBar when switching to other home screens; Menubar was not being repainted properly; 2013-10-03 08:17:44 +00:00
Maxmtg
2d8e4e9053 removed some (not all) slowdonws in quest card shop when owner of a big collection purchases a fatpack. (there were N redraws of owned card list instead of just one) 2013-10-03 08:06:10 +00:00
swordshine
9500b3a5ae - Updated scripts 2013-10-03 07:37:00 +00:00
swordshine
84e124bbf5 - Updated Cube by Juzamjedi 2013-10-03 07:28:58 +00:00
drdev
0d0377ae4a Remove clock from titlebar 2013-10-03 02:14:25 +00:00
drdev
9e1ec67253 Move version and clock to new status bar 2013-10-03 02:11:03 +00:00
drdev
4f8ee5ea52 Increase height of titlebar and size of minimize, maximize, and close icons
Fix so minimize, maximize, and close buttons allow clicking very top of screen and allow canceling click by moving mouse away before releasing
2013-10-03 00:50:31 +00:00
drdev
56654ffcb1 Cache time formatter 2013-10-03 00:28:55 +00:00
Chris
ac48d3634b fixed 2 broken pic URLs. 2013-10-02 13:45:03 +00:00
spr
934e4cbb2c - Constructed game now remembers the last deck played (both left and right decks) and restores at next startup.
- Default human deck type is Preconstructed decks.
2013-10-02 13:35:09 +00:00
Chris
53e631d65f - Cleared out the changes.txt file, now ready for new material. 2013-10-02 12:47:46 +00:00
swordshine
1483d7c969 - update some SVars 2013-10-02 08:29:48 +00:00
spr
71ddee1033 - Constructed screen updates based on feedback. 2013-10-02 06:54:59 +00:00
Maxmtg
480bba7a8c making multimaps in combat class synchronized 2013-10-01 20:40:28 +00:00
Chris
9c4c529c62 [maven-release-plugin] prepare for next development iteration 2013-10-01 14:22:19 +00:00
16844 changed files with 109382 additions and 87893 deletions

View File

@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/> <classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>

31479
.gitattributes vendored

File diff suppressed because it is too large Load Diff

27
.gitignore vendored
View File

@@ -2,20 +2,29 @@
/*.iml /*.iml
/*.tmp /*.tmp
/.metadata /.metadata
forge-ai/forge-ai.iml
forge-ai/target
forge-core/forge-core.iml
forge-core/target
forge-game/target
forge-gui/forge-gui.iml
forge-gui/forge.profile.properties
forge-gui/res/*.log
forge-gui/res/PerSetTrackingResults
forge-gui/res/cardsfolder/*.bat
forge-gui/res/decks
forge-gui/res/layouts
forge-gui/res/pics*
forge-gui/res/pics_product
forge-gui/target
forge-gui/tools/PerSetTrackingResults
forge-gui/tools/oracleScript.log
forge-net/target
/forge.profile.properties /forge.profile.properties
/nbactions.xml /nbactions.xml
/pom.xml.next /pom.xml.next
/pom.xml.releaseBackup /pom.xml.releaseBackup
/pom.xml.tag /pom.xml.tag
/release.properties /release.properties
res/*.log
res/PerSetTrackingResults
res/cardsfolder/*.bat
res/decks
res/layouts
res/pics*
res/pics_product
/target /target
/test-output /test-output
tools/PerSetTrackingResults
tools/oracleScript.log

View File

@@ -4,36 +4,7 @@
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures> <natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

View File

@@ -1,101 +0,0 @@
Forge Beta: 10-01-2013 ver 1.5.1
13328 cards in total.
-------------
Release Notes
-------------
- Forge freezing during a match bug -
A number of people have reported this bug and we now feel that it may have been fixed in this version. Please play test this version and let us know.
- Skinned titlebar for main window -
Titlebar is now skinned instead of displaying using standard OS window titlebar
Maximizing window now displays full-screen
Can use Layout > View > Titlebar (F11) to toggle visibility of titlebar (will also open full-screen if hiding titlebar)
- Forge now requires Java 7 -
Please update your Java runtime environment. At this point Forge versions 1.4.2 and above will no longer run under Java 6. Those who are using Mac OS should install the JDK version rather than the JRE version.
- The Mac OS X application -
At this time Forge now requires Java 7 and will no longer run under Java 6.
Unfortunately, the Mac OS X builder that we were using does not support Java 7. We hope to find and to use a different Mac OS X builder in order to continue releasing a Mac OS bundled application like we have in the past.
Currently, the windows/unix release of Forge includes a launcher file named "forge.command". Double click on the "forge.command" launcher command file and this will in turn launch the Forge jar file via the terminal application while increasing the Java heap space. This should be a temporary inconvenience.
---------
New Cards
---------
Chaos Moon
Deep Water
Infernal Darkness
Mana Reflection
Mausoleum Turnkey
Naked Singularity
Pale Moon
Pulse of Llanowar
Reality Twist
Ritual of Subdual
-----------
New Schemes
-----------
Nature Demands an Offering
--------------------
New Vanguard Avatars
--------------------
Mirri
------------
Known Issues
------------
Several people have noticed that the cards displayed on the battlefield will fail to be displayed when the number of cards on the battlefield increases. Maximizing the human panel can help to re-display the cards.
Some time was spent turning the static ETB triggers into the proper ETB replacement effects they should be, mainly to interact correctly with each other. This work is not yet finished. As a result there is currently some inconsistencies with "Enters the battlefield with counters" (Not incredibly noticeable).
A recent contribution to the code base should fix some of the bugs that people noticed with cloning type abilities. At this time there is one remaining issue that we hope will be addressed in the near future:
Copies of cards that setup Zone Change triggers via addComesIntoPlayCommand and addLeavesPlayCommand will not function correctly.
The Forge archive includes a readme.txt file and we ask that you spend a few minutes reading this file as it contains some information that may prove useful. We do tend to update this file at times and you should quickly read this file and look for new information for each and every new release. Thank you.
The archive format used for the Forge distribution is ".tar.bz2". There are utilities for Windows, Mac OS and the various *nix's that can be used to extract/decompress these ".tar.bz2" archives. We recommend that you extract/decompress the Forge archive into a new and unused folder.
Some people use the Windows application 7zip. This utility can be found at http://www.7-zip.org/download.html. Mac users can double click on the archive and the application Archive Utility will launch and extract the archive. Mac users do not need to download a separate utility.
----------------------------
Contributors to This Release
----------------------------
DrDev
Dripton
Gos
Hellfish
Max
Sloth
spr
Swordshine
Chris H
(Quest icons used created by Teekatas, from his Legendora set http://raindropmemory.deviantart.com)
(Thanks to the MAGE team for permission to use their targeting arrows.)
(Thanks to http://www.freesound.org/browse/ for providing some sound files.)
end

9
forge-ai/.classpath Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

23
forge-ai/.project Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>forge-ai</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//src/main/java=ISO-8859-1
encoding//src/test/java=ISO-8859-1
encoding/<project>=ISO-8859-1

View File

@@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

28
forge-ai/pom.xml Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.5.12</version>
</parent>
<artifactId>forge-ai</artifactId>
<name>Forge AI</name>
<dependencies>
<dependency>
<groupId>forge</groupId>
<artifactId>forge-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>forge</groupId>
<artifactId>forge-game</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

9
forge-core/.classpath Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

23
forge-core/.project Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>forge-core</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//src/main/java=ISO-8859-1
encoding//src/test/java=ISO-8859-1
encoding/<project>=ISO-8859-1

View File

@@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

27
forge-core/pom.xml Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.5.12</version>
</parent>
<artifactId>forge-core</artifactId>
<name>Forge Core</name>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>15.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,375 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.lang3.time.StopWatch;
import forge.card.CardRules;
import forge.util.FileUtil;
import forge.util.ThreadUtil;
/**
* <p>
* CardReader class.
* </p>
*
* @author Forge
* @version $Id: CardStorageReader.java 23742 2013-11-22 16:32:56Z Max mtg $
*/
public class CardStorageReader {
public interface Observer {
public void cardLoaded(CardRules rules, List<String> lines, File fileOnDisk);
}
public interface ProgressObserver{
void setOperationName(String name, boolean usePercents);
void report(int current, int total);
// does nothing, used when they pass null instead of an instance
public final static ProgressObserver emptyObserver = new ProgressObserver() {
@Override public void setOperationName(String name, boolean usePercents) {}
@Override public void report(int current, int total) {}
};
}
private static final String CARD_FILE_DOT_EXTENSION = ".txt";
/** Default charset when loading from files. */
public static final String DEFAULT_CHARSET_NAME = "US-ASCII";
private final boolean useThreadPool = ThreadUtil.isMultiCoreSystem();
private final static int NUMBER_OF_PARTS = 25;
private final ProgressObserver progressObserver;
private transient File cardsfolder;
private transient ZipFile zip;
private final transient Charset charset;
private final Observer observer;
// 8/18/11 10:56 PM
/**
* <p>
* Constructor for CardReader.
* </p>
*
* @param theCardsFolder
* indicates location of the cardsFolder
* @param useZip
* if true, attempts to load cards from a zip file, if one
* exists.
*/
public CardStorageReader(String cardDataDir, CardStorageReader.ProgressObserver progressObserver, Observer observer) {
this.progressObserver = progressObserver != null ? progressObserver : CardStorageReader.ProgressObserver.emptyObserver;
this.cardsfolder = new File(cardDataDir);
this.observer = observer;
// These read data for lightweight classes.
if (!cardsfolder.exists()) {
throw new RuntimeException("CardReader : constructor error -- " + cardsfolder.getAbsolutePath() + " file/folder not found.");
}
if (!cardsfolder.isDirectory()) {
throw new RuntimeException("CardReader : constructor error -- not a directory -- " + cardsfolder.getAbsolutePath());
}
final File zipFile = new File(cardsfolder, "cardsfolder.zip");
if (zipFile.exists()) {
try {
this.zip = new ZipFile(zipFile);
} catch (final Exception exn) {
System.err.printf("Error reading zip file \"%s\": %s. Defaulting to txt files in \"%s\".%n", zipFile.getAbsolutePath(), exn, cardsfolder.getAbsolutePath());
}
}
this.charset = Charset.forName(CardStorageReader.DEFAULT_CHARSET_NAME);
} // CardReader()
private final List<CardRules> loadCardsInRange(final List<File> files, int from, int to) {
CardRules.Reader rulesReader = new CardRules.Reader();
List<CardRules> result = new ArrayList<CardRules>();
for(int i = from; i < to; i++) {
File cardTxtFile = files.get(i);
result.add(this.loadCard(rulesReader, cardTxtFile));
}
return result;
}
private final List<CardRules> loadCardsInRangeFromZip(final List<ZipEntry> files, int from, int to) {
CardRules.Reader rulesReader = new CardRules.Reader();
List<CardRules> result = new ArrayList<CardRules>();
for(int i = from; i < to; i++) {
ZipEntry ze = files.get(i);
// if (ze.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION)) // already filtered!
result.add(this.loadCard(rulesReader, ze));
}
return result;
}
/**
* Starts reading cards into memory until the given card is found.
*
* After that, we save our place in the list of cards (on disk) in case we
* need to load more.
*
* @return the Card or null if it was not found.
*/
public final Iterable<CardRules> loadCards() {
progressObserver.setOperationName("Loading cards, examining folder", true);
// Iterate through txt files or zip archive.
// Report relevant numbers to progress monitor model.
Set<CardRules> result = new TreeSet<CardRules>(new Comparator<CardRules>() {
@Override
public int compare(CardRules o1, CardRules o2) {
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
}
});
final List<File> allFiles = collectCardFiles(new ArrayList<File>(), this.cardsfolder);
if(!allFiles.isEmpty()) {
int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3;
if( allFiles.size() < fileParts * 100)
fileParts = allFiles.size() / 100; // to avoid creation of many threads for a dozen of files
final CountDownLatch cdlFiles = new CountDownLatch(fileParts);
List<Callable<List<CardRules>>> taskFiles = makeTaskListForFiles(allFiles, cdlFiles);
progressObserver.setOperationName("Loading cards from folders", true);
progressObserver.report(0, taskFiles.size());
StopWatch sw = new StopWatch();
sw.start();
executeLoadTask(result, taskFiles, cdlFiles);
sw.stop();
final long timeOnParse = sw.getTime();
System.out.printf("Read cards: %s files in %d ms (%d parts) %s%n", allFiles.size(), timeOnParse, taskFiles.size(), useThreadPool ? "using thread pool" : "in same thread");
}
if( this.zip != null ) {
final CountDownLatch cdlZip = new CountDownLatch(NUMBER_OF_PARTS);
List<Callable<List<CardRules>>> taskZip = new ArrayList<>();
ZipEntry entry;
List<ZipEntry> entries = new ArrayList<ZipEntry>();
// zipEnum was initialized in the constructor.
Enumeration<? extends ZipEntry> zipEnum = this.zip.entries();
while (zipEnum.hasMoreElements()) {
entry = zipEnum.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION))
continue;
entries.add(entry);
}
taskZip = makeTaskListForZip(entries, cdlZip);
progressObserver.setOperationName("Loading cards from archive", true);
progressObserver.report(0, taskZip.size());
StopWatch sw = new StopWatch();
sw.start();
executeLoadTask(result, taskZip, cdlZip);
sw.stop();
final long timeOnParse = sw.getTime();
System.out.printf("Read cards: %s archived files in %d ms (%d parts) %s%n", this.zip.size(), timeOnParse, taskZip.size(), useThreadPool ? "using thread pool" : "in same thread");
}
return result;
} // loadCardsUntilYouFind(String)
private void executeLoadTask(Collection<CardRules> result, final List<Callable<List<CardRules>>> tasks, CountDownLatch cdl) {
try {
if ( useThreadPool ) {
final ExecutorService executor = ThreadUtil.getComputingPool(0.5f);
final List<Future<List<CardRules>>> parts = executor.invokeAll(tasks);
executor.shutdown();
cdl.await();
for(Future<List<CardRules>> pp : parts) {
result.addAll(pp.get());
}
} else {
for(Callable<List<CardRules>> c : tasks) {
result.addAll(c.call());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (Exception e) { // this clause comes from non-threaded branch
throw new RuntimeException(e);
}
}
private List<Callable<List<CardRules>>> makeTaskListForZip(final List<ZipEntry> entries, final CountDownLatch cdl) {
int totalFiles = entries.size();
final int maxParts = (int) cdl.getCount();
int filesPerPart = totalFiles / maxParts;
final List<Callable<List<CardRules>>> tasks = new ArrayList<Callable<List<CardRules>>>();
for (int iPart = 0; iPart < maxParts; iPart++) {
final int from = iPart * filesPerPart;
final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart;
tasks.add(new Callable<List<CardRules>>() {
@Override
public List<CardRules> call() throws Exception{
List<CardRules> res = loadCardsInRangeFromZip(entries, from, till);
cdl.countDown();
progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
return res;
}
});
}
return tasks;
}
private List<Callable<List<CardRules>>> makeTaskListForFiles(final List<File> allFiles, final CountDownLatch cdl) {
int totalFiles = allFiles.size();
final int maxParts = (int) cdl.getCount();
int filesPerPart = totalFiles / maxParts;
final List<Callable<List<CardRules>>> tasks = new ArrayList<Callable<List<CardRules>>>();
for (int iPart = 0; iPart < maxParts; iPart++) {
final int from = iPart * filesPerPart;
final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart;
tasks.add(new Callable<List<CardRules>>() {
@Override
public List<CardRules> call() throws Exception{
List<CardRules> res = loadCardsInRange(allFiles, from, till);
cdl.countDown();
progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
return res;
}
});
}
return tasks;
}
public static List<File> collectCardFiles(List<File> accumulator, File startDir) {
String[] list = startDir.list();
for (String filename : list) {
File entry = new File(startDir, filename);
if (!entry.isDirectory()) {
if (entry.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION))
accumulator.add(entry);
continue;
}
if (filename.startsWith(".")) {
continue;
}
collectCardFiles(accumulator, entry);
}
return accumulator;
}
private List<String> readScript(final InputStream inputStream) {
return FileUtil.readAllLines(new InputStreamReader(inputStream, this.charset), true);
}
/**
* Load a card from a txt file.
*
* @param pathToTxtFile
* the full or relative path to the file to load
*
* @return a new Card instance
*/
protected final CardRules loadCard(final CardRules.Reader reader, final File file) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
reader.reset();
List<String> lines = readScript(fileInputStream);
CardRules rules = reader.readCard(lines);
if ( null != observer )
observer.cardLoaded(rules, lines, file);
return rules;
} catch (final FileNotFoundException ex) {
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
} finally {
try {
fileInputStream.close();
} catch (final IOException ignored) {
// 11:08
// PM
}
}
}
/**
* Load a card from an entry in a zip file.
*
* @param entry
* to load from
*
* @return a new Card instance
*/
protected final CardRules loadCard(final CardRules.Reader rulesReader, final ZipEntry entry) {
InputStream zipInputStream = null;
try {
zipInputStream = this.zip.getInputStream(entry);
rulesReader.reset();
CardRules rules = rulesReader.readCard(readScript(zipInputStream));
return rules;
} catch (final IOException exn) {
throw new RuntimeException(exn);
// PM
} finally {
try {
if (zipInputStream != null) {
zipInputStream.close();
}
} catch (final IOException ignored) {
// 11:08
// PM
}
}
}
}

View File

@@ -0,0 +1,102 @@
package forge;
import java.io.File;
import java.util.Map;
import java.util.TreeMap;
import forge.card.CardDb;
import forge.card.CardEdition;
import forge.card.CardRules;
import forge.card.PrintSheet;
import forge.item.FatPack;
import forge.item.SealedProduct;
import forge.util.storage.IStorage;
import forge.util.storage.StorageBase;
/**
* The class holding game invariants, such as cards, editions, game formats. All that data, which is not supposed to be changed by player
*
* @author Max
*/
public class StaticData {
private final CardDb commonCards;
private final CardDb variantCards;
private final CardEdition.Collection editions;
private final IStorage<SealedProduct.Template> boosters;
private final IStorage<SealedProduct.Template> specialBoosters;
private final IStorage<SealedProduct.Template> tournaments;
private final IStorage<FatPack.Template> fatPacks;
private final IStorage<PrintSheet> printSheets;
private static StaticData lastInstance = null;
public StaticData(CardStorageReader reader, String editionFolder, String blockDataFolder) {
this.editions = new CardEdition.Collection(new CardEdition.Reader(new File(editionFolder)));
lastInstance = this;
final Map<String, CardRules> regularCards = new TreeMap<String, CardRules>(String.CASE_INSENSITIVE_ORDER);
final Map<String, CardRules> variantsCards = new TreeMap<String, CardRules>(String.CASE_INSENSITIVE_ORDER);
for (CardRules card : reader.loadCards()) {
if (null == card) continue;
final String cardName = card.getName();
if ( card.isVariant() ) {
variantsCards.put(cardName, card);
}
else {
regularCards.put(cardName, card);
}
}
commonCards = new CardDb(regularCards, editions, false, false);
variantCards = new CardDb(variantsCards, editions, false, false);
this.boosters = new StorageBase<SealedProduct.Template>("Boosters", editions.getBoosterGenerator());
this.specialBoosters = new StorageBase<SealedProduct.Template>("Special boosters", new SealedProduct.Template.Reader(new File(blockDataFolder, "boosters-special.txt")));
this.tournaments = new StorageBase<SealedProduct.Template>("Starter sets", new SealedProduct.Template.Reader(new File(blockDataFolder, "starters.txt")));
this.fatPacks = new StorageBase<FatPack.Template>("Fat packs", new FatPack.Template.Reader("res/blockdata/fatpacks.txt"));
this.printSheets = new StorageBase<PrintSheet>("Special print runs", new PrintSheet.Reader(new File(blockDataFolder, "printsheets.txt")));
}
public final static StaticData instance() {
return lastInstance;
}
public final CardEdition.Collection getEditions() {
return this.editions;
}
/** @return {@link forge.util.storage.IStorageView}<{@link forge.item.FatPackTemplate}> */
public IStorage<FatPack.Template> getFatPacks() {
return fatPacks;
}
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.BoosterTemplate}> */
public final IStorage<SealedProduct.Template> getTournamentPacks() {
return tournaments;
}
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.BoosterTemplate}> */
public final IStorage<SealedProduct.Template> getBoosters() {
return boosters;
}
public final IStorage<SealedProduct.Template> getSpecialBoosters() {
return specialBoosters;
}
public IStorage<PrintSheet> getPrintSheets() {
return printSheets;
}
public CardDb getCommonCards() {
return commonCards;
}
public CardDb getVariantCards() {
return variantCards;
}
}

View File

@@ -34,10 +34,10 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.Singletons; import forge.StaticData;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.item.IPaperCard; import forge.item.IPaperCard;
import forge.item.PrintSheet; import forge.item.SealedProduct;
import forge.util.TextUtil; import forge.util.TextUtil;
/** /**
@@ -50,26 +50,15 @@ import forge.util.TextUtil;
*/ */
public class BoosterGenerator { public class BoosterGenerator {
private static final String LAND = "Land";
public static final String ANY = "Any";
public static final String COMMON = "Common";
public static final String UNCOMMON = "Uncommon";
public static final String UNCOMMON_RARE = "UncommonRare";
public static final String RARE = "Rare";
public static final String RARE_MYTHIC = "RareMythic";
public static final String MYTHIC = "Mythic";
public static final String BASIC_LAND = "BasicLand";
public static final String TIME_SHIFTED = "TimeShifted";
private final static Map<String, PrintSheet> cachedSheets = new TreeMap<String, PrintSheet>(String.CASE_INSENSITIVE_ORDER); private final static Map<String, PrintSheet> cachedSheets = new TreeMap<String, PrintSheet>(String.CASE_INSENSITIVE_ORDER);
private static final synchronized PrintSheet getPrintSheet(String key) { private static final synchronized PrintSheet getPrintSheet(String key) {
if( !cachedSheets.containsKey(key) ) if( !cachedSheets.containsKey(key) )
cachedSheets.put(key, makeSheet(key, CardDb.instance().getAllCards())); cachedSheets.put(key, makeSheet(key, StaticData.instance().getCommonCards().getAllCards()));
return cachedSheets.get(key); return cachedSheets.get(key);
} }
public static final List<PaperCard> getBoosterPack(SealedProductTemplate booster) { public static final List<PaperCard> getBoosterPack(SealedProduct.Template booster) {
List<PaperCard> result = new ArrayList<PaperCard>(); List<PaperCard> result = new ArrayList<PaperCard>();
for(Pair<String, Integer> slot : booster.getSlots()) { for(Pair<String, Integer> slot : booster.getSlots()) {
String slotType = slot.getLeft(); // add expansion symbol here? String slotType = slot.getLeft(); // add expansion symbol here?
@@ -77,7 +66,7 @@ public class BoosterGenerator {
String[] sType = TextUtil.splitWithParenthesis(slotType, ' '); String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
String setCode = sType.length == 1 && booster.getEdition() != null ? booster.getEdition() : null; String setCode = sType.length == 1 && booster.getEdition() != null ? booster.getEdition() : null;
String sheetKey = Singletons.getModel().getEditions().contains(setCode) ? slotType.trim() + " " + setCode: slotType.trim(); String sheetKey = StaticData.instance().getEditions().contains(setCode) ? slotType.trim() + " " + setCode: slotType.trim();
PrintSheet ps = getPrintSheet(sheetKey); PrintSheet ps = getPrintSheet(sheetKey);
result.addAll(ps.random(numCards, true)); result.addAll(ps.random(numCards, true));
@@ -100,7 +89,7 @@ public class BoosterGenerator {
String mainCode = itMod.next(); String mainCode = itMod.next();
if ( mainCode.regionMatches(true, 0, "fromSheet", 0, 9)) { // custom print sheet if ( mainCode.regionMatches(true, 0, "fromSheet", 0, 9)) { // custom print sheet
String sheetName = StringUtils.strip(mainCode.substring(9), "()\" "); String sheetName = StringUtils.strip(mainCode.substring(9), "()\" ");
src = Singletons.getModel().getPrintSheets().get(sheetName).toFlatList(); src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
setPred = Predicates.alwaysTrue(); setPred = Predicates.alwaysTrue();
} else if (mainCode.startsWith("promo")) { // get exactly the named cards, that's a tiny inlined print sheet } else if (mainCode.startsWith("promo")) { // get exactly the named cards, that's a tiny inlined print sheet
@@ -108,7 +97,7 @@ public class BoosterGenerator {
String[] cardNames = TextUtil.splitWithParenthesis(list, ',', '"', '"'); String[] cardNames = TextUtil.splitWithParenthesis(list, ',', '"', '"');
List<PaperCard> srcList = new ArrayList<PaperCard>(); List<PaperCard> srcList = new ArrayList<PaperCard>();
for(String cardName: cardNames) for(String cardName: cardNames)
srcList.add(CardDb.instance().getCard(cardName)); srcList.add(StaticData.instance().getCommonCards().getCard(cardName));
src = srcList; src = srcList;
setPred = Predicates.alwaysTrue(); setPred = Predicates.alwaysTrue();
@@ -121,18 +110,18 @@ public class BoosterGenerator {
// only special operators should remain by now - the ones that could not be turned into one predicate // only special operators should remain by now - the ones that could not be turned into one predicate
String mainCode = operators.isEmpty() ? null : operators.get(0).trim(); String mainCode = operators.isEmpty() ? null : operators.get(0).trim();
if( null == mainCode || mainCode.equalsIgnoreCase(ANY) ) { // no restriction on rarity if( null == mainCode || mainCode.equalsIgnoreCase(BoosterSlots.ANY) ) { // no restriction on rarity
Predicate<PaperCard> predicate = Predicates.and(setPred, extraPred); Predicate<PaperCard> predicate = Predicates.and(setPred, extraPred);
ps.addAll(Iterables.filter(src, predicate)); ps.addAll(Iterables.filter(src, predicate));
} else if ( mainCode.equalsIgnoreCase(UNCOMMON_RARE) ) { // for sets like ARN, where U1 cards are considered rare and U3 are uncommon } else if ( mainCode.equalsIgnoreCase(BoosterSlots.UNCOMMON_RARE) ) { // for sets like ARN, where U1 cards are considered rare and U3 are uncommon
Predicate<PaperCard> predicateRares = Predicates.and(setPred, IPaperCard.Predicates.Presets.IS_RARE, extraPred); Predicate<PaperCard> predicateRares = Predicates.and(setPred, IPaperCard.Predicates.Presets.IS_RARE, extraPred);
ps.addAll(Iterables.filter(src, predicateRares)); ps.addAll(Iterables.filter(src, predicateRares));
Predicate<PaperCard> predicateUncommon = Predicates.and( setPred, IPaperCard.Predicates.Presets.IS_UNCOMMON, extraPred); Predicate<PaperCard> predicateUncommon = Predicates.and( setPred, IPaperCard.Predicates.Presets.IS_UNCOMMON, extraPred);
ps.addAll(Iterables.filter(src, predicateUncommon), 3); ps.addAll(Iterables.filter(src, predicateUncommon), 3);
} else if ( mainCode.equalsIgnoreCase(RARE_MYTHIC) ) { } else if ( mainCode.equalsIgnoreCase(BoosterSlots.RARE_MYTHIC) ) {
// Typical ratio of rares to mythics is 53:15, changing to 35:10 in smaller sets. // Typical ratio of rares to mythics is 53:15, changing to 35:10 in smaller sets.
// To achieve the desired 1:8 are all mythics are added once, and all rares added twice per print sheet. // To achieve the desired 1:8 are all mythics are added once, and all rares added twice per print sheet.
@@ -168,13 +157,13 @@ public class BoosterGenerator {
Predicate<PaperCard> toAdd = null; Predicate<PaperCard> toAdd = null;
if( operator.equalsIgnoreCase("dfc") ) { toAdd = Predicates.compose(CardRulesPredicates.splitType(CardSplitType.Transform), PaperCard.FN_GET_RULES); if( operator.equalsIgnoreCase("dfc") ) { toAdd = Predicates.compose(CardRulesPredicates.splitType(CardSplitType.Transform), PaperCard.FN_GET_RULES);
} else if ( operator.equalsIgnoreCase(LAND) ) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES); } else if ( operator.equalsIgnoreCase(BoosterSlots.LAND) ) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES);
} else if ( operator.equalsIgnoreCase(BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND; } else if ( operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND;
} else if ( operator.equalsIgnoreCase(TIME_SHIFTED)) { toAdd = IPaperCard.Predicates.Presets.IS_SPECIAL; } else if ( operator.equalsIgnoreCase(BoosterSlots.TIME_SHIFTED)) { toAdd = IPaperCard.Predicates.Presets.IS_SPECIAL;
} else if ( operator.equalsIgnoreCase(MYTHIC)) { toAdd = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE; } else if ( operator.equalsIgnoreCase(BoosterSlots.MYTHIC)) { toAdd = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE;
} else if ( operator.equalsIgnoreCase(RARE)) { toAdd = IPaperCard.Predicates.Presets.IS_RARE; } else if ( operator.equalsIgnoreCase(BoosterSlots.RARE)) { toAdd = IPaperCard.Predicates.Presets.IS_RARE;
} else if ( operator.equalsIgnoreCase(UNCOMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_UNCOMMON; } else if ( operator.equalsIgnoreCase(BoosterSlots.UNCOMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_UNCOMMON;
} else if ( operator.equalsIgnoreCase(COMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_COMMON; } else if ( operator.equalsIgnoreCase(BoosterSlots.COMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_COMMON;
} else if ( operator.startsWith("name(") ) { } else if ( operator.startsWith("name(") ) {
operator = StringUtils.strip(operator.substring(4), "() "); operator = StringUtils.strip(operator.substring(4), "() ");
String[] cardNames = TextUtil.splitWithParenthesis(operator, ',', '"', '"'); String[] cardNames = TextUtil.splitWithParenthesis(operator, ',', '"', '"');

View File

@@ -0,0 +1,14 @@
package forge.card;
public class BoosterSlots {
public static final String LAND = "Land";
public static final String ANY = "Any";
public static final String COMMON = "Common";
public static final String UNCOMMON = "Uncommon";
public static final String UNCOMMON_RARE = "UncommonRare";
public static final String RARE = "Rare";
public static final String RARE_MYTHIC = "RareMythic";
public static final String MYTHIC = "Mythic";
public static final String BASIC_LAND = "BasicLand";
public static final String TIME_SHIFTED = "TimeShifted";
}

View File

@@ -1,4 +1,4 @@
package forge; package forge.card;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.

View File

@@ -0,0 +1,511 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import forge.card.CardEdition.CardInSet;
import forge.card.CardEdition.Type;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.CollectionSuppliers;
import forge.util.Lang;
import forge.util.MyRandom;
import forge.util.TextUtil;
public final class CardDb implements ICardDatabase {
public final static String foilSuffix = "+";
public final static char NameSetSeparator = '|';
// need this to obtain cardReference by name+set+artindex
private final ListMultimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<String,Collection<PaperCard>>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.<PaperCard>arrayLists());
private final Map<String, PaperCard> uniqueCardsByName = new TreeMap<String, PaperCard>(String.CASE_INSENSITIVE_ORDER);
private final Map<String, CardRules> rulesByName;
private final List<PaperCard> allCards = new ArrayList<PaperCard>();
private final List<PaperCard> roAllCards = Collections.unmodifiableList(allCards);
private final Collection<PaperCard> roUniqueCards = Collections.unmodifiableCollection(uniqueCardsByName.values());
private final CardEdition.Collection editions;
public enum SetPreference {
Latest(false),
LatestCoreExp(true),
Earliest(false),
EarliestCoreExp(true),
Random(false);
final boolean filterSets;
private SetPreference(boolean filterIrregularSets) {
filterSets = filterIrregularSets;
}
public boolean accept(CardEdition ed) {
return !filterSets || ed.getType() == Type.CORE || ed.getType() == Type.EXPANSION || ed.getType() == Type.REPRINT;
}
}
// NO GETTERS/SETTERS HERE!
private static class CardRequest {
public String cardName;
public String edition;
public int artIndex;
public boolean isFoil;
public CardRequest(String name, String edition, int artIndex, boolean isFoil) {
cardName = name;
this.edition = edition;
this.artIndex = artIndex;
this.isFoil = isFoil;
}
public static CardRequest fromString(String name) {
boolean isFoil = name.endsWith(foilSuffix);
if( isFoil )
name = name.substring(0, name.length() - foilSuffix.length());
String[] nameParts = TextUtil.split(name, NameSetSeparator);
int setPos = nameParts.length >= 2 && !StringUtils.isNumeric(nameParts[1]) ? 1 : -1;
int artPos = nameParts.length >= 2 && StringUtils.isNumeric(nameParts[1]) ? 1 : nameParts.length >= 3 && StringUtils.isNumeric(nameParts[2]) ? 2 : -1;
String cardName = nameParts[0];
if( cardName.endsWith(foilSuffix)) {
cardName = cardName.substring(0, cardName.length() - foilSuffix.length());
isFoil = true;
}
int artIndex = artPos > 0 ? Integer.parseInt(nameParts[artPos]) : 0;
String setName = setPos > 0 ? nameParts[setPos] : null;
if( "???".equals(setName) )
setName = null;
return new CardRequest(cardName, setName, artIndex, isFoil);
}
}
public CardDb(Map<String, CardRules> rules, CardEdition.Collection editions0, boolean logMissingPerEdition, boolean logMissingSummary) {
this.rulesByName = rules;
this.editions = editions0;
Set<String> allMissingCards = new LinkedHashSet<String>();
List<String> missingCards = new ArrayList<String>();
for(CardEdition e : editions.getOrderedEditions()) {
boolean isCoreExpSet = e.getType() == CardEdition.Type.CORE || e.getType() == CardEdition.Type.EXPANSION || e.getType() == CardEdition.Type.REPRINT;
if(logMissingPerEdition && isCoreExpSet)
System.out.print(e.getName() + " (" + e.getCards().length + " cards)");
String lastCardName = null;
int artIdx = 1;
for(CardEdition.CardInSet cis : e.getCards()) {
if ( cis.name.equals(lastCardName) )
artIdx++;
else {
artIdx = 1;
lastCardName = cis.name;
}
CardRules cr = rulesByName.get(lastCardName);
if( cr != null )
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx));
else
missingCards.add(cis.name);
}
if(isCoreExpSet && logMissingPerEdition) {
if(missingCards.isEmpty())
System.out.println(" ... 100% ");
else {
int missing = (e.getCards().length - missingCards.size()) * 10000 / e.getCards().length;
System.out.printf(" ... %.2f%% (%s missing: %s )%n", missing * 0.01f, Lang.nounWithAmount(missingCards.size(), "card"), StringUtils.join(missingCards, " | ") );
}
}
if( isCoreExpSet && logMissingSummary )
allMissingCards.addAll(missingCards);
missingCards.clear();
}
if( logMissingSummary ) {
System.out.printf("Totally %d cards not implemented: %s\n", allMissingCards.size(), StringUtils.join(allMissingCards, " | "));
}
for(CardRules cr : rulesByName.values()) {
if( !allCardsByName.containsKey(cr.getName()) )
{
System.err.println("The card " + cr.getName() + " was not assigned to any set. Adding it to UNKNOWN set... to fix see res/cardeditions/ folder. ");
addCard(new PaperCard(cr, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1));
}
}
reIndex();
}
private void addCard(PaperCard paperCard) {
allCardsByName.put(paperCard.getName(), paperCard);
}
private void reIndex() {
uniqueCardsByName.clear();
allCards.clear();
for(Entry<String, Collection<PaperCard>> kv : allCardsByName.asMap().entrySet()) {
uniqueCardsByName.put(kv.getKey(), Iterables.getFirst(kv.getValue(), null));
allCards.addAll(kv.getValue());
}
}
@Override
public PaperCard getCard(final String cardName) {
CardRequest request = CardRequest.fromString(cardName);
return tryGetCard(request);
}
@Override
public PaperCard getCard(final String cardName, String setName) {
CardRequest request = CardRequest.fromString(cardName);
if(setName != null)
request.edition = setName;
return tryGetCard(request);
}
@Override
public PaperCard getCard(final String cardName, String setName, int artIndex) {
CardRequest request = CardRequest.fromString(cardName);
if(setName != null)
request.edition = setName;
if(artIndex > 0)
request.artIndex = artIndex;
return tryGetCard(request);
}
private PaperCard tryGetCard(CardRequest request) {
Collection<PaperCard> cards = allCardsByName.get(request.cardName);
if ( null == cards ) return null;
PaperCard result = null;
String reqEdition = request.edition;
if(reqEdition != null && !editions.contains(reqEdition)) {
CardEdition edition = editions.get(reqEdition);
if( edition != null )
reqEdition = edition.getCode();
}
if ( request.artIndex <= 0 ) { // this stands for 'random art'
List<PaperCard> candidates = new ArrayList<PaperCard>(9); // 9 cards with same name per set is a maximum of what has been printed (Arnchenemy)
for( PaperCard pc : cards ) {
if( pc.getEdition().equalsIgnoreCase(reqEdition) || reqEdition == null )
candidates.add(pc);
}
if (candidates.isEmpty())
return null;
result = Aggregates.random(candidates);
} else {
for( PaperCard pc : cards ) {
if( pc.getEdition().equalsIgnoreCase(reqEdition) && request.artIndex == pc.getArtIndex() ) {
result = pc;
break;
}
}
}
if ( result == null ) return null;
return request.isFoil ? getFoiled(result) : result;
}
@Override
public PaperCard getCardFromEdition(final String cardName, SetPreference fromSet) {
return getCardFromEdition(cardName, null, fromSet);
}
@Override
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final SetPreference fromSet) {
return getCardFromEdition(cardName, printedBefore, fromSet, -1);
}
@Override
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final SetPreference fromSet, int artIndex) {
List<PaperCard> cards = this.allCardsByName.get(cardName);
int sz = cards.size();
if( fromSet == SetPreference.Earliest || fromSet == SetPreference.EarliestCoreExp) {
for(int i = sz - 1 ; i >= 0 ; i--) {
PaperCard pc = cards.get(i);
CardEdition ed = editions.get(pc.getEdition());
if(!fromSet.accept(ed))
continue;
if((artIndex <= 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore)))
return pc;
}
return null;
} else if( fromSet == SetPreference.LatestCoreExp || fromSet == SetPreference.Latest || fromSet == null || fromSet == SetPreference.Random ) {
for(int i = 0 ; i < sz ; i++) {
PaperCard pc = cards.get(i);
CardEdition ed = editions.get(pc.getEdition());
if(!fromSet.accept(ed))
continue;
if((artIndex < 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore))) {
if( fromSet == SetPreference.LatestCoreExp || fromSet == SetPreference.Latest )
return pc;
return cards.get(i + MyRandom.getRandom().nextInt(sz-i));
}
}
return null;
}
return null;
}
public PaperCard getFoiled(PaperCard card0) {
// Here - I am still unsure if there should be a cache Card->Card from unfoiled to foiled, to avoid creation of N instances of single plains
return new PaperCard(card0.getRules(), card0.getEdition(), card0.getRarity(), card0.getArtIndex(), true);
}
@Override
public int getPrintCount(String cardName, String edition) {
int cnt = 0;
for( PaperCard pc : allCardsByName.get(cardName) ) {
if( pc.getEdition().equals(edition) )
cnt++;
}
return cnt;
}
@Override
public int getMaxPrintCount(String cardName) {
int max = -1;
for( PaperCard pc : allCardsByName.get(cardName) ) {
if ( max < pc.getArtIndex() )
max = pc.getArtIndex();
}
return max;
}
@Override
public int getArtCount(String cardName, String setName) {
int cnt = 0;
Collection<PaperCard> cards = allCardsByName.get(cardName);
if ( null == cards ) {
return 0;
}
for ( PaperCard pc : cards ) {
if ( pc.getEdition().equalsIgnoreCase(setName) ) {
cnt++;
}
}
return cnt;
}
// returns a list of all cards from their respective latest editions
@Override
public Collection<PaperCard> getUniqueCards() {
return roUniqueCards;
}
@Override
public List<PaperCard> getAllCards() {
return roAllCards;
}
/** Returns a modifiable list of cards matching the given predicate */
@Override
public List<PaperCard> getAllCards(Predicate<PaperCard> predicate) {
return Lists.newArrayList(Iterables.filter(this.roAllCards, predicate));
}
@Override
public Iterator<PaperCard> iterator() {
return this.roAllCards.iterator();
}
public Predicate<? super PaperCard> wasPrintedInSets(List<String> setCodes) {
return new PredicateExistsInSets(setCodes);
}
private class PredicateExistsInSets implements Predicate<PaperCard> {
private final List<String> sets;
public PredicateExistsInSets(final List<String> wantSets) {
this.sets = wantSets; // maybe should make a copy here?
}
@Override
public boolean apply(final PaperCard subject) {
Collection<PaperCard> cc = allCardsByName.get(subject.getName());
for(PaperCard c : cc)
if (sets.contains(c.getEdition()))
return true;
return false;
}
}
public StringBuilder appendCardToStringBuilder(PaperCard card, StringBuilder sb) {
final boolean hasBadSetInfo = "???".equals(card.getEdition()) || StringUtils.isBlank(card.getEdition());
sb.append(card.getName());
if (!hasBadSetInfo) {
int artCount = getArtCount(card.getName(), card.getEdition());
sb.append(CardDb.NameSetSeparator).append(card.getEdition());
if (artCount > 1) {
sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions
}
}
if(card.isFoil()) {
sb.append(CardDb.foilSuffix);
}
return sb;
}
public String cardToString(PaperCard pc) {
return appendCardToStringBuilder(pc, new StringBuilder()).toString();
}
public PaperCard createUnsuportedCard(String cardName) {
CardRequest request = CardRequest.fromString(cardName);
CardEdition cE = CardEdition.UNKNOWN;
CardRarity cR = CardRarity.Unknown;
// May iterate over editions and find out if there is any card named 'cardName' but not implemented with Forge script.
if( StringUtils.isBlank(request.edition) ) {
for(CardEdition e : editions) {
for(CardInSet cs : e.getCards() ) {
if( cs.name.equals(request.cardName)) {
cE = e;
cR = cs.rarity;
break;
}
}
if (cE != CardEdition.UNKNOWN)
break;
}
} else {
cE = editions.get(request.edition);
if ( cE != null )
for(CardInSet cs : cE.getCards() ) {
if( cs.name.equals(request.cardName)) {
cR = cs.rarity;
break;
}
}
else
cE = CardEdition.UNKNOWN;
}
// Write to log that attempt,
if (cR == CardRarity.Unknown )
System.err.println(String.format("An unknown card found when loading Forge decks: \"%s\" Forge does not know of such a card's existence. Have you mistyped the card name?", cardName));
else
System.err.println(String.format("An unsupported card was requested: \"%s\" from \"%s\" set. We're sorry, but you cannot use this card yet.", request.cardName, cE.getName()));
return new PaperCard(CardRules.getUnsupportedCardNamed(request.cardName), cE.getCode(), cR, 1);
}
private final Editor editor = new Editor();
public Editor getEditor() { return editor; }
public class Editor {
private boolean immediateReindex = true;
public CardRules putCard(CardRules rules) { return putCard(rules, null); /* will use data from editions folder */ }
public CardRules putCard(CardRules rules, List<Pair<String, CardRarity>> whenItWasPrinted){ // works similarly to Map<K,V>, returning prev. value
String cardName = rules.getName();
CardRules result = rulesByName.get(cardName);
if (result != null && result.getName().equals(cardName)){ // change properties only
result.reinitializeFromRules(rules);
return result;
}
result = rulesByName.put(cardName, rules);
// 1. generate all paper cards from edition data we have (either explicit, or found in res/editions, or add to unknown edition)
List<PaperCard> paperCards = new ArrayList<PaperCard>();
if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) {
for(CardEdition e : editions.getOrderedEditions()) {
int artIdx = 1;
for(CardInSet cis : e.getCards()) {
if( !cis.name.equals(cardName) )
continue;
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++));
}
}
} else {
String lastEdition = null;
int artIdx = 0;
for(Pair<String, CardRarity> tuple : whenItWasPrinted){
if(!tuple.getKey().equals(lastEdition)) {
artIdx = 1;
lastEdition = tuple.getKey();
}
CardEdition ed = editions.get(lastEdition);
if(null == ed)
continue;
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++));
}
}
if(paperCards.isEmpty())
paperCards.add(new PaperCard(rules, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1));
// 2. add them to db
for (PaperCard paperCard : paperCards)
addCard(paperCard);
// 3. reindex can be temporary disabled and run after the whole batch of rules is added to db.
if(immediateReindex)
reIndex();
return result;
}
public void removeCard(String name) {
allCardsByName.removeAll(name);
uniqueCardsByName.remove(name);
rulesByName.remove(name);
Iterator<PaperCard> it = allCards.iterator();
while(it.hasNext()) {
PaperCard pc = it.next();
if( pc.getName().equalsIgnoreCase(name))
it.remove();
}
}
public void rebuildIndex() { reIndex(); }
public boolean isImmediateReindex() {
return immediateReindex;
}
public void setImmediateReindex(boolean immediateReindex) {
this.immediateReindex = immediateReindex;
}
}
}

View File

@@ -0,0 +1,486 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.io.File;
import java.io.FilenameFilter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardDb.SetPreference;
import forge.deck.CardPool;
import forge.item.PaperCard;
import forge.item.SealedProduct;
import forge.util.Aggregates;
import forge.util.FileSection;
import forge.util.FileUtil;
import forge.util.IItemReader;
import forge.util.storage.StorageBase;
import forge.util.storage.StorageReaderBase;
import forge.util.storage.StorageReaderFolder;
/**
* <p>
* CardSet class.
* </p>
*
* @author Forge
* @version $Id: CardSet.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardEdition implements Comparable<CardEdition> { // immutable
public enum Type {
UNKNOWN,
CORE,
EXPANSION,
REPRINT,
STARTER,
DUEL_DECKS,
PREMIUM_DECK_SERIES,
FROM_THE_VAULT,
OTHER,
THIRDPARTY // custom sets
}
public enum FoilType {
NOT_SUPPORTED, // sets before Urza's Legacy
OLD_STYLE, // sets between Urza's Legacy and 8th Edition
MODERN // 8th Edition and newer
}
public static class CardInSet {
public final CardRarity rarity;
public final String name;
public CardInSet(final String name, final CardRarity rarity) {
this.rarity = rarity;
this.name = name;
}
}
/** The Constant unknown. */
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
public static final CardEdition UNKNOWN = new CardEdition("1990-01-01", "??", "???", Type.UNKNOWN, "Undefined", FoilType.NOT_SUPPORTED, new CardInSet[]{});
private Date date;
private String code2;
private String code;
private Type type;
private String name;
private String alias = null;
private boolean whiteBorder = false;
private FoilType foilType = FoilType.NOT_SUPPORTED;
private int foilChanceInBooster = 0;
private boolean foilAlwaysInCommonSlot = false;
private final CardInSet[] cards;
private int boosterArts = 1;
private SealedProduct.Template boosterTpl = null;
private CardEdition(CardInSet[] cards) {
this.cards = cards;
}
/**
* Instantiates a new card set.
*
* @param index indicates order of set release date
* @param code2 the 2 (usually) letter code used for image filenames/URLs distributed by the HQ pics team that
* use Magic Workstation-type edition codes. Older sets only had 2-letter codes, and some of the 3-letter
* codes they use now aren't the same as the official list of 3-letter codes. When Forge downloads set-pics,
* it uses the 3-letter codes for the folder no matter the age of the set.
* @param code the MTG 3-letter set code
* @param type the set type
* @param name the name of the set
* @param an optional secondary code alias for the set
*/
private CardEdition(String date, String code2, String code, Type type, String name, FoilType foil, CardInSet[] cards) {
this(cards);
this.code2 = code2;
this.code = code;
this.type = type;
this.name = name;
this.date = parseDate(date);
this.foilType = foil;
}
private static Date parseDate(String date) {
if( date.length() <= 7 )
date = date + "-01";
try {
return formatter.parse(date);
} catch (ParseException e) {
return new Date();
}
}
public Date getDate() { return date; }
public String getCode2() { return code2; }
public String getCode() { return code; }
public Type getType() { return type; }
public String getName() { return name; }
public String getAlias() { return alias; }
public FoilType getFoilType() { return foilType; }
public int getFoilChanceInBooster() { return foilChanceInBooster; }
public boolean getFoilAlwaysInCommonSlot() { return foilAlwaysInCommonSlot; }
public CardInSet[] getCards() { return cards; }
/** The Constant fnGetName. */
public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() {
@Override
public String apply(final CardEdition arg1) {
return arg1.getCode();
}
};
@Override
public int compareTo(final CardEdition o) {
if (o == null) {
return 1;
}
return date.compareTo(o.date);
}
@Override
public int hashCode() {
return (this.code.hashCode() * 17) + this.name.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final CardEdition other = (CardEdition) obj;
return other.name.equals(this.name) && this.code.equals(other.code);
}
@Override
public String toString() {
return this.name + " (set)";
}
/**
* @return the whiteBorder
*/
public boolean isWhiteBorder() {
return whiteBorder;
}
public int getCntBoosterPictures() {
return boosterArts;
}
public SealedProduct.Template getBoosterTemplate() {
return boosterTpl;
}
public boolean hasBoosterTemplate() {
return boosterTpl != null;
}
public static class Reader extends StorageReaderFolder<CardEdition> {
public Reader(File path) {
super(path, CardEdition.FN_GET_CODE);
}
public final static CardInSet[] arrCards = new CardInSet[] {};
@Override
protected CardEdition read(File file) {
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
List<CardEdition.CardInSet> processedCards = new ArrayList<CardEdition.CardInSet>();
for(String line : contents.get("cards")) {
if (StringUtils.isBlank(line))
continue;
// You may omit rarity for early development
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
String cardName = hadRarity ? line.substring(2) : line;
CardInSet cis = new CardInSet(cardName, r);
processedCards.add(cis);
}
CardEdition res = new CardEdition(processedCards.toArray(arrCards));
FileSection section = FileSection.parse(contents.get("metadata"), "=");
res.name = section.get("name");
res.date = parseDate(section.get("date"));
res.code = section.get("code");
res.code2 = section.get("code2");
if( res.code2 == null )
res.code2 = res.code;
res.boosterArts = section.getInt("BoosterCovers", 1);
String boosterDesc = section.get("Booster");
res.boosterTpl = boosterDesc == null ? null : new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(boosterDesc));
res.alias = section.get("alias");
res.whiteBorder = "white".equalsIgnoreCase(section.get("border"));
String type = section.get("type");
Type enumType = Type.UNKNOWN;
if (null != type && !type.isEmpty()) {
try {
enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
} catch (IllegalArgumentException e) {
// ignore; type will get UNKNOWN
System.err.println(String.format("Ignoring unknown type in set definitions: name: %s; type: %s", res.name, type));
}
}
res.type = enumType;
switch(section.get("foil", "newstyle").toLowerCase()) {
case "notsupported":
res.foilType = FoilType.NOT_SUPPORTED;
break;
case "oldstyle":
case "classic":
res.foilType = FoilType.OLD_STYLE;
break;
case "newstyle":
case "modern":
res.foilType = FoilType.MODERN;
break;
default:
res.foilType = FoilType.NOT_SUPPORTED;
break;
}
res.foilChanceInBooster = section.getInt("FoilChanceInBooster", 16);
res.foilAlwaysInCommonSlot = section.getBoolean("FoilAlwaysInCommonSlot", false);
return res;
}
@Override
protected FilenameFilter getFileFilter() {
return TXT_FILE_FILTER;
}
public static final FilenameFilter TXT_FILE_FILTER = new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith(".txt");
}
};
}
public static class Collection extends StorageBase<CardEdition> {
private final Map<String, CardEdition> aliasToEdition = new TreeMap<String, CardEdition>(String.CASE_INSENSITIVE_ORDER);
public Collection(IItemReader<CardEdition> reader) {
super("Card editions", reader);
for (CardEdition ee : this) {
String alias = ee.getAlias();
if (null != alias) {
aliasToEdition.put(alias, ee);
}
aliasToEdition.put(ee.getCode2(), ee);
}
}
/**
* Gets a sets by code. It will search first by three letter codes, then by aliases and two-letter codes.
*
* @param code
* the code
* @return the sets the by code
*/
@Override
public CardEdition get(final String code) {
CardEdition baseResult = super.get(code);
return baseResult == null ? aliasToEdition.get(code) : baseResult;
}
public Iterable<CardEdition> getOrderedEditions() {
List<CardEdition> res = Lists.newArrayList(this);
Collections.sort(res);
Collections.reverse(res);
return res;
}
/**
* Gets the sets by code or throw.
*
* @param code
* the code
* @return the sets the by code or throw
*/
public CardEdition getEditionByCodeOrThrow(final String code) {
final CardEdition set = this.get(code);
if (null == set) {
throw new RuntimeException(String.format("Edition with code '%s' not found", code));
}
return set;
}
// used by image generating code
/**
* Gets the code2 by code.
*
* @param code
* the code
* @return the code2 by code
*/
public String getCode2ByCode(final String code) {
final CardEdition set = this.get(code);
return set == null ? "" : set.getCode2();
}
public final Function<String, CardEdition> FN_EDITION_BY_CODE = new Function<String, CardEdition>() {
@Override
public CardEdition apply(String code) {
return Collection.this.get(code);
};
};
/**
* TODO: Write javadoc for this method.
* @return
*/
public IItemReader<SealedProduct.Template> getBoosterGenerator() {
// TODO Auto-generated method stub
return new StorageReaderBase<SealedProduct.Template>(null) {
@Override
public Map<String, SealedProduct.Template> readAll() {
Map<String, SealedProduct.Template> map = new TreeMap<String, SealedProduct.Template>(String.CASE_INSENSITIVE_ORDER);
for(CardEdition ce : Collection.this) {
map.put(ce.getCode(), ce.getBoosterTemplate());
}
return map;
}
@Override
public String getItemKey(SealedProduct.Template item) {
return item.getEdition();
}
};
}
public CardEdition getEarliestEditionWithAllCards(CardPool cards) {
Set<String> minEditions = new HashSet<String>();
SetPreference strictness = SetPreference.EarliestCoreExp;
for(Entry<PaperCard, Integer> k : cards) {
PaperCard cp = StaticData.instance().getCommonCards().getCardFromEdition(k.getKey().getName(), strictness);
if( cp == null && strictness == SetPreference.EarliestCoreExp) {
strictness = SetPreference.Earliest; // card is not found in core and expansions only (probably something CMD or C13)
cp = StaticData.instance().getCommonCards().getCardFromEdition(k.getKey().getName(), strictness);
}
if ( cp == null )
cp = k.getKey(); // it's unlikely, this code will ever run
minEditions.add(cp.getEdition());
}
for(CardEdition ed : getOrderedEditions()) {
if(minEditions.contains(ed.getCode()))
return ed;
}
return UNKNOWN;
}
}
public static class Predicates {
/** The Constant canMakeBooster. */
public static final Predicate<CardEdition> CAN_MAKE_BOOSTER = new CanMakeBooster();
private static class CanMakeBooster implements Predicate<CardEdition> {
@Override
public boolean apply(final CardEdition subject) {
return subject.hasBoosterTemplate();
}
}
public final static CardEdition getRandomSetWithAllBasicLands(Iterable<CardEdition> allEditions) {
return Aggregates.random(Iterables.filter(allEditions, hasBasicLands));
}
public static final Predicate<CardEdition> HAS_TOURNAMENT_PACK = new CanMakeStarter();
private static class CanMakeStarter implements Predicate<CardEdition> {
@Override
public boolean apply(final CardEdition subject) {
return StaticData.instance().getTournamentPacks().contains(subject.getCode());
}
}
public static final Predicate<CardEdition> HAS_FAT_PACK = new CanMakeFatPack();
private static class CanMakeFatPack implements Predicate<CardEdition> {
@Override
public boolean apply(final CardEdition subject) {
return StaticData.instance().getFatPacks().contains(subject.getCode());
}
}
public static final Predicate<CardEdition> hasBasicLands = new Predicate<CardEdition>() {
@Override
public boolean apply(CardEdition ed) {
for(String landName : MagicColor.Constant.BASIC_LANDS) {
if (null == StaticData.instance().getCommonCards().getCard(landName, ed.getCode(), 0))
return false;
}
return true;
};
};
}
}

View File

@@ -0,0 +1,124 @@
package forge.card;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import forge.card.mana.ManaCost;
//
// DO NOT AUTOFORMAT / CHECKSTYLE THIS FILE
//
/**
* Represents a single side or part of a magic card with its original characteristics.
* <br><br>
* <i>Do not use reference to class except for card parsing.<br>Always use reference to interface type outside of package.</i>
*/
final class CardFace implements ICardFace {
public enum FaceSelectionMethod { //
USE_ACTIVE_FACE,
USE_PRIMARY_FACE,
COMBINE;
}
private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<String>());
private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<String, String>());
private final String name;
private CardType type = null;
private ManaCost manaCost = ManaCost.NO_COST;
private ColorSet color = null;
private String oracleText = null;
private int iPower = -1;
private int iToughness = -1;
private String power = null;
private String toughness = null;
private int initialLoyalty = -1;
private String nonAbilityText = null;
private List<String> keywords = null;
private List<String> abilities = null;
private List<String> staticAbilities = null;
private List<String> triggers = null;
private List<String> replacements = null;
private Map<String, String> variables = null;
// these implement ICardCharacteristics
@Override public String getOracleText() { return oracleText; }
@Override public int getIntPower() { return iPower; }
@Override public int getIntToughness() { return iToughness; }
@Override public String getPower() { return power; }
@Override public String getToughness() { return toughness; }
@Override public int getInitialLoyalty() { return initialLoyalty; }
@Override public String getName() { return this.name; }
@Override public CardType getType() { return this.type; }
@Override public ManaCost getManaCost() { return this.manaCost; }
@Override public ColorSet getColor() { return this.color; }
// these are raw and unparsed used for Card creation
@Override public Iterable<String> getKeywords() { return keywords; }
@Override public Iterable<String> getAbilities() { return abilities; }
@Override public Iterable<String> getStaticAbilities() { return staticAbilities; }
@Override public Iterable<String> getTriggers() { return triggers; }
@Override public Iterable<String> getReplacements() { return replacements; }
@Override public String getNonAbilityText() { return nonAbilityText; }
@Override public Iterable<Entry<String, String>> getVariables() { return variables.entrySet(); }
public CardFace(String name0) {
this.name = name0;
if ( StringUtils.isBlank(name0) )
throw new RuntimeException("Card name is empty");
}
// Here come setters to allow parser supply values
void setType(CardType type0) { this.type = type0; }
void setManaCost(ManaCost manaCost0) { this.manaCost = manaCost0; }
void setColor(ColorSet color0) { this.color = color0; }
void setOracleText(String text) { this.oracleText = text; }
void setInitialLoyalty(int value) { this.initialLoyalty = value; }
void setPtText(String value) {
final int slashPos = value.indexOf('/');
if (slashPos == -1) {
throw new RuntimeException(String.format("Creature '%s' has bad p/t stats", this.getName()));
}
this.power = value.substring(0, slashPos);
this.toughness = value.substring(slashPos + 1);
this.iPower = StringUtils.isNumeric(this.power) ? Integer.parseInt(this.power) : 0;
this.iToughness = StringUtils.isNumeric(this.toughness) ? Integer.parseInt(this.toughness) : 0;
}
// Raw fields used for Card creation
void setNonAbilityText(String value) { this.nonAbilityText = value; }
void addKeyword(String value) { if (null == this.keywords) { this.keywords = new ArrayList<String>(); } this.keywords.add(value); }
void addAbility(String value) { if (null == this.abilities) { this.abilities = new ArrayList<String>(); } this.abilities.add(value);}
void addTrigger(String value) { if (null == this.triggers) { this.triggers = new ArrayList<String>(); } this.triggers.add(value);}
void addStaticAbility(String value) { if (null == this.staticAbilities) { this.staticAbilities = new ArrayList<String>(); } this.staticAbilities.add(value);}
void addReplacementEffect(String value) { if (null == this.replacements) { this.replacements = new ArrayList<String>(); } this.replacements.add(value);}
void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); }
void assignMissingFields() { // Most scripts do not specify color explicitly
if ( null == oracleText ) { System.err.println(name + " has no Oracle text."); oracleText = ""; }
if ( manaCost == null && color == null ) System.err.println(name + " has neither ManaCost nor Color");
if ( color == null ) color = ColorSet.fromManaCost(manaCost);
if ( keywords == null ) keywords = emptyList;
if ( abilities == null ) abilities = emptyList;
if ( staticAbilities == null ) staticAbilities = emptyList;
if ( triggers == null ) triggers = emptyList;
if ( replacements == null ) replacements = emptyList;
if ( variables == null ) variables = emptyMap;
if ( null == nonAbilityText ) nonAbilityText = "";
}
}

View File

@@ -0,0 +1,506 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.lang3.StringUtils;
import forge.card.mana.IParserManaCost;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
/**
* A collection of methods containing full
* meta and gameplay properties of a card.
*
* @author Forge
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardRules implements ICardCharacteristics {
private CardSplitType splitType;
private ICardFace mainPart;
private ICardFace otherPart;
private CardAiHints aiHints;
private ColorSet colorIdentity;
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
splitType = altMode;
mainPart = faces[0];
otherPart = faces[1];
aiHints = cah;
//calculate color identity
byte colMask = calculateColorIdentity(mainPart);
if (otherPart != null) {
colMask |= calculateColorIdentity(otherPart);
}
colorIdentity = ColorSet.fromMask(colMask);
}
void reinitializeFromRules(CardRules newRules) {
if(!newRules.getName().equals(this.getName()))
throw new UnsupportedOperationException("You cannot rename the card using the same CardRules object");
splitType = newRules.splitType;
mainPart = newRules.mainPart;
otherPart = newRules.otherPart;
aiHints = newRules.aiHints;
colorIdentity = newRules.colorIdentity;
}
private byte calculateColorIdentity(ICardFace face) {
byte res = face.getColor().getColor();
boolean isReminder = false;
boolean isSymbol = false;
String oracleText = face.getOracleText();
int len = oracleText.length();
for(int i = 0; i < len; i++) {
char c = oracleText.charAt(i); // This is to avoid needless allocations performed by toCharArray()
switch(c) {
case('('): isReminder = i > 0; break; // if oracle has only reminder, consider it valid rules (basic and true lands need this)
case(')'): isReminder = false; break;
case('{'): isSymbol = true; break;
case('}'): isSymbol = false; break;
default:
if(isSymbol && !isReminder) {
switch(c) {
case('W'): res |= MagicColor.WHITE; break;
case('U'): res |= MagicColor.BLUE; break;
case('B'): res |= MagicColor.BLACK; break;
case('R'): res |= MagicColor.RED; break;
case('G'): res |= MagicColor.GREEN; break;
}
}
break;
}
}
return res;
}
public boolean isVariant() {
CardType t = getType();
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon();
}
public CardSplitType getSplitType() {
return splitType;
}
public ICardFace getMainPart() {
return mainPart;
}
public ICardFace getOtherPart() {
return otherPart;
}
public String getName() {
switch(splitType.getAggregationMethod()) {
case COMBINE:
return mainPart.getName() + " // " + otherPart.getName();
default:
return mainPart.getName();
}
}
public CardAiHints getAiHints() {
return aiHints;
}
@Override
public CardType getType() {
switch(splitType.getAggregationMethod()) {
case COMBINE: // no cards currently have different types
return CardType.combine(mainPart.getType(), otherPart.getType());
default:
return mainPart.getType();
}
}
@Override
public ManaCost getManaCost() {
switch(splitType.getAggregationMethod()) {
case COMBINE:
return ManaCost.combine(mainPart.getManaCost(), otherPart.getManaCost());
default:
return mainPart.getManaCost();
}
}
@Override
public ColorSet getColor() {
switch(splitType.getAggregationMethod()) {
case COMBINE:
return ColorSet.fromMask(mainPart.getColor().getColor() | otherPart.getColor().getColor());
default:
return mainPart.getColor();
}
}
private boolean canCastFace(ICardFace face, byte colorCode) {
if (face.getManaCost().isNoCost()) {
//if card face has no cost, assume castable only by mana of its defined color
return face.getColor().hasNoColorsExcept(colorCode);
}
return face.getManaCost().canBePaidWithAvaliable(colorCode);
}
public boolean canCastWithAvailable(byte colorCode) {
switch(splitType.getAggregationMethod()) {
case COMBINE:
return canCastFace(mainPart, colorCode) || canCastFace(otherPart, colorCode);
default:
return canCastFace(mainPart, colorCode);
}
}
@Override public int getIntPower() { return mainPart.getIntPower(); }
@Override public int getIntToughness() { return mainPart.getIntToughness(); }
@Override public String getPower() { return mainPart.getPower(); }
@Override public String getToughness() { return mainPart.getToughness(); }
@Override public int getInitialLoyalty() { return mainPart.getInitialLoyalty(); }
@Override
public String getOracleText() {
switch(splitType.getAggregationMethod()) {
case COMBINE:
return mainPart.getOracleText() + "\r\n\r\n" + otherPart.getOracleText();
default:
return mainPart.getOracleText();
}
}
// public Set<String> getSets() { return this.setsPrinted.keySet(); }
// public CardInSet getEditionInfo(final String setCode) {
// final CardInSet result = this.setsPrinted.get(setCode);
// return result; // if returns null, String.format("Card '%s' was never printed in set '%s'", this.getName(), setCode);
// }
// vanguard card fields, they don't use sides.
private int deltaHand;
private int deltaLife;
public int getHand() { return deltaHand; }
public int getLife() { return deltaLife; }
public void setVanguardProperties(String pt) {
final int slashPos = pt == null ? -1 : pt.indexOf('/');
if (slashPos == -1) {
throw new RuntimeException(String.format("Vanguard '%s' has bad hand/life stats", this.getName()));
}
this.deltaHand = Integer.parseInt(pt.substring(0, slashPos).replace("+", ""));
this.deltaLife = Integer.parseInt(pt.substring(slashPos+1).replace("+", ""));
}
// Downloadable image
private String dlUrl;
private String dlUrlOtherSide;
public String getPictureUrl(boolean backface ) { return backface ? dlUrlOtherSide : dlUrl; }
public void setDlUrls(String[] dlUrls) { this.dlUrl = dlUrls[0]; this.dlUrlOtherSide = dlUrls[1]; }
public final List<String> getReplacements() {
return null;
}
public final List<String> getTriggers() {
return null;
}
public final List<String> getStaticAbilities() {
return null;
}
public final List<String> getAbilities() {
return null;
}
public ColorSet getColorIdentity() {
return colorIdentity;
}
/** Instantiates class, reads a card. For batch operations better create you own reader instance. */
public static CardRules fromScript(Iterable<String> script) {
Reader crr = new Reader();
for(String line : script) {
crr.parseLine(line);
}
return crr.getCard();
}
// Reads cardname.txt
public static class Reader {
// fields to build
private CardFace[] faces = new CardFace[] { null, null };
private String[] pictureUrl = new String[] { null, null };
private int curFace = 0;
private CardSplitType altMode = CardSplitType.None;
private String handLife = null;
// fields to build CardAiHints
private boolean removedFromAIDecks = false;
private boolean removedFromRandomDecks = false;
private DeckHints hints = null;
private DeckHints needs = null;
/**
* Reset all fields to parse next card (to avoid allocating new CardRulesReader N times)
*/
public final void reset() {
this.curFace = 0;
this.faces[0] = null;
this.faces[1] = null;
this.pictureUrl[0] = null;
this.pictureUrl[1] = null;
this.handLife = null;
this.altMode = CardSplitType.None;
this.removedFromAIDecks = false;
this.removedFromRandomDecks = false;
this.needs = null;
this.hints = null;
}
/**
* Gets the card.
*
* @return the card
*/
public final CardRules getCard() {
CardAiHints cah = new CardAiHints(removedFromAIDecks, removedFromRandomDecks, hints, needs );
faces[0].assignMissingFields();
if (null != faces[1]) faces[1].assignMissingFields();
final CardRules result = new CardRules(faces, altMode, cah);
result.setDlUrls(pictureUrl);
if (StringUtils.isNotBlank(handLife))
result.setVanguardProperties(handLife);
return result;
}
public final CardRules readCard(final Iterable<String> script) {
this.reset();
for (String line : script) {
if (line.isEmpty() || line.charAt(0) == '#') {
continue;
}
this.parseLine(line);
}
return this.getCard();
}
/**
* Parses the line.
*
* @param line
* the line
*/
public final void parseLine(final String line) {
int colonPos = line.indexOf(':');
String key = colonPos > 0 ? line.substring(0, colonPos) : line;
String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null;
switch(key.charAt(0)) {
case 'A':
if ("A".equals(key))
this.faces[curFace].addAbility(value);
else if ("AlternateMode".equals(key)) {
//System.out.println(faces[curFace].getName());
this.altMode = CardSplitType.smartValueOf(value);
} else if ("ALTERNATE".equals(key)) {
this.curFace = 1;
}
break;
case 'C':
if ("Colors".equals(key)) {
// This is forge.card.CardColor not forge.CardColor.
// Why do we have two classes with the same name?
ColorSet newCol = ColorSet.fromNames(value.split(","));
this.faces[this.curFace].setColor(newCol);
}
break;
case 'D':
if ("DeckHints".equals(key)) {
hints = new DeckHints(value);
} else if ("DeckNeeds".equals(key)) {
needs = new DeckHints(value);
}
break;
case 'H':
if ("HandLifeModifier".equals(key)) {
handLife = value;
}
break;
case 'K':
if ("K".equals(key)) {
this.faces[this.curFace].addKeyword(value);
}
break;
case 'L':
if ("Loyalty".equals(key)) {
this.faces[this.curFace].setInitialLoyalty(Integer.valueOf(value));
}
break;
case 'M':
if ("ManaCost".equals(key)) {
this.faces[this.curFace].setManaCost("no cost".equals(value) ? ManaCost.NO_COST
: new ManaCost(new ManaCostParser(value)));
}
break;
case 'N':
if ("Name".equals(key)) {
this.faces[this.curFace] = new CardFace(value);
}
break;
case 'O':
if ("Oracle".equals(key)) {
this.faces[this.curFace].setOracleText(value);
}
break;
case 'P':
if ("PT".equals(key)) {
this.faces[this.curFace].setPtText(value);
}
break;
case 'R':
if ("R".equals(key)) {
this.faces[this.curFace].addReplacementEffect(value);
}
break;
case 'S':
if ("S".equals(key)) {
this.faces[this.curFace].addStaticAbility(value);
} else if ( "SVar".equals(key) ) {
if ( null == value ) throw new IllegalArgumentException("SVar has no variable name");
colonPos = value.indexOf(':');
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
value = colonPos > 0 ? value.substring(1+colonPos) : null;
if ( "RemAIDeck".equals(variable) ) {
this.removedFromAIDecks = "True".equalsIgnoreCase(value);
} else if ( "RemRandomDeck".equals(variable) ) {
this.removedFromRandomDecks = "True".equalsIgnoreCase(value);
} else if ( "Picture".equals(variable) ) {
this.pictureUrl[this.curFace] = value;
} else if ( "Rarity".equals(variable) ) {
// discard that, they should supply it in SetInfo
} else
this.faces[curFace].addSVar(variable, value);
} else if ("SetInfo".equals(key)) {
// deprecated
}
break;
case 'T':
if ("T".equals(key)) {
this.faces[this.curFace].addTrigger(value);
} else if ("Types".equals(key)) {
this.faces[this.curFace].setType(CardType.parse(value));
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
this.faces[this.curFace].setNonAbilityText(value);
}
break;
}
}
/**
* The Class ParserCardnameTxtManaCost.
*/
private static class ManaCostParser implements IParserManaCost {
private final StringTokenizer st;
private int colorlessCost;
public ManaCostParser(final String cost) {
st = new StringTokenizer(cost, " ");
this.colorlessCost = 0;
}
@Override
public final int getTotalColorlessCost() {
if (this.hasNext()) {
throw new RuntimeException("Colorless cost should be obtained after iteration is complete");
}
return this.colorlessCost;
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#hasNext()
*/
@Override
public final boolean hasNext() {
return st.hasMoreTokens();
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#next()
*/
@Override
public final ManaCostShard next() {
final String unparsed = st.nextToken();
// System.out.println(unparsed);
try {
int iVal = Integer.parseInt(unparsed);
this.colorlessCost += iVal;
return null;
}
catch (NumberFormatException nex) { }
return ManaCostShard.parseNonGeneric(unparsed);
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
} // unsuported
}
}
public static CardRules getUnsupportedCardNamed(String name) {
CardAiHints cah = new CardAiHints(true, true, null, null);
CardFace[] faces = { new CardFace(name), null};
faces[0].setColor(ColorSet.fromMask(0));
faces[0].setType(CardType.parse(""));
faces[0].setOracleText("This card is not supported by Forge. Whenever you start a game with this card, it will be bugged.");
faces[0].setNonAbilityText("This card is not supported by Forge.\nWhenever you start a game with this card, it will be bugged.");
faces[0].assignMissingFields();
final CardRules result = new CardRules(faces, CardSplitType.None, cah);
return result;
}
}

View File

@@ -6,6 +6,7 @@ import java.util.List;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.util.ComparableOp; import forge.util.ComparableOp;
import forge.util.PredicateString; import forge.util.PredicateString;
@@ -170,7 +171,7 @@ public final class CardRulesPredicates {
*/ */
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) { public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
try { try {
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardCoreType.class, what)); return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardType.CoreType.class, what));
} catch (final Exception e) { } catch (final Exception e) {
return com.google.common.base.Predicates.alwaysFalse(); return com.google.common.base.Predicates.alwaysFalse();
} }
@@ -185,7 +186,7 @@ public final class CardRulesPredicates {
* the type * the type
* @return the predicate * @return the predicate
*/ */
public static Predicate<CardRules> coreType(final boolean isEqual, final CardCoreType type) { public static Predicate<CardRules> coreType(final boolean isEqual, final CardType.CoreType type) {
return new PredicateCoreType(type, isEqual); return new PredicateCoreType(type, isEqual);
} }
@@ -200,7 +201,7 @@ public final class CardRulesPredicates {
*/ */
public static Predicate<CardRules> superType(final boolean isEqual, final String what) { public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
try { try {
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardSuperType.class, what)); return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardType.SuperType.class, what));
} catch (final Exception e) { } catch (final Exception e) {
return com.google.common.base.Predicates.alwaysFalse(); return com.google.common.base.Predicates.alwaysFalse();
} }
@@ -215,7 +216,7 @@ public final class CardRulesPredicates {
* the type * the type
* @return the predicate * @return the predicate
*/ */
public static Predicate<CardRules> superType(final boolean isEqual, final CardSuperType type) { public static Predicate<CardRules> superType(final boolean isEqual, final CardType.SuperType type) {
return new PredicateSuperType(type, isEqual); return new PredicateSuperType(type, isEqual);
} }
@@ -224,7 +225,7 @@ public final class CardRulesPredicates {
* Checks for color. * Checks for color.
* *
* @param thatColor * @param thatColor
* the that color * color to check
* @return the predicate * @return the predicate
*/ */
public static Predicate<CardRules> hasColor(final byte thatColor) { public static Predicate<CardRules> hasColor(final byte thatColor) {
@@ -235,13 +236,24 @@ public final class CardRulesPredicates {
* Checks if is color. * Checks if is color.
* *
* @param thatColor * @param thatColor
* the that color * color to check
* @return the predicate * @return the predicate
*/ */
public static Predicate<CardRules> isColor(final byte thatColor) { public static Predicate<CardRules> isColor(final byte thatColor) {
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor); return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
} }
/**
* Checks if card can be cast with unlimited mana of given color set.
*
* @param thatColor
* color to check
* @return the predicate
*/
public static Predicate<CardRules> canCastWithAvailable(final byte thatColor) {
return new LeafColor(LeafColor.ColorOperator.CanCast, thatColor);
}
/** /**
* Checks if is exactly that color. * Checks if is exactly that color.
* *
@@ -310,7 +322,7 @@ public final class CardRulesPredicates {
private static class LeafColor implements Predicate<CardRules> { private static class LeafColor implements Predicate<CardRules> {
public enum ColorOperator { public enum ColorOperator {
CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals, CanCast
} }
private final LeafColor.ColorOperator op; private final LeafColor.ColorOperator op;
@@ -337,6 +349,8 @@ public final class CardRulesPredicates {
return subject.getColor().hasAllColors(this.color); return subject.getColor().hasAllColors(this.color);
case HasAnyOf: case HasAnyOf:
return subject.getColor().hasAnyColor(this.color); return subject.getColor().hasAnyColor(this.color);
case CanCast:
return subject.canCastWithAvailable(this.color);
default: default:
return false; return false;
} }
@@ -396,7 +410,7 @@ public final class CardRulesPredicates {
} }
private static class PredicateCoreType implements Predicate<CardRules> { private static class PredicateCoreType implements Predicate<CardRules> {
private final CardCoreType operand; private final CardType.CoreType operand;
private final boolean shouldBeEqual; private final boolean shouldBeEqual;
@Override @Override
@@ -407,14 +421,14 @@ public final class CardRulesPredicates {
return this.shouldBeEqual == card.getType().typeContains(this.operand); return this.shouldBeEqual == card.getType().typeContains(this.operand);
} }
public PredicateCoreType(final CardCoreType type, final boolean wantEqual) { public PredicateCoreType(final CardType.CoreType type, final boolean wantEqual) {
this.operand = type; this.operand = type;
this.shouldBeEqual = wantEqual; this.shouldBeEqual = wantEqual;
} }
} }
private static class PredicateSuperType implements Predicate<CardRules> { private static class PredicateSuperType implements Predicate<CardRules> {
private final CardSuperType operand; private final CardType.SuperType operand;
private final boolean shouldBeEqual; private final boolean shouldBeEqual;
@Override @Override
@@ -422,7 +436,7 @@ public final class CardRulesPredicates {
return this.shouldBeEqual == card.getType().superTypeContains(this.operand); return this.shouldBeEqual == card.getType().superTypeContains(this.operand);
} }
public PredicateSuperType(final CardSuperType type, final boolean wantEqual) { public PredicateSuperType(final CardType.SuperType type, final boolean wantEqual) {
this.operand = type; this.operand = type;
this.shouldBeEqual = wantEqual; this.shouldBeEqual = wantEqual;
} }
@@ -448,21 +462,21 @@ public final class CardRulesPredicates {
/** The Constant isCreature. */ /** The Constant isCreature. */
public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates
.coreType(true, CardCoreType.Creature); .coreType(true, CardType.CoreType.Creature);
public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates
.superType(true, CardSuperType.Legendary); .superType(true, CardType.SuperType.Legendary);
/** The Constant isArtifact. */ /** The Constant isArtifact. */
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
.coreType(true, CardCoreType.Artifact); .coreType(true, CardType.CoreType.Artifact);
/** The Constant isEquipment. */ /** The Constant isEquipment. */
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
.subType("Equipment"); .subType("Equipment");
/** The Constant isLand. */ /** The Constant isLand. */
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardCoreType.Land); public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardType.CoreType.Land);
/** The Constant isBasicLand. */ /** The Constant isBasicLand. */
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
@@ -480,31 +494,17 @@ public final class CardRulesPredicates {
} }
}; };
/** The Constant isPlaneswalker. */ public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
CardCoreType.Planeswalker); public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardType.CoreType.Sorcery);
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardType.CoreType.Enchantment);
/** The Constant isInstant. */ public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardType.CoreType.Plane);
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardCoreType.Instant); public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardType.CoreType.Phenomenon);
/** The Constant isSorcery. */
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardCoreType.Sorcery);
/** The Constant isEnchantment. */
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardCoreType.Enchantment);
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardCoreType.Plane);
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardCoreType.Phenomenon);
public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON); public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON);
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardCoreType.Scheme); public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardType.CoreType.Scheme);
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardCoreType.Vanguard); public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
/** The Constant isNonLand. */ public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardCoreType.Land);
/** The Constant isNonCreatureSpell. */
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(
Presets.IS_CREATURE, Presets.IS_LAND));
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/ /** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -533,6 +533,7 @@ public final class CardRulesPredicates {
/** The Constant isMulticolor. */ /** The Constant isMulticolor. */
public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2); public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2);
/** The Constant isMonocolor. */
public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1); public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1);
/** The Constant colors. */ /** The Constant colors. */

View File

@@ -0,0 +1,36 @@
package forge.card;
import forge.card.CardFace.FaceSelectionMethod;
public enum CardSplitType
{
None(FaceSelectionMethod.USE_PRIMARY_FACE, null),
Transform(FaceSelectionMethod.USE_ACTIVE_FACE, CardCharacteristicName.Transformed),
Split(FaceSelectionMethod.COMBINE, CardCharacteristicName.RightSplit),
Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardCharacteristicName.Flipped),
// used by 12 licid creatures to switch type into enchantment aura
Licid(FaceSelectionMethod.USE_PRIMARY_FACE, CardCharacteristicName.Licid);
private CardSplitType(FaceSelectionMethod calcMode, CardCharacteristicName stateName) {
method = calcMode;
this.changedStateName = stateName;
}
public FaceSelectionMethod getAggregationMethod() {
return method;
}
private final FaceSelectionMethod method;
private final CardCharacteristicName changedStateName;
public static CardSplitType smartValueOf(String text) {
if ("DoubleFaced".equals(text)) return Transform;
// Will throw exceptions here if bad text passed
CardSplitType res = CardSplitType.valueOf(text);
return res;
}
public CardCharacteristicName getChangedStateName() {
return changedStateName;
}
}

View File

@@ -0,0 +1,405 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
/**
* <p>
* Immutable Card type. Can be built only from parsing a string.
* </p>
*
* @author Forge
* @version $Id: CardType.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardType implements Comparable<CardType> {
public enum CoreType {
Artifact(true),
Creature(true),
Enchantment(true),
Instant(false),
Land(true),
Plane(false),
Planeswalker(true),
Scheme(false),
Sorcery(false),
Tribal(false),
Vanguard(false),
Phenomenon(false);
public final boolean isPermanent;
private CoreType(final boolean permanent) {
isPermanent = permanent;
}
public static CoreType smartValueOf(final String value) { return smartValueOf(value, true); }
public static CoreType smartValueOf(final String value, boolean throwIfNotFound) {
if (value == null) {
return null;
}
final String valToCompate = value.trim();
for (final CoreType v : CoreType.values()) {
if (v.name().equalsIgnoreCase(valToCompate)) {
return v;
}
}
if (throwIfNotFound)
throw new IllegalArgumentException("No element named " + value + " in enum CoreType");
return null;
}
public static boolean isAPermanentType(final String cardType) {
CoreType ct = smartValueOf(cardType, false);
return ct != null && ct.isPermanent;
}
}
public enum SuperType {
/** The Basic. */
Basic,
/** The Legendary. */
Legendary,
/** The Snow. */
Snow,
/** The Ongoing. */
Ongoing,
/** The World. */
World
}
private final List<String> subType = new ArrayList<String>();
private final EnumSet<CardType.CoreType> coreType = EnumSet.noneOf(CardType.CoreType.class);
private final EnumSet<CardType.SuperType> superType = EnumSet.noneOf(CardType.SuperType.class);
private String calculatedType = null; // since obj is immutable, this is
// calc'd once
// This will be useful for faster parses
private static HashMap<String, CardType.CoreType> stringToCoreType = new HashMap<String, CardType.CoreType>();
private static HashMap<String, CardType.SuperType> stringToSuperType = new HashMap<String, CardType.SuperType>();
static {
for (final CardType.SuperType st : CardType.SuperType.values()) {
CardType.stringToSuperType.put(st.name(), st);
}
for (final CardType.CoreType ct : CardType.CoreType.values()) {
CardType.stringToCoreType.put(ct.name(), ct);
}
}
private CardType() { }
// TODO: Debug this code
public static CardType parse(final String typeText) {
// Most types and subtypes, except "Serra's Realm" and
// "Bolas's Meditation Realm" consist of only one word
final char space = ' ';
final CardType result = new CardType();
int iTypeStart = 0;
int iSpace = typeText.indexOf(space);
boolean hasMoreTypes = typeText.length() > 0;
while (hasMoreTypes) {
final String type = typeText.substring(iTypeStart, iSpace == -1 ? typeText.length() : iSpace);
hasMoreTypes = iSpace != -1;
if (!CardType.isMultiwordType(type) || !hasMoreTypes) {
iTypeStart = iSpace + 1;
result.parseAndAdd(type);
}
iSpace = typeText.indexOf(space, iSpace + 1);
}
return result;
}
public static CardType combine(final CardType a, final CardType b) {
CardType result = new CardType();
result.superType.addAll(a.superType);
result.superType.addAll(b.superType);
result.coreType.addAll(a.coreType);
result.coreType.addAll(b.coreType);
result.subType.addAll(a.subType);
result.subType.addAll(b.subType);
return result;
}
private static boolean isMultiwordType(final String type) {
final String[] multiWordTypes = { "Serra's Realm", "Bolas's Meditation Realm" };
// no need to loop for only 2 exceptions!
if (multiWordTypes[0].startsWith(type) && !multiWordTypes[0].equals(type)) {
return true;
}
if (multiWordTypes[1].startsWith(type) && !multiWordTypes[1].equals(type)) {
return true;
}
return false;
}
private void parseAndAdd(final String type) {
if ("-".equals(type)) {
return;
}
final CardType.CoreType ct = CardType.stringToCoreType.get(type);
if (ct != null) {
this.coreType.add(ct);
return;
}
final CardType.SuperType st = CardType.stringToSuperType.get(type);
if (st != null) {
this.superType.add(st);
return;
}
// If not recognized by super- and core- this must be subtype
this.subType.add(type);
}
public boolean subTypeContains(final String operand) {
return this.subType.contains(operand);
}
public boolean typeContains(final CardType.CoreType operand) {
return this.coreType.contains(operand);
}
public boolean superTypeContains(final CardType.SuperType operand) {
return this.superType.contains(operand);
}
public boolean isCreature() {
return this.coreType.contains(CardType.CoreType.Creature);
}
public boolean isPlaneswalker() {
return this.coreType.contains(CardType.CoreType.Planeswalker);
}
public boolean isLand() {
return this.coreType.contains(CardType.CoreType.Land);
}
public boolean isArtifact() {
return this.coreType.contains(CardType.CoreType.Artifact);
}
public boolean isInstant() {
return this.coreType.contains(CardType.CoreType.Instant);
}
public boolean isSorcery() {
return this.coreType.contains(CardType.CoreType.Sorcery);
}
public boolean isVanguard() {
return this.coreType.contains(CardType.CoreType.Vanguard);
}
public boolean isScheme() {
return this.coreType.contains(CardType.CoreType.Scheme);
}
public boolean isEnchantment() {
return this.coreType.contains(CardType.CoreType.Enchantment);
}
public boolean isBasic() {
return this.superType.contains(CardType.SuperType.Basic);
}
public boolean isLegendary() {
return this.superType.contains(CardType.SuperType.Legendary);
}
public boolean isBasicLand() {
return this.isBasic() && this.isLand();
}
@Override
public String toString() {
if (null == this.calculatedType) {
this.calculatedType = this.toStringImpl();
}
return this.calculatedType;
}
private String toStringImpl() {
if (this.subType.isEmpty()) {
return StringUtils.join(this.getTypesBeforeDash(), ' ');
} else {
return String.format("%s - %s", StringUtils.join(this.getTypesBeforeDash(), ' '), StringUtils.join(this.subType, " "));
}
}
public List<String> getTypesBeforeDash() {
final ArrayList<String> types = new ArrayList<String>();
for (final CardType.SuperType st : this.superType) {
types.add(st.name());
}
for (final CardType.CoreType ct : this.coreType) {
types.add(ct.name());
}
return types;
}
@Override
public int compareTo(final CardType o) {
return this.toString().compareTo(o.toString());
}
public List<String> getSubTypes() {
return this.subType;
}
public boolean sharesSubTypeWith(CardType ctOther) {
for (String t : ctOther.getSubTypes()) {
if (this.subTypeContains(t)) {
return true;
}
}
return false;
}
public boolean isPlane() {
return this.coreType.contains(CardType.CoreType.Plane);
}
public boolean isPhenomenon() {
return this.coreType.contains(CardType.CoreType.Phenomenon);
}
/**
* The Interface CardTypes.
*/
public static class Constant {
/** The loaded. */
public static final boolean[] LOADED = { false };
/** The card types. */
public static final List<String> CARD_TYPES = new ArrayList<String>();
/** The super types. */
public static final List<String> SUPER_TYPES = new ArrayList<String>();
/** The basic types. */
public static final List<String> BASIC_TYPES = new ArrayList<String>();
/** The land types. */
public static final List<String> LAND_TYPES = new ArrayList<String>();
/** The creature types. */
public static final List<String> CREATURE_TYPES = new ArrayList<String>();
/** The instant types. */
public static final List<String> INSTANT_TYPES = new ArrayList<String>();
/** The sorcery types. */
public static final List<String> SORCERY_TYPES = new ArrayList<String>();
/** The enchantment types. */
public static final List<String> ENCHANTMENT_TYPES = new ArrayList<String>();
/** The artifact types. */
public static final List<String> ARTIFACT_TYPES = new ArrayList<String>();
/** The walker types. */
public static final List<String> WALKER_TYPES = new ArrayList<String>();
}
///////// Utility methods
public static boolean isACardType(final String cardType) {
return CardType.getAllCardTypes().contains(cardType);
}
public static ArrayList<String> getAllCardTypes() {
final ArrayList<String> types = new ArrayList<String>();
// types.addAll(getCardTypes());
types.addAll(Constant.CARD_TYPES);
// not currently used by Forge
types.add("Plane");
types.add("Scheme");
types.add("Vanguard");
return types;
}
public static ArrayList<String> getBasicTypes() {
final ArrayList<String> types = new ArrayList<String>();
types.addAll(Constant.BASIC_TYPES);
return types;
}
public static ArrayList<String> getLandTypes() {
final ArrayList<String> types = new ArrayList<String>();
types.addAll(Constant.BASIC_TYPES);
types.addAll(Constant.LAND_TYPES);
return types;
}
public static ArrayList<String> getCreatureTypes() {
final ArrayList<String> types = new ArrayList<String>();
types.addAll(Constant.CREATURE_TYPES);
return types;
}
public static boolean isASuperType(final String cardType) {
return (Constant.SUPER_TYPES.contains(cardType));
}
public static boolean isASubType(final String cardType) {
return (!CardType.isASuperType(cardType) && !CardType.isACardType(cardType));
}
public static boolean isACreatureType(final String cardType) {
return (Constant.CREATURE_TYPES.contains(cardType));
}
public static boolean isALandType(final String cardType) {
return (Constant.LAND_TYPES.contains(cardType));
}
public static boolean isAPlaneswalkerType(final String cardType) {
return (Constant.WALKER_TYPES.contains(cardType));
}
public static boolean isABasicLandType(final String cardType) {
return (Constant.BASIC_TYPES.contains(cardType));
}
}

View File

@@ -17,7 +17,11 @@
*/ */
package forge.card; package forge.card;
import forge.Constant; import java.util.Iterator;
import java.util.NoSuchElementException;
import com.google.common.collect.UnmodifiableIterator;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.util.BinaryUtil; import forge.util.BinaryUtil;
@@ -31,10 +35,10 @@ import forge.util.BinaryUtil;
* *
* *
*/ */
public final class ColorSet implements Comparable<ColorSet> { public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte> {
private final byte myColor; private final byte myColor;
private final int orderWeight; private final float orderWeight;
private static ColorSet[] allColors = new ColorSet[32]; private static ColorSet[] allColors = new ColorSet[32];
private static final ColorSet noColor = new ColorSet(); private static final ColorSet noColor = new ColorSet();
@@ -55,11 +59,10 @@ public final class ColorSet implements Comparable<ColorSet> {
private ColorSet(final byte mask) { private ColorSet(final byte mask) {
this.myColor = mask; this.myColor = mask;
this.orderWeight = this.getOrderWeight(); this.orderWeight = this.getOrderWeight();
} }
public static ColorSet fromMask(final int mask) { public static ColorSet fromMask(final int mask) {
int mask32 = (mask & MagicColor.ALL_COLORS) >> 1; int mask32 = mask & MagicColor.ALL_COLORS;
if (allColors[mask32] == null) { if (allColors[mask32] == null) {
allColors[mask32] = new ColorSet((byte) mask); allColors[mask32] = new ColorSet((byte) mask);
} }
@@ -134,8 +137,14 @@ public final class ColorSet implements Comparable<ColorSet> {
* *
* @return the order weight * @return the order weight
*/ */
public int getOrderWeight() { public float getOrderWeight() {
return this.myColor == 0 ? 0x400 : (this.countColors() == 1 ? this.myColor : 0x200); float res = this.countColors();
if(hasWhite()) res += 0.0005f;
if(hasBlue()) res += 0.0020f;
if(hasBlack()) res += 0.0080f;
if(hasRed()) res += 0.0320f;
if(hasGreen()) res += 0.1280f;
return res;
} }
/** /**
@@ -183,7 +192,7 @@ public final class ColorSet implements Comparable<ColorSet> {
*/ */
@Override @Override
public int compareTo(final ColorSet other) { public int compareTo(final ColorSet other) {
return this.orderWeight - other.orderWeight; return Float.compare(this.orderWeight, other.orderWeight);
} }
// Presets // Presets
@@ -253,7 +262,7 @@ public final class ColorSet implements Comparable<ColorSet> {
return "n/a"; return "n/a";
} }
String toReturn = MagicColor.toLongString(myColor); String toReturn = MagicColor.toLongString(myColor);
if (toReturn == Constant.Color.COLORLESS && myColor != 0) { if (toReturn == MagicColor.Constant.COLORLESS && myColor != 0) {
return "multi"; return "multi";
} }
return toReturn; return toReturn;
@@ -281,4 +290,37 @@ public final class ColorSet implements Comparable<ColorSet> {
public ColorSet getOffColors(ColorSet ccOther) { public ColorSet getOffColors(ColorSet ccOther) {
return ColorSet.fromMask(~this.myColor & ccOther.myColor); return ColorSet.fromMask(~this.myColor & ccOther.myColor);
} }
@Override
public Iterator<Byte> iterator() {
return new ColorIterator();
}
private class ColorIterator extends UnmodifiableIterator<Byte> {
int currentBit = -1;
private int getIndexOfNextColor(){
int nextBit = currentBit + 1;
while(nextBit < MagicColor.NUMBER_OR_COLORS) {
if((myColor & MagicColor.WUBRG[nextBit]) != 0)
break;
nextBit++;
}
return nextBit;
}
@Override
public boolean hasNext() {
return getIndexOfNextColor() < MagicColor.NUMBER_OR_COLORS;
}
@Override
public Byte next() {
currentBit = getIndexOfNextColor();
if (currentBit >= MagicColor.NUMBER_OR_COLORS)
throw new NoSuchElementException();
return MagicColor.WUBRG[currentBit];
}
}
} }

View File

@@ -0,0 +1,33 @@
package forge.card;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import com.google.common.base.Predicate;
import forge.card.CardDb.SetPreference;
import forge.item.PaperCard;
public interface ICardDatabase extends Iterable<PaperCard> {
PaperCard getCard(String cardName);
PaperCard getCard(String cardName, String edition);
PaperCard getCard(String cardName, String edition, int artIndex);
PaperCard getCardFromEdition(String cardName, SetPreference fromSet);
PaperCard getCardFromEdition(String cardName, Date printedBefore, SetPreference fromSet);
PaperCard getCardFromEdition(String cardName, Date printedBefore, SetPreference fromSet, int artIndex);
PaperCard getFoiled(PaperCard cpi);
int getPrintCount(String cardName, String edition);
int getMaxPrintCount(String cardName);
int getArtCount(String cardName, String edition);
Collection<PaperCard> getUniqueCards();
List<PaperCard> getAllCards();
List<PaperCard> getAllCards(Predicate<PaperCard> predicate);
Predicate<? super PaperCard> wasPrintedInSets(List<String> allowedSetCodes);
}

View File

@@ -0,0 +1,122 @@
package forge.card;
import java.util.List;
import com.google.common.collect.ImmutableList;
/**
* Holds byte values for each color magic has.
*
*/
public class MagicColor {
public static final byte COLORLESS = 0;
public static final byte WHITE = 1 << 0;
public static final byte BLUE = 1 << 1;
public static final byte BLACK = 1 << 2;
public static final byte RED = 1 << 3;
public static final byte GREEN = 1 << 4;
public static final byte ALL_COLORS = BLACK | BLUE | WHITE | RED | GREEN;
public static final int NUMBER_OR_COLORS = 5;
public static final byte[] WUBRG = new byte[] { WHITE, BLUE, BLACK, RED, GREEN };
public static byte fromName(String s) {
if( s == null ) return 0;
if (s.equalsIgnoreCase(Constant.WHITE) || s.equalsIgnoreCase("w")) {
return MagicColor.WHITE;
}
if (s.equalsIgnoreCase(Constant.BLUE) || s.equalsIgnoreCase("u")) {
return MagicColor.BLUE;
}
if (s.equalsIgnoreCase(Constant.BLACK) || s.equalsIgnoreCase("b")) {
return MagicColor.BLACK;
}
if (s.equalsIgnoreCase(Constant.RED) || s.equalsIgnoreCase("r")) {
return MagicColor.RED;
}
if (s.equalsIgnoreCase(Constant.GREEN) || s.equalsIgnoreCase("g")) {
return MagicColor.GREEN;
}
return 0; // colorless
}
public static byte fromName(char c) {
switch(Character.toLowerCase(c)) {
case 'w': return MagicColor.WHITE;
case 'u': return MagicColor.BLUE;
case 'b': return MagicColor.BLACK;
case 'r': return MagicColor.RED;
case 'g': return MagicColor.GREEN;
}
return 0; // unknown means 'colorless'
}
public static String toShortString(String color) {
if (color.equalsIgnoreCase(Constant.SNOW)) return "S"; // compatibility
return toShortString(fromName(color));
}
public static String toLongString(String color) {
if (color.equalsIgnoreCase("s")) return Constant.SNOW; // compatibility
return toLongString(fromName(color));
}
public static String toShortString(byte color) {
switch(color){
case GREEN: return "G";
case RED: return "R";
case BLUE: return "U";
case BLACK: return "B";
case WHITE: return "W";
default: return "1";
}
}
public static String toLongString(byte color) {
switch(color){
case GREEN: return Constant.GREEN ;
case RED: return Constant.RED;
case BLUE: return Constant.BLUE;
case BLACK: return Constant.BLACK;
case WHITE: return Constant.WHITE;
default: return Constant.COLORLESS;
}
}
/**
* The Interface Color.
*/
public static class Constant {
/** The Black. */
public static final String BLACK = "black";
/** The Blue. */
public static final String BLUE = "blue";
/** The Green. */
public static final String GREEN = "green";
/** The Red. */
public static final String RED = "red";
/** The White. */
public static final String WHITE = "white";
/** The Colorless. */
public static final String COLORLESS = "colorless";
// color order "wubrg"
/** The only colors. */
public static final ImmutableList<String> ONLY_COLORS = ImmutableList.of(WHITE, BLUE, BLACK, RED, GREEN);
/** The Snow. */
public static final String SNOW = "snow";
/** The Basic lands. */
public static final List<String> BASIC_LANDS = ImmutableList.of("Plains", "Island", "Swamp", "Mountain", "Forest");
public static final List<String> SNOW_LANDS = ImmutableList.of("Snow-Covered Plains", "Snow-Covered Island", "Snow-Covered Swamp", "Snow-Covered Mountain", "Snow-Covered Forest");
}
}

View File

@@ -1,5 +1,6 @@
package forge.item; package forge.card;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.ArrayList; import java.util.ArrayList;
@@ -7,7 +8,9 @@ import java.util.Collection;
import com.google.common.base.Function; import com.google.common.base.Function;
import forge.util.ItemPool;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.item.PaperCard;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.storage.StorageReaderFileSections; import forge.util.storage.StorageReaderFileSections;
@@ -30,7 +33,7 @@ public class PrintSheet {
this(name0, null); this(name0, null);
} }
private PrintSheet(String name0, ItemPool<PaperCard> pool) { public PrintSheet(String name0, ItemPool<PaperCard> pool) {
name = name0; name = name0;
cardsWithWeights = pool != null ? pool : new ItemPool<PaperCard>(PaperCard.class); cardsWithWeights = pool != null ? pool : new ItemPool<PaperCard>(PaperCard.class);
} }
@@ -105,9 +108,17 @@ public class PrintSheet {
return result; return result;
} }
public boolean isEmpty() {
return cardsWithWeights.isEmpty();
}
public Iterable<PaperCard> toFlatList() {
return cardsWithWeights.toFlatList();
}
public static class Reader extends StorageReaderFileSections<PrintSheet> { public static class Reader extends StorageReaderFileSections<PrintSheet> {
public Reader(String fileName) { public Reader(File file) {
super(fileName, PrintSheet.FN_GET_KEY); super(file, PrintSheet.FN_GET_KEY);
} }
@Override @Override
@@ -117,13 +128,4 @@ public class PrintSheet {
} }
public boolean isEmpty() {
return cardsWithWeights.isEmpty();
}
public Iterable<PaperCard> toFlatList() {
return cardsWithWeights.toFlatList();
}
} }

View File

@@ -10,13 +10,15 @@ import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.StaticData;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.item.ItemPoolView; import forge.item.SealedProduct;
import forge.item.PrintSheet; import forge.util.ItemPool;
public class UnOpenedProduct implements IUnOpenedProduct { public class UnOpenedProduct implements IUnOpenedProduct {
private final SealedProductTemplate tpl; private final SealedProduct.Template tpl;
private final Map<String, PrintSheet> sheets; private final Map<String, PrintSheet> sheets;
private boolean poolLimited = false; // if true after successful generation cards are removed from printsheets. private boolean poolLimited = false; // if true after successful generation cards are removed from printsheets.
@@ -30,24 +32,24 @@ public class UnOpenedProduct implements IUnOpenedProduct {
// Means to select from all unique cards (from base game, ie. no schemes or avatars) // Means to select from all unique cards (from base game, ie. no schemes or avatars)
public UnOpenedProduct(SealedProductTemplate template) { public UnOpenedProduct(SealedProduct.Template template) {
tpl = template; tpl = template;
sheets = null; sheets = null;
} }
// Invoke this constructor only if you are sure that the pool is not equal to deafult carddb // Invoke this constructor only if you are sure that the pool is not equal to deafult carddb
public UnOpenedProduct(SealedProductTemplate template, ItemPoolView<PaperCard> pool) { public UnOpenedProduct(SealedProduct.Template template, ItemPool<PaperCard> pool) {
this(template, pool.toFlatList()); this(template, pool.toFlatList());
} }
public UnOpenedProduct(SealedProductTemplate template, Iterable<PaperCard> cards) { public UnOpenedProduct(SealedProduct.Template template, Iterable<PaperCard> cards) {
tpl = template; tpl = template;
sheets = new TreeMap<String, PrintSheet>(); sheets = new TreeMap<String, PrintSheet>();
prebuildSheets(cards); prebuildSheets(cards);
} }
public UnOpenedProduct(SealedProductTemplate sealedProductTemplate, Predicate<PaperCard> filterPrinted) { public UnOpenedProduct(SealedProduct.Template sealedProductTemplate, Predicate<PaperCard> filterPrinted) {
this(sealedProductTemplate, Iterables.filter(CardDb.instance().getAllCards(), filterPrinted)); this(sealedProductTemplate, Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), filterPrinted));
} }
private void prebuildSheets(Iterable<PaperCard> sourceList) { private void prebuildSheets(Iterable<PaperCard> sourceList) {

View File

@@ -0,0 +1,35 @@
package forge.card.mana;
import forge.card.MagicColor;
/** A bitmask to represent any mana symbol as an integer. */
public abstract class ManaAtom {
public static final int COLORLESS = 1 << 7;
/** The Constant WHITE. */
public static final int WHITE = MagicColor.WHITE;
/** The Constant BLUE. */
public static final int BLUE = MagicColor.BLUE;
/** The Constant BLACK. */
public static final int BLACK = MagicColor.BLACK;
/** The Constant RED. */
public static final int RED = MagicColor.RED;
/** The Constant GREEN. */
public static final int GREEN = MagicColor.GREEN;
/** The Constant IS_X. */
public static final int IS_X = 1 << 8;
/** The Constant OR_2_COLORLESS. */
public static final int OR_2_COLORLESS = 1 << 9;
/** The Constant OR_2_LIFE. */
public static final int OR_2_LIFE = 1 << 10;
/** The Constant IS_SNOW. */
public static final int IS_SNOW = 1 << 11;
}

View File

@@ -19,10 +19,9 @@ package forge.card.mana;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.List; import java.util.List;
import forge.card.ColorSet;
/** /**
* <p> * <p>
* CardManaCost class. * CardManaCost class.
@@ -32,7 +31,7 @@ import forge.card.ColorSet;
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $ * @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
*/ */
public final class ManaCost implements Comparable<ManaCost> { public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostShard> {
private List<ManaCostShard> shards; private List<ManaCostShard> shards;
private final int genericCost; private final int genericCost;
private final boolean hasNoCost; // lands cost private final boolean hasNoCost; // lands cost
@@ -97,23 +96,20 @@ public final class ManaCost implements Comparable<ManaCost> {
return "no cost"; return "no cost";
} }
if (this.shards.isEmpty()) { if (this.shards.isEmpty()) {
return Integer.toString(this.genericCost); return "{" + this.genericCost + "}";
} }
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
boolean isFirst = true;
if (this.genericCost > 0) { if (this.genericCost > 0) {
sb.append(this.genericCost); sb.append("{" + this.genericCost + "}");
isFirst = false;
} }
for (final ManaCostShard s : this.shards) { for (final ManaCostShard s : this.shards) {
if (!isFirst) { if (s == ManaCostShard.X) {
sb.append(' '); sb.insert(0, s.toString());
} else { } else {
isFirst = false;
}
sb.append(s.toString()); sb.append(s.toString());
} }
}
return sb.toString(); return sb.toString();
} }
@@ -143,15 +139,6 @@ public final class ManaCost implements Comparable<ManaCost> {
return result; return result;
} }
/**
* Gets the shards.
*
* @return the shards
*/
public List<ManaCostShard> getShards() {
return this.shards;
}
public int getShardCount(ManaCostShard which) { public int getShardCount(ManaCostShard which) {
if (which == ManaCostShard.COLORLESS) { if (which == ManaCostShard.COLORLESS) {
return genericCost; return genericCost;
@@ -260,13 +247,12 @@ public final class ManaCost implements Comparable<ManaCost> {
/** /**
* Can this mana cost be paid with unlimited mana of given color set. * Can this mana cost be paid with unlimited mana of given color set.
* @param color * @param colorCode
* @return * @return
*/ */
public boolean canBePaidWithAvaliable(byte colorCode) {
public boolean canBePaidWithAvaliable(ColorSet color) {
for (ManaCostShard shard : shards) { for (ManaCostShard shard : shards) {
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(color.getColor())) { if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(colorCode)) {
return false; return false;
} }
} }
@@ -288,4 +274,16 @@ public final class ManaCost implements Comparable<ManaCost> {
return res; return res;
} }
@Override
public Iterator<ManaCostShard> iterator() {
return this.shards.iterator();
}
public int getGlyphCount() { // counts all colored shards or 1 for {0} costs
int width = shards.size();
if (genericCost > 0 || (genericCost == 0 && width == 0)) {
width++;
}
return width;
}
} }

View File

@@ -11,6 +11,17 @@ public class ManaCostParser implements IParserManaCost {
private int nextToken; private int nextToken;
private int colorlessCost; private int colorlessCost;
/**
* Parse the given cost and output formatted cost string
*
* @param cost
*/
public static String parse(final String cost) {
final ManaCostParser parser = new ManaCostParser(cost);
final ManaCost manaCost = new ManaCost(parser);
return manaCost.toString();
}
/** /**
* Instantiates a new parser cardname txt mana cost. * Instantiates a new parser cardname txt mana cost.
* *
@@ -54,7 +65,6 @@ public class ManaCostParser implements IParserManaCost {
*/ */
@Override @Override
public final ManaCostShard next() { public final ManaCostShard next() {
final String unparsed = this.cost[this.nextToken++]; final String unparsed = this.cost[this.nextToken++];
// System.out.println(unparsed); // System.out.println(unparsed);
if (StringUtils.isNumeric(unparsed)) { if (StringUtils.isNumeric(unparsed)) {

View File

@@ -35,14 +35,14 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
/* Hybrid */ /* Hybrid */
WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"), WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"),
WB(ManaAtom.WHITE | ManaAtom.BLACK, "W/B", "WB"), WB(ManaAtom.WHITE | ManaAtom.BLACK, "W/B", "WB"),
WR(ManaAtom.WHITE | ManaAtom.RED, "W/R", "RW"),
WG(ManaAtom.WHITE | ManaAtom.GREEN, "W/G", "GW"),
UB(ManaAtom.BLUE | ManaAtom.BLACK, "U/B", "UB"), UB(ManaAtom.BLUE | ManaAtom.BLACK, "U/B", "UB"),
UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"), UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"),
UG(ManaAtom.BLUE | ManaAtom.GREEN, "U/G", "GU"),
BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"), BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"),
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"), BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
RW(ManaAtom.RED | ManaAtom.WHITE, "R/W", "RW"),
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"), RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
GW(ManaAtom.GREEN | ManaAtom.WHITE, "G/W", "GW"),
GU(ManaAtom.GREEN | ManaAtom.BLUE, "G/U", "GU"),
/* Or 2 colorless */ /* Or 2 colorless */
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"), W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
@@ -102,15 +102,12 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
this.shard = value; this.shard = value;
this.cmc = this.getCMC(); this.cmc = this.getCMC();
this.cmpc = this.getCmpCost(); this.cmpc = this.getCmpCost();
this.stringValue = sValue; this.stringValue = "{" + sValue + "}";
this.imageKey = imgKey; this.imageKey = imgKey;
} }
public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN; public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN;
private int getCMC() { private int getCMC() {
if (0 != (this.shard & ManaAtom.IS_X)) { if (0 != (this.shard & ManaAtom.IS_X)) {
return 0; return 0;
@@ -267,7 +264,6 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
public boolean isMonoColor() { public boolean isMonoColor() {
return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 1; return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 1;
} }
public boolean isOr2Colorless() { public boolean isOr2Colorless() {

View File

@@ -0,0 +1,199 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardDb;
import forge.item.PaperCard;
import forge.util.ItemPool;
import forge.util.ItemPoolSorter;
import forge.util.MyRandom;
/**
* Deck section.
*
*/
public class CardPool extends ItemPool<PaperCard> {
public CardPool() {
super(PaperCard.class);
}
public CardPool(final Iterable<Entry<PaperCard, Integer>> cards) {
this();
this.addAll(cards);
}
public void add(final String cardName, final int amount) {
this.add(cardName, null, -1, amount);
}
public void add(final String cardName, final String setCode) {
this.add(cardName, setCode, -1, 1);
}
public void add(final String cardName, final String setCode, final int amount) {
this.add(cardName, setCode, -1, amount);
}
// NOTE: ART indices are "1" -based
public void add(String cardName, String setCode, final int artIndex, final int amount) {
PaperCard cp = StaticData.instance().getCommonCards().getCard(cardName, setCode, artIndex);
boolean isCommonCard = cp != null;
if ( !isCommonCard ) {
cp = StaticData.instance().getVariantCards().getCard(cardName, setCode);
}
boolean artIndexExplicitlySet = artIndex > 0 || Character.isDigit(cardName.charAt(cardName.length()-1)) && cardName.charAt(cardName.length()-2) == CardDb.NameSetSeparator;
int artCount = 1;
if (cp != null ) {
setCode = cp.getEdition();
cardName = cp.getName();
artCount = isCommonCard ? StaticData.instance().getCommonCards().getArtCount(cardName, setCode) : 1;
}
else {
cp = StaticData.instance().getCommonCards().createUnsuportedCard(cardName);
}
if (artIndexExplicitlySet || artCount <= 1) {
// either a specific art index is specified, or there is only one art, so just add the card
this.add(cp, amount);
} else {
// random art index specified, make sure we get different groups of cards with different art
int[] artGroups = MyRandom.splitIntoRandomGroups(amount, artCount);
for (int i = 1; i <= artGroups.length; i++) {
int cnt = artGroups[i-1];
if (cnt <= 0)
continue;
PaperCard cp_random = isCommonCard
? StaticData.instance().getCommonCards().getCard(cardName, setCode, i)
: StaticData.instance().getVariantCards().getCard(cardName, setCode, i);
this.add(cp_random, cnt);
}
}
}
/**
* Add all from a List of CardPrinted.
*
* @param list
* CardPrinteds to add
*/
public void add(final Iterable<PaperCard> list) {
for (PaperCard cp : list) {
this.add(cp);
}
}
/**
* returns n-th card from this DeckSection. LINEAR time. No fixed order between changes
* @param i
* @return
*/
public PaperCard get(int n) {
for (Entry<PaperCard, Integer> e : this) {
n -= e.getValue();
if ( n <= 0 ) return e.getKey();
}
return null;
}
@Override
public String toString() {
if (this.isEmpty()) { return "[]"; }
boolean isFirst = true;
StringBuilder sb = new StringBuilder();
sb.append('[');
for (Entry<PaperCard, Integer> e : this) {
if (isFirst) {
isFirst = false;
}
else {
sb.append(", ");
}
sb.append(e.getValue()).append(" x ").append(e.getKey().getName());
}
return sb.append(']').toString();
}
private final static Pattern p = Pattern.compile("((\\d+)\\s+)?(.*?)");
public static CardPool fromCardList(final Iterable<String> lines) {
CardPool pool = new CardPool();
if (lines == null) {
return pool;
}
final Iterator<String> lineIterator = lines.iterator();
while (lineIterator.hasNext()) {
final String line = lineIterator.next();
if (line.startsWith(";") || line.startsWith("#")) { continue; } // that is a comment or not-yet-supported card
final Matcher m = p.matcher(line.trim());
m.matches();
final String sCnt = m.group(2);
final String cardName = m.group(3);
if (StringUtils.isBlank(cardName)) {
continue;
}
final int count = sCnt == null ? 1 : Integer.parseInt(sCnt);
pool.add(cardName, count);
}
return pool;
}
public String toCardList(String separator) {
List<Entry<PaperCard, Integer>> main2sort = Lists.newArrayList(this);
Collections.sort(main2sort, ItemPoolSorter.BY_NAME_THEN_SET);
final CardDb commonDb = StaticData.instance().getCommonCards();
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (final Entry<PaperCard, Integer> e : main2sort) {
if(!isFirst)
sb.append(separator);
else
isFirst = false;
CardDb db = !e.getKey().getRules().isVariant() ? commonDb : StaticData.instance().getVariantCards();
sb.append(e.getValue()).append(" ");
db.appendCardToStringBuilder(e.getKey(), sb);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,212 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck;
import java.util.Calendar;
import java.util.Date;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import com.google.common.base.Function;
import forge.StaticData;
import forge.card.CardEdition;
import forge.card.CardDb.SetPreference;
import forge.item.PaperCard;
/**
* <p>
* Deck class.
* </p>
*
* The set of MTG legal cards that become player's library when the game starts.
* Any other data is not part of a deck and should be stored elsewhere. Current
* fields allowed for deck metadata are Name, Title, Description and Deck Type.
*/
@SuppressWarnings("serial")
public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPool>> {
private final Map<DeckSection, CardPool> parts = new EnumMap<DeckSection, CardPool>(DeckSection.class);
private final Set<String> tags = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
// gameType is from Constant.GameType, like GameType.Regular
/**
* <p>
* Decks have their named finalled.
* </p>
*/
public Deck() {
this("");
}
/**
* Instantiates a new deck.
*
* @param name0 the name0
*/
public Deck(final String name0) {
super(name0);
getOrCreate(DeckSection.Main);
}
@Override
public String getItemType() {
return "Deck";
}
@Override
public int hashCode() {
return this.getName().hashCode();
}
/** {@inheritDoc} */
@Override
public String toString() {
return this.getName();
}
public CardPool getMain() {
return this.parts.get(DeckSection.Main);
}
// may return nulls
public CardPool get(DeckSection deckSection) {
return this.parts.get(deckSection);
}
public boolean has(DeckSection deckSection) {
final CardPool cp = get(deckSection);
return cp != null && !cp.isEmpty();
}
// will return new if it was absent
public CardPool getOrCreate(DeckSection deckSection) {
CardPool p = get(deckSection);
if (p != null) {
return p;
}
p = new CardPool();
this.parts.put(deckSection, p);
return p;
}
public void putSection(DeckSection section, CardPool pool) {
this.parts.put(section, pool);
}
/* (non-Javadoc)
* @see forge.deck.DeckBase#cloneFieldsTo(forge.deck.DeckBase)
*/
@Override
protected void cloneFieldsTo(final DeckBase clone) {
super.cloneFieldsTo(clone);
final Deck result = (Deck) clone;
for (Entry<DeckSection, CardPool> kv : parts.entrySet()) {
CardPool cp = new CardPool();
result.parts.put(kv.getKey(), cp);
cp.addAll(kv.getValue());
}
}
/*
* (non-Javadoc)
*
* @see forge.deck.DeckBase#newInstance(java.lang.String)
*/
@Override
protected DeckBase newInstance(final String name0) {
return new Deck(name0);
}
public void convertByXitaxMethod() {
CardEdition earliestSet = StaticData.instance().getEditions().getEarliestEditionWithAllCards(getAllCardsInASinglePool());
Calendar cal = Calendar.getInstance();
cal.setTime(earliestSet.getDate());
cal.add(Calendar.DATE, 1);
Date dayAfterNewestSetRelease = cal.getTime();
for(Entry<DeckSection, CardPool> p : parts.entrySet()) {
if( p.getKey() == DeckSection.Planes || p.getKey() == DeckSection.Schemes || p.getKey() == DeckSection.Avatar)
continue;
CardPool newPool = new CardPool();
for(Entry<PaperCard, Integer> cp : p.getValue()){
String cardName = cp.getKey().getName();
int artIndex = cp.getKey().getArtIndex();
PaperCard c = StaticData.instance().getCommonCards().getCardFromEdition(cardName, dayAfterNewestSetRelease, SetPreference.LatestCoreExp, artIndex);
if( null == c ) {
c = StaticData.instance().getCommonCards().getCardFromEdition(cardName, dayAfterNewestSetRelease, SetPreference.LatestCoreExp, -1);
if( c == null)
c = StaticData.instance().getCommonCards().getCardFromEdition(cardName, dayAfterNewestSetRelease, SetPreference.Latest, -1);
if( c != null ) {
newPool.add(cardName, c.getEdition(), cp.getValue()); // this is to randomize art of all those cards
} else // I give up!
newPool.add(cp.getKey(), cp.getValue());
} else
newPool.add(c, cp.getValue());
}
parts.put(p.getKey(), newPool);
}
}
public static final Function<Deck, String> FN_NAME_SELECTOR = new Function<Deck, String>() {
@Override
public String apply(Deck arg1) {
return arg1.getName();
}
};
/* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Entry<DeckSection, CardPool>> iterator() {
return parts.entrySet().iterator();
}
/**
* @return the associated tags, a writable set
*/
public Set<String> getTags() {
return tags;
}
public CardPool getAllCardsInASinglePool() {
CardPool allCards = new CardPool(); // will count cards in this pool to enforce restricted
allCards.addAll(this.getMain());
if (this.has(DeckSection.Sideboard))
allCards.addAll(this.get(DeckSection.Sideboard));
if (this.has(DeckSection.Commander))
allCards.addAll(this.get(DeckSection.Commander));
// do not include schemes / avatars and any non-regular cards
return allCards;
}
}

View File

@@ -19,13 +19,13 @@ package forge.deck;
import java.io.Serializable; import java.io.Serializable;
import forge.util.IHasName; import forge.item.InventoryItem;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
* *
*/ */
public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IHasName { public abstract class DeckBase implements Serializable, Comparable<DeckBase>, InventoryItem {
private static final long serialVersionUID = -7538150536939660052L; private static final long serialVersionUID = -7538150536939660052L;
// gameType is from Constant.GameType, like GameType.Regular // gameType is from Constant.GameType, like GameType.Regular
@@ -52,8 +52,8 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IH
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (o instanceof Deck) { if (o instanceof DeckBase) {
final Deck d = (Deck) o; final DeckBase d = (DeckBase) o;
return this.getName().equals(d.getName()); return this.getName().equals(d.getName());
} }
return false; return false;
@@ -76,6 +76,11 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IH
return this.name; return this.name;
} }
@Override
public String toString() {
return this.name;
}
/** /**
* Sets the comment. * Sets the comment.
* *
@@ -133,5 +138,4 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IH
public final String getBestFileName() { public final String getBestFileName() {
return this.getName().replaceAll("[^-_$#@.,{[()]} a-zA-Z0-9]", ""); return this.getName().replaceAll("[^-_$#@.,{[()]} a-zA-Z0-9]", "");
} }
} }

View File

@@ -0,0 +1,245 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import org.apache.commons.lang3.Range;
import forge.StaticData;
import forge.card.CardType;
import forge.card.ColorSet;
import forge.item.PaperCard;
import forge.item.IPaperCard;
import forge.util.Aggregates;
/**
* GameType is an enum to determine the type of current game. :)
*/
public enum DeckFormat {
// Main board: allowed size SB: restriction Max distinct non basic cards
Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4),
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
Commander ( Range.is(99), Range.between(0, 10), 1),
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4);
private final Range<Integer> mainRange;
private final Range<Integer> sideRange; // null => no check
private final int maxCardCopies;
DeckFormat(Range<Integer> main, Range<Integer> side, int maxCopies) {
mainRange = main;
sideRange = side;
maxCardCopies = maxCopies;
}
/**
* Smart value of.
*
* @param value the value
* @param defaultValue the default value
* @return the game type
*/
public static DeckFormat smartValueOf(final String value, DeckFormat defaultValue) {
if (null == value) {
return defaultValue;
}
final String valToCompate = value.trim();
for (final DeckFormat v : DeckFormat.values()) {
if (v.name().compareToIgnoreCase(valToCompate) == 0) {
return v;
}
}
throw new IllegalArgumentException("No element named " + value + " in enum GameType");
}
/**
* @return the sideRange
*/
public Range<Integer> getSideRange() {
return sideRange;
}
/**
* @return the mainRange
*/
public Range<Integer> getMainRange() {
return mainRange;
}
/**
* @return the maxCardCopies
*/
public int getMaxCardCopies() {
return maxCardCopies;
}
@SuppressWarnings("incomplete-switch")
public String getDeckConformanceProblem(Deck deck) {
if(deck == null) {
return "is not selected";
}
int deckSize = deck.getMain().countAll();
int min = getMainRange().getMinimum();
int max = getMainRange().getMaximum();
if (deckSize < min) {
return String.format("should have a minimum of %d cards", min);
}
if (deckSize > max) {
return String.format("should not exceed a maximum of %d cards", max);
}
switch(this) {
case Commander: //Must contain exactly 1 legendary Commander and a sideboard of 10 or zero cards.
final CardPool cmd = deck.get(DeckSection.Commander);
if (null == cmd || cmd.isEmpty()) {
return "is missing a commander";
}
if (!cmd.get(0).getRules().getType().isLegendary()
|| !cmd.get(0).getRules().getType().isCreature()) {
return "has a commander that is not a legendary creature";
}
ColorSet cmdCI = cmd.get(0).getRules().getColorIdentity();
List<PaperCard> erroneousCI = new ArrayList<PaperCard>();
for(Entry<PaperCard, Integer> cp : deck.get(DeckSection.Main)) {
if(!cp.getKey().getRules().getColorIdentity().hasNoColorsExcept(cmdCI.getColor()))
{
erroneousCI.add(cp.getKey());
}
}
if(deck.has(DeckSection.Sideboard))
{
for(Entry<PaperCard, Integer> cp : deck.get(DeckSection.Sideboard)) {
if(!cp.getKey().getRules().getColorIdentity().hasNoColorsExcept(cmdCI.getColor()))
{
erroneousCI.add(cp.getKey());
}
}
}
if(erroneousCI.size() > 0)
{
StringBuilder sb = new StringBuilder("contains card that do not match the commanders color identity:");
for(PaperCard cp : erroneousCI)
{
sb.append("\n").append(cp.getName());
}
return sb.toString();
}
break;
case Archenemy: //Must contain at least 20 schemes, max 2 of each.
final CardPool schemes = deck.get(DeckSection.Schemes);
if (schemes == null || schemes.countAll() < 20) {
return "must contain at least 20 schemes";
}
for (Entry<PaperCard, Integer> cp : schemes) {
if (cp.getValue() > 2) {
return String.format("must not contain more than 2 copies of any Scheme, but has %d of '%s'", cp.getValue(), cp.getKey().getName());
}
}
break;
}
int maxCopies = getMaxCardCopies();
if (maxCopies < Integer.MAX_VALUE) {
//Must contain no more than 4 of the same card
//shared among the main deck and sideboard, except
//basic lands, Shadowborn Apostle and Relentless Rats
CardPool tmp = new CardPool(deck.getMain());
if ( deck.has(DeckSection.Sideboard))
tmp.addAll(deck.get(DeckSection.Sideboard));
if ( deck.has(DeckSection.Commander) && this == Commander)
tmp.addAll(deck.get(DeckSection.Commander));
List<String> limitExceptions = Arrays.asList(new String[]{"Relentless Rats", "Shadowborn Apostle"});
// should group all cards by name, so that different editions of same card are really counted as the same card
for (Entry<String, Integer> cp : Aggregates.groupSumBy(tmp, PaperCard.FN_GET_NAME)) {
IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
boolean canHaveMultiple = simpleCard.getRules().getType().isBasicLand() || limitExceptions.contains(cp.getKey());
if (!canHaveMultiple && cp.getValue() > maxCopies) {
return String.format("must not contain more than %d of '%s' card", maxCopies, cp.getKey());
}
}
}
// The sideboard must contain either 0 or 15 cards
int sideboardSize = deck.has(DeckSection.Sideboard) ? deck.get(DeckSection.Sideboard).countAll() : 0;
Range<Integer> sbRange = getSideRange();
if (sbRange != null && sideboardSize > 0 && !sbRange.contains(sideboardSize)) {
return sbRange.getMinimum() == sbRange.getMaximum()
? String.format("must have a sideboard of %d cards or no sideboard at all", sbRange.getMaximum())
: String.format("must have a sideboard of %d to %d cards or no sideboard at all", sbRange.getMinimum(), sbRange.getMaximum());
}
return null;
}
public String getPlaneSectionConformanceProblem(CardPool planes) {
//Must contain at least 10 planes/phenomenons, but max 2 phenomenons. Singleton.
if (planes == null || planes.countAll() < 10) {
return "should have at least 10 planes";
}
int phenoms = 0;
for (Entry<PaperCard, Integer> cp : planes) {
if (cp.getKey().getRules().getType().typeContains(CardType.CoreType.Phenomenon)) {
phenoms++;
}
if (cp.getValue() > 1) {
return "must not contain multiple copies of any Plane or Phenomena";
}
}
if (phenoms > 2) {
return "must not contain more than 2 Phenomena";
}
return null;
}
}

View File

@@ -82,6 +82,11 @@ public class DeckGroup extends DeckBase {
Collections.sort(aiDecks, comparator); Collections.sort(aiDecks, comparator);
} }
@Override
public String getItemType() {
return "Group of decks";
}
@Override @Override
protected void cloneFieldsTo(final DeckBase clone) { protected void cloneFieldsTo(final DeckBase clone) {
super.cloneFieldsTo(clone); super.cloneFieldsTo(clone);
@@ -135,4 +140,11 @@ public class DeckGroup extends DeckBase {
} }
}; };
public static final Function<DeckGroup, Deck> FN_HUMAN_DECK = new Function<DeckGroup, Deck>() {
@Override
public Deck apply(DeckGroup arg1) {
return arg1.getHumanDeck();
}
};
} }

View File

@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import forge.card.CardDb; import forge.card.CardDb;
import forge.card.CardDb.SetPreference;
import forge.card.ICardDatabase; import forge.card.ICardDatabase;
import forge.item.PaperCard; import forge.item.PaperCard;
@@ -163,13 +164,19 @@ public class DeckRecognizer {
//private static final Pattern READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)"); //private static final Pattern READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)");
private final boolean useLastSet; private final SetPreference useLastSet;
private final ICardDatabase db; private final ICardDatabase db;
private Date recognizeCardsPrintedBefore = null; private Date recognizeCardsPrintedBefore = null;
public DeckRecognizer(boolean fromLatestSet) { public DeckRecognizer(boolean fromLatestSet, boolean onlyCoreAndExp, CardDb db) {
useLastSet = fromLatestSet; if(!fromLatestSet)
db = CardDb.instance(); useLastSet = null;
else if (onlyCoreAndExp)
useLastSet = SetPreference.LatestCoreExp;
else
useLastSet = SetPreference.Latest;
this.db = db;
} }
public Token recognizeLine(final String rawLine) { public Token recognizeLine(final String rawLine) {
@@ -202,9 +209,7 @@ public class DeckRecognizer {
} }
private PaperCard tryGetCard(String text) { private PaperCard tryGetCard(String text) {
if(recognizeCardsPrintedBefore != null ) return db.getCardFromEdition(text, recognizeCardsPrintedBefore, useLastSet);
return db.tryGetCardPrintedByDate(text, useLastSet, recognizeCardsPrintedBefore);
return db.tryGetCard(text, useLastSet);
} }
private Token recognizePossibleNameAndNumber(final String name, final int n) { private Token recognizePossibleNameAndNumber(final String name, final int n) {

View File

@@ -0,0 +1,114 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck.generation;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.collect.Lists;
import forge.card.ColorSet;
import forge.card.ICardDatabase;
import forge.card.MagicColor;
import forge.deck.CardPool;
/**
* <p>
* Generate2ColorDeck class.
* </p>
*
* @author Forge
* @version $Id$
*/
public class DeckGenerator2Color extends DeckGeneratorBase {
@Override protected final float getLandsPercentage() { return 0.39f; }
@Override protected final float getCreatPercentage() { return 0.36f; }
@Override protected final float getSpellPercentage() { return 0.25f; }
@SuppressWarnings("unchecked")
final List<ImmutablePair<FilterCMC, Integer>> cmcRelativeWeights = Lists.newArrayList(
ImmutablePair.of(new FilterCMC(0, 2), 6),
ImmutablePair.of(new FilterCMC(3, 4), 4),
ImmutablePair.of(new FilterCMC(5, 6), 2),
ImmutablePair.of(new FilterCMC(7, 20), 1)
);
// mana curve of the card pool
// 20x 0 - 2
// 16x 3 - 4
// 12x 5 - 6
// 4x 7 - 20
// = 52x - card pool (before further random filtering)
/**
* <p>
* Constructor for Generate2ColorDeck.
* </p>
*
* @param clr1
* a {@link java.lang.String} object.
* @param clr2
* a {@link java.lang.String} object.
*/
public DeckGenerator2Color(ICardDatabase cardDb, final String clr1, final String clr2) {
super(cardDb);
int c1 = MagicColor.fromName(clr1);
int c2 = MagicColor.fromName(clr2);
if( c1 == 0 && c2 == 0) {
int color1 = r.nextInt(5);
int color2 = (color1 + 1 + r.nextInt(4)) % 5;
colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2);
} else if ( c1 == 0 || c2 == 0 ) {
byte knownColor = (byte) (c1 | c2);
int color1 = Arrays.binarySearch(MagicColor.WUBRG, knownColor);
int color2 = (color1 + 1 + r.nextInt(4)) % 5;
colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2);
} else {
colors = ColorSet.fromMask(c1 | c2);
}
}
@Override
public final CardPool getDeck(final int size, final boolean forAi) {
addCreaturesAndSpells(size, cmcRelativeWeights, forAi);
// Add lands
int numLands = Math.round(size * getLandsPercentage());
adjustDeckSize(size - numLands);
tmpDeck.append(String.format("Adjusted deck size to: %d, should add %d land(s)%n", size - numLands, numLands));
// Add dual lands
List<String> duals = getDualLandList();
for (String s : duals) {
this.cardCounts.put(s, 0);
}
int dblsAdded = addSomeStr((numLands / 6), duals);
numLands -= dblsAdded;
addBasicLand(numLands);
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
//System.out.println(tmpDeck.toString());
return tDeck;
}
}

View File

@@ -0,0 +1,120 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck.generation;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.collect.Lists;
import forge.card.ColorSet;
import forge.card.ICardDatabase;
import forge.card.MagicColor;
import forge.deck.CardPool;
import forge.util.MyRandom;
/**
* <p>
* Generate3ColorDeck class.
* </p>
*
* @author Forge
* @version $Id$
*/
public class DeckGenerator3Color extends DeckGeneratorBase {
@SuppressWarnings("unchecked")
final List<ImmutablePair<FilterCMC, Integer>> cmcLevels = Lists.newArrayList(
ImmutablePair.of(new FilterCMC(0, 2), 12),
ImmutablePair.of(new FilterCMC(3, 5), 9),
ImmutablePair.of(new FilterCMC(6, 20), 3)
);
/**
* <p>
* Constructor for Generate3ColorDeck.
* </p>
*
* @param clr1
* a {@link java.lang.String} object.
* @param clr2
* a {@link java.lang.String} object.
* @param clr3
* a {@link java.lang.String} object.
*/
public DeckGenerator3Color(ICardDatabase cardDb, final String clr1, final String clr2, final String clr3) {
super(cardDb);
int c1 = MagicColor.fromName(clr1);
int c2 = MagicColor.fromName(clr2);
int c3 = MagicColor.fromName(clr3);
int rc = 0;
int combo = c1 | c2 | c3;
ColorSet param = ColorSet.fromMask(combo);
switch(param.countColors()) {
case 3:
colors = param;
return;
case 0:
int color1 = r.nextInt(5);
int color2 = (color1 + 1 + r.nextInt(4)) % 5;
colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2).inverse();
return;
case 1:
do {
rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5);
} while ( rc == combo );
combo |= rc;
// fall-through
case 2:
do {
rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5);
} while ( (rc & combo) != 0 );
combo |= rc;
break;
}
colors = ColorSet.fromMask(combo);
}
@Override
public final CardPool getDeck(final int size, final boolean forAi) {
addCreaturesAndSpells(size, cmcLevels, forAi);
// Add lands
int numLands = Math.round(size * getLandsPercentage());
adjustDeckSize(size - numLands);
tmpDeck.append("numLands:").append(numLands).append("\n");
// Add dual lands
List<String> duals = getDualLandList();
for (String s : duals) {
this.cardCounts.put(s, 0);
}
int dblsAdded = addSomeStr((numLands / 4), duals);
numLands -= dblsAdded;
addBasicLand(numLands);
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
return tDeck;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck.generation;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.collect.Lists;
import forge.card.ColorSet;
import forge.card.ICardDatabase;
import forge.deck.CardPool;
/**
* <p>
* Generate5ColorDeck class.
* </p>
*
* @author Forge
* @version $Id$
*/
public class DeckGenerator5Color extends DeckGeneratorBase {
@SuppressWarnings("unchecked")
final List<ImmutablePair<FilterCMC, Integer>> cmcLevels = Lists.newArrayList(
ImmutablePair.of(new FilterCMC(0, 2), 3),
ImmutablePair.of(new FilterCMC(3, 5), 2),
ImmutablePair.of(new FilterCMC(6, 20), 1)
);
// resulting mana curve of the card pool
// 30x 0 - 2
// 20x 3 - 5
// 10x 6 - 20
// =60x - card pool
/**
* Instantiates a new generate5 color deck.
*/
public DeckGenerator5Color(ICardDatabase cardDb) {
super(cardDb);
colors = ColorSet.fromMask(0).inverse();
}
@Override
public final CardPool getDeck(final int size, final boolean forAi) {
addCreaturesAndSpells(size, cmcLevels, forAi);
// Add lands
int numLands = Math.round(size * getLandsPercentage());
adjustDeckSize(size - numLands);
tmpDeck.append("numLands:").append(numLands).append("\n");
// Add dual lands
List<String> duals = getDualLandList();
for (String s : duals) {
this.cardCounts.put(s, 0);
}
int dblsAdded = addSomeStr((numLands / 4), duals);
numLands -= dblsAdded;
addBasicLand(numLands);
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
return tDeck;
}
}

View File

@@ -0,0 +1,409 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck.generation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.TreeMap;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
import forge.card.ColorSet;
import forge.card.ICardDatabase;
import forge.card.MagicColor;
import forge.card.mana.ManaCost;
import forge.deck.CardPool;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.ItemPool;
import forge.util.MyRandom;
/**
* <p>
* Generate2ColorDeck class.
* </p>
*
* @author Forge
* @version $Id: Generate2ColorDeck.java 14959 2012-03-28 14:03:43Z Chris H. $
*/
public abstract class DeckGeneratorBase {
protected final Random r = MyRandom.getRandom();
protected final Map<String, Integer> cardCounts = new HashMap<String, Integer>();
protected int maxDuplicates = 4;
protected boolean useArtifacts = true;
protected ColorSet colors;
protected final CardPool tDeck = new CardPool();
protected final ICardDatabase cardDb;
// 2-colored deck generator has its own constants. The rest works fine with these ones
protected float getLandsPercentage() { return 0.44f; }
protected float getCreatPercentage() { return 0.34f; }
protected float getSpellPercentage() { return 0.22f; }
StringBuilder tmpDeck = new StringBuilder();
public DeckGeneratorBase(ICardDatabase cardDb) {
this.cardDb = cardDb;
}
public void setSingleton(boolean singleton){
this.maxDuplicates = singleton ? 1 : 4;
}
public void setUseArtifacts(boolean value) {
this.useArtifacts = value;
}
protected void addCreaturesAndSpells(int size, List<ImmutablePair<FilterCMC, Integer>> cmcLevels, boolean forAi) {
tmpDeck.append("Building deck of ").append(size).append("cards\n");
final Iterable<PaperCard> cards = selectCardsOfMatchingColorForPlayer(forAi);
// build subsets based on type
final Iterable<PaperCard> creatures = Iterables.filter(cards, Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES));
final int creatCnt = (int) Math.ceil(getCreatPercentage() * size);
tmpDeck.append("Creatures to add:").append(creatCnt).append("\n");
addCmcAdjusted(creatures, creatCnt, cmcLevels);
Predicate<PaperCard> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NONCREATURE_SPELL_FOR_GENERATOR, PaperCard.FN_GET_RULES);
final Iterable<PaperCard> spells = Iterables.filter(cards, preSpells);
final int spellCnt = (int) Math.ceil(getSpellPercentage() * size);
tmpDeck.append("Spells to add:").append(spellCnt).append("\n");
addCmcAdjusted(spells, spellCnt, cmcLevels);
tmpDeck.append(String.format("Current deck size: %d... should be %f%n", tDeck.countAll(), size * (getCreatPercentage() + getSpellPercentage())));
}
public CardPool getDeck(final int size, final boolean forAi) {
return null; // all but theme deck do override this method
}
protected void addSome(int cnt, List<PaperCard> source) {
for (int i = 0; i < cnt; i++) {
PaperCard cp;
int lc = 0;
int srcLen = source.size();
do {
cp = source.get(this.r.nextInt(srcLen));
lc++;
} while (this.cardCounts.get(cp.getName()) > this.maxDuplicates - 1 && lc <= 100);
if (lc > 100) {
throw new RuntimeException("Generate2ColorDeck : get2ColorDeck -- looped too much -- Cr12");
}
tDeck.add(cardDb.getCard(cp.getName()));
final int n = this.cardCounts.get(cp.getName());
this.cardCounts.put(cp.getName(), n + 1);
if( n + 1 == this.maxDuplicates )
source.remove(cp);
tmpDeck.append(String.format("(%d) %s [%s]%n", cp.getRules().getManaCost().getCMC(), cp.getName(), cp.getRules().getManaCost()));
}
}
protected int addSomeStr(int cnt, List<String> source) {
int res = 0;
for (int i = 0; i < cnt; i++) {
String s;
int lc = 0;
do {
s = source.get(this.r.nextInt(source.size()));
lc++;
} while ((this.cardCounts.get(s) > 3) && (lc <= 20));
// not an error if looped too much - could play singleton mode, with 6 slots for 3 non-basic lands.
tDeck.add(cardDb.getCard(s));
final int n = this.cardCounts.get(s);
this.cardCounts.put(s, n + 1);
tmpDeck.append(s + "\n");
res++;
}
return res;
}
protected void addBasicLand(int cnt) {
tmpDeck.append(cnt).append(" basic lands remain").append("\n");
// attempt to optimize basic land counts according to colors of picked cards
final Map<String, Integer> clrCnts = countLands(tDeck);
// total of all ClrCnts
float totalColor = 0;
for (Entry<String, Integer> c : clrCnts.entrySet()) {
totalColor += c.getValue();
tmpDeck.append(c.getKey()).append(":").append(c.getValue()).append("\n");
}
tmpDeck.append("totalColor:").append(totalColor).append("\n");
int landsLeft = cnt;
for (Entry<String, Integer> c : clrCnts.entrySet()) {
String basicLandName = c.getKey();
// calculate number of lands for each color
final int nLand = Math.min(landsLeft, Math.round(cnt * c.getValue() / totalColor));
tmpDeck.append("nLand-").append(basicLandName).append(":").append(nLand).append("\n");
// just to prevent a null exception by the deck size fixing code
this.cardCounts.put(basicLandName, nLand);
PaperCard cp = cardDb.getCard(basicLandName);
String basicLandSet = cp.getEdition();
for (int i=0; i < nLand; i++) {
tDeck.add(cardDb.getCard(cp.getName(), basicLandSet, -1), 1);
}
landsLeft -= nLand;
}
}
protected void adjustDeckSize(int targetSize) {
// fix under-sized or over-sized decks, due to integer arithmetic
int actualSize = tDeck.countAll();
if (actualSize < targetSize) {
final int diff = targetSize - actualSize;
addSome(diff, tDeck.toFlatList());
} else if (actualSize > targetSize) {
Predicate<PaperCard> exceptBasicLand = Predicates.not(Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
for (int i = 0; i < 3 && actualSize > targetSize; i++) {
Iterable<PaperCard> matchingCards = Iterables.filter(tDeck.toFlatList(), exceptBasicLand);
List<PaperCard> toRemove = Aggregates.random(matchingCards, actualSize - targetSize);
tDeck.removeAllFlat(toRemove);
for (PaperCard c : toRemove) {
tmpDeck.append("Removed:").append(c.getName()).append("\n");
}
actualSize = tDeck.countAll();
}
}
}
protected void addCmcAdjusted(Iterable<PaperCard> source, int cnt, List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
int totalWeight = 0;
for (ImmutablePair<FilterCMC, Integer> pair : cmcLevels) {
totalWeight += pair.getRight();
}
float variability = 0.6f; // if set to 1, you'll get minimum cards to choose from
float desiredWeight = (float)cnt / ( maxDuplicates * variability );
float desiredOverTotal = desiredWeight / totalWeight;
float requestedOverTotal = (float)cnt / totalWeight;
for (ImmutablePair<FilterCMC, Integer> pair : cmcLevels) {
Iterable<PaperCard> matchingCards = Iterables.filter(source, Predicates.compose(pair.getLeft(), PaperCard.FN_GET_RULES));
int cmcCountForPool = (int) Math.ceil(pair.getRight().intValue() * desiredOverTotal);
int addOfThisCmc = Math.round(pair.getRight().intValue() * requestedOverTotal);
tmpDeck.append(String.format("Adding %d cards for cmc range from a pool with %d cards:%n", addOfThisCmc, cmcCountForPool));
final List<PaperCard> curved = Aggregates.random(matchingCards, cmcCountForPool);
final List<PaperCard> curvedRandomized = Lists.newArrayList();
for (PaperCard c : curved) {
this.cardCounts.put(c.getName(), 0);
curvedRandomized.add(cardDb.getCard(c.getName()));
}
addSome(addOfThisCmc, curvedRandomized);
}
}
protected Iterable<PaperCard> selectCardsOfMatchingColorForPlayer(boolean forAi) {
// start with all cards
// remove cards that generated decks don't like
Predicate<CardRules> canPlay = forAi ? AI_CAN_PLAY : HUMAN_CAN_PLAY;
Predicate<CardRules> hasColor = new MatchColorIdentity(colors);
if (useArtifacts) {
hasColor = Predicates.or(hasColor, COLORLESS_CARDS);
}
return Iterables.filter(cardDb.getAllCards(), Predicates.compose(Predicates.and(canPlay, hasColor), PaperCard.FN_GET_RULES));
}
protected static Map<String, Integer> countLands(ItemPool<PaperCard> outList) {
// attempt to optimize basic land counts according
// to color representation
Map<String, Integer> res = new TreeMap<String, Integer>();
// count each card color using mana costs
// TODO: count hybrid mana differently?
for (Entry<PaperCard, Integer> cpe : outList) {
int profile = cpe.getKey().getRules().getManaCost().getColorProfile();
if ((profile & MagicColor.WHITE) != 0) {
increment(res, MagicColor.Constant.BASIC_LANDS.get(0), cpe.getValue());
} else if ((profile & MagicColor.BLUE) != 0) {
increment(res, MagicColor.Constant.BASIC_LANDS.get(1), cpe.getValue());
} else if ((profile & MagicColor.BLACK) != 0) {
increment(res, MagicColor.Constant.BASIC_LANDS.get(2), cpe.getValue());
} else if ((profile & MagicColor.RED) != 0) {
increment(res, MagicColor.Constant.BASIC_LANDS.get(3), cpe.getValue());
} else if ((profile & MagicColor.GREEN) != 0) {
increment(res, MagicColor.Constant.BASIC_LANDS.get(4), cpe.getValue());
}
}
return res;
}
protected static void increment(Map<String, Integer> map, String key, int delta)
{
final Integer boxed = map.get(key);
map.put(key, boxed == null ? delta : boxed.intValue() + delta);
}
public static final Predicate<CardRules> AI_CAN_PLAY = new Predicate<CardRules>() {
@Override
public boolean apply(CardRules c) {
return !c.getAiHints().getRemAIDecks() && !c.getAiHints().getRemRandomDecks();
}
};
public static final Predicate<CardRules> HUMAN_CAN_PLAY = new Predicate<CardRules>() {
@Override
public boolean apply(CardRules c) {
return !c.getAiHints().getRemRandomDecks();
}
};
public static final Predicate<CardRules> COLORLESS_CARDS = new Predicate<CardRules>() {
@Override
public boolean apply(CardRules c) {
ManaCost mc = c.getManaCost();
return c.getColorIdentity().isColorless() && !mc.isNoCost();
}
};
public static class MatchColorIdentity implements Predicate<CardRules> {
private final ColorSet allowedColor;
public MatchColorIdentity(ColorSet color) {
allowedColor = color;
}
@Override
public boolean apply(CardRules subject) {
ManaCost mc = subject.getManaCost();
return !mc.isPureGeneric() && allowedColor.containsAllColorsFrom(subject.getColorIdentity().getColor());
//return mc.canBePaidWithAvaliable(allowedColor);
// return allowedColor.containsAllColorsFrom(mc.getColorProfile());
}
}
public static class FilterCMC implements Predicate<CardRules> {
private final int min;
private final int max;
public FilterCMC(int from, int to) {
min = from;
max = to;
}
@Override
public boolean apply(CardRules c) {
ManaCost mc = c.getManaCost();
int cmc = mc.getCMC();
return cmc >= min && cmc <= max && !mc.isNoCost();
}
}
private static Map<Integer, String[]> dualLands = new HashMap<Integer, String[]>();
static {
dualLands.put(MagicColor.WHITE | MagicColor.BLUE, new String[] { "Tundra", "Hallowed Fountain", "Flooded Strand" });
dualLands.put(MagicColor.BLACK | MagicColor.BLUE, new String[] { "Underground Sea", "Watery Grave", "Polluted Delta" });
dualLands.put(MagicColor.BLACK | MagicColor.RED, new String[] { "Badlands", "Blood Crypt", "Bloodstained Mire" });
dualLands.put(MagicColor.GREEN | MagicColor.RED, new String[] { "Taiga", "Stomping Ground", "Wooded Foothills" });
dualLands.put(MagicColor.GREEN | MagicColor.WHITE, new String[] { "Savannah", "Temple Garden", "Windswept Heath" });
dualLands.put(MagicColor.WHITE | MagicColor.BLACK, new String[] { "Scrubland", "Godless Shrine", "Marsh Flats" });
dualLands.put(MagicColor.BLUE | MagicColor.RED, new String[] { "Volcanic Island", "Steam Vents", "Scalding Tarn" });
dualLands.put(MagicColor.BLACK | MagicColor.GREEN, new String[] { "Bayou", "Overgrown Tomb", "Verdant Catacombs" });
dualLands.put(MagicColor.WHITE | MagicColor.RED, new String[] { "Plateau", "Sacred Foundry", "Arid Mesa" });
dualLands.put(MagicColor.GREEN | MagicColor.BLUE, new String[] { "Tropical Island", "Breeding Pool", "Misty Rainforest" });
}
/**
* Get list of dual lands for this color combo.
*
* @param color
* the color
* @return dual land names
*/
protected List<String> getDualLandList() {
final List<String> dLands = new ArrayList<String>();
if (colors.countColors() > 3) {
dLands.add("Rupture Spire");
dLands.add("Undiscovered Paradise");
}
if (colors.countColors() > 2) {
dLands.add("Evolving Wilds");
dLands.add("Terramorphic Expanse");
}
for (Entry<Integer, String[]> dual : dualLands.entrySet()) {
if (colors.hasAllColors(dual.getKey())) {
for (String s : dual.getValue()) {
dLands.add(s);
}
}
}
return dLands;
}
/**
* Get all dual lands that do not match this color combo.
*
* @param color
* the color
* @return dual land names
*/
protected List<String> getInverseDualLandList() {
final List<String> dLands = new ArrayList<String>();
for (Entry<Integer, String[]> dual : dualLands.entrySet()) {
if (!colors.hasAllColors(dual.getKey())) {
for (String s : dual.getValue()) {
dLands.add(s);
}
}
}
return dLands;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck.generation;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.collect.Lists;
import forge.card.ColorSet;
import forge.card.ICardDatabase;
import forge.card.MagicColor;
import forge.deck.CardPool;
/**
* <p>
* Generate2ColorDeck class.
* </p>
*
* @author Forge
* @version $Id: Generate2ColorDeck.java 19765 2013-02-20 03:01:37Z myk $
*/
public class DeckGeneratorMonoColor extends DeckGeneratorBase {
@Override protected final float getLandsPercentage() { return 0.39f; }
@Override protected final float getCreatPercentage() { return 0.36f; }
@Override protected final float getSpellPercentage() { return 0.25f; }
@SuppressWarnings("unchecked")
final List<ImmutablePair<FilterCMC, Integer>> cmcLevels = Lists.newArrayList(
ImmutablePair.of(new FilterCMC(0, 2), 10),
ImmutablePair.of(new FilterCMC(3, 4), 8),
ImmutablePair.of(new FilterCMC(5, 6), 5),
ImmutablePair.of(new FilterCMC(7, 20), 3)
);
// mana curve of the card pool
// 20x 0 - 2
// 16x 3 - 4
// 12x 5 - 6
// 4x 7 - 20
// = 52x - card pool (before further random filtering)
/**
* <p>
* Constructor for Generate2ColorDeck.
* </p>
*
* @param clr1
* a {@link java.lang.String} object.
* @param clr2
* a {@link java.lang.String} object.
*/
public DeckGeneratorMonoColor(ICardDatabase cardDb, final String clr1) {
super(cardDb);
if (MagicColor.fromName(clr1) == 0) {
int color1 = r.nextInt(5);
colors = ColorSet.fromMask(MagicColor.WHITE << color1);
} else {
colors = ColorSet.fromNames(clr1);
}
}
@Override
public final CardPool getDeck(final int size, final boolean forAi) {
addCreaturesAndSpells(size, cmcLevels, forAi);
// Add lands
int numLands = (int) (getLandsPercentage() * size);
tmpDeck.append("numLands:").append(numLands).append("\n");
addBasicLand(numLands);
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
adjustDeckSize(size);
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
return tDeck;
}
}

View File

@@ -0,0 +1,3 @@
/** Forge Card Game. */
package forge.deck.generation;

View File

@@ -23,7 +23,6 @@ import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import forge.deck.DeckFormat; import forge.deck.DeckFormat;
import forge.game.player.PlayerType;
import forge.util.FileSection; import forge.util.FileSection;
/** /**
@@ -48,7 +47,6 @@ public class DeckFileHeader {
private static final String PLAYER_TYPE = "PlayerType"; private static final String PLAYER_TYPE = "PlayerType";
private final DeckFormat deckType; private final DeckFormat deckType;
private final PlayerType playerType;
private final boolean customPool; private final boolean customPool;
private final String name; private final String name;
@@ -56,6 +54,15 @@ public class DeckFileHeader {
private final Set<String> tags; private final Set<String> tags;
private final boolean intendedForAi;
/**
* @return the intendedForAi
*/
public boolean isIntendedForAi() {
return intendedForAi;
}
/** /**
* TODO: Write javadoc for Constructor. * TODO: Write javadoc for Constructor.
* *
@@ -67,8 +74,7 @@ public class DeckFileHeader {
this.comment = kvPairs.get(DeckFileHeader.COMMENT); this.comment = kvPairs.get(DeckFileHeader.COMMENT);
this.deckType = DeckFormat.smartValueOf(kvPairs.get(DeckFileHeader.DECK_TYPE), DeckFormat.Constructed); this.deckType = DeckFormat.smartValueOf(kvPairs.get(DeckFileHeader.DECK_TYPE), DeckFormat.Constructed);
this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL); this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL);
boolean isForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE)); this.intendedForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE));
this.playerType = isForAi ? PlayerType.COMPUTER : PlayerType.HUMAN;
this.tags = new TreeSet<String>(); this.tags = new TreeSet<String>();
String rawTags = kvPairs.get(DeckFileHeader.TAGS); String rawTags = kvPairs.get(DeckFileHeader.TAGS);
@@ -81,15 +87,6 @@ public class DeckFileHeader {
} }
/**
* Gets the player type.
*
* @return the player type
*/
public final PlayerType getPlayerType() {
return this.playerType;
}
/** /**
* Checks if is custom pool. * Checks if is custom pool.
* *

View File

@@ -0,0 +1,114 @@
package forge.deck.io;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import forge.card.CardDb;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.item.IPaperCard;
import forge.util.FileSection;
import forge.util.FileSectionManual;
import forge.util.FileUtil;
public class DeckSerializer {
public static void writeDeck(final Deck d, final File f) {
FileUtil.writeFile(f, serializeDeck(d));
}
static DeckFileHeader readDeckMetadata(final Map<String, List<String>> map) {
if (map == null) {
return null;
}
final List<String> metadata = map.get("metadata");
if (metadata != null) {
return new DeckFileHeader(FileSection.parse(metadata, "="));
}
final List<String> general = map.get("general");
if (general != null) {
final FileSectionManual fs = new FileSectionManual();
fs.put(DeckFileHeader.NAME, StringUtils.join(map.get(""), " "));
fs.put(DeckFileHeader.DECK_TYPE, StringUtils.join(general, " "));
return new DeckFileHeader(fs);
}
return null;
}
private static List<String> serializeDeck(Deck d) {
final List<String> out = new ArrayList<String>();
out.add(String.format("[metadata]"));
out.add(String.format("%s=%s", DeckFileHeader.NAME, d.getName().replaceAll("\n", "")));
// these are optional
if (d.getComment() != null) {
out.add(String.format("%s=%s", DeckFileHeader.COMMENT, d.getComment().replaceAll("\n", "")));
}
if (!d.getTags().isEmpty()) {
out.add(String.format("%s=%s", DeckFileHeader.TAGS, StringUtils.join(d.getTags(), DeckFileHeader.TAGS_SEPARATOR)));
}
for(Entry<DeckSection, CardPool> s : d) {
out.add(String.format("[%s]", s.getKey().toString()));
out.add(s.getValue().toCardList(System.getProperty("line.separator")));
}
return out;
}
public static Deck fromFile(final File deckFile) {
return fromSections(FileSection.parseSections(FileUtil.readFile(deckFile)));
}
public static Deck fromSections(final Map<String, List<String>> sections) {
if (sections == null || sections.isEmpty()) {
return null;
}
final DeckFileHeader dh = readDeckMetadata(sections);
if (dh == null) {
return null;
}
final Deck d = new Deck(dh.getName());
d.setComment(dh.getComment());
d.getTags().addAll(dh.getTags());
boolean hasExplicitlySpecifiedSet = false;
for (Entry<String, List<String>> s : sections.entrySet()) {
DeckSection sec = DeckSection.smartValueOf(s.getKey());
if (sec == null) {
continue;
}
for(String k : s.getValue())
if ( k.indexOf(CardDb.NameSetSeparator) > 0 )
hasExplicitlySpecifiedSet = true;
CardPool pool = CardPool.fromCardList(s.getValue());
// I used to store planes and schemes under sideboard header, so this will assign them to a correct section
IPaperCard sample = pool.get(0);
if (sample != null && ( sample.getRules().getType().isPlane() || sample.getRules().getType().isPhenomenon())) {
sec = DeckSection.Planes;
}
if (sample != null && sample.getRules().getType().isScheme()) {
sec = DeckSection.Schemes;
}
d.putSection(sec, pool);
}
if (!hasExplicitlySpecifiedSet) {
d.convertByXitaxMethod();
}
return d;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck.io;
import java.io.File;
import java.io.FilenameFilter;
import java.util.List;
import java.util.Map;
import forge.deck.Deck;
import forge.util.FileSection;
import forge.util.FileUtil;
import forge.util.IItemReader;
import forge.util.IItemSerializer;
import forge.util.storage.StorageReaderFolder;
/**
* This class knows how to make a file out of a deck object and vice versa.
*/
public class DeckStorage extends StorageReaderFolder<Deck> implements IItemSerializer<Deck> {
private final boolean moveWronglyNamedDecks;
public static final String FILE_EXTENSION = ".dck";
/** Constant <code>DCKFileFilter</code>. */
public static final FilenameFilter DCK_FILE_FILTER = new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith(FILE_EXTENSION);
}
};
public DeckStorage(final File deckDir0) {
this(deckDir0, false);
}
public DeckStorage(final File deckDir0, boolean moveWrongDecks) {
super(deckDir0, Deck.FN_NAME_SELECTOR);
moveWronglyNamedDecks = moveWrongDecks;
}
/* (non-Javadoc)
* @see forge.util.storage.StorageReaderBase#getReaderForFolder(java.io.File)
*/
@Override
public IItemReader<Deck> getReaderForFolder(File subfolder) {
if ( !subfolder.getParentFile().equals(directory) )
throw new UnsupportedOperationException("Only child folders of " + directory + " may be processed");
return new DeckStorage(subfolder, false);
}
@Override
public void save(final Deck unit) {
DeckSerializer.writeDeck(unit, this.makeFileFor(unit));
}
@Override
public void erase(final Deck unit) {
this.makeFileFor(unit).delete();
}
public File makeFileFor(final Deck deck) {
return new File(this.directory, deck.getBestFileName() + FILE_EXTENSION);
}
@Override
protected Deck read(final File file) {
final Map<String, List<String>> sections = FileSection.parseSections(FileUtil.readFile(file));
Deck result = DeckSerializer.fromSections(sections);
if (moveWronglyNamedDecks) {
adjustFileLocation(file, result);
}
return result;
}
private void adjustFileLocation(final File file, final Deck result) {
if (result == null) {
file.delete();
} else {
String destFilename = result.getBestFileName() + FILE_EXTENSION;
if (!file.getName().equals(destFilename)) {
file.renameTo(new File(file.getParentFile().getParentFile(), destFilename));
}
}
}
@Override
protected FilenameFilter getFileFilter() {
return DCK_FILE_FILTER;
}
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.deck.io;

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.deck;

View File

@@ -20,26 +20,25 @@ package forge.item;
import com.google.common.base.Function; import com.google.common.base.Function;
import forge.Singletons; import forge.StaticData;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.card.SealedProductTemplate;
import forge.util.MyRandom; import forge.util.MyRandom;
public class BoosterPack extends OpenablePack { public class BoosterPack extends SealedProduct {
private final int artIndex; private final int artIndex;
private final int hash; private final int hash;
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() { public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() {
@Override @Override
public BoosterPack apply(final CardEdition arg1) { public BoosterPack apply(final CardEdition arg1) {
SealedProductTemplate d = Singletons.getModel().getBoosters().get(arg1.getCode()); Template d = StaticData.instance().getBoosters().get(arg1.getCode());
return new BoosterPack(arg1.getName(), d); return new BoosterPack(arg1.getName(), d);
} }
}; };
public BoosterPack(final String name0, final SealedProductTemplate boosterData) { public BoosterPack(final String name0, final Template boosterData) {
super(name0, boosterData); super(name0, boosterData);
int maxIdx = Singletons.getModel().getEditions().get(boosterData.getEdition()).getCntBoosterPictures(); int maxIdx = StaticData.instance().getEditions().get(boosterData.getEdition()).getCntBoosterPictures();
artIndex = MyRandom.getRandom().nextInt(maxIdx) + 1; artIndex = MyRandom.getRandom().nextInt(maxIdx) + 1;
hash = super.hashCode() ^ artIndex; hash = super.hashCode() ^ artIndex;
} }
@@ -58,7 +57,7 @@ public class BoosterPack extends OpenablePack {
return new BoosterPack(name, contents); return new BoosterPack(name, contents);
} }
public SealedProductTemplate getBoosterData() { public Template getBoosterData() {
return contents; return contents;
} }

View File

@@ -0,0 +1,141 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import forge.StaticData;
import forge.card.BoosterGenerator;
import forge.card.CardEdition;
import forge.util.TextUtil;
import forge.util.storage.StorageReaderFile;
public class FatPack extends SealedProduct {
public static final Function<CardEdition, FatPack> FN_FROM_SET = new Function<CardEdition, FatPack>() {
@Override
public FatPack apply(final CardEdition arg1) {
FatPack.Template d = StaticData.instance().getFatPacks().get(arg1.getCode());
return new FatPack(arg1.getName(), d);
}
};
private final FatPack.Template fpData;
public FatPack(final String name0, final FatPack.Template fpData0) {
super(name0, StaticData.instance().getBoosters().get(fpData0.getEdition()));
fpData = fpData0;
}
@Override
public String getDescription() {
return fpData.toString() + contents.toString();
}
@Override
public final String getItemType() {
return "Fat Pack";
}
@Override
protected List<PaperCard> generate() {
List<PaperCard> result = new ArrayList<PaperCard>();
for (int i = 0; i < fpData.getCntBoosters(); i++) {
result.addAll(super.generate());
}
result.addAll(BoosterGenerator.getBoosterPack(fpData));
return result;
}
@Override
public final Object clone() {
return new FatPack(name, fpData);
}
@Override
public int getTotalCards() {
return super.getTotalCards() * fpData.getCntBoosters() + fpData.getNumberOfCardsExpected();
}
public static class Template extends SealedProduct.Template {
private final int cntBoosters;
public int getCntBoosters() { return cntBoosters; }
private Template(String edition, int boosters, Iterable<Pair<String, Integer>> itrSlots)
{
super(edition, itrSlots);
cntBoosters = boosters;
}
public static final class Reader extends StorageReaderFile<Template> {
public Reader(String pathname) {
super(pathname, Template.FN_GET_NAME);
}
@Override
protected Template read(String line, int i) {
String[] headAndData = TextUtil.split(line, ':', 2);
final String edition = headAndData[0];
final String[] data = TextUtil.splitWithParenthesis(headAndData[1], ',');
int nBoosters = 6;
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
for(String slotDesc : data) {
String[] kv = TextUtil.split(slotDesc, ' ', 2);
if (kv[1].startsWith("Booster"))
nBoosters = Integer.parseInt(kv[0]);
else
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
}
return new FatPack.Template(edition, nBoosters, slots);
}
}
@Override
public String toString() {
if (0 >= cntBoosters) {
return "no cards";
}
StringBuilder s = new StringBuilder();
for(Pair<String, Integer> p : slots) {
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
}
// trim the last comma and space
if( s.length() > 0 )
s.replace(s.length() - 2, s.length(), "");
if (0 < cntBoosters) {
if( s.length() > 0 )
s.append(" and ");
s.append(cntBoosters).append(" booster packs ");
}
return s.toString();
}
}
}

View File

@@ -9,10 +9,9 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.Card; //import forge.Card;
import forge.card.CardRarity; import forge.card.CardRarity;
import forge.card.CardRules; import forge.card.CardRules;
import forge.game.player.Player;
import forge.util.PredicateString; import forge.util.PredicateString;
public interface IPaperCard extends InventoryItem { public interface IPaperCard extends InventoryItem {
@@ -161,7 +160,4 @@ public interface IPaperCard extends InventoryItem {
public abstract String getItemType(); public abstract String getItemType();
public abstract Card getMatchingForgeCard();
public abstract Card toForgeCard(Player owner);
} }

View File

@@ -57,9 +57,6 @@ public abstract class ItemPredicate {
public static class Presets { public static class Presets {
/** The Item IsPack. */ /** The Item IsPack. */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static final Predicate<InventoryItem> IS_PACK = Predicates.or(IsBoosterPack, IsFatPack, IsTournamentPack); public static final Predicate<InventoryItem> IS_PACK_OR_DECK = Predicates.or(IsBoosterPack, IsFatPack, IsTournamentPack, IsStarterDeck, IsPrebuiltDeck);
/** The Item IsDeck. */
public static final Predicate<InventoryItem> IS_DECK = Predicates.or(IsStarterDeck, IsPrebuiltDeck);
} }
} }

View File

@@ -0,0 +1,193 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
import com.google.common.base.Function;
import forge.card.CardRarity;
import forge.card.CardRules;
/**
* A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade).
* <br><br>
* The full set of rules is in the CardRules class.
*
* @author Forge
*/
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
// Reference to rules
private final transient CardRules rules;
// These fields are kinda PK for PrintedCard
private final String name;
private final String edition;
private final int artIndex;
private final boolean foil;
// Calculated fields are below:
private final transient CardRarity rarity; // rarity is given in ctor when set is assigned
@Override
public String getName() {
return this.name;
}
@Override
public String getEdition() {
return this.edition;
}
@Override
public int getArtIndex() {
return this.artIndex;
}
@Override
public boolean isFoil() {
return this.foil;
}
@Override
public boolean isToken() {
return false;
}
@Override
public CardRules getRules() {
return this.rules;
}
@Override
public CardRarity getRarity() {
return this.rarity;
}
// @Override
// public String getImageKey() {
// return getImageLocator(getImageName(), getArtIndex(), true, false);
// }
@Override
public String getItemType() {
return "Card";
}
/**
* Lambda to get rules for selects from list of printed cards.
*/
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
@Override
public CardRules apply(final PaperCard from) {
return from.rules;
}
};
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
@Override
public String apply(final PaperCard from) {
return from.getName();
}
};
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index) {
this(c, edition0, rare, index, false);
}
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
if ( edition0 == null || c == null || rare == null )
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
this.rules = c;
this.name = c.getName();
this.edition = edition0;
this.artIndex = index;
this.foil = foil;
this.rarity = rare;
}
// Want this class to be a key for HashTable
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final PaperCard other = (PaperCard) obj;
if (!this.name.equals(other.name)) {
return false;
}
if (!this.edition.equals(other.edition)) {
return false;
}
if ((other.foil != this.foil) || (other.artIndex != this.artIndex)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int code = (this.name.hashCode() * 11) + (this.edition.hashCode() * 59) + (this.artIndex * 2);
if (this.foil) {
return code + 1;
}
return code;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.name;
// cannot still decide, if this "name|set" format is needed anymore
// return String.format("%s|%s", name, cardSet);
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(final IPaperCard o) {
final int nameCmp = this.getName().compareToIgnoreCase(o.getName());
if (0 != nameCmp) {
return nameCmp;
}
// TODO compare sets properly
int setDiff = this.edition.compareTo(o.getEdition());
if ( 0 != setDiff )
return setDiff;
return Integer.compare(artIndex, o.getArtIndex());
}
}

View File

@@ -2,12 +2,10 @@ package forge.item;
import java.util.Locale; import java.util.Locale;
import forge.Card;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.card.CardRarity; import forge.card.CardRarity;
import forge.card.CardRules; import forge.card.CardRules;
import forge.card.cardfactory.CardFactory;
import forge.game.player.Player;
public class PaperToken implements InventoryItemFromSet, IPaperCard { public class PaperToken implements InventoryItemFromSet, IPaperCard {
private String name; private String name;
@@ -49,8 +47,8 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
@Override public String getName() { return name; } @Override public String getName() { return name; }
@Override public String toString() { return name; }
@Override public String getEdition() { return edition.getCode(); } @Override public String getEdition() { return edition.getCode(); }
@Override public int getArtIndex() { return 0; } // This might change however @Override public int getArtIndex() { return 0; } // This might change however
@Override public boolean isFoil() { return false; } @Override public boolean isFoil() { return false; }
@Override public CardRules getRules() { return card; } @Override public CardRules getRules() { return card; }
@@ -61,13 +59,6 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
public String getImageFilename() { return imageFileName; } public String getImageFilename() { return imageFileName; }
@Override public String getItemType() { return "Token"; } @Override public String getItemType() { return "Token"; }
@Override public Card getMatchingForgeCard() { return toForgeCard(null); } // hope this won't be queried too frequently, so no cache
@Override
public Card toForgeCard(Player owner) {
final Card c = CardFactory.getCard(this, owner);
return c;
}
@Override public boolean isToken() { return true; } @Override public boolean isToken() { return true; }
} }

View File

@@ -0,0 +1,156 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Nate
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
import java.io.File;
import java.io.FilenameFilter;
import java.util.List;
import java.util.Map;
import com.google.common.base.Function;
import forge.StaticData;
import forge.deck.Deck;
import forge.deck.io.DeckSerializer;
import forge.deck.io.DeckStorage;
import forge.util.FileSection;
import forge.util.FileUtil;
import forge.util.storage.StorageReaderFolder;
/**
* TODO: Write javadoc for this type.
*
*/
public class PreconDeck implements InventoryItemFromSet {
private final Deck deck;
private final String set;
private final String description;
private String imageFilename;
// private final SellRules recommendedDeals;
/*
* (non-Javadoc)
*
* @see forge.item.InventoryItemFromSet#getName()
*/
@Override
public String getName() {
return this.deck.getName();
}
/*
* (non-Javadoc)
*
* @see forge.item.InventoryItem#getType()
*/
@Override
public String getItemType() {
return "Prebuilt Deck";
}
@Override
public String toString() {
return this.deck.toString();
}
public PreconDeck(final Deck d, String set, String description) {
deck = d;
this.set = set;
this.description = description;
}
public final Deck getDeck() {
return this.deck;
}
/**
* Gets the recommended deals.
*
* @return the recommended deals
*/
// public final SellRules getRecommendedDeals() {
// return this.recommendedDeals;
// }
public final String getImageFilename() {
return imageFilename;
}
/*
* (non-Javadoc)
*
* @see forge.item.InventoryItemFromSet#getSet()
*/
@Override
public String getEdition() {
return this.set;
}
/**
* Gets the description.
*
* @return the description
*/
public final String getDescription() {
return this.description;
}
public static final Function<PreconDeck, String> FN_NAME_SELECTOR = new Function<PreconDeck, String>() {
@Override
public String apply(PreconDeck arg1) {
return arg1.getName();
}
};
public static class Reader extends StorageReaderFolder<PreconDeck> {
public Reader(final File deckDir0) {
super(deckDir0, PreconDeck.FN_NAME_SELECTOR);
}
@Override
protected PreconDeck read(final File file) {
return getPreconDeckFromSections(FileSection.parseSections(FileUtil.readFile(file)));
}
// To be able to read "shops" section in overloads
protected PreconDeck getPreconDeckFromSections(final Map<String, List<String>> sections) {
FileSection kv = FileSection.parse(sections.get("metadata"), "=");
String imageFilename = kv.get("Image");
String description = kv.get("Description");
String deckEdition = kv.get("set");
String set = deckEdition == null || StaticData.instance().getEditions().get(deckEdition.toUpperCase()) == null ? "n/a" : deckEdition;
PreconDeck result = new PreconDeck(DeckSerializer.fromSections(sections), set, description);
result.imageFilename = imageFilename;
return result;
}
@Override
protected FilenameFilter getFileFilter() {
return DeckStorage.DCK_FILE_FILTER;
}
}
public static final Function<PreconDeck, Deck> FN_GET_DECK = new Function<PreconDeck, Deck>() {
@Override
public Deck apply(PreconDeck arg1) {
return arg1.getDeck();
}
};
}

View File

@@ -0,0 +1,218 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.BoosterGenerator;
import forge.card.BoosterSlots;
import forge.card.CardRulesPredicates;
import forge.util.Aggregates;
import forge.util.TextUtil;
import forge.util.storage.StorageReaderFile;
public abstract class SealedProduct implements InventoryItemFromSet {
protected final Template contents;
protected final String name;
private final int hash;
private List<PaperCard> cards = null;
public SealedProduct(String name0, Template boosterData) {
if (null == name0) { throw new IllegalArgumentException("name0 must not be null"); }
if (null == boosterData) { throw new IllegalArgumentException("boosterData must not be null"); }
contents = boosterData;
name = name0;
hash = name.hashCode() ^ getClass().hashCode() ^ contents.hashCode();
}
@Override
public final String getName() {
return name + " " + getItemType();
}
public String getDescription() {
return contents.toString();
}
@Override
public final String getEdition() {
return contents.getEdition();
}
public final List<PaperCard> getCards() {
if (null == cards) {
cards = generate();
}
return cards;
}
public int getTotalCards() {
return contents.getNumberOfCardsExpected();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
SealedProduct other = (SealedProduct)obj;
return name.equals(other.name) && contents.equals(other.contents);
}
@Override
public int hashCode() {
return hash;
}
@Override
public String toString() {
return getName();
}
protected List<PaperCard> generate() {
return BoosterGenerator.getBoosterPack(contents);
}
protected PaperCard getRandomBasicLand(final String setCode) {
return this.getRandomBasicLands(setCode, 1).get(0);
}
protected List<PaperCard> getRandomBasicLands(final String setCode, final int count) {
Predicate<PaperCard> cardsRule = Predicates.and(
IPaperCard.Predicates.printedInSet(setCode),
Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
return Aggregates.random(Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), cardsRule), count);
}
public static class Template {
@SuppressWarnings("unchecked")
public final static Template genericBooster = new Template(null, Lists.newArrayList(
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1)
));
protected final List<Pair<String, Integer>> slots;
protected final String name;
public final List<Pair<String, Integer>> getSlots() {
return slots;
}
public final String getEdition() {
return name;
}
public Template(Iterable<Pair<String, Integer>> itrSlots)
{
this(null, itrSlots);
}
public Template(String name0, Iterable<Pair<String, Integer>> itrSlots)
{
slots = Lists.newArrayList(itrSlots);
name = name0;
}
public Template(String code, String boosterDesc) {
this(code, Reader.parseSlots(boosterDesc));
}
public int getNumberOfCardsExpected() {
int sum = 0;
for(Pair<String, Integer> p : slots) {
sum += p.getRight().intValue();
}
return sum;
}
public static final Function<? super Template, String> FN_GET_NAME = new Function<Template, String>() {
@Override
public String apply(Template arg1) {
return arg1.name;
}
};
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("consisting of ");
for(Pair<String, Integer> p : slots) {
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
}
// trim the last comma and space
s.replace(s.length() - 2, s.length(), "");
// put an 'and' before the previous comma
int lastCommaIdx = s.lastIndexOf(",");
if (0 < lastCommaIdx) {
s.replace(lastCommaIdx+1, lastCommaIdx+1, " and");
}
return s.toString();
}
public final static class Reader extends StorageReaderFile<Template> {
public Reader(File file) {
super(file, Template.FN_GET_NAME);
}
public static List<Pair<String, Integer>> parseSlots(String data) {
final String[] dataz = TextUtil.splitWithParenthesis(data, ',');
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
for(String slotDesc : dataz) {
String[] kv = TextUtil.splitWithParenthesis(slotDesc, ' ', 2);
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
}
return slots;
}
@Override
protected Template read(String line, int i) {
String[] headAndData = TextUtil.split(line, ':', 2);
return new Template(headAndData[0], parseSlots(headAndData[1]));
}
}
}
}

View File

@@ -21,23 +21,22 @@ import java.util.List;
import com.google.common.base.Function; import com.google.common.base.Function;
import forge.Singletons; import forge.StaticData;
import forge.card.BoosterGenerator; import forge.card.BoosterGenerator;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.card.SealedProductTemplate;
public class TournamentPack extends OpenablePack { public class TournamentPack extends SealedProduct {
/** The Constant fnFromSet. */ /** The Constant fnFromSet. */
public static final Function<CardEdition, TournamentPack> FN_FROM_SET = new Function<CardEdition, TournamentPack>() { public static final Function<CardEdition, TournamentPack> FN_FROM_SET = new Function<CardEdition, TournamentPack>() {
@Override @Override
public TournamentPack apply(final CardEdition arg1) { public TournamentPack apply(final CardEdition arg1) {
SealedProductTemplate d = Singletons.getModel().getTournamentPacks().get(arg1.getCode()); Template d = StaticData.instance().getTournamentPacks().get(arg1.getCode());
return new TournamentPack(arg1.getName(), d); return new TournamentPack(arg1.getName(), d);
} }
}; };
public TournamentPack(final String name0, final SealedProductTemplate boosterData) { public TournamentPack(final String name0, final Template boosterData) {
super(name0, boosterData); super(name0, boosterData);
} }

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.item;

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge;

View File

@@ -79,8 +79,7 @@ public class Aggregates {
if( null == source ) if( null == source )
return null; return null;
Random rnd = MyRandom.getRandom(); Random rnd = MyRandom.getRandom();
if ( source instanceof List<?> ) if ( source instanceof List<?> ) {
{
List<T> src = (List<T>)source; List<T> src = (List<T>)source;
int len = src.size(); int len = src.size();
switch(len) { switch(len) {
@@ -88,33 +87,43 @@ public class Aggregates {
case 1: return src.get(0); case 1: return src.get(0);
default: return src.get(rnd.nextInt(len)); default: return src.get(rnd.nextInt(len));
} }
} }
int n = 0;
T candidate = null; T candidate = null;
int lowest = Integer.MAX_VALUE;
for (final T item : source) { for (final T item : source) {
if ((rnd.nextDouble() * ++n) < 1) { int next = rnd.nextInt();
if(next < lowest) {
lowest = next;
candidate = item; candidate = item;
} }
} }
return candidate; return candidate;
} }
// Get several random values
// should improve to make 1 pass over source and track N candidates at once
public static final <T> List<T> random(final Iterable<T> source, final int count) { public static final <T> List<T> random(final Iterable<T> source, final int count) {
final List<T> result = new ArrayList<T>(); final List<T> result = new ArrayList<T>();
for (int i = 0; i < count; ++i) { final int[] randoms = new int[count];
final T toAdd = Aggregates.random(source); for(int i = 0; i < count; i++) {
if (toAdd == null) { randoms[i] = Integer.MAX_VALUE;
result.add(null);
}
Random rnd = MyRandom.getRandom();
for(T item : source) {
int next = rnd.nextInt();
for(int i = 0; i < count; i++) {
if(next < randoms[i]) {
randoms[i] = next;
result.set(i, item);
break; break;
} }
result.add(toAdd); }
} }
return result; return result;
} }
public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic
final Map<K, U> uniques = new Hashtable<K, U>(); final Map<K, U> uniques = new Hashtable<K, U>();
for (final U c : source) { for (final U c : source) {
@@ -123,7 +132,6 @@ public class Aggregates {
return uniques.values(); return uniques.values();
} }
public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) { public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
if (source == null) { return null; } if (source == null) { return null; }
int max = Integer.MAX_VALUE; int max = Integer.MAX_VALUE;

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package forge.model; package forge.util;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;

View File

@@ -1,4 +1,4 @@
package forge.util.maps; package forge.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;

View File

@@ -31,8 +31,6 @@ import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import forge.error.BugReporter;
/** /**
* <p> * <p>
* FileUtil class. * FileUtil class.
@@ -109,17 +107,12 @@ public final class FileUtil {
} }
p.close(); p.close();
} catch (final Exception ex) { } catch (final Exception ex) {
BugReporter.reportException(ex);
throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + file + " : " + ex); throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + file + " : " + ex);
} }
} // writeAllDecks() } // writeAllDecks()
public static String readFileToString(String filename) { public static String readFileToString(String filename) {
StringBuilder s = new StringBuilder(); return TextUtil.join(readFile(filename), "\n");
for (String line : readFile(filename)) {
s.append(line).append('\n');
}
return s.toString();
} }
public static List<String> readFile(final String filename) { public static List<String> readFile(final String filename) {
@@ -145,7 +138,6 @@ public final class FileUtil {
} }
return FileUtil.readAllLines(new FileReader(file), false); return FileUtil.readAllLines(new FileReader(file), false);
} catch (final Exception ex) { } catch (final Exception ex) {
BugReporter.reportException(ex);
throw new RuntimeException("FileUtil : readFile() error, " + ex); throw new RuntimeException("FileUtil : readFile() error, " + ex);
} }
} // readFile() } // readFile()
@@ -180,7 +172,6 @@ public final class FileUtil {
} }
in.close(); in.close();
} catch (final IOException ex) { } catch (final IOException ex) {
BugReporter.reportException(ex);
throw new RuntimeException("FileUtil : readAllLines() error, " + ex); throw new RuntimeException("FileUtil : readAllLines() error, " + ex);
} }
return list; return list;

View File

@@ -0,0 +1,381 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import forge.item.InventoryItem;
/**
* <p>
* ItemPool class.
* </p>
* Represents a list of items with amount of each
*
* @param <T>
* an Object
*/
public class ItemPool<T extends InventoryItem> implements Iterable<Entry<T, Integer>> {
/** The fn to printed. */
public final transient Function<Entry<T, Integer>, T> FN_GET_KEY = new Function<Entry<T, Integer>, T>() {
@Override
public T apply(final Entry<T, Integer> from) {
return from.getKey();
}
};
/** The fn to item name. */
public final transient Function<Entry<T, Integer>, String> FN_GET_NAME = new Function<Entry<T, Integer>, String>() {
@Override
public String apply(final Entry<T, Integer> from) {
return from.getKey().getName();
}
};
/** The fn to count. */
public final transient Function<Entry<T, Integer>, Integer> FN_GET_COUNT = new Function<Entry<T, Integer>, Integer>() {
@Override
public Integer apply(final Entry<T, Integer> from) {
return from.getValue();
}
};
// Constructors here
/**
*
* ItemPool Constructor.
*
* @param cls
* a T
*/
public ItemPool(final Class<T> cls) {
this(new Hashtable<T, Integer>(), cls);
}
@SuppressWarnings("unchecked")
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final ItemPool<Tin> from, final Class<Tout> clsHint) {
final ItemPool<Tout> result = new ItemPool<Tout>(clsHint);
if (from != null) {
for (final Entry<Tin, Integer> e : from) {
final Tin srcKey = e.getKey();
if (clsHint.isInstance(srcKey)) {
result.add((Tout) srcKey, e.getValue());
}
}
}
return result;
}
@SuppressWarnings("unchecked")
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final Iterable<Tin> from, final Class<Tout> clsHint) {
final ItemPool<Tout> result = new ItemPool<Tout>(clsHint);
if (from != null) {
for (final Tin srcKey : from) {
if (clsHint.isInstance(srcKey)) {
result.add((Tout) srcKey, Integer.valueOf(1));
}
}
}
return result;
}
protected ItemPool(final Map<T, Integer> inMap, final Class<T> cls) {
this.items = inMap;
this.myClass = cls;
}
// Data members
/** The items. */
protected final Map<T, Integer> items;
/** The my class. */
private final Class<T> myClass; // class does not keep this in runtime by
// itself
/**
* iterator.
*
* @return Iterator<Entry<T, Integer>>
*/
@Override
public final Iterator<Entry<T, Integer>> iterator() {
return this.items.entrySet().iterator();
}
// Items read only operations
/**
*
* contains.
*
* @param item
* a T
* @return boolean
*/
public final boolean contains(final T item) {
if (this.items == null) {
return false;
}
return this.items.containsKey(item);
}
/**
*
* count.
*
* @param item
* a T
* @return int
*/
public final int count(final T item) {
if (this.items == null) {
return 0;
}
final Integer boxed = this.items.get(item);
return boxed == null ? 0 : boxed.intValue();
}
/**
*
* countAll.
*
* @return int
*/
public final int countAll() {
return countAll(null, myClass);
}
public final int countAll(Predicate<T> condition) {
return countAll(condition, myClass);
}
public final <U extends InventoryItem> int countAll(Predicate<U> condition, Class<U> cls) {
int result = 0;
if (this.items != null) {
final boolean isSameClass = cls == myClass;
for (final Entry<T, Integer> kv : this) {
final T key = kv.getKey();
@SuppressWarnings("unchecked")
final U castKey = isSameClass || cls.isInstance(key) ? (U)key : null;
if (null == condition || castKey != null && condition.apply(castKey))
result += kv.getValue();
}
}
return result;
}
/**
*
* countDistinct.
*
* @return int
*/
public final int countDistinct() {
return this.items.size();
}
/**
*
* isEmpty.
*
* @return boolean
*/
public final boolean isEmpty() {
return (this.items == null) || this.items.isEmpty();
}
/**
*
* toFlatList.
*
* @return List<T>
*/
public final List<T> toFlatList() {
final List<T> result = new ArrayList<T>();
for (final Entry<T, Integer> e : this) {
for (int i = 0; i < e.getValue(); i++) {
result.add(e.getKey());
}
}
return result;
}
/**
* Gets the my class.
*
* @return the myClass
*/
public Class<T> getMyClass() {
return this.myClass;
}
// get
/**
*
* Get item view.
*
* @return a ItemPoolView
*/
public ItemPool<T> getView() {
return new ItemPool<T>(Collections.unmodifiableMap(this.items), this.getMyClass());
}
// Items manipulation
/**
*
* Add a single item.
*
* @param item
* a T
*/
public void add(final T item) {
this.add(item, 1);
}
/**
*
* Add multiple items.
*
* @param item
* a T
* @param amount
* a int
*/
public void add(final T item, final int amount) {
if (amount <= 0) {
return;
}
this.items.put(item, Integer.valueOf(this.count(item) + amount));
}
/**
* addAllFlat.
*
* @param <U>
* a InventoryItem
* @param items
* a Iterable<U>
*/
@SuppressWarnings("unchecked")
public <U extends InventoryItem> void addAllFlat(final Iterable<U> items) {
for (final U cr : items) {
if (this.getMyClass().isInstance(cr)) {
this.add((T) cr);
}
}
}
/**
* addAll.
*
* @param <U>
* an InventoryItem
* @param map
* a Iterable<Entry<U, Integer>>
*/
@SuppressWarnings("unchecked")
public <U extends InventoryItem> void addAll(final Iterable<Entry<U, Integer>> map) {
Class<T> myClass = this.getMyClass();
for (final Entry<U, Integer> e : map) {
if (myClass.isInstance(e.getKey())) {
this.add((T) e.getKey(), e.getValue());
}
}
}
/**
*
* Remove.
*
* @param item
* a T
*/
public boolean remove(final T item) {
return this.remove(item, 1);
}
/**
*
* Remove.
*
* @param item
* a T
* @param amount
* a int
*/
public boolean remove(final T item, final int amount) {
final int count = this.count(item);
if ((count == 0) || (amount <= 0)) {
return false;
}
if (count <= amount) {
this.items.remove(item);
} else {
this.items.put(item, count - amount);
}
return true;
}
public boolean removeAll(final T item) {
return this.items.remove(item) != null;
}
/**
*
* RemoveAll.
*
* @param map
* a T
*/
public void removeAll(final Iterable<Entry<T, Integer>> map) {
for (final Entry<T, Integer> e : map) {
this.remove(e.getKey(), e.getValue());
}
// need not set out-of-sync: either remove did set, or nothing was removed
}
/**
*
* TODO: Write javadoc for this method.
* @param flat Iterable<T>
*/
public void removeAllFlat(final Iterable<T> flat) {
for (final T e : flat) {
this.remove(e);
}
// need not set out-of-sync: either remove did set, or nothing was removed
}
/**
*
* Clear.
*/
public void clear() {
this.items.clear();
}
}

View File

@@ -15,13 +15,15 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package forge.item; package forge.util;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.google.common.base.Function; import com.google.common.base.Function;
import forge.item.PaperCard;
/** /**
* <p> * <p>

View File

@@ -46,19 +46,24 @@ public class Lang {
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor, String lastUnion) { public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor, String lastUnion) {
int remaining = objects.size(); int remaining = objects.size();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for(T obj : objects) { for (T obj : objects) {
remaining--; remaining--;
if( accessor != null ) if (accessor != null) {
sb.append(accessor.apply(obj)); sb.append(accessor.apply(obj));
else }
else {
sb.append(obj); sb.append(obj);
if( remaining > 1 ) sb.append(", "); }
if( remaining == 1 ) sb.append(" ").append(lastUnion).append(" "); if (remaining > 1) {
sb.append(", ");
}
else if (remaining == 1) {
sb.append(" ").append(lastUnion).append(" ");
}
} }
return sb.toString(); return sb.toString();
} }
public static <T> String joinVerb(List<T> subjects, String verb) { public static <T> String joinVerb(List<T> subjects, String verb) {
return subjects.size() > 1 || !subjectIsSingle3rdPerson(Iterables.getFirst(subjects, "it").toString()) ? verb : verbs3rdPersonSingular(verb); return subjects.size() > 1 || !subjectIsSingle3rdPerson(Iterables.getFirst(subjects, "it").toString()) ? verb : verbs3rdPersonSingular(verb);
} }
@@ -78,16 +83,18 @@ public class Lang {
} }
public static String getPlural(String noun) { public static String getPlural(String noun) {
return noun + ( noun.endsWith("s") || noun.endsWith("x") ? "es" : "s"); return noun + (noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
} }
public static <T> String nounWithAmount(int cnt, String noun) { public static <T> String nounWithAmount(int cnt, String noun) {
String countedForm = cnt <= 1 ? noun : getPlural(noun); String countedForm = cnt == 1 ? noun : getPlural(noun);
final String strCount; final String strCount;
if( cnt == 1 ) if (cnt == 1) {
strCount = startsWithVowel(noun) ? "an " : "a "; strCount = startsWithVowel(noun) ? "an " : "a ";
else }
else {
strCount = String.valueOf(cnt) + " "; strCount = String.valueOf(cnt) + " ";
}
return strCount + countedForm; return strCount + countedForm;
} }
@@ -101,6 +108,10 @@ public class Lang {
return name.endsWith("s") ? name + "'" : name + "'s"; return name.endsWith("s") ? name + "'" : name + "'s";
} }
public static String getPossessedObject(String owner, String object) {
return getPossesive(owner) + " " + object;
}
public static boolean startsWithVowel(String word) { public static boolean startsWithVowel(String word) {
return isVowel(word.trim().charAt(0)); return isVowel(word.trim().charAt(0));
} }
@@ -108,10 +119,10 @@ public class Lang {
private static final char[] vowels = { 'a', 'i', 'e', 'o', 'u' }; private static final char[] vowels = { 'a', 'i', 'e', 'o', 'u' };
public static boolean isVowel(char letter) { public static boolean isVowel(char letter) {
char l = Character.toLowerCase(letter); char l = Character.toLowerCase(letter);
for(char c : vowels) for (char c : vowels) {
if ( c == l ) return true; if (c == l) return true;
}
return false; return false;
} }
public final static String[] numbers0 = new String[] { public final static String[] numbers0 = new String[] {
@@ -122,9 +133,9 @@ public class Lang {
public static String getNumeral(int n) { public static String getNumeral(int n) {
String prefix = n < 0 ? "minus " : ""; String prefix = n < 0 ? "minus " : "";
n = Math.abs(n); n = Math.abs(n);
if ( n >= 0 && n < 20 ) if (n >= 0 && n < 20)
return prefix + numbers0[n]; return prefix + numbers0[n];
if ( n < 100 ) { if (n < 100) {
int n1 = n % 10; int n1 = n % 10;
String ones = n1 == 0 ? "" : numbers0[n1]; String ones = n1 == 0 ? "" : numbers0[n1];
return prefix + numbers20[(n / 10) - 2] + " " + ones; return prefix + numbers20[(n / 10) - 2] + " " + ones;

View File

@@ -55,4 +55,14 @@ public class MyRandom {
public static Random getRandom() { public static Random getRandom() {
return MyRandom.random; return MyRandom.random;
} }
public static int[] splitIntoRandomGroups(final int value, final int numGroups) {
int[] groups = new int[numGroups];
for (int i = 0; i < value; i++) {
groups[random.nextInt(numGroups)]++;
}
return groups;
}
} }

View File

@@ -0,0 +1,44 @@
package forge.util;
import java.lang.reflect.Constructor;
/**
* TODO: Write javadoc for this type.
*
*/
public class ReflectionUtil {
public static <T> T makeDefaultInstanceOf(Class<? extends T> cls) {
if ( null == cls )
throw new IllegalArgumentException("Class<? extends T> cls must not be null");
@SuppressWarnings("unchecked")
Constructor<? extends T>[] cc = (Constructor<? extends T>[]) cls.getConstructors();
for (Constructor<? extends T> c : cc) {
Class<?>[] pp = c.getParameterTypes();
if (pp.length == 0) {
try {
T res = c.newInstance();
return res;
} catch (Exception e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("No default constructor found in class " + cls.getName());
}
/**
* Cast object to a given type if possible, returning null if not possible
* @param obj
* @param type
*/
@SuppressWarnings("unchecked")
public static <T> T safeCast(Object obj, Class<T> type) {
if (type.isInstance(obj)) {
return (T) obj;
}
return null;
}
}

View File

@@ -0,0 +1,180 @@
package forge.util;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import forge.item.PaperCard;
/**
* TODO: Write javadoc for this type.
*
*/
public class TextUtil {
/**
* Safely converts an object to a String.
*
* @param obj
* to convert; may be null
*
* @return "null" if obj is null, obj.toString() otherwise
*/
public static String safeToString(final Object obj) {
return obj == null ? "null" : obj.toString();
}
public static String mapToString(Map<String, ?> map) {
StringBuilder mapAsString = new StringBuilder();
boolean isFirst = true;
for (Entry<String, ?> p : map.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
mapAsString.append("; ");
}
mapAsString.append(p.getKey() + " => " + (p.getValue() == null ? "(null)" : p.getValue().toString()));
}
return mapAsString.toString();
}
public static String[] split(CharSequence input, char delimiter) {
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '\0', '\0', true);
}
public static String[] split(CharSequence input, char delimiter, int limit) {
return splitWithParenthesis(input, delimiter, limit, '\0', '\0', true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter) {
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '(', ')', true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter, char openPar, char closePar) {
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, openPar, closePar, true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int limit) {
return splitWithParenthesis(input, delimiter, limit, '(', ')', true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter, char openPar, char closePar, int limit) {
return splitWithParenthesis(input, delimiter, limit, openPar, closePar, true);
}
/**
* Split string separated by a single char delimiter, can take parenthesis in account
* It's faster than String.split, and allows parenthesis
*/
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int maxEntries, char openPar, char closePar, boolean skipEmpty) {
List<String> result = new ArrayList<String>();
// Assume that when equal non-zero parenthesis are passed, they need to be discarded
boolean trimParenthesis = openPar == closePar && openPar > 0;
int nPar = 0;
int len = input.length();
int start = 0;
int idx = 1;
for (int iC = 0; iC < len; iC++) {
char c = input.charAt(iC);
if (closePar > 0 && c == closePar && nPar > 0) { nPar--; }
else if (openPar > 0 && c == openPar) nPar++;
if (c == delimiter && nPar == 0 && idx < maxEntries) {
if (iC > start || !skipEmpty) {
result.add(input.subSequence(start, iC).toString());
idx++;
}
start = iC + 1;
}
}
if (len > start || !skipEmpty)
result.add(input.subSequence(start, len).toString());
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
return trimParenthesis ? StringUtils.stripAll(toReturn, String.valueOf(openPar)) : toReturn;
}
public static String join(Iterable<String> strs, String delim) {
StringBuilder sb = new StringBuilder();
for (String str : strs) {
if (sb.length() > 0) {
sb.append(delim);
}
sb.append(str);
}
return sb.toString();
}
/**
* Converts an enum value to a printable label but upcasing the first letter
* and lcasing all subsequent letters
*/
public static String enumToLabel(Enum<?> val) {
return val.toString().substring(0, 1).toUpperCase(Locale.ENGLISH) +
val.toString().substring(1).toLowerCase(Locale.ENGLISH);
}
public static String buildFourColumnList(String firstLine, Iterable<PaperCard> cAnteRemoved) {
StringBuilder sb = new StringBuilder(firstLine);
int i = 0;
for (PaperCard cp: cAnteRemoved) {
if (i != 0) { sb.append(", "); }
if (i % 4 == 0) { sb.append("\n"); }
sb.append(cp);
i++;
}
return sb.toString();
}
public static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return (!Character.isISOControl(c)) &&
c != KeyEvent.CHAR_UNDEFINED &&
block != null &&
block != Character.UnicodeBlock.SPECIALS;
}
public enum PhraseCase {
Title,
Sentence,
Lower
}
public static String splitCompoundWord(String word, PhraseCase phraseCase) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (Character.isUpperCase(ch)) {
if (i > 0) {
builder.append(" ");
}
switch (phraseCase) {
case Title:
builder.append(ch);
break;
case Sentence:
if (i > 0) {
builder.append(ch);
}
else {
builder.append(Character.toLowerCase(ch));
}
break;
case Lower:
builder.append(Character.toLowerCase(ch));
continue;
}
}
else {
builder.append(ch);
}
}
return builder.toString();
}
}

View File

@@ -0,0 +1,52 @@
package forge.util;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
public class ThreadUtil {
static {
System.out.printf("(ThreadUtil first call): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() );
}
private static class WorkerThreadFactory implements ThreadFactory {
private int countr = 0;
private String prefix = "";
public WorkerThreadFactory(String prefix) {
this.prefix = prefix;
}
public Thread newThread(Runnable r) {
return new Thread(r, prefix + "-" + countr++);
}
}
private final static ExecutorService gameThreadPool = Executors.newCachedThreadPool(new WorkerThreadFactory("Game"));
private static ExecutorService getGameThreadPool() { return gameThreadPool; }
private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2, new WorkerThreadFactory("Delayed"));
private static ScheduledExecutorService getScheduledPool() { return scheduledPool; }
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
public final static ExecutorService getComputingPool(float loadFactor) {
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
}
public static boolean isMultiCoreSystem() {
return Runtime.getRuntime().availableProcessors() > 1;
}
public static void invokeInGameThread(Runnable toRun) {
getGameThreadPool().execute(toRun);
}
public static void delay(int milliseconds, Runnable inputUpdater) {
getScheduledPool().schedule(inputUpdater, milliseconds, TimeUnit.MILLISECONDS);
}
public static boolean isGameThread() {
return Thread.currentThread().getName().startsWith("Game");
}
}

View File

@@ -0,0 +1,86 @@
package forge.util.maps;
import java.util.EnumMap;
import java.util.Map;
/**
* TODO: Write javadoc for this type.
*
*/
public class EnumMapToAmount<T extends Enum<T>> extends EnumMap<T, Integer> implements MapToAmount<T> {
private static final long serialVersionUID = -4749796492075359368L;
public EnumMapToAmount(Class<T> keyType) {
super(keyType);
}
public EnumMapToAmount(EnumMap<T, ? extends Integer> m) {
super(m);
}
public EnumMapToAmount(Map<T, ? extends Integer> m) {
super(m);
}
@Override
public void add(T item) {
add(item, 1);
}
@Override
public void add(T item, int amount) {
if (amount <= 0) { return; } // throw an exception maybe?
Integer cur = get(item);
int newVal = cur == null ? amount : amount + cur.intValue();
put(item, Integer.valueOf(newVal));
}
@Override
public void addAll(Iterable<T> items) {
for (T i : items) {
add(i, 1);
}
}
@Override
public boolean substract(T item) {
return substract(item, 1);
}
@Override
public boolean substract(T item, int amount) {
Integer cur = get(item);
if (cur == null) { return false; }
int newVal = cur.intValue() - amount;
if (newVal > 0) {
put(item, Integer.valueOf(newVal));
}
else {
remove(item);
}
return true;
}
@Override
public void substractAll(Iterable<T> items) {
for (T i : items) {
substract(i);
}
}
@Override
public int countAll() {
int c = 0;
for (java.util.Map.Entry<T, Integer> kv : this.entrySet()) {
c += kv.getValue().intValue();
}
return c;
}
@Override
public int count(T item) {
Integer cur = get(item);
return cur == null ? 0 : cur.intValue();
}
}

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