Compare commits

..

1499 Commits

Author SHA1 Message Date
Chris
6d9e785aec [maven-release-plugin] prepare release forge-1.5.13 2014-02-21 14:18:55 +00:00
Chris
e33be38fa6 Preparing the changes.txt file for the next beta build and release. 2014-02-21 13:48:15 +00:00
Sloth
734b6f748e - Fixed Souls of the Faultless. 2014-02-21 13:39:37 +00:00
Sloth
104e6dc306 - Fixed BecomesBlockedAi. 2014-02-21 13:35:34 +00:00
drdev
a336cd7b0e Start input handling 2014-02-20 22:17:39 +00:00
drdev
f62f83bdda Avoid enabling Scissors preemptively 2014-02-20 20:50:14 +00:00
drdev
08ec2cc9d9 Refactor Graphics to support clipping and avoid drawing things outside the bounds of its parent 2014-02-20 20:47:27 +00:00
moomarc
4425029892 - missed a commit 2014-02-20 17:04:39 +00:00
moomarc
3b571cb7ec - Moved Commander to Constructed match setup.
- Variants menu removed
2014-02-20 17:04:13 +00:00
drdev
8edd870602 Refactor out FContainer from FDisplayObject 2014-02-20 16:02:57 +00:00
Chris
f81c3f40b8 Added new card names to changes.txt. 2014-02-20 13:29:10 +00:00
drdev
0cd306d23b Fix support for alpha colors 2014-02-20 07:21:53 +00:00
drdev
cafff3f379 Add logo to home screen 2014-02-20 06:49:15 +00:00
drdev
04d336f674 Support rendering text 2014-02-20 06:31:40 +00:00
swordshine
4f8babf927 - Added Ogre Geargrabber 2014-02-20 05:33:38 +00:00
drdev
c2bbd84204 Make proper background texture be rendered 2014-02-20 04:33:10 +00:00
drdev
f87f3489d1 Make rendering work for mobile game 2014-02-20 04:29:10 +00:00
drdev
77329e8fe5 Refactor FSkin for mobile game 2014-02-20 02:09:35 +00:00
drdev
c947da28c2 More mobile game refactoring 2014-02-19 23:37:59 +00:00
drdev
de51f43301 Refactor mobile Forge game 2014-02-19 22:41:15 +00:00
Sloth
bec8706fcf - Added the hard quest opponent Surtr 3. 2014-02-19 14:43:22 +00:00
Chris
7b6026e17d Added new card names to changes.txt. 2014-02-19 12:38:37 +00:00
asepetci
1324b0754b Updated draft rankings 2014-02-19 06:49:21 +00:00
swordshine
7da1d97769 - Added Leonin Arbiter 2014-02-19 02:28:14 +00:00
Maxmtg
1b5ba15321 ai mana costs will be adjusted by game state 2014-02-18 23:31:33 +00:00
Maxmtg
b299711be7 This restores mana payment prompts (lost yesterday) 2014-02-18 20:28:03 +00:00
Agetian
164490ffc9 - Get rid of the TextUtil import, use the splitting convention used everywhere else in CardFactoryUtil. 2014-02-18 14:11:02 +00:00
Agetian
eadba12739 - Comment fixes. 2014-02-18 13:57:40 +00:00
Agetian
e52e154774 - Added a way to count specific colors of mana spent on X (Count$XColorPaid followed by a list of colors in one-letter abbreviation format e.g. Count$XColorPaid WUG).
- Added Soul Burn.
2014-02-18 13:55:15 +00:00
Chris
878c037554 Added new card names to changes.txt. 2014-02-18 12:52:43 +00:00
moomarc
03cdddce6e - Fixed a bug with team allocation for variant matches 2014-02-18 12:30:13 +00:00
swordshine
f23f17be4f - Fixed Chorus of the Conclave 2014-02-18 11:51:19 +00:00
Maxmtg
fa423233ab only remove javadoc and whitespace 2014-02-18 09:11:23 +00:00
Maxmtg
c9c15d0157 remove custom maps, use guava multimaps 2014-02-18 08:33:40 +00:00
drdev
1b2e23b309 Start working on mobile home screen 2014-02-18 06:29:48 +00:00
drdev
acaa61319f Support loading skins without crashing 2014-02-18 04:18:41 +00:00
drdev
c476415bfc Create symbolic links to allow asset files to be shared amongst gui projects 2014-02-18 03:51:03 +00:00
Sol
5e3786fe1c - Fix for Players not being assigned to any team causing issues from Quest mode 2014-02-18 03:04:40 +00:00
swordshine
07915701c3 - Added Jeweled Amulet 2014-02-18 02:45:28 +00:00
Maxmtg
b69228e28b I believe this would break ai's mana payment 2014-02-18 01:12:37 +00:00
Maxmtg
42fcd856d0 remove getManaPaid() from SpellAbility because there is already a method getPayingMana()
Remove input/InputPayManaExecuteCommands.java, using Common input instead
2014-02-18 00:35:45 +00:00
Maxmtg
22be9fffb6 moved some specific code to ai 2014-02-17 22:19:45 +00:00
moomarc
204f65faac - Archenemy moved to Constructed match setup
- Basic team setup implemented
2014-02-17 16:12:21 +00:00
moomarc
7e2b336ecc - Fixed previous commit 2014-02-17 16:10:14 +00:00
moomarc
df86422e1b - Attempt to implement a basic team victory 2014-02-17 15:53:41 +00:00
moomarc
89c2130ff6 - Attempt to stop AI players in multiplayer games from always only attacking the human until dead. They will now gang up on an opponent with less than 8 life but otherwise attack a random opponent. Feel free to improve the ai. 2014-02-17 15:47:04 +00:00
Sloth
9d90ca99cb - Improved DestroyAllAi. 2014-02-17 15:18:03 +00:00
Chris
f8224a0f7e Added new card names to changes.txt. 2014-02-17 14:58:15 +00:00
moomarc
eef2e004b3 - Improved AI for Idle Thoughts 2014-02-17 14:39:39 +00:00
moomarc
4fda7abaff - Removed unneeded import 2014-02-17 12:01:30 +00:00
asepetci
72aefc9152 Read rankings from cache instead of reading file every time. 2014-02-17 11:03:45 +00:00
Maxmtg
2feac87574 add all triggered abilities to stack when priority is gained, and after devmode cheat 'add to play' 2014-02-17 08:18:48 +00:00
drdev
adcceb74ee Fix so Unique Cards Only option saved between sessions 2014-02-17 07:53:35 +00:00
drdev
9d0db8580e Support saving filters being shown/hidden between sessions 2014-02-17 07:35:47 +00:00
drdev
7106098c91 Support saving column widths and positions between sessions 2014-02-17 07:23:20 +00:00
drdev
51acbeeab2 Fix typo 2014-02-17 07:07:54 +00:00
drdev
77f7031f44 Support saving many item manager options between sessions 2014-02-17 07:02:21 +00:00
drdev
447f07ef7f Refactor how ItemView configuration is setup 2014-02-17 05:08:05 +00:00
swordshine
83a1b4f35e - Prevent NPE when the spell is countered. 2014-02-17 04:33:58 +00:00
swordshine
8cbab3a5a7 - Updated some scripts 2014-02-17 04:16:10 +00:00
Sol
8fdadd61d0 - Added Perplexing Chimera (Exchange portion of ControlSpell) 2014-02-17 03:31:38 +00:00
Sol
1c8b589b1f - Improve description of Poultice Silver 2014-02-17 03:02:39 +00:00
Maxmtg
3d360d216b kill duplicate 2014-02-16 23:21:35 +00:00
Maxmtg
2194592016 This should track the mana paid for each spell 2014-02-16 23:18:21 +00:00
Sloth
7bccaa769a - Fixed playSpellAbilityForFree not being mandatory for the AI. 2014-02-16 20:25:09 +00:00
Sol
1982f73e3d - Added an Effect to gain Control of Spells.
- Added Commandeer
2014-02-16 17:15:44 +00:00
Sloth
4525633ad8 - Added the medium quest opponent Nekusar the Mindrazer 2. 2014-02-16 14:46:55 +00:00
Agetian
6c75215113 - Restore the operation of "remove" and "remove all" buttons in DualListBox so that it's possible to remove added elements one by one or all at once without having to add all the elements first. 2014-02-16 09:08:00 +00:00
Agetian
d841ed5858 - Fix loading skins that have cropped sprite_icons.png with no physical data for missing elements (e.g. the new targeting arrows). Will now return a fully transparent color instead of bailing out with a crash. 2014-02-16 04:12:48 +00:00
drdev
255d508437 Update tooltip for Ranking column 2014-02-15 20:15:31 +00:00
drdev
ee2da8d8cd Remove deprecated Preferences pane from Deck Editor
Reorganize Deck Editor default layout
2014-02-15 20:01:30 +00:00
drdev
c4e4bc1871 Add Unique Cards Only checkbox to options panel on Constructed Catalog 2014-02-15 19:54:27 +00:00
drdev
45ce34ff0b Support showing/hiding columns via checkboxes in options panel
Truncate ranking after 4 decimal places
2014-02-15 19:33:50 +00:00
drdev
f7256824bb Make arrow buttons account for insets 2014-02-15 18:09:27 +00:00
drdev
06f33d21ee Support horizontal scroll button for option panel 2014-02-15 18:00:22 +00:00
drdev
60ae9a110b Prevent both view buttons being toggled off 2014-02-15 17:29:13 +00:00
Agetian
dbabf696ce - Better place for a comment. 2014-02-15 17:12:17 +00:00
Agetian
bcac77bd0d - Non-combat and combat targeting arrows are now themed (use CLR_NORMAL_TARGETING_ARROW and CLR_EFFECT_TARGETING_ARROW).
- Default theme is updated to include a demonstration of where and how to specify the targeting arrow colors.
- Themes which do not have the targeting arrow colors specified will use the default red color with 60% opacity for the combat arrow and CLR_ACTIVE with 60% opacity (as before) for the non-combat arrow.
- Combat targeting arrow opacity is now defined by the theme, not hard-coded.
2014-02-15 17:08:30 +00:00
Agetian
030f6c3939 - Improved the visual appearance of targeting arrows.
- Targeting overlay now displays arrows for creatures attacking planeswalkers.
- Combat arrows (blocking creatures, attacking planeswalkers) are now displayed in red while non-combat arrows are displayed in the more neutral theme-based color.
- Optimized the display of targeting arrows in "always on" mode (should be somewhat faster when many arrows are displayed at once).
- Some refactoring and minor fixes in targeting overlay code.
2014-02-15 15:23:14 +00:00
moomarc
9fbf9903ef - fixed My Undead Horde Awakens 2014-02-15 12:26:30 +00:00
swordshine
0b67f989b9 - Converted keyword "If damage would be dealt to CARDNAME, prevent that damage. Remove a +1/+1 counter from CARDNAME." to script 2014-02-15 04:27:17 +00:00
Agetian
68e8b47838 - Fix the BasicLand definition for Born of the Gods booster packs (uses Theros lands now). 2014-02-14 15:37:18 +00:00
asepetci
52965b233f updated rankings.txt 2014-02-14 15:07:14 +00:00
swordshine
a239710a6b - Update Draft Rankings 2014-02-14 09:09:42 +00:00
drdev
4f86ee6025 Remove bin folders from SVN 2014-02-13 22:40:07 +00:00
Agetian
102468d2e3 - Fix a typo. 2014-02-13 16:53:18 +00:00
Sloth
ee8f8def8e - Prevent NPE in lifeInSeriousDanger. 2014-02-13 15:52:07 +00:00
Chris
4bc8a89544 Added new card names to changes.txt. 2014-02-13 14:59:38 +00:00
swordshine
e66a776f9f - Updated scripts for Sphere of the Suns 2014-02-13 08:38:09 +00:00
drdev
0961590f20 Remove unneeded UIManager call 2014-02-13 06:50:37 +00:00
drdev
8bf4345ac3 Work towards being apply to load skins for Android app 2014-02-13 06:48:53 +00:00
swordshine
e7f0776098 - Fixed a typo 2014-02-13 05:17:48 +00:00
swordshine
35d8ea0949 - Added Daxos of Meletis and Psychic Intrusion (only updated mana color conversion for human players) 2014-02-13 05:13:14 +00:00
drdev
59979056b9 Add start of mobile Forge gui 2014-02-13 04:54:38 +00:00
Sol
dbbb3741d7 - Removing awkward Skeleton Shard cost descriptions 2014-02-13 03:08:02 +00:00
Sloth
7ace65b8c9 - Added SVar:RemAIDeck:True to Peregrine Mask. 2014-02-12 21:29:09 +00:00
Sloth
dcccae1f7c - Fixed AI using Idle Thoughts. 2014-02-12 21:28:28 +00:00
Maxmtg
fa3a140470 fix crashes in deck import when 'use latest edition' is unchecked. 2014-02-12 18:23:42 +00:00
Sloth
d9b657acf4 - Fixed AI suspending Aeon Chronicler for 0. 2014-02-12 15:28:35 +00:00
moomarc
b9046d8326 - Implemented alternate token art parameter in TokenEffect
- updated Eldrazi spawn token generators to reflect correct brood lineage according to https://www.wizards.com/magic/magazine/Article.aspx?x=mtg/daily/arcana/472
- updated token urls with THS tokens
- updated Theros scripts with token image params
2014-02-12 10:12:27 +00:00
swordshine
5b70f38a9c - Fixed TokenTapped 2014-02-12 10:05:21 +00:00
drdev
f94fcfc997 Lock resize cursors when resizing dock panes 2014-02-12 06:50:16 +00:00
Sloth
73e835df10 - Fixed Demonic Tutor and friends revealing the fetched card. 2014-02-12 06:30:39 +00:00
drdev
3dc4594173 Fix so I-Beam cursor displayed when over text fields 2014-02-12 06:29:49 +00:00
drdev
1606bfa2c5 Show resize cursor when hover border between columns and while resizing column 2014-02-12 06:17:17 +00:00
drdev
b39e0f4052 Allow manual resizing of fixed width columns 2014-02-12 05:40:26 +00:00
drdev
f61ac6b361 Column tweaks 2014-02-12 04:41:00 +00:00
drdev
b447b4ab6b Avoid showing unnecessary Folder column in certain DeckManagers 2014-02-12 04:25:20 +00:00
drdev
968a035672 Support marking decks favorites similar to cards 2014-02-12 04:03:49 +00:00
drdev
d976bed702 Support restoring the current deck in the Constructed Deck Editor after closing and reopening Forge 2014-02-11 19:26:29 +00:00
Maxmtg
720a44b4fd change zone ai won't crash with oob 2014-02-11 09:37:55 +00:00
swordshine
fb2287b8c2 - Fixed RearrangeTopOfLibraryEffect 2014-02-11 05:03:26 +00:00
Maxmtg
6468dcc712 bugfix: for cases when no foil is present 2014-02-11 04:56:01 +00:00
Maxmtg
c5923c31e9 DGM booster generation fixes 2014-02-11 04:50:12 +00:00
Sol
7ab28b5226 - Improving two card details text 2014-02-11 04:48:41 +00:00
swordshine
f862a01a78 - Fixed ThisTurnEntered and LastTurnEnded so controller change effects will not count 2014-02-11 04:41:38 +00:00
swordshine
875d019b1c - Fixed 2 cards 2014-02-11 03:58:24 +00:00
Sol
a4271de319 - Remove a phased out token from the battlefield, instead of exiling it, so Attached permanents get permanently phased out 2014-02-11 02:12:13 +00:00
Sol
4d00ab3ecf - Fix crash related to the legendary rule 2014-02-11 01:15:16 +00:00
Sloth
bebe569d10 - Fixed Steamflogger Boss. 2014-02-10 20:03:30 +00:00
moomarc
1c147936d7 - Added links to the Theros booster pack images
- Removed some old Planechase and Vanguard variant stuff
2014-02-10 15:06:13 +00:00
Chris
7e9220d414 Added new card names to changes.txt. 2014-02-10 14:28:24 +00:00
Agetian
01867e5352 - Made FoilAlwaysInCommonSlot the default choice (since all current sets use this option anyway).
- Refactored the edition definitions, now slots newer than TSP do not include the FoilAlwaysInCommonSlot line, while the older sets with foils have a FoilAlwaysInCommonSlot=False line.
- Some definition fixes related to foil generation.
2014-02-10 10:18:56 +00:00
Agetian
09b638075c - Another MMA foil update. 2014-02-10 08:44:52 +00:00
Agetian
79a61f2e12 - Fix for the previous fix. 2014-02-10 08:38:40 +00:00
Agetian
ed39723ab6 - Fix Modern Masters definition to work correctly with foils. 2014-02-10 08:38:03 +00:00
Agetian
b22ce18572 - Fix imports. 2014-02-10 08:34:09 +00:00
Agetian
9b55d3c36b - Update the Modern Masters definition. 2014-02-10 08:33:36 +00:00
Agetian
02bb5942b3 - Get rid of a debug println. 2014-02-10 08:33:24 +00:00
Agetian
a72655fc79 - Read the foil chance in booster value from edition files as a US locale double. 2014-02-10 08:32:49 +00:00
Agetian
30fda0e7a8 - Simplify foil chance calculations, use double to represent chance. 2014-02-10 08:17:34 +00:00
Maxmtg
9957c12ff8 split Command interface - separate class for ui, and another one for game. 2014-02-10 08:11:32 +00:00
Maxmtg
bfee22e968 disbanded 'constant' class from game 2014-02-10 08:04:02 +00:00
Agetian
485aa06339 - Revert the previous representation of foil chances in booster (doesn't work the way I thought it would).
- Changed the default chance of a foil in booster to 0.2143 (21.43%) which seems to match the official 1 foil per 70 cards more closely.
2014-02-10 07:38:13 +00:00
Agetian
bd5471cb30 - A comment is not necessary here (moved elsewhere). 2014-02-10 06:56:40 +00:00
Agetian
39c83b4870 - A more intuitive way to represent foil chance in booster in edition files (1:6 instead of 1667, 1:1 instead of 10000).
- Updated Modern Masters definition file to use the new format.
2014-02-10 06:56:00 +00:00
swordshine
d32858b4c4 - Added False Dawn 2014-02-10 05:21:14 +00:00
Sol
de0348592b - Add an extra Restriction Matrix to handle Celestial Dawn for Mana Conversion
- Added Celestial Dawn
2014-02-10 03:22:19 +00:00
Maxmtg
804a8801e5 Moved ai to dedicated module, fix api dictionary 2014-02-09 21:12:26 +00:00
Maxmtg
dd34a3aa9b remove AI class from ApiType 2014-02-09 21:01:34 +00:00
Maxmtg
0f37de9627 removed another ai hook 2014-02-09 20:25:47 +00:00
Maxmtg
14a0e6e3ac by adding a bit ugly method to player controller, I've removed last isHuman check and the last reference from api executor to AI 2014-02-09 20:22:15 +00:00
Maxmtg
2f2d24eb3e move some ai-classes to right package 2014-02-09 20:15:58 +00:00
Maxmtg
06484f81f6 Separate AI decision making from SpellAbility classes 2014-02-09 20:05:05 +00:00
drdev
b3f019420f Fix so current quest deck remembered correctly 2014-02-09 19:47:33 +00:00
Agetian
2487f7ee93 - Modern Masters now correctly has a foil in every booster pack. 2014-02-09 19:38:54 +00:00
drdev
e9c4f0913d Fix so quest decks can be deleted properly 2014-02-09 19:18:57 +00:00
drdev
81c5912add Remove unused imports 2014-02-09 19:07:10 +00:00
Hellfish
767524fb57 *More overzealous rename refactoring done away with 2014-02-09 17:44:00 +00:00
Hellfish
40f7d164bf *Fix overzealous rename refactoring 2014-02-09 17:38:39 +00:00
Hellfish
1ff23dcc56 *Apparently the reintroduced TriggerReplacementBase was never added. It is now! 2014-02-09 17:33:14 +00:00
Agetian
a4e9aa6243 - Added the star icon to old-style (pre-8E) foils for authenticity. 2014-02-09 16:41:19 +00:00
Hellfish
50c40311a7 *Reintroduced TriggerReplacementBase as a specialized subclass of CardTraitBase 2014-02-09 16:03:27 +00:00
Chris
d331c87ab5 Added new card names to changes.txt. 2014-02-09 15:24:50 +00:00
Hellfish
c4e1552b8b *StaticAbility shares some functionality with Trigger and ReplacementEffect, extend their base instead of duplicating code 2014-02-09 14:42:39 +00:00
Hellfish
94783575cc *Prettied up TriggerReplacementBase 2014-02-09 14:14:08 +00:00
Hellfish
a54a0ba985 *Moved common functionality of Trigger and ReplacementEffect into base class. 2014-02-09 13:56:04 +00:00
swordshine
e9da0ec519 - Fixed Fact or Fiction 2014-02-09 11:19:54 +00:00
swordshine
8a9a13c5d7 - "enters the battlefield tapped with counters" now is a single replacement effect. 2014-02-09 10:43:27 +00:00
swordshine
85d967e5a5 - Fixed Eureka so these cards actually start with the activator when resolving 2014-02-09 10:39:48 +00:00
swordshine
453bf76d63 - Added Fearsome Temper 2014-02-09 10:36:23 +00:00
swordshine
72bb290215 - Added Contamination 2014-02-09 10:30:21 +00:00
swordshine
6230b6f28b - Converted "CARDNAME enters the battlefield tapped unless you control a" keyword to an ETBtapped replacement effect. 2014-02-09 10:28:35 +00:00
swordshine
0f5228259a - Fixed Mistbind Clique 2014-02-09 10:18:46 +00:00
Sloth
4b5bd3d619 - Added Vortex Elemental. 2014-02-09 09:10:38 +00:00
Maxmtg
1ad1714388 fix hybrids payment {2/C} 2014-02-09 07:22:08 +00:00
drdev
f10fd27869 Fix so view has focus when opening FDeckViewer 2014-02-09 06:05:56 +00:00
drdev
1f883c993b Fix so switching group by options retains all groups being collapsed
Add option to group by rarity
2014-02-09 05:30:14 +00:00
drdev
319b470acd Prevent crash when attempting to being build Draft deck immediately after ending drafting process and saving the draft 2014-02-09 05:10:51 +00:00
Agetian
e09ec6c165 - Better variable name. 2014-02-09 04:45:40 +00:00
Agetian
1fa0a2c81c - Fixed a bug in booster generator that caused the foil from a wrong print sheet to be generated for sets that did not have a foil always in common slot. 2014-02-09 04:41:54 +00:00
Sol
a8e54c5052 - Added Warping Wurm by squee1968 2014-02-09 03:39:53 +00:00
drdev
f17fa767c0 Sort draft pack by rarity then by color 2014-02-09 02:57:00 +00:00
Sol
c8eed79a2a - Initial checkin for Additive Mana Conversion Static Ability (Restrictive Conversions currently not 100% correct)
- Added Mycosynth Lattice
- Fixed Sunglasses of Urza
2014-02-09 02:37:09 +00:00
Sol
537f95c79d - Fixed infinite loop caused by Max's trigger fixes when no triggers are on the stack 2014-02-09 02:10:19 +00:00
Maxmtg
c923a7d976 fix comile erre 2014-02-08 22:49:56 +00:00
Maxmtg
b0939aeed8 bug fix 2014-02-08 21:10:23 +00:00
Maxmtg
a7d359fa10 loop until nothing added to stack, AP is turn owner 2014-02-08 20:52:02 +00:00
drdev
862d8ed857 Fix so foils display properly in CardManager Image Views 2014-02-08 20:41:07 +00:00
drdev
83cebd7e82 Fix label 2014-02-08 20:26:30 +00:00
Maxmtg
1eeab52cdf all player will add their triggers to stack when ANY player has priority 2014-02-08 20:24:35 +00:00
Agetian
686807b0d2 - A closer approximation of foil chance in booster (16.67% for 1:6 ratio instead of just 16%). 2014-02-08 20:03:01 +00:00
Agetian
f0464395ab - A little correction. 2014-02-08 19:55:25 +00:00
Agetian
87de44dd04 - Added information about extended foil support to CHANGES.txt. 2014-02-08 19:55:06 +00:00
Agetian
5fe65ad74b - Added a preference to support enabling/disabling foil overlay display in GUI.
- Tied the card panel foil display to the user preference setting.
2014-02-08 19:35:36 +00:00
drdev
58600fbfad Lock input while refreshing images 2014-02-08 19:34:37 +00:00
drdev
c92be334ae Maximize space for viewing pack cards by eliminating Choose Card button and hiding view options by default 2014-02-08 18:51:55 +00:00
drdev
8276b5c51a Support apply foil effect to card images in CardManager Image View 2014-02-08 18:29:04 +00:00
Agetian
d7224d7515 - Fixed a bug that caused the game to crash when generating boosters with no specified code (fixes full cardpool draft).
.
2014-02-08 18:24:04 +00:00
drdev
96ff2dc09e Support hiding filters by default on Draft screen 2014-02-08 18:23:21 +00:00
Agetian
a40da18132 - Added Born of the Gods quest precons. 2014-02-08 17:34:49 +00:00
drdev
8cb889da68 Prevent horizontal line appearing when hiding filters and not showing button panel 2014-02-08 17:28:21 +00:00
drdev
08d2d7f4e8 Prevent Add Player button getting stuck 2014-02-08 17:22:39 +00:00
Maxmtg
e9c273e40f logic optimized 2014-02-08 17:13:35 +00:00
Maxmtg
9500b3d951 acquire booster edition once 2014-02-08 17:07:15 +00:00
drdev
2b4e935373 Align buttons 2014-02-08 17:01:23 +00:00
Agetian
4f9fd821ec - Added foil generation in booster packs (the UI support is currently incomplete, foils will not be distinguished in deck editors or displayed in pile view or booster card list for now; also, there is currently no option to disable the foil visual effect in GUI). 2014-02-08 16:16:23 +00:00
Agetian
b221b7eaab - Added a method to add a color replacement to ManaPool.
- Fixed a mistype in method name.
2014-02-08 16:04:54 +00:00
Chris
4b8ed1df67 Added new card names to changes.txt. 2014-02-08 15:19:30 +00:00
Chris
9399097069 Cleared out the changes.txt file, now ready for new material. 2014-02-08 15:17:27 +00:00
Maxmtg
337f5b7320 clean up code style 2014-02-08 15:01:46 +00:00
Agetian
80c70d321e - Append the foil suffix to the card name instead of to the end of the line (looks better in the deck file and fixes foil card recognition). 2014-02-08 13:16:36 +00:00
Maxmtg
676fee32c6 renamed add/subtract methods, fixed bug with colorless payment 2014-02-08 12:56:54 +00:00
Maxmtg
715ee1f9ab color conversion routine for mana implemented 2014-02-08 12:21:55 +00:00
Maxmtg
c5a791f361 more paths require manapool 2014-02-08 11:33:17 +00:00
Maxmtg
1ec656f2ed ManaCostBeingPaid no longer be built from string, its "pay" methods require reference to manaPool (which is to hold color replacement rules) 2014-02-08 11:26:27 +00:00
Sloth
97b579a68d - Fixed Lightning Volley. 2014-02-08 07:47:31 +00:00
drdev
59f75710dc Add Expand/Collapse all groups button to Image View 2014-02-08 04:47:45 +00:00
drdev
c7e74b2ddc Add combo box as way to change column count in addition to Ctrl+MouseWheel 2014-02-08 02:54:59 +00:00
drdev
6380b4c8fe Change image scaling method to be based on column count rather than fixed size 2014-02-08 02:21:27 +00:00
Sol
3d4f9e7297 - Committing Ertai's Familiar and Shimmering Efreet by squee1968 2014-02-07 17:29:15 +00:00
Chris
bfe48d2611 [maven-release-plugin] prepare for next development iteration 2014-02-07 17:25:48 +00:00
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
17030 changed files with 123738 additions and 102288 deletions

View File

@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>

31550
.gitattributes vendored

File diff suppressed because it is too large Load Diff

30
.gitignore vendored
View File

@@ -2,20 +2,32 @@
/*.iml
/*.tmp
/.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-m-android/bin
forge-m-base/bin
forge-m-desktop/bin
forge-net/target
/forge.profile.properties
/nbactions.xml
/pom.xml.next
/pom.xml.releaseBackup
/pom.xml.tag
/release.properties
res/*.log
res/PerSetTrackingResults
res/cardsfolder/*.bat
res/decks
res/layouts
res/pics*
res/pics_product
/target
/test-output
tools/PerSetTrackingResults
tools/oracleScript.log

View File

@@ -4,36 +4,7 @@
<comment></comment>
<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>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
</natures>
</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.13</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>

View File

@@ -15,25 +15,26 @@
* 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.game.ai;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.GameEntity;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
//doesHumanAttackAndWin() uses the global variable AllZone.getComputerPlayer()
@@ -58,6 +59,7 @@ public class AiAttackController {
private List<Card> myList; // holds computer creatures
private final Player ai;
private Player defendingOpponent;
private int aiAggression = 0; // added by Masher, how aggressive the ai is
// attack will be depending on circumstances
@@ -75,22 +77,34 @@ public class AiAttackController {
*/
public AiAttackController(final Player ai) {
this.ai = ai;
Player opponent = ai.getOpponent();
this.defendingOpponent = choosePreferredDefenderPlayer();
this.oppList = Lists.newArrayList();
this.oppList.addAll(opponent.getCreaturesInPlay());
this.oppList.addAll(this.defendingOpponent.getCreaturesInPlay());
this.myList = ai.getCreaturesInPlay();
this.attackers = new ArrayList<Card>();
for (Card c : myList) {
if (CombatUtil.canAttack(c, opponent)) {
if (CombatUtil.canAttack(c, this.defendingOpponent)) {
attackers.add(c);
}
}
this.blockers = this.getPossibleBlockers(oppList, this.attackers);
} // constructor
/** Choose opponent for AI to attack here. Expand as necessary. */
private Player choosePreferredDefenderPlayer() {
Player defender = ai.getWeakestOpponent(); //Gets opponent with the least life
if (defender.getLife() < 8) { //Concentrate on opponent within easy kill range
return defender;
} else { //Otherwise choose a random opponent to ensure no ganging up on players
defender = ai.getOpponents().get(MyRandom.getRandom().nextInt(ai.getOpponents().size()));
}
return defender;
}
/**
* <p>
* sortAttackers.
@@ -129,7 +143,7 @@ public class AiAttackController {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param combat
* a {@link forge.game.combat.Combat} object.
* @return a boolean.
@@ -141,7 +155,7 @@ public class AiAttackController {
return false;
}
final Player opp = ai.getOpponent();
final Player opp = this.defendingOpponent;
if (ComputerUtilCombat.damageIfUnblocked(attacker, opp, combat) > 0) {
return true;
}
@@ -192,7 +206,7 @@ public class AiAttackController {
* </p>
*
* @param c
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param attackers
* a {@link forge.CardList} object.
* @return a boolean.
@@ -277,7 +291,7 @@ public class AiAttackController {
return notNeededAsBlockers;
}
final Player opp = ai.getOpponent();
final Player opp = this.defendingOpponent;
// Increase the total number of blockers needed by 1 if Finest Hour in
// play
@@ -385,11 +399,11 @@ public class AiAttackController {
final List<Card> unblockedAttackers = new ArrayList<Card>();
final List<Card> remainingAttackers = new ArrayList<Card>(this.attackers);
final List<Card> remainingBlockers = new ArrayList<Card>(this.blockers);
final Player opp = ai.getOpponent();
final Player opp = this.defendingOpponent;
for (Card attacker : attackers) {
if (!CombatUtil.canBeBlocked(attacker, this.blockers)
if (!CombatUtil.canBeBlocked(attacker, this.blockers, null)
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
unblockedAttackers.add(attacker);
}
@@ -449,27 +463,28 @@ public class AiAttackController {
if (defs.size() == 1) {
return defs.get(0);
}
Player prefDefender = (Player) (defs.contains(this.defendingOpponent) ? this.defendingOpponent : defs.get(0));
final GameEntity entity = ai.getMustAttackEntity();
if (null != entity) {
int n = defs.indexOf(entity);
if (-1 == n) {
System.out.println("getMustAttackEntity() returned something not in defenders.");
return defs.get(0);
return prefDefender;
} else {
return entity;
}
} else {
// 1. assault the opponent if you can kill him
if (bAssault) {
return getDefendingPlayers(c).get(0);
return prefDefender;
}
// 2. attack planeswalkers
List<Card> pwDefending = c.getDefendingPlaneswalkers();
if (!pwDefending.isEmpty()) {
return pwDefending.get(0);
} else {
return defs.get(0);
return prefDefender;
}
}
}
@@ -623,7 +638,7 @@ public class AiAttackController {
aiLifeToPlayerDamageRatio = (double) ai.getLife() / candidateCounterAttackDamage;
}
final Player opp = ai.getOpponent();
final Player opp = this.defendingOpponent;
// get the potential damage and strength of the AI forces
final List<Card> candidateAttackers = new ArrayList<Card>();
int candidateUnblockedDamage = 0;
@@ -792,7 +807,7 @@ public class AiAttackController {
for (int i = 0; i < attackersLeft.size(); i++) {
final Card attacker = attackersLeft.get(i);
if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
&& ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, ai.getOpponent())
&& ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, this.defendingOpponent)
>= ComputerUtilCombat.getDamageToKill(attacker)) {
continue;
}
@@ -808,9 +823,9 @@ public class AiAttackController {
List<Card> attacking = combat.getAttackersOf(defender);
CardLists.sortByPowerAsc(attacking);
for (Card atta : attacking) {
if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers)) {
if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers, combat)) {
damage += ComputerUtilCombat.damageIfUnblocked(atta, opp, null);
} else if (CombatUtil.canBeBlocked(attacker, this.blockers)) {
} else if (CombatUtil.canBeBlocked(attacker, this.blockers, combat)) {
attackNum++;
}
}
@@ -859,7 +874,7 @@ public class AiAttackController {
* </p>
*
* @param c
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public final int getAttack(final Card c) {
@@ -878,7 +893,7 @@ public class AiAttackController {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defenders
* a {@link forge.CardList} object.
* @param combat
@@ -961,7 +976,7 @@ public class AiAttackController {
return true;
}
if (numberOfPossibleBlockers > 1 || (numberOfPossibleBlockers == 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1))) {
if (numberOfPossibleBlockers > 1 || (numberOfPossibleBlockers == 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1, combat))) {
canBeBlocked = true;
}
/*System.out.println(attacker + " canBeKilledByOne: " + canBeKilledByOne + " canKillAll: "

View File

@@ -15,26 +15,25 @@
* 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.game.ai;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.CounterType;
import forge.GameEntity;
import forge.card.TriggerReplacementBase;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType;
import forge.game.CardTraitBase;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
@@ -137,7 +136,7 @@ public class AiBlockController {
// Begin with the attackers that pose the biggest threat
CardLists.sortByEvaluateCreature(firstAttacker);
ComputerUtilCard.sortByEvaluateCreature(firstAttacker);
CardLists.sortByPowerDesc(firstAttacker);
// If I don't have any planeswalkers than sorting doesn't really matter
@@ -186,7 +185,9 @@ public class AiBlockController {
for (final Card attacker : attackersLeft) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|| attacker.hasKeyword("CARDNAME can't be blocked unless " +
"all creatures defending player controls block it.")) {
continue;
}
@@ -225,7 +226,7 @@ public class AiBlockController {
// check for triggers when unblocked
for (Trigger trigger : attacker.getTriggers()) {
final HashMap<String, String> trigParams = trigger.getMapParams();
final Map<String, String> trigParams = trigger.getMapParams();
TriggerType mode = trigger.getMode();
if (!trigger.requirementsCheck(attacker.getGame())) {
@@ -234,14 +235,14 @@ public class AiBlockController {
if (mode == TriggerType.DamageDone) {
if ((!trigParams.containsKey("ValidSource")
|| TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), attacker))
|| CardTraitBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), attacker))
&& attacker.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget")
|| TriggerReplacementBase.matchesValid(combat.getDefenderByAttacker(attacker), trigParams.get("ValidTarget").split(","), attacker))) {
|| CardTraitBase.matchesValid(combat.getDefenderByAttacker(attacker), trigParams.get("ValidTarget").split(","), attacker))) {
value += 50;
}
} else if (mode == TriggerType.AttackerUnblocked) {
if (TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), attacker)) {
if (CardTraitBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), attacker)) {
value += 50;
}
}
@@ -402,7 +403,9 @@ public class AiBlockController {
for (final Card attacker : attackersLeft) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|| attacker.hasKeyword("CARDNAME can't be blocked unless " +
"all creatures defending player controls block it.")) {
continue;
}
@@ -437,7 +440,8 @@ public class AiBlockController {
Card attacker = attackers.get(0);
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
attackers.remove(0);
makeChumpBlocks(combat, attackers);
return;
@@ -484,11 +488,12 @@ public class AiBlockController {
for (final Card attacker : currentAttackers) {
if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")) {
if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
&& !attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
continue;
}
List<Card> possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
if (!CombatUtil.canAttackerBeBlockedWithAmount(attacker, possibleBlockers.size())) {
if (!CombatUtil.canAttackerBeBlockedWithAmount(attacker, possibleBlockers.size(), combat)) {
continue;
}
List<Card> usedBlockers = new ArrayList<Card>();
@@ -496,12 +501,12 @@ public class AiBlockController {
if (CombatUtil.canBlock(attacker, blocker, combat)) {
combat.addBlocker(attacker, blocker);
usedBlockers.add(blocker);
if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size())) {
if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size(), combat)) {
break;
}
}
}
if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size())) {
if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size(), combat)) {
attackersLeft.remove(attacker);
} else {
for (Card blocker : usedBlockers) {
@@ -526,7 +531,8 @@ public class AiBlockController {
for (final Card attacker : tramplingAttackers) {
if ((attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") && !combat.isBlocked(attacker))
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
continue;
}
@@ -756,7 +762,7 @@ public class AiBlockController {
public static List<Card> orderBlockers(Card attacker, List<Card> blockers) {
// ordering of blockers, sort by evaluate, then try to kill the best
int damage = attacker.getNetCombatDamage();
CardLists.sortByEvaluateCreature(blockers);
ComputerUtilCard.sortByEvaluateCreature(blockers);
final List<Card> first = new ArrayList<Card>();
final List<Card> last = new ArrayList<Card>();
for (Card blocker : blockers) {
@@ -780,7 +786,7 @@ public class AiBlockController {
// This shouldn't really take trample into account, but otherwise should be pretty similar to orderBlockers
// ordering of blockers, sort by evaluate, then try to kill the best
int damage = blocker.getNetCombatDamage();
CardLists.sortByEvaluateCreature(attackers);
ComputerUtilCard.sortByEvaluateCreature(attackers);
final List<Card> first = new ArrayList<Card>();
final List<Card> last = new ArrayList<Card>();
for (Card attacker : attackers) {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,591 @@
package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.card.CardType;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AiCostDecision extends CostDecisionMakerBase implements ICostVisitor<PaymentDecision> {
private final SpellAbility ability;
private final Card source;
public AiCostDecision(Player ai0, SpellAbility sa) {
super(ai0);
ability = sa;
source = ability.getHostCard();
}
@Override
public PaymentDecision visit(CostAddMana cost) {
Integer c = cost.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostChooseCreatureType cost) {
String choice = player.getController().chooseSomeType("Creature", ability, new ArrayList<String>(CardType.getCreatureTypes()), new ArrayList<String>());
return PaymentDecision.type(choice);
}
@Override
public PaymentDecision visit(CostDiscard cost) {
final String type = cost.getType();
final List<Card> hand = player.getCardsIn(ZoneType.Hand);
if (type.equals("LastDrawn")) {
if (!hand.contains(player.getLastDrawnCard())) {
return null;
}
return PaymentDecision.card(player.getLastDrawnCard());
}
else if (cost.payCostFromSource()) {
if (!hand.contains(source)) {
return null;
}
return PaymentDecision.card(source);
}
else if (type.equals("Hand")) {
return PaymentDecision.card(hand);
}
if (type.contains("WithSameName")) {
return null;
}
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
if (sVar.equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
if (type.equals("Random")) {
return PaymentDecision.card(CardLists.getRandomSubList(hand, c));
}
else {
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability));
}
}
@Override
public PaymentDecision visit(CostDamage cost) {
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
// Generalize cost
if (sVar.equals("XChoice")) {
return null; // cannot pay
} else {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
}
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostDraw cost) {
Integer c = cost.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostExile cost) {
if (cost.payCostFromSource()) {
return PaymentDecision.card(source);
}
if (cost.getType().equals("All")) {
return PaymentDecision.card(player.getCardsIn(cost.getFrom()));
}
else if (cost.getType().contains("FromTopGrave")) {
return null;
}
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
// Generalize cost
if (sVar.equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
if (cost.getFrom().equals(ZoneType.Library)) {
return PaymentDecision.card(player.getCardsIn(ZoneType.Library, c));
}
else if (cost.sameZone) {
// TODO Determine exile from same zone for AI
return null;
}
else {
List<Card> chosen = ComputerUtil.chooseExileFrom(player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c);
return null == chosen ? null : PaymentDecision.card(chosen);
}
}
@Override
public PaymentDecision visit(CostExiledMoveToGrave cost) {
Integer c = cost.convertAmount();
List<Card> chosen = new ArrayList<Card>();
if (c == null) {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
List<Card> typeList = player.getGame().getCardsIn(ZoneType.Exile);
typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), player, source);
if (typeList.size() < c) {
return null;
}
CardLists.sortByPowerAsc(typeList);
Collections.reverse(typeList);
for (int i = 0; i < c; i++) {
chosen.add(typeList.get(i));
}
return chosen.isEmpty() ? null : PaymentDecision.card(chosen);
}
@Override
public PaymentDecision visit(CostFlipCoin cost) {
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
// Generalize cost
if (sVar.equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostGainControl cost) {
if (cost.payCostFromSource())
return PaymentDecision.card(source);
Integer c = cost.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
final List<Card> typeList = CardLists.getValidCards(player.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source);
if (typeList.size() < c) {
return null;
}
CardLists.sortByPowerAsc(typeList);
final List<Card> res = new ArrayList<Card>();
for (int i = 0; i < c; i++) {
res.add(typeList.get(i));
}
return res.isEmpty() ? null : PaymentDecision.card(res);
}
@Override
public PaymentDecision visit(CostGainLife cost) {
final List<Player> oppsThatCanGainLife = new ArrayList<Player>();
for (final Player opp : cost.getPotentialTargets(player, source)) {
if (opp.canGainLife()) {
oppsThatCanGainLife.add(opp);
}
}
if (oppsThatCanGainLife.size() == 0) {
return null;
}
return PaymentDecision.players(oppsThatCanGainLife);
}
@Override
public PaymentDecision visit(CostMill cost) {
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
// Generalize cost
if (sVar.equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
List<Card> topLib = player.getCardsIn(ZoneType.Library, c);
return topLib.size() < c ? null : PaymentDecision.card(topLib);
}
@Override
public PaymentDecision visit(CostPartMana cost) {
return PaymentDecision.number(0);
}
@Override
public PaymentDecision visit(CostPayLife cost) {
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
// Generalize cost
if (sVar.equals("XChoice")) {
return null;
} else {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
}
if (!player.canPayLife(c)) {
return null;
}
// activator.payLife(c, null);
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostPutCardToLib cost) {
Integer c = cost.convertAmount();
final Game game = player.getGame();
List<Card> chosen = new ArrayList<Card>();
List<Card> list;
if (cost.isSameZone()) {
list = new ArrayList<Card>(game.getCardsIn(cost.getFrom()));
} else {
list = new ArrayList<Card>(player.getCardsIn(cost.getFrom()));
}
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
// Generalize cost
if (sVar.equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
list = CardLists.getValidCards(list, cost.getType().split(";"), player, source);
if (cost.isSameZone()) {
// Jotun Grunt
// TODO: improve AI
final List<Player> players = game.getPlayers();
for (Player p : players) {
List<Card> enoughType = CardLists.filter(list, CardPredicates.isOwner(p));
if (enoughType.size() >= c) {
chosen.addAll(enoughType);
break;
}
}
chosen = chosen.subList(0, c);
} else {
chosen = ComputerUtil.choosePutToLibraryFrom(player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c);
}
return chosen.isEmpty() ? null : PaymentDecision.card(chosen);
}
@Override
public PaymentDecision visit(CostPutCounter cost) {
if (cost.payCostFromSource()) {
return PaymentDecision.card(source);
}
final List<Card> typeList = CardLists.getValidCards(player.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source);
Card card = null;
if (cost.getType().equals("Creature.YouCtrl")) {
card = ComputerUtilCard.getWorstCreatureAI(typeList);
} else {
card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false);
}
return PaymentDecision.card(card);
}
@Override
public PaymentDecision visit(CostTap cost) {
return PaymentDecision.number(0);
}
@Override
public PaymentDecision visit(CostTapType cost) {
final String amount = cost.getAmount();
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) {
List<Card> typeList =
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), ability.getActivatingPlayer(), ability.getHostCard());
typeList = CardLists.filter(typeList, Presets.UNTAPPED);
c = typeList.size();
source.setSVar("ChosenX", "Number$" + Integer.toString(c));
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
if (cost.getType().contains("sharesCreatureTypeWith") || cost.getType().contains("withTotalPowerGE")) {
return null;
}
List<Card> totap = ComputerUtil.chooseTapType(player, cost.getType(), source, !cost.canTapSource, c);
if (totap == null) {
System.out.println("Couldn't find a valid card to tap for: " + source.getName());
return null;
}
return PaymentDecision.card(totap);
}
@Override
public PaymentDecision visit(CostSacrifice cost) {
if (cost.payCostFromSource()) {
return PaymentDecision.card(source);
}
if (cost.getAmount().equals("All")) {
/*List<Card> typeList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield));
typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), activator, source);
if (activator.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) {
typeList = CardLists.getNotType(typeList, "Creature");
}*/
// Does the AI want to use Sacrifice All?
return null;
}
Integer c = cost.convertAmount();
if (c == null) {
if (ability.getSVar(cost.getAmount()).equals("XChoice")) {
return null;
}
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
List<Card> list = ComputerUtil.chooseSacrificeType(player, cost.getType(), source, ability.getTargetCard(), c);
return PaymentDecision.card(list);
}
@Override
public PaymentDecision visit(CostReturn cost) {
if (cost.payCostFromSource())
return PaymentDecision.card(source);
Integer c = cost.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
List<Card> res = ComputerUtil.chooseReturnType(player, cost.getType(), source, ability.getTargetCard(), c);
return res.isEmpty() ? null : PaymentDecision.card(res);
}
@Override
public PaymentDecision visit(CostReveal cost) {
final String type = cost.getType();
List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand));
if (cost.payCostFromSource()) {
if (!hand.contains(source)) {
return null;
}
return PaymentDecision.card(source);
}
if (cost.getType().equals("Hand"))
return PaymentDecision.card(player.getCardsIn(ZoneType.Hand));
if (cost.getType().equals("SameColor")) {
return null;
}
hand = CardLists.getValidCards(hand, type.split(";"), player, source);
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(cost.getAmount());
if (sVar.equals("XChoice")) {
c = hand.size();
} else {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
}
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability));
}
@Override
public PaymentDecision visit(CostRemoveAnyCounter cost) {
final String amount = cost.getAmount();
final int c = AbilityUtils.calculateAmount(source, amount, ability);
final String type = cost.getType();
List<Card> typeList = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), player, source);
List<Card> hperms = CardLists.filter(typeList, new Predicate<Card>() {
@Override
public boolean apply(final Card crd) {
for (final CounterType c1 : CounterType.values()) {
if (crd.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, crd)) {
return true;
}
}
return false;
}
});
if(hperms.isEmpty())
return null;
PaymentDecision result = PaymentDecision.card(hperms);
Card valid = hperms.get(0);
for (CounterType c1 : valid.getCounters().keySet()) {
if (valid.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, valid)) {
result.ct = c1;
break;
}
}
// Only find cards with enough negative counters
// TODO: add ai for Chisei, Heart of Oceans
return result;
}
@Override
public PaymentDecision visit(CostRemoveCounter cost) {
final String amount = cost.getAmount();
Integer c = cost.convertAmount();
final String type = cost.getType();
if (c == null) {
final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) {
return null;
}
if (amount.equals("All")) {
c = source.getCounters(cost.counter);
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
if (!cost.payCostFromSource()) {
List<Card> typeList;
if (type.equals("OriginalHost")) {
typeList = Lists.newArrayList(ability.getOriginalHost());
} else {
typeList = CardLists.getValidCards(player.getCardsIn(cost.zone), type.split(";"), player, source);
}
for (Card card : typeList) {
if (card.getCounters(cost.counter) >= c) {
return PaymentDecision.card(card, c);
}
}
return null;
}
if (c > source.getCounters(cost.counter)) {
System.out.println("Not enough " + cost.counter + " on " + source.getName());
return null;
}
return PaymentDecision.card(source, c);
}
@Override
public PaymentDecision visit(CostUntapType cost) {
final String amount = cost.getAmount();
Integer c = cost.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) {
List<Card> typeList = player.getGame().getCardsIn(ZoneType.Battlefield);
typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), player, ability.getHostCard());
if (!cost.canUntapSource) {
typeList.remove(source);
}
typeList = CardLists.filter(typeList, Presets.TAPPED);
c = typeList.size();
source.setSVar("ChosenX", "Number$" + Integer.toString(c));
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
List<Card> list = ComputerUtil.chooseUntapType(player, cost.getType(), source, cost.canUntapSource, c);
if (list == null) {
System.out.println("Couldn't find a valid card to untap for: " + source.getName());
return null;
}
return PaymentDecision.card(list);
}
@Override
public PaymentDecision visit(CostUntap cost) {
return PaymentDecision.number(0);
}
@Override
public PaymentDecision visit(CostUnattach cost) {
Card cardToUnattach = cost.findCardToUnattach(source, (Player) player, ability);
if (cardToUnattach == null) {
// We really shouldn't be able to get here if there's nothing to unattach
return null;
}
return PaymentDecision.card(cardToUnattach);
}
@Override
public boolean paysRightAfterDecision() {
return false;
}
}

View File

@@ -0,0 +1,20 @@
package forge.ai;
public enum AiPlayDecision {
WillPlay,
CantPlaySa,
CantPlayAi,
CantAfford,
CantAffordX,
WaitForMain2,
AnotherTime,
MissingNeededCards,
NeedsToPlayCriteriaNotMet,
TargetingFailed,
CostNotAcceptable,
WouldDestroyLegend,
WouldDestroyOtherPlaneswalker,
WouldBecomeZeroToughnessCreature,
WouldDestroyWorldEnchantment,
BadEtbEffects;
}

View File

@@ -15,20 +15,19 @@
* 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.game.ai;
package forge.ai;
import forge.game.player.LobbyPlayer;
import forge.game.player.LobbyPlayerAi;
import forge.util.Aggregates;
import forge.util.FileUtil;
import org.apache.commons.lang3.ArrayUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import org.apache.commons.lang.ArrayUtils;
/**
* Holds default AI personality profile values in an enum.

View File

@@ -15,7 +15,7 @@
* 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.game.ai;
package forge.ai;
/**
* AI personality profile settings identifiers, and their default values.
@@ -28,7 +28,8 @@ public enum AiProps { /** */
DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE ("3"), /** */
DEFAULT_PLANAR_DIE_ROLL_CHANCE ("50"), /** */
MULLIGAN_THRESHOLD ("5"), /** */
PLANAR_DIE_ROLL_HESITATION_CHANCE ("10"); /** */
PLANAR_DIE_ROLL_HESITATION_CHANCE ("10"),
CHEAT_WITH_MANA_ON_SHUFFLE ("FALSE"); /** */
private final String strDefaultVal;

View File

@@ -15,60 +15,44 @@
* 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.game.ai;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.Constant;
import forge.CounterType;
import forge.CardPredicates.Presets;
import forge.CardUtil;
import forge.ITargetable;
import forge.card.CardType;
import forge.card.CardType.Constant;
import forge.card.MagicColor;
import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType;
import forge.card.ability.effects.CharmEffect;
import forge.card.cost.Cost;
import forge.card.cost.CostDiscard;
import forge.card.cost.CostPart;
import forge.card.cost.CostPayment;
import forge.card.cost.CostSacrifice;
import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.TargetRestrictions;
import forge.card.staticability.StaticAbility;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType;
import forge.error.BugReporter;
import forge.game.Game;
import forge.game.GameObject;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.effects.CharmEffect;
import forge.game.card.*;
import forge.game.card.CardPredicates.Presets;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.cost.*;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerControllerAi;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.util.Aggregates;
import forge.util.MyRandom;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* <p>
@@ -86,24 +70,28 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
*/
public static boolean handlePlayingSpellAbility(final Player ai, final SpellAbility sa, final Game game) {
game.getStack().freezeStack();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setSourceCard(game.getAction().moveToStack(source));
sa.setHostCard(game.getAction().moveToStack(source));
}
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
CharmEffect.makeChoices(sa);
}
final Cost cost = sa.getPayCosts();
if (sa.hasParam("Bestow")) {
sa.getHostCard().animateBestow();
}
final Cost cost = sa.getPayCosts();
// TODO: update mana color conversion for Daxos of Meletis
if (cost == null) {
if (ComputerUtilMana.payManaCost(ai, sa)) {
game.getStack().addAndUnfreeze(sa);
@@ -111,18 +99,16 @@ public class ComputerUtil {
}
} else {
final CostPayment pay = new CostPayment(cost, sa);
if (pay.payComputerCosts(ai, game)) {
if (pay.payComputerCosts(new AiCostDecision(ai, sa))) {
game.getStack().addAndUnfreeze(sa);
if (sa.getSplicedCards() != null && !sa.getSplicedCards().isEmpty()) {
GuiChoose.oneOrNone("Computer reveals spliced cards:", sa.getSplicedCards());
game.getAction().reveal(sa.getSplicedCards(), ai, true, "Computer reveals spliced cards from ");
}
return true;
// TODO: solve problems with TapsForMana triggers by adding
// sources tapped here if possible (ArsenalNut)
}
}
//Should not arrive here
System.out.println("AI failed to play " + sa.getSourceCard());
System.out.println("AI failed to play " + sa.getHostCard());
return false;
}
@@ -147,7 +133,7 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a int.
*/
public static int counterSpellRestriction(final Player ai, final SpellAbility sa) {
@@ -156,7 +142,7 @@ public class ComputerUtil {
int restrict = 0;
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions();
@@ -204,7 +190,8 @@ public class ComputerUtil {
// And lastly give some bonus points to least restrictive TargetType
// (Spell,Ability,Triggered)
final String tgtType = tgt.getTargetSpellAbilityType();
final String tgtType = sa.getParam("TargetType");
if (tgtType != null)
restrict -= (5 * tgtType.split(",").length);
return restrict;
@@ -217,16 +204,16 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
*/
public static final void playStack(final SpellAbility sa, final Player ai, final Game game) {
sa.setActivatingPlayer(ai);
if (!ComputerUtilCost.canPayCost(sa, ai))
return;
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setSourceCard(game.getAction().moveToStack(source));
sa.setHostCard(game.getAction().moveToStack(source));
}
final Cost cost = sa.getPayCosts();
if (cost == null) {
@@ -234,7 +221,7 @@ public class ComputerUtil {
game.getStack().add(sa);
} else {
final CostPayment pay = new CostPayment(cost, sa);
if (pay.payComputerCosts(ai, game)) {
if (pay.payComputerCosts(new AiCostDecision(ai, sa))) {
game.getStack().add(sa);
}
}
@@ -246,14 +233,14 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
*/
public static final void playSpellAbilityForFree(final Player ai, final SpellAbility sa) {
sa.setActivatingPlayer(ai);
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setSourceCard(ai.getGame().getAction().moveToStack(source));
sa.setHostCard(ai.getGame().getAction().moveToStack(source));
}
ai.getGame().getStack().add(sa);
@@ -265,7 +252,7 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
*/
public static final void playSpellAbilityWithoutPayingManaCost(final Player ai, final SpellAbility sa, final Game game) {
final SpellAbility newSA = sa.copyWithNoManaCost();
@@ -275,13 +262,13 @@ public class ComputerUtil {
return;
}
final Card source = newSA.getSourceCard();
final Card source = newSA.getHostCard();
if (newSA.isSpell() && !source.isCopiedSpell()) {
newSA.setSourceCard(game.getAction().moveToStack(source));
newSA.setHostCard(game.getAction().moveToStack(source));
}
final CostPayment pay = new CostPayment(newSA.getPayCosts(), newSA);
pay.payComputerCosts(ai, game);
pay.payComputerCosts(new AiCostDecision(ai, sa));
game.getStack().add(newSA);
}
@@ -292,15 +279,15 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
*/
public static final void playNoStack(final Player ai, final SpellAbility sa, final Game game) {
sa.setActivatingPlayer(ai);
// TODO: We should really restrict what doesn't use the Stack
if (ComputerUtilCost.canPayCost(sa, ai)) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setSourceCard(game.getAction().moveToStack(source));
sa.setHostCard(game.getAction().moveToStack(source));
}
final Cost cost = sa.getPayCosts();
@@ -308,7 +295,7 @@ public class ComputerUtil {
ComputerUtilMana.payManaCost(ai, sa);
} else {
final CostPayment pay = new CostPayment(cost, sa);
pay.payComputerCosts(ai, game);
pay.payComputerCosts(new AiCostDecision(ai, sa));
}
AbilityUtils.resolve(sa);
@@ -324,12 +311,12 @@ public class ComputerUtil {
* </p>
*
* @param activate
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param pref
* a {@link java.lang.String} object.
* @param typeList
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getCardPreference(final Player ai, final Card activate, final String pref, final List<Card> typeList) {
@@ -414,9 +401,9 @@ public class ComputerUtil {
* @param type
* a {@link java.lang.String} object.
* @param source
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param target
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param amount
* a int.
* @return a {@link forge.CardList} object.
@@ -463,9 +450,9 @@ public class ComputerUtil {
* @param type
* a {@link java.lang.String} object.
* @param activate
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param target
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param amount
* a int.
* @return a {@link forge.CardList} object.
@@ -510,9 +497,9 @@ public class ComputerUtil {
* @param type
* a {@link java.lang.String} object.
* @param activate
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param target
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param amount
* a int.
* @return a {@link forge.CardList} object.
@@ -553,7 +540,7 @@ public class ComputerUtil {
* @param type
* a {@link java.lang.String} object.
* @param activate
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param tap
* a boolean.
* @param amount
@@ -593,7 +580,7 @@ public class ComputerUtil {
* @param type
* a {@link java.lang.String} object.
* @param activate
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param untap
* a boolean.
* @param amount
@@ -633,9 +620,9 @@ public class ComputerUtil {
* @param type
* a {@link java.lang.String} object.
* @param activate
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param target
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param amount
* a int.
* @return a {@link forge.CardList} object.
@@ -763,7 +750,7 @@ public class ComputerUtil {
* @param ai
*
* @param card
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean canRegenerate(Player ai, final Card card) {
@@ -807,15 +794,15 @@ public class ComputerUtil {
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), controller, sa.getSourceCard()).contains(card)) {
if (CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), controller, sa.getHostCard()).contains(card)) {
return true;
}
} else if (AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa).contains(card)) {
} else if (AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).contains(card)) {
return true;
}
} catch (final Exception ex) {
BugReporter.reportException(ex, "There is an error in the card code for %s:%n", c.getName(), ex.getMessage());
throw new RuntimeException(String.format("There is an error in the card code for %s:%n", c.getName(), ex.getMessage()), ex);
}
}
}
@@ -828,7 +815,7 @@ public class ComputerUtil {
* </p>
*
* @param card
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int possibleDamagePrevention(final Card card) {
@@ -850,20 +837,19 @@ public class ComputerUtil {
if (sa.getApi() == ApiType.PreventDamage && sa.canPlay()
&& ComputerUtilCost.canPayCost(sa, controller)) {
if (AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa).contains(card)) {
prevented += AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("Amount"), sa);
if (AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).contains(card)) {
prevented += AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa);
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), controller, sa.getSourceCard()).contains(card)) {
prevented += AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("Amount"), sa);
if (CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), controller, sa.getHostCard()).contains(card)) {
prevented += AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa);
}
}
}
} catch (final Exception ex) {
BugReporter.reportException(ex, "There is an error in the card code for %s:%n", c.getName(),
ex.getMessage());
throw new RuntimeException(String.format("There is an error in the card code for %s:%n", c.getName(), ex.getMessage()), ex);
}
}
}
@@ -880,7 +866,7 @@ public class ComputerUtil {
* @return a boolean.
*/
public static boolean castPermanentInMain1(final Player ai, final SpellAbility sa) {
final Card card = sa.getSourceCard();
final Card card = sa.getHostCard();
if ("True".equals(card.getSVar("NonStackingEffect")) && card.getController().isCardInPlay(card.getName())) {
return false;
}
@@ -1044,19 +1030,19 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean (returns true if it's better to wait until blockers are declared).
*/
public static boolean waitForBlocking(final SpellAbility sa) {
final Game game = sa.getActivatingPlayer().getGame();
final PhaseHandler ph = game.getPhaseHandler();
return (sa.getSourceCard().isCreature()
return (sa.getHostCard().isCreature()
&& sa.getPayCosts().hasTapCost()
&& (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| !ph.getNextTurn().equals(sa.getActivatingPlayer()))
&& !sa.getSourceCard().hasKeyword("At the beginning of the end step, exile CARDNAME.")
&& !sa.getSourceCard().hasKeyword("At the beginning of the end step, sacrifice CARDNAME."));
&& !sa.getHostCard().hasKeyword("At the beginning of the end step, exile CARDNAME.")
&& !sa.getHostCard().hasKeyword("At the beginning of the end step, sacrifice CARDNAME."));
}
/**
@@ -1065,11 +1051,11 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean (returns true if it's better to wait until blockers are declared).
*/
public static boolean castSpellInMain1(final Player ai, final SpellAbility sa) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final SpellAbility sub = sa.getSubAbility();
// Cipher spells
@@ -1086,7 +1072,7 @@ public class ComputerUtil {
}
}
final List<Card> buffed = ai.getCardsIn(ZoneType.Battlefield);
boolean checkThreshold = sa.isSpell() && !ai.hasThreshold() && !sa.getSourceCard().isInZone(ZoneType.Graveyard);
boolean checkThreshold = sa.isSpell() && !ai.hasThreshold() && !sa.getHostCard().isInZone(ZoneType.Graveyard);
for (Card buffedCard : buffed) {
if (buffedCard.hasSVar("BuffedBy")) {
final String buffedby = buffedCard.getSVar("BuffedBy");
@@ -1131,17 +1117,23 @@ public class ComputerUtil {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean (returns true if the AI should stop using the ability).
*/
public static boolean preventRunAwayActivations(final SpellAbility sa) {
final int activations = sa.getRestrictions().getNumberTurnActivations();
int activations = sa.getRestrictions().getNumberTurnActivations();
if (sa.isTemporary()) {
final Random r = MyRandom.getRandom();
return r.nextFloat() >= .95; // Abilities created by static abilities have no memory
}
if (activations < 10) { //10 activations per turn should still be acceptable
return false;
}
final Random r = MyRandom.getRandom();
return r.nextFloat() <= Math.pow(.95, activations);
final Random r = MyRandom.getRandom();
return r.nextFloat() >= Math.pow(.95, activations);
}
/**
@@ -1151,7 +1143,7 @@ public class ComputerUtil {
*/
public static boolean activateForCost(SpellAbility sa, final Player ai) {
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (abCost == null) {
return false;
}
@@ -1163,6 +1155,12 @@ public class ComputerUtil {
}
}
}
} else if (sa.hasParam("Planeswalker") && ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostPutCounter) {
return true;
}
}
}
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostSacrifice) {
@@ -1202,7 +1200,7 @@ public class ComputerUtil {
for (final Card c : all) {
if (c.isEquipment()) {
for (StaticAbility stAb : c.getStaticAbilities()) {
HashMap<String, String> params = stAb.getMapParams();
Map<String, String> params = stAb.getMapParams();
if ("Continuous".equals(params.get("Mode")) && params.containsKey("AddKeyword")
&& params.get("AddKeyword").contains("Haste") && c.getEquippingCard() == null) {
return true;
@@ -1241,7 +1239,7 @@ public class ComputerUtil {
continue;
}
final String numDam = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getSourceCard(), numDam, sa);
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), numDam, sa);
if (dmg <= damage) {
continue;
}
@@ -1272,9 +1270,9 @@ public class ComputerUtil {
* @return a {@link java.util.ArrayList} object.
* @since 1.0.15
*/
public static List<ITargetable> predictThreatenedObjects(final Player aiPlayer, final SpellAbility sa) {
public static List<GameObject> predictThreatenedObjects(final Player aiPlayer, final SpellAbility sa) {
final Game game = aiPlayer.getGame();
final List<ITargetable> objects = new ArrayList<ITargetable>();
final List<GameObject> objects = new ArrayList<GameObject>();
if (game.getStack().isEmpty()) {
return objects;
}
@@ -1294,21 +1292,21 @@ public class ComputerUtil {
* @param saviourAf
* a AbilityFactory object
* @param topStack
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a {@link java.util.ArrayList} object.
* @since 1.0.15
*/
private static Iterable<? extends ITargetable> predictThreatenedObjects(final Player aiPlayer, final SpellAbility saviour,
private static Iterable<? extends GameObject> predictThreatenedObjects(final Player aiPlayer, final SpellAbility saviour,
final SpellAbility topStack) {
Iterable<? extends ITargetable> objects = new ArrayList<ITargetable>();
final List<ITargetable> threatened = new ArrayList<ITargetable>();
Iterable<? extends GameObject> objects = new ArrayList<GameObject>();
final List<GameObject> threatened = new ArrayList<GameObject>();
ApiType saviourApi = saviour == null ? null : saviour.getApi();
if (topStack == null) {
return objects;
}
final Card source = topStack.getSourceCard();
final Card source = topStack.getHostCard();
final ApiType threatApi = topStack.getApi();
// Can only Predict things from AFs
@@ -1334,7 +1332,7 @@ public class ComputerUtil {
// Lethal Damage => prevent damage/regeneration/bounce/shroud
if (threatApi == ApiType.DealDamage || threatApi == ApiType.DamageAll) {
// If PredictDamage is >= Lethal Damage
final int dmg = AbilityUtils.calculateAmount(topStack.getSourceCard(),
final int dmg = AbilityUtils.calculateAmount(topStack.getHostCard(),
topStack.getParam("NumDmg"), topStack);
for (final Object o : objects) {
if (o instanceof Card) {
@@ -1346,7 +1344,7 @@ public class ComputerUtil {
}
// already regenerated
if (c.getShield() > 0) {
if (!c.getShield().isEmpty()) {
continue;
}
@@ -1398,7 +1396,7 @@ public class ComputerUtil {
}
// already regenerated
if (c.getShield() > 0) {
if (!c.getShield().isEmpty()) {
continue;
}
@@ -1453,7 +1451,7 @@ public class ComputerUtil {
}
public static boolean playImmediately(Player ai, SpellAbility sa) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Zone zone = source.getZone();
if (zone.getZoneType() == ZoneType.Battlefield) {
@@ -1615,6 +1613,22 @@ public class ComputerUtil {
// based on whether it needs a creature or land,
// otherwise, lib search for most common type left
// then, reveal chosenType to Human
if (game.getPhaseHandler().is(PhaseType.UNTAP) && logic == null) { // Storage Matrix
double amount = 0;
for (String type : Constant.CARD_TYPES) {
if (!invalidTypes.contains(type)) {
List<Card> list = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isType(type), Presets.TAPPED);
double i = type.equals("Creature") ? list.size() * 1.5 : list.size();
if (i > amount) {
amount = i;
chosen = type;
}
}
}
}
if (StringUtils.isEmpty(chosen)) {
chosen = "Creature";
}
} else if (kindOfType.equals("Creature")) {
Player opp = ai.getOpponent();
if (logic != null ) {
@@ -1646,7 +1660,7 @@ public class ComputerUtil {
if (logic.equals("MostNeededType")) {
// Choose a type that is in the deck, but not in hand or on the battlefield
final ArrayList<String> basics = new ArrayList<String>();
basics.addAll(Constant.CardTypes.BASIC_TYPES);
basics.addAll(CardType.Constant.BASIC_TYPES);
List<Card> presentCards = ai.getCardsIn(ZoneType.Battlefield);
presentCards.addAll(ai.getCardsIn(ZoneType.Hand));
List<Card> possibleCards = ai.getAllCards();
@@ -1696,8 +1710,6 @@ public class ComputerUtil {
chosen = "Island";
}
GuiChoose.one("Computer picked: ", new String[]{chosen});
return chosen;
}
@@ -1725,7 +1737,7 @@ public class ComputerUtil {
theTriggers.addAll(card.getTriggers());
}
for (Trigger trigger : theTriggers) {
HashMap<String, String> trigParams = trigger.getMapParams();
Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
@@ -1782,7 +1794,6 @@ public class ComputerUtil {
return damage;
}
// although this should be in AI's code
public static boolean isNegativeCounter(CounterType type, Card c) {
return type == CounterType.AGE || type == CounterType.BLAZE || type == CounterType.BRIBERY || type == CounterType.DOOM
|| type == CounterType.ICE || type == CounterType.M1M1 || type == CounterType.M0M2 || type == CounterType.M0M1

View File

@@ -1,40 +1,31 @@
package forge.game.ai;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
package forge.ai;
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 forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.CardUtil;
import forge.Constant;
import forge.card.CardType;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.game.Game;
import forge.game.card.*;
import forge.game.combat.Combat;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.util.Aggregates;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
import java.util.Map.Entry;
/**
* TODO: Write javadoc for this type.
*
@@ -49,10 +40,10 @@ public class ComputerUtilCard {
* @param list
* a {@link forge.CardList} object.
* @param spell
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param targeted
* a boolean.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getMostExpensivePermanentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) {
List<Card> all = list;
@@ -68,6 +59,20 @@ public class ComputerUtilCard {
return ComputerUtilCard.getMostExpensivePermanentAI(all);
}
/**
* <p>
* Sorts a List<Card> by "best" using the EvaluateCreature function.
* the best creatures will be first in the list.
* </p>
*
* @param list
* a {@link forge.CardList} object.
*/
public static void sortByEvaluateCreature(final List<Card> list) {
Collections.sort(list, ComputerUtilCard.EvaluateCreatureComparator);
} // sortByEvaluateCreature()
// The AI doesn't really pick the best artifact, just the most expensive.
/**
* <p>
@@ -76,7 +81,7 @@ public class ComputerUtilCard {
*
* @param list
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getBestArtifactAI(final List<Card> list) {
List<Card> all = CardLists.filter(list, CardPredicates.Presets.ARTIFACTS);
@@ -96,10 +101,10 @@ public class ComputerUtilCard {
* @param list
* a {@link forge.CardList} object.
* @param spell
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param targeted
* a boolean.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getBestEnchantmentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) {
List<Card> all = CardLists.filter(list, CardPredicates.Presets.ENCHANTMENTS);
@@ -124,7 +129,7 @@ public class ComputerUtilCard {
*
* @param list
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getBestLandAI(final Collection<Card> list) {
final List<Card> land = CardLists.filter(list, CardPredicates.Presets.LANDS);
@@ -145,7 +150,7 @@ public class ComputerUtilCard {
int iminBL = 20000; // hopefully no one will ever have more than 20000
// lands of one type....
int n = 0;
for (String name : Constant.Color.BASIC_LANDS) {
for (String name : MagicColor.Constant.BASIC_LANDS) {
n = CardLists.getType(land, name).size();
if ((n < iminBL) && (n > 0)) {
// if two or more are tied, only the
@@ -177,10 +182,10 @@ public class ComputerUtilCard {
* @param list
* a {@link forge.CardList} object.
* @param spell
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param targeted
* a boolean.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getCheapestPermanentAI(Collection<Card> all, final SpellAbility spell, final boolean targeted) {
if (targeted) {
@@ -216,7 +221,7 @@ public class ComputerUtilCard {
*
* @param list
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getBestAI(final Collection<Card> list) {
// Get Best will filter by appropriate getBest list if ALL of the list
@@ -250,9 +255,9 @@ public class ComputerUtilCard {
*
* @param list
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getWorstCreatureAI(final List<Card> list) {
public static Card getWorstCreatureAI(final Collection<Card> list) {
return Aggregates.itemWithMin(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.fnEvaluateCreature);
}
@@ -264,7 +269,7 @@ public class ComputerUtilCard {
*
* @param list
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getBestCreatureToBounceAI(final List<Card> list) {
final int tokenBonus = 60;
@@ -290,7 +295,7 @@ public class ComputerUtilCard {
*
* @param list
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getWorstAI(final Collection<Card> list) {
return ComputerUtilCard.getWorstPermanentAI(list, false, false, false, false);
@@ -311,7 +316,7 @@ public class ComputerUtilCard {
* a boolean.
* @param biasCreature
* a boolean.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getWorstPermanentAI(final Collection<Card> list, final boolean biasEnch, final boolean biasLand,
final boolean biasArt, final boolean biasCreature) {
@@ -375,7 +380,7 @@ public class ComputerUtilCard {
* </p>
*
* @param c
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int evaluateCreature(final Card c) {
@@ -503,7 +508,7 @@ public class ComputerUtilCard {
value -= toughness * 5;
}
if (c.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
if (c.hasSVar("DestroyWhenDamaged")) {
value -= (toughness - 1) * 9;
}
@@ -523,8 +528,7 @@ public class ComputerUtilCard {
value -= 50;
} else if (c.hasStartOfKeyword("Cumulative upkeep")) {
value -= 30;
} else if (c.hasStartOfKeyword("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")
|| c.hasStartOfKeyword("Upkeep:")) {
} else if (c.hasStartOfKeyword("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")) {
value -= 20;
} else if (c.hasStartOfKeyword("(Echo unpaid)")) {
value -= 10;
@@ -608,7 +612,7 @@ public class ComputerUtilCard {
* @param ai
* the AI player
* @param card
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean doesCreatureAttackAI(final Player ai, final Card card) {
@@ -745,7 +749,7 @@ public class ComputerUtilCard {
if ( (colors & c) != 0 )
return MagicColor.toLongString(c);
}
return Constant.Color.WHITE; // no difference, there was no prominent color
return MagicColor.Constant.WHITE; // no difference, there was no prominent color
}
public static String getMostProminentColor(final List<Card> list, final List<String> restrictedToColors) {
@@ -797,7 +801,7 @@ public class ComputerUtilCard {
*
* @param lands
* a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card getWorstLand(final List<Card> lands) {
Card worstLand = null;
@@ -827,5 +831,67 @@ public class ComputerUtilCard {
return true;
}
};
public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) {
List<String> chosen = new ArrayList<String>();
Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame();
Player opp = ai.getOpponent();
if (sa.hasParam("AILogic")) {
final String logic = sa.getParam("AILogic");
if (logic.equals("MostProminentInHumanDeck")) {
chosen.add(ComputerUtilCard.getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), opp), colorChoices));
} else if (logic.equals("MostProminentInComputerDeck")) {
chosen.add(ComputerUtilCard.getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), ai), colorChoices));
} else if (logic.equals("MostProminentDualInComputerDeck")) {
List<String> prominence = ComputerUtilCard.getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
chosen.add(prominence.get(0));
chosen.add(prominence.get(1));
}
else if (logic.equals("MostProminentInGame")) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getCardsInGame(), colorChoices));
}
else if (logic.equals("MostProminentHumanCreatures")) {
List<Card> list = opp.getCreaturesInPlay();
if (list.isEmpty()) {
list = CardLists.filter(CardLists.filterControlledBy(game.getCardsInGame(), opp), CardPredicates.Presets.CREATURES);
}
chosen.add(ComputerUtilCard.getMostProminentColor(list, colorChoices));
}
else if (logic.equals("MostProminentComputerControls")) {
chosen.add(ComputerUtilCard.getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield), colorChoices));
}
else if (logic.equals("MostProminentHumanControls")) {
chosen.add(ComputerUtilCard.getMostProminentColor(ai.getOpponent().getCardsIn(ZoneType.Battlefield), colorChoices));
}
else if (logic.equals("MostProminentPermanent")) {
final List<Card> list = game.getCardsIn(ZoneType.Battlefield);
chosen.add(ComputerUtilCard.getMostProminentColor(list, colorChoices));
}
else if (logic.equals("MostProminentAttackers") && game.getPhaseHandler().inCombat()) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getCombat().getAttackers(), colorChoices));
}
else if (logic.equals("MostProminentInActivePlayerHand")) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Hand), colorChoices));
}
else if (logic.equals("MostProminentKeywordInComputerDeck")) {
List<Card> list = ai.getAllCards();
int m1 = 0;
String chosenColor = MagicColor.Constant.WHITE;
for (final String c : MagicColor.Constant.ONLY_COLORS) {
final int cmp = CardLists.filter(list, CardPredicates.containsKeyword(c)).size();
if (cmp > m1) {
m1 = cmp;
chosenColor = c;
}
}
chosen.add(chosenColor);
}
}
if (chosen.size() == 0) {
chosen.add(MagicColor.Constant.GREEN);
}
return chosen;
}
}

View File

@@ -15,38 +15,37 @@
* 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.game.ai;
package forge.ai;
import com.google.common.base.Predicate;
import forge.game.CardTraitBase;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityActivated;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.GameEntity;
import forge.card.TriggerReplacementBase;
import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.replacement.ReplacementEffect;
import forge.card.spellability.AbilityActivated;
import forge.card.spellability.SpellAbility;
import forge.card.staticability.StaticAbility;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType;
import forge.game.Game;
import forge.game.GlobalRuleChange;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
/**
* <p>
@@ -64,7 +63,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param player
* a {@link forge.game.player.Player} object.
* @return a int.
@@ -91,7 +90,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param c
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int getAttack(final Card c) {
@@ -112,7 +111,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param attacked
* a {@link forge.game.player.Player} object.
* @param combat
@@ -142,7 +141,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param attacked
* a {@link forge.game.player.Player} object.
* @param combat
@@ -373,7 +372,7 @@ public class ComputerUtilCombat {
public static boolean lifeInSeriousDanger(final Player ai, final Combat combat) {
// life in danger only cares about the player's life. Not about a
// Planeswalkers life
if (ai.cantLose()) {
if (ai.cantLose() || combat == null) {
return false;
}
@@ -412,7 +411,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defenders
* a {@link forge.CardList} object.
* @return a int.
@@ -434,9 +433,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int dealsDamageAsBlocker(final Card attacker, final Card defender) {
@@ -487,7 +486,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defenders
* a {@link forge.CardList} object.
* @return a int.
@@ -511,9 +510,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int shieldDamage(final Card attacker, final Card defender) {
@@ -548,7 +547,7 @@ public class ComputerUtilCombat {
* @param ai
*
* @param combatant
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean combatantWouldBeDestroyed(Player ai, final Card combatant, Combat combat) {
@@ -570,7 +569,7 @@ public class ComputerUtilCombat {
* @param ai
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean attackerWouldBeDestroyed(Player ai, final Card attacker, Combat combat) {
@@ -593,11 +592,11 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param trigger
* a {@link forge.card.trigger.Trigger} object.
* a {@link forge.game.trigger.Trigger} object.
* @param combat
* a {@link forge.game.combat.Combat} object.
* @return a boolean.
@@ -605,7 +604,7 @@ public class ComputerUtilCombat {
public static boolean combatTriggerWillTrigger(final Card attacker, final Card defender, final Trigger trigger,
Combat combat) {
final Game game = attacker.getGame();
final HashMap<String, String> trigParams = trigger.getMapParams();
final Map<String, String> trigParams = trigger.getMapParams();
boolean willTrigger = false;
final Card source = trigger.getHostCard();
if (combat == null) {
@@ -629,8 +628,8 @@ public class ComputerUtilCombat {
return false; // The trigger should have triggered already
}
if (trigParams.containsKey("ValidCard")) {
if (!TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), source)
&& !(combat.isAttacking(source) && TriggerReplacementBase.matchesValid(source,
if (!CardTraitBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), source)
&& !(combat.isAttacking(source) && CardTraitBase.matchesValid(source,
trigParams.get("ValidCard").split(","), source)
&& !trigParams.containsKey("Alone"))) {
return false;
@@ -642,7 +641,7 @@ public class ComputerUtilCombat {
if ((defender == null) && mode == TriggerType.AttackerUnblocked) {
willTrigger = true;
if (trigParams.containsKey("ValidCard")) {
if (!TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), source)) {
if (!CardTraitBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), source)) {
return false;
}
}
@@ -664,7 +663,7 @@ public class ComputerUtilCombat {
return false;
}
}
if (!TriggerReplacementBase.matchesValid(attacker, validBlocked.split(","), source)) {
if (!CardTraitBase.matchesValid(attacker, validBlocked.split(","), source)) {
return false;
}
}
@@ -678,35 +677,35 @@ public class ComputerUtilCombat {
return false;
}
}
if (!TriggerReplacementBase.matchesValid(defender, validBlocker.split(","), source)) {
if (!CardTraitBase.matchesValid(defender, validBlocker.split(","), source)) {
return false;
}
}
} else if (mode == TriggerType.AttackerBlocked) {
willTrigger = true;
if (trigParams.containsKey("ValidBlocker")) {
if (!TriggerReplacementBase.matchesValid(defender, trigParams.get("ValidBlocker").split(","), source)) {
if (!CardTraitBase.matchesValid(defender, trigParams.get("ValidBlocker").split(","), source)) {
return false;
}
}
if (trigParams.containsKey("ValidCard")) {
if (!TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), source)) {
if (!CardTraitBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), source)) {
return false;
}
}
} else if (mode == TriggerType.DamageDone) {
willTrigger = true;
if (trigParams.containsKey("ValidSource")) {
if (TriggerReplacementBase.matchesValid(defender, trigParams.get("ValidSource").split(","), source)
if (CardTraitBase.matchesValid(defender, trigParams.get("ValidSource").split(","), source)
&& defender.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget")
|| TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidTarget").split(","), source))) {
|| CardTraitBase.matchesValid(attacker, trigParams.get("ValidTarget").split(","), source))) {
return true;
}
if (TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), source)
if (CardTraitBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), source)
&& attacker.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget")
|| TriggerReplacementBase.matchesValid(defender, trigParams.get("ValidTarget").split(","), source))) {
|| CardTraitBase.matchesValid(defender, trigParams.get("ValidTarget").split(","), source))) {
return true;
}
}
@@ -724,9 +723,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int predictPowerBonusOfBlocker(final Card attacker, final Card defender, boolean withoutAbilities) {
@@ -736,6 +735,13 @@ public class ComputerUtilCombat {
power -= attacker.getAmountOfKeyword("Flanking");
}
// Serene Master switches power with attacker
if (defender.getName().equals("Serene Master")) {
power += attacker.getNetAttack() - defender.getNetAttack();
} else if (defender.getName().equals("Shape Stealer")) {
power += attacker.getNetAttack() - defender.getNetAttack();
}
// if the attacker has first strike and wither the blocker will deal
// less damage than expected
if ((attacker.hasKeyword("First Strike") || attacker.hasKeyword("Double Strike"))
@@ -753,7 +759,7 @@ public class ComputerUtilCombat {
final List<Card> cardList = game.getCardsIn(ZoneType.Battlefield);
for (final Card card : cardList) {
for (final StaticAbility stAb : card.getStaticAbilities()) {
final HashMap<String, String> params = stAb.getMapParams();
final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) {
continue;
}
@@ -782,7 +788,7 @@ public class ComputerUtilCombat {
}
theTriggers.addAll(attacker.getTriggers());
for (final Trigger trigger : theTriggers) {
final HashMap<String, String> trigParams = trigger.getMapParams();
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, defender, trigger, null)
@@ -842,7 +848,7 @@ public class ComputerUtilCombat {
}
if (ComputerUtilCost.canPayCost(ability, defender.getController())) {
int pBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("NumAtt"), ability);
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
if (pBonus > 0) {
power += pBonus;
}
@@ -853,7 +859,7 @@ public class ComputerUtilCombat {
}
if (ComputerUtilCost.canPayCost(ability, defender.getController())) {
int pBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("CounterNum"), ability);
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
if (pBonus > 0) {
power += pBonus;
}
@@ -871,9 +877,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a int.
*/
public static int predictToughnessBonusOfBlocker(final Card attacker, final Card defender, boolean withoutAbilities) {
@@ -883,6 +889,10 @@ public class ComputerUtilCombat {
toughness -= attacker.getAmountOfKeyword("Flanking");
}
if (defender.getName().equals("Shape Stealer")) {
toughness += attacker.getNetDefense() - defender.getNetDefense();
}
toughness += defender.getKeywordMagnitude("Bushido");
final Game game = attacker.getGame();
final ArrayList<Trigger> theTriggers = new ArrayList<Trigger>();
@@ -891,7 +901,7 @@ public class ComputerUtilCombat {
}
theTriggers.addAll(attacker.getTriggers());
for (final Trigger trigger : theTriggers) {
final HashMap<String, String> trigParams = trigger.getMapParams();
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, defender, trigger, null)
@@ -967,7 +977,7 @@ public class ComputerUtilCombat {
if (ability.getApi() != ApiType.Pump || !ability.hasParam("NumDef")) {
if (ComputerUtilCost.canPayCost(ability, defender.getController())) {
int tBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("NumDef"), ability);
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
@@ -978,7 +988,7 @@ public class ComputerUtilCombat {
}
if (ComputerUtilCost.canPayCost(ability, defender.getController())) {
int tBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("CounterNum"), ability);
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
@@ -996,9 +1006,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param combat
* a {@link forge.game.combat.Combat} object.
* @return a int.
@@ -1015,6 +1025,13 @@ public class ComputerUtilCombat {
}
}
// Serene Master switches power with attacker
if (defender!= null && defender.getName().equals("Serene Master")) {
power += defender.getNetAttack() - attacker.getNetAttack();
} else if (defender != null && attacker.getName().equals("Shape Stealer")) {
power += defender.getNetAttack() - attacker.getNetAttack();
}
final Game game = attacker.getGame();
final ArrayList<Trigger> theTriggers = new ArrayList<Trigger>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
@@ -1037,7 +1054,7 @@ public class ComputerUtilCombat {
final List<Card> cardList = game.getCardsIn(ZoneType.Battlefield);
for (final Card card : cardList) {
for (final StaticAbility stAb : card.getStaticAbilities()) {
final HashMap<String, String> params = stAb.getMapParams();
final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) {
continue;
}
@@ -1061,7 +1078,7 @@ public class ComputerUtilCombat {
}
for (final Trigger trigger : theTriggers) {
final HashMap<String, String> trigParams = trigger.getMapParams();
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, defender, trigger, combat)
@@ -1137,7 +1154,7 @@ public class ComputerUtilCombat {
}
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
int pBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("NumAtt"), ability);
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumAtt"), ability);
if (pBonus > 0) {
power += pBonus;
}
@@ -1148,7 +1165,7 @@ public class ComputerUtilCombat {
}
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
int pBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("CounterNum"), ability);
int pBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
if (pBonus > 0) {
power += pBonus;
}
@@ -1166,9 +1183,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param combat
* a {@link forge.game.combat.Combat} object.
* @return a int.
@@ -1184,6 +1201,10 @@ public class ComputerUtilCombat {
}
}
if (defender != null && attacker.getName().equals("Shape Stealer")) {
toughness += defender.getNetDefense() - attacker.getNetDefense();
}
final Game game = attacker.getGame();
final ArrayList<Trigger> theTriggers = new ArrayList<Trigger>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
@@ -1199,7 +1220,7 @@ public class ComputerUtilCombat {
final List<Card> cardList = game.getCardsIn(ZoneType.Battlefield);
for (final Card card : cardList) {
for (final StaticAbility stAb : card.getStaticAbilities()) {
final HashMap<String, String> params = stAb.getMapParams();
final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) {
continue;
}
@@ -1223,7 +1244,7 @@ public class ComputerUtilCombat {
}
for (final Trigger trigger : theTriggers) {
final HashMap<String, String> trigParams = trigger.getMapParams();
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, defender, trigger, combat)
@@ -1314,7 +1335,7 @@ public class ComputerUtilCombat {
if (ability.getApi() != ApiType.Pump || !ability.hasParam("NumDef")) {
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
int tBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("NumDef"), ability);
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
@@ -1325,7 +1346,7 @@ public class ComputerUtilCombat {
}
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
int tBonus = AbilityUtils.calculateAmount(ability.getSourceCard(), ability.getParam("CounterNum"), ability);
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
@@ -1342,9 +1363,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean checkDestroyBlockerTrigger(final Card attacker, final Card defender) {
@@ -1354,7 +1375,7 @@ public class ComputerUtilCombat {
theTriggers.addAll(card.getTriggers());
}
for (Trigger trigger : theTriggers) {
HashMap<String, String> trigParams = trigger.getMapParams();
Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, defender, trigger, null)) {
@@ -1398,9 +1419,9 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean checkDestroyAttackerTrigger(final Card attacker, final Card defender) {
@@ -1409,7 +1430,7 @@ public class ComputerUtilCombat {
theTriggers.addAll(card.getTriggers());
}
for (Trigger trigger : theTriggers) {
HashMap<String, String> trigParams = trigger.getMapParams();
Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, defender, trigger, null)) {
@@ -1454,9 +1475,9 @@ public class ComputerUtilCombat {
* @param ai
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param combat
* a {@link forge.game.combat.Combat} object.
* @param withoutAbilities
@@ -1528,7 +1549,7 @@ public class ComputerUtilCombat {
if (defender.hasKeyword("Double Strike")) {
if (defenderDamage > 0 && (defender.hasKeyword("Deathtouch")
|| attacker.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| attacker.hasSVar("DestroyWhenDamaged"))) {
return true;
}
if (defenderDamage >= attackerLife) {
@@ -1542,7 +1563,7 @@ public class ComputerUtilCombat {
return false;
}
if (attackerDamage > 0 && (attacker.hasKeyword("Deathtouch")
|| defender.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| defender.hasSVar("DestroyWhenDamaged"))) {
return false;
}
}
@@ -1560,13 +1581,13 @@ public class ComputerUtilCombat {
return false;
}
if (attackerDamage > 0 && (attacker.hasKeyword("Deathtouch")
|| defender.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| defender.hasSVar("DestroyWhenDamaged"))) {
return false;
}
}
if (defenderDamage > 0 && (defender.hasKeyword("Deathtouch")
|| attacker.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| attacker.hasSVar("DestroyWhenDamaged"))) {
return true;
}
@@ -1584,7 +1605,7 @@ public class ComputerUtilCombat {
* @param ai
*
* @param blocker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public static boolean blockerWouldBeDestroyed(Player ai, final Card blocker, Combat combat) {
@@ -1609,9 +1630,9 @@ public class ComputerUtilCombat {
* @param ai
*
* @param defender
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param combat
* a {@link forge.game.combat.Combat} object.
* @param withoutAbilities
@@ -1684,7 +1705,7 @@ public class ComputerUtilCombat {
if (attacker.hasKeyword("Double Strike")) {
if (attackerDamage > 0 && (attacker.hasKeyword("Deathtouch")
|| defender.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| defender.hasSVar("DestroyWhenDamaged"))) {
return true;
}
if (attackerDamage >= defenderLife) {
@@ -1698,7 +1719,7 @@ public class ComputerUtilCombat {
return false;
}
if (defenderDamage > 0 && (defender.hasKeyword("Deathtouch")
|| attacker.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| attacker.hasSVar("DestroyWhenDamaged"))) {
return false;
}
}
@@ -1716,13 +1737,13 @@ public class ComputerUtilCombat {
return false;
}
if (defenderDamage > 0 && (defender.hasKeyword("Deathtouch")
|| attacker.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| attacker.hasSVar("DestroyWhenDamaged"))) {
return false;
}
}
if (attackerDamage > 0 && (attacker.hasKeyword("Deathtouch")
|| defender.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it."))) {
|| defender.hasSVar("DestroyWhenDamaged"))) {
return true;
}
@@ -1739,7 +1760,7 @@ public class ComputerUtilCombat {
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param block
* a {@link forge.CardList} object.
* @param dmgCanDeal
@@ -1834,7 +1855,7 @@ public class ComputerUtilCombat {
* @param maxDamage
* a int.
* @param source
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param isCombat
* a boolean.
* @return a int.
@@ -1851,7 +1872,7 @@ public class ComputerUtilCombat {
* @param maxDamage
* a int.
* @param source
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param isCombat
* a boolean.
* @param noPrevention
@@ -1862,7 +1883,7 @@ public class ComputerUtilCombat {
final boolean noPrevention) {
final int killDamage = ComputerUtilCombat.getDamageToKill(c);
if (c.hasKeyword("Indestructible") || (c.getShield() > 0)) {
if (c.hasKeyword("Indestructible") || !c.getShield().isEmpty()) {
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
return maxDamage + 1;
}
@@ -1904,7 +1925,7 @@ public class ComputerUtilCombat {
public final static int getDamageToKill(final Card c) {
int killDamage = c.getLethalDamage() + c.getPreventNextDamageTotalShields();
if ((killDamage > c.getPreventNextDamageTotalShields())
&& c.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
&& c.hasSVar("DestroyWhenDamaged")) {
killDamage = 1 + c.getPreventNextDamageTotalShields();
}
@@ -1920,7 +1941,7 @@ public class ComputerUtilCombat {
* @param damage
* a int.
* @param source
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param isCombat
* a boolean.
* @return a int.
@@ -1981,7 +2002,7 @@ public class ComputerUtilCombat {
* @param damage
* a int.
* @param source
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param isCombat
* a boolean.
* @return a int.
@@ -2011,7 +2032,7 @@ public class ComputerUtilCombat {
* @param possiblePrevention
* a int.
* @param source
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param isCombat
* a boolean.
* @return a int.

View File

@@ -1,29 +1,20 @@
package forge.game.ai;
package forge.ai;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.player.Player;
import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.card.ability.AbilityUtils;
import forge.card.cost.Cost;
import forge.card.cost.CostDamage;
import forge.card.cost.CostDiscard;
import forge.card.cost.CostPart;
import forge.card.cost.CostPayLife;
import forge.card.cost.CostPayment;
import forge.card.cost.CostPutCounter;
import forge.card.cost.CostRemoveCounter;
import forge.card.cost.CostSacrifice;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.TextUtil;
/**
* TODO: Write javadoc for this type.
*
@@ -73,7 +64,7 @@ public class ComputerUtilCost {
if (part instanceof CostRemoveCounter) {
final CostRemoveCounter remCounter = (CostRemoveCounter) part;
final CounterType type = remCounter.getCounter();
final CounterType type = remCounter.counter;
if (!part.payCostFromSource()) {
if (type.name().equals("P1P1")) {
return false;
@@ -287,7 +278,7 @@ public class ComputerUtilCost {
* </p>
*
* @param hostCard
* a {@link forge.Card} object.
* a {@link forge.game.card.Card} object.
* @param costString
* a {@link java.lang.String} object.
* @return a boolean.
@@ -315,7 +306,7 @@ public class ComputerUtilCost {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param player
* a {@link forge.game.player.Player} object.
* @return a boolean.
@@ -358,7 +349,7 @@ public class ComputerUtilCost {
} // canPayCost()
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, List<Player> payers) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
boolean payForOwnOnly = "OnlyOwn".equals(sa.getParam("UnlessAI"));
boolean payOwner = sa.hasParam("UnlessAI") ? sa.getParam("UnlessAI").startsWith("Defined") : false;
boolean payNever = "Never".equals(sa.getParam("UnlessAI"));
@@ -395,6 +386,10 @@ public class ComputerUtilCost {
if (payerCreatures > sourceCreatures + 1) {
return false;
}
} else if ("LifeLE2".equals(sa.getParam("UnlessAI"))) {
if (payer.getLife() < 3) {
return true;
}
}
// AI will only pay when it's not already payed and only opponents abilities

View File

@@ -0,0 +1,911 @@
package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import forge.card.MagicColor;
import forge.card.mana.ManaAtom;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.cost.Cost;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostPayment;
import forge.game.mana.Mana;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.mana.ManaPool;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* TODO: Write javadoc for this type.
*
*/
public class ComputerUtilMana {
private final static boolean DEBUG_MANA_PAYMENT = false;
public static boolean canPayManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
cost = new ManaCostBeingPaid(cost); //check copy of cost so it doesn't modify the exist cost being paid
return payManaCost(cost, sa, ai, true, 0, true);
}
public static boolean payManaCost(ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
return payManaCost(cost, sa, ai, false, 0, true);
}
public static boolean canPayManaCost(final SpellAbility sa, final Player ai, final int extraMana) {
return payManaCost(sa, ai, true, extraMana, true);
}
// Does not check if mana sources can be used right now, just checks for potential chance.
public static boolean hasEnoughManaSourcesToCast(final SpellAbility sa, final Player ai) {
sa.setActivatingPlayer(ai);
return payManaCost(sa, ai, true, 0, false);
}
public static boolean payManaCost(final Player ai, final SpellAbility sa) {
return payManaCost(sa, ai, false, 0, true);
}
private static void refundMana(List<Mana> manaSpent, Player ai, SpellAbility sa) {
if (sa.getHostCard() != null) {
sa.getHostCard().setCanCounter(true);
}
for (final Mana m : manaSpent) {
ai.getManaPool().addMana(m);
}
manaSpent.clear();
}
private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) {
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana);
return payManaCost(cost, sa, ai, test, extraMana, checkPlayable);
}
private static boolean payManaCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) {
adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai);
List<Mana> manaSpentToPay = test ? new ArrayList<Mana>() : sa.getPayingMana();
final ManaPool manapool = ai.getManaPool();
List<ManaCostShard> unpaidShards = cost.getUnpaidShards();
Collections.sort(unpaidShards); // most difficult shards must come first
for (ManaCostShard part : unpaidShards) {
if (part != ManaCostShard.X) {
if (cost.isPaid()) {
continue;
}
// get a mana of this type from floating, bail if none available
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction());
if (mana == null) {
continue; // no matching mana in the pool
}
else {
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) {
manaSpentToPay.add(0, mana);
}
}
}
}
if (cost.isPaid()) {
// refund any mana taken from mana pool when test
if(test)
refundMana(manaSpentToPay, ai, sa);
handleOfferingsAI(sa, test, cost.isPaid());
return true;
}
// arrange all mana abilities by color produced.
final Multimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable);
if (manaAbilityMap.isEmpty()) {
refundMana(manaSpentToPay, ai, sa);
handleOfferingsAI(sa, test, cost.isPaid());
return false;
}
if (DEBUG_MANA_PAYMENT) {
System.out.println("DEBUG_MANA_PAYMENT: manaAbilityMap = " + manaAbilityMap);
}
// select which abilities may be used for each shard
Multimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
// if (DEBUG_MANA_PAYMENT) {
// System.out.println((test ? "test -- " : "PROD -- ") + FThreads.debugGetStackTraceItem(5, true));
// for (Entry<ManaCostShard, Collection<SpellAbility>> src : sourcesForShards.entrySet()) {
// System.out.println("\t" +src.getKey() + " : " + src.getValue().size() + " source(s)");
// for (SpellAbility sss : src.getValue()) {
// System.out.printf("\t\t%s - %s%n", sss.getHostCard(), sss);
// }
// }
// }
List<String> paymentPlan = new ArrayList<String>();
ManaCostShard toPay = null;
// Loop over mana needed
while (!cost.isPaid()) {
toPay = getNextShardToPay(cost, sourcesForShards);
Collection<SpellAbility> saList = sourcesForShards.get(toPay);
SpellAbility saPayment = null;
if (saList != null) {
for (final SpellAbility ma : saList) {
if (ma.getHostCard() == sa.getHostCard()) {
continue;
}
final String typeRes = cost.getSourceRestriction();
if (StringUtils.isNotBlank(typeRes) && !ma.getHostCard().isType(typeRes)) {
continue;
}
if (canPayShardWithSpellAbility(toPay, ai, ma, sa, checkPlayable || !test)) {
saPayment = ma;
break;
}
}
} else {
break;
}
if (DEBUG_MANA_PAYMENT) {
paymentPlan.add(String.format("%s : (%s) %s", toPay, saPayment == null ? "LIFE" : saPayment.getHostCard(), saPayment));
}
if (saPayment == null) {
if (!toPay.isPhyrexian() || !ai.canPayLife(2)) {
break; // cannot pay
}
cost.payPhyrexian();
if (!test) {
ai.payLife(2, sa.getHostCard());
}
continue;
}
setExpressColorChoice(sa, ai, cost, toPay, saPayment);
if (test) {
String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment);
manaProduced = AbilityManaPart.applyManaReplacement(saPayment, manaProduced);
//System.out.println(manaProduced);
payMultipleMana(cost, manaProduced, ai);
// remove from available lists
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next();
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
itSa.remove();
}
}
}
else {
if (saPayment.getPayCosts() != null) {
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) {
saList.remove(saPayment);
continue;
}
}
else {
System.err.println("Ability " + saPayment + " from " + saPayment.getHostCard() + " had NULL as payCost");
saPayment.getHostCard().tap();
}
ai.getGame().getStack().addAndUnfreeze(saPayment);
// subtract mana from mana pool
manapool.payManaFromAbility(sa, cost, saPayment);
// no need to remove abilities from resource map,
// once their costs are paid and consume resources, they can not be used again
}
}
handleOfferingsAI(sa, test, cost.isPaid());
// if (DEBUG_MANA_PAYMENT) {
// System.err.printf("%s > [%s] payment has %s (%s +%d) for (%s) %s:%n\t%s%n%n",
// FThreads.debugGetCurrThreadId(), test ? "test" : "PROD", cost.isPaid() ? "*PAID*" : "failed", originalCost,
// extraMana, sa.getHostCard(), sa.toUnsuppressedString(), StringUtils.join(paymentPlan, "\n\t"));
// }
if (!cost.isPaid()) {
refundMana(manaSpentToPay, ai, sa);
if (test) {
return false;
}
else {
System.out.println("ComputerUtil : payManaCost() cost was not paid for " + sa.getHostCard().getName() + ". Didn't find what to pay for " + toPay);
return false;
}
}
if (test)
refundMana(manaSpentToPay, ai, sa);
sa.getHostCard().setColorsPaid(cost.getColorsPaid());
// if (sa instanceof Spell_Permanent) // should probably add this
sa.getHostCard().setSunburstValue(cost.getSunburst());
return true;
} // payManaCost()
/**
* <p>
* getManaFrom.
* </p>
*
* @param saBeingPaidFor
* a {@link forge.game.spellability.SpellAbility} object.
* @return a {@link forge.game.mana.Mana} object.
*/
private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) {
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction);
// Exclude border case
if (weightedOptions.isEmpty()) {
return null; // There is no matching mana in the pool
}
// select equal weight possibilities
List<Mana> manaChoices = new ArrayList<Mana>();
int bestWeight = Integer.MIN_VALUE;
for (Pair<Mana, Integer> option : weightedOptions) {
int thisWeight = option.getRight();
Mana thisMana = option.getLeft();
if (thisWeight > bestWeight) {
manaChoices.clear();
bestWeight = thisWeight;
}
if (thisWeight == bestWeight) {
// add only distinct Mana-s
boolean haveDuplicate = false;
for (Mana m : manaChoices) {
if (m.equals(thisMana)) {
haveDuplicate = true;
break;
}
}
if (!haveDuplicate) {
manaChoices.add(thisMana);
}
}
}
// got an only one best option?
if (manaChoices.size() == 1) {
return manaChoices.get(0);
}
// Let them choose then
return ai.getController().chooseManaFromPool(manaChoices);
}
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) {
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<Pair<Mana, Integer>>();
for (final Mana thisMana : manapool) {
if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) {
continue;
}
if (thisMana.getManaAbility() != null && !thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) {
continue;
}
boolean canPay = manapool.canPayForShardWithColor(shard, thisMana.getColor());
if (!canPay || (shard.isSnow() && !thisMana.isSnow())) {
continue;
}
if (StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().isType(restriction)) {
continue;
}
// prefer colorless mana to spend
int weight = thisMana.isColorless() ? 5 : 0;
// prefer restricted mana to spend
if (thisMana.isRestricted()) {
weight += 2;
}
// Spend non-snow mana first
if (!thisMana.isSnow()) {
weight += 1;
}
weightedOptions.add(Pair.of(thisMana, weight));
}
return weightedOptions;
}
private static void setExpressColorChoice(final SpellAbility sa, final Player ai, ManaCostBeingPaid cost,
ManaCostShard toPay, SpellAbility saPayment) {
AbilityManaPart m = saPayment.getManaPart();
if (m.isComboMana())
getComboManaChoice(ai, saPayment, sa, cost);
else if (saPayment.getApi() == ApiType.ManaReflected) {
System.out.println("Evaluate reflected mana of: " + saPayment.getHostCard());
Set<String> reflected = CardUtil.getReflectableManaColors(saPayment);
for (byte c : MagicColor.WUBRG) {
if (ai.getManaPool().canPayForShardWithColor(toPay, c) && reflected.contains(MagicColor.toLongString(c))) {
m.setExpressChoice(MagicColor.toShortString(c));
return;
}
}
} else if (m.isAnyMana()) {
byte colorChoice = 0;
if (toPay.isOr2Colorless())
colorChoice = toPay.getColorMask();
else {
for (byte c : MagicColor.WUBRG) {
if (ai.getManaPool().canPayForShardWithColor(toPay, c)) {
colorChoice = c;
break;
}
}
}
m.setExpressChoice(MagicColor.toShortString(colorChoice));
}
}
private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts) {
final Card sourceCard = ma.getHostCard();
if (toPay.isSnow() && !sourceCard.isSnow()) { return false; }
AbilityManaPart m = ma.getManaPart();
if (!m.meetsManaRestrictions(sa)) {
return false;
}
if (checkCosts) {
// Check if AI can still play this mana ability
ma.setActivatingPlayer(ai);
if (ma.getPayCosts() != null) { // if the AI can't pay the additional costs skip the mana ability
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
return false;
}
} else if (sourceCard.isTapped()) {
return false;
}
}
if (m.isComboMana()) {
for (String s : m.getComboColors().split(" ")) {
if ("Any".equals(s) || ai.getManaPool().canPayForShardWithColor(toPay, MagicColor.fromName(s)))
return true;
}
return false;
} else if (ma.getApi() == ApiType.ManaReflected) {
Set<String> reflected = CardUtil.getReflectableManaColors(ma);
for (byte c : MagicColor.WUBRG) {
if (ai.getManaPool().canPayForShardWithColor(toPay, c) && reflected.contains(MagicColor.toLongString(c))) {
m.setExpressChoice(MagicColor.toShortString(c));
return true;
}
}
return false;
}
return true;
}
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Multimap<ManaCostShard, SpellAbility> sourcesForShards) {
// mind the priorities
// * Pay mono-colored first,
// * Pay 2/C with matching colors
// * pay hybrids
// * pay phyrexian, keep mana for colorless
// * pay colorless
for (ManaCostShard s : cost.getDistinctShards()) { // should check in which order EnumMap enumerates keys. If it's same as enum member declaration, nothing else needs to be done.
return s;
}
return null;
}
private static void adjustManaCostToAvoidNegEffects(ManaCostBeingPaid cost, final Card card, Player ai) {
// Make mana needed to avoid negative effect a mandatory cost for the AI
for (String manaPart : card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")) {
// convert long color strings to short color strings
byte mask = MagicColor.fromName(manaPart);
// make mana mandatory for AI
if (!cost.needsColor(mask, ai.getManaPool()) && cost.getColorlessManaAmount() > 0) {
ManaCostShard shard = ManaCostShard.valueOf(mask);
cost.increaseShard(shard, 1);
cost.decreaseColorlessMana(1);
}
}
}
/**
* <p>
* getComboManaChoice.
* </p>
*
* @param abMana
* a {@link forge.card.spellability.AbilityMana} object.
* @param saRoot
* a {@link forge.game.spellability.SpellAbility} object.
* @param cost
* a {@link forge.game.mana.ManaCostBeingPaid} object.
* @return String
*/
private static void getComboManaChoice(final Player ai, final SpellAbility manaAb, final SpellAbility saRoot, final ManaCostBeingPaid cost) {
final StringBuilder choiceString = new StringBuilder();
final Card source = manaAb.getHostCard();
final AbilityManaPart abMana = manaAb.getManaPart();
if (abMana.isComboMana()) {
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1;
final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost);
final String[] comboColors = abMana.getComboColors().split(" ");
for (int nMana = 1; nMana <= amount; nMana++) {
String choice = "";
// Use expressChoice first
if (!abMana.getExpressChoice().isEmpty()) {
choice = abMana.getExpressChoice();
abMana.clearExpressChoice();
byte colorMask = MagicColor.fromName(choice);
if (abMana.canProduce(choice, manaAb) && testCost.isAnyPartPayableWith(colorMask, ai.getManaPool())) {
choiceString.append(choice);
payMultipleMana(testCost, choice, ai);
continue;
}
}
// check colors needed for cost
if (!testCost.isPaid()) {
// Loop over combo colors
for (String color : comboColors) {
if (testCost.isAnyPartPayableWith(MagicColor.fromName(color), ai.getManaPool())) {
payMultipleMana(testCost, color, ai);
if (nMana != 1) {
choiceString.append(" ");
}
choiceString.append(color);
choice = color;
break;
}
}
if (!choice.isEmpty()) {
continue;
}
}
// check if combo mana can produce most common color in hand
String commonColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn(
ZoneType.Hand));
if (!commonColor.isEmpty() && abMana.getComboColors().contains(MagicColor.toShortString(commonColor))) {
choice = MagicColor.toShortString(commonColor);
}
else {
// default to first color
choice = comboColors[0];
}
if (nMana != 1) {
choiceString.append(" ");
}
choiceString.append(choice);
}
}
if (choiceString.toString().isEmpty()) {
choiceString.append("0");
}
abMana.setExpressChoice(choiceString.toString());
}
/**
* <p>
* payMultipleMana.
* </p>
* @param testCost
*
* @param mana
* a {@link java.lang.String} object.
* @return a boolean.
*/
private final static String payMultipleMana(ManaCostBeingPaid testCost, String mana, final Player p) {
List<String> unused = new ArrayList<String>(4);
for (String manaPart : TextUtil.split(mana, ' ')) {
if (StringUtils.isNumeric(manaPart)) {
for (int i = Integer.parseInt(manaPart); i > 0; i--) {
boolean wasNeeded = testCost.ai_payMana("1", p.getManaPool());
if (!wasNeeded) {
unused.add(Integer.toString(i));
break;
}
}
}
else {
String color = MagicColor.toShortString(manaPart);
boolean wasNeeded = testCost.ai_payMana(color, p.getManaPool());
if (!wasNeeded) {
unused.add(color);
}
}
}
return unused.isEmpty() ? null : StringUtils.join(unused, ' ');
}
/**
* Find all mana sources.
* @param manaAbilityMap
* @param partSources
* @param partPriority
* @param costParts
* @param foundAllSources
* @return Were all mana sources found?
*/
private static Multimap<ManaCostShard, SpellAbility> groupAndOrderToPayShards(final Player ai, final Multimap<Integer, SpellAbility> manaAbilityMap,
final ManaCostBeingPaid cost) {
Multimap<ManaCostShard, SpellAbility> res = ArrayListMultimap.create();
// loop over cost parts
for (ManaCostShard shard : cost.getDistinctShards()) {
if (shard == ManaCostShard.S) {
res.putAll(shard, manaAbilityMap.get(ManaAtom.IS_SNOW));
continue;
}
if (shard.isOr2Colorless()) {
Integer colorKey = Integer.valueOf(shard.getColorMask());
if (manaAbilityMap.containsKey(colorKey))
res.putAll(shard, manaAbilityMap.get(colorKey));
if (manaAbilityMap.containsKey(ManaAtom.COLORLESS))
res.putAll(shard, manaAbilityMap.get(ManaAtom.COLORLESS));
continue;
}
for (Entry<Integer, SpellAbility> kv : manaAbilityMap.entries()) {
// apply mana color change matrix here
if (ai.getManaPool().canPayForShardWithColor(shard, kv.getKey().byteValue())) {
res.put(shard, kv.getValue());
}
}
}
if (cost.getColorlessManaAmount() > 0 && manaAbilityMap.containsKey(ManaAtom.COLORLESS)) {
res.putAll(ManaCostShard.COLORLESS, manaAbilityMap.get(ManaAtom.COLORLESS));
}
return res;
}
/**
* Calculate the ManaCost for the given SpellAbility.
* @param sa
* @param test
* @param extraMana
* @return ManaCost
*/
static ManaCostBeingPaid calculateManaCost(final SpellAbility sa, final boolean test, final int extraMana) {
ZoneType castFromBackup = null;
if (test && sa.isSpell()) {
castFromBackup = sa.getHostCard().getCastFrom();
sa.getHostCard().setCastFrom(sa.getHostCard().getZone().getZoneType());
}
Cost payCosts = sa.getPayCosts();
CostPartMana manapart = payCosts != null ? payCosts.getCostMana() : null;
final ManaCost mana = payCosts != null ? ( manapart == null ? ManaCost.ZERO : manapart.getManaCostFor(sa) ) : ManaCost.NO_COST;
String restriction = null;
if (payCosts != null && payCosts.getCostMana() != null) {
restriction = payCosts.getCostMana().getRestiction();
}
ManaCostBeingPaid cost = new ManaCostBeingPaid(mana, restriction);
cost.applySpellCostChange(sa, test);
final Card card = sa.getHostCard();
// Tack xMana Payments into mana here if X is a set value
if ((sa.getPayCosts() != null) && (cost.getXcounter() > 0 || extraMana > 0)) {
int manaToAdd = 0;
if (test && extraMana > 0) {
final int multiplicator = Math.max(cost.getXcounter(), 1);
manaToAdd = extraMana * multiplicator;
} else {
// For Count$xPaid set PayX in the AFs then use that here
// Else calculate it as appropriate.
final String xSvar = card.getSVar("X").startsWith("Count$xPaid") ? "PayX" : "X";
if (!card.getSVar(xSvar).equals("")) {
if (xSvar.equals("PayX")) {
manaToAdd = Integer.parseInt(card.getSVar(xSvar)) * cost.getXcounter(); // X
} else {
manaToAdd = AbilityUtils.calculateAmount(card, xSvar, sa) * cost.getXcounter();
}
}
}
String manaXColor = sa.getParam("XColor");
ManaCostShard shardToGrow = ManaCostShard.parseNonGeneric(manaXColor == null ? "1" : manaXColor);
cost.increaseShard(shardToGrow, manaToAdd);
if (!test) {
card.setXManaCostPaid(manaToAdd / cost.getXcounter());
}
}
if (test && sa.isSpell()) {
sa.getHostCard().setCastFrom(castFromBackup);
}
return cost;
}
//This method is currently used by AI to estimate available mana
public static List<Card> getAvailableMana(final Player ai, final boolean checkPlayable) {
final List<Card> list = ai.getCardsIn(ZoneType.Battlefield);
list.addAll(ai.getCardsIn(ZoneType.Hand));
final List<Card> manaSources = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (final SpellAbility am : getAIPlayableMana(c)) {
am.setActivatingPlayer(ai);
if (!checkPlayable || am.canPlay()) {
return true;
}
}
return false;
}
}); // CardListFilter
final List<Card> sortedManaSources = new ArrayList<Card>();
final List<Card> otherManaSources = new ArrayList<Card>();
final List<Card> colorlessManaSources = new ArrayList<Card>();
final List<Card> oneManaSources = new ArrayList<Card>();
final List<Card> twoManaSources = new ArrayList<Card>();
final List<Card> threeManaSources = new ArrayList<Card>();
final List<Card> fourManaSources = new ArrayList<Card>();
final List<Card> fiveManaSources = new ArrayList<Card>();
final List<Card> anyColorManaSources = new ArrayList<Card>();
// Sort mana sources
// 1. Use lands that can only produce colorless mana without
// drawback/cost first
// 2. Search for mana sources that have a certain number of abilities
// 3. Use lands that produce any color many
// 4. all other sources (creature, costs, drawback, etc.)
for (Card card : manaSources) {
if (card.isCreature() || card.isEnchanted()) {
otherManaSources.add(card);
continue; // don't use creatures before other permanents
}
int usableManaAbilities = 0;
boolean needsLimitedResources = false;
boolean producesAnyColor = false;
final ArrayList<SpellAbility> manaAbilities = getAIPlayableMana(card);
for (final SpellAbility m : manaAbilities) {
if (m.getManaPart().isAnyMana()) {
producesAnyColor = true;
}
final Cost cost = m.getPayCosts();
if (cost != null) {
needsLimitedResources |= !cost.isReusuableResource();
}
// if the AI can't pay the additional costs skip the mana ability
if (cost != null) {
m.setActivatingPlayer(ai);
if (!CostPayment.canPayAdditionalCosts(m.getPayCosts(), m)) {
continue;
}
}
// don't use abilities with dangerous drawbacks
AbilitySub sub = m.getSubAbility();
if (sub != null && !card.getName().equals("Pristine Talisman") && !card.getName().equals("Zhur-Taa Druid")) {
if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) {
continue;
}
needsLimitedResources = true; // TODO: check for good drawbacks (gainLife)
}
usableManaAbilities++;
}
if (needsLimitedResources) {
otherManaSources.add(card);
} else if (producesAnyColor) {
anyColorManaSources.add(card);
} else if (usableManaAbilities == 1) {
if (manaAbilities.get(0).getManaPart().mana().equals("1")) {
colorlessManaSources.add(card);
} else {
oneManaSources.add(card);
}
} else if (usableManaAbilities == 2) {
twoManaSources.add(card);
} else if (usableManaAbilities == 3) {
threeManaSources.add(card);
} else if (usableManaAbilities == 4) {
fourManaSources.add(card);
} else {
fiveManaSources.add(card);
}
}
sortedManaSources.addAll(colorlessManaSources);
sortedManaSources.addAll(oneManaSources);
sortedManaSources.addAll(twoManaSources);
sortedManaSources.addAll(threeManaSources);
sortedManaSources.addAll(fourManaSources);
sortedManaSources.addAll(fiveManaSources);
sortedManaSources.addAll(anyColorManaSources);
//use better creatures later
ComputerUtilCard.sortByEvaluateCreature(otherManaSources);
Collections.reverse(otherManaSources);
sortedManaSources.addAll(otherManaSources);
return sortedManaSources;
} // getAvailableMana()
//This method is currently used by AI to estimate mana available
private static Multimap<Integer, SpellAbility> groupSourcesByManaColor(final Player ai, boolean checkPlayable) {
final Multimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create();
final Game game = ai.getGame();
// Loop over all current available mana sources
for (final Card sourceCard : getAvailableMana(ai, checkPlayable)) {
for (final SpellAbility m : getAIPlayableMana(sourceCard)) {
m.setActivatingPlayer(ai);
if (checkPlayable && !m.canPlay()) {
continue;
}
// don't use abilities with dangerous drawbacks
AbilitySub sub = m.getSubAbility();
if (sub != null) {
if (!SpellApiToAi.Converter.get(sub.getApi()).chkDrawbackWithSubs(ai, sub)) {
continue;
}
}
manaMap.put(ManaAtom.COLORLESS, m); // add to colorless source list
AbilityManaPart mp = m.getManaPart();
// setup produce mana replacement effects
final Map<String, Object> repParams = new HashMap<String, Object>();
repParams.put("Event", "ProduceMana");
repParams.put("Mana", mp.getOrigProduced());
repParams.put("Affected", sourceCard);
repParams.put("Player", ai);
repParams.put("AbilityMana", m);
for (final Player p : game.getPlayers()) {
for (final Card crd : p.getAllCards()) {
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
if (replacementEffect.requirementsCheck(game)
&& replacementEffect.canReplace(repParams)
&& replacementEffect.getMapParams().containsKey("ManaReplacement")
&& replacementEffect.zonesCheck(game.getZoneOf(crd))) {
String repType = crd.getSVar(replacementEffect.getMapParams().get("ManaReplacement"));
if (repType.contains("Chosen")) {
repType = repType.replace("Chosen", MagicColor.toShortString(crd.getChosenColor().get(0)));
}
mp.setManaReplaceType(repType);
}
}
}
}
Set<String> reflectedColors = CardUtil.getReflectableManaColors(m);
// find possible colors
if (mp.canProduce("W", m) || reflectedColors.contains(MagicColor.Constant.WHITE)) {
manaMap.put(ManaAtom.WHITE, m);
}
if (mp.canProduce("U", m) || reflectedColors.contains(MagicColor.Constant.BLUE)) {
manaMap.put(ManaAtom.BLUE, m);
}
if (mp.canProduce("B", m) || reflectedColors.contains(MagicColor.Constant.BLACK)) {
manaMap.put(ManaAtom.BLACK, m);
}
if (mp.canProduce("R", m) || reflectedColors.contains(MagicColor.Constant.RED)) {
manaMap.put(ManaAtom.RED, m);
}
if (mp.canProduce("G", m) || reflectedColors.contains(MagicColor.Constant.GREEN)) {
manaMap.put(ManaAtom.GREEN, m);
}
if (mp.isSnow()) {
manaMap.put(ManaAtom.IS_SNOW, m);
}
} // end of mana abilities loop
} // end of mana sources loop
return manaMap;
}
/**
* <p>
* determineLeftoverMana.
* </p>
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param player
* a {@link forge.game.player.Player} object.
* @return a int.
* @since 1.0.15
*/
public static int determineLeftoverMana(final SpellAbility sa, final Player player) {
for (int i = 1; i < 100; i++)
if (!canPayManaCost(sa, player, i))
return i - 1;
return 99;
}
// Returns basic mana abilities plus "reflected mana" abilities
/**
* <p>
* getAIPlayableMana.
* </p>
*
* @return a {@link java.util.ArrayList} object.
*/
public static final ArrayList<SpellAbility> getAIPlayableMana(Card c) {
final ArrayList<SpellAbility> res = new ArrayList<SpellAbility>();
for (final SpellAbility a : c.getManaAbility()) {
// if a mana ability has a mana cost the AI will miscalculate
// if there is a parent ability the AI can't use it
final Cost cost = a.getPayCosts();
if (!cost.hasNoManaCost() || (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) {
continue;
}
if (!res.contains(a)) {
res.add(a);
}
}
return res;
}
private static void handleOfferingsAI(final SpellAbility sa, boolean test, boolean costIsPaid) {
if (sa.isOffering() && sa.getSacrificedAsOffering() != null) {
final Card offering = sa.getSacrificedAsOffering();
offering.setUsedToPay(false);
if (costIsPaid && !test) {
sa.getHostCard().getController().getGame().getAction().sacrifice(offering, sa);
}
sa.resetSacrificedAsOffering();
}
}
}

View File

@@ -0,0 +1,63 @@
package forge.ai;
import forge.game.Game;
import forge.game.player.LobbyPlayer;
import forge.game.player.Player;
public class LobbyPlayerAi extends LobbyPlayer {
public LobbyPlayerAi(String name) {
super(name);
}
private String aiProfile = "";
private boolean rotateProfileEachGame;
private boolean allowCheatShuffle;
public boolean isAllowCheatShuffle() {
return allowCheatShuffle;
}
public void setAllowCheatShuffle(boolean allowCheatShuffle) {
this.allowCheatShuffle = allowCheatShuffle;
}
public void setAiProfile(String profileName) {
aiProfile = profileName;
}
public String getAiProfile() {
return aiProfile;
}
public void setRotateProfileEachGame(boolean rotateProfileEachGame) {
this.rotateProfileEachGame = rotateProfileEachGame;
}
@Override
protected PlayerType getType() {
return PlayerType.COMPUTER;
}
@Override
public PlayerControllerAi createControllerFor(Player ai) {
PlayerControllerAi result = new PlayerControllerAi(ai.getGame(), ai, this);
result.allowCheatShuffle(allowCheatShuffle);
return result;
}
@Override
public Player createIngamePlayer(Game game) {
Player ai = new Player(getName(), game);
ai.setFirstController(createControllerFor(ai));
if( rotateProfileEachGame ) {
setAiProfile(AiProfileUtil.getRandomProfile());
System.out.println(String.format("AI profile %s was chosen for the lobby player %s.", getAiProfile(), getName()));
}
return ai;
}
@Override
public void hear(LobbyPlayer player, String message) { /* Local AI is deaf. */ }
}

View File

@@ -0,0 +1,765 @@
package forge.ai;
import com.esotericsoftware.minlog.Log;
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 com.google.common.collect.Multimap;
import forge.ai.ability.ChangeZoneAi;
import forge.ai.ability.CharmAi;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
import forge.deck.Deck;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.GameType;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*;
import forge.game.card.CardPredicates.Presets;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
import forge.game.cost.CostPartMana;
import forge.game.mana.Mana;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.LobbyPlayer;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerController;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*;
import forge.game.trigger.Trigger;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.MyRandom;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.awt.event.MouseEvent;
import java.security.InvalidParameterException;
import java.util.*;
/**
* A prototype for player controller class
*
* Handles phase skips for now.
*/
public class PlayerControllerAi extends PlayerController {
private final AiController brains;
public PlayerControllerAi(Game game, Player p, LobbyPlayer lp) {
super(game, p, lp);
brains = new AiController(p, game);
}
public void allowCheatShuffle(boolean value){
brains.allowCheatShuffle(value);
}
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities, MouseEvent triggerEvent) {
if (abilities.size() == 0) {
return null;
}
else {
return abilities.get(0);
}
}
/**
* TODO: Write javadoc for this method.
* @param c
*/
/**public void playFromSuspend(Card c) {
final List<SpellAbility> choices = c.getBasicSpells();
c.setSuspendCast(true);
getAi().chooseAndPlaySa(choices, true, true);
}**/
/**
* TODO: Write javadoc for this method.
* @return
*/
public AiController getAi() {
return brains;
}
@Override
public List<PaperCard> sideboard(Deck deck, GameType gameType) {
// AI does not know how to sideboard
return null;
}
@Override
public Map<Card, Integer> assignCombatDamage(Card attacker, List<Card> blockers, int damageDealt, GameEntity defender, boolean overrideOrder) {
return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender, overrideOrder);
}
@Override
public Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero) {
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
return null; // return incorrect value to indicate that
}
@Override
public List<Card> choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List<Card> validTargets, String message) {
return ComputerUtil.choosePermanentsToSacrifice(player, validTargets, max, sa, false, min == 0);
}
@Override
public List<Card> choosePermanentsToDestroy(SpellAbility sa, int min, int max, List<Card> validTargets, String message) {
return ComputerUtil.choosePermanentsToSacrifice(player, validTargets, max, sa, true, min == 0);
}
@Override
public List<Card> chooseCardsForEffect(List<Card> sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) {
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional);
}
@Override
public <T extends GameEntity> T chooseSingleEntityForEffect(Collection<T> options, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) {
ApiType api = sa.getApi();
if (null == api) {
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
}
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, options, isOptional, targetedPlayer);
}
@Override
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title) {
ApiType api = sa.getApi();
if (null == api) {
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
}
return SpellApiToAi.Converter.get(api).chooseSingleSpellAbility(player, sa, spells);
}
@Override
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return getAi().confirmAction(sa, mode, message);
}
@Override
public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) {
return getAi().confirmStaticApplication(hostCard, affected, logic, message);
}
@Override
public boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map<String, String> triggerParams, boolean isMandatory) {
if (triggerParams.containsKey("DelayedTrigger")) {
//TODO: The only card with an optional delayed trigger is Shirei, Shizo's Caretaker,
// needs to be expanded when a more difficult cards comes up
return true;
}
// Store/replace target choices more properly to get this SA cleared.
TargetChoices tc = null;
TargetChoices subtc = null;
boolean storeChoices = sa.getTargetRestrictions() != null;
final SpellAbility sub = sa.getSubAbility();
boolean storeSubChoices = sub != null && sub.getTargetRestrictions() != null;
boolean ret = true;
if (storeChoices) {
tc = sa.getTargets();
sa.resetTargets();
}
if (storeSubChoices) {
subtc = sub.getTargets();
sub.resetTargets();
}
// There is no way this doTrigger here will have the same target as stored above
// So it's possible it's making a different decision here than will actually happen
if (!brains.doTrigger(sa, isMandatory)) {
ret = false;
}
if (storeChoices) {
sa.resetTargets();
sa.setTargets(tc);
}
if (storeSubChoices) {
sub.resetTargets();
sub.setTargets(subtc);
}
return ret;
}
@Override
public boolean getWillPlayOnFirstTurn(boolean isFirstGame) {
return true; // AI is brave :)
}
@Override
public List<Card> orderBlockers(Card attacker, List<Card> blockers) {
return AiBlockController.orderBlockers(attacker, blockers);
}
@Override
public List<Card> orderAttackers(Card blocker, List<Card> attackers) {
return AiBlockController.orderAttackers(blocker, attackers);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#reveal(java.lang.String, java.util.List)
*/
@Override
public void reveal(Collection<Card> cards, ZoneType zone, Player owner, String messagePrefix) {
// We don't know how to reveal cards to AI
}
@Override
public ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN) {
List<Card> toBottom = new ArrayList<Card>();
List<Card> toTop = new ArrayList<Card>();
for (Card c: topN) {
if (ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c)) {
toBottom.add(c);
}
else {
toTop.add(c);
}
}
// put the rest on top in random order
Collections.shuffle(toTop);
return ImmutablePair.of(toTop, toBottom);
}
@Override
public boolean willPutCardOnTop(Card c) {
return true; // AI does not know what will happen next (another clash or that would become his topdeck)
}
@Override
public List<Card> orderMoveToZoneList(List<Card> cards, ZoneType destinationZone) {
//TODO Add logic for AI ordering here
return cards;
}
@Override
public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> validCards, int min, int max) {
if (p == player) {
return brains.getCardsToDiscard(min, max, validCards, sa);
}
boolean isTargetFriendly = !p.isOpponentOf(player);
return isTargetFriendly
? ComputerUtil.getCardsToDiscardFromFriend(player, p, sa, validCards, min, max)
: ComputerUtil.getCardsToDiscardFromOpponent(player, p, sa, validCards, min, max);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#mayPlaySpellAbilityForFree(forge.card.spellability.SpellAbility)
*/
@Override
public void playSpellAbilityForFree(SpellAbility copySA, boolean mayChooseNewTargets) {
// Ai is known to set targets in doTrigger, so if it cannot choose new targets, we won't call canPlays
if (mayChooseNewTargets) {
if (copySA instanceof Spell) {
Spell spell = (Spell) copySA;
((PlayerControllerAi) player.getController()).getAi().canPlayFromEffectAI(spell, true, true);
}
else {
getAi().canPlaySa(copySA);
}
}
ComputerUtil.playSpellAbilityForFree(player, copySA);
}
@Override
public void playSpellAbilityNoStack(SpellAbility effectSA, boolean canSetupTargets) {
if (canSetupTargets)
brains.doTrigger(effectSA, true); // first parameter does not matter, since return value won't be used
ComputerUtil.playNoStack(player, effectSA, game);
}
@Override
public void playMiracle(SpellAbility miracle, Card card) {
getAi().chooseAndPlaySa(false, false, miracle);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseCardsToDelve(int, java.util.List)
*/
@Override
public List<Card> chooseCardsToDelve(int colorlessCost, List<Card> grave) {
return getAi().chooseCardsToDelve(colorlessCost, grave);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseTargets(forge.card.spellability.SpellAbility, forge.card.spellability.SpellAbilityStackInstance)
*/
@Override
public TargetChoices chooseNewTargetsFor(SpellAbility ability) {
// AI currently can't do this. But when it can it will need to be based on Ability API
return null;
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseCardsToDiscardUnlessType(int, java.util.List, java.lang.String, forge.card.spellability.SpellAbility)
*/
@Override
public List<Card> chooseCardsToDiscardUnlessType(int num, List<Card> hand, String uType, SpellAbility sa) {
final List<Card> cardsOfType = CardLists.getType(hand, uType);
if (!cardsOfType.isEmpty()) {
Card toDiscard = Aggregates.itemWithMin(cardsOfType, CardPredicates.Accessors.fnGetCmc);
return Lists.newArrayList(toDiscard);
}
return getAi().getCardsToDiscard(num, (String[])null, sa);
}
@Override
public Mana chooseManaFromPool(List<Mana> manaChoices) {
return manaChoices.get(0); // no brains used
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#ChooseSomeType(java.lang.String, java.util.List, java.util.List)
*/
@Override
public String chooseSomeType(String kindOfType, SpellAbility sa, List<String> validTypes, List<String> invalidTypes, boolean isOptional) {
String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), invalidTypes);
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty())
{
chosen = validTypes.get(0);
Log.warn("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen");
}
game.getAction().nofityOfValue(sa, null, "Computer picked: " + chosen, player);
return chosen;
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#confirmReplacementEffect(forge.card.replacement.ReplacementEffect, forge.card.spellability.SpellAbility, java.lang.String)
*/
@Override
public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) {
return brains.aiShouldRun(replacementEffect, effectSA);
}
@Override
public List<Card> getCardsToMulligan(boolean isCommander, Player firstPlayer) {
if (!ComputerUtil.wantMulligan(player)) {
return null;
}
if (!isCommander) {
return player.getCardsIn(ZoneType.Hand);
}
else {
return ComputerUtil.getPartialParisCandidates(player);
}
}
@Override
public void declareAttackers(Player attacker, Combat combat) {
brains.declareAttackers(attacker, combat);
}
@Override
public void declareBlockers(Player defender, Combat combat) {
brains.declareBlockersFor(defender, combat);
}
@Override
public SpellAbility chooseSpellAbilityToPlay() {
return brains.choooseSpellAbilityToPlay();
}
@Override
public void playChosenSpellAbility(SpellAbility sa)
{
// System.out.println("Playing sa: " + sa);
if ( sa == Ability.PLAY_LAND_SURROGATE )
player.playLand(sa.getHostCard(), false);
else
ComputerUtil.handlePlayingSpellAbility(player, sa, game);
}
@Override
public List<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard) {
return brains.getCardsToDiscard(numDiscard, (String[])null, null);
}
@Override
public List<Card> chooseCardsToRevealFromHand(int min, int max, List<Card> valid) {
int numCardsToReveal = Math.min(max, valid.size());
return numCardsToReveal == 0 ? Lists.<Card>newArrayList() : valid.subList(0, numCardsToReveal);
}
@Override
public boolean payManaOptional(Card c, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose) {
final Ability ability = new AbilityStatic(c, cost, null) { @Override public void resolve() {} };
ability.setActivatingPlayer(c.getController());
if (ComputerUtilCost.canPayCost(ability, c.getController())) {
ComputerUtil.playNoStack(c.getController(), ability, game);
return true;
}
return false;
}
@Override
public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) {
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
return brains.chooseSaToActivateFromOpeningHand(usableFromOpeningHand);
}
@Override
public int chooseNumber(SpellAbility sa, String title, int min, int max) {
return brains.chooseNumber(sa, title, min, max);
}
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
return brains.chooseNumber(sa, title, options, relatedPlayer);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseFlipResult(forge.Card, forge.game.player.Player, java.lang.String[], boolean)
*/
@Override
public boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call) {
if (call) {
// Win if possible
boolean result = false;
for (boolean s : results) {
if (s) {
result = s;
break;
}
}
return result;
} else {
// heads or tails, AI doesn't know which is better now
int i = MyRandom.getRandom().nextInt(results.length);
return results[i];
}
}
@Override
public Pair<SpellAbilityStackInstance, GameObject> chooseTarget(SpellAbility saSrc, List<Pair<SpellAbilityStackInstance, GameObject>> allTargets) {
// TODO Teach AI how to use Spellskite
return allTargets.get(0);
}
@Override
public void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value) {
// AI should take into consideration creature types, numbers and other information (mostly choices) arriving through this channel
}
@Override
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultVal) {
switch(kindOfChoice) {
case TapOrUntap: return true;
case UntapOrLeaveTapped: return defaultVal != null && defaultVal.booleanValue();
case UntapTimeVault: return false; // TODO Should AI skip his turn for time vault?
default:
return MyRandom.getRandom().nextBoolean();
}
}
@Override
public Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap) {
int i = MyRandom.getRandom().nextInt(options.size());
return choiceMap.get(options.get(i));
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseModeForAbility(forge.card.spellability.SpellAbility, java.util.List, int, int)
*/
@Override
public List<AbilitySub> chooseModeForAbility(SpellAbility sa, int min, int num) {
return CharmAi.chooseOptionsAi(sa, player, sa.isTrigger(), num, min, !player.equals(sa.getActivatingPlayer()));
}
@Override
public Pair<CounterType,String> chooseAndRemoveOrPutCounter(Card cardWithCounter) {
if (!cardWithCounter.hasCounters()) {
System.out.println("chooseCounterType was reached with a card with no counters on it. Consider filtering this card out earlier");
return null;
}
final Player controller = cardWithCounter.getController();
final List<Player> enemies = player.getOpponents();
final List<Player> allies = player.getAllies();
allies.add(player);
List<CounterType> countersToIncrease = new ArrayList<CounterType>();
List<CounterType> countersToDecrease = new ArrayList<CounterType>();
for (final CounterType counter : cardWithCounter.getCounters().keySet()) {
if ((!ComputerUtil.isNegativeCounter(counter, cardWithCounter) && allies.contains(controller))
|| (ComputerUtil.isNegativeCounter(counter, cardWithCounter) && enemies.contains(controller))) {
countersToIncrease.add(counter);
} else {
countersToDecrease.add(counter);
}
}
if (!countersToIncrease.isEmpty()) {
int random = MyRandom.getRandom().nextInt(countersToIncrease.size());
return new ImmutablePair<CounterType,String>(countersToIncrease.get(random),"Put");
}
else if (!countersToDecrease.isEmpty()) {
int random = MyRandom.getRandom().nextInt(countersToDecrease.size());
return new ImmutablePair<CounterType,String>(countersToDecrease.get(random),"Remove");
}
// shouldn't reach here but just in case, remove random counter
List<CounterType> countersOnCard = new ArrayList<CounterType>();
int random = MyRandom.getRandom().nextInt(countersOnCard.size());
return new ImmutablePair<CounterType,String>(countersOnCard.get(random),"Remove");
}
@Override
public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) {
final String c = ComputerUtilCard.getMostProminentColor(player.getCardsIn(ZoneType.Hand));
byte chosenColorMask = MagicColor.fromName(c);
if ((colors.getColor() & chosenColorMask) != 0) {
return chosenColorMask;
} else {
return Iterables.getFirst(colors, (byte)0);
}
}
@Override
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
// You may switch on sa.getApi() here and use sa.getParam("AILogic")
List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand));
if( sa.getApi() == ApiType.Mana )
hand.addAll(player.getCardsIn(ZoneType.Stack));
final String c = ComputerUtilCard.getMostProminentColor(hand);
byte chosenColorMask = MagicColor.fromName(c);
if ((colors.getColor() & chosenColorMask) != 0) {
return chosenColorMask;
} else {
return Iterables.getFirst(colors, MagicColor.WHITE);
}
}
@Override
public PaperCard chooseSinglePaperCard(SpellAbility sa, String message,
Predicate<PaperCard> cpp, String name) {
throw new UnsupportedOperationException("Should not be called for AI"); // or implement it if you know how
}
@Override
public List<String> chooseColors(String message, SpellAbility sa, int min, int max, List<String> options) {
return ComputerUtilCard.chooseColor(sa, min, max, options);
}
@Override
public CounterType chooseCounterType(Collection<CounterType> options, SpellAbility sa, String prompt) {
// may write a smarter AI if you need to (with calls to AI-clas for given API ability)
// TODO: ArsenalNut (06 Feb 12)computer needs
// better logic to pick a counter type and probably
// an initial target
// find first nonzero counter on target
return Iterables.getFirst(options, null);
}
@Override
public boolean confirmPayment(CostPart costPart, String prompt) {
return brains.confirmPayment(costPart); // AI is expected to know what it is paying for at the moment (otherwise add another parameter to this method)
}
@Override
public ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, HashMap<String, Object> runParams) {
// AI logic for choosing which replacement effect to apply
// happens here.
return possibleReplacers.get(0);
}
@Override
public String chooseProtectionType(String string, SpellAbility sa, List<String> choices) {
String choice = choices.get(0);
final String logic = sa.getParam("AILogic");
if (logic == null || logic.equals("MostProminentHumanCreatures")) {
List<Card> list = new ArrayList<Card>();
for (Player opp : player.getOpponents()) {
list.addAll(opp.getCreaturesInPlay());
}
if (list.isEmpty()) {
list = CardLists.filterControlledBy(game.getCardsInGame(), player.getOpponents());
}
if (!list.isEmpty()) {
choice = ComputerUtilCard.getMostProminentColor(list);
}
}
return choice;
}
@Override
public boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, List<Player> allPayers) {
final Card source = sa.getHostCard();
final Ability emptyAbility = new AbilityStatic(source, cost, sa.getTargetRestrictions()) { @Override public void resolve() { } };
emptyAbility.setActivatingPlayer(player);
if (ComputerUtilCost.willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers) && ComputerUtilCost.canPayCost(emptyAbility, player)) {
ComputerUtil.playNoStack(player, emptyAbility, game); // AI needs something to resolve to pay that cost
return true;
}
return false;
}
@Override
public void orderAndPlaySimultaneousSa(List<SpellAbility> activePlayerSAs) {
for (final SpellAbility sa : activePlayerSAs) {
prepareSingleSa(sa.getHostCard(),sa,true);
ComputerUtil.playStack(sa, player, game);
}
}
private void prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory){
if (sa.hasParam("TargetingPlayer")) {
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
targetingPlayer.getController().chooseTargetsFor(sa);
} else {
brains.doTrigger(sa, isMandatory);
}
}
@Override
public void playTrigger(Card host, WrappedAbility wrapperAbility, boolean isMandatory) {
prepareSingleSa(host, wrapperAbility, isMandatory);
ComputerUtil.playNoStack(wrapperAbility.getActivatingPlayer(), wrapperAbility, game);
}
@Override
public boolean playSaFromPlayEffect(SpellAbility tgtSA) {
boolean optional = tgtSA.hasParam("Optional");
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
Spell spell = (Spell) tgtSA;
if (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional) {
if (noManaCost) {
ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, game);
} else {
ComputerUtil.playStack(tgtSA, player, game);
}
} else
return false; // didn't play spell
}
return true;
}
@Override
public Map<GameEntity, CounterType> chooseProliferation() {
return brains.chooseProliferation();
}
@Override
public boolean chooseTargetsFor(SpellAbility currentAbility) {
return brains.doTrigger(currentAbility, true);
}
@Override
public boolean chooseCardsPile(SpellAbility sa, List<Card> pile1, List<Card> pile2, boolean faceUp) {
if (!faceUp) {
// AI will choose the first pile if it is larger or the same
// TODO Improve this to be slightly more random to not be so predictable
return pile1.size() >= pile2.size();
} else {
boolean allCreatures = Iterables.all(Iterables.concat(pile1, pile2), CardPredicates.Presets.CREATURES);
int cmc1 = allCreatures ? ComputerUtilCard.evaluateCreatureList(pile1) : ComputerUtilCard.evaluatePermanentList(pile1);
int cmc2 = allCreatures ? ComputerUtilCard.evaluateCreatureList(pile2) : ComputerUtilCard.evaluatePermanentList(pile2);
System.out.println("value:" + cmc1 + " " + cmc2);
// for now, this assumes that the outcome will be bad
// TODO: This should really have a ChooseLogic param to
// figure this out
return "Worst".equals(sa.getParam("AILogic")) ^ (cmc1 >= cmc2);
}
}
@Override
public void revealAnte(String message, Multimap<Player, PaperCard> removedAnteCards) {
// Ai won't understand that anyway
}
@Override
public Collection<? extends PaperCard> complainCardsCantPlayWell(Deck myDeck) {
return brains.complainCardsCantPlayWell(myDeck);
}
@Override
public List<Card> cheatShuffle(List<Card> list) {
return brains.getBooleanProperty(AiProps.CHEAT_WITH_MANA_ON_SHUFFLE) ? brains.cheatShuffle(list) : list;
}
@Override
public CardShields chooseRegenerationShield(Card c) {
return Iterables.getFirst(c.getShield(), null);
}
@Override
public List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses) {
// TODO AI takes all by default
return losses;
}
@Override
public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */ ) {
// TODO Auto-generated method stub
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, false, 0);
return ComputerUtilMana.payManaCost(cost, sa, player);
}
@Override
public Map<Card, ManaCostShard> chooseCardsForConvoke(SpellAbility sa, ManaCost manaCost,
List<Card> untappedCreats) {
// TODO: AI to choose a creature to tap would go here
// Probably along with deciding how many creatures to tap
return new HashMap<Card, ManaCostShard>();
}
@Override
public String chooseCardName(SpellAbility sa, Predicate<PaperCard> cpp, String valid, String message) {
if (sa.hasParam("AILogic")) {
final String logic = sa.getParam("AILogic");
if (logic.equals("MostProminentInComputerDeck")) {
return ComputerUtilCard.getMostProminentCardName(player.getCardsIn(ZoneType.Library));
} else if (logic.equals("MostProminentInHumanDeck")) {
return ComputerUtilCard.getMostProminentCardName(player.getOpponent().getCardsIn(ZoneType.Library));
} else if (logic.equals("BestCreatureInComputerDeck")) {
return ComputerUtilCard.getBestCreatureAI(player.getCardsIn(ZoneType.Library)).getName();
} else if (logic.equals("RandomInComputerDeck")) {
return Aggregates.random(player.getCardsIn(ZoneType.Library)).getName();
}
} else {
List<Card> list = CardLists.filterControlledBy(game.getCardsInGame(), player.getOpponent());
list = CardLists.filter(list, Predicates.not(Presets.LANDS));
if (!list.isEmpty()) {
return list.get(0).getName();
}
}
return "Morphling";
}
@Override
public Card chooseSingleCardForZoneChange(ZoneType destination,
List<ZoneType> origin, SpellAbility sa, List<Card> fetchList,
String selectPrompt, boolean b, Player decider) {
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
}
}

View File

@@ -0,0 +1,175 @@
package forge.ai;
import com.google.common.collect.Iterables;
import forge.game.GameEntity;
import forge.game.ability.SaTargetRoutines;
import forge.game.card.Card;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import java.util.Collection;
import java.util.List;
public abstract class SpellAbilityAi extends SaTargetRoutines {
public final boolean canPlayAIWithSubs(final Player aiPlayer, final SpellAbility sa) {
if (!canPlayAI(aiPlayer, sa)) {
return false;
}
final AbilitySub subAb = sa.getSubAbility();
return subAb == null || chkDrawbackWithSubs(aiPlayer, subAb);
}
protected abstract boolean canPlayAI(final Player aiPlayer, final SpellAbility sa);
public final boolean doTriggerAI(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
if (!ComputerUtilCost.canPayCost(sa, aiPlayer) && !mandatory) {
return false;
}
return doTriggerNoCostWithSubs(aiPlayer, sa, mandatory);
}
public final boolean doTriggerNoCostWithSubs(final Player aiPlayer, final SpellAbility sa, final boolean mandatory)
{
if (!doTriggerAINoCost(aiPlayer, sa, mandatory)) {
return false;
}
final AbilitySub subAb = sa.getSubAbility();
return subAb == null || chkDrawbackWithSubs(aiPlayer, subAb) || mandatory;
}
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
if (canPlayAI(aiPlayer, sa)) {
return true;
}
if (mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player opp = aiPlayer.getOpponent();
if (tgt != null) {
if (opp.canBeTargetedBy(sa)) {
sa.resetTargets();
sa.getTargets().add(opp);
} else if (mandatory) {
if (aiPlayer.canBeTargetedBy(sa)) {
sa.resetTargets();
sa.getTargets().add(opp);
} else {
return false;
}
} else {
return false;
}
}
return true;
}
return false;
}
public boolean chkAIDrawback(final SpellAbility sa, final Player aiPlayer) {
return true;
}
/**
* <p>
* isSorcerySpeed.
* </p>
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
*/
protected static boolean isSorcerySpeed(final SpellAbility sa) {
return ( sa.isSpell() && sa.getHostCard().isSorcery() )
|| ( sa.isAbility() && sa.getRestrictions().isSorcerySpeed() );
}
/**
* <p>
* playReusable.
* </p>
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
*/
protected static boolean playReusable(final Player ai, final SpellAbility sa) {
// TODO probably also consider if winter orb or similar are out
if (sa.getPayCosts() == null || sa instanceof AbilitySub) {
return true; // This is only true for Drawbacks and triggers
}
if (ComputerUtil.playImmediately(ai, sa)) {
return true;
}
if (!sa.getPayCosts().isReusuableResource()) {
return false;
}
if (sa.getRestrictions().getPlaneswalker() && ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
return true;
}
if (sa.isTrigger()) {
return true;
}
if (sa.isSpell() && !sa.isBuyBackAbility()) {
return false;
}
PhaseHandler phase = ai.getGame().getPhaseHandler();
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);
}
/**
* TODO: Write javadoc for this method.
* @param ai
* @param subAb
* @return
*/
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
final AbilitySub subAb = ab.getSubAbility();
return SpellApiToAi.Converter.get(ab.getApi()).chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
}
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
System.err.println("Warning: default (ie. inherited from base class) implementation of confirmAction is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return true;
}
@SuppressWarnings("unchecked")
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
T firstOption = Iterables.getFirst(options, null);
if( firstOption instanceof Card)
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
else if ( firstOption instanceof Player)
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
else
return null;
}
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleSpellAbility is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return spells.get(0);
}
protected Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleEntity is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Collection<Player> options) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleEntity is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
}

View File

@@ -0,0 +1,153 @@
package forge.ai;
import java.util.EnumMap;
import forge.ai.ability.*;
import forge.game.ability.ApiType;
import forge.util.ReflectionUtil;
public enum SpellApiToAi {
Converter;
private final static EnumMap<ApiType, Class<? extends SpellAbilityAi>> apiToClass = new EnumMap<>(ApiType.class);
private final EnumMap<ApiType, SpellAbilityAi> apiToInstance = new EnumMap<>(ApiType.class);
static {
apiToClass.put(ApiType.Abandon, AlwaysPlayAi.class);
apiToClass.put(ApiType.AddOrRemoveCounter, CountersPutOrRemoveAi.class);
apiToClass.put(ApiType.AddPhase, AddPhaseAi.class);
apiToClass.put(ApiType.AddTurn, AddTurnAi.class);
apiToClass.put(ApiType.Animate, AnimateAi.class);
apiToClass.put(ApiType.AnimateAll, AnimateAllAi.class);
apiToClass.put(ApiType.Attach, AttachAi.class);
apiToClass.put(ApiType.Balance, BalanceAi.class);
apiToClass.put(ApiType.BecomesBlocked, BecomesBlockedAi.class);
apiToClass.put(ApiType.Bond, BondAi.class);
apiToClass.put(ApiType.ChangeTargets, ChangeTargetsAi.class);
apiToClass.put(ApiType.ChangeZone, ChangeZoneAi.class);
apiToClass.put(ApiType.ChangeZoneAll, ChangeZoneAllAi.class);
apiToClass.put(ApiType.Charm, CharmAi.class);
apiToClass.put(ApiType.ChooseCard, ChooseCardAi.class);
apiToClass.put(ApiType.ChooseColor, ChooseColorAi.class);
apiToClass.put(ApiType.ChooseNumber, CannotPlayAi.class);
apiToClass.put(ApiType.ChoosePlayer, ChoosePlayerAi.class);
apiToClass.put(ApiType.ChooseSource, ChooseSourceAi.class);
apiToClass.put(ApiType.ChooseType, ChooseTypeAi.class);
apiToClass.put(ApiType.Clash, ClashAi.class);
apiToClass.put(ApiType.Cleanup, AlwaysPlayAi.class);
apiToClass.put(ApiType.Clone, CloneAi.class);
apiToClass.put(ApiType.CopyPermanent, CopyPermanentAi.class);
apiToClass.put(ApiType.CopySpellAbility, CanPlayAsDrawbackAi.class);
apiToClass.put(ApiType.ControlPlayer, CannotPlayAi.class);
apiToClass.put(ApiType.ControlSpell, CannotPlayAi.class);
apiToClass.put(ApiType.Counter, CounterAi.class);
apiToClass.put(ApiType.DamageAll, DamageAllAi.class);
apiToClass.put(ApiType.DealDamage, DamageDealAi.class);
apiToClass.put(ApiType.Debuff, DebuffAi.class);
apiToClass.put(ApiType.DebuffAll, DebuffAllAi.class);
apiToClass.put(ApiType.DeclareCombatants, CannotPlayAi.class);
apiToClass.put(ApiType.DelayedTrigger, DelayedTriggerAi.class);
apiToClass.put(ApiType.Destroy, DestroyAi.class);
apiToClass.put(ApiType.DestroyAll, DestroyAllAi.class);
apiToClass.put(ApiType.Dig, DigAi.class);
apiToClass.put(ApiType.DigUntil, DigUntilAi.class);
apiToClass.put(ApiType.Discard, DiscardAi.class);
apiToClass.put(ApiType.DrainMana, DrainManaAi.class);
apiToClass.put(ApiType.Draw, DrawAi.class);
apiToClass.put(ApiType.EachDamage, DamageEachAi.class);
apiToClass.put(ApiType.Effect, EffectAi.class);
apiToClass.put(ApiType.Encode, EncodeAi.class);
apiToClass.put(ApiType.EndTurn, EndTurnAi.class);
apiToClass.put(ApiType.ExchangeLife, LifeExchangeAi.class);
apiToClass.put(ApiType.ExchangeControl, ControlExchangeAi.class);
apiToClass.put(ApiType.ExchangePower, PowerExchangeAi.class);
apiToClass.put(ApiType.ExchangeZone, ZoneExchangeAi.class);
apiToClass.put(ApiType.Fight, FightAi.class);
apiToClass.put(ApiType.FlipACoin, FlipACoinAi.class);
apiToClass.put(ApiType.Fog, FogAi.class);
apiToClass.put(ApiType.GainControl, ControlGainAi.class);
apiToClass.put(ApiType.GainLife, LifeGainAi.class);
apiToClass.put(ApiType.GainOwnership, CannotPlayAi.class);
apiToClass.put(ApiType.GenericChoice, ChooseGenericEffectAi.class);
apiToClass.put(ApiType.LoseLife, LifeLoseAi.class);
apiToClass.put(ApiType.LosesGame, GameLossAi.class);
apiToClass.put(ApiType.Mana, ManaEffectAi.class);
apiToClass.put(ApiType.ManaReflected, CannotPlayAi.class);
apiToClass.put(ApiType.Mill, MillAi.class);
apiToClass.put(ApiType.MoveCounter, CountersMoveAi.class);
apiToClass.put(ApiType.MultiplePiles, CannotPlayAi.class);
apiToClass.put(ApiType.MustAttack, MustAttackAi.class);
apiToClass.put(ApiType.MustBlock, MustBlockAi.class);
apiToClass.put(ApiType.NameCard, ChooseCardNameAi.class);
apiToClass.put(ApiType.PeekAndReveal, PeekAndRevealAi.class);
apiToClass.put(ApiType.PermanentCreature, PermanentCreatureAi.class);
apiToClass.put(ApiType.PermanentNoncreature, PermanentNoncreatureAi.class);
apiToClass.put(ApiType.Phases, PhasesAi.class);
apiToClass.put(ApiType.Planeswalk, AlwaysPlayAi.class);
apiToClass.put(ApiType.Play, PlayAi.class);
apiToClass.put(ApiType.Poison, PoisonAi.class);
apiToClass.put(ApiType.PreventDamage, DamagePreventAi.class);
apiToClass.put(ApiType.PreventDamageAll, DamagePreventAllAi.class);
apiToClass.put(ApiType.Proliferate, CountersProliferateAi.class);
apiToClass.put(ApiType.Protection, ProtectAi.class);
apiToClass.put(ApiType.ProtectionAll, ProtectAllAi.class);
apiToClass.put(ApiType.Pump, PumpAi.class);
apiToClass.put(ApiType.PumpAll, PumpAllAi.class);
apiToClass.put(ApiType.PutCounter, CountersPutAi.class);
apiToClass.put(ApiType.PutCounterAll, CountersPutAllAi.class);
apiToClass.put(ApiType.RearrangeTopOfLibrary, RearrangeTopOfLibraryAi.class);
apiToClass.put(ApiType.Regenerate, RegenerateAi.class);
apiToClass.put(ApiType.RegenerateAll, RegenerateAllAi.class);
apiToClass.put(ApiType.RemoveCounter, CountersRemoveAi.class);
apiToClass.put(ApiType.RemoveCounterAll, CannotPlayAi.class);
apiToClass.put(ApiType.RemoveFromCombat, RemoveFromCombatAi.class);
apiToClass.put(ApiType.ReorderZone, AlwaysPlayAi.class);
apiToClass.put(ApiType.Repeat, RepeatAi.class);
apiToClass.put(ApiType.RepeatEach, RepeatEachAi.class);
apiToClass.put(ApiType.RestartGame, RestartGameAi.class);
apiToClass.put(ApiType.Reveal, RevealAi.class);
apiToClass.put(ApiType.RevealHand, RevealHandAi.class);
apiToClass.put(ApiType.RollPlanarDice, RollPlanarDiceAi.class);
apiToClass.put(ApiType.RunSVarAbility, AlwaysPlayAi.class);
apiToClass.put(ApiType.Sacrifice, SacrificeAi.class);
apiToClass.put(ApiType.SacrificeAll, SacrificeAllAi.class);
apiToClass.put(ApiType.Scry, ScryAi.class);
apiToClass.put(ApiType.SetInMotion, AlwaysPlayAi.class);
apiToClass.put(ApiType.SetLife, LifeSetAi.class);
apiToClass.put(ApiType.SetState, SetStateAi.class);
apiToClass.put(ApiType.Shuffle, ShuffleAi.class);
apiToClass.put(ApiType.SkipTurn, CannotPlayAi.class);
apiToClass.put(ApiType.StoreSVar, StoreSVarAi.class);
apiToClass.put(ApiType.Tap, TapAi.class);
apiToClass.put(ApiType.TapAll, TapAllAi.class);
apiToClass.put(ApiType.TapOrUntap, TapOrUntapAi.class);
apiToClass.put(ApiType.TapOrUntapAll, TapOrUntapAllAi.class);
apiToClass.put(ApiType.Token, TokenAi.class);
apiToClass.put(ApiType.TwoPiles, TwoPilesAi.class);
apiToClass.put(ApiType.Unattach, CannotPlayAi.class);
apiToClass.put(ApiType.UnattachAll, UnattachAllAi.class);
apiToClass.put(ApiType.Untap, UntapAi.class);
apiToClass.put(ApiType.UntapAll, UntapAllAi.class);
apiToClass.put(ApiType.WinsGame, GameWinAi.class);
apiToClass.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class);
apiToClass.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class);
apiToClass.put(ApiType.InternalHaunt, HauntAi.class);
}
public SpellAbilityAi get(ApiType api) {
SpellAbilityAi result = apiToInstance.get(api);
if( null == result ) {
Class<? extends SpellAbilityAi> clz = apiToClass.get(api);
if(null == clz) {
System.err.println("No AI assigned for API: " + api);
clz = CannotPlayAi.class;
}
result = ReflectionUtil.makeDefaultInstanceOf(clz);
apiToInstance.put(api, result);
}
return result;
}
}

View File

@@ -0,0 +1,18 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
/**
* TODO: Write javadoc for this type.
*
*/
public class AddPhaseAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return false;
}
}

View File

@@ -15,15 +15,15 @@
* 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.ability.ai;
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.List;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.player.Player;
/**
* <p>
* AbilityFactory_Turns class.
@@ -50,14 +50,14 @@ public class AddTurnAi extends SpellAbilityAi {
break;
}
}
if (!sa.getTargetRestrictions().isMinTargetsChosen(sa.getSourceCard(), sa) && sa.canTarget(opp)) {
if (!sa.getTargetRestrictions().isMinTargetsChosen(sa.getHostCard(), sa) && sa.canTarget(opp)) {
sa.getTargets().add(opp);
} else {
return false;
}
}
} else {
final List<Player> tgtPlayers = AbilityUtils.getDefinedPlayers(sa.getSourceCard(), sa.getParam("Defined"), sa);
final List<Player> tgtPlayers = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
for (final Player p : tgtPlayers) {
if (p.isOpponentOf(ai) && !mandatory) {
return false;

View File

@@ -0,0 +1,16 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class AlwaysPlayAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return true;
}
}

View File

@@ -1,21 +1,20 @@
package forge.card.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.collect.Iterables;
import forge.Card;
import forge.CardPredicates;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardPredicates;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
/**
* <p>
* AbilityFactoryAnimate class.
@@ -33,7 +32,7 @@ public class AnimateAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Game game = aiPlayer.getGame();
final PhaseHandler ph = game.getPhaseHandler();
@@ -141,11 +140,11 @@ public class AnimateAi extends SpellAbilityAi {
* animateTriggerAI.
* </p>
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/
@@ -172,9 +171,9 @@ public class AnimateAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
*/
private boolean animateTgtAI(final SpellAbility sa) {

View File

@@ -0,0 +1,19 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class AnimateAllAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return false;
} // end animateAllCanPlayAI()
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
return false;
}
} // end class AbilityFactoryAnimate

View File

@@ -1,42 +1,27 @@
package forge.card.ability.ai;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.CardUtil;
import forge.ITargetable;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType;
import forge.card.ability.SpellAbilityAi;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.card.staticability.StaticAbility;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.ai.*;
import forge.game.GameObject;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*;
import forge.game.combat.CombatUtil;
import forge.game.cost.Cost;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.util.MyRandom;
import java.util.*;
public class AttachAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -46,7 +31,7 @@ public class AttachAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (abCost != null) {
// AI currently disabled for these costs
@@ -169,7 +154,7 @@ public class AttachAi extends SpellAbilityAi {
String type = "";
for (final StaticAbility stAb : attachSource.getStaticAbilities()) {
final HashMap<String, String> stab = stAb.getMapParams();
final Map<String, String> stab = stAb.getMapParams();
if (stab.get("Mode").equals("Continuous") && stab.containsKey("AddType")) {
type = stab.get("AddType");
}
@@ -334,7 +319,7 @@ public class AttachAi extends SpellAbilityAi {
final Card attachSource) {
// AI For choosing a Card to Animate.
List<Card> betterList = CardLists.getNotType(list, "Creature");
if (sa.getSourceCard().getName().equals("Animate Artifact")) {
if (sa.getHostCard().getName().equals("Animate Artifact")) {
betterList = CardLists.filter(betterList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -400,7 +385,7 @@ public class AttachAi extends SpellAbilityAi {
// I know this isn't much better than Hardcoding, but some cards need it for now
final Player ai = sa.getActivatingPlayer();
Card chosen = null;
if ("Guilty Conscience".equals(sa.getSourceCard().getName())) {
if ("Guilty Conscience".equals(sa.getHostCard().getName())) {
List<Card> aiStuffies = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -626,12 +611,12 @@ public class AttachAi extends SpellAbilityAi {
*/
@Override
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
final Card card = sa.getSourceCard();
final Card card = sa.getHostCard();
// Check if there are any valid targets
List<ITargetable> targets = new ArrayList<ITargetable>();
List<GameObject> targets = new ArrayList<GameObject>();
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) {
targets = AbilityUtils.getDefinedObjects(sa.getSourceCard(), sa.getParam("Defined"), sa);
targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
} else {
AttachAi.attachPreference(sa, tgt, mandatory);
targets = sa.getTargets().getTargets();
@@ -703,7 +688,7 @@ public class AttachAi extends SpellAbilityAi {
* @return true, if successful
*/
private static boolean attachPreference(final SpellAbility sa, final TargetRestrictions tgt, final boolean mandatory) {
ITargetable o;
GameObject o;
if (tgt.canTgtPlayer()) {
o = attachToPlayerAIPreferences(sa.getActivatingPlayer(), sa, mandatory);
} else {
@@ -778,7 +763,7 @@ public class AttachAi extends SpellAbilityAi {
for (Card target : list) {
for (Trigger t : target.getTriggers()) {
if (t.getMode() == TriggerType.SpellCast) {
final HashMap<String, String> params = t.getMapParams();
final Map<String, String> params = t.getMapParams();
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))) {
magnetList.add(target);
break;
@@ -945,7 +930,7 @@ public class AttachAi extends SpellAbilityAi {
*/
private static Card attachToCardAIPreferences(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card attachSource = sa.getSourceCard();
final Card attachSource = sa.getHostCard();
// TODO AttachSource is currently set for the Source of the Spell, but
// at some point can support attaching a different card
@@ -1171,6 +1156,8 @@ public class AttachAi extends SpellAbilityAi {
if (card.hasKeyword("Shroud") || card.hasKeyword("Hexproof")) {
return false;
}
} else if (keyword.equals("Defender")) {
return false;
}
return true;
}
@@ -1234,12 +1221,12 @@ public class AttachAi extends SpellAbilityAi {
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
return attachToCardAIPreferences(ai, sa, true);
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, List<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Collection<Player> options) {
return attachToPlayerAIPreferences(ai, sa, true);
}
}

View File

@@ -1,17 +1,16 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import org.apache.commons.lang.math.RandomUtils;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
public class BalanceAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -48,6 +47,6 @@ public class BalanceAi extends SpellAbilityAi {
diff += 0.5 * (humHand.size() - compHand.size());
// Larger differential == more chance to actually cast this spell
return diff > 2 && RandomUtils.nextInt(100) < diff*10;
return diff > 2 && MyRandom.getRandom().nextInt(100) < diff*10;
}
}

View File

@@ -0,0 +1,77 @@
package forge.ai.ability;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
public class BecomesBlockedAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Game game = aiPlayer.getGame();
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| !game.getPhaseHandler().getPlayerTurn().isOpponentOf(aiPlayer)) {
return false;
}
if (tgt != null) {
sa.resetTargets();
List<Card> list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.filterControlledBy(list, aiPlayer.getOpponents());
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source);
list = CardLists.getTargetableCards(list, sa);
list = CardLists.getNotKeyword(list, "Trample");
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
Card choice = null;
if (list.isEmpty()) {
return false;
}
choice = ComputerUtilCard.getBestCreatureAI(list);
if (choice == null) { // can't find anything left
return false;
}
list.remove(choice);
sa.getTargets().add(choice);
}
}
return true;
}
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
// TODO - implement AI
return false;
}
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
*/
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
boolean chance;
// TODO - implement AI
chance = false;
return chance;
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.ai.ability;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.Collection;
/**
* <p>
* AbilityFactoryBond class.
* </p>
*
* @author Forge
* @version $Id: AbilityFactoryBond.java 15090 2012-04-07 12:50:31Z Max mtg $
*/
public final class BondAi extends SpellAbilityAi {
/**
* <p>
* bondCanPlayAI.
* </p>
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param af
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return true;
} // end bondCanPlayAI()
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
return ComputerUtilCard.getBestCreatureAI(options);
}
}

View File

@@ -1,12 +1,12 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.List;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.player.Player;
public class CanPlayAsDrawbackAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -22,11 +22,11 @@ public class CanPlayAsDrawbackAi extends SpellAbilityAi {
* copySpellTriggerAI.
* </p>
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/

View File

@@ -1,9 +1,9 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class CannotPlayAi extends SpellAbilityAi {
/* (non-Javadoc)

View File

@@ -1,8 +1,8 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class ChangeTargetsAi extends SpellAbilityAi {

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,22 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class ChangeZoneAllAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -26,7 +26,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
// Change Zone All, can be any type moving from one zone to another
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
@@ -159,9 +159,9 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
* changeZoneAllPlayDrawbackAI.
* </p>
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/

View File

@@ -1,17 +1,21 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import forge.card.ability.SpellAbilityAi;
import forge.card.ability.effects.CharmEffect;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.ai.AiController;
import forge.ai.AiPlayDecision;
import forge.ai.PlayerControllerAi;
import forge.ai.SpellAbilityAi;
import forge.game.ability.effects.CharmEffect;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.util.Aggregates;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
public class CharmAi extends SpellAbilityAi {
@Override
@@ -44,18 +48,18 @@ public class CharmAi extends SpellAbilityAi {
return choices.subList(1, choices.size());
}
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
for (int i = 0; i < num; i++) {
AbilitySub thisPick = null;
for (SpellAbility sub : choices) {
sub.setActivatingPlayer(ai);
if (!playNow && sub.canPlayAI()) {
if (!playNow && AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
thisPick = (AbilitySub) sub;
choices.remove(sub);
playNow = true;
break;
}
if ((playNow || i < num - 1) && sub.doTrigger(false, ai)) {
if ((playNow || i < num - 1) && aic.doTrigger(sub, false)) {
thisPick = (AbilitySub) sub;
choices.remove(sub);
break;
@@ -70,7 +74,7 @@ public class CharmAi extends SpellAbilityAi {
AbilitySub thisPick = null;
for (SpellAbility sub : choices) {
sub.setActivatingPlayer(ai);
if (sub.doTrigger(true, ai)) {
if (aic.doTrigger(sub, true)) {
thisPick = (AbilitySub) sub;
choices.remove(sub);
break;
@@ -88,7 +92,7 @@ public class CharmAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, List<Player> opponents) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Collection<Player> opponents) {
return Aggregates.random(opponents);
}

View File

@@ -1,24 +1,24 @@
package forge.card.ability.ai;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates.Presets;
import forge.game.combat.Combat;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates.Presets;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.Game;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.game.combat.Combat;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
public class ChooseCardAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -26,7 +26,7 @@ public class ChooseCardAi extends SpellAbilityAi {
*/
@Override
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
final Card host = sa.getSourceCard();
final Card host = sa.getHostCard();
final Game game = ai.getGame();
final TargetRestrictions tgt = sa.getTargetRestrictions();
@@ -99,8 +99,8 @@ public class ChooseCardAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
final Card host = sa.getSourceCard();
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
final Card host = sa.getHostCard();
final String logic = sa.getParam("AILogic");
Card choice = null;
if (logic == null) {
@@ -159,6 +159,27 @@ public class ChooseCardAi extends SpellAbilityAi {
Collections.reverse(creats);
choice = creats.get(0);
}
} else if ("TangleWire".equals(logic)) {
List<Card> betterList = CardLists.filter(options, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (c.isCreature()) {
return false;
}
for (SpellAbility sa : c.getAllSpellAbilities()) {
if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
return false;
}
}
return true;
}
});
System.out.println("Tangle Wire" + options + " - " + betterList);
if (!betterList.isEmpty()) {
choice = betterList.get(0);
} else {
choice = ComputerUtilCard.getWorstPermanentAI(options, false, false, false, false);
}
} else {
choice = ComputerUtilCard.getBestAI(options);
}

View File

@@ -1,19 +1,19 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.Card;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
public class ChooseCardNameAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
Card source = sa.getSourceCard();
Card source = sa.getHostCard();
if (sa.hasParam("AILogic")) {
// Don't tap creatures that may be able to block
if (ComputerUtil.waitForBlocking(sa)) {

View File

@@ -1,8 +1,8 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.MyRandom;
public class ChooseColorAi extends SpellAbilityAi {

View File

@@ -0,0 +1,36 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.Aggregates;
import java.util.List;
/**
* TODO: Write javadoc for this type.
*
*/
public class ChooseGenericEffectAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return false;
}
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
return canPlayAI(aiPlayer, sa);
}
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
if ("Random".equals(sa.getParam("AILogic"))) {
return Aggregates.random(spells);
} else {
return spells.get(0);
}
}
}

View File

@@ -0,0 +1,87 @@
package forge.ai.ability;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.Collection;
import java.util.List;
public class ChoosePlayerAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
return true;
}
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
return canPlayAI(ai, sa);
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
return canPlayAI(ai, sa);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Collection<Player> choices) {
Player chosen = null;
if ("Curse".equals(sa.getParam("AILogic"))) {
for (Player pc : choices) {
if (pc.isOpponentOf(ai)) {
chosen = pc;
break;
}
}
if (chosen == null) {
chosen = Iterables.getFirst(choices, null);
System.out.println("No good curse choices. Picking first available: " + chosen);
}
} else if ("Pump".equals(sa.getParam("AILogic"))) {
chosen = choices.contains(ai) ? ai : Iterables.getFirst(choices, null);
} else if ("BestAllyBoardPosition".equals(sa.getParam("AILogic"))) {
List<Player> prefChoices = Lists.newArrayList(choices);
prefChoices.removeAll(ai.getOpponents());
if (!prefChoices.isEmpty()) {
chosen = ComputerUtil.evaluateBoardPosition(prefChoices);
}
if (chosen == null) {
chosen = Iterables.getFirst(choices, null);
System.out.println("No good curse choices. Picking first available: " + chosen);
}
} else if ("MostCardsInHand".equals(sa.getParam("AILogic"))) {
int cardsInHand = 0;
for (final Player p : choices) {
int hand = p.getCardsIn(ZoneType.Hand).size();
if (hand >= cardsInHand) {
chosen = p;
cardsInHand = hand;
}
}
} else if ("LeastCreatures".equals(sa.getParam("AILogic"))) {
int creats = 50;
for (final Player p : choices) {
int curr = p.getCreaturesInPlay().size();
if (curr <= creats) {
chosen = p;
creats = curr;
}
}
} else {
System.out.println("Default player choice logic.");
chosen = choices.contains(ai) ? ai : Iterables.getFirst(choices, null);
}
return chosen;
}
}

View File

@@ -0,0 +1,186 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.GameObject;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.Collection;
import java.util.List;
public class ChooseSourceAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
// TODO: AI Support! Currently this is copied from AF ChooseCard.
// When implementing AI, I believe AI also needs to be made aware of the damage sources chosen
// to be prevented (e.g. so the AI doesn't attack with a creature that will not deal any damage
// to the player because a CoP was pre-activated on it - unless, of course, there's another
// possible reason to attack with that creature).
final Card host = sa.getHostCard();
final Cost abCost = sa.getPayCosts();
final Card source = sa.getHostCard();
if (abCost != null) {
// AI currently disabled for these costs
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
return false;
}
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();
if (sa.canTarget(ai.getOpponent())) {
sa.getTargets().add(ai.getOpponent());
} else {
return false;
}
}
if (sa.hasParam("AILogic")) {
final Game game = ai.getGame();
if (sa.getParam("AILogic").equals("NeedsPrevention")) {
if (!game.getStack().isEmpty()) {
final SpellAbility topStack = game.getStack().peekAbility();
if (sa.hasParam("Choices") && !topStack.getHostCard().isValid(sa.getParam("Choices"), ai, source)) {
return false;
}
final ApiType threatApi = topStack.getApi();
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
return false;
}
final Card threatSource = topStack.getHostCard();
List<? extends GameObject> objects = getTargets(topStack);
if (!topStack.usesTargeting() && topStack.hasParam("ValidPlayers") && !topStack.hasParam("Defined")) {
objects = AbilityUtils.getDefinedPlayers(threatSource, topStack.getParam("ValidPlayers"), topStack);
}
if (!objects.contains(ai) || topStack.hasParam("NoPrevention")) {
return false;
}
int dmg = AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) {
return false;
}
return true;
}
if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
return false;
}
List<Card> choices = game.getCardsIn(ZoneType.Battlefield);
if (sa.hasParam("Choices")) {
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
}
final Combat combat = game.getCombat();
choices = CardLists.filter(choices, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat) > 0;
}
});
if (choices.isEmpty()) {
return false;
}
}
}
return true;
}
@Override
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
final Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame();
if (!game.getStack().isEmpty()) {
Card choseCard = chooseCardOnStack(sa, ai, game);
if (choseCard != null) {
return choseCard;
}
}
final Combat combat = game.getCombat();
List<Card> permanentSources = CardLists.filter(options, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (c == null || c.getZone() == null || c.getZone().getZoneType() != ZoneType.Battlefield
|| combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat) > 0;
}
});
return ComputerUtilCard.getBestCreatureAI(permanentSources);
} else {
return ComputerUtilCard.getBestAI(options);
}
}
private Card chooseCardOnStack(SpellAbility sa, Player ai, Game game) {
for (SpellAbilityStackInstance si : game.getStack()) {
final Card source = si.getSourceCard();
final SpellAbility abilityOnStack = si.getSpellAbility();
if (sa.hasParam("Choices") && !abilityOnStack.getHostCard().isValid(sa.getParam("Choices"), ai, sa.getHostCard())) {
continue;
}
final ApiType threatApi = abilityOnStack.getApi();
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
continue;
}
List<? extends GameObject> objects = getTargets(abilityOnStack);
if (!abilityOnStack.usesTargeting() && !abilityOnStack.hasParam("Defined") && abilityOnStack.hasParam("ValidPlayers"))
objects = AbilityUtils.getDefinedPlayers(source, abilityOnStack.getParam("ValidPlayers"), abilityOnStack);
if (!objects.contains(ai) || abilityOnStack.hasParam("NoPrevention")) {
continue;
}
int dmg = AbilityUtils.calculateAmount(source, abilityOnStack.getParam("NumDmg"), abilityOnStack);
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false) <= 0) {
continue;
}
return source;
}
return null;
}
}

View File

@@ -1,9 +1,9 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class ChooseTypeAi extends SpellAbilityAi {
@Override
@@ -21,7 +21,7 @@ public class ChooseTypeAi extends SpellAbilityAi {
sa.resetTargets();
sa.getTargets().add(ai);
} else {
for (final Player p : AbilityUtils.getDefinedPlayers(sa.getSourceCard(), sa.getParam("Defined"), sa)) {
for (final Player p : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa)) {
if (p.isOpponentOf(ai) && !mandatory) {
return false;
}

View File

@@ -1,10 +1,10 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
public class ClashAi extends SpellAbilityAi {

View File

@@ -1,24 +1,24 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.List;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import java.util.List;
public class CloneAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Game game = source.getGame();
boolean useAbility = true;
@@ -129,9 +129,9 @@ public class CloneAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean.
*/
private boolean cloneTgtAI(final SpellAbility sa) {

View File

@@ -1,21 +1,20 @@
package forge.card.ability.ai;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class ControlExchangeAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -29,7 +28,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
sa.resetTargets();
List<Card> list =
CardLists.getValidCards(ai.getOpponent().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getSourceCard());
CardLists.getValidCards(ai.getOpponent().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard());
// AI won't try to grab cards that are filtered out of AI decks on
// purpose
list = CardLists.filter(list, new Predicate<Card>() {
@@ -41,10 +40,10 @@ public class ControlExchangeAi extends SpellAbilityAi {
});
object1 = ComputerUtilCard.getBestAI(list);
if (sa.hasParam("Defined")) {
object2 = AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa).get(0);
} else if (tgt.getMinTargets(sa.getSourceCard(), sa) > 1) {
object2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);
} else if (tgt.getMinTargets(sa.getHostCard(), sa) > 1) {
List<Card> list2 = ai.getCardsIn(ZoneType.Battlefield);
list2 = CardLists.getValidCards(list2, tgt.getValidTgts(), ai, sa.getSourceCard());
list2 = CardLists.getValidCards(list2, tgt.getValidTgts(), ai, sa.getHostCard());
object2 = ComputerUtilCard.getWorstAI(list2);
sa.getTargets().add(object2);
}

View File

@@ -15,27 +15,27 @@
* 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.ability.ai;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.combat.CombatUtil;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.Game;
import forge.game.ai.ComputerUtilCard;
import forge.game.combat.CombatUtil;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
//AB:GainControl|ValidTgts$Creature|TgtPrompt$Select target legendary creature|LoseControl$Untap,LoseControl|SpellDescription$Gain control of target xxxxxxx
@@ -89,9 +89,13 @@ public class ControlGainAi extends SpellAbilityAi {
if (!opp.canBeTargetedBy(sa)) {
return false;
}
if (tgt.isRandomTarget()) {
sa.getTargets().add(Aggregates.random(tgt.getAllCandidates(sa, false)));
} else {
sa.getTargets().add(opp);
}
}
}
// Don't steal something if I can't Attack without, or prevent it from
// blocking at least
@@ -102,7 +106,7 @@ public class ControlGainAi extends SpellAbilityAi {
}
List<Card> list =
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard());
// AI won't try to grab cards that are filtered out of AI decks on purpose
list = CardLists.filter(list, new Predicate<Card>() {
@@ -126,7 +130,7 @@ public class ControlGainAi extends SpellAbilityAi {
return false;
}
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
Card t = null;
for (final Card c : list) {
if (c.isCreature()) {
@@ -144,7 +148,7 @@ public class ControlGainAi extends SpellAbilityAi {
}
if (list.isEmpty()) {
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
} else {

View File

@@ -1,24 +1,22 @@
package forge.card.ability.ai;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates.Presets;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Random;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates.Presets;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCard;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class CopyPermanentAi extends SpellAbilityAi {
@@ -27,29 +25,23 @@ public class CopyPermanentAi extends SpellAbilityAi {
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
// Card source = sa.getSourceCard();
// Card source = sa.getHostCard();
// TODO - I'm sure someone can do this AI better
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false;
}
if (sa.hasParam("AtEOT") && !aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1)) {
return false;
} else {
double chance = .4; // 40 percent chance with instant speed stuff
if (SpellAbilityAi.isSorcerySpeed(sa)) {
chance = .667; // 66.7% chance for sorcery speed (since it will
// never activate EOT)
}
final Random r = MyRandom.getRandom();
if (r.nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1)) {
return this.doTriggerAINoCost(aiPlayer, sa, false);
} else {
return false;
}
}
}
@Override
protected boolean doTriggerAINoCost(final Player aiPlayer, SpellAbility sa, boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
// ////
// Targeting
@@ -69,9 +61,9 @@ public class CopyPermanentAi extends SpellAbilityAi {
});
sa.resetTargets();
// target loop
while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getHostCard(), sa)) {
if (list.isEmpty()) {
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa))
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
@@ -95,7 +87,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
}
if (choice == null) { // can't find anything left
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa))
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
@@ -127,7 +119,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
// Select a card to attach to
return ComputerUtilCard.getBestAI(options);
}

View File

@@ -1,16 +1,16 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardFactoryUtil;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.util.MyRandom;
public class CounterAi extends SpellAbilityAi {
@@ -19,7 +19,7 @@ public class CounterAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
boolean toReturn = true;
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Game game = ai.getGame();
if (game.getStack().isEmpty()) {
return false;
@@ -39,12 +39,12 @@ public class CounterAi extends SpellAbilityAi {
if (tgt != null) {
final SpellAbility topSA = game.getStack().peekAbility();
if (!CardFactoryUtil.isCounterableBy(topSA.getSourceCard(), sa) || topSA.getActivatingPlayer() == ai) {
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || topSA.getActivatingPlayer() == ai) {
// might as well check for player's friendliness
return false;
}
if (sa.hasParam("AITgts") && (topSA.getSourceCard() == null
|| !topSA.getSourceCard().isValid(sa.getParam("AITgts"), sa.getActivatingPlayer(), source))) {
if (sa.hasParam("AITgts") && (topSA.getHostCard() == null
|| !topSA.getHostCard().isValid(sa.getParam("AITgts"), sa.getActivatingPlayer(), source))) {
return false;
}
@@ -115,7 +115,7 @@ public class CounterAi extends SpellAbilityAi {
return false;
}
final SpellAbility topSA = game.getStack().peekAbility();
if (!CardFactoryUtil.isCounterableBy(topSA.getSourceCard(), sa) || topSA.getActivatingPlayer() == ai) {
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || topSA.getActivatingPlayer() == ai) {
return false;
}
@@ -128,7 +128,7 @@ public class CounterAi extends SpellAbilityAi {
String unlessCost = sa.hasParam("UnlessCost") ? sa.getParam("UnlessCost").trim() : null;
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (unlessCost != null) {
// Is this Usable Mana Sources? Or Total Available Mana?
final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size();

View File

@@ -15,18 +15,17 @@
* 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.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.game.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCard;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.util.Aggregates;
import java.util.List;
/**
* <p>
@@ -50,7 +49,7 @@ public abstract class CountersAi {
* a {@link java.lang.String} object.
* @param amount
* a int.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card chooseCursedTarget(final List<Card> list, final String type, final int amount) {
Card choice;
@@ -83,18 +82,17 @@ public abstract class CountersAi {
* a {@link forge.CardList} object.
* @param type
* a {@link java.lang.String} object.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
public static Card chooseBoonTarget(final List<Card> list, final String type) {
Card choice;
if (type.equals("P1P1")) {
choice = ComputerUtilCard.getBestCreatureAI(list);
} else if (type.equals("DIVINITY") || type.equals("FATE")) {
} else if (type.equals("DIVINITY")) {
final List<Card> boon = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getCounters(CounterType.DIVINITY) == 0
&& c.getCounters(CounterType.FATE) == 0;
return c.getCounters(CounterType.DIVINITY) == 0;
}
});
choice = ComputerUtilCard.getMostExpensivePermanentAI(boon, null, false);

View File

@@ -1,21 +1,21 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
public class CountersMoveAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
@@ -28,7 +28,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// TODO handle proper calculation of X values based on Cost
int amount = 0;
if (!sa.getParam("CounterNum").equals("All")) {
amount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
}
// don't use it if no counters to add
if (amount <= 0) {
@@ -47,13 +47,13 @@ public class CountersMoveAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card host = sa.getSourceCard();
final Card host = sa.getHostCard();
final TargetRestrictions abTgt = sa.getTargetRestrictions();
final String type = sa.getParam("CounterType");
final String amountStr = sa.getParam("CounterNum");
int amount = 0;
if (!sa.getParam("CounterNum").equals("All")) {
amount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
}
boolean chance = false;
boolean preferred = true;

View File

@@ -1,18 +1,17 @@
package forge.card.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtil;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
public class CountersProliferateAi extends SpellAbilityAi {
@Override
@@ -43,7 +42,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
}
});
if ((cperms.size() == 0) && (hperms.size() == 0) && (ai.getOpponent().getPoisonCounters() == 0)) {
if (cperms.isEmpty() && hperms.isEmpty() && ai.getOpponent().getPoisonCounters() == 0) {
return false;
}
return chance;

View File

@@ -0,0 +1,401 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.combat.CombatUtil;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
public class CountersPutAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, final SpellAbility sa) {
// AI needs to be expanded, since this function can be pretty complex
// based on
// what the expected targets could be
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final TargetRestrictions abTgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard();
List<Card> list;
Card choice = null;
final String type = sa.getParam("CounterType");
final String amountStr = sa.getParam("CounterNum");
final boolean divided = sa.hasParam("DividedAsYouChoose");
final Player player = sa.isCurse() ? ai.getOpponent() : ai;
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false;
}
if ("Never".equals(sa.getParam("AILogic"))) {
return false;
}
if (abCost != null) {
// AI currently disabled for these costs
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkCreatureSacrificeCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
return false;
}
}
if (sa.hasParam("Monstrosity") && source.isMonstrous()) {
return false;
}
if (sa.hasParam("LevelUp")) {
// creatures enchanted by curse auras have low priority
if (source.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
for (Card aura : source.getEnchantedBy()) {
if (aura.getController().isOpponentOf(ai)) {
return false;
}
}
}
int maxLevel = Integer.parseInt(sa.getParam("MaxLevel"));
return source.getCounters(CounterType.LEVEL) < maxLevel;
}
// TODO handle proper calculation of X values based on Cost
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
// Set PayX here to maximum value.
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
source.setSVar("PayX", Integer.toString(amount));
}
// don't use it if no counters to add
if (amount <= 0) {
return false;
}
// Targeting
if (abTgt != null) {
sa.resetTargets();
// target loop
list = CardLists.filter(player.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.canBeTargetedBy(sa) && c.canReceiveCounters(CounterType.valueOf(type));
}
});
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source);
if (list.size() < abTgt.getMinTargets(source, sa)) {
return false;
}
while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getHostCard(), sa)) {
if (list.isEmpty()) {
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
if (sa.isCurse()) {
choice = CountersAi.chooseCursedTarget(list, type, amount);
} else {
choice = CountersAi.chooseBoonTarget(list, type);
}
if (choice == null) { // can't find anything left
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
list.remove(choice);
sa.getTargets().add(choice);
if (divided) {
abTgt.addDividedAllocation(choice, amount);
break;
}
}
} else {
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
// Don't activate Curse abilities on my cards and non-curse abilites
// on my opponents
if (cards.isEmpty() || !cards.get(0).getController().equals(player)) {
return false;
}
final int currCounters = cards.get(0).getCounters(CounterType.valueOf(type));
// each non +1/+1 counter on the card is a 10% chance of not
// activating this ability.
if (!(type.equals("P1P1") || type.equals("M1M1") || type.equals("ICE")) && (r.nextFloat() < (.1 * currCounters))) {
return false;
}
}
if (ComputerUtil.playImmediately(ai, sa)) {
return true;
}
// Don't use non P1P1/M1M1 counters before main 2 if possible
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
&& !sa.hasParam("ActivationPhases")
&& !(type.equals("P1P1") || type.equals("M1M1"))
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
return false;
}
if (ComputerUtil.waitForBlocking(sa)) {
return false;
}
return true;
} // putCanPlayAI
@Override
public boolean chkAIDrawback(final SpellAbility sa, Player ai) {
boolean chance = true;
final TargetRestrictions abTgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard();
Card choice = null;
final String type = sa.getParam("CounterType");
final String amountStr = sa.getParam("CounterNum");
final boolean divided = sa.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
final Player player = sa.isCurse() ? ai.getOpponent() : ai;
if (abTgt != null) {
List<Card> list =
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), abTgt.getValidTgts(), source.getController(), source);
if (list.size() == 0) {
return false;
}
sa.resetTargets();
// target loop
while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getHostCard(), sa)) {
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return sa.canTarget(c);
}
});
if (list.size() == 0) {
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
} else {
break;
}
}
if (sa.isCurse()) {
choice = CountersAi.chooseCursedTarget(list, type, amount);
} else {
choice = CountersAi.chooseBoonTarget(list, type);
}
if (choice == null) { // can't find anything left
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
list.remove(choice);
sa.getTargets().add(choice);
if (divided) {
abTgt.addDividedAllocation(choice, amount);
break;
}
}
}
return chance;
} // putPlayDrawbackAI
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final TargetRestrictions abTgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard();
// boolean chance = true;
boolean preferred = true;
List<Card> list;
boolean isCurse = sa.isCurse();
final Player player = isCurse ? ai.getOpponent() : ai;
final String type = sa.getParam("CounterType");
final String amountStr = sa.getParam("CounterNum");
final boolean divided = sa.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
if (abTgt == null) {
// No target. So must be defined
list = new ArrayList<Card>(AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa));
if (!mandatory) {
// TODO - If Trigger isn't mandatory, when wouldn't we want to
// put a counter?
// things like Powder Keg, which are way too complex for the AI
}
} else {
list = CardLists.getTargetableCards(player.getCardsIn(ZoneType.Battlefield), sa);
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source);
if (list.isEmpty() && mandatory) {
// If there isn't any prefered cards to target, gotta choose
// non-preferred ones
list = player.getOpponent().getCardsIn(ZoneType.Battlefield);
list = CardLists.getTargetableCards(list, sa);
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source);
preferred = false;
}
// Not mandatory, or the the list was regenerated and is still
// empty,
// so return false since there are no targets
if (list.isEmpty()) {
return false;
}
Card choice = null;
// Choose targets here:
if (isCurse) {
if (preferred) {
choice = CountersAi.chooseCursedTarget(list, type, amount);
}
else {
if (type.equals("M1M1")) {
choice = ComputerUtilCard.getWorstCreatureAI(list);
} else {
choice = Aggregates.random(list);
}
}
} else {
if (preferred) {
choice = CountersAi.chooseBoonTarget(list, type);
}
else {
if (type.equals("P1P1")) {
choice = ComputerUtilCard.getWorstCreatureAI(list);
} else {
choice = Aggregates.random(list);
}
}
if (choice != null && divided) {
abTgt.addDividedAllocation(choice, amount);
}
}
// TODO - I think choice can be null here. Is that ok for
// addTarget()?
sa.getTargets().add(choice);
}
return true;
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
final Card source = sa.getHostCard();
if (mode == PlayerActionConfirmMode.Tribute) {
// add counter if that opponent has a giant creature
final List<Card> creats = player.getCreaturesInPlay();
final int tributeAmount = source.getKeywordMagnitude("Tribute");
final boolean isHaste = source.hasKeyword("Haste");
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
@Override
public boolean apply(Card c) {
return CombatUtil.canBlock(source, c, !isHaste)
&& (c.getNetDefense() > source.getNetAttack() + tributeAmount || c.hasKeyword("DeathTouch"));
}
});
if (!threatening.isEmpty()) {
return true;
}
if (source.hasSVar("TributeAILogic")) {
final String logic = source.getSVar("TributeAILogic");
if (logic.equals("Always")) {
return true;
} else if (logic.equals("Never")) {
return false;
} else if (logic.equals("CanBlockThisTurn")) {
// pump haste
List<Card> canBlock = CardLists.filter(creats, new Predicate<Card>() {
@Override
public boolean apply(Card c) {
return CombatUtil.canBlock(source, c) && (c.getNetDefense() > source.getNetAttack() || c.hasKeyword("DeathTouch"));
}
});
if (!canBlock.isEmpty()) {
return false;
}
} else if (logic.equals("DontControlCreatures")) {
return !creats.isEmpty();
}
}
}
return MyRandom.getRandom().nextBoolean();
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(Player, SpellAbility, Collection<Player>)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Collection<Player> options) {
// logic?
return Iterables.getFirst(options, null);
}
}

View File

@@ -1,26 +1,26 @@
package forge.card.ability.ai;
import java.util.List;
import java.util.Random;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
public class CountersPutAllAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
@@ -29,7 +29,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
// the expected targets could be
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
List<Card> hList;
List<Card> cList;
final String type = sa.getParam("CounterType");
@@ -72,7 +72,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
amount = ComputerUtilMana.determineLeftoverMana(sa, ai);
source.setSVar("PayX", Integer.toString(amount));
} else {
amount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
}
// prevent run-away activations - first time will always return true
@@ -133,4 +133,11 @@ public class CountersPutAllAi extends SpellAbilityAi {
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
return canPlayAI(ai, sa);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return player.getCreaturesInPlay().size() >= player.getOpponent().getCreaturesInPlay().size();
}
}

View File

@@ -15,21 +15,20 @@
* 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.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
/**
* <p>
* AbilityFactory_PutOrRemoveCountersAi class.
@@ -52,7 +51,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
List<ZoneType> zones = ZoneType.listValueOf(sa.getParamOrDefault("TgtZones", "Battlefield"));
List<Card> validCards = CardLists.getValidCards(ai.getGame().getCardsIn(zones),
tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard());
if (validCards.isEmpty()) {
return false;
@@ -73,9 +72,9 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
}
}
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
Card targetCard = null;
if (cWithCounters.isEmpty() && ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa))
if (cWithCounters.isEmpty() && ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0))) {
sa.resetTargets();
return false;

View File

@@ -1,15 +1,15 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.Card;
import forge.CounterType;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCost;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
public class CountersRemoveAi extends SpellAbilityAi {
@@ -20,7 +20,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// the expected targets could be
final Cost abCost = sa.getPayCosts();
TargetRestrictions abTgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
// List<Card> list;
// Card choice = null;
@@ -59,7 +59,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
// TODO handle proper calculation of X values based on Cost
// final int amount = calculateAmount(sa.getSourceCard(), amountStr, sa);
// final int amount = calculateAmount(sa.getHostCard(), amountStr, sa);
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false;
@@ -77,7 +77,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
if (!type.matches("Any")) {
final int currCounters = sa.getSourceCard().getCounters(CounterType.valueOf(type));
final int currCounters = sa.getHostCard().getCounters(CounterType.valueOf(type));
if (currCounters < 1) {
return false;
}

View File

@@ -1,19 +1,18 @@
package forge.card.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.collect.Iterables;
import forge.Card;
import forge.CardPredicates;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ai.ComputerUtilCombat;
import forge.game.card.Card;
import forge.game.card.CardPredicates;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
public abstract class DamageAiBase extends SpellAbilityAi {
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention) {
int restDamage = d;
@@ -22,15 +21,19 @@ public abstract class DamageAiBase extends SpellAbilityAi {
if (!sa.canTarget(enemy)) {
return false;
}
if (sa.getTargets() != null && sa.getTargets().getTargets().contains(enemy)) {
return false;
}
// burn Planeswalkers
if (Iterables.any(enemy.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS)) {
return true;
}
if (!noPrevention) {
restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, sa.getSourceCard(), false);
restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, sa.getHostCard(), false);
} else {
restDamage = enemy.staticReplaceDamage(restDamage, sa.getSourceCard(), false);
restDamage = enemy.staticReplaceDamage(restDamage, sa.getHostCard(), false);
}
if (restDamage == 0) {

View File

@@ -1,26 +1,21 @@
package forge.card.ability.ai;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class DamageAllAi extends SpellAbilityAi {
@Override
@@ -29,12 +24,12 @@ public class DamageAllAi extends SpellAbilityAi {
// based on what the expected targets could be
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
String validP = "";
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getSourceCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
@@ -104,11 +99,11 @@ public class DamageAllAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
String validP = "";
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getSourceCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
// Set PayX here to maximum value.
@@ -156,9 +151,9 @@ public class DamageAllAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param player
* a {@link forge.game.player.Player} object.
* @param dmg
@@ -166,7 +161,7 @@ public class DamageAllAi extends SpellAbilityAi {
* @return a {@link forge.CardList} object.
*/
private List<Card> getKillableCreatures(final SpellAbility sa, final Player player, final int dmg) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
String validC = sa.hasParam("ValidCards") ? sa.getParam("ValidCards") : "";
// TODO: X may be something different than X paid
@@ -188,11 +183,11 @@ public class DamageAllAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
String validP = "";
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getSourceCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
// Set PayX here to maximum value.

View File

@@ -1,42 +1,36 @@
package forge.card.ability.ai;
import java.util.List;
import java.util.Random;
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.ITargetable;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetChoices;
import forge.card.spellability.TargetRestrictions;
import forge.ai.*;
import forge.game.Game;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.GameObject;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.cost.Cost;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetChoices;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
public class DamageDealAi extends DamageAiBase {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getSourceCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
// Set PayX here to maximum value.
@@ -53,10 +47,10 @@ public class DamageDealAi extends DamageAiBase {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getSourceCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
// Set PayX here to maximum value.
@@ -136,7 +130,7 @@ public class DamageDealAi extends DamageAiBase {
* a {@link forge.game.player.Player} object.
* @param mandatory
* a boolean.
* @return a {@link forge.Card} object.
* @return a {@link forge.game.card.Card} object.
*/
private Card dealDamageChooseTgtC(final Player ai, final SpellAbility sa, final int d, final boolean noPrevention,
final Player pl, final boolean mandatory) {
@@ -146,10 +140,10 @@ public class DamageDealAi extends DamageAiBase {
return null;
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
List<Card> hPlay = CardLists.getValidCards(pl.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, source);
final List<ITargetable> objects = Lists.newArrayList(sa.getTargets().getTargets());
final List<GameObject> objects = Lists.newArrayList(sa.getTargets().getTargets());
if (sa.hasParam("TargetUnique")) {
objects.addAll(sa.getUniqueTargets());
}
@@ -201,7 +195,7 @@ public class DamageDealAi extends DamageAiBase {
* </p>
*
* @param saMe
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param dmg
* a int.
* @return a boolean.
@@ -226,9 +220,9 @@ public class DamageDealAi extends DamageAiBase {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param tgt
* a {@link forge.card.spellability.TargetRestrictions} object.
* a {@link forge.game.spellability.TargetRestrictions} object.
* @param dmg
* a int.
* @param mandatory
@@ -237,7 +231,7 @@ public class DamageDealAi extends DamageAiBase {
*/
private boolean damageChoosingTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, int dmg,
final boolean isTrigger, final boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final boolean noPrevention = sa.hasParam("NoPrevention");
final Game game = source.getGame();
final PhaseHandler phase = game.getPhaseHandler();
@@ -344,7 +338,7 @@ public class DamageDealAi extends DamageAiBase {
}
}
// fell through all the choices, no targets left?
if (((sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) || (sa.getTargets().getNumTargeted() == 0))) {
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa) || sa.getTargets().getNumTargeted() == 0) {
if (!mandatory) {
sa.resetTargets();
return false;
@@ -367,21 +361,21 @@ public class DamageDealAi extends DamageAiBase {
* @param ai
*
* @param saMe
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param dmg
* a int.
* @return a boolean.
*/
private boolean damageChooseNontargeted(Player ai, final SpellAbility saMe, final int dmg) {
// TODO: Improve circumstances where the Defined Damage is unwanted
final List<ITargetable> objects = AbilityUtils.getDefinedObjects(saMe.getSourceCard(), saMe.getParam("Defined"), saMe);
final List<GameObject> objects = AbilityUtils.getDefinedObjects(saMe.getHostCard(), saMe.getParam("Defined"), saMe);
boolean urgent = false; // can it wait?
boolean positive = false;
for (final Object o : objects) {
if (o instanceof Card) {
Card c = (Card) o;
final int restDamage = ComputerUtilCombat.predictDamageTo(c, dmg, saMe.getSourceCard(), false);
final int restDamage = ComputerUtilCombat.predictDamageTo(c, dmg, saMe.getHostCard(), false);
if (!c.hasKeyword("Indestructible") && ComputerUtilCombat.getDamageToKill(c) <= restDamage) {
if (c.getController().equals(ai)) {
return false;
@@ -394,7 +388,7 @@ public class DamageDealAi extends DamageAiBase {
}
} else if (o instanceof Player) {
final Player p = (Player) o;
final int restDamage = ComputerUtilCombat.predictDamageTo(p, dmg, saMe.getSourceCard(), false);
final int restDamage = ComputerUtilCombat.predictDamageTo(p, dmg, saMe.getHostCard(), false);
if (!p.isOpponentOf(ai) && p.canLoseLife() && restDamage + 3 >= p.getLife() && restDamage > 0) {
// from this spell will kill me
return false;
@@ -422,9 +416,9 @@ public class DamageDealAi extends DamageAiBase {
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param tgt
* a {@link forge.card.spellability.TargetRestrictions} object.
* a {@link forge.game.spellability.TargetRestrictions} object.
* @param dmg
* a int.
* @param mandatory
@@ -437,7 +431,7 @@ public class DamageDealAi extends DamageAiBase {
final boolean noPrevention = sa.hasParam("NoPrevention");
final boolean divided = sa.hasParam("DividedAsYouChoose");
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
// TODO: Consider targeting the planeswalker
if (tgt.canTgtCreature()) {
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, ai, mandatory);
@@ -471,7 +465,7 @@ public class DamageDealAi extends DamageAiBase {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(source, damage, sa);

View File

@@ -1,10 +1,10 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ability.AbilityUtils;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
public class DamageEachAi extends DamageAiBase {
@@ -21,7 +21,7 @@ public class DamageEachAi extends DamageAiBase {
}
final String damage = sa.getParam("NumDmg");
final int iDmg = AbilityUtils.calculateAmount(sa.getSourceCard(), damage, sa);
final int iDmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
return this.shouldTgtP(ai, sa, iDmg, false);
}

View File

@@ -1,33 +1,30 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.ArrayList;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.ITargetable;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.*;
import forge.game.Game;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.GameObject;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetChoices;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.ArrayList;
import java.util.List;
public class DamagePreventAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Card hostCard = sa.getSourceCard();
final Card hostCard = sa.getHostCard();
final Game game = ai.getGame();
final Combat combat = game.getCombat();
boolean chance = false;
@@ -55,11 +52,11 @@ public class DamagePreventAi extends SpellAbilityAi {
if (tgt == null) {
// As far as I can tell these Defined Cards will only have one of
// them
final List<ITargetable> objects = AbilityUtils.getDefinedObjects(sa.getSourceCard(), sa.getParam("Defined"), sa);
final List<GameObject> objects = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
// react to threats on the stack
if (!game.getStack().isEmpty()) {
final List<ITargetable> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
for (final Object o : objects) {
if (threatenedObjects.contains(o)) {
chance = true;
@@ -93,11 +90,12 @@ public class DamagePreventAi extends SpellAbilityAi {
// react to threats on the stack
else if (!game.getStack().isEmpty()) {
sa.resetTargets();
final TargetChoices tcs = sa.getTargets();
// check stack for something on the stack will kill anything i control
final List<ITargetable> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
if (objects.contains(ai)) {
sa.getTargets().add(ai);
tcs.add(ai);
chance = true;
}
final List<Card> threatenedTargets = new ArrayList<Card>();
@@ -113,17 +111,18 @@ public class DamagePreventAi extends SpellAbilityAi {
if (!threatenedTargets.isEmpty()) {
// Choose "best" of the remaining to save
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(threatenedTargets));
tcs.add(ComputerUtilCard.getBestCreatureAI(threatenedTargets));
chance = true;
}
} // Protect combatants
else if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
sa.resetTargets();
final TargetChoices tcs = sa.getTargets();
if (sa.canTarget(ai) && ComputerUtilCombat.wouldLoseLife(ai, combat)
&& (ComputerUtilCombat.lifeInDanger(ai, combat) || sa.isAbility() || sa.isTrigger())
&& game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
sa.getTargets().add(ai);
tcs.add(ai);
chance = true;
} else {
// filter AIs battlefield by what I can target
@@ -135,18 +134,18 @@ public class DamagePreventAi extends SpellAbilityAi {
return false;
}
final List<Card> combatants = CardLists.filter(targetables, CardPredicates.Presets.CREATURES);
CardLists.sortByEvaluateCreature(combatants);
ComputerUtilCard.sortByEvaluateCreature(combatants);
for (final Card c : combatants) {
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
sa.getTargets().add(c);
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && tcs.getNumTargeted() < tgt.getMaxTargets(hostCard, sa)) {
tcs.add(c);
chance = true;
}
}
}
}
if (sa.hasParam("DividedAsYouChoose") && sa.getTargets() != null && !sa.getTargets().getTargets().isEmpty()) {
tgt.addDividedAllocation(sa.getTargets().getTargets().get(0), AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("Amount"), sa));
tgt.addDividedAllocation(sa.getTargets().getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
}
return chance;
@@ -172,9 +171,9 @@ public class DamagePreventAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @return a boolean.
@@ -185,7 +184,7 @@ public class DamagePreventAi extends SpellAbilityAi {
// filter AIs battlefield by what I can target
final Game game = ai.getGame();
List<Card> targetables = game.getCardsIn(ZoneType.Battlefield);
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, sa.getSourceCard());
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, sa.getHostCard());
final List<Card> compTargetables = CardLists.filterControlledBy(targetables, ai);
Card target = null;
@@ -199,7 +198,7 @@ public class DamagePreventAi extends SpellAbilityAi {
if (!compTargetables.isEmpty()) {
final List<Card> combatants = CardLists.filter(compTargetables, CardPredicates.Presets.CREATURES);
CardLists.sortByEvaluateCreature(combatants);
ComputerUtilCard.sortByEvaluateCreature(combatants);
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
Combat combat = game.getCombat();
for (final Card c : combatants) {
@@ -217,7 +216,7 @@ public class DamagePreventAi extends SpellAbilityAi {
}
sa.getTargets().add(target);
if (sa.hasParam("DividedAsYouChoose")) {
tgt.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("Amount"), sa));
tgt.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
}
return true;
}

View File

@@ -1,13 +1,13 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.Card;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtilCost;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class DamagePreventAllAi extends SpellAbilityAi {
@@ -16,7 +16,7 @@ public class DamagePreventAllAi extends SpellAbilityAi {
*/
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Card hostCard = sa.getSourceCard();
final Card hostCard = sa.getHostCard();
boolean chance = false;
final Cost cost = sa.getPayCosts();

View File

@@ -1,30 +1,29 @@
package forge.card.ability.ai;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityRestriction;
import forge.card.spellability.TargetRestrictions;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityRestriction;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DebuffAi extends SpellAbilityAi {
// *************************************************************************
// ***************************** Debuff ************************************
@@ -33,7 +32,7 @@ public class DebuffAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
// if there is no target and host card isn't in play, don't activate
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Game game = ai.getGame();
if ((sa.getTargetRestrictions() == null) && !source.isInPlay()) {
return false;
@@ -76,7 +75,7 @@ public class DebuffAi extends SpellAbilityAi {
}
if (!sa.usesTargeting() || !sa.getTargetRestrictions().doesTarget()) {
List<Card> cards = AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa);
List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
final Combat combat = game.getCombat();
@@ -117,9 +116,9 @@ public class DebuffAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param kws
* a {@link java.util.ArrayList} object.
* @param mandatory
@@ -135,7 +134,7 @@ public class DebuffAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions();
sa.resetTargets();
List<Card> list = getCurseCreatures(ai, sa, kws == null ? Lists.<String>newArrayList() : kws);
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard());
// several uses here:
// 1. make human creatures lose evasion when they are attacking
@@ -148,12 +147,12 @@ public class DebuffAi extends SpellAbilityAi {
return mandatory && debuffMandatoryTarget(ai, sa, mandatory);
}
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
Card t = null;
// boolean goodt = false;
if (list.isEmpty()) {
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().getNumTargeted() == 0)) {
if (mandatory) {
return debuffMandatoryTarget(ai, sa, mandatory);
}
@@ -180,9 +179,9 @@ public class DebuffAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param kws
* a {@link java.util.ArrayList} object.
* @return a {@link forge.CardList} object.
@@ -211,9 +210,9 @@ public class DebuffAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @return a boolean.
@@ -221,9 +220,9 @@ public class DebuffAi extends SpellAbilityAi {
private boolean debuffMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
List<Card> list = ai.getGame().getCardsIn(ZoneType.Battlefield);
final TargetRestrictions tgt = sa.getTargetRestrictions();
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard());
if (list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
sa.resetTargets();
return false;
}
@@ -235,7 +234,7 @@ public class DebuffAi extends SpellAbilityAi {
final List<Card> pref = CardLists.filterControlledBy(list, ai.getOpponent());
final List<Card> forced = CardLists.filterControlledBy(list, ai);
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
if (pref.isEmpty()) {
@@ -254,7 +253,7 @@ public class DebuffAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
if (forced.isEmpty()) {
break;
}
@@ -273,7 +272,7 @@ public class DebuffAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
sa.resetTargets();
return false;
}

View File

@@ -1,27 +1,26 @@
package forge.card.ability.ai;
import java.util.List;
import java.util.Random;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.combat.CombatUtil;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
public class DebuffAllAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
String valid = "";
final Random r = MyRandom.getRandom();
// final Card source = sa.getSourceCard();
final Card hostCard = sa.getSourceCard();
// final Card source = sa.getHostCard();
final Card hostCard = sa.getHostCard();
final Player opp = ai.getOpponent();
final boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn()); // to

View File

@@ -0,0 +1,50 @@
package forge.ai.ability;
import forge.ai.AiController;
import forge.ai.AiPlayDecision;
import forge.ai.PlayerControllerAi;
import forge.ai.SpellAbilityAi;
import forge.ai.SpellApiToAi;
import forge.game.ability.AbilityFactory;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
public class DelayedTriggerAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
final String svarName = sa.getParam("Execute");
final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard());
trigsa.setActivatingPlayer(ai);
if (trigsa instanceof AbilitySub) {
return SpellApiToAi.Converter.get(((AbilitySub) trigsa).getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
} else {
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
}
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final String svarName = sa.getParam("Execute");
final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard());
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
trigsa.setActivatingPlayer(ai);
if (!sa.hasParam("OptionalDecider")) {
return aic.doTrigger(trigsa, true);
} else {
return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You"));
}
}
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final String svarName = sa.getParam("Execute");
final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard());
trigsa.setActivatingPlayer(ai);
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
}
}

View File

@@ -1,28 +1,27 @@
package forge.card.ability.ai;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
import forge.game.cost.CostSacrifice;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.cost.CostPart;
import forge.card.cost.CostSacrifice;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class DestroyAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -42,7 +41,7 @@ public class DestroyAi extends SpellAbilityAi {
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final TargetRestrictions abTgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final boolean noRegen = sa.hasParam("NoRegen");
List<Card> list;
@@ -104,7 +103,7 @@ public class DestroyAi extends SpellAbilityAi {
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return ((c.getShield() == 0) && !ComputerUtil.canRegenerate(ai, c));
return (c.getShield().isEmpty() && !ComputerUtil.canRegenerate(ai, c));
}
});
}
@@ -113,9 +112,9 @@ public class DestroyAi extends SpellAbilityAi {
return false;
}
// target loop
while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getHostCard(), sa)) {
if (list.size() == 0) {
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa))
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
@@ -136,7 +135,7 @@ public class DestroyAi extends SpellAbilityAi {
}
if (choice == null) { // can't find anything left
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa))
if ((sa.getTargets().getNumTargeted() < abTgt.getMinTargets(sa.getHostCard(), sa))
|| (sa.getTargets().getNumTargeted() == 0)) {
sa.resetTargets();
return false;
@@ -176,7 +175,7 @@ public class DestroyAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final boolean noRegen = sa.hasParam("NoRegen");
if (tgt != null) {
List<Card> list;
@@ -184,7 +183,7 @@ public class DestroyAi extends SpellAbilityAi {
list = CardLists.getTargetableCards(list, sa);
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source);
if (list.isEmpty() || list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (list.isEmpty() || list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
return false;
}
@@ -201,7 +200,7 @@ public class DestroyAi extends SpellAbilityAi {
preferred = CardLists.filter(preferred, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getShield() == 0;
return c.getShield().isEmpty();
}
});
}
@@ -210,10 +209,10 @@ public class DestroyAi extends SpellAbilityAi {
list.remove(c);
}
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
if (preferred.isEmpty()) {
if ((sa.getTargets().getNumTargeted() == 0)
|| (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa))) {
|| (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa))) {
if (!mandatory) {
sa.resetTargets();
return false;
@@ -237,7 +236,7 @@ public class DestroyAi extends SpellAbilityAi {
}
}
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
if (list.isEmpty()) {
break;
} else {
@@ -252,7 +251,7 @@ public class DestroyAi extends SpellAbilityAi {
}
}
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
return false;
}
} else {

View File

@@ -1,21 +1,20 @@
package forge.card.ability.ai;
import java.util.List;
import java.util.Random;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
public class DestroyAllAi extends SpellAbilityAi {
@@ -31,7 +30,7 @@ public class DestroyAllAi extends SpellAbilityAi {
*/
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
String valid = "";
if (mandatory) {
return true;
@@ -79,9 +78,8 @@ public class DestroyAllAi extends SpellAbilityAi {
protected boolean canPlayAI(final Player ai, SpellAbility sa) {
// AI needs to be expanded, since this function can be pretty complex
// based on what the expected targets could be
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
String valid = "";
if (sa.hasParam("ValidCards")) {
@@ -115,29 +113,31 @@ public class DestroyAllAi extends SpellAbilityAi {
}
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false;
}
// if only creatures are affected evaluate both lists and pass only if
// human creatures are more valuable
if ((CardLists.getNotType(humanlist, "Creature").size() == 0) && (CardLists.getNotType(computerlist, "Creature").size() == 0)) {
if ((ComputerUtilCard.evaluateCreatureList(computerlist) + 200) >= ComputerUtilCard
.evaluateCreatureList(humanlist)) {
if (CardLists.getNotType(humanlist, "Creature").isEmpty() && CardLists.getNotType(computerlist, "Creature").isEmpty()) {
if (ComputerUtilCard.evaluateCreatureList(computerlist) + 200 >= ComputerUtilCard.evaluateCreatureList(humanlist)) {
return false;
}
} // only lands involved
else if ((CardLists.getNotType(humanlist, "Land").size() == 0) && (CardLists.getNotType(computerlist, "Land").size() == 0)) {
if ((ComputerUtilCard.evaluatePermanentList(computerlist) + 1) >= ComputerUtilCard
.evaluatePermanentList(humanlist)) {
else if (CardLists.getNotType(humanlist, "Land").isEmpty() && CardLists.getNotType(computerlist, "Land").isEmpty()) {
if (ai.isCardInPlay("Crucible of Worlds") && !ai.getOpponent().isCardInPlay("Crucible of Worlds") && !humanlist.isEmpty()) {
return true;
}
if (ComputerUtilCard.evaluatePermanentList(computerlist) + 1 >= ComputerUtilCard.evaluatePermanentList(humanlist)) {
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human
// permanents are more valuable
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
else if ((ComputerUtilCard.evaluatePermanentList(computerlist) + 3) >= ComputerUtilCard
.evaluatePermanentList(humanlist)) {
return false;
}
return chance;
return true;
}
}

View File

@@ -1,18 +1,20 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.Random;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtil;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.Collection;
import java.util.Random;
public class DigAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -22,7 +24,7 @@ public class DigAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
Player opp = ai.getOpponent();
final Card host = sa.getSourceCard();
final Card host = sa.getHostCard();
Player libraryOwner = ai;
if (sa.usesTargeting()) {
@@ -79,6 +81,15 @@ public class DigAi extends SpellAbilityAi {
return true;
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> valid, boolean isOptional, Player relatedPlayer) {
Card chosen = ComputerUtilCard.getBestAI(valid);
if (sa.getActivatingPlayer().isOpponentOf(ai) && relatedPlayer.isOpponentOf(ai)) {
return ComputerUtilCard.getWorstAI(valid);
}
return chosen;
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/

View File

@@ -1,25 +1,25 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtilMana;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class DigUntilAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
Card source = sa.getSourceCard();
Card source = sa.getHostCard();
double chance = .4; // 40 percent chance with instant speed stuff
if (SpellAbilityAi.isSorcerySpeed(sa)) {
chance = .667; // 66.7% chance for sorcery speed (since it will

View File

@@ -1,29 +1,29 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
public class DiscardAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Cost abCost = sa.getPayCosts();
if (abCost != null) {
@@ -54,7 +54,7 @@ public class DiscardAi extends SpellAbilityAi {
}
} else {
// TODO: Add appropriate restrictions
final List<Player> players = AbilityUtils.getDefinedPlayers(sa.getSourceCard(),
final List<Player> players = AbilityUtils.getDefinedPlayers(sa.getHostCard(),
sa.getParam("Defined"), sa);
if (players.size() == 1) {
@@ -85,7 +85,7 @@ public class DiscardAi extends SpellAbilityAi {
}
source.setSVar("PayX", Integer.toString(cardsToDiscard));
} else {
if (AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa) < 1) {
if (AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) < 1) {
return false;
}
}
@@ -115,7 +115,7 @@ public class DiscardAi extends SpellAbilityAi {
private boolean discardTargetAI(final Player ai, final SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
Player opp = ai.getOpponent();
if (opp.getCardsIn(ZoneType.Hand).isEmpty()) {
if (opp.getCardsIn(ZoneType.Hand).isEmpty() && !ComputerUtil.activateForCost(sa, ai)) {
return false;
}
if (tgt != null) {
@@ -144,11 +144,11 @@ public class DiscardAi extends SpellAbilityAi {
}
}
} else {
if ("X".equals(sa.getParam("RevealNumber")) && sa.getSourceCard().getSVar("X").equals("Count$xPaid")) {
if ("X".equals(sa.getParam("RevealNumber")) && sa.getHostCard().getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ai.getOpponent()
.getCardsIn(ZoneType.Hand).size());
sa.getSourceCard().setSVar("PayX", Integer.toString(cardsToDiscard));
sa.getHostCard().setSVar("PayX", Integer.toString(cardsToDiscard));
}
}

View File

@@ -1,16 +1,16 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.player.Player;
import forge.util.MyRandom;
public class DrainManaAi extends SpellAbilityAi {
@Override
@@ -18,7 +18,7 @@ public class DrainManaAi extends SpellAbilityAi {
// AI cannot use this properly until he can use SAs during Humans turn
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Player opp = ai.getOpponent();
final Random r = MyRandom.getRandom();
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
@@ -45,7 +45,7 @@ public class DrainManaAi extends SpellAbilityAi {
final Player opp = ai.getOpponent();
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (null == tgt) {
if (mandatory) {
@@ -71,7 +71,7 @@ public class DrainManaAi extends SpellAbilityAi {
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
// AI cannot use this properly until he can use SAs during Humans turn
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
boolean randomReturn = true;

View File

@@ -16,25 +16,22 @@
* 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.ability.ai;
package forge.ai.ability;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.cost.CostDiscard;
import forge.card.cost.CostPart;
import forge.card.cost.PaymentDecision;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.*;
import forge.game.Game;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.cost.CostDiscard;
import forge.game.cost.CostPart;
import forge.game.cost.PaymentDecision;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
public class DrawAi extends SpellAbilityAi {
@@ -50,7 +47,7 @@ public class DrawAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Cost abCost = sa.getPayCosts();
final Game game = ai.getGame();
@@ -65,10 +62,10 @@ public class DrawAi extends SpellAbilityAi {
}
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
AiCostDecision aiDecisions = new AiCostDecision(ai, sa);
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostDiscard) {
CostDiscard cd = (CostDiscard) part;
PaymentDecision decision = cd.decideAIPayment(ai, sa, sa.getSourceCard());
PaymentDecision decision = part.accept(aiDecisions);
if ( null == decision )
return false;
for (Card discard : decision.cards) {
@@ -106,6 +103,10 @@ public class DrawAi extends SpellAbilityAi {
return true;
}
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
return false;
}
// Don't use draw abilities before main 2 if possible
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
&& !sa.hasParam("ActivationPhases") && !ComputerUtil.castSpellInMain1(ai, sa)) {
@@ -129,7 +130,7 @@ public class DrawAi extends SpellAbilityAi {
private boolean targetAI(final Player ai, final SpellAbility sa, final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final boolean drawback = (sa instanceof AbilitySub);
final Game game = ai.getGame();
Player opp = ai.getOpponent();
@@ -146,7 +147,7 @@ public class DrawAi extends SpellAbilityAi {
int numCards = 1;
if (sa.hasParam("NumCards")) {
numCards = AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa);
numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
}
boolean xPaid = false;
@@ -164,8 +165,7 @@ public class DrawAi extends SpellAbilityAi {
//if (n)
// TODO: if xPaid and one of the below reasons would fail, instead of
// bailing
// reduce toPay amount to acceptable level
// bailing reduce toPay amount to acceptable level
if (tgt != null) {
// ability is targeted
@@ -264,7 +264,7 @@ public class DrawAi extends SpellAbilityAi {
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa) : 1;
int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) : 1;
// AI shouldn't mill itself
return numCards < player.getZone(ZoneType.Library).size();
}

View File

@@ -1,28 +1,27 @@
package forge.card.ability.ai;
import java.util.List;
import java.util.Random;
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.Card;
import forge.CardLists;
import forge.card.ability.ApiType;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.TargetRestrictions;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.combat.CombatUtil;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
public class EffectAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
@@ -65,7 +64,7 @@ public class EffectAi extends SpellAbilityAi {
if (tgt != null) {
sa.resetTargets();
List<Card> list = game.getCombat().getAttackers();
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard());
list = CardLists.getTargetableCards(list, sa);
Card target = ComputerUtilCard.getBestCreatureAI(list);
if (target == null) {
@@ -124,7 +123,7 @@ public class EffectAi extends SpellAbilityAi {
if ("False".equals(sa.getParam("Stackable"))) {
String name = sa.getParam("Name");
if (name == null) {
name = sa.getSourceCard().getName() + "'s Effect";
name = sa.getHostCard().getName() + "'s Effect";
}
final List<Card> list = sa.getActivatingPlayer().getCardsIn(ZoneType.Command, name);
if (!list.isEmpty()) {

View File

@@ -15,21 +15,20 @@
* 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.ability.ai;
import java.util.Collection;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import java.util.Collection;
import java.util.List;
/**
* <p>
@@ -45,9 +44,9 @@ public final class EncodeAi extends SpellAbilityAi {
* bondCanPlayAI.
* </p>
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/
@@ -72,7 +71,7 @@ public final class EncodeAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
Card choice = null;
// final String logic = sa.getParam("AILogic");
// if (logic == null) {

View File

@@ -1,9 +1,9 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
/**
* TODO: Write javadoc for this type.

View File

@@ -1,19 +1,19 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.game.player.Player;
import forge.util.MyRandom;
public class FightAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -22,7 +22,7 @@ public class FightAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
sa.resetTargets();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
List<Card> aiCreatures = ai.getCreaturesInPlay();
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
@@ -107,7 +107,7 @@ public class FightAi extends SpellAbilityAi {
}
//try to make a good trade or no trade
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
List<Card> humCreatures = ai.getOpponent().getCreaturesInPlay();
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
if (humCreatures.isEmpty()) {

View File

@@ -1,8 +1,8 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class FlipACoinAi extends SpellAbilityAi {

View File

@@ -1,12 +1,12 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ai.ComputerUtilCombat;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class FogAi extends SpellAbilityAi {

View File

@@ -1,9 +1,9 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
public class GameLossAi extends SpellAbilityAi {
@Override

View File

@@ -1,9 +1,9 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class GameWinAi extends SpellAbilityAi {
/* (non-Javadoc)

View File

@@ -0,0 +1,30 @@
package forge.ai.ability;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.Collection;
import java.util.List;
public class HauntAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return false; // should not get here
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> creats, boolean isOptional, Player targetedPlayer) {
final List<Card> oppCreats = CardLists.filterControlledBy(creats, ai.getOpponents());
return ComputerUtilCard.getWorstCreatureAI(oppCreats.isEmpty() ? creats : oppCreats);
}
}

View File

@@ -1,13 +1,12 @@
package forge.card.ability.ai;
import java.util.Collection;
package forge.ai.ability;
import com.google.common.collect.Iterables;
import forge.Card;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.Collection;
/**
* TODO: Write javadoc for this type.
@@ -25,7 +24,7 @@ public class LegendaryRuleAi extends SpellAbilityAi {
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional, Player targetedPlayer) {
// Choose a single legendary/planeswalker card to keep
Card firstOption = Iterables.getFirst(options, null);
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();

View File

@@ -1,13 +1,13 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.util.MyRandom;
import java.util.Random;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.player.Player;
import forge.util.MyRandom;
public class LifeExchangeAi extends SpellAbilityAi {
/*

View File

@@ -1,24 +1,15 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.ai.*;
import forge.game.Game;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
/**
* TODO: Write javadoc for this type.
*
*/
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
public class LifeGainAi extends SpellAbilityAi {
@@ -28,27 +19,28 @@ public class LifeGainAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Game game = source.getGame();
final int life = ai.getLife();
final String amountStr = sa.getParam("LifeAmount");
int lifeAmount = 0;
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
// Set PayX here to maximum value.
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
source.setSVar("PayX", Integer.toString(xPay));
lifeAmount = xPay;
} else {
lifeAmount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
lifeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
}
// don't use it if no life to gain
if (lifeAmount <= 0) {
if (!activateForCost && lifeAmount <= 0) {
return false;
}
// don't play if the conditions aren't met, unless it would trigger a
// beneficial sub-condition
if (!sa.getConditions().areMet(sa)) {
if (!activateForCost && !sa.getConditions().areMet(sa)) {
final AbilitySub abSub = sa.getSubAbility();
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
if (!abSub.getConditions().areMet(abSub)) {
@@ -80,7 +72,7 @@ public class LifeGainAi extends SpellAbilityAi {
}
}
if (!ai.canGainLife()) {
if (!activateForCost && !ai.canGainLife()) {
return false;
}
@@ -99,10 +91,9 @@ public class LifeGainAi extends SpellAbilityAi {
return false;
}
if (!lifeCritical && (!game.getPhaseHandler().getNextTurn().equals(ai)
if (!lifeCritical && !activateForCost && (!game.getPhaseHandler().getNextTurn().equals(ai)
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)
&& !ComputerUtil.activateForCost(sa, ai)) {
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)) {
return false;
}
@@ -130,11 +121,11 @@ public class LifeGainAi extends SpellAbilityAi {
* gainLifeDoTriggerAINoCost.
* </p>
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/
@@ -157,7 +148,7 @@ public class LifeGainAi extends SpellAbilityAi {
}
}
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String amountStr = sa.getParam("LifeAmount");
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
// Set PayX here to maximum value.

View File

@@ -1,17 +1,18 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.List;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import java.util.List;
public class LifeLoseAi extends SpellAbilityAi {
@@ -20,7 +21,7 @@ public class LifeLoseAi extends SpellAbilityAi {
List<Player> tgtPlayers = getTargetPlayers(sa);
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String amountStr = sa.getParam("LifeAmount");
int amount = 0;
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
@@ -45,13 +46,13 @@ public class LifeLoseAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String amountStr = sa.getParam("LifeAmount");
// TODO handle proper calculation of X values based on Cost and what
// would be paid
int amount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {
// Set PayX here to maximum value.
@@ -145,7 +146,7 @@ public class LifeLoseAi extends SpellAbilityAi {
}
}
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String amountStr = sa.getParam("LifeAmount");
int amount = 0;
if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) {

View File

@@ -1,25 +1,25 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.Random;
import forge.Card;
import forge.CounterType;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.util.MyRandom;
import java.util.Random;
public class LifeSetAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Random r = MyRandom.getRandom();
// Ability_Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final int myLife = ai.getLife();
final Player opponent = ai.getOpponent();
final int hlife = opponent.getLife();
@@ -45,7 +45,7 @@ public class LifeSetAi extends SpellAbilityAi {
source.setSVar("PayX", Integer.toString(xPay));
amount = xPay;
} else {
amount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
}
// prevent run-away activations - first time will always return true
@@ -105,7 +105,7 @@ public class LifeSetAi extends SpellAbilityAi {
final int myLife = ai.getLife();
final Player opponent = ai.getOpponent();
final int hlife = opponent.getLife();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final String amountStr = sa.getParam("LifeAmount");
@@ -116,7 +116,7 @@ public class LifeSetAi extends SpellAbilityAi {
source.setSVar("PayX", Integer.toString(xPay));
amount = xPay;
} else {
amount = AbilityUtils.calculateAmount(sa.getSourceCard(), amountStr, sa);
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
}
if (source.getName().equals("Eternity Vessel")

View File

@@ -0,0 +1,35 @@
package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class ManaEffectAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
if (ai.getGame().getPhaseHandler().is(PhaseType.MAIN2) && ComputerUtil.activateForCost(sa, ai)) {
return true;
}
return false;
}
/**
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @param af
* a {@link forge.game.ability.AbilityFactory} object.
*
* @return a boolean.
*/
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
return true;
}
}

View File

@@ -1,26 +1,26 @@
package forge.card.ability.ai;
package forge.ai.ability;
import java.util.List;
import forge.Card;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
public class MillAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Cost abCost = sa.getPayCosts();
if (abCost != null) {
@@ -94,7 +94,7 @@ public class MillAi extends SpellAbilityAi {
return false;
}
final int numCards = AbilityUtils.calculateAmount(sa.getSourceCard(), sa.getParam("NumCards"), sa);
final int numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
final List<Card> pLibrary = opp.getCardsIn(ZoneType.Library);
@@ -135,7 +135,7 @@ public class MillAi extends SpellAbilityAi {
return false;
}
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, aiPlayer), aiPlayer.getOpponent()

View File

@@ -1,9 +1,9 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class MustAttackAi extends SpellAbilityAi {

View File

@@ -1,23 +1,22 @@
package forge.card.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.combat.CombatUtil;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
public class MustBlockAi extends SpellAbilityAi {
@Override
@@ -34,7 +33,7 @@ public class MustBlockAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final TargetRestrictions abTgt = sa.getTargetRestrictions();
// only use on creatures that can attack
@@ -44,7 +43,7 @@ public class MustBlockAi extends SpellAbilityAi {
Card attacker = null;
if (sa.hasParam("DefinedAttacker")) {
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("DefinedAttacker"), sa);
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
if (cards.isEmpty()) {
return false;
}

View File

@@ -0,0 +1,39 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.ai.SpellApiToAi;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
/**
* TODO: Write javadoc for this type.
*
*/
public class PeekAndRevealAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
if (sa instanceof AbilityStatic) {
return false;
}
// So far this only appears on Triggers, but will expand
// once things get converted from Dig + NoMove
return true;
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
AbilitySub subAb = sa.getSubAbility();
return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb);
}
}

View File

@@ -1,20 +1,19 @@
package forge.card.ability.ai;
import java.util.List;
package forge.ai.ability;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.Card;
import forge.CardPredicates;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ai.ComputerUtil;
import forge.game.card.Card;
import forge.game.card.CardPredicates;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
/**
* AbilityFactory for Creature Spells.
*
@@ -46,7 +45,7 @@ public class PermanentCreatureAi extends SpellAbilityAi {
for(Card c : cards) {
ArrayList<StaticAbility> statics = c.getStaticAbilities();
for(StaticAbility s : statics) {
final Map<String, String> stabMap = s.getMapParams();
final Map<String, String> stabMap = s.parseParams();
if (!stabMap.get("Mode").equals("Continuous")) {
continue;

View File

@@ -1,10 +1,10 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtil;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
/**
* AbilityFactory for Creature Spells.

View File

@@ -1,19 +1,19 @@
package forge.card.ability.ai;
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Random;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityAi;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.TargetRestrictions;
import forge.game.Game;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class PhasesAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -23,7 +23,7 @@ public class PhasesAi extends SpellAbilityAi {
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
// This still needs to be fleshed out
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final Random r = MyRandom.getRandom();
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
@@ -89,18 +89,18 @@ public class PhasesAi extends SpellAbilityAi {
* </p>
*
* @param tgt
* a {@link forge.card.spellability.TargetRestrictions} object.
* a {@link forge.game.spellability.TargetRestrictions} object.
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @return a boolean.
*/
private boolean phasesPrefTargeting(final TargetRestrictions tgt, final SpellAbility sa,
final boolean mandatory) {
// Card source = sa.getSourceCard();
// Card source = sa.getHostCard();
// List<Card> phaseList =
// AllZoneUtil.getCardsIn(Zone.Battlefield).getTargetableCards(source)
@@ -126,15 +126,15 @@ public class PhasesAi extends SpellAbilityAi {
* </p>
*
* @param af
* a {@link forge.card.ability.AbilityFactory} object.
* a {@link forge.game.ability.AbilityFactory} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
* @return a boolean.
*/
private boolean phasesUnpreferredTargeting(final Game game, final SpellAbility sa, final boolean mandatory) {
final Card source = sa.getSourceCard();
final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions();
List<Card> list = game.getCardsIn(ZoneType.Battlefield);

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