mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
Compare commits
1163 Commits
untapRewor
...
forge-1.6.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22dc1a27a0 | ||
|
|
c374e0ae81 | ||
|
|
fc1b3547b2 | ||
|
|
b0d3fbe783 | ||
|
|
3a02a10258 | ||
|
|
33d83dc0c5 | ||
|
|
d35a2cdead | ||
|
|
507c88269b | ||
|
|
2c7dfc0c41 | ||
|
|
a8ab95f321 | ||
|
|
672cff54ef | ||
|
|
44cbd9af3e | ||
|
|
d3bcfea727 | ||
|
|
20fa72d24e | ||
|
|
10faa20490 | ||
|
|
62c9d187eb | ||
|
|
a5148e8983 | ||
|
|
0bb7a84e55 | ||
|
|
1218786b3e | ||
|
|
62a1ee6d3d | ||
|
|
64f17b49fe | ||
|
|
0d939da60d | ||
|
|
cc321b1b2e | ||
|
|
71bf43b63a | ||
|
|
2d7840aa7d | ||
|
|
a1be9d0278 | ||
|
|
8a18f815b4 | ||
|
|
2b529a07c2 | ||
|
|
c0349d87fb | ||
|
|
c488ab4772 | ||
|
|
d83f8deb7a | ||
|
|
f1f480d16a | ||
|
|
a1ffa625e1 | ||
|
|
d7e38f7fd8 | ||
|
|
a5683d4f12 | ||
|
|
cb1d4b7904 | ||
|
|
c6def2cc5b | ||
|
|
81dc9fecba | ||
|
|
4629817268 | ||
|
|
b6bbe3d18b | ||
|
|
b437db8ffa | ||
|
|
3be58fee75 | ||
|
|
fe09671795 | ||
|
|
9f35da4698 | ||
|
|
3c5a62056b | ||
|
|
43a38e3c6d | ||
|
|
f5a89afa93 | ||
|
|
7a49dd28e1 | ||
|
|
d14f20bc43 | ||
|
|
14a40e493f | ||
|
|
9b8e63501a | ||
|
|
61097e9f80 | ||
|
|
404ce34076 | ||
|
|
e546b6a689 | ||
|
|
7e31e15ead | ||
|
|
62d7824155 | ||
|
|
8c5a90d6e9 | ||
|
|
9345ff6725 | ||
|
|
c45573ff2e | ||
|
|
55300347ce | ||
|
|
c85584445e | ||
|
|
ceeb5d623f | ||
|
|
1f4eff8b1c | ||
|
|
41af869d3c | ||
|
|
2747d93e4c | ||
|
|
5c9251e295 | ||
|
|
fa67ee73a5 | ||
|
|
e1659a4539 | ||
|
|
d5b578b306 | ||
|
|
32b56018a6 | ||
|
|
aa12085345 | ||
|
|
3dd20d9d0b | ||
|
|
8b922fc8f2 | ||
|
|
c88a419e03 | ||
|
|
b9618509be | ||
|
|
1076b1e29f | ||
|
|
4717955afb | ||
|
|
af771d0c2c | ||
|
|
8fea6b4cc0 | ||
|
|
9249029dc1 | ||
|
|
e5d40554e1 | ||
|
|
8b8e39ff41 | ||
|
|
40591b04d2 | ||
|
|
1c1e2416e4 | ||
|
|
bdf9d3f88e | ||
|
|
960760a564 | ||
|
|
f488cb1d0b | ||
|
|
c4e2004af3 | ||
|
|
dcb151f561 | ||
|
|
4550ee26e3 | ||
|
|
ecdea545fc | ||
|
|
643c08850b | ||
|
|
fed6d788cf | ||
|
|
3ccd979b63 | ||
|
|
be52fb772e | ||
|
|
633c106708 | ||
|
|
aec62cbffb | ||
|
|
5b61712d70 | ||
|
|
7fa248db4c | ||
|
|
33f04148d3 | ||
|
|
3aa33be214 | ||
|
|
612045f8c0 | ||
|
|
0bd6b16247 | ||
|
|
eaa7d296d2 | ||
|
|
235162fb84 | ||
|
|
fc4a491111 | ||
|
|
0cd9b1435b | ||
|
|
61184ade4c | ||
|
|
f2e29c9c93 | ||
|
|
e676073548 | ||
|
|
6b315b2571 | ||
|
|
6e028a1c45 | ||
|
|
f07e2bc2ec | ||
|
|
3bc4124fe8 | ||
|
|
bb5af6298e | ||
|
|
3c6c0f534d | ||
|
|
4f3a8590dd | ||
|
|
2a993eb0e0 | ||
|
|
f99e819ff0 | ||
|
|
0f6b65a9ff | ||
|
|
6e94378cc1 | ||
|
|
ff31718839 | ||
|
|
2ab5a286c5 | ||
|
|
e07208e81b | ||
|
|
1ec9c6cade | ||
|
|
04e94f68f5 | ||
|
|
e325f42ca8 | ||
|
|
f3c078b23c | ||
|
|
4f0deea15f | ||
|
|
db396e6b5c | ||
|
|
dfe7ea0ecc | ||
|
|
55cba2513f | ||
|
|
9bbc3c8ef2 | ||
|
|
c473ecb827 | ||
|
|
f654982e38 | ||
|
|
38394ee263 | ||
|
|
c9010d4224 | ||
|
|
cb1b8024b4 | ||
|
|
075a0e99af | ||
|
|
5f4c86697a | ||
|
|
1fee06c843 | ||
|
|
23bc7a629b | ||
|
|
86ec94ca0d | ||
|
|
93f0ade107 | ||
|
|
40bb3d2750 | ||
|
|
86a9cb13d9 | ||
|
|
e1138ff7b5 | ||
|
|
52fe82b834 | ||
|
|
15e3c4f5e5 | ||
|
|
4e5853e29a | ||
|
|
34d0d143c5 | ||
|
|
d37cb22698 | ||
|
|
b5787407b6 | ||
|
|
4d1d79098d | ||
|
|
3fc0c1e20f | ||
|
|
726b8bfbe5 | ||
|
|
0306ab97f1 | ||
|
|
083df572b8 | ||
|
|
12c705476d | ||
|
|
4880d70129 | ||
|
|
f49d5e2352 | ||
|
|
f8b9a31bf2 | ||
|
|
808a43b44d | ||
|
|
f2c523c267 | ||
|
|
b847f7f8c9 | ||
|
|
a9f13ee503 | ||
|
|
e0b7ea4960 | ||
|
|
a41c0454fa | ||
|
|
226716c816 | ||
|
|
3c6f82294b | ||
|
|
600d672b4a | ||
|
|
48cb11566f | ||
|
|
c3be51c117 | ||
|
|
841808d99a | ||
|
|
258f0a2072 | ||
|
|
0b2e60b0c1 | ||
|
|
669bc9ceaa | ||
|
|
93d9d12879 | ||
|
|
9d0f3f325b | ||
|
|
d66b61f0a3 | ||
|
|
9d79b6c262 | ||
|
|
7721b227cb | ||
|
|
d1106f8df7 | ||
|
|
794fcdafe5 | ||
|
|
344126d5aa | ||
|
|
2a72a87ef4 | ||
|
|
7b1af4035d | ||
|
|
931d08d3ac | ||
|
|
ea582088a6 | ||
|
|
468dd46205 | ||
|
|
b03f263fce | ||
|
|
15b69d3d70 | ||
|
|
8004892058 | ||
|
|
f03e386399 | ||
|
|
40478a45ac | ||
|
|
01a17b4b2c | ||
|
|
63f958fa60 | ||
|
|
c916c4da31 | ||
|
|
9e79d7810f | ||
|
|
0dbbcd4edc | ||
|
|
eb039b3bdd | ||
|
|
9be6a5a6a3 | ||
|
|
81c06f230a | ||
|
|
645a11f9c3 | ||
|
|
68ca9ec622 | ||
|
|
b4883eb77e | ||
|
|
7e57ffb62d | ||
|
|
820666b278 | ||
|
|
9d0c2968aa | ||
|
|
d3f2b07c83 | ||
|
|
57ebad948d | ||
|
|
3f1b9df62a | ||
|
|
f990f19795 | ||
|
|
b2e1855949 | ||
|
|
850e54fbbc | ||
|
|
9931b33be7 | ||
|
|
040518d094 | ||
|
|
823c651532 | ||
|
|
b412a361b9 | ||
|
|
022ea1bb75 | ||
|
|
f08f7b09d5 | ||
|
|
16452dd11a | ||
|
|
b0791a0c6a | ||
|
|
2b8d826209 | ||
|
|
900ba9ec5b | ||
|
|
8db0281f91 | ||
|
|
9637091ab5 | ||
|
|
45771ec8c0 | ||
|
|
5c3ff8297e | ||
|
|
722f512e53 | ||
|
|
0cceaf0485 | ||
|
|
23d0d70478 | ||
|
|
2eea49a8e4 | ||
|
|
a7d8a62a69 | ||
|
|
c8fdc6f272 | ||
|
|
9990062ec1 | ||
|
|
6817b4af66 | ||
|
|
133f1656d8 | ||
|
|
599d75351c | ||
|
|
c42202a140 | ||
|
|
d6c0cf6ef8 | ||
|
|
72ad7804ca | ||
|
|
6bf1af44d8 | ||
|
|
a74f1a1427 | ||
|
|
df88cb970f | ||
|
|
452dee7e11 | ||
|
|
f6a6c33193 | ||
|
|
6a98c8f371 | ||
|
|
9546594e8d | ||
|
|
23f5b7696a | ||
|
|
93ae66ad39 | ||
|
|
4d3ce706a5 | ||
|
|
5b57ef3531 | ||
|
|
eadc0afe1a | ||
|
|
687b5f18ef | ||
|
|
25178869ae | ||
|
|
eedcad8280 | ||
|
|
50c94755fb | ||
|
|
ec79434759 | ||
|
|
4192d56909 | ||
|
|
2c82aedb74 | ||
|
|
049ed5f9c4 | ||
|
|
b183862e8d | ||
|
|
57bc3f930a | ||
|
|
48a3b4d652 | ||
|
|
bee93cbab3 | ||
|
|
9d0a683db8 | ||
|
|
e163132a1e | ||
|
|
d35cb8de32 | ||
|
|
162a27f5a1 | ||
|
|
d1c2096024 | ||
|
|
50c8e129d0 | ||
|
|
01bd5e5d54 | ||
|
|
562a9e1f8d | ||
|
|
548811b370 | ||
|
|
6ae29a311a | ||
|
|
9934e20f8b | ||
|
|
e7a5ee57f1 | ||
|
|
06f2f416e0 | ||
|
|
8bda30179e | ||
|
|
9806c2c63c | ||
|
|
9d5d6dd6d2 | ||
|
|
8fecf07c47 | ||
|
|
a539d9bf5a | ||
|
|
ddc531c5f2 | ||
|
|
ef0a1a843d | ||
|
|
bf200de73b | ||
|
|
9a56fb8998 | ||
|
|
4a3740dac7 | ||
|
|
31145ef2eb | ||
|
|
23ab256f7d | ||
|
|
0f435b58a1 | ||
|
|
5d1a0421fd | ||
|
|
55ed4c1bc0 | ||
|
|
22a6a72f12 | ||
|
|
7c38ac7dfa | ||
|
|
d29dc72831 | ||
|
|
e062c83c02 | ||
|
|
c25971bba9 | ||
|
|
92a11d801c | ||
|
|
87e6b38fa5 | ||
|
|
2dcc56934d | ||
|
|
f5cb80cec5 | ||
|
|
1d71b1efea | ||
|
|
62e206f8ed | ||
|
|
0de140538c | ||
|
|
55167c5dca | ||
|
|
9c78dbfde4 | ||
|
|
0ca0458656 | ||
|
|
1b86738d10 | ||
|
|
ccfbaf5b3d | ||
|
|
16ecae29f0 | ||
|
|
7a9fb7a4d8 | ||
|
|
aef84aed51 | ||
|
|
a8b1ce3c32 | ||
|
|
7dde40beff | ||
|
|
ccf39adde4 | ||
|
|
5b37a6fb53 | ||
|
|
942225291f | ||
|
|
4787784d15 | ||
|
|
093f157f77 | ||
|
|
78f3614c0a | ||
|
|
ae060a1b76 | ||
|
|
244b3d6d24 | ||
|
|
01bf10c719 | ||
|
|
5718072f60 | ||
|
|
1dfd977238 | ||
|
|
9030a39d1d | ||
|
|
d74a4143f3 | ||
|
|
f6509dd30e | ||
|
|
6c512dd451 | ||
|
|
00c83f1d25 | ||
|
|
28615040e8 | ||
|
|
0482d4636b | ||
|
|
86de537fcb | ||
|
|
fb7fdb65f2 | ||
|
|
15c36118d6 | ||
|
|
092eae52fe | ||
|
|
ac58058938 | ||
|
|
64117ad1fc | ||
|
|
317833af6d | ||
|
|
54d367ae8c | ||
|
|
1381de7d55 | ||
|
|
caacde3aad | ||
|
|
49e55f1326 | ||
|
|
5082da429f | ||
|
|
ea4e821f51 | ||
|
|
83c0195648 | ||
|
|
5c465f8bf0 | ||
|
|
c670d175d9 | ||
|
|
415e2a1623 | ||
|
|
dff5962b8e | ||
|
|
d7febae2e1 | ||
|
|
a757e033d9 | ||
|
|
c3b05157c5 | ||
|
|
cb3430b806 | ||
|
|
a86dad40c3 | ||
|
|
04066b9bc2 | ||
|
|
6580883ccc | ||
|
|
fa30bbe67c | ||
|
|
e39b70a5d8 | ||
|
|
45f33106f2 | ||
|
|
415f911e46 | ||
|
|
7b4b869992 | ||
|
|
3d57759d73 | ||
|
|
ad0bec318f | ||
|
|
9571b2695f | ||
|
|
f09bcc4a66 | ||
|
|
fc424b1bdd | ||
|
|
8a9b7c562c | ||
|
|
4d9c730f6f | ||
|
|
9a1223c228 | ||
|
|
d7787dc974 | ||
|
|
a3f30d899b | ||
|
|
ca48e75a9d | ||
|
|
e5051d2635 | ||
|
|
c5a6145ed7 | ||
|
|
b2398ec8e0 | ||
|
|
1b739ab6e5 | ||
|
|
2e20a88b3b | ||
|
|
9d6798e698 | ||
|
|
f3078059b2 | ||
|
|
fe2c3c90d6 | ||
|
|
b994bce116 | ||
|
|
f86d6fc2a8 | ||
|
|
747fa3eb3c | ||
|
|
f723f3347a | ||
|
|
8d1367461d | ||
|
|
3fd3b2d642 | ||
|
|
0f853983c9 | ||
|
|
1d20aeddda | ||
|
|
ba10aa935a | ||
|
|
56c63bc6d1 | ||
|
|
ef70434277 | ||
|
|
313112a488 | ||
|
|
9030939938 | ||
|
|
c87fdb820b | ||
|
|
fd260af645 | ||
|
|
f7904aac2b | ||
|
|
c338988650 | ||
|
|
07c4e1b57b | ||
|
|
a5b58aa3ae | ||
|
|
31fc6e3650 | ||
|
|
75dc2674d6 | ||
|
|
f212cf1dc8 | ||
|
|
0a3f1bf43c | ||
|
|
1039657a82 | ||
|
|
43299a5298 | ||
|
|
5f8f82fc93 | ||
|
|
171cc23dba | ||
|
|
9e5e05ae3c | ||
|
|
e67b63375c | ||
|
|
dda23854a3 | ||
|
|
625673e167 | ||
|
|
d8845a413d | ||
|
|
30f80f75df | ||
|
|
c4c9d5e042 | ||
|
|
b56de30ea2 | ||
|
|
2782046bed | ||
|
|
116fa84b16 | ||
|
|
bdb10fe59c | ||
|
|
7aa93d61b3 | ||
|
|
74ed34b6dd | ||
|
|
faf9ace2ab | ||
|
|
1602a1e78a | ||
|
|
c6b0a0e072 | ||
|
|
311eaa70e3 | ||
|
|
659b1c7a63 | ||
|
|
c29432cef2 | ||
|
|
7256cc2f8f | ||
|
|
a9693495d7 | ||
|
|
feae4ebf81 | ||
|
|
e97ca38cbe | ||
|
|
eff1be31da | ||
|
|
9ee07411d0 | ||
|
|
a0e94f2f51 | ||
|
|
be99725251 | ||
|
|
25e9b942b1 | ||
|
|
ac041a79f6 | ||
|
|
c59d99b8ae | ||
|
|
3885e02d60 | ||
|
|
8b432cffd9 | ||
|
|
51e5ce0e17 | ||
|
|
f2f984aff6 | ||
|
|
a0d8a8a241 | ||
|
|
b8101d0387 | ||
|
|
55d02d5adb | ||
|
|
cf91b69d90 | ||
|
|
c2fdd86c29 | ||
|
|
81af887694 | ||
|
|
85ef3aaa23 | ||
|
|
c8610da04b | ||
|
|
490e204060 | ||
|
|
b2044944ff | ||
|
|
cbf7cc0b4c | ||
|
|
b9dbc14a4d | ||
|
|
d1364066db | ||
|
|
72d16f0636 | ||
|
|
3aa43b8246 | ||
|
|
ad288f5303 | ||
|
|
fcc96cf303 | ||
|
|
ca1504b839 | ||
|
|
e265cb84cc | ||
|
|
6452189938 | ||
|
|
3d7bd388e1 | ||
|
|
21cfcac0c9 | ||
|
|
5eae6acae0 | ||
|
|
1c0cb3a885 | ||
|
|
b43fbcfabc | ||
|
|
76146d3c6b | ||
|
|
641a443c82 | ||
|
|
e1027ce7ef | ||
|
|
46422a163b | ||
|
|
556d8bfd48 | ||
|
|
5b8e709271 | ||
|
|
6fce5cc5a4 | ||
|
|
b504b60112 | ||
|
|
c8b4e574b8 | ||
|
|
32f117c7ec | ||
|
|
95d3642d2e | ||
|
|
c2ed631327 | ||
|
|
1f90f4e470 | ||
|
|
d397db9cfb | ||
|
|
b5616362b0 | ||
|
|
5d2a32c114 | ||
|
|
5a64b6c297 | ||
|
|
095e720435 | ||
|
|
6e4908acc3 | ||
|
|
b053f79de3 | ||
|
|
33564d023c | ||
|
|
57f445b791 | ||
|
|
f79e3cad2a | ||
|
|
0d8a2d4a1d | ||
|
|
2df1770781 | ||
|
|
51ec73bdf9 | ||
|
|
4299db4041 | ||
|
|
c3a95a07a1 | ||
|
|
38d614a567 | ||
|
|
c0d77a7fdf | ||
|
|
806d62129b | ||
|
|
dffd0cd62c | ||
|
|
333a96bb8d | ||
|
|
1aadc7991f | ||
|
|
a41dc4bbd9 | ||
|
|
6529e02c83 | ||
|
|
476c2e4ce3 | ||
|
|
706055f1b5 | ||
|
|
18c964ee3c | ||
|
|
2a6ba11552 | ||
|
|
ef9fb6ea68 | ||
|
|
7055c11d16 | ||
|
|
b0bc918bc1 | ||
|
|
bc030355fc | ||
|
|
873c748e76 | ||
|
|
9a987ec7a3 | ||
|
|
b7d2790a8b | ||
|
|
98c7b47681 | ||
|
|
992c81263a | ||
|
|
d9e9076fb4 | ||
|
|
938d5259a4 | ||
|
|
8d64694ac7 | ||
|
|
1a55341dd3 | ||
|
|
3f2d70c315 | ||
|
|
fe005f3232 | ||
|
|
38f573bfce | ||
|
|
efc2d3a8e6 | ||
|
|
b2f0f0ca84 | ||
|
|
fd347c10d0 | ||
|
|
0aacc3e2da | ||
|
|
fea69dfe06 | ||
|
|
e8e45cc378 | ||
|
|
5d6047ca7d | ||
|
|
6ad5e6d974 | ||
|
|
82ced80a3e | ||
|
|
79347c0f9c | ||
|
|
7b060320b5 | ||
|
|
38f4d4aa1b | ||
|
|
3dc80a8342 | ||
|
|
8dbfd9050c | ||
|
|
c5604bddac | ||
|
|
789401ed24 | ||
|
|
c0d967a467 | ||
|
|
75d49554da | ||
|
|
d228429b56 | ||
|
|
9630d1a26b | ||
|
|
2922f99ae3 | ||
|
|
cf7a7177c7 | ||
|
|
f5dcbf4e6e | ||
|
|
60c946a501 | ||
|
|
28393d4571 | ||
|
|
eacfa96df4 | ||
|
|
576f83b5dc | ||
|
|
b35ea79083 | ||
|
|
45f2193e2a | ||
|
|
b329786659 | ||
|
|
adf181aaec | ||
|
|
a95fff5ae3 | ||
|
|
f401c3900c | ||
|
|
1f01e2ae16 | ||
|
|
d5cc3289dc | ||
|
|
9a0ba159b0 | ||
|
|
71dfe3146b | ||
|
|
9ed01ebbb6 | ||
|
|
0e7af09df3 | ||
|
|
3c4844e46d | ||
|
|
965b67577a | ||
|
|
8bcb38d4fc | ||
|
|
071873718e | ||
|
|
7583b67bda | ||
|
|
96d3ddf9b5 | ||
|
|
dc0ddf542b | ||
|
|
724e483ae3 | ||
|
|
910fef3ecd | ||
|
|
65288905d0 | ||
|
|
82d7171a92 | ||
|
|
20b51f59b0 | ||
|
|
16c06c7d7d | ||
|
|
0aa20cb3f4 | ||
|
|
71787b7059 | ||
|
|
a9407467a5 | ||
|
|
896eb79d2c | ||
|
|
fb6f34d67e | ||
|
|
9e9570d191 | ||
|
|
1b130ab157 | ||
|
|
74005529f5 | ||
|
|
4186a365cb | ||
|
|
4366db1acc | ||
|
|
4679e00a9e | ||
|
|
51960f0e28 | ||
|
|
a27f69b1c5 | ||
|
|
8ab01cce3e | ||
|
|
e10235aae9 | ||
|
|
05591c6bd9 | ||
|
|
a25e469291 | ||
|
|
ebd14944c6 | ||
|
|
1b08fc3592 | ||
|
|
57457946e5 | ||
|
|
918a39ab60 | ||
|
|
2cc39c2580 | ||
|
|
3aca94dc9b | ||
|
|
65ad9d61d9 | ||
|
|
138e3a4661 | ||
|
|
41844ee7e3 | ||
|
|
d4f2594e26 | ||
|
|
babc9de575 | ||
|
|
11a5683025 | ||
|
|
b6f8f89c7a | ||
|
|
0885fe5898 | ||
|
|
8aa1f049ab | ||
|
|
f7aa7bf580 | ||
|
|
b74033fe17 | ||
|
|
596296cabf | ||
|
|
adcd86e7d7 | ||
|
|
308768ae50 | ||
|
|
235636ca4a | ||
|
|
359ce5e39d | ||
|
|
c6e13e98b3 | ||
|
|
0cc9687e39 | ||
|
|
3d59f1832c | ||
|
|
58fdd58e9a | ||
|
|
ea661c4fbb | ||
|
|
28abe52106 | ||
|
|
e689fd8721 | ||
|
|
fea6c85e97 | ||
|
|
4e80a1b818 | ||
|
|
bdde3520f4 | ||
|
|
165ae649b1 | ||
|
|
0536ebf1c8 | ||
|
|
fb176abe05 | ||
|
|
28f0232736 | ||
|
|
4c29fd8faf | ||
|
|
df8d289ee0 | ||
|
|
203de48dd4 | ||
|
|
97f763b1a0 | ||
|
|
9fd8969ea5 | ||
|
|
d70e699c81 | ||
|
|
39bf8f5313 | ||
|
|
68eb8e2510 | ||
|
|
a20800cfb3 | ||
|
|
adce811a3d | ||
|
|
084a7ae73e | ||
|
|
14a697269b | ||
|
|
7de8cdee90 | ||
|
|
20333491f7 | ||
|
|
d529c55277 | ||
|
|
9e3bb1ca72 | ||
|
|
0caf41f1d6 | ||
|
|
817aadb995 | ||
|
|
c6016c2728 | ||
|
|
492cab9c06 | ||
|
|
64dd84b560 | ||
|
|
496768b566 | ||
|
|
b2fcc6e743 | ||
|
|
1584fcbb82 | ||
|
|
2860111154 | ||
|
|
cfdbb98cf9 | ||
|
|
87ec3a528b | ||
|
|
8ebabd978f | ||
|
|
51fae70e24 | ||
|
|
2f0024a8dd | ||
|
|
cd498e408c | ||
|
|
6d193c315e | ||
|
|
c81d9f6bc2 | ||
|
|
e13e5d0b16 | ||
|
|
ceb892d610 | ||
|
|
2690a4d548 | ||
|
|
59a8e550d6 | ||
|
|
4793425c58 | ||
|
|
e47ee567bf | ||
|
|
c9f33415e1 | ||
|
|
d1a0c9f1ff | ||
|
|
40b43b00c2 | ||
|
|
cea6d3a9df | ||
|
|
ecda5377a3 | ||
|
|
fb9c7290b2 | ||
|
|
5b185d2fe3 | ||
|
|
c4712e705c | ||
|
|
d4656b619f | ||
|
|
e8c5a58d85 | ||
|
|
143cfa04de | ||
|
|
ef752c5db6 | ||
|
|
f4ba889c84 | ||
|
|
f20e61520b | ||
|
|
88f082e261 | ||
|
|
6304fa4091 | ||
|
|
4b01074d0f | ||
|
|
65c8e47e26 | ||
|
|
80c6e30691 | ||
|
|
8cfc922d97 | ||
|
|
92e6c303ad | ||
|
|
762dc60600 | ||
|
|
373b81072a | ||
|
|
9bf2d57715 | ||
|
|
ef71a7befc | ||
|
|
dc34c4e985 | ||
|
|
9a495e516a | ||
|
|
6fcd744132 | ||
|
|
70bc280cf1 | ||
|
|
470fe4b877 | ||
|
|
86f472d29f | ||
|
|
b54ce90df1 | ||
|
|
db270d0c7c | ||
|
|
9d5837bb87 | ||
|
|
12848af745 | ||
|
|
2d28139ca0 | ||
|
|
272663dc4a | ||
|
|
d4a47260cb | ||
|
|
7023521fb2 | ||
|
|
97fe8dc35b | ||
|
|
fb0c0bd807 | ||
|
|
2285541b6d | ||
|
|
e965b899c0 | ||
|
|
011811d183 | ||
|
|
65efbc57f3 | ||
|
|
437587766b | ||
|
|
a1fe5c5db1 | ||
|
|
c44d91281e | ||
|
|
56fdf15b6f | ||
|
|
19be99d106 | ||
|
|
2e25c83775 | ||
|
|
dae91de27e | ||
|
|
15424d2fea | ||
|
|
cd1d744e1f | ||
|
|
dc77591571 | ||
|
|
074467e65e | ||
|
|
c81f93551e | ||
|
|
1f6cbd7d19 | ||
|
|
bd35e224a4 | ||
|
|
ae5986f101 | ||
|
|
5cbf5649c0 | ||
|
|
3da4db3edc | ||
|
|
47121019d4 | ||
|
|
c97b390161 | ||
|
|
f97312dcf2 | ||
|
|
0eed744018 | ||
|
|
655737dcb5 | ||
|
|
488fa4a4dd | ||
|
|
8344522e12 | ||
|
|
e1e085d610 | ||
|
|
e35904e727 | ||
|
|
6dc8d7648a | ||
|
|
c296b32209 | ||
|
|
69f2bb70e8 | ||
|
|
a499e0a551 | ||
|
|
bc99962621 | ||
|
|
bf9b7054b3 | ||
|
|
50be973e57 | ||
|
|
108c39ef7c | ||
|
|
ae6ea42aa9 | ||
|
|
6c4ceccc30 | ||
|
|
fde7d6ff09 | ||
|
|
1af3505d0b | ||
|
|
047f018da4 | ||
|
|
a90e9f05e2 | ||
|
|
7db7e10934 | ||
|
|
397b340701 | ||
|
|
a050b584ac | ||
|
|
d68c740e1d | ||
|
|
b807ce30c4 | ||
|
|
a4bd13d050 | ||
|
|
244960d0d7 | ||
|
|
267c8c6b0d | ||
|
|
06fc409234 | ||
|
|
77ed24a203 | ||
|
|
ed3c519404 | ||
|
|
9fcf8e82ef | ||
|
|
dd4df9baaa | ||
|
|
4384b1621b | ||
|
|
0e1c82a31f | ||
|
|
b5babd5451 | ||
|
|
5cb8502566 | ||
|
|
b7c4720030 | ||
|
|
074243891c | ||
|
|
1117ff1e7c | ||
|
|
5dd24341e1 | ||
|
|
19d4f36faa | ||
|
|
ac9f8e3191 | ||
|
|
9d193fb3bb | ||
|
|
c20b0c3620 | ||
|
|
8409a7e6d1 | ||
|
|
3d59118b94 | ||
|
|
e0fb373b3d | ||
|
|
f0c8deb25e | ||
|
|
b288c9f61a | ||
|
|
823c278cd7 | ||
|
|
ba4ba8d051 | ||
|
|
467268c451 | ||
|
|
3f1f8321d3 | ||
|
|
5730a407c3 | ||
|
|
22974f8bf4 | ||
|
|
6414347d91 | ||
|
|
113f891348 | ||
|
|
cbab3e6000 | ||
|
|
17663bd2c0 | ||
|
|
c226a4dbd5 | ||
|
|
0c2d0e6cb6 | ||
|
|
d63106a71a | ||
|
|
2c26e3f509 | ||
|
|
50e0387cda | ||
|
|
b05dc40a1c | ||
|
|
3846be2321 | ||
|
|
2fa3179a31 | ||
|
|
2617ff339d | ||
|
|
e36311db44 | ||
|
|
2449166c36 | ||
|
|
eb16486ee6 | ||
|
|
e0c858a3d6 | ||
|
|
71749cef3f | ||
|
|
15839e343e | ||
|
|
9a98b39e94 | ||
|
|
9c6c0f1861 | ||
|
|
318d919111 | ||
|
|
c753a1c875 | ||
|
|
e304337a62 | ||
|
|
3768ca8f77 | ||
|
|
12f76827e9 | ||
|
|
3c5f464039 | ||
|
|
9f2304b1a5 | ||
|
|
8a0ba4f27a | ||
|
|
43dc6d35c0 | ||
|
|
d04c593adc | ||
|
|
4e1b7e886f | ||
|
|
8544e805bb | ||
|
|
7e18cbca97 | ||
|
|
f5734bb626 | ||
|
|
08823bd94c | ||
|
|
04cfc7a81f | ||
|
|
91656fd7c0 | ||
|
|
e984bd5708 | ||
|
|
eb0c0f3f84 | ||
|
|
acac0ee5e2 | ||
|
|
19b7daeaee | ||
|
|
c933e722f3 | ||
|
|
1536377eb2 | ||
|
|
a11640cbc9 | ||
|
|
d51fe454b5 | ||
|
|
550fd937f1 | ||
|
|
bd1add4d47 | ||
|
|
944eb38002 | ||
|
|
ddf481f4fd | ||
|
|
2e7d84b58f | ||
|
|
db853cb5ba | ||
|
|
d0d652b427 | ||
|
|
231e048936 | ||
|
|
e95982a043 | ||
|
|
1a7b2677f3 | ||
|
|
b77bb5768d | ||
|
|
2be9fc65b1 | ||
|
|
c4e04fe003 | ||
|
|
964e06b8cb | ||
|
|
78d6847db5 | ||
|
|
25d6ce6763 | ||
|
|
fc26b5d3a9 | ||
|
|
9e4dc631e8 | ||
|
|
b6bff03ad3 | ||
|
|
394a365497 | ||
|
|
d5de2f387b | ||
|
|
5106de0abe | ||
|
|
510c81d251 | ||
|
|
b4437636a8 | ||
|
|
cc64997832 | ||
|
|
c35afecce7 | ||
|
|
2d9e8cc2af | ||
|
|
a9c4842b96 | ||
|
|
c8b21a12ed | ||
|
|
d0a75c0943 | ||
|
|
c75d3b2d04 | ||
|
|
50d8c03f8b | ||
|
|
dde3e08e6a | ||
|
|
2dd3385849 | ||
|
|
bd6cc759c7 | ||
|
|
39f695f120 | ||
|
|
61839df123 | ||
|
|
e6b3ec048a | ||
|
|
4fb5e7d305 | ||
|
|
8d4a80089e | ||
|
|
539e321c53 | ||
|
|
737700719a | ||
|
|
b2bc26142a | ||
|
|
9a125bf4f5 | ||
|
|
b53135d83a | ||
|
|
86262f3810 | ||
|
|
9237de38df | ||
|
|
18d5d7da27 | ||
|
|
cba653d891 | ||
|
|
9ea265625e | ||
|
|
d4aa0ecae4 | ||
|
|
bb5ee52133 | ||
|
|
33d5d45d3c | ||
|
|
87bad70939 | ||
|
|
047bc9c5cd | ||
|
|
f098e333d2 | ||
|
|
785835632d | ||
|
|
360b72fd91 | ||
|
|
19082049e7 | ||
|
|
eacc29672f | ||
|
|
c7b6ac1670 | ||
|
|
0aa54cea23 | ||
|
|
9fe50f6c66 | ||
|
|
2bb84cf79f | ||
|
|
8d0ade1eab | ||
|
|
242c7c3b21 | ||
|
|
ff272b89fd | ||
|
|
4e83e73dca | ||
|
|
7d1ffa4e90 | ||
|
|
aab83d882a | ||
|
|
4f69b673bf | ||
|
|
7d17e58e1f | ||
|
|
3d6677bbd5 | ||
|
|
30d57a5745 | ||
|
|
2fb2b64d33 | ||
|
|
1dc8fed6e8 | ||
|
|
44c6605804 | ||
|
|
560f8ac14a | ||
|
|
b99dea604d | ||
|
|
f3ff419462 | ||
|
|
d79c2469ec | ||
|
|
94de9e7cf2 | ||
|
|
a9d9c800b8 | ||
|
|
678d09558c | ||
|
|
831edab34b | ||
|
|
5b64b41f21 | ||
|
|
78a5827976 | ||
|
|
a9b6faeb7c | ||
|
|
bebc58d91d | ||
|
|
2c70b8b7e0 | ||
|
|
07d7b7cb58 | ||
|
|
4c80dd3c42 | ||
|
|
0b436479be | ||
|
|
036cd90516 | ||
|
|
f0076117df | ||
|
|
f8b4a843c5 | ||
|
|
5a24092dc0 | ||
|
|
bf1c32f12a | ||
|
|
f0fb6539ef | ||
|
|
a85a761bee | ||
|
|
2c61dc9a75 | ||
|
|
9516929495 | ||
|
|
5350a4c476 | ||
|
|
26dc246c52 | ||
|
|
ad6716f00e | ||
|
|
dffb63d95f | ||
|
|
5069c71dd8 | ||
|
|
287abb9f37 | ||
|
|
7f8325fb6c | ||
|
|
6c38674716 | ||
|
|
54fdb69e88 | ||
|
|
068cee2326 | ||
|
|
e9c2e1034c | ||
|
|
f453c2af78 | ||
|
|
cc3c631cad | ||
|
|
7cfd060ed8 | ||
|
|
c77f5e9b61 | ||
|
|
f18bd75257 | ||
|
|
555a1f8411 | ||
|
|
c600dc3b4f | ||
|
|
0f138a1bf5 | ||
|
|
1c19f36637 | ||
|
|
d19973208c | ||
|
|
0a9078a2ec | ||
|
|
bf2368ee44 | ||
|
|
d0a23fbf14 | ||
|
|
015f561740 | ||
|
|
cff4a29f1c | ||
|
|
f50ee0ba36 | ||
|
|
fe5d8e537c | ||
|
|
d943891e4f | ||
|
|
da3d1d7099 | ||
|
|
86dd21ae7b | ||
|
|
2d30184f76 | ||
|
|
271063934a | ||
|
|
3fb601adfb | ||
|
|
757e11af42 | ||
|
|
7ec12fb58d | ||
|
|
6c9cf02884 | ||
|
|
0c904e208a | ||
|
|
d12a898333 | ||
|
|
0e665dc673 | ||
|
|
02299a0400 | ||
|
|
86d7069626 | ||
|
|
776b865761 | ||
|
|
a3a4ef22df | ||
|
|
c2db78baf7 | ||
|
|
9a30d78f54 | ||
|
|
db98ce160b | ||
|
|
ce8b5b53e0 | ||
|
|
0c4055726c | ||
|
|
64ae4bae0c | ||
|
|
044cc793e8 | ||
|
|
ca81b2b55c | ||
|
|
e310dc30d7 | ||
|
|
f239755249 | ||
|
|
57686c0554 | ||
|
|
5ecde572c3 | ||
|
|
23e9974950 | ||
|
|
ec98d128f1 | ||
|
|
5c07951604 | ||
|
|
7be34625f6 | ||
|
|
f15762b746 | ||
|
|
e365e78756 | ||
|
|
5f6cb893ef | ||
|
|
3b636be2fe | ||
|
|
1af940b034 | ||
|
|
8cb1789e60 | ||
|
|
01782de3a6 | ||
|
|
3a7e35c51d | ||
|
|
bff24a1d3d | ||
|
|
8a075000a2 | ||
|
|
5e0761d085 | ||
|
|
2184ddf1bb | ||
|
|
fd032f6ccd | ||
|
|
164c819523 | ||
|
|
987043ead2 | ||
|
|
4aa9c224d0 | ||
|
|
2ac6fa4542 | ||
|
|
2797f95cd3 | ||
|
|
5eb9be6248 | ||
|
|
23eebf9037 | ||
|
|
b59adab68d | ||
|
|
b2d44105be | ||
|
|
2235546f2a | ||
|
|
59d104f68b | ||
|
|
6abdfd391d | ||
|
|
a6ff0b5b10 | ||
|
|
02969cfe5b | ||
|
|
c6a2c35850 | ||
|
|
4c0a71f37d | ||
|
|
74e3bd1895 | ||
|
|
121c9f5012 | ||
|
|
7b1cd816b7 | ||
|
|
02ae8f8108 | ||
|
|
6a76cc8bc6 | ||
|
|
9b9c38126e | ||
|
|
e4ee0c768f | ||
|
|
340de153c8 | ||
|
|
2bf477102d | ||
|
|
42a15b40b3 | ||
|
|
a2589cd433 | ||
|
|
7ec7025ed4 | ||
|
|
5edeb6df94 | ||
|
|
001a1981cf | ||
|
|
446fb59473 | ||
|
|
3b58d6df42 | ||
|
|
a80c683901 | ||
|
|
a5b65eaaed | ||
|
|
31182289b7 | ||
|
|
85eb740264 | ||
|
|
4318e23a40 | ||
|
|
b1b01f2426 | ||
|
|
4ca7352d5c | ||
|
|
84905bd726 | ||
|
|
18e16368be | ||
|
|
7ed84c4c3f | ||
|
|
f334211395 | ||
|
|
164ca8541e | ||
|
|
42f4126aff | ||
|
|
5790e29daa | ||
|
|
88a4a2c6cf | ||
|
|
90b72fc11e | ||
|
|
00391df1f0 | ||
|
|
15de0c0bba | ||
|
|
a2fdce9be9 | ||
|
|
fa6fce9589 | ||
|
|
cc1f03fc94 | ||
|
|
8b25f6f129 | ||
|
|
b37937421c | ||
|
|
df7be19487 | ||
|
|
f17d2bf7fc | ||
|
|
3369db8150 | ||
|
|
408c9df2df | ||
|
|
63b6d665a1 | ||
|
|
cfc8b7fb28 | ||
|
|
1497345a79 | ||
|
|
bbd2940b63 | ||
|
|
b9c65fb8ee | ||
|
|
0b37b4e543 | ||
|
|
4c3e4f2170 | ||
|
|
dc12c50c1a | ||
|
|
2b986f5bac | ||
|
|
2accf7543e | ||
|
|
ba37189410 | ||
|
|
d578eee402 | ||
|
|
5107d89ef5 | ||
|
|
72b8b5c98e | ||
|
|
402885391f | ||
|
|
b4d153ab3b | ||
|
|
1a0cb62ac8 | ||
|
|
c0baf70c59 | ||
|
|
166cf2623c | ||
|
|
8567b69073 | ||
|
|
3a4271e66d | ||
|
|
8b723aebd9 | ||
|
|
eb59d6c86b | ||
|
|
a291b75dd9 | ||
|
|
f1a76e1e76 | ||
|
|
a391f7414f | ||
|
|
299de54ba5 | ||
|
|
37bae14dfd | ||
|
|
d780aa43d4 | ||
|
|
91534776d1 | ||
|
|
ef82e11c53 | ||
|
|
31bb611ecf | ||
|
|
762481e28a | ||
|
|
25194c7abe | ||
|
|
967af29c47 | ||
|
|
ae9ee57d79 | ||
|
|
17706bd5e4 | ||
|
|
9ed54f726c | ||
|
|
53c88f0302 | ||
|
|
ad1cc78578 | ||
|
|
f94a613eef | ||
|
|
e6fa89206a | ||
|
|
b010da744c | ||
|
|
d53eb3a3fe | ||
|
|
ea25ad2c3b | ||
|
|
ac7f34d3f5 | ||
|
|
4969547938 | ||
|
|
6808be7a42 | ||
|
|
70986149ef | ||
|
|
bc859b3e52 | ||
|
|
eb376b8eb3 | ||
|
|
9fce28bed7 | ||
|
|
0bcc0e9248 | ||
|
|
aecee3cea2 | ||
|
|
3432b9fdcd | ||
|
|
8bde4d5f92 | ||
|
|
67c596726f | ||
|
|
60fb1c0d02 | ||
|
|
3261e1a260 | ||
|
|
2e1fa8fa40 | ||
|
|
cadb2ae791 | ||
|
|
2d3990df81 | ||
|
|
23d4904ee7 | ||
|
|
f66f51b25e | ||
|
|
826ff27bd1 | ||
|
|
132aa8d49a | ||
|
|
67148cd84c | ||
|
|
941e3a7537 | ||
|
|
4487952c28 | ||
|
|
5089663b07 | ||
|
|
3ec5bfd4ff | ||
|
|
adb1ef35d9 | ||
|
|
df85278a44 | ||
|
|
7a04669560 | ||
|
|
2e24a1bde4 | ||
|
|
16134cb857 | ||
|
|
d396a8755b | ||
|
|
7617b95fae | ||
|
|
ecdda5b678 | ||
|
|
2a16a29ea2 | ||
|
|
81d7bec2cd | ||
|
|
87ffbe4c20 | ||
|
|
94a0fb20d2 | ||
|
|
7313670d8f | ||
|
|
5047e7732c | ||
|
|
98a459ac9c | ||
|
|
eec3f05dc7 | ||
|
|
4db2fd4ee7 | ||
|
|
41e0760678 | ||
|
|
36b9689cf8 | ||
|
|
c5b09815e3 | ||
|
|
3da7b0f8e6 | ||
|
|
1aa0d321cc |
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.33</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-ai</artifactId>
|
||||
|
||||
@@ -1281,7 +1281,8 @@ public class AiBlockController {
|
||||
oppCreatureCount = ComputerUtil.countUsefulCreatures(attackersLeft.get(0).getController());
|
||||
}
|
||||
|
||||
if (attacker.getOwner().equals(ai) && "6".equals(attacker.getSVar("SacMe"))) {
|
||||
if (attacker != null && attacker.getOwner() != null)
|
||||
if (attacker.getOwner().equals(ai) && "6".equals(attacker.getSVar("SacMe"))) {
|
||||
// Temporarily controlled object - don't trade with it
|
||||
// TODO: find a more reliable way to figure out that control will be reestablished next turn
|
||||
return false;
|
||||
|
||||
@@ -579,7 +579,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
@Override
|
||||
public PaymentDecision visit(CostReveal cost) {
|
||||
final String type = cost.getType();
|
||||
CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
|
||||
CardCollectionView hand = player.getCardsIn(cost.getRevealFrom());
|
||||
|
||||
if (cost.payCostFromSource()) {
|
||||
if (!hand.contains(source)) {
|
||||
|
||||
@@ -91,9 +91,6 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -109,7 +106,7 @@ public class ComputerUtil {
|
||||
if (chooseTargets != null) {
|
||||
chooseTargets.run();
|
||||
}
|
||||
if (sa.hasParam("Bestow")) {
|
||||
if (sa.isBestow()) {
|
||||
sa.getHostCard().animateBestow();
|
||||
}
|
||||
|
||||
@@ -219,9 +216,6 @@ public class ComputerUtil {
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -246,9 +240,6 @@ public class ComputerUtil {
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -267,9 +258,6 @@ public class ComputerUtil {
|
||||
|
||||
final Card source = newSA.getHostCard();
|
||||
if (newSA.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(newSA);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
|
||||
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
||||
@@ -290,9 +278,6 @@ public class ComputerUtil {
|
||||
if (ComputerUtilCost.canPayCost(sa, ai)) {
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
source.setCastSA(sa);
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
@@ -2338,7 +2323,7 @@ public class ComputerUtil {
|
||||
return chosen;
|
||||
}
|
||||
|
||||
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes) {
|
||||
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes, Player forPlayer) {
|
||||
final Card source = sa.getHostCard();
|
||||
final Player controller = source.getController();
|
||||
final Game game = controller.getGame();
|
||||
|
||||
@@ -82,6 +82,10 @@ public class ComputerUtilAbility {
|
||||
final List<SpellAbility> spellAbilities = Lists.newArrayList();
|
||||
for (final Card c : l) {
|
||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||
// Spells of permanents can't be activated on the battlefield
|
||||
if (c.isPermanent() && sa.isSpell() && c.isInZone(ZoneType.Battlefield)) {
|
||||
continue;
|
||||
}
|
||||
spellAbilities.add(sa);
|
||||
}
|
||||
if (c.isFaceDown() && c.isInZone(ZoneType.Exile) && !c.mayPlay(player).isEmpty()) {
|
||||
|
||||
@@ -643,7 +643,7 @@ public class ComputerUtilCard {
|
||||
return getMostProminentType(list, CardType.getAllCreatureTypes());
|
||||
}
|
||||
|
||||
public static String getMostProminentType(final CardCollectionView list, final List<String> valid) {
|
||||
public static String getMostProminentType(final CardCollectionView list, final Collection<String> valid) {
|
||||
if (list.size() == 0) {
|
||||
return "";
|
||||
}
|
||||
@@ -964,6 +964,22 @@ public class ComputerUtilCard {
|
||||
}
|
||||
chosen.add(chosenColor);
|
||||
}
|
||||
else if (logic.equals("HighestDevotionToColor")) {
|
||||
int curDevotion = 0;
|
||||
String chosenColor = MagicColor.Constant.WHITE;
|
||||
CardCollectionView hand = ai.getCardsIn(ZoneType.Hand);
|
||||
for(byte c : MagicColor.WUBRG) {
|
||||
String devotionCode = "Count$Devotion." + MagicColor.toLongString(c);
|
||||
|
||||
int devotion = CardFactoryUtil.xCount(sa.getHostCard(), devotionCode);
|
||||
if (devotion > curDevotion && !CardLists.filter(hand, CardPredicates.isColor(c)).isEmpty()) {
|
||||
curDevotion = devotion;
|
||||
chosenColor = MagicColor.toLongString(c);
|
||||
}
|
||||
}
|
||||
chosen.add(chosenColor);
|
||||
}
|
||||
|
||||
}
|
||||
if (chosen.isEmpty()) {
|
||||
chosen.add(MagicColor.Constant.GREEN);
|
||||
@@ -1431,7 +1447,8 @@ public class ComputerUtilCard {
|
||||
}
|
||||
if (pumpedDmg > dmg) {
|
||||
if ((!c.hasKeyword(Keyword.INFECT) && pumpedDmg >= opp.getLife())
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())) {
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|
||||
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2389,7 +2389,7 @@ public class ComputerUtilCombat {
|
||||
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
|
||||
|
||||
// Predict replacement effects
|
||||
for (final Card ca : game.getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final ReplacementEffect re : ca.getReplacementEffects()) {
|
||||
Map<String, String> params = re.getMapParams();
|
||||
if (!re.getMode().equals(ReplacementType.DamageDone) || !params.containsKey("PreventionEffect")) {
|
||||
|
||||
@@ -9,12 +9,10 @@ import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.effects.DetachedCardEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.token.TokenInfo;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
@@ -24,6 +22,7 @@ import forge.game.mana.ManaPool;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilityManaPart;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -244,7 +243,7 @@ public abstract class GameState {
|
||||
if (card instanceof DetachedCardEffect) {
|
||||
continue;
|
||||
}
|
||||
addCard(zone, card.getOwner() == ai ? aiCardTexts : humanCardTexts, card);
|
||||
addCard(zone, card.getController() == ai ? aiCardTexts : humanCardTexts, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,6 +270,10 @@ public abstract class GameState {
|
||||
}
|
||||
|
||||
if (zoneType == ZoneType.Battlefield) {
|
||||
if (c.getOwner() != c.getController()) {
|
||||
// TODO: Handle more than 2-player games.
|
||||
newText.append("|Owner:" + (c.getOwner().isAI() ? "AI" : "Human"));
|
||||
}
|
||||
if (c.isTapped()) {
|
||||
newText.append("|Tapped");
|
||||
}
|
||||
@@ -362,6 +365,12 @@ public abstract class GameState {
|
||||
if (c.isFaceDown()) {
|
||||
newText.append("|FaceDown"); // Exiled face down
|
||||
}
|
||||
if (c.isAdventureCard() && c.getZone().is(ZoneType.Exile)) {
|
||||
// TODO: this will basically default all exiled cards with Adventure to being "On Adventure".
|
||||
// Need to figure out a better way to detect if it's actually on adventure.
|
||||
newText.append("|OnAdventure");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (zoneType == ZoneType.Battlefield || zoneType == ZoneType.Exile) {
|
||||
@@ -807,6 +816,12 @@ public abstract class GameState {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("RememberTargets")) {
|
||||
for (final GameObject o : sa.getTargets().getTargets()) {
|
||||
sa.getHostCard().addRemembered(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleScriptExecution(final Game game) {
|
||||
@@ -966,14 +981,27 @@ public abstract class GameState {
|
||||
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
|
||||
}
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
|
||||
Card c = null;
|
||||
|
||||
if (pc == null) {
|
||||
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
|
||||
return;
|
||||
if (StringUtils.isNumeric(spellDef)) {
|
||||
// Precast from a specific host
|
||||
c = idToCard.get(Integer.parseInt(spellDef));
|
||||
if (c == null) {
|
||||
System.err.println("ERROR: Could not find a card with ID " + spellDef + " to precast!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Precast from a card by name
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
|
||||
|
||||
if (pc == null) {
|
||||
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
|
||||
return;
|
||||
}
|
||||
|
||||
c = Card.fromPaperCard(pc, activator);
|
||||
}
|
||||
|
||||
Card c = Card.fromPaperCard(pc, activator);
|
||||
SpellAbility sa = null;
|
||||
|
||||
if (!scriptID.isEmpty()) {
|
||||
@@ -1202,6 +1230,16 @@ public abstract class GameState {
|
||||
c.setState(CardStateName.Flipped, true);
|
||||
} else if (info.startsWith("Meld")) {
|
||||
c.setState(CardStateName.Meld, true);
|
||||
} else if (info.startsWith("OnAdventure")) {
|
||||
String abAdventure = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
|
||||
AbilitySub saAdventure = (AbilitySub)AbilityFactory.getAbility(abAdventure, c);
|
||||
StringBuilder sbPlay = new StringBuilder();
|
||||
sbPlay.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
|
||||
sbPlay.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
|
||||
saAdventure.setSVar("Play", sbPlay.toString());
|
||||
saAdventure.setActivatingPlayer(c.getOwner());
|
||||
saAdventure.resolve();
|
||||
c.setExiledWith(c); // This seems to be the way it's set up internally. Potentially not needed here?
|
||||
} else if (info.startsWith("IsCommander")) {
|
||||
// TODO: This doesn't seem to properly restore the ability to play the commander. Why?
|
||||
c.setCommander(true);
|
||||
|
||||
@@ -502,20 +502,19 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseSomeType(String kindOfType, SpellAbility sa, List<String> validTypes, List<String> invalidTypes, boolean isOptional) {
|
||||
public String chooseSomeType(String kindOfType, SpellAbility sa, Collection<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);
|
||||
System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen");
|
||||
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) {
|
||||
chosen = validTypes.iterator().next();
|
||||
System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: chosen");
|
||||
}
|
||||
game.getAction().nofityOfValue(sa, player, chosen, player);
|
||||
return chosen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes) {
|
||||
return ComputerUtil.vote(player, options, sa, votes);
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||
return ComputerUtil.vote(player, options, sa, votes, forPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1295,6 +1295,26 @@ public class SpecialCardAi {
|
||||
}
|
||||
}
|
||||
|
||||
// Timmerian Fiends
|
||||
public static class TimmerianFiends {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
final Card targeted = sa.getParentTargetingCard().getTargetCard();
|
||||
if (targeted == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targeted.isCreature()) {
|
||||
if (ComputerUtil.aiLifeInDanger(ai, true, 0)) {
|
||||
return true; // do it, hoping to save a valuable potential blocker etc.
|
||||
}
|
||||
return ComputerUtilCard.evaluateCreature(targeted) >= 200; // might need tweaking
|
||||
} else {
|
||||
// TODO: this currently compares purely by CMC. To be somehow improved, especially for stuff like the Power Nine etc.
|
||||
return ComputerUtilCard.evaluatePermanentList(new CardCollection(targeted)) >= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Volrath's Shapeshifter
|
||||
public static class VolrathsShapeshifter {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
|
||||
@@ -1031,7 +1031,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
Card c = null;
|
||||
List<Card> magnetList = null;
|
||||
String stCheck = null;
|
||||
if (attachSource.isAura() || sa.hasParam("Bestow")) {
|
||||
if (attachSource.isAura() || sa.isBestow()) {
|
||||
stCheck = "EnchantedBy";
|
||||
magnetList = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
|
||||
@@ -8,10 +8,7 @@ import forge.ai.SpecialCardAi;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.*;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -69,9 +66,7 @@ public class ChooseColorAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logic.equals("MostProminentInComputerDeck")) {
|
||||
} else if (logic.equals("MostProminentInComputerDeck")) {
|
||||
if ("Astral Cornucopia".equals(sourceName)) {
|
||||
// activate in Main 2 hoping that the extra mana surplus will make a difference
|
||||
// if there are some nonland permanents in hand
|
||||
@@ -80,6 +75,11 @@ public class ChooseColorAi extends SpellAbilityAi {
|
||||
|
||||
return permanents.size() > 0 && ph.is(PhaseType.MAIN2, ai);
|
||||
}
|
||||
} else if (logic.equals("HighestDevotionToColor")) {
|
||||
// currently only works more or less reliably in Main2 to cast own spells
|
||||
if (!ph.is(PhaseType.MAIN2, ai)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||
|
||||
@@ -34,12 +34,12 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.collect.FCollectionView;
|
||||
|
||||
|
||||
//AB:GainControl|ValidTgts$Creature|TgtPrompt$Select target legendary creature|LoseControl$Untap,LoseControl|SpellDescription$Gain control of target xxxxxxx
|
||||
@@ -54,8 +54,6 @@ import forge.util.collect.FCollectionView;
|
||||
// (as a "&"-separated list; like Haste, Sacrifice CARDNAME at EOT, any standard keyword)
|
||||
// OppChoice - set to True if opponent chooses creature (for Preacher) - not implemented yet
|
||||
// Untap - set to True if target card should untap when control is taken
|
||||
// DestroyTgt - actions upon which the tgt should be destroyed. same list as LoseControl
|
||||
// NoRegen - set if destroyed creature can't be regenerated. used only with DestroyTgt
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -77,7 +75,7 @@ public class ControlGainAi extends SpellAbilityAi {
|
||||
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final Game game = ai.getGame();
|
||||
final FCollectionView<Player> opponents = ai.getOpponents();
|
||||
final PlayerCollection opponents = ai.getOpponents();
|
||||
|
||||
// if Defined, then don't worry about targeting
|
||||
if (tgt == null) {
|
||||
@@ -94,18 +92,19 @@ public class ControlGainAi extends SpellAbilityAi {
|
||||
sa.setTargetingPlayer(targetingPlayer);
|
||||
return targetingPlayer.getController().chooseTargetsFor(sa);
|
||||
}
|
||||
if (tgt.isRandomTarget()) {
|
||||
sa.getTargets().add(Aggregates.random(tgt.getAllCandidates(sa, false)));
|
||||
}
|
||||
|
||||
if (tgt.canOnlyTgtOpponent()) {
|
||||
List<Player> oppList = Lists
|
||||
.newArrayList(Iterables.filter(opponents, PlayerPredicates.isTargetableBy(sa)));
|
||||
List<Player> oppList = opponents.filter(PlayerPredicates.isTargetableBy(sa));
|
||||
|
||||
if (oppList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sa.getTargets().add(oppList.get(0));
|
||||
if (tgt.isRandomTarget()) {
|
||||
sa.getTargets().add(Aggregates.random(oppList));
|
||||
} else {
|
||||
sa.getTargets().add(oppList.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.ai.*;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
@@ -180,6 +181,13 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
// if no targeting, it should always be ok
|
||||
}
|
||||
|
||||
if ("TriggeredCardController".equals(sa.getParam("Controller"))) {
|
||||
Card trigCard = (Card)sa.getTriggeringObject(AbilityKey.Card);
|
||||
if (!mandatory && trigCard != null && trigCard.getController().isOpponentOf(aiPlayer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,14 @@ import java.util.Map;
|
||||
|
||||
public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayWithoutRestrict(final Player ai, final SpellAbility sa) {
|
||||
if ("Always".equals(sa.getParam("AILogic"))) {
|
||||
return true;
|
||||
}
|
||||
return super.canPlayWithoutRestrict(ai, sa);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.ai.*;
|
||||
@@ -472,6 +473,22 @@ public class DamageDealAi extends DamageAiBase {
|
||||
return bestTgt;
|
||||
}
|
||||
|
||||
private Card getWorstPlaneswalkerToDamage(final List<Card> pws) {
|
||||
Card bestTgt = null;
|
||||
|
||||
int bestScore = Integer.MAX_VALUE;
|
||||
for (Card pw : pws) {
|
||||
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
|
||||
|
||||
if (curLoyalty < bestScore) {
|
||||
bestScore = curLoyalty;
|
||||
bestTgt = pw;
|
||||
}
|
||||
}
|
||||
|
||||
return bestTgt;
|
||||
}
|
||||
|
||||
|
||||
private List<Card> getTargetableCards(Player ai, SpellAbility sa, Player pl, TargetRestrictions tgt, Player activator, Card source, Game game) {
|
||||
List<Card> hPlay = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), activator, source, sa);
|
||||
@@ -556,6 +573,13 @@ public class DamageDealAi extends DamageAiBase {
|
||||
sa.getTargets().add(enemy);
|
||||
}
|
||||
return true;
|
||||
} else if ("DamageAfterPutCounter".equals(logic)
|
||||
&& sa.getParent() != null
|
||||
&& "P1P1".equals(sa.getParent().getParam("CounterType"))) {
|
||||
// assuming the SA parent is of PutCounter type. Perhaps it's possible to predict counter multipliers here somehow?
|
||||
final String amountStr = sa.getParent().getParamOrDefault("CounterNum", "1");
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||
dmg += amount;
|
||||
}
|
||||
|
||||
// AssumeAtLeastOneTarget is used for cards with funky targeting implementation like Fight with Fire which would
|
||||
@@ -566,7 +590,10 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
immediately |= ComputerUtil.playImmediately(ai, sa);
|
||||
|
||||
sa.resetTargets();
|
||||
if (!(sa.getParent() != null && sa.getParent().isTargetNumberValid())) {
|
||||
sa.resetTargets();
|
||||
}
|
||||
|
||||
// target loop
|
||||
TargetChoices tcs = sa.getTargets();
|
||||
|
||||
@@ -785,7 +812,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
return false;
|
||||
} else {
|
||||
// If the trigger is mandatory, gotta choose my own stuff now
|
||||
return this.damageChooseRequiredTargets(ai, sa, tgt, dmg, mandatory);
|
||||
return this.damageChooseRequiredTargets(ai, sa, tgt, dmg);
|
||||
}
|
||||
} else {
|
||||
// TODO is this good enough? for up to amounts?
|
||||
@@ -862,12 +889,9 @@ public class DamageDealAi extends DamageAiBase {
|
||||
* a {@link forge.game.spellability.TargetRestrictions} object.
|
||||
* @param dmg
|
||||
* a int.
|
||||
* @param mandatory
|
||||
* a boolean.
|
||||
* @return a boolean.
|
||||
*/
|
||||
private boolean damageChooseRequiredTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, final int dmg,
|
||||
final boolean mandatory) {
|
||||
private boolean damageChooseRequiredTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, final int dmg) {
|
||||
// this is for Triggered targets that are mandatory
|
||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||
@@ -875,7 +899,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||
if (tgt.canTgtPlaneswalker()) {
|
||||
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, mandatory);
|
||||
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, true);
|
||||
if (c != null) {
|
||||
sa.getTargets().add(c);
|
||||
if (divided) {
|
||||
@@ -888,7 +912,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
// TODO: This currently also catches planeswalkers that can be killed (still necessary? Or can be removed?)
|
||||
if (tgt.canTgtCreature()) {
|
||||
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, ai, mandatory);
|
||||
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, ai, true);
|
||||
if (c != null) {
|
||||
sa.getTargets().add(c);
|
||||
if (divided) {
|
||||
@@ -909,6 +933,32 @@ public class DamageDealAi extends DamageAiBase {
|
||||
}
|
||||
}
|
||||
|
||||
// See if there's an indestructible target that can be used
|
||||
CardCollection indestructible = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
|
||||
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.Presets.PLANESWALKERS, CardPredicates.hasKeyword(Keyword.INDESTRUCTIBLE), CardPredicates.isTargetableBy(sa)));
|
||||
|
||||
if (!indestructible.isEmpty()) {
|
||||
Card c = ComputerUtilCard.getWorstPermanentAI(indestructible, false, false, false, false);
|
||||
sa.getTargets().add(c);
|
||||
if (divided) {
|
||||
tgt.addDividedAllocation(c, dmg);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (tgt.canTgtPlaneswalker()) {
|
||||
// Second pass for planeswalkers: choose AI's worst planeswalker
|
||||
final Card c = getWorstPlaneswalkerToDamage(CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.and(CardPredicates.Presets.PLANESWALKERS), CardPredicates.isTargetableBy(sa)));
|
||||
if (c != null) {
|
||||
sa.getTargets().add(c);
|
||||
if (divided) {
|
||||
tgt.addDividedAllocation(c, dmg);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.canTarget(ai)) {
|
||||
if (sa.getTargets().add(ai)) {
|
||||
if (divided) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.ai.*;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -157,6 +159,15 @@ public class DigAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
/* (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, Iterable<Player> options) {
|
||||
// an opponent choose a card from
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||
*/
|
||||
|
||||
@@ -514,6 +514,10 @@ public class DrawAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
if (!mandatory && !willPayCosts(ai, sa, sa.getPayCosts(), sa.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return targetAI(ai, sa, mandatory);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
@@ -307,6 +308,16 @@ public class EffectAi extends SpellAbilityAi {
|
||||
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
|
||||
return false;
|
||||
}
|
||||
} else if (logic.equals("Bribe")) {
|
||||
Card host = sa.getHostCard();
|
||||
Combat combat = game.getCombat();
|
||||
if (combat != null && combat.isAttacking(host, ai) && !combat.isBlocked(host)
|
||||
&& game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& !AiCardMemory.isRememberedCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) {
|
||||
AiCardMemory.rememberCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN); // ideally needs once per combat or something
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else { //no AILogic
|
||||
return false;
|
||||
|
||||
@@ -45,6 +45,8 @@ public class FightAi extends SpellAbilityAi {
|
||||
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
|
||||
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
|
||||
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
|
||||
if (humCreatures.isEmpty())
|
||||
return false; //prevent IndexOutOfBoundsException on MOJHOSTO variant
|
||||
|
||||
// assumes the triggered card belongs to the ai
|
||||
if (sa.hasParam("Defined")) {
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpecialCardAi;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
@@ -24,6 +19,11 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MillAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
@@ -38,6 +38,8 @@ public class MillAi extends SpellAbilityAi {
|
||||
} else if (aiLogic.equals("LilianaMill")) {
|
||||
// Only mill if a "Raise Dead" target is available, in case of control decks with few creatures
|
||||
return CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES).size() >= 1;
|
||||
} else if (aiLogic.equals("Rebirth")) {
|
||||
return ai.getLife() <= 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -194,6 +196,10 @@ public class MillAi extends SpellAbilityAi {
|
||||
*/
|
||||
@Override
|
||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
if ("TimmerianFiends".equals(sa.getParam("AILogic"))) {
|
||||
return SpecialCardAi.TimmerianFiends.consider(player, sa);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.ai.*;
|
||||
import forge.game.Game;
|
||||
@@ -12,11 +12,9 @@ import forge.game.card.CardPredicates;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
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;
|
||||
@@ -28,7 +26,6 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = aiPlayer.getGame();
|
||||
final Combat combat = game.getCombat();
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
final boolean onlyLethal = !"AllowNonLethal".equals(sa.getParam("AILogic"));
|
||||
|
||||
if (combat == null || !combat.isAttacking(source)) {
|
||||
@@ -39,7 +36,6 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
final List<Card> list = determineGoodBlockers(source, aiPlayer, combat.getDefenderPlayerByAttacker(source), sa, onlyLethal,false);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
@@ -69,7 +65,6 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
|
||||
// only use on creatures that can attack
|
||||
if (!ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
|
||||
@@ -94,7 +89,7 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
boolean chance = false;
|
||||
|
||||
if (abTgt != null) {
|
||||
if (sa.usesTargeting()) {
|
||||
final List<Card> list = determineGoodBlockers(definedAttacker, ai, ai.getWeakestOpponent(), sa, true,true);
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
@@ -119,6 +114,9 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
sa.getTargets().add(blocker);
|
||||
chance = true;
|
||||
} else if (sa.hasParam("Choices")) {
|
||||
// currently choice is attacked player
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -126,16 +124,9 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
return chance;
|
||||
}
|
||||
|
||||
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
|
||||
private List<Card> determineBlockerFromList(final Card attacker, final Player ai, Iterable<Card> options, SpellAbility sa,
|
||||
final boolean onlyLethal, final boolean testTapped) {
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
|
||||
List<Card> list = Lists.newArrayList();
|
||||
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
List<Card> list = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
boolean tapped = c.isTapped();
|
||||
@@ -161,4 +152,40 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
|
||||
final boolean onlyLethal, final boolean testTapped) {
|
||||
|
||||
List<Card> list = Lists.newArrayList();
|
||||
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
}
|
||||
return determineBlockerFromList(attacker, ai, list, sa, onlyLethal, testTapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
Card attacker = host;
|
||||
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
List<Card> attackers = AbilityUtils.getDefinedCards(host, sa.getParam("DefinedAttacker"), sa);
|
||||
attacker = Iterables.getFirst(attackers, null);
|
||||
}
|
||||
if (attacker == null) {
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
List<Card> better = determineBlockerFromList(attacker, ai, options, sa, false, false);
|
||||
|
||||
if (!better.isEmpty()) {
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,7 @@ import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.ai.AiAttackController;
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.ComputerUtilCombat;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.ai.*;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameObject;
|
||||
@@ -221,6 +217,11 @@ public class ProtectAi extends SpellAbilityAi {
|
||||
// Don't target cards that will die.
|
||||
list = ComputerUtil.getSafeTargets(ai, sa, list);
|
||||
|
||||
// Don't target self if the cost includes sacrificing itself
|
||||
if (ComputerUtilCost.isSacrificeSelfCost(sa.getPayCosts())) {
|
||||
list.remove(source);
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return mandatory && protectMandatoryTarget(ai, sa, mandatory);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
final boolean destroy = sa.hasParam("Destroy");
|
||||
|
||||
Player opp = ai.getWeakestOpponent();
|
||||
|
||||
if (tgt != null) {
|
||||
sa.resetTargets();
|
||||
if (!opp.canBeTargetedBy(sa)) {
|
||||
@@ -74,8 +75,16 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
num = (num == null) ? "1" : num;
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
|
||||
|
||||
List<Card> list =
|
||||
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
List<Card> list = null;
|
||||
try {
|
||||
list = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (list == null)
|
||||
return false;
|
||||
}//prevent NPE on MoJhoSto
|
||||
|
||||
for (Card c : list) {
|
||||
if (c.hasSVar("SacMe") && Integer.parseInt(c.getSVar("SacMe")) > 3) {
|
||||
return false;
|
||||
@@ -131,15 +140,31 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
amount = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount);
|
||||
}
|
||||
|
||||
List<Card> humanList =
|
||||
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
List<Card> humanList = null;
|
||||
try {
|
||||
humanList = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (humanList == null)
|
||||
return false;
|
||||
}//prevent NPE on MoJhoSto
|
||||
|
||||
// Since all of the cards have AI:RemoveDeck:All, I enabled 1 for 1
|
||||
// (or X for X) trades for special decks
|
||||
return humanList.size() >= amount;
|
||||
} else if (defined.equals("You")) {
|
||||
List<Card> computerList =
|
||||
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
|
||||
List<Card> computerList = null;
|
||||
try {
|
||||
computerList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (computerList == null)
|
||||
return false;
|
||||
}//prevent NPE on MoJhoSto
|
||||
|
||||
for (Card c : computerList) {
|
||||
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) <= 135) {
|
||||
return true;
|
||||
|
||||
@@ -46,6 +46,12 @@ public class VoteAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
||||
if (params.containsKey("Voter")) {
|
||||
Player p = (Player)params.get("Voter");
|
||||
if (p.isOpponentOf(player)) {
|
||||
return min;
|
||||
}
|
||||
}
|
||||
if (sa.getActivatingPlayer().isOpponentOf(player)) {
|
||||
return min;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ public class GameStateEvaluator {
|
||||
if (phase.isAfter(PhaseType.COMBAT_DAMAGE) || evalGame.isGameOver()) {
|
||||
return null;
|
||||
}
|
||||
// If the current player has no creatures in play, there won't be any combat. This avoids
|
||||
// an expensive game copy operation.
|
||||
// Note: This is is safe to do because the simulation is based on the current game state,
|
||||
// so there isn't a chance to play creatures in between.
|
||||
if (evalGame.getPhaseHandler().getPlayerTurn().getCreaturesInPlay().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
GameCopier copier = new GameCopier(evalGame);
|
||||
Game gameCopy = copier.makeCopy();
|
||||
gameCopy.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_DAMAGE);
|
||||
|
||||
@@ -23,6 +23,7 @@ import forge.util.TextUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SpellAbilityPicker {
|
||||
private Game game;
|
||||
@@ -307,7 +308,7 @@ public class SpellAbilityPicker {
|
||||
if (conditions == null) {
|
||||
return true;
|
||||
}
|
||||
List<PhaseType> phases = conditions.getPhases();
|
||||
Set<PhaseType> phases = conditions.getPhases();
|
||||
return phases.isEmpty() || phases.contains(PhaseType.MAIN1);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.33</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-core</artifactId>
|
||||
@@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -111,7 +111,22 @@ public final class ImageKeys {
|
||||
file = findFile(dir, TextUtil.fastReplace(filename, "AE", "Ae"));
|
||||
if (file != null) { return file; }
|
||||
}
|
||||
|
||||
//try fullborder...
|
||||
if (filename.contains(".full")) {
|
||||
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
|
||||
file = findFile(dir, fullborderFile);
|
||||
if (file != null) { return file; }
|
||||
// if there's an art variant try without it
|
||||
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
|
||||
if (file != null) { return file; }
|
||||
}
|
||||
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
|
||||
if (!filename.contains(".full")) {
|
||||
file = findFile(dir, TextUtil.addSuffix(filename,".full"));
|
||||
if (file != null) { return file; }
|
||||
file = findFile(dir, TextUtil.addSuffix(filename,".fullborder"));
|
||||
if (file != null) { return file; }
|
||||
}
|
||||
// some S00 cards are really part of 6ED
|
||||
String s2kAlias = getSetFolder("S00");
|
||||
if (filename.startsWith(s2kAlias)) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
public abstract class LobbyPlayer {
|
||||
protected String name;
|
||||
private int avatarIndex = -1;
|
||||
private int sleeveIndex = -1;
|
||||
private String avatarCardImageKey;
|
||||
|
||||
public LobbyPlayer(String name) {
|
||||
@@ -59,9 +60,15 @@ public abstract class LobbyPlayer {
|
||||
public int getAvatarIndex() {
|
||||
return avatarIndex;
|
||||
}
|
||||
public int getSleeveIndex() {
|
||||
return sleeveIndex;
|
||||
}
|
||||
public void setAvatarIndex(int avatarIndex) {
|
||||
this.avatarIndex = avatarIndex;
|
||||
}
|
||||
public void setSleeveIndex(int sleeveIndex) {
|
||||
this.sleeveIndex = sleeveIndex;
|
||||
}
|
||||
|
||||
public String getAvatarCardImageKey() {
|
||||
return avatarCardImageKey;
|
||||
|
||||
@@ -35,6 +35,7 @@ public class StaticData {
|
||||
|
||||
private Predicate<PaperCard> standardPredicate;
|
||||
private Predicate<PaperCard> brawlPredicate;
|
||||
private Predicate<PaperCard> pioneerPredicate;
|
||||
private Predicate<PaperCard> modernPredicate;
|
||||
private Predicate<PaperCard> commanderPredicate;
|
||||
private Predicate<PaperCard> oathbreakerPredicate;
|
||||
@@ -197,13 +198,13 @@ public class StaticData {
|
||||
|
||||
public TokenDb getAllTokens() { return allTokens; }
|
||||
|
||||
public Predicate<PaperCard> getStandardPredicate() {
|
||||
return standardPredicate;
|
||||
}
|
||||
|
||||
|
||||
public void setStandardPredicate(Predicate<PaperCard> standardPredicate) { this.standardPredicate = standardPredicate; }
|
||||
|
||||
public void setModernPredicate(Predicate<PaperCard> modernPredicate) { this.modernPredicate = standardPredicate; }
|
||||
public void setPioneerPredicate(Predicate<PaperCard> pioneerPredicate) { this.pioneerPredicate = pioneerPredicate; }
|
||||
|
||||
public void setModernPredicate(Predicate<PaperCard> modernPredicate) { this.modernPredicate = modernPredicate; }
|
||||
|
||||
public void setCommanderPredicate(Predicate<PaperCard> commanderPredicate) { this.commanderPredicate = commanderPredicate; }
|
||||
|
||||
@@ -211,9 +212,11 @@ public class StaticData {
|
||||
|
||||
public void setBrawlPredicate(Predicate<PaperCard> brawlPredicate) { this.brawlPredicate = brawlPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getModernPredicate() {
|
||||
return modernPredicate;
|
||||
}
|
||||
public Predicate<PaperCard> getStandardPredicate() { return standardPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getPioneerPredicate() { return pioneerPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getModernPredicate() { return modernPredicate; }
|
||||
|
||||
public Predicate<PaperCard> getCommanderPredicate() { return commanderPredicate; }
|
||||
|
||||
|
||||
@@ -312,17 +312,21 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
return tryGetCard(request);
|
||||
}
|
||||
|
||||
public int getCardCollectorNumber(String cardName, String reqEdition) {
|
||||
public String getCardCollectorNumber(String cardName, String reqEdition, int artIndex) {
|
||||
cardName = getName(cardName);
|
||||
CardEdition edition = editions.get(reqEdition);
|
||||
if (edition == null)
|
||||
return -1;
|
||||
return null;
|
||||
int numMatches = 0;
|
||||
for (CardInSet card : edition.getCards()) {
|
||||
if (card.name.equalsIgnoreCase(cardName)) {
|
||||
return card.collectorNumber;
|
||||
numMatches += 1;
|
||||
if (numMatches == artIndex) {
|
||||
return card.collectorNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
private PaperCard tryGetCard(CardRequest request) {
|
||||
@@ -553,6 +557,23 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
return Lists.newArrayList(Iterables.filter(this.roAllCards, predicate));
|
||||
}
|
||||
|
||||
// Do I want a foiled version of these cards?
|
||||
@Override
|
||||
public List<PaperCard> getAllCardsFromEdition(CardEdition edition) {
|
||||
List<PaperCard> cards = Lists.newArrayList();
|
||||
|
||||
for(CardInSet cis : edition.getCards()) {
|
||||
PaperCard card = this.getCard(cis.name, edition.getCode());
|
||||
if (card == null) {
|
||||
// Just in case the card is listed in the edition file but Forge doesn't support it
|
||||
continue;
|
||||
}
|
||||
|
||||
cards.add(card);
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String name) {
|
||||
return allCardsByName.containsKey(getName(name));
|
||||
|
||||
@@ -38,6 +38,8 @@ import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
@@ -75,10 +77,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
public static class CardInSet {
|
||||
public final CardRarity rarity;
|
||||
public final int collectorNumber;
|
||||
public final String collectorNumber;
|
||||
public final String name;
|
||||
|
||||
public CardInSet(final String name, final int collectorNumber, final CardRarity rarity) {
|
||||
public CardInSet(final String name, final String collectorNumber, final CardRarity rarity) {
|
||||
this.name = name;
|
||||
this.collectorNumber = collectorNumber;
|
||||
this.rarity = rarity;
|
||||
@@ -86,7 +88,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (collectorNumber != -1) {
|
||||
if (collectorNumber != null) {
|
||||
sb.append(collectorNumber);
|
||||
sb.append(' ');
|
||||
}
|
||||
@@ -110,6 +112,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
private Type type;
|
||||
private String name;
|
||||
private String alias = null;
|
||||
private String prerelease = null;
|
||||
private boolean whiteBorder = false;
|
||||
private FoilType foilType = FoilType.NOT_SUPPORTED;
|
||||
private double foilChanceInBooster = 0;
|
||||
@@ -178,6 +181,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
public Type getType() { return type; }
|
||||
public String getName() { return name; }
|
||||
public String getAlias() { return alias; }
|
||||
public String getPrerelease() { return prerelease; }
|
||||
public FoilType getFoilType() { return foilType; }
|
||||
public double getFoilChanceInBooster() { return foilChanceInBooster; }
|
||||
public boolean getFoilAlwaysInCommonSlot() { return foilAlwaysInCommonSlot; }
|
||||
@@ -188,6 +192,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
public boolean getSmallSetOverride() { return smallSetOverride; }
|
||||
public String getBoosterMustContain() { return boosterMustContain; }
|
||||
public CardInSet[] getCards() { return cards; }
|
||||
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
|
||||
|
||||
public Map<String, Integer> getTokens() { return tokenNormalized; }
|
||||
|
||||
@@ -264,24 +269,33 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
Map<String, Integer> tokenNormalized = new HashMap<>();
|
||||
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
||||
if (contents.containsKey("cards")) {
|
||||
final Pattern pattern = Pattern.compile(
|
||||
/*
|
||||
The following pattern will match the WAR Japanese art entries,
|
||||
it should also match the Un-set and older alternate art cards
|
||||
like Merseine from FEM (should the editions files ever be updated)
|
||||
*/
|
||||
//"(^(?<cnum>[0-9]+.?) )?((?<rarity>[SCURML]) )?(?<name>.*)$"
|
||||
/* Ideally we'd use the named group above, but Android 6 and
|
||||
earlier don't appear to support named groups.
|
||||
So, untill support for those devices is officially dropped,
|
||||
we'll have to suffice with numbered groups.
|
||||
We are looking for:
|
||||
* cnum - grouping #2
|
||||
* rarity - grouping #4
|
||||
* name - grouping #5
|
||||
*/
|
||||
"(^([0-9]+.?) )?(([SCURML]) )?(.*)$"
|
||||
);
|
||||
for(String line : contents.get("cards")) {
|
||||
if (StringUtils.isBlank(line))
|
||||
continue;
|
||||
|
||||
// Optional collector number at the start.
|
||||
String[] split = line.split(" ", 2);
|
||||
int collectorNumber = -1;
|
||||
if (split.length >= 2 && StringUtils.isNumeric(split[0])) {
|
||||
collectorNumber = Integer.parseInt(split[0]);
|
||||
line = split[1];
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
if (matcher.matches()) {
|
||||
String collectorNumber = matcher.group(2);
|
||||
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
|
||||
String cardName = matcher.group(5);
|
||||
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
|
||||
processedCards.add(cis);
|
||||
}
|
||||
|
||||
// You may omit rarity for early development
|
||||
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
|
||||
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
|
||||
String cardName = hadRarity ? line.substring(2) : line;
|
||||
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
|
||||
processedCards.add(cis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +317,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
tokenNormalized
|
||||
);
|
||||
|
||||
FileSection section = FileSection.parse(contents.get("metadata"), "=");
|
||||
FileSection section = FileSection.parse(contents.get("metadata"), FileSection.EQUALS_KV_SEPARATOR);
|
||||
res.name = section.get("name");
|
||||
res.date = parseDate(section.get("date"));
|
||||
res.code = section.get("code");
|
||||
@@ -333,6 +347,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
}
|
||||
}
|
||||
res.type = enumType;
|
||||
res.prerelease = section.get("Prerelease", null);
|
||||
|
||||
switch(section.get("foil", "newstyle").toLowerCase()) {
|
||||
case "notsupported":
|
||||
@@ -413,6 +428,16 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||
return res;
|
||||
}
|
||||
|
||||
public Iterable<CardEdition> getPrereleaseEditions() {
|
||||
List<CardEdition> res = Lists.newArrayList(this);
|
||||
return Iterables.filter(res, new Predicate<CardEdition>() {
|
||||
@Override
|
||||
public boolean apply(final CardEdition edition) {
|
||||
return edition.getPrerelease() != null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public CardEdition getEditionByCodeOrThrow(final String code) {
|
||||
final CardEdition set = this.get(code);
|
||||
if (null == set) {
|
||||
|
||||
@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
|
||||
|
||||
public boolean canBeBrawlCommander() {
|
||||
CardType type = mainPart.getType();
|
||||
return (type.isLegendary() && type.isCreature()) || type.isPlaneswalker();
|
||||
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
||||
}
|
||||
|
||||
public boolean canBeTinyLeadersCommander() {
|
||||
CardType type = mainPart.getType();
|
||||
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
||||
}
|
||||
|
||||
public String getMeldWith() {
|
||||
|
||||
@@ -594,8 +594,10 @@ public final class CardRulesPredicates {
|
||||
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
||||
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
|
||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||
public static final Predicate<CardRules> CAN_BE_TINY_LEADERS_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||
|
||||
/** The Constant IS_NON_CREATURE_SPELL. **/
|
||||
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -35,10 +36,8 @@ import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.util.EnumUtil;
|
||||
import forge.util.Settable;
|
||||
|
||||
/**
|
||||
@@ -47,7 +46,6 @@ import forge.util.Settable;
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: java 9708 2011-08-09 19:34:12Z jendave $
|
||||
*/
|
||||
public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
private static final long serialVersionUID = 4629853583167022151L;
|
||||
@@ -71,7 +69,8 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
Vanguard(false);
|
||||
|
||||
public final boolean isPermanent;
|
||||
private static final ImmutableList<String> allCoreTypeNames = EnumUtil.getNames(CoreType.class);
|
||||
private static Map<String, CoreType> stringToCoreType = EnumUtils.getEnumMap(CoreType.class);
|
||||
private static final Set<String> allCoreTypeNames = stringToCoreType.keySet();
|
||||
|
||||
CoreType(final boolean permanent) {
|
||||
isPermanent = permanent;
|
||||
@@ -86,19 +85,8 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
Ongoing,
|
||||
World;
|
||||
|
||||
private static final ImmutableList<String> allSuperTypeNames = EnumUtil.getNames(Supertype.class);
|
||||
}
|
||||
|
||||
// This will be useful for faster parses
|
||||
private static Map<String, CoreType> stringToCoreType = Maps.newHashMap();
|
||||
private static Map<String, Supertype> stringToSupertype = Maps.newHashMap();
|
||||
static {
|
||||
for (final Supertype st : Supertype.values()) {
|
||||
stringToSupertype.put(st.name(), st);
|
||||
}
|
||||
for (final CoreType ct : CoreType.values()) {
|
||||
stringToCoreType.put(ct.name(), ct);
|
||||
}
|
||||
private static Map<String, Supertype> stringToSupertype = EnumUtils.getEnumMap(Supertype.class);
|
||||
private static final Set<String> allSuperTypeNames = stringToSupertype.keySet();
|
||||
}
|
||||
|
||||
private final Set<CoreType> coreTypes = EnumSet.noneOf(CoreType.class);
|
||||
@@ -120,12 +108,12 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
|
||||
public boolean add(final String t) {
|
||||
boolean changed;
|
||||
final CoreType ct = stringToCoreType.get(t);
|
||||
final CoreType ct = EnumUtils.getEnum(CoreType.class, t);
|
||||
if (ct != null) {
|
||||
changed = coreTypes.add(ct);
|
||||
}
|
||||
else {
|
||||
final Supertype st = stringToSupertype.get(t);
|
||||
final Supertype st = EnumUtils.getEnum(Supertype.class, t);
|
||||
if (st != null) {
|
||||
changed = supertypes.add(st);
|
||||
}
|
||||
@@ -183,20 +171,28 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
subtypes.clear();
|
||||
calculatedType = null;
|
||||
}
|
||||
|
||||
|
||||
public boolean remove(final Supertype st) {
|
||||
return supertypes.remove(st);
|
||||
}
|
||||
|
||||
public boolean remove(final String str) {
|
||||
boolean changed = false;
|
||||
if (CardType.isASupertype(str) && supertypes.remove(stringToSupertype.get(str))) {
|
||||
changed = true;
|
||||
} else if (CardType.isACardType(str) && coreTypes.remove(stringToCoreType.get(str))) {
|
||||
changed = true;
|
||||
} else if (subtypes.remove(str)) {
|
||||
|
||||
// try to remove sub type first if able
|
||||
if (subtypes.remove(str)) {
|
||||
changed = true;
|
||||
} else {
|
||||
Supertype st = EnumUtils.getEnum(Supertype.class, str);
|
||||
if (st != null && supertypes.remove(st)) {
|
||||
changed = true;
|
||||
}
|
||||
CoreType ct = EnumUtils.getEnum(CoreType.class, str);
|
||||
if (ct != null && coreTypes.remove(ct)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
calculatedType = null;
|
||||
}
|
||||
@@ -267,15 +263,13 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
if (hasSubtype(t)) {
|
||||
return true;
|
||||
}
|
||||
final char firstChar = t.charAt(0);
|
||||
if (Character.isLowerCase(firstChar)) {
|
||||
t = Character.toUpperCase(firstChar) + t.substring(1); //ensure string is proper case for enum types
|
||||
}
|
||||
final CoreType type = stringToCoreType.get(t);
|
||||
|
||||
t = StringUtils.capitalize(t);
|
||||
final CoreType type = EnumUtils.getEnum(CoreType.class, t);
|
||||
if (type != null) {
|
||||
return hasType(type);
|
||||
}
|
||||
final Supertype supertype = stringToSupertype.get(t);
|
||||
final Supertype supertype = EnumUtils.getEnum(Supertype.class, t);
|
||||
if (supertype != null) {
|
||||
return hasSupertype(supertype);
|
||||
}
|
||||
@@ -307,18 +301,18 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
return subtypes.contains(creatureType) || subtypes.contains("AllCreatureTypes");
|
||||
}
|
||||
private static String toMixedCase(final String s) {
|
||||
if (s.equals("")) {
|
||||
if (s.isEmpty()) {
|
||||
return s;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
// to handle hyphenated Types
|
||||
// TODO checkout WordUtils for this
|
||||
final String[] types = s.split("-");
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append("-");
|
||||
}
|
||||
sb.append(types[i].substring(0, 1).toUpperCase());
|
||||
sb.append(types[i].substring(1).toLowerCase());
|
||||
sb.append(StringUtils.capitalize(types[i]));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -606,14 +600,14 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
|
||||
public static class Constant {
|
||||
public static final Settable LOADED = new Settable();
|
||||
public static final List<String> BASIC_TYPES = Lists.newArrayList();
|
||||
public static final List<String> LAND_TYPES = Lists.newArrayList();
|
||||
public static final List<String> CREATURE_TYPES = Lists.newArrayList();
|
||||
public static final List<String> SPELL_TYPES = Lists.newArrayList();
|
||||
public static final List<String> ENCHANTMENT_TYPES = Lists.newArrayList();
|
||||
public static final List<String> ARTIFACT_TYPES = Lists.newArrayList();
|
||||
public static final List<String> WALKER_TYPES = Lists.newArrayList();
|
||||
|
||||
public static final Set<String> BASIC_TYPES = Sets.newHashSet();
|
||||
public static final Set<String> LAND_TYPES = Sets.newHashSet();
|
||||
public static final Set<String> CREATURE_TYPES = Sets.newHashSet();
|
||||
public static final Set<String> SPELL_TYPES = Sets.newHashSet();
|
||||
public static final Set<String> ENCHANTMENT_TYPES = Sets.newHashSet();
|
||||
public static final Set<String> ARTIFACT_TYPES = Sets.newHashSet();
|
||||
public static final Set<String> WALKER_TYPES = Sets.newHashSet();
|
||||
|
||||
// singular -> plural
|
||||
public static final BiMap<String,String> pluralTypes = HashBiMap.create();
|
||||
// plural -> singular
|
||||
@@ -662,14 +656,14 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////// Utility methods
|
||||
public static boolean isACardType(final String cardType) {
|
||||
return getAllCardTypes().contains(cardType);
|
||||
return EnumUtils.isValidEnum(CoreType.class, cardType);
|
||||
}
|
||||
|
||||
public static ImmutableList<String> getAllCardTypes() {
|
||||
public static Set<String> getAllCardTypes() {
|
||||
return CoreType.allCoreTypeNames;
|
||||
}
|
||||
|
||||
@@ -699,12 +693,12 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
return sortedSubTypes;
|
||||
}
|
||||
|
||||
public static List<String> getBasicTypes() {
|
||||
return Collections.unmodifiableList(Constant.BASIC_TYPES);
|
||||
public static Collection<String> getBasicTypes() {
|
||||
return Collections.unmodifiableCollection(Constant.BASIC_TYPES);
|
||||
}
|
||||
|
||||
public static List<String> getAllCreatureTypes() {
|
||||
return Collections.unmodifiableList(Constant.CREATURE_TYPES);
|
||||
public static Collection<String> getAllCreatureTypes() {
|
||||
return Collections.unmodifiableCollection(Constant.CREATURE_TYPES);
|
||||
}
|
||||
public static List<String> getAllLandTypes() {
|
||||
return ImmutableList.<String>builder()
|
||||
@@ -714,7 +708,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
}
|
||||
|
||||
public static boolean isASupertype(final String cardType) {
|
||||
return (Supertype.allSuperTypeNames.contains(cardType));
|
||||
return EnumUtils.isValidEnum(Supertype.class, cardType);
|
||||
}
|
||||
|
||||
public static boolean isASubType(final String cardType) {
|
||||
@@ -740,7 +734,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
public static boolean isABasicLandType(final String cardType) {
|
||||
return (Constant.BASIC_TYPES.contains(cardType));
|
||||
}
|
||||
|
||||
|
||||
public static boolean isAnEnchantmentType(final String cardType) {
|
||||
return (Constant.ENCHANTMENT_TYPES.contains(cardType));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ public interface ICardDatabase extends Iterable<PaperCard> {
|
||||
List<PaperCard> getAllCards(String cardName);
|
||||
List<PaperCard> getAllCards(Predicate<PaperCard> predicate);
|
||||
|
||||
List<PaperCard> getAllCardsFromEdition(CardEdition edition);
|
||||
|
||||
Predicate<? super PaperCard> wasPrintedInSets(List<String> allowedSetCodes);
|
||||
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
@@ -37,8 +36,11 @@ import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.Range;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* GameType is an enum to determine the type of current game. :)
|
||||
@@ -71,7 +73,7 @@ public enum DeckFormat {
|
||||
private final Set<String> bannedCards = ImmutableSet.of(
|
||||
"Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star",
|
||||
"Fastbond", "Flash", "Goblin Recruiter", "Grindstone", "Hermit Druid", "Imperial Seal", "Jeweled Bird", "Karakas", "Library of Alexandria", "Mana Crypt", "Mana Drain", "Mana Vault", "Metalworker", "Mind Twist", "Mishra's Workshop",
|
||||
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
|
||||
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Najeela, the Blade Blossom", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
|
||||
"Timmerian Fiends", "Tolarian Academy", "Umezawa's Jitte", "Vampiric Tutor", "Wheel of Fortune", "Yawgmoth's Will");
|
||||
|
||||
@Override
|
||||
@@ -324,23 +326,32 @@ public enum DeckFormat {
|
||||
}
|
||||
|
||||
final int maxCopies = getMaxCardCopies();
|
||||
if (maxCopies < Integer.MAX_VALUE) {
|
||||
//Must contain no more than 4 of the same card
|
||||
//shared among the main deck and sideboard, except
|
||||
//basic lands, Shadowborn Apostle, Relentless Rats and Rat Colony
|
||||
//Must contain no more than 4 of the same card
|
||||
//shared among the main deck and sideboard, except
|
||||
//basic lands, Shadowborn Apostle, Relentless Rats and Rat Colony
|
||||
// Seven Dwarves can have 7 in the deck. More than 7 in deck + sb is ok in Limited
|
||||
|
||||
final CardPool allCards = deck.getAllCardsInASinglePool(hasCommander());
|
||||
final CardPool allCards = deck.getAllCardsInASinglePool(hasCommander());
|
||||
|
||||
// should group all cards by name, so that different editions of same card are really counted as the same card
|
||||
for (final Entry<String, Integer> cp : Aggregates.groupSumBy(allCards, PaperCard.FN_GET_NAME)) {
|
||||
final IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
|
||||
if (simpleCard == null) {
|
||||
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
|
||||
}
|
||||
// should group all cards by name, so that different editions of same card are really counted as the same card
|
||||
for (final Entry<String, Integer> cp : Aggregates.groupSumBy(allCards, PaperCard.FN_GET_NAME)) {
|
||||
final IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
|
||||
// Might cause issues since it ignores "Special" Cards
|
||||
if (simpleCard == null) {
|
||||
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
|
||||
}
|
||||
|
||||
if (!canHaveAnyNumberOf(simpleCard) && cp.getValue() > maxCopies) {
|
||||
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(maxCopies), "copies of the card", cp.getKey());
|
||||
}
|
||||
if (canHaveAnyNumberOf(simpleCard)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer cardCopies = canHaveSpecificNumberInDeck(simpleCard);
|
||||
if (cardCopies != null && deck.getMain().countByName(cp.getKey(), true) > cardCopies) {
|
||||
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(cardCopies), "copies of the card", cp.getKey());
|
||||
}
|
||||
|
||||
if (cardCopies == null && cp.getValue() > maxCopies) {
|
||||
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(maxCopies), "copies of the card", cp.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,6 +373,16 @@ public enum DeckFormat {
|
||||
"A deck can have any number of cards named CARDNAME.");
|
||||
}
|
||||
|
||||
public static Integer canHaveSpecificNumberInDeck(final IPaperCard card) {
|
||||
// Ideally, this would be parsed during card parsing and set this value
|
||||
if (Iterables.contains(card.getRules().getMainPart().getKeywords(),
|
||||
"A deck can have up to seven cards named CARDNAME.")) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getPlaneSectionConformanceProblem(final CardPool planes) {
|
||||
//Must contain at least 10 planes/phenomenons, but max 2 phenomenons. Singleton.
|
||||
if (planes == null || planes.countAll() < 10) {
|
||||
@@ -442,6 +463,9 @@ public enum DeckFormat {
|
||||
if (this.equals(DeckFormat.Brawl)) {
|
||||
return rules.canBeBrawlCommander();
|
||||
}
|
||||
if (this.equals(DeckFormat.TinyLeaders)) {
|
||||
return rules.canBeTinyLeadersCommander();
|
||||
}
|
||||
return rules.canBeCommander();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DeckSerializer {
|
||||
}
|
||||
final List<String> metadata = map.get("metadata");
|
||||
if (metadata != null) {
|
||||
return new DeckFileHeader(FileSection.parse(metadata, "="));
|
||||
return new DeckFileHeader(FileSection.parse(metadata, FileSection.EQUALS_KV_SEPARATOR));
|
||||
}
|
||||
final List<String> general = map.get("general");
|
||||
if (general != null) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardType.CoreType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.util.PredicateCard;
|
||||
import forge.util.PredicateString;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -60,6 +61,8 @@ public interface IPaperCard extends InventoryItem {
|
||||
return new PredicateNames(what);
|
||||
}
|
||||
|
||||
public static PredicateCards cards(final List<PaperCard> what) { return new PredicateCards(what); }
|
||||
|
||||
private static final class PredicateColor implements Predicate<PaperCard> {
|
||||
|
||||
private final byte operand;
|
||||
@@ -161,6 +164,25 @@ public interface IPaperCard extends InventoryItem {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PredicateCards extends PredicateCard<PaperCard> {
|
||||
private final List<PaperCard> operand;
|
||||
|
||||
@Override
|
||||
public boolean apply(final PaperCard card) {
|
||||
for (final PaperCard element : this.operand) {
|
||||
if (this.op(card, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private PredicateCards(final List<PaperCard> operand) {
|
||||
super(StringOp.EQUALS);
|
||||
this.operand = operand;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-built predicates are stored here to allow their re-usage and
|
||||
* easier access from code.
|
||||
|
||||
@@ -108,7 +108,7 @@ public class PreconDeck implements InventoryItemFromSet {
|
||||
|
||||
// To be able to read "shops" section in overloads
|
||||
protected PreconDeck getPreconDeckFromSections(final Map<String, List<String>> sections) {
|
||||
FileSection kv = FileSection.parse(sections.get("metadata"), "=");
|
||||
FileSection kv = FileSection.parse(sections.get("metadata"), FileSection.EQUALS_KV_SEPARATOR);
|
||||
String imageFilename = kv.get("Image");
|
||||
String description = kv.get("Description");
|
||||
String deckEdition = kv.get("set");
|
||||
|
||||
@@ -566,12 +566,8 @@ public class BoosterGenerator {
|
||||
toAdd = IPaperCard.Predicates.printedInSets(sets);
|
||||
} else if (operator.startsWith("fromSheet(") && invert) {
|
||||
String sheetName = StringUtils.strip(operator.substring(9), "()\" ");
|
||||
Iterable<PaperCard> src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||
List<String> cardNames = Lists.newArrayList();
|
||||
for (PaperCard card : src) {
|
||||
cardNames.add(card.getName());
|
||||
}
|
||||
toAdd = IPaperCard.Predicates.names(Lists.newArrayList(cardNames));
|
||||
Iterable<PaperCard> cards = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||
toAdd = IPaperCard.Predicates.cards(Lists.newArrayList(cards));
|
||||
}
|
||||
|
||||
if (toAdd == null) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package forge.card;
|
||||
package forge.util;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.google.common.base.Charsets;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.LineReader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -15,12 +12,12 @@ public class CardTranslation {
|
||||
private static Map <String, String> translatednames;
|
||||
private static Map <String, String> translatedtypes;
|
||||
private static Map <String, String> translatedoracles;
|
||||
private static String languageSelected;
|
||||
private static String languageSelected = "en-US";
|
||||
|
||||
private static void readTranslationFile(String language) {
|
||||
private static void readTranslationFile(String language, String languagesDirectory) {
|
||||
String filename = "cardnames-" + language + ".txt";
|
||||
|
||||
try (LineReader translationFile = new LineReader(new FileInputStream(ForgeConstants.LANG_DIR + filename), Charsets.UTF_8)) {
|
||||
try (LineReader translationFile = new LineReader(new FileInputStream(languagesDirectory + filename), Charsets.UTF_8)) {
|
||||
for (String line : translationFile.readLines()) {
|
||||
String[] matches = line.split("\\|");
|
||||
if (matches.length >= 2) {
|
||||
@@ -34,7 +31,7 @@ public class CardTranslation {
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.error("Error reading translation file: cardnames-" + language + ".txt");
|
||||
System.err.println("Error reading translation file: cardnames-" + language + ".txt");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +63,7 @@ public class CardTranslation {
|
||||
}
|
||||
|
||||
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) {
|
||||
HashMap<String, String> translations = new HashMap<String, String>();
|
||||
HashMap<String, String> translations = new HashMap<>();
|
||||
translations.put("name", getTranslatedName(cardname));
|
||||
translations.put("oracle", getTranslatedOracle(cardname));
|
||||
translations.put("altname", getTranslatedName(altcardname));
|
||||
@@ -78,14 +75,14 @@ public class CardTranslation {
|
||||
return !languageSelected.equals("en-US");
|
||||
}
|
||||
|
||||
public static void preloadTranslation(String language) {
|
||||
public static void preloadTranslation(String language, String languagesDirectory) {
|
||||
languageSelected = language;
|
||||
|
||||
if (needsTranslation()) {
|
||||
translatednames = new HashMap<>();
|
||||
translatedtypes = new HashMap<>();
|
||||
translatedoracles = new HashMap<>();
|
||||
readTranslationFile(languageSelected);
|
||||
readTranslationFile(languageSelected, languagesDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,12 @@
|
||||
*/
|
||||
package forge.util;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -56,34 +59,40 @@ public class FileSection {
|
||||
protected FileSection(Map<String, String> lines0) {
|
||||
lines = lines0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the.
|
||||
*
|
||||
* @param line the line
|
||||
* @param kvSeparator the kv separator
|
||||
* @param pairSeparator the pair separator
|
||||
* @return the file section
|
||||
*/
|
||||
public static FileSection parse(final String line, final String kvSeparator, final String pairSeparator) {
|
||||
Map<String, String> map = parseToMap(line, kvSeparator, pairSeparator);
|
||||
return new FileSection(map);
|
||||
}
|
||||
|
||||
public static Map<String, String> parseToMap(final String line, final String kvSeparator, final String pairSeparator) {
|
||||
Map<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
if (!StringUtils.isEmpty(line)) {
|
||||
final String[] pairs = line.split(Pattern.quote(pairSeparator));
|
||||
final Pattern splitter = Pattern.compile(Pattern.quote(kvSeparator));
|
||||
|
||||
for (final String dd : pairs) {
|
||||
final String[] v = splitter.split(dd, 2);
|
||||
result.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
|
||||
}
|
||||
|
||||
public static final Pattern DOLLAR_SIGN_KV_SEPARATOR = Pattern.compile(Pattern.quote("$"));
|
||||
public static final Pattern ARROW_KV_SEPARATOR = Pattern.compile(Pattern.quote("->"));
|
||||
public static final Pattern EQUALS_KV_SEPARATOR = Pattern.compile(Pattern.quote("="));
|
||||
public static final Pattern COLON_KV_SEPARATOR = Pattern.compile(Pattern.quote(":"));
|
||||
|
||||
private static final String BAR_PAIR_SPLITTER = Pattern.quote("|");
|
||||
|
||||
private static Table<String, Pattern, Map<String, String>> parseToMapCache = HashBasedTable.create();
|
||||
|
||||
public static Map<String, String> parseToMap(final String line, final Pattern kvSeparator) {
|
||||
Map<String, String> result = parseToMapCache.get(line, kvSeparator);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
result = parseToMapImpl(line, kvSeparator);
|
||||
parseToMapCache.put(line, kvSeparator, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, String> parseToMapImpl(final String line, final Pattern kvSeparator) {
|
||||
if (StringUtils.isEmpty(line)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
final Map<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
final String[] pairs = line.split(BAR_PAIR_SPLITTER);
|
||||
for (final String dd : pairs) {
|
||||
final String[] v = kvSeparator.split(dd, 2);
|
||||
result.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
|
||||
}
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the.
|
||||
*
|
||||
@@ -91,11 +100,10 @@ public class FileSection {
|
||||
* @param kvSeparator the kv separator
|
||||
* @return the file section
|
||||
*/
|
||||
public static FileSection parse(final Iterable<String> lines, final String kvSeparator) {
|
||||
public static FileSection parse(final Iterable<String> lines, final Pattern kvSeparator) {
|
||||
final FileSection result = new FileSection();
|
||||
final Pattern splitter = Pattern.compile(Pattern.quote(kvSeparator));
|
||||
for (final String dd : lines) {
|
||||
final String[] v = splitter.split(dd, 2);
|
||||
final String[] v = kvSeparator.split(dd, 2);
|
||||
result.lines.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class Localizer {
|
||||
|
||||
public String getMessage(final String key, final Object... messageArguments) {
|
||||
MessageFormat formatter = null;
|
||||
|
||||
|
||||
try {
|
||||
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
|
||||
formatter = new MessageFormat(resourceBundle.getString(key), locale);
|
||||
@@ -72,15 +72,29 @@ public class Localizer {
|
||||
formatter.setLocale(locale);
|
||||
|
||||
String formattedMessage = "CHAR ENCODING ERROR";
|
||||
final String[] charsets = { "ISO-8859-1", "UTF-8" };
|
||||
//Support non-English-standard characters
|
||||
String detectedCharset = charset(new String(formatter.format(messageArguments)), new String[] { "ISO-8859-1", "UTF-8" });
|
||||
String detectedCharset = charset(resourceBundle.getString(key), charsets);
|
||||
|
||||
final int argLength = messageArguments.length;
|
||||
Object[] syncEncodingMessageArguments = new Object[argLength];
|
||||
//when messageArguments encoding not equal resourceBundle.getString(key),convert to equal
|
||||
//avoid convert to a have two encoding content formattedMessage string.
|
||||
for (int i = 0; i < argLength; i++) {
|
||||
String objCharset = charset(messageArguments[i].toString(), charsets);
|
||||
try {
|
||||
syncEncodingMessageArguments[i] = convert(messageArguments[i].toString(), objCharset, detectedCharset);
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
System.err.println("Cannot Convert '" + messageArguments[i].toString() + "' from '" + objCharset + "' To '" + detectedCharset + "'");
|
||||
return "encoding '" + key + "' translate string failure";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
formattedMessage = new String(formatter.format(messageArguments).getBytes(detectedCharset), StandardCharsets.UTF_8);
|
||||
formattedMessage = new String(formatter.format(syncEncodingMessageArguments).getBytes(detectedCharset), StandardCharsets.UTF_8);
|
||||
} catch(UnsupportedEncodingException ignored) {}
|
||||
|
||||
return formattedMessage;
|
||||
|
||||
}
|
||||
|
||||
public void setLanguage(final String languageRegionID, final String languagesDirectory) {
|
||||
|
||||
83
forge-core/src/main/java/forge/util/PredicateCard.java
Normal file
83
forge-core/src/main/java/forge/util/PredicateCard.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2020 Jamin W. Collins
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.util;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
/**
|
||||
* Special predicate class to perform string operations.
|
||||
*
|
||||
* @param <T>
|
||||
* the generic type
|
||||
*/
|
||||
public abstract class PredicateCard<T> implements Predicate<T> {
|
||||
/** Possible operators for string operands. */
|
||||
public enum StringOp {
|
||||
/** The EQUALS. */
|
||||
EQUALS,
|
||||
}
|
||||
|
||||
/** The operator. */
|
||||
private final StringOp operator;
|
||||
|
||||
/**
|
||||
* Op.
|
||||
*
|
||||
* @param op1
|
||||
* the op1
|
||||
* @param op2
|
||||
* the op2
|
||||
* @return true, if successful
|
||||
*/
|
||||
protected final boolean op(final PaperCard op1, final PaperCard op2) {
|
||||
switch (this.getOperator()) {
|
||||
case EQUALS:
|
||||
return op1.equals(op2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new predicate string.
|
||||
*
|
||||
* @param operator
|
||||
* the operator
|
||||
*/
|
||||
public PredicateCard(final StringOp operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the operator
|
||||
*/
|
||||
public StringOp getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public static PredicateCard<PaperCard> equals(final PaperCard what) {
|
||||
return new PredicateCard<PaperCard>(StringOp.EQUALS) {
|
||||
@Override
|
||||
public boolean apply(PaperCard subject) {
|
||||
return op(subject, what);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import forge.item.PaperCard;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -17,6 +19,22 @@ import java.util.Map.Entry;
|
||||
*/
|
||||
public class TextUtil {
|
||||
|
||||
static ImmutableSortedMap<Integer,String> romanMap = ImmutableSortedMap.<Integer,String>naturalOrder()
|
||||
.put(1000, "M").put(900, "CM")
|
||||
.put(500, "D").put(400, "CD")
|
||||
.put(100, "C").put(90, "XC")
|
||||
.put(50, "L").put(40, "XL")
|
||||
.put(10, "X").put(9, "IX")
|
||||
.put(5, "V").put(4, "IV").put(1, "I").build();
|
||||
|
||||
public final static String toRoman(int number) {
|
||||
if (number <= 0) {
|
||||
return "";
|
||||
}
|
||||
int l = romanMap.floorKey(number);
|
||||
return romanMap.get(l) + toRoman(number-l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts an object to a String.
|
||||
*
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>forge</artifactId>
|
||||
<groupId>forge</groupId>
|
||||
<version>1.6.30-SNAPSHOT</version>
|
||||
<version>1.6.33</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-game</artifactId>
|
||||
@@ -32,8 +32,8 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.sentry</groupId>
|
||||
<artifactId>sentry-log4j</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<artifactId>sentry-log4j2</artifactId>
|
||||
<version>1.7.27</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -72,8 +72,9 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
|
||||
return this.mapParams;
|
||||
}
|
||||
|
||||
public final String getParamOrDefault(String key, String defaultValue) {
|
||||
return hasParam(key) ? getParam(key) : defaultValue;
|
||||
public String getParamOrDefault(String key, String defaultValue) {
|
||||
String param = mapParams.get(key);
|
||||
return param != null ? param : defaultValue;
|
||||
}
|
||||
|
||||
public String getParam(String key) {
|
||||
@@ -560,4 +561,12 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
|
||||
// this does overwrite the original MapParams
|
||||
this.originalMapParams = Maps.newHashMap(this.mapParams);
|
||||
}
|
||||
|
||||
protected void copyHelper(CardTraitBase copy, Card host) {
|
||||
copy.originalMapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.mapParams = Maps.newHashMap(originalMapParams);
|
||||
copy.sVars = Maps.newHashMap(sVars);
|
||||
// dont use setHostCard to not trigger the not copied parts yet
|
||||
copy.hostCard = host;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,8 @@ public class ForgeScript {
|
||||
return found;
|
||||
} else if (property.equals("YouCtrl")) {
|
||||
return sa.getActivatingPlayer().equals(sourceController);
|
||||
} else if (property.equals("OppCtrl")) {
|
||||
return sa.getActivatingPlayer().isOpponentOf(sourceController);
|
||||
} else if (sa.getHostCard() != null) {
|
||||
return sa.getHostCard().hasProperty(property, sourceController, source, spellAbility);
|
||||
}
|
||||
|
||||
@@ -914,4 +914,17 @@ public class Game {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Player getControlVote() {
|
||||
Player result = null;
|
||||
long maxValue = 0;
|
||||
for (Player p : getPlayers()) {
|
||||
Long v = p.getHighestControlVote();
|
||||
if (v != null && v > maxValue) {
|
||||
maxValue = v;
|
||||
result = p;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -57,7 +57,7 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* Methods for common actions performed during a game.
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -80,7 +80,7 @@ public class GameAction {
|
||||
public Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause) {
|
||||
return changeZone(zoneFrom, zoneTo, c, position, cause, null);
|
||||
}
|
||||
|
||||
|
||||
private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
if (c.isCopiedSpell() || (c.isImmutable() && zoneTo.is(ZoneType.Exile))) {
|
||||
// Remove Effect from command immediately, this is essential when some replacement
|
||||
@@ -125,11 +125,16 @@ public class GameAction {
|
||||
Card copied = null;
|
||||
Card lastKnownInfo = null;
|
||||
|
||||
// get the LKI from above like ChangeZoneEffect
|
||||
if (params != null && params.containsKey(AbilityKey.CardLKI)) {
|
||||
lastKnownInfo = (Card) params.get(AbilityKey.CardLKI);
|
||||
}
|
||||
|
||||
if (c.isSplitCard()) {
|
||||
boolean resetToOriginal = false;
|
||||
|
||||
if (c.isManifested()) {
|
||||
if (zoneFrom.is(ZoneType.Battlefield)) {
|
||||
if (fromBattlefield) {
|
||||
// Make sure the card returns from the battlefield as the original card with two halves
|
||||
resetToOriginal = true;
|
||||
}
|
||||
@@ -164,8 +169,22 @@ public class GameAction {
|
||||
// Don't copy Tokens, copy only cards leaving the battlefield
|
||||
// and returning to hand (to recreate their spell ability information)
|
||||
if (suppress || (!fromBattlefield && !toHand)) {
|
||||
lastKnownInfo = c;
|
||||
copied = c;
|
||||
|
||||
// if to Battlefield and it is caused by an replacement effect,
|
||||
// try to get previous LKI if able
|
||||
if (zoneTo.is(ZoneType.Battlefield)) {
|
||||
if (cause != null && cause.isReplacementAbility()) {
|
||||
ReplacementEffect re = cause.getReplacementEffect();
|
||||
if (ReplacementType.Moved.equals(re.getMode())) {
|
||||
lastKnownInfo = (Card) cause.getReplacingObject(AbilityKey.CardLKI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastKnownInfo == null) {
|
||||
lastKnownInfo = CardUtil.getLKICopy(c);
|
||||
}
|
||||
} else {
|
||||
// if from Battlefield to Graveyard and Card does exist in LastStateBattlefield
|
||||
// use that instead
|
||||
@@ -189,15 +208,13 @@ public class GameAction {
|
||||
}
|
||||
|
||||
if (!c.isToken()) {
|
||||
if (c.isCloned() || c.hasTextChangeState()) {
|
||||
c.removeCloneStates();
|
||||
c.removeTextChangeStates();
|
||||
if (c.removeChangedState()) {
|
||||
c.updateStateForView();
|
||||
}
|
||||
|
||||
copied = CardFactory.copyCard(c, false);
|
||||
|
||||
if (fromBattlefield && copied.getCurrentStateName() != CardStateName.Original) {
|
||||
if (fromBattlefield) {
|
||||
// when a card leaves the battlefield, ensure it's in its original state
|
||||
// (we need to do this on the object before copying it, or it won't work correctly e.g.
|
||||
// on Transformed objects)
|
||||
@@ -227,59 +244,6 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
// special rule for Worms of the Earth
|
||||
if (toBattlefield && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLandBattlefield)) {
|
||||
// something that is already a Land cant enter the battlefield
|
||||
Card noLandLKI = c;
|
||||
if (!c.isLand()) {
|
||||
// check if something would be a land
|
||||
noLandLKI = CardUtil.getLKICopy(c);
|
||||
// this check needs to check if this card would be on the battlefield
|
||||
noLandLKI.setLastKnownZone(zoneTo);
|
||||
|
||||
CardCollection preList = new CardCollection(noLandLKI);
|
||||
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
|
||||
|
||||
// fake etb counters thing, then if something changed,
|
||||
// need to apply checkStaticAbilities again
|
||||
if(!noLandLKI.isLand()) {
|
||||
if (noLandLKI.putEtbCounters(null)) {
|
||||
// counters are added need to check again
|
||||
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(noLandLKI.isLand()) {
|
||||
// if it isn't on the Stack, it stays in that Zone
|
||||
if (!c.isInZone(ZoneType.Stack)) {
|
||||
return c;
|
||||
}
|
||||
// if something would only be a land when entering the battlefield and not before
|
||||
// put it into the graveyard instead
|
||||
zoneTo = c.getOwner().getZone(ZoneType.Graveyard);
|
||||
|
||||
// reset facedown
|
||||
copied.setState(CardStateName.Original, false);
|
||||
copied.setManifested(false);
|
||||
copied.updateStateForView();
|
||||
|
||||
// not to battlefield anymore!
|
||||
toBattlefield = false;
|
||||
|
||||
if (c.isCloned() || c.hasTextChangeState()) {
|
||||
c.removeCloneStates();
|
||||
c.removeTextChangeStates();
|
||||
c.updateStateForView();
|
||||
}
|
||||
|
||||
if (copied.getCurrentStateName() != CardStateName.Original) {
|
||||
copied.setState(CardStateName.Original, false);
|
||||
}
|
||||
|
||||
copied.updateStateForView();
|
||||
}
|
||||
}
|
||||
|
||||
if (!suppress) {
|
||||
if (zoneFrom == null) {
|
||||
copied.getOwner().addInboundToken(copied);
|
||||
@@ -298,15 +262,29 @@ public class GameAction {
|
||||
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
|
||||
if (repres != ReplacementResult.NotReplaced) {
|
||||
// reset failed manifested Cards back to original
|
||||
if (c.isManifested()) {
|
||||
if (c.isManifested() && !c.isInZone(ZoneType.Battlefield)) {
|
||||
c.turnFaceUp(false, false);
|
||||
}
|
||||
|
||||
if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard) && repres == ReplacementResult.Prevented) {
|
||||
copied.getOwner().removeInboundToken(copied);
|
||||
return moveToGraveyard(c, cause, params);
|
||||
}
|
||||
copied.getOwner().removeInboundToken(copied);
|
||||
|
||||
if (repres == ReplacementResult.Prevented) {
|
||||
if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard)) {
|
||||
return moveToGraveyard(c, cause, params);
|
||||
}
|
||||
|
||||
copied.clearDevoured();
|
||||
copied.clearDelved();
|
||||
copied.clearConvoked();
|
||||
copied.clearExploited();
|
||||
}
|
||||
|
||||
// was replaced with another Zone Change
|
||||
if (toBattlefield && !c.isInZone(ZoneType.Battlefield)) {
|
||||
if (c.removeChangedState()) {
|
||||
c.updateStateForView();
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -370,7 +348,7 @@ public class GameAction {
|
||||
|
||||
// do ETB counters after zone add
|
||||
if (!suppress) {
|
||||
if (toBattlefield ) {
|
||||
if (toBattlefield) {
|
||||
copied.putEtbCounters(table);
|
||||
// enable replacement effects again
|
||||
for (final ReplacementEffect re : copied.getReplacementEffects()) {
|
||||
@@ -393,6 +371,14 @@ public class GameAction {
|
||||
c.setMustAttackEntity(null);
|
||||
}
|
||||
|
||||
// for ETB trigger to work correct,
|
||||
// the LKI needs to be the Card itself,
|
||||
// or it might not updated correctly
|
||||
// TODO be reworked when ZoneTrigger Update is done
|
||||
if (toBattlefield || zoneTo.is(ZoneType.Stack)) {
|
||||
lastKnownInfo = c;
|
||||
}
|
||||
|
||||
// Need to apply any static effects to produce correct triggers
|
||||
checkStaticAbilities();
|
||||
game.getTriggerHandler().clearInstrinsicActiveTriggers(c, zoneFrom);
|
||||
@@ -415,7 +401,7 @@ public class GameAction {
|
||||
}
|
||||
|
||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true);
|
||||
if (zoneFrom != null && zoneFrom.is(ZoneType.Battlefield) && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
|
||||
if (fromBattlefield && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
|
||||
final Map<AbilityKey, Object> runParams2 = AbilityKey.mapFromCard(lastKnownInfo);
|
||||
runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer());
|
||||
if(params != null) {
|
||||
@@ -447,7 +433,7 @@ public class GameAction {
|
||||
copied.clearExploited();
|
||||
}
|
||||
|
||||
// rule 504.6: reveal a face-down card leaving the stack
|
||||
// rule 504.6: reveal a face-down card leaving the stack
|
||||
if (zoneFrom != null && zoneTo != null && zoneFrom.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield) && wasFacedown) {
|
||||
Card revealLKI = CardUtil.getLKICopy(c);
|
||||
revealLKI.turnFaceUp(true, false);
|
||||
@@ -491,7 +477,7 @@ public class GameAction {
|
||||
// Remove all changed keywords
|
||||
copied.removeAllChangedText(game.getNextTimestamp());
|
||||
} else if (toBattlefield) {
|
||||
// reset timestamp in changezone effects so they have same timestamp if ETB simutaneously
|
||||
// reset timestamp in changezone effects so they have same timestamp if ETB simutaneously
|
||||
copied.setTimestamp(game.getNextTimestamp());
|
||||
for (Player p : game.getPlayers()) {
|
||||
copied.getDamageHistory().setNotAttackedSinceLastUpkeepOf(p);
|
||||
@@ -514,6 +500,11 @@ public class GameAction {
|
||||
}
|
||||
}
|
||||
|
||||
// Cards not on the battlefield / stack should not have controller
|
||||
if (!zoneTo.is(ZoneType.Battlefield) && !zoneTo.is(ZoneType.Stack)) {
|
||||
c.clearControllers();
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
@@ -535,7 +526,7 @@ public class GameAction {
|
||||
return moveTo(zoneTo, c, position, cause, null);
|
||||
}
|
||||
|
||||
private Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
public final Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
// FThreads.assertExecutedByEdt(false); // This code must never be executed from EDT,
|
||||
// use FThreads.invokeInNewThread to run code in a pooled thread
|
||||
return moveTo(zoneTo, c, null, cause, params);
|
||||
@@ -556,6 +547,13 @@ public class GameAction {
|
||||
c.setCastSA(null);
|
||||
} else if (zoneTo.is(ZoneType.Stack)) {
|
||||
c.setCastFrom(zoneFrom.getZoneType());
|
||||
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard()) && !c.isCopiedSpell()) {
|
||||
cause.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
cause.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
c.setCastSA(cause);
|
||||
} else {
|
||||
c.setCastSA(null);
|
||||
}
|
||||
} else if (!(zoneTo.is(ZoneType.Battlefield) && zoneFrom.is(ZoneType.Stack))) {
|
||||
c.setCastFrom(null);
|
||||
c.setCastSA(null);
|
||||
@@ -574,16 +572,31 @@ public class GameAction {
|
||||
public final void controllerChangeZoneCorrection(final Card c) {
|
||||
System.out.println("Correcting zone for " + c.toString());
|
||||
final Zone oldBattlefield = game.getZoneOf(c);
|
||||
if (oldBattlefield == null || oldBattlefield.getZoneType() == ZoneType.Stack) {
|
||||
|
||||
if (oldBattlefield == null || oldBattlefield.is(ZoneType.Stack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Player original = oldBattlefield.getPlayer();
|
||||
final PlayerZone newBattlefield = c.getController().getZone(oldBattlefield.getZoneType());
|
||||
final Player controller = c.getController();
|
||||
if (original == null || controller == null || original.equals(controller)) {
|
||||
return;
|
||||
}
|
||||
final PlayerZone newBattlefield = controller.getZone(oldBattlefield.getZoneType());
|
||||
|
||||
if (newBattlefield == null || oldBattlefield.equals(newBattlefield)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 702.94e A paired creature becomes unpaired if any of the following occur:
|
||||
// another player gains control of it or the creature it’s paired with
|
||||
if (c.isPaired()) {
|
||||
Card partner = c.getPairedWith();
|
||||
c.setPairedWith(null);
|
||||
partner.setPairedWith(null);
|
||||
partner.updateStateForView();
|
||||
}
|
||||
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
for (Player p : game.getPlayers()) {
|
||||
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(false);
|
||||
@@ -613,8 +626,12 @@ public class GameAction {
|
||||
}
|
||||
|
||||
public final Card moveToStack(final Card c, SpellAbility cause) {
|
||||
return moveToStack(c, cause, null);
|
||||
}
|
||||
|
||||
public final Card moveToStack(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
final Zone stack = game.getStackZone();
|
||||
return moveTo(stack, c, cause);
|
||||
return moveTo(stack, c, cause, params);
|
||||
}
|
||||
|
||||
public final Card moveToGraveyard(final Card c, SpellAbility cause) {
|
||||
@@ -629,7 +646,7 @@ public class GameAction {
|
||||
public final Card moveToHand(final Card c, SpellAbility cause) {
|
||||
return moveToHand(c, cause, null);
|
||||
}
|
||||
|
||||
|
||||
public final Card moveToHand(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
final PlayerZone hand = c.getOwner().getZone(ZoneType.Hand);
|
||||
return moveTo(hand, c, cause, params);
|
||||
@@ -643,7 +660,7 @@ public class GameAction {
|
||||
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause) {
|
||||
return moveToPlay(c, p, cause, null);
|
||||
}
|
||||
|
||||
|
||||
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
// move to a specific player's Battlefield
|
||||
final PlayerZone play = p.getZone(ZoneType.Battlefield);
|
||||
@@ -653,7 +670,7 @@ public class GameAction {
|
||||
public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause) {
|
||||
return moveToBottomOfLibrary(c, cause, null);
|
||||
}
|
||||
|
||||
|
||||
public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
return moveToLibrary(c, -1, cause, params);
|
||||
}
|
||||
@@ -661,7 +678,7 @@ public class GameAction {
|
||||
public final Card moveToLibrary(final Card c, SpellAbility cause) {
|
||||
return moveToLibrary(c, cause, null);
|
||||
}
|
||||
|
||||
|
||||
public final Card moveToLibrary(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
return moveToLibrary(c, 0, cause, params);
|
||||
}
|
||||
@@ -669,7 +686,7 @@ public class GameAction {
|
||||
public final Card moveToLibrary(Card c, int libPosition, SpellAbility cause) {
|
||||
return moveToLibrary(c, libPosition, cause, null);
|
||||
}
|
||||
|
||||
|
||||
public final Card moveToLibrary(Card c, int libPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
final PlayerZone library = c.getOwner().getZone(ZoneType.Library);
|
||||
if (libPosition == -1 || libPosition > library.size()) {
|
||||
@@ -679,11 +696,15 @@ public class GameAction {
|
||||
}
|
||||
|
||||
public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause) {
|
||||
return moveToVariantDeck(c, zone, deckPosition, cause, null);
|
||||
}
|
||||
|
||||
public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
final PlayerZone deck = c.getOwner().getZone(zone);
|
||||
if (deckPosition == -1 || deckPosition > deck.size()) {
|
||||
deckPosition = deck.size();
|
||||
}
|
||||
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause);
|
||||
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause, params);
|
||||
}
|
||||
|
||||
public final Card exile(final Card c, SpellAbility cause) {
|
||||
@@ -701,7 +722,9 @@ public class GameAction {
|
||||
// Run triggers
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
||||
runParams.put(AbilityKey.Cause, cause);
|
||||
runParams.put(AbilityKey.Origin, origin.getZoneType().name());
|
||||
if (origin != null) { // is generally null when adding via dev mode
|
||||
runParams.put(AbilityKey.Origin, origin.getZoneType().name());
|
||||
}
|
||||
if (params != null) {
|
||||
runParams.putAll(params);
|
||||
}
|
||||
@@ -716,16 +739,20 @@ public class GameAction {
|
||||
}
|
||||
|
||||
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause) {
|
||||
return moveTo(name, c, libPosition, cause, null);
|
||||
}
|
||||
|
||||
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
// Call specific functions to set PlayerZone, then move onto moveTo
|
||||
switch(name) {
|
||||
case Hand: return moveToHand(c, cause);
|
||||
case Library: return moveToLibrary(c, libPosition, cause);
|
||||
case Battlefield: return moveToPlay(c, cause);
|
||||
case Graveyard: return moveToGraveyard(c, cause);
|
||||
case Exile: return exile(c, cause);
|
||||
case Stack: return moveToStack(c, cause);
|
||||
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause);
|
||||
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause);
|
||||
case Hand: return moveToHand(c, cause, params);
|
||||
case Library: return moveToLibrary(c, libPosition, cause, params);
|
||||
case Battlefield: return moveToPlay(c, c.getController(), cause, params);
|
||||
case Graveyard: return moveToGraveyard(c, cause, params);
|
||||
case Exile: return exile(c, cause, params);
|
||||
case Stack: return moveToStack(c, cause, params);
|
||||
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
|
||||
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
|
||||
default: // sideboard will also get there
|
||||
return moveTo(c.getOwner().getZone(name), c, cause);
|
||||
}
|
||||
@@ -786,7 +813,7 @@ public class GameAction {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
final Comparator<StaticAbility> comp = new Comparator<StaticAbility>() {
|
||||
@Override
|
||||
public int compare(final StaticAbility a, final StaticAbility b) {
|
||||
@@ -1066,7 +1093,7 @@ public class GameAction {
|
||||
if (!game.isGameOver()) {
|
||||
checkGameOverCondition();
|
||||
}
|
||||
|
||||
|
||||
if (game.getAge() != GameStage.Play) {
|
||||
return;
|
||||
}
|
||||
@@ -1551,8 +1578,7 @@ public class GameAction {
|
||||
// Where there are none, it should bring up speed controls
|
||||
game.fireEvent(new GameEventGameStarted(gameType, first, game.getPlayers()));
|
||||
|
||||
// Emissary's Plot
|
||||
// runPreOpeningHandActions(first);
|
||||
runPreOpeningHandActions(first);
|
||||
|
||||
game.setAge(GameStage.Mulligan);
|
||||
for (final Player p1 : game.getPlayers()) {
|
||||
|
||||
@@ -22,9 +22,10 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
@@ -33,9 +34,15 @@ import forge.game.cost.Cost;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -75,77 +82,19 @@ public final class GameActionUtil {
|
||||
Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
if (sa.isSpell()) {
|
||||
if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
boolean lkicheck = false;
|
||||
|
||||
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.turnFaceUp(false, false);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (sa.hasParam("Bestow") && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (sa.isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
source.turnFaceDownNoUpdate();
|
||||
lkicheck = true;
|
||||
} else if (sa.isAdventure() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.Adventure, false);
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
lkicheck = true;
|
||||
} else if (source.isSplitCard() && (sa.isLeftSplit() || sa.isRightSplit())) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
if (sa.isLeftSplit()) {
|
||||
if (!source.hasState(CardStateName.LeftSplit)) {
|
||||
source.addAlternateState(CardStateName.LeftSplit, false);
|
||||
source.getState(CardStateName.LeftSplit).copyFrom(
|
||||
sa.getHostCard().getState(CardStateName.LeftSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.LeftSplit, false);
|
||||
}
|
||||
|
||||
if (sa.isRightSplit()) {
|
||||
if (!source.hasState(CardStateName.RightSplit)) {
|
||||
source.addAlternateState(CardStateName.RightSplit, false);
|
||||
source.getState(CardStateName.RightSplit).copyFrom(
|
||||
sa.getHostCard().getState(CardStateName.RightSplit), true);
|
||||
}
|
||||
|
||||
source.setState(CardStateName.RightSplit, false);
|
||||
}
|
||||
|
||||
// need to reset CMC
|
||||
source.setLKICMC(-1);
|
||||
source.setLKICMC(source.getCMC());
|
||||
Card newHost = ((Spell)sa).getAlternateHost(source);
|
||||
if (newHost != null) {
|
||||
source = newHost;
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (lkicheck) {
|
||||
// double freeze tracker, so it doesn't update view
|
||||
game.getTracker().freeze();
|
||||
source.clearChangedCardKeywords(false);
|
||||
CardCollection preList = new CardCollection(source);
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
||||
}
|
||||
@@ -207,6 +156,58 @@ public final class GameActionUtil {
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
|
||||
// need to be done there before static abilities does reset the card
|
||||
if (sa.isBasicSpell()) {
|
||||
for (final KeywordInterface inst : source.getKeywords()) {
|
||||
final String keyword = inst.getOriginal();
|
||||
|
||||
if (keyword.startsWith("Escape")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost escapeCost = new Cost(k[1], true);
|
||||
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost);
|
||||
newSA.setActivatingPlayer(activator);
|
||||
|
||||
newSA.getMapParams().put("PrecostDesc", "Escape—");
|
||||
newSA.getMapParams().put("CostDesc", escapeCost.toString());
|
||||
|
||||
// makes new SpellDescription
|
||||
final StringBuilder desc = new StringBuilder();
|
||||
desc.append(newSA.getCostDescription());
|
||||
desc.append("(").append(inst.getReminderText()).append(")");
|
||||
newSA.setDescription(desc.toString());
|
||||
|
||||
// Stack Description only for Permanent or it might crash
|
||||
if (source.isPermanent()) {
|
||||
final StringBuilder sbStack = new StringBuilder();
|
||||
sbStack.append(sa.getStackDescription()).append(" (Escaped)");
|
||||
newSA.setStackDescription(sbStack.toString());
|
||||
}
|
||||
newSA.setAlternativeCost(AlternativeCost.Escape);
|
||||
newSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
|
||||
alternatives.add(newSA);
|
||||
} else if (keyword.startsWith("Flashback")) {
|
||||
// if source has No Mana cost, and flashback doesn't have own one,
|
||||
// flashback can't work
|
||||
if (keyword.equals("Flashback") && source.getManaCost().isNoCost()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SpellAbility flashback = sa.copy(activator);
|
||||
flashback.setAlternativeCost(AlternativeCost.Flashback);
|
||||
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
|
||||
// there is a flashback cost (and not the cards cost)
|
||||
if (keyword.contains(":")) {
|
||||
final String[] k = keyword.split(":");
|
||||
flashback.setPayCosts(new Cost(k[1], false));
|
||||
}
|
||||
alternatives.add(flashback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
@@ -224,6 +225,7 @@ public final class GameActionUtil {
|
||||
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
||||
// set the cost to this directly to buypass non mana cost
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>");
|
||||
newSA.setActivatingPlayer(activator);
|
||||
newSA.setBasicSpell(false);
|
||||
newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0"));
|
||||
// makes new SpellDescription
|
||||
@@ -244,28 +246,6 @@ public final class GameActionUtil {
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
|
||||
for (final KeywordInterface inst : source.getKeywords()) {
|
||||
final String keyword = inst.getOriginal();
|
||||
if (sa.isSpell() && keyword.startsWith("Flashback")) {
|
||||
// if source has No Mana cost, and flashback doesn't have own one,
|
||||
// flashback can't work
|
||||
if (keyword.equals("Flashback") && source.getManaCost().isNoCost()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SpellAbility flashback = sa.copy(activator);
|
||||
flashback.setFlashBackAbility(true);
|
||||
|
||||
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
|
||||
// there is a flashback cost (and not the cards cost)
|
||||
if (keyword.contains(":")) {
|
||||
final String[] k = keyword.split(":");
|
||||
flashback.setPayCosts(new Cost(k[1], false));
|
||||
}
|
||||
alternatives.add(flashback);
|
||||
}
|
||||
}
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
@@ -391,10 +371,11 @@ public final class GameActionUtil {
|
||||
}
|
||||
SpellAbility result = null;
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final PlayerController pc = activator.getController();
|
||||
|
||||
host.getGame().getAction().checkStaticAbilities(false);
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
|
||||
boolean reset = false;
|
||||
|
||||
@@ -457,7 +438,60 @@ public final class GameActionUtil {
|
||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||
|
||||
if (v > 0) {
|
||||
host.addReplacementEffect(CardFactoryUtil.makeEtbCounter("etbCounter:P1P1:" + v, host, false));
|
||||
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(c.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setToken(true); // Set token to true, so when leaving play it gets nuked
|
||||
eff.setOwner(activator);
|
||||
|
||||
eff.setImageKey(c.getImageKey());
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
eff.setImmutable(true);
|
||||
// try to get the SpellAbility from the mana ability
|
||||
//eff.setEffectSource((SpellAbility)null);
|
||||
|
||||
eff.addRemembered(host);
|
||||
|
||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
|
||||
|
||||
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
|
||||
|
||||
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
||||
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||
re.setLayer(ReplacementLayer.Other);
|
||||
re.setOverridingAbility(saAb);
|
||||
|
||||
eff.addReplacementEffect(re);
|
||||
|
||||
// Forgot Trigger
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
eff.addTrigger(parsedTrigger);
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
|
||||
if (result == null) {
|
||||
result = sa.copy();
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
}
|
||||
|
||||
// CantTarget static abilities
|
||||
for (final Card ca : getGame().getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
|
||||
for (final Card ca : getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (stAb.applyAbility("CantAttach", attach, this)) {
|
||||
return false;
|
||||
|
||||
@@ -47,7 +47,7 @@ import java.util.Map.Entry;
|
||||
public class GameFormat implements Comparable<GameFormat> {
|
||||
private final String name;
|
||||
public enum FormatType {Sanctioned, Casual, Historic, Digital, Custom}
|
||||
public enum FormatSubType {Block, Standard, Extended, Modern, Legacy, Vintage, Commander, Planechase, Videogame, MTGO, Custom}
|
||||
public enum FormatSubType {Block, Standard, Extended, Pioneer, Modern, Legacy, Vintage, Commander, Planechase, Videogame, MTGO, Custom}
|
||||
|
||||
// contains allowed sets, when empty allows all sets
|
||||
private FormatType formatType;
|
||||
@@ -290,6 +290,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
||||
private List<String> coreFormats = new ArrayList<>();
|
||||
{
|
||||
coreFormats.add("Standard.txt");
|
||||
coreFormats.add("Pioneer.txt");
|
||||
coreFormats.add("Modern.txt");
|
||||
coreFormats.add("Legacy.txt");
|
||||
coreFormats.add("Vintage.txt");
|
||||
@@ -320,7 +321,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
||||
if (formatStrings == null){
|
||||
return null;
|
||||
}
|
||||
FileSection section = FileSection.parse(formatStrings, ":");
|
||||
FileSection section = FileSection.parse(formatStrings, FileSection.COLON_KV_SEPARATOR);
|
||||
String title = section.get("name");
|
||||
FormatType formatType;
|
||||
try {
|
||||
@@ -468,6 +469,10 @@ public class GameFormat implements Comparable<GameFormat> {
|
||||
return this.map.get("Extended");
|
||||
}
|
||||
|
||||
public GameFormat getPioneer() {
|
||||
return this.map.get("Pioneer");
|
||||
}
|
||||
|
||||
public GameFormat getModern() {
|
||||
return this.map.get("Modern");
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ import forge.game.player.RegisteredPlayer;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.maps.MapOfLists;
|
||||
|
||||
public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
private final GameLog log;
|
||||
|
||||
public GameLogFormatter(GameLog gameLog) {
|
||||
log = gameLog;
|
||||
}
|
||||
@@ -52,16 +52,15 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventScry ev) {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
String scryOutcome = "";
|
||||
String toTop = Lang.nounWithAmount(ev.toTop, "card") + " to the top of the library";
|
||||
String toBottom = Lang.nounWithAmount(ev.toBottom, "card") + " to the bottom of the library";
|
||||
|
||||
if (ev.toTop > 0 && ev.toBottom > 0) {
|
||||
scryOutcome = ev.player.toString() + " scried " + toTop + " and " + toBottom;
|
||||
scryOutcome = localizer.getMessage("lblLogScryTopBottomLibrary").replace("%s", ev.player.toString()).replace("%top", String.valueOf(ev.toTop)).replace("%bottom", String.valueOf(ev.toBottom));
|
||||
} else if (ev.toBottom == 0) {
|
||||
scryOutcome = ev.player.toString() + " scried " + toTop;
|
||||
scryOutcome = localizer.getMessage("lblLogScryTopLibrary").replace("%s", ev.player.toString()).replace("%top", String.valueOf(ev.toTop));
|
||||
} else {
|
||||
scryOutcome = ev.player.toString() + " scried " + toBottom;
|
||||
scryOutcome = localizer.getMessage("lblLogScryBottomLibrary").replace("%s", ev.player.toString()).replace("%bottom", String.valueOf(ev.toBottom));
|
||||
}
|
||||
|
||||
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, scryOutcome);
|
||||
@@ -69,16 +68,15 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventSurveil ev) {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
String surveilOutcome = "";
|
||||
String toLibrary = Lang.nounWithAmount(ev.toLibrary, "card") + " to the top of the library";
|
||||
String toGraveyard = Lang.nounWithAmount(ev.toGraveyard, "card") + " to the graveyard";
|
||||
|
||||
if (ev.toLibrary > 0 && ev.toGraveyard > 0) {
|
||||
surveilOutcome = ev.player.toString() + " surveiled " + toLibrary + " and " + toGraveyard;
|
||||
surveilOutcome = localizer.getMessage("lblLogSurveiledToLibraryGraveyard", ev.player.toString(), String.valueOf(ev.toLibrary), String.valueOf(ev.toGraveyard));
|
||||
} else if (ev.toGraveyard == 0) {
|
||||
surveilOutcome = ev.player.toString() + " surveiled " + toLibrary;
|
||||
surveilOutcome = localizer.getMessage("lblLogSurveiledToLibrary", ev.player.toString(), String.valueOf(ev.toLibrary));
|
||||
} else {
|
||||
surveilOutcome = ev.player.toString() + " surveiled " + toGraveyard;
|
||||
surveilOutcome = localizer.getMessage("lblLogSurveiledToGraveyard", ev.player.toString(), String.valueOf(ev.toGraveyard));
|
||||
}
|
||||
|
||||
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, surveilOutcome);
|
||||
@@ -86,21 +84,25 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventSpellResolved ev) {
|
||||
String messageForLog = ev.hasFizzled ? ev.spell.getHostCard().getName() + " ability fizzles." : ev.spell.getStackDescription();
|
||||
String messageForLog = ev.hasFizzled ? Localizer.getInstance().getMessage("lblLogCardAbilityFizzles", ev.spell.getHostCard().toString()) : ev.spell.getStackDescription();
|
||||
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, messageForLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventSpellAbilityCast event) {
|
||||
String who = event.sa.getActivatingPlayer().getName();
|
||||
String action = event.sa.isSpell() ? " cast " : event.sa.isTrigger() ? " triggered " : " activated ";
|
||||
String what = event.sa.getStackDescription().startsWith("Morph ") ? "Morph" : event.sa.getHostCard().toString();
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
String player = event.sa.getActivatingPlayer().getName();
|
||||
String action = event.sa.isSpell() ? localizer.getMessage("lblCast")
|
||||
: event.sa.isTrigger() ? localizer.getMessage("lblTriggered")
|
||||
: localizer.getMessage("lblActivated");
|
||||
String object = event.sa.getStackDescription().startsWith("Morph ")
|
||||
? localizer.getMessage("lblMorph")
|
||||
: event.sa.getHostCard().toString();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(who).append(action).append(what);
|
||||
String messageForLog = "";
|
||||
|
||||
if (event.sa.getTargetRestrictions() != null) {
|
||||
sb.append(" targeting ");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
List<TargetChoices> targets = event.sa.getAllTargetChoices();
|
||||
// Include the TargetChoices from the stack instance, since the real target choices
|
||||
@@ -111,10 +113,12 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
sb.append(ch.getTargetedString());
|
||||
}
|
||||
}
|
||||
messageForLog = localizer.getMessage("lblLogPlayerActionObjectWitchTarget", player, action, object, sb.toString());
|
||||
} else {
|
||||
messageForLog = localizer.getMessage("lblLogPlayerActionObject", player, action, object);
|
||||
}
|
||||
sb.append(".");
|
||||
|
||||
return new GameLogEntry(GameLogEntryType.STACK_ADD, sb.toString());
|
||||
return new GameLogEntry(GameLogEntryType.STACK_ADD, messageForLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,7 +127,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
return null;
|
||||
}
|
||||
|
||||
String modeChoiceOutcome = ev.player.toString() + " has chosen " + ev.mode + " for " + ev.cardName + ".";
|
||||
String modeChoiceOutcome = Localizer.getInstance().getMessage("lblLogPlayerChosenModeForCard", ev.player.toString(), ev.mode, ev.cardName);
|
||||
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, modeChoiceOutcome);
|
||||
}
|
||||
|
||||
@@ -157,9 +161,9 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
final String message;
|
||||
if (newLobbyPlayer == null) {
|
||||
message = p.getName() + " has restored control over themself";
|
||||
message = Localizer.getInstance().getMessage("lblLogPlayerHasRestoredControlThemself", p.getName());
|
||||
} else {
|
||||
message = p.getName() + "is controlled by" + newLobbyPlayer.getName();
|
||||
message = Localizer.getInstance().getMessage("lblLogPlayerControlledTargetPlayer", p.getName(), newLobbyPlayer.getName());
|
||||
}
|
||||
return new GameLogEntry(GameLogEntryType.PLAYER_CONROL, message);
|
||||
}
|
||||
@@ -172,17 +176,18 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventCardDamaged event) {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
String additionalLog = "";
|
||||
if (event.type == DamageType.Deathtouch) {
|
||||
additionalLog = " (Deathtouch)";
|
||||
additionalLog = localizer.getMessage("lblDeathtouch");
|
||||
}
|
||||
if (event.type == DamageType.M1M1Counters) {
|
||||
additionalLog = " (As -1/-1 Counters)";
|
||||
additionalLog = localizer.getMessage("lblAsM1M1Counters");
|
||||
}
|
||||
if (event.type == DamageType.LoyaltyLoss) {
|
||||
additionalLog = " (Removing " + Lang.nounWithAmount(event.amount, "loyalty counter") + ")";
|
||||
additionalLog = localizer.getMessage("lblRemovingNLoyaltyCounter", String.valueOf(event.amount));
|
||||
}
|
||||
String message = event.source.toString() + " deals " + event.amount + " damage" + additionalLog + " to " + event.card.toString() + ".";
|
||||
String message = localizer.getMessage("lblSourceDealsNDamageToDest", event.source.toString(), String.valueOf(event.amount), additionalLog, event.card.toString());
|
||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||
}
|
||||
|
||||
@@ -191,33 +196,36 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
*/
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventLandPlayed ev) {
|
||||
String message = ev.player.toString() + " played " + ev.land.toString();
|
||||
String message = Localizer.getInstance().getMessage("lblLogPlayerPlayedLand", ev.player.toString(), ev.land.toString());
|
||||
return new GameLogEntry(GameLogEntryType.LAND, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventTurnBegan event) {
|
||||
String message = "Turn " + event.turnNumber + " (" + event.turnOwner.toString() + ")";
|
||||
String message = Localizer.getInstance().getMessage("lblLogTurnNOwnerByPlayer", String.valueOf(event.turnNumber), event.turnOwner.toString());;
|
||||
return new GameLogEntry(GameLogEntryType.TURN, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventPlayerDamaged ev) {
|
||||
String extra = ev.infect ? " (as poison counters)" : "";
|
||||
String damageType = ev.combat ? "combat" : "non-combat";
|
||||
String message = ev.source.toString() + " deals " + ev.amount + " " + damageType + " damage to " + ev.target.toString() + extra + ".";
|
||||
String extra = ev.infect ? Localizer.getInstance().getMessage("lblLogAsPoisonCounters") : "";
|
||||
String damageType = ev.combat ? Localizer.getInstance().getMessage("lblCombat") : Localizer.getInstance().getMessage("lblNonCombat");
|
||||
String message = Localizer.getInstance().getMessage("lblLogSourceDealsNDamageOfTypeToDest", ev.source.toString(),
|
||||
String.valueOf(ev.amount), damageType, ev.target.toString(), extra);
|
||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventPlayerPoisoned ev) {
|
||||
String message = ev.receiver.toString() + " receives " + Lang.nounWithAmount(ev.amount, "posion counter") + " from " + ev.source.toString();
|
||||
String message = Localizer.getInstance().getMessage("lblLogPlayerReceivesNPosionCounterFrom",
|
||||
ev.receiver.toString(), String.valueOf(ev.amount), ev.source.toString());
|
||||
return new GameLogEntry(GameLogEntryType.DAMAGE, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(final GameEventAttackersDeclared ev) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
// Loop through Defenders
|
||||
// Append Defending Player/Planeswalker
|
||||
@@ -229,11 +237,10 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
continue;
|
||||
}
|
||||
if (sb.length() > 0) sb.append("\n");
|
||||
sb.append(ev.player).append(" assigned ").append(Lang.joinHomogenous(attackers));
|
||||
sb.append(" to attack ").append(k).append(".");
|
||||
sb.append(localizer.getMessage("lblLogPlayerAssignedAttackerToAttackTarget", ev.player, Lang.joinHomogenous(attackers), k));
|
||||
}
|
||||
if (sb.length() == 0) {
|
||||
sb.append(ev.player).append(" didn't attack this turn.");
|
||||
sb.append(localizer.getMessage("lblPlayerDidntAttackThisTurn").replace("%s", ev.player.toString()));
|
||||
}
|
||||
return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString());
|
||||
}
|
||||
@@ -265,13 +272,11 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
blockers = att.getValue();
|
||||
if (blockers.isEmpty()) {
|
||||
sb.append(controllerName).append(" didn't block ");
|
||||
sb.append(Localizer.getInstance().getMessage("lblLogPlayerDidntBlockAttacker", controllerName, att.getKey()));
|
||||
}
|
||||
else {
|
||||
sb.append(controllerName).append(" assigned ").append(Lang.joinHomogenous(blockers)).append(" to block ");
|
||||
sb.append(Localizer.getInstance().getMessage("lblLogPlayerAssignedBlockerToBlockAttacker", controllerName, Lang.joinHomogenous(blockers), att.getKey()));
|
||||
}
|
||||
|
||||
sb.append(att.getKey()).append(".");
|
||||
firstAttacker = false;
|
||||
}
|
||||
}
|
||||
@@ -281,7 +286,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
|
||||
@Override
|
||||
public GameLogEntry visit(GameEventMulligan ev) {
|
||||
String message = ev.player.toString() + " has mulliganed down to " + ev.player.getZone(ZoneType.Hand).size() + " cards.";
|
||||
String message = Localizer.getInstance().getMessage("lblPlayerHasMulliganedDownToNCards").replace("%d", String.valueOf(ev.player.getZone(ZoneType.Hand).size())).replace("%s", ev.player.toString());
|
||||
return new GameLogEntry(GameLogEntryType.MULLIGAN, message);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.game;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Predicate<GameObject> interface.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
*/
|
||||
public final class GameObjectPredicates {
|
||||
|
||||
public static final Predicate<GameObject> restriction(final String[] restrictions, final Player sourceController, final Card source, final SpellAbility spellAbility) {
|
||||
return new Predicate<GameObject>() {
|
||||
@Override
|
||||
public boolean apply(final GameObject c) {
|
||||
return (c != null) && c.isValid(restrictions, sourceController, source, spellAbility);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package forge.game;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.base.Function;
|
||||
import forge.StaticData;
|
||||
import forge.deck.CardPool;
|
||||
@@ -144,4 +148,19 @@ public enum GameType {
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static GameType smartValueOf(String name) {
|
||||
return Enums.getIfPresent(GameType.class, name).orNull();
|
||||
}
|
||||
|
||||
public static Set<GameType> listValueOf(final String values) {
|
||||
final Set<GameType> result = EnumSet.noneOf(GameType.class);
|
||||
for (final String s : values.split(",")) {
|
||||
GameType g = GameType.smartValueOf(s);
|
||||
if (g != null) {
|
||||
result.add(g);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ public enum GlobalRuleChange {
|
||||
alwaysWither ("All damage is dealt as though it's source had wither."),
|
||||
attackerChoosesBlockers ("The attacking player chooses how each creature blocks each turn."),
|
||||
manapoolsDontEmpty ("Mana pools don't empty as steps and phases end."),
|
||||
noCycling ("Players can't cycle cards."),
|
||||
noCreatureETBTriggers ("Creatures entering the battlefield don't cause abilities to trigger."),
|
||||
noCreatureDyingTriggers ("Creatures dying don't cause abilities to trigger."),
|
||||
noLegendRule ("The legend rule doesn't apply."),
|
||||
@@ -35,8 +34,7 @@ public enum GlobalRuleChange {
|
||||
onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."),
|
||||
onlyTwoBlockers ("No more than two creatures can block each combat."),
|
||||
toughnessAssignsDamage ("Each creature assigns combat damage equal to its toughness rather than its power."),
|
||||
blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll."),
|
||||
noLandBattlefield("Lands can't enter the battlefield.");
|
||||
blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll.");
|
||||
|
||||
private final String ruleText;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
@@ -291,12 +292,13 @@ public class Match {
|
||||
}
|
||||
}
|
||||
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
if (!rAICards.isEmpty() && !rules.getGameType().isCardPoolLimited()) {
|
||||
game.getAction().revealAnte("AI can't play these cards well", rAICards);
|
||||
game.getAction().revealAnte(localizer.getMessage("lblAICantPlayCards"), rAICards);
|
||||
}
|
||||
|
||||
if (!removedAnteCards.isEmpty()) {
|
||||
game.getAction().revealAnte("These ante cards were removed", removedAnteCards);
|
||||
game.getAction().revealAnte(localizer.getMessage("lblAnteCardsRemoved"), removedAnteCards);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -37,7 +37,7 @@ import com.google.common.collect.Maps;
|
||||
* <p>
|
||||
* StaticEffect class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -72,7 +72,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* setTimestamp TODO Write javadoc for this method.
|
||||
*
|
||||
*
|
||||
* @param t
|
||||
* a long
|
||||
*/
|
||||
@@ -82,7 +82,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* getTimestamp. TODO Write javadoc for this method.
|
||||
*
|
||||
*
|
||||
* @return a long
|
||||
*/
|
||||
public final long getTimestamp() {
|
||||
@@ -93,7 +93,7 @@ public class StaticEffect {
|
||||
* <p>
|
||||
* Getter for the field <code>source</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
public final Card getSource() {
|
||||
@@ -104,7 +104,7 @@ public class StaticEffect {
|
||||
* <p>
|
||||
* Getter for the field <code>affectedCards</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
public final CardCollectionView getAffectedCards() {
|
||||
@@ -115,7 +115,7 @@ public class StaticEffect {
|
||||
* <p>
|
||||
* Setter for the field <code>affectedCards</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* a {@link forge.CardList} object.
|
||||
*/
|
||||
@@ -125,7 +125,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Gets the affected players.
|
||||
*
|
||||
*
|
||||
* @return the affected players
|
||||
*/
|
||||
public final List<Player> getAffectedPlayers() {
|
||||
@@ -134,7 +134,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Sets the affected players.
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* the new affected players
|
||||
*/
|
||||
@@ -144,7 +144,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* setParams. TODO Write javadoc for this method.
|
||||
*
|
||||
*
|
||||
* @param params
|
||||
* a HashMap
|
||||
*/
|
||||
@@ -154,7 +154,7 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Gets the params.
|
||||
*
|
||||
*
|
||||
* @return the params
|
||||
*/
|
||||
public final Map<String, String> getParams() {
|
||||
@@ -171,13 +171,12 @@ public class StaticEffect {
|
||||
|
||||
/**
|
||||
* Undo everything that was changed by this effect.
|
||||
*
|
||||
*
|
||||
* @return a {@link CardCollectionView} of all affected cards.
|
||||
*/
|
||||
final CardCollectionView remove() {
|
||||
final CardCollectionView affectedCards = getAffectedCards();
|
||||
final List<Player> affectedPlayers = getAffectedPlayers();
|
||||
//final Map<String, String> params = getParams();
|
||||
|
||||
String changeColorWordsTo = null;
|
||||
|
||||
@@ -242,6 +241,13 @@ public class StaticEffect {
|
||||
p.setUnlimitedHandSize(false);
|
||||
p.setMaxHandSize(p.getStartingHandSize());
|
||||
p.removeChangedKeywords(getTimestamp());
|
||||
|
||||
p.removeMaxLandPlays(getTimestamp());
|
||||
p.removeMaxLandPlaysInfinite(getTimestamp());
|
||||
|
||||
p.removeControlVote(getTimestamp());
|
||||
p.removeAdditionalVote(getTimestamp());
|
||||
p.removeAdditionalOptionalVote(getTimestamp());
|
||||
}
|
||||
|
||||
// modify the affected card
|
||||
@@ -273,6 +279,10 @@ public class StaticEffect {
|
||||
affectedCard.removeChangedCardKeywords(getTimestamp());
|
||||
}
|
||||
|
||||
if (hasParam("CantHaveKeyword")) {
|
||||
affectedCard.removeCantHaveKeyword(getTimestamp());
|
||||
}
|
||||
|
||||
if (addHiddenKeywords != null) {
|
||||
for (final String k : addHiddenKeywords) {
|
||||
affectedCard.removeHiddenExtrinsicKeyword(k);
|
||||
|
||||
@@ -54,7 +54,8 @@ public final class AbilityFactory {
|
||||
"Execute", // DelayedTrigger
|
||||
"FallbackAbility", // Complex Unless costs which can be unpayable
|
||||
"ChooseSubAbility", // Can choose a player via ChoosePlayer
|
||||
"CantChooseSubAbility" // Can't choose a player via ChoosePlayer
|
||||
"CantChooseSubAbility", // Can't choose a player via ChoosePlayer
|
||||
"AnimateSubAbility" // For ChangeZone Effects to Animate before ETB
|
||||
);
|
||||
|
||||
public enum AbilityRecordType {
|
||||
@@ -382,21 +383,10 @@ public final class AbilityFactory {
|
||||
* @param mapParams
|
||||
*/
|
||||
private static final void initializeParams(final SpellAbility sa, Map<String, String> mapParams) {
|
||||
if (mapParams.containsKey("Flashback")) {
|
||||
sa.setFlashBackAbility(true);
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("NonBasicSpell")) {
|
||||
sa.setBasicSpell(false);
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("Dash")) {
|
||||
sa.setDash(true);
|
||||
}
|
||||
|
||||
if (mapParams.containsKey("Outlast")) {
|
||||
sa.setOutlast(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,7 +439,7 @@ public final class AbilityFactory {
|
||||
}
|
||||
|
||||
public static final Map<String, String> getMapParams(final String abString) {
|
||||
return FileSection.parseToMap(abString, "$", "|");
|
||||
return FileSection.parseToMap(abString, FileSection.DOLLAR_SIGN_KV_SEPARATOR);
|
||||
}
|
||||
|
||||
public static final void adjustChangeZoneTarget(final Map<String, String> params, final SpellAbility sa) {
|
||||
|
||||
@@ -260,6 +260,8 @@ public class AbilityUtils {
|
||||
list = sa.getRootAbility().getPaidList("SacrificedCards");
|
||||
} else if (defined.startsWith("Sacrificed")) {
|
||||
list = sa.getRootAbility().getPaidList("Sacrificed");
|
||||
} else if (defined.startsWith("Revealed")) {
|
||||
list = sa.getRootAbility().getPaidList("Revealed");
|
||||
} else if (defined.startsWith("DiscardedCards")) {
|
||||
list = sa.getRootAbility().getPaidList("DiscardedCards");
|
||||
} else if (defined.startsWith("Discarded")) {
|
||||
@@ -1279,6 +1281,8 @@ public class AbilityUtils {
|
||||
if (o instanceof Card) {
|
||||
final Card rem = (Card) o;
|
||||
sas.addAll(game.getCardState(rem).getSpellAbilities());
|
||||
} else if (o instanceof SpellAbility) {
|
||||
sas.add((SpellAbility) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1314,9 +1318,16 @@ public class AbilityUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
Player pl = sa.getActivatingPlayer();
|
||||
final Game game = pl.getGame();
|
||||
|
||||
if (sa.isTrigger() && sa.getParent() == null && sa.getPayCosts() != null) {
|
||||
// when trigger cost are paid before the effect does resolve, need to clean the trigger
|
||||
game.getTriggerHandler().resetActiveTriggers();
|
||||
}
|
||||
|
||||
// do blessing there before condition checks
|
||||
if (sa.isSpell() && sa.isBlessing() && !sa.getHostCard().isPermanent()) {
|
||||
Player pl = sa.getActivatingPlayer();
|
||||
if (pl != null && pl.getZone(ZoneType.Battlefield).size() >= 10) {
|
||||
pl.setBlessing(true);
|
||||
}
|
||||
@@ -1331,7 +1342,7 @@ public class AbilityUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityUtils.resolveApiAbility(sa, sa.getActivatingPlayer().getGame());
|
||||
AbilityUtils.resolveApiAbility(sa, game);
|
||||
}
|
||||
|
||||
private static void resolveSubAbilities(final SpellAbility sa, final Game game) {
|
||||
@@ -1617,7 +1628,19 @@ public class AbilityUtils {
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Count$AttachedTo <DefinedCards related to spellability> <restriction>
|
||||
if (sq[0].startsWith("AttachedTo")) {
|
||||
final String[] k = l[0].split(" ");
|
||||
int sum = 0;
|
||||
for (Card card : AbilityUtils.getDefinedCards(sa.getHostCard(), k[1], sa)) {
|
||||
// Hateful Eidolon: the script uses LKI so that the attached cards have to be defined
|
||||
// This card needs the spellability ("Auras You control", you refers to the activating player)
|
||||
// CardFactoryUtils.xCount doesn't have the sa parameter, SVar:X:TriggeredCard$Valid <restriction> cannot handle this
|
||||
CardCollection list = CardLists.getValidCards(card.getAttachedCards(), k[2].split(","), sa.getActivatingPlayer(), c, sa);
|
||||
sum += list.size();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
// Count$Adamant.<Color>.<True>.<False>
|
||||
if (sq[0].startsWith("Adamant")) {
|
||||
final String payingMana = StringUtils.join(sa.getRootAbility().getPayingMana());
|
||||
@@ -1708,7 +1731,7 @@ public class AbilityUtils {
|
||||
if (res.checkTimingRestrictions(tgtCard, newSA)
|
||||
// still need to check the other restrictions like Aftermath
|
||||
&& res.checkOtherRestrictions(tgtCard, newSA, controller)
|
||||
&& newSA.checkOtherRestrictions()) {
|
||||
&& newSA.checkOtherRestrictions(tgtCard)) {
|
||||
sas.add(newSA);
|
||||
}
|
||||
}
|
||||
@@ -1836,6 +1859,11 @@ public class AbilityUtils {
|
||||
} else if (def.endsWith("Owner")) {
|
||||
players.add(c.getOwner());
|
||||
}
|
||||
} else if (o instanceof SpellAbility) {
|
||||
final SpellAbility c = (SpellAbility) o;
|
||||
if (def.endsWith("Controller")) {
|
||||
players.add(c.getHostCard().getController());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +337,23 @@ public abstract class SpellAbilityEffect {
|
||||
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||
addedTrigger.setIntrinsic(true);
|
||||
}
|
||||
|
||||
protected static void addForgetCounterTrigger(final Card card, final String counterType) {
|
||||
String trig = "Mode$ CounterRemoved | TriggerZones$ Command | ValidCard$ Card.IsRemembered | CounterType$ " + counterType + " | NewCounterAmount$ 0 | Static$ True";
|
||||
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||
addedTrigger.setIntrinsic(true);
|
||||
}
|
||||
|
||||
protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
@@ -8,6 +8,8 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -23,7 +25,7 @@ public class AbandonEffect extends SpellAbilityEffect {
|
||||
Player controller = source.getController();
|
||||
|
||||
boolean isOptional = sa.hasParam("Optional");
|
||||
if (isOptional && !controller.getController().confirmAction(sa, null, "Would you like to abandon the scheme " + source + "?")) {
|
||||
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeAbandonSource", CardTranslation.getTranslatedName(source.getName())))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -51,7 +52,7 @@ public class ActivateAbilityEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
SpellAbility manaAb = p.getController().chooseSingleSpellForEffect(
|
||||
possibleAb, sa, "Choose a mana ability:", ImmutableMap.of());
|
||||
possibleAb, sa, Localizer.getInstance().getMessage("lblChooseManaAbility"), ImmutableMap.of());
|
||||
p.getController().playChosenSpellAbility(manaAb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -61,7 +62,7 @@ public class AddTurnEffect extends SpellAbilityEffect {
|
||||
extra.setCantSetSchemesInMotion(true);
|
||||
}
|
||||
if (sa.hasParam("ShowMessage")) {
|
||||
p.getGame().getAction().nofityOfValue(sa, p, p + " takes an extra turn.", null);
|
||||
p.getGame().getAction().nofityOfValue(sa, p, Localizer.getInstance().getMessage("lblPlayerTakesExtraTurn", p.toString()), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.token.TokenInfo;
|
||||
import forge.game.event.GameEventTokenCreated;
|
||||
@@ -16,6 +17,7 @@ import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class AmassEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -44,6 +46,17 @@ public class AmassEffect extends SpellAbilityEffect {
|
||||
final int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("Num", "1"), sa);
|
||||
final boolean remember = sa.hasParam("RememberAmass");
|
||||
|
||||
boolean useZoneTable = true;
|
||||
CardZoneTable triggerList = sa.getChangeZoneTable();
|
||||
if (triggerList == null) {
|
||||
triggerList = new CardZoneTable();
|
||||
useZoneTable = false;
|
||||
}
|
||||
if (sa.hasParam("ChangeZoneTable")) {
|
||||
sa.setChangeZoneTable(triggerList);
|
||||
useZoneTable = true;
|
||||
}
|
||||
|
||||
// create army token if needed
|
||||
if (CardLists.count(activator.getCardsIn(ZoneType.Battlefield), CardPredicates.isType("Army")) == 0) {
|
||||
final String tokenScript = "b_0_0_zombie_army";
|
||||
@@ -54,14 +67,21 @@ public class AmassEffect extends SpellAbilityEffect {
|
||||
|
||||
// Should this be catching the Card that's returned?
|
||||
Card c = game.getAction().moveToPlay(tok, sa);
|
||||
if (c.getZone() != null) {
|
||||
triggerList.put(ZoneType.None, c.getZone().getZoneType(), c);
|
||||
}
|
||||
c.updateStateForView();
|
||||
}
|
||||
|
||||
if (!useZoneTable) {
|
||||
triggerList.triggerChangesZoneAll(game);
|
||||
triggerList.clear();
|
||||
}
|
||||
game.fireEvent(new GameEventTokenCreated());
|
||||
}
|
||||
|
||||
CardCollectionView tgtCards = CardLists.getType(activator.getCardsIn(ZoneType.Battlefield), "Army");
|
||||
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, "Choose an army to put counters on", 1, 1, false);
|
||||
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), 1, 1, false);
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
for(final Card tgtCard : tgtCards) {
|
||||
|
||||
@@ -20,7 +20,7 @@ package forge.game.ability.effects;
|
||||
import forge.card.CardType;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import java.util.List;
|
||||
|
||||
@@ -101,6 +101,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
||||
c.addChangedCardKeywords(keywords, removeKeywords,
|
||||
sa.hasParam("RemoveAllAbilities"), sa.hasParam("RemoveIntrinsicAbilities"), timestamp);
|
||||
|
||||
if (sa.hasParam("CantHaveKeyword")) {
|
||||
c.addCantHaveKeyword(timestamp, Keyword.setValueOf(sa.getParam("CantHaveKeyword")));
|
||||
}
|
||||
|
||||
for (final String k : hiddenKeywords) {
|
||||
c.addHiddenExtrinsicKeyword(k);
|
||||
}
|
||||
@@ -138,6 +142,8 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
||||
|
||||
c.removeChangedCardTraits(timestamp);
|
||||
|
||||
c.removeCantHaveKeyword(timestamp);
|
||||
|
||||
for (final String k : hiddenKeywords) {
|
||||
c.removeHiddenExtrinsicKeyword(k);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class AssignGroupEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -49,7 +50,7 @@ public class AssignGroupEffect extends SpellAbilityEffect {
|
||||
Multimap<SpellAbility, GameObject> result = ArrayListMultimap.create();
|
||||
|
||||
for (GameObject g : defined) {
|
||||
final String title = "Choose ability for " + g.toString();
|
||||
final String title = Localizer.getInstance().getMessage("lblChooseAbilityForObject", g.toString());
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Affected", g);
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -44,6 +46,14 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
} else {
|
||||
attachTo = targets.get(0);
|
||||
}
|
||||
|
||||
String attachToName = null;
|
||||
if (attachTo instanceof Card) {
|
||||
attachToName = CardTranslation.getTranslatedName(((Card)attachTo).getName());
|
||||
}
|
||||
else {
|
||||
attachToName = attachTo.toString();
|
||||
}
|
||||
|
||||
final Player p = sa.getActivatingPlayer();
|
||||
|
||||
@@ -52,7 +62,9 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ChooseAnObject")) {
|
||||
Card c = p.getController().chooseSingleEntityForEffect(attachments, sa, sa.getParam("ChooseAnObject"));
|
||||
attachments.clear();
|
||||
attachments.add(c);
|
||||
if (c != null) {
|
||||
attachments.add(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attachments = new CardCollection(source);
|
||||
@@ -60,7 +72,7 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
|
||||
// If Cast Targets will be checked on the Stack
|
||||
for (final Card attachment : attachments) {
|
||||
String message = "Do you want to attach " + attachment + " to " + attachTo + "?";
|
||||
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
|
||||
if ( sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message) )
|
||||
continue;
|
||||
handleAttachment(attachment, attachTo, sa);
|
||||
@@ -173,7 +185,7 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
players.add(player);
|
||||
}
|
||||
}
|
||||
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura, source + " - Select a player to attach to.");
|
||||
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())));
|
||||
if (pa != null) {
|
||||
handleAura(source, pa);
|
||||
return true;
|
||||
@@ -186,7 +198,7 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Card o = p.getController().chooseSingleEntityForEffect(list, aura, source + " - Select a card to attach to.");
|
||||
final Card o = p.getController().chooseSingleEntityForEffect(list, aura, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())));
|
||||
if (o != null) {
|
||||
handleAura(source, o);
|
||||
//source.enchantEntity((Card) o);
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class BidLifeEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
@@ -27,7 +28,7 @@ public class BidLifeEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("StartBidding")) {
|
||||
String start = sa.getParam("StartBidding");
|
||||
if ("Any".equals(start)) {
|
||||
startBidding = activator.getController().announceRequirements(sa, "Choose a starting bid", true);
|
||||
startBidding = activator.getController().announceRequirements(sa, Localizer.getInstance().getMessage("lblChooseStartingBid"), true);
|
||||
} else {
|
||||
startBidding = AbilityUtils.calculateAmount(host, start, sa);
|
||||
}
|
||||
@@ -54,12 +55,12 @@ public class BidLifeEffect extends SpellAbilityEffect {
|
||||
willBid = false;
|
||||
for (final Player p : bidPlayers) {
|
||||
final boolean result = p.getController().confirmBidAction(sa, PlayerActionConfirmMode.BidLife,
|
||||
"Do you want to top bid? Current Bid =" + bid, bid, winner);
|
||||
Localizer.getInstance().getMessage("lblDoYouWantTopBid") + bid, bid, winner);
|
||||
willBid |= result;
|
||||
if (result) { // a different choose number
|
||||
bid += p.getController().chooseNumber(sa, "Bid life:", 1, 9);
|
||||
bid += p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblBidLife") + ":", 1, 9);
|
||||
winner = p;
|
||||
host.getGame().getAction().nofityOfValue(sa, p, "topped bid with " + bid + " life", p);
|
||||
host.getGame().getAction().nofityOfValue(sa, p, Localizer.getInstance().getMessage("lblTopBidWithValueLife", String.valueOf(bid)), p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,7 +31,7 @@ public class BondEffect extends SpellAbilityEffect {
|
||||
Card partner = cards.getFirst();
|
||||
// skip choice if only one card on list
|
||||
if (cards.size() > 1) {
|
||||
partner = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(cards, sa, "Select a card to pair with");
|
||||
partner = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(cards, sa, Localizer.getInstance().getMessage("lblSelectACardPair"));
|
||||
}
|
||||
|
||||
// pair choices together
|
||||
|
||||
@@ -13,6 +13,8 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -44,7 +46,7 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
||||
final GameEntity originalDefender = combat.getDefenderByAttacker(c);
|
||||
final FCollectionView<GameEntity> defs = combat.getDefenders();
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa,
|
||||
"Choose which defender to attack with " + c, false);
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false);
|
||||
if (originalDefender != null && !originalDefender.equals(defender)) {
|
||||
AttackingBand ab = combat.getBandOfAttacker(c);
|
||||
if (ab != null) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.game.zone.MagicStack;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -50,8 +51,8 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
||||
// Redirect rules read 'you MAY choose new targets' ... okay!
|
||||
// TODO: Don't even ask to change targets, if the SA and subs don't actually have targets
|
||||
boolean isOptional = sa.hasParam("Optional");
|
||||
if (isOptional && !chooser.getController().confirmAction(sa, null, "Do you want to change targets of " + tgtSA.getHostCard() + "?")) {
|
||||
continue;
|
||||
if (isOptional && !chooser.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantChangeAbilityTargets", tgtSA.getHostCard().toString()))) {
|
||||
continue;
|
||||
}
|
||||
if (changesOneTarget) {
|
||||
// 1. choose a target of target spell
|
||||
|
||||
@@ -14,6 +14,7 @@ import forge.game.card.Card;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class ChangeTextEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -33,7 +34,7 @@ public class ChangeTextEffect extends SpellAbilityEffect {
|
||||
final String[] changedColorWordsArray = sa.getParam("ChangeColorWord").split(" ");
|
||||
if (changedColorWordsArray[0].equals("Choose")) {
|
||||
originalColor = sa.getActivatingPlayer().getController().chooseColor(
|
||||
"Choose a color word to replace", sa, ColorSet.ALL_COLORS);
|
||||
Localizer.getInstance().getMessage("lblChooseColorReplace"), sa, ColorSet.ALL_COLORS);
|
||||
changedColorWordOriginal = TextUtil.capitalize(MagicColor.toLongString(originalColor));
|
||||
} else {
|
||||
changedColorWordOriginal = changedColorWordsArray[0];
|
||||
@@ -48,7 +49,7 @@ public class ChangeTextEffect extends SpellAbilityEffect {
|
||||
possibleNewColors = ColorSet.fromMask(originalColor).inverse();
|
||||
}
|
||||
final byte newColor = sa.getActivatingPlayer().getController().chooseColor(
|
||||
"Choose a new color word", sa, possibleNewColors);
|
||||
Localizer.getInstance().getMessage("lblChooseNewColor"), sa, possibleNewColors);
|
||||
changedColorWordNew = TextUtil.capitalize(MagicColor.toLongString(newColor));
|
||||
} else {
|
||||
changedColorWordNew = changedColorWordsArray[1];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityKey;
|
||||
@@ -14,6 +16,7 @@ import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -91,26 +94,16 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
sa.getActivatingPlayer().getController().reveal(handCards, ZoneType.Hand, handCards.get(0).getOwner());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cards = (CardCollection)AbilityUtils.filterListByType(cards, sa.getParam("ChangeType"), sa);
|
||||
|
||||
if (sa.hasParam("Optional")) {
|
||||
final String targets = Lang.joinHomogenous(cards);
|
||||
final String message;
|
||||
if (sa.hasParam("OptionQuestion")) {
|
||||
message = TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets);
|
||||
message = TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets);
|
||||
} else {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Move ");
|
||||
sb.append(targets);
|
||||
sb.append(" from ");
|
||||
sb.append(Lang.joinHomogenous(origin));
|
||||
sb.append(" to ");
|
||||
sb.append(destination);
|
||||
sb.append("?");
|
||||
|
||||
message = sb.toString();
|
||||
message = Localizer.getInstance().getMessage("lblMoveTargetFromOriginToDestination", targets, Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName());
|
||||
}
|
||||
|
||||
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, message)) {
|
||||
@@ -159,7 +152,19 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
|
||||
if (destination == ZoneType.Battlefield) {
|
||||
|
||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||
// need LKI before Animate does apply
|
||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
||||
|
||||
source.addRemembered(c);
|
||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
||||
source.removeRemembered(c);
|
||||
}
|
||||
|
||||
// Auras without Candidates stay in their current location
|
||||
if (c.isAura()) {
|
||||
final SpellAbility saAura = c.getFirstAttachSpell();
|
||||
@@ -174,9 +179,9 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
Card movedCard = null;
|
||||
if (sa.hasParam("GainControl")) {
|
||||
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
|
||||
movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa);
|
||||
movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa, moveParams);
|
||||
} else {
|
||||
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa);
|
||||
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa, moveParams);
|
||||
if (destination == ZoneType.Exile && !c.isToken()) {
|
||||
Card host = sa.getOriginalHost();
|
||||
if (host == null) {
|
||||
@@ -214,7 +219,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
if (destination == ZoneType.Battlefield) {
|
||||
movedCard.setTimestamp(ts);
|
||||
}
|
||||
|
||||
|
||||
if (!movedCard.getZone().equals(originZone)) {
|
||||
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ 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.Maps;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
@@ -32,6 +34,8 @@ import forge.util.MessageUtil;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -439,7 +443,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
hostCard.addRemembered(CardUtil.getLKICopy(tgtC));
|
||||
}
|
||||
|
||||
final String prompt = TextUtil.concatWithSpace("Do you want to move", tgtC.toString(), "from", origin.toString(), "to", TextUtil.addSuffix(destination.toString(),"?"));
|
||||
final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().getMessage("lblDoYouWantMoveTargetFromOriToDest", CardTranslation.getTranslatedName(tgtC.getName()), Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName()));
|
||||
if (optional && !player.getController().confirmAction(sa, null, prompt) )
|
||||
continue;
|
||||
|
||||
@@ -503,7 +507,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachedTo"), tgtC.getController(), tgtC);
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a card to attach to.");
|
||||
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", tgtC.toString()));
|
||||
tgtC.attachToEntity(attachedTo);
|
||||
} else { // When it should enter the battlefield attached to an illegal permanent it fails
|
||||
continue;
|
||||
@@ -513,7 +517,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AttachedToPlayer")) {
|
||||
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AttachedToPlayer"), sa);
|
||||
if (!list.isEmpty()) {
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, tgtC + " - Select a player to attach to.");
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", tgtC.toString()));
|
||||
tgtC.attachToEntity(attachedTo);
|
||||
}
|
||||
else { // When it should enter the battlefield attached to an illegal player it fails
|
||||
@@ -521,6 +525,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
|
||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||
// need LKI before Animate does apply
|
||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(tgtC));
|
||||
|
||||
hostCard.addRemembered(tgtC);
|
||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
||||
hostCard.removeRemembered(tgtC);
|
||||
}
|
||||
|
||||
// Auras without Candidates stay in their current
|
||||
// location
|
||||
if (tgtC.isAura()) {
|
||||
@@ -528,13 +543,16 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (saAura != null) {
|
||||
saAura.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||
tgtC.removeChangedState();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
movedCard = game.getAction().moveTo(
|
||||
tgtC.getController().getZone(destination), tgtC, sa);
|
||||
tgtC.getController().getZone(destination), tgtC, sa, moveParams);
|
||||
if (sa.hasParam("Unearth")) {
|
||||
movedCard.setUnearthed(true);
|
||||
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false, false,
|
||||
@@ -560,7 +578,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, "Declare a defender for " + movedCard );
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(movedCard.getName())));
|
||||
}
|
||||
if (defender != null) {
|
||||
combat.addAttacker(movedCard, defender);
|
||||
@@ -727,7 +745,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(sa.getParam("AlternativeMessage")).append(" ");
|
||||
sb.append(altFetchList.size()).append(" cards match your searching type in Alternate Zones.");
|
||||
sb.append(altFetchList.size()).append(" " + Localizer.getInstance().getMessage("lblCardMatchSearchingTypeInAlternateZones"));
|
||||
|
||||
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneFromAltSource, sb.toString())) {
|
||||
origin = alt;
|
||||
@@ -749,7 +767,14 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean optional = sa.hasParam("Optional");
|
||||
if (optional) {
|
||||
String message = MessageUtil.formatMessage(defined ? "Put that card from {player's} " + Lang.joinHomogenous(origin).toLowerCase() + " to " + destination.name().toLowerCase() : "Search {player's} " + Lang.joinHomogenous(origin).toLowerCase() + "?", decider, player);
|
||||
String prompt;
|
||||
if (defined) {
|
||||
prompt = Localizer.getInstance().getMessage("lblPutThatCardFromPlayerOriginToDestination", "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase(), destination.getTranslatedName().toLowerCase());
|
||||
}
|
||||
else {
|
||||
prompt = Localizer.getInstance().getMessage("lblSearchPlayerZoneConfirm", "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase());
|
||||
}
|
||||
String message = MessageUtil.formatMessage(prompt , decider, player);
|
||||
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message)) {
|
||||
return;
|
||||
}
|
||||
@@ -801,10 +826,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
final int fetchNum = Math.min(player.getCardsIn(ZoneType.Library).size(), 4);
|
||||
CardCollectionView shown = !decider.hasKeyword("LimitSearchLibrary") ? player.getCardsIn(ZoneType.Library) : player.getCardsIn(ZoneType.Library, fetchNum);
|
||||
// Look at whole library before moving onto choosing a card
|
||||
delayedReveal = new DelayedReveal(shown, ZoneType.Library, PlayerView.get(player), source.getName() + " - Looking at cards in ");
|
||||
delayedReveal = new DelayedReveal(shown, ZoneType.Library, PlayerView.get(player), CardTranslation.getTranslatedName(source.getName()) + " - " + Localizer.getInstance().getMessage("lblLookingCardIn") + " ");
|
||||
}
|
||||
else if (origin.contains(ZoneType.Hand) && player.isOpponentOf(decider)) {
|
||||
delayedReveal = new DelayedReveal(player.getCardsIn(ZoneType.Hand), ZoneType.Hand, PlayerView.get(player), source.getName() + " - Looking at cards in ");
|
||||
delayedReveal = new DelayedReveal(player.getCardsIn(ZoneType.Hand), ZoneType.Hand, PlayerView.get(player), CardTranslation.getTranslatedName(source.getName()) + " - " + Localizer.getInstance().getMessage("lblLookingCardIn") + " ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,7 +846,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
SpellAbility tgtSA = decider.getController().getAbilityToPlay(tgtCard, sas);
|
||||
if (!decider.getController().confirmAction(tgtSA, null, "Do you want to play " + tgtCard + "?")) {
|
||||
if (!decider.getController().confirmAction(tgtSA, null, Localizer.getInstance().getMessage("lblDoYouWantPlayCard", CardTranslation.getTranslatedName(tgtCard.getName())))) {
|
||||
continue;
|
||||
}
|
||||
// if played, that card cannot be found
|
||||
@@ -853,7 +878,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
final boolean champion = sa.hasParam("Champion");
|
||||
final boolean forget = sa.hasParam("ForgetChanged");
|
||||
final boolean imprint = sa.hasParam("Imprint");
|
||||
String selectPrompt = sa.hasParam("SelectPrompt") ? sa.getParam("SelectPrompt") : MessageUtil.formatMessage("Select a card from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
||||
String selectPrompt = sa.hasParam("SelectPrompt") ? sa.getParam("SelectPrompt") : MessageUtil.formatMessage(Localizer.getInstance().getMessage("lblSelectCardFromPlayerZone", "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase()), decider, player);
|
||||
final String totalcmc = sa.getParam("WithTotalCMC");
|
||||
int totcmc = AbilityUtils.calculateAmount(source, totalcmc, sa);
|
||||
|
||||
@@ -866,9 +891,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (! sa.hasParam("SelectPrompt")) {
|
||||
// new default messaging for multi select
|
||||
if (fetchList.size() > changeNum) {
|
||||
selectPrompt = MessageUtil.formatMessage("Select up to " + changeNum + " cards from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
||||
//Select up to %changeNum cards from %players %origin
|
||||
selectPrompt = MessageUtil.formatMessage(Localizer.getInstance().getMessage("lblSelectUpToNumCardFromPlayerZone", String.valueOf(changeNum), "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase()), decider, player);
|
||||
} else {
|
||||
selectPrompt = MessageUtil.formatMessage("Select cards from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
||||
selectPrompt = MessageUtil.formatMessage(Localizer.getInstance().getMessage("lblSelectCardsFromPlayerZone", "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase()), decider, player);
|
||||
}
|
||||
}
|
||||
// ensure that selection is within maximum allowed changeNum
|
||||
@@ -930,7 +956,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
if (c == null) {
|
||||
final int num = Math.min(fetchList.size(), changeNum - i);
|
||||
String message = "Cancel Search? Up to " + num + " more card" + (num != 1 ? "s" : "") + " can be selected.";
|
||||
String message = Localizer.getInstance().getMessage("lblCancelSearchUpToSelectNumCards", String.valueOf(num));
|
||||
|
||||
if (fetchList.isEmpty() || decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message)) {
|
||||
break;
|
||||
@@ -973,6 +999,18 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("Tapped")) {
|
||||
c.setTapped(true);
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
|
||||
|
||||
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
|
||||
// need LKI before Animate does apply
|
||||
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
|
||||
|
||||
source.addRemembered(c);
|
||||
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
|
||||
source.removeRemembered(c);
|
||||
}
|
||||
|
||||
if (sa.hasParam("GainControl")) {
|
||||
Player newController = sa.getActivatingPlayer();
|
||||
if (sa.hasParam("NewController")) {
|
||||
@@ -1001,7 +1039,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (!list.isEmpty()) {
|
||||
Card attachedTo = null;
|
||||
if (list.size() > 1) {
|
||||
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, c + " - Select a card to attach to.");
|
||||
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())));
|
||||
}
|
||||
else {
|
||||
attachedTo = list.get(0);
|
||||
@@ -1019,7 +1057,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AttachedToPlayer")) {
|
||||
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(source, sa.getParam("AttachedToPlayer"), sa);
|
||||
if (!list.isEmpty()) {
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, c + " - Select a player to attach to.");
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())));
|
||||
c.attachToEntity(attachedTo);
|
||||
}
|
||||
else { // When it should enter the battlefield attached to an illegal permanent it fails
|
||||
@@ -1042,7 +1080,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, "Declare a defender for " + c );
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
|
||||
}
|
||||
if (defender != null) {
|
||||
combat.addAttacker(c, defender);
|
||||
@@ -1099,7 +1137,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
c.addFaceupCommand(unanimate);
|
||||
}
|
||||
}
|
||||
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa);
|
||||
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa, moveParams);
|
||||
if (sa.hasParam("Tapped")) {
|
||||
movedCard.setTapped(true);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -82,7 +83,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
for (final String type : CardType.getBasicTypes()) {
|
||||
final CardCollectionView cl = CardLists.getType(land, type);
|
||||
if (!cl.isEmpty()) {
|
||||
final String prompt = "Choose " + Lang.nounWithAmount(1, type);
|
||||
final String prompt = Localizer.getInstance().getMessage("lblChoose") + " " + Lang.nounWithAmount(1, type);
|
||||
Card c = p.getController().chooseSingleEntityForEffect(cl, sa, prompt, false);
|
||||
if (c != null) {
|
||||
chosen.add(c);
|
||||
@@ -98,10 +99,10 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
int chosenP = 0;
|
||||
while (!creature.isEmpty()) {
|
||||
Card c = p.getController().chooseSingleEntityForEffect(creature, sa,
|
||||
"Select creature(s) with total power less than or equal to " + (totP - chosenP - negativeNum)
|
||||
+ "\r\n(Selected:" + chosenPool + ")\r\n" + "(Total Power: " + chosenP + ")", chosenP <= totP);
|
||||
Localizer.getInstance().getMessage("lblSelectCreatureWithTotalPowerLessOrEqualTo", (totP - chosenP - negativeNum))
|
||||
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP);
|
||||
if (c == null) {
|
||||
if (p.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, "Cancel Choose?")) {
|
||||
if (p.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -118,7 +119,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AtRandom") && !choices.isEmpty()) {
|
||||
Aggregates.random(choices, validAmount, chosen);
|
||||
} else {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
|
||||
chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, !sa.hasParam("Mandatory")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.ComparableOp;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -51,7 +52,9 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
|
||||
String valid = "Card";
|
||||
String validDesc = "card";
|
||||
String validDesc = null;
|
||||
String message = null;
|
||||
|
||||
if (sa.hasParam("ValidCards")) {
|
||||
valid = sa.getParam("ValidCards");
|
||||
validDesc = sa.getParam("ValidDesc");
|
||||
@@ -59,6 +62,17 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
|
||||
boolean randomChoice = sa.hasParam("AtRandom");
|
||||
boolean chooseFromDefined = sa.hasParam("ChooseFromDefinedCards");
|
||||
|
||||
if (!randomChoice) {
|
||||
if (sa.hasParam("SelectPrompt")) {
|
||||
message = sa.getParam("SelectPrompt");
|
||||
} else if (null == validDesc) {
|
||||
message = Localizer.getInstance().getMessage("lblChooseACardName");
|
||||
} else {
|
||||
message = Localizer.getInstance().getMessage("lblChooseASpecificCard", validDesc);
|
||||
}
|
||||
}
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||
String chosen = "";
|
||||
@@ -99,11 +113,9 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
Collections.sort(faces);
|
||||
chosen = p.getController().chooseCardName(sa, faces, "Choose a card name");
|
||||
chosen = p.getController().chooseCardName(sa, faces, message);
|
||||
} else {
|
||||
// use CardFace because you might name a alternate name
|
||||
final String message = validDesc.equals("card") ? "Name a card" : "Name a " + validDesc + " card.";
|
||||
|
||||
// use CardFace because you might name a alternate names
|
||||
Predicate<ICardFace> cpp = Predicates.alwaysTrue();
|
||||
if (sa.hasParam("ValidCards")) {
|
||||
cpp = CardFacePredicates.valid(valid);
|
||||
@@ -114,9 +126,12 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
|
||||
host.setNamedCard(chosen);
|
||||
if(!randomChoice) {
|
||||
p.getGame().getAction().nofityOfValue(sa, host, p.getName() + " picked " + chosen, p);
|
||||
p.getGame().getAction().nofityOfValue(sa, host, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p);
|
||||
p.setNamedCard(chosen);
|
||||
}
|
||||
if (sa.hasParam("NoteFor")) {
|
||||
p.addNoteForName(sa.getParam("NoteFor"), "Name:" + chosen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -49,27 +50,28 @@ public class ChooseColorEffect extends SpellAbilityEffect {
|
||||
List<String> chosenColors;
|
||||
int cntMin = sa.hasParam("TwoColors") ? 2 : 1;
|
||||
int cntMax = sa.hasParam("TwoColors") ? 2 : sa.hasParam("OrColors") ? colorChoices.size() : 1;
|
||||
String prompt;
|
||||
String prompt = null;
|
||||
if (cntMax == 1) {
|
||||
prompt = "Choose a color";
|
||||
prompt = Localizer.getInstance().getMessage("lblChooseAColor");
|
||||
}
|
||||
else {
|
||||
prompt = "Choose " + Lang.getNumeral(cntMin);
|
||||
if (cntMax > cntMin) {
|
||||
if (cntMax >= MagicColor.NUMBER_OR_COLORS) {
|
||||
prompt += " or more";
|
||||
} else {
|
||||
prompt += " to " + Lang.getNumeral(cntMax);
|
||||
}
|
||||
if (cntMax >= MagicColor.NUMBER_OR_COLORS) {
|
||||
prompt = Localizer.getInstance().getMessage("lblAtLastChooseNumColors", Lang.getNumeral(cntMin));
|
||||
} else {
|
||||
prompt = Localizer.getInstance().getMessage("lblChooseSpecifiedRangeColors", Lang.getNumeral(cntMin), Lang.getNumeral(cntMax));
|
||||
}
|
||||
}
|
||||
else {
|
||||
prompt = Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(cntMax));
|
||||
}
|
||||
prompt += " colors";
|
||||
}
|
||||
chosenColors = p.getController().chooseColors(prompt, sa, cntMin, cntMax, colorChoices);
|
||||
if (chosenColors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
card.setChosenColors(chosenColors);
|
||||
p.getGame().getAction().nofityOfValue(sa, card, p.getName() + " picked " + Lang.joinHomogenous(chosenColors), p);
|
||||
p.getGame().getAction().nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), Lang.joinHomogenous(chosenColors)), p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class ChooseDirectionEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
@@ -18,11 +19,11 @@ public class ChooseDirectionEffect extends SpellAbilityEffect {
|
||||
final Game game = source.getGame();
|
||||
final FCollection<Player> left = new FCollection<>(game.getPlayers());
|
||||
// TODO: We'd better set up turn order UI here
|
||||
final String info = "Left (clockwise): " + left + "\r\nRight (anticlockwise):" + Lists.reverse(left);
|
||||
final String info = Localizer.getInstance().getMessage("lblLeftClockwise") + ": " + left + "\r\n" + Localizer.getInstance().getMessage("lblRightAntiClockwise") + ":" + Lists.reverse(left);
|
||||
sa.getActivatingPlayer().getController().notifyOfValue(sa, source, info);
|
||||
|
||||
boolean chosen = sa.getActivatingPlayer().getController().chooseBinary(sa,
|
||||
"Choose a direction", BinaryChoiceType.LeftOrRight);
|
||||
Localizer.getInstance().getMessage("lblChooseDirection"), BinaryChoiceType.LeftOrRight);
|
||||
source.setChosenDirection(chosen ? Direction.Left : Direction.Right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.event.GameEventCardModeChosen;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -69,7 +70,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
int idxChosen = MyRandom.getRandom().nextInt(abilities.size());
|
||||
chosenSA = abilities.get(idxChosen);
|
||||
} else {
|
||||
chosenSA = p.getController().chooseSingleSpellForEffect(abilities, sa, "Choose one",
|
||||
chosenSA = p.getController().chooseSingleSpellForEffect(abilities, sa, Localizer.getInstance().getMessage("lblChooseOne"),
|
||||
ImmutableMap.of());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -57,7 +58,7 @@ public class ChooseNumberEffect extends SpellAbilityEffect {
|
||||
chosen = MyRandom.getRandom().nextInt(max - min) + min;
|
||||
p.getGame().getAction().nofityOfValue(sa, p, Integer.toString(chosen), null);
|
||||
} else {
|
||||
String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : "Choose a number";
|
||||
String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : Localizer.getInstance().getMessage("lblChooseNumber");
|
||||
if (anyNumber) {
|
||||
Integer value = p.getController().announceRequirements(sa, title, true);
|
||||
chosen = (value == null ? 0 : value);
|
||||
@@ -72,7 +73,7 @@ public class ChooseNumberEffect extends SpellAbilityEffect {
|
||||
card.setChosenNumber(chosen);
|
||||
}
|
||||
if (sa.hasParam("Notify")) {
|
||||
p.getGame().getAction().nofityOfValue(sa, card, p.getName() + " picked " + chosen, p);
|
||||
p.getGame().getAction().nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +86,7 @@ public class ChooseNumberEffect extends SpellAbilityEffect {
|
||||
for (Entry<Player, Integer> ev : chooseMap.entrySet()) {
|
||||
int num = ev.getValue();
|
||||
Player player = ev.getKey();
|
||||
sb.append(player).append(" chose ").append(num);
|
||||
sb.append(Localizer.getInstance().getMessage("lblPlayerChoseNum", player.getName(), String.valueOf(num)));
|
||||
sb.append("\r\n");
|
||||
if (num > highest) {
|
||||
highestNum.clear();
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -37,7 +38,7 @@ public class ChoosePlayerEffect extends SpellAbilityEffect {
|
||||
final FCollectionView<Player> choices = sa.hasParam("Choices") ? AbilityUtils.getDefinedPlayers(
|
||||
sa.getHostCard(), sa.getParam("Choices"), sa) : sa.getActivatingPlayer().getGame().getPlayersInTurnOrder();
|
||||
|
||||
final String choiceDesc = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a player";
|
||||
final String choiceDesc = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoosePlayer");
|
||||
final boolean random = sa.hasParam("Random");
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -134,7 +135,7 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
|
||||
final CardCollection chosen = new CardCollection();
|
||||
if (tgt == null || p.canBeTargetedBy(sa)) {
|
||||
for (int i = 0; i < validAmount; i++) {
|
||||
final String choiceTitle = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a source ";
|
||||
final String choiceTitle = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseSource") + " ";
|
||||
Card o = null;
|
||||
do {
|
||||
o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle);
|
||||
|
||||
@@ -12,6 +12,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -74,7 +75,7 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
*/
|
||||
final Card source = sa.getHostCard();
|
||||
final Player player = source.getController();
|
||||
final Player opponent = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(player.getOpponents(), sa, "Choose a opponent") ;
|
||||
final Player opponent = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(player.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseOpponent")) ;
|
||||
final ZoneType lib = ZoneType.Library;
|
||||
|
||||
if (sa.hasParam("RememberClasher")) {
|
||||
@@ -110,11 +111,11 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
|
||||
// TODO: Split cards will return two CMC values, so both players may become winners of clash
|
||||
|
||||
reveal.append(player).append(" reveals: ").append(pCard.getName()).append(". CMC = ").append(pCMC);
|
||||
reveal.append(player).append(" " + Localizer.getInstance().getMessage("lblReveals") + ": ").append(pCard.getName()).append(". " + Localizer.getInstance().getMessage("lblCMC") + "= ").append(pCMC);
|
||||
reveal.append("\r\n");
|
||||
reveal.append(opponent).append(" reveals: ").append(oCard.getName()).append(". CMC = ").append(oCMC);
|
||||
reveal.append(opponent).append(" " + Localizer.getInstance().getMessage("lblReveals") + ": ").append(oCard.getName()).append(". " + Localizer.getInstance().getMessage("lblCMC") + "= ").append(oCMC);
|
||||
reveal.append("\r\n\r\n");
|
||||
reveal.append(player).append(pCMC > oCMC ? " wins clash." : " loses clash.");
|
||||
reveal.append(player).append(pCMC > oCMC ? " " + Localizer.getInstance().getMessage("lblWinsClash") + "." : " " + Localizer.getInstance().getMessage("lblLosesClash") + ".");
|
||||
|
||||
player.getGame().getAction().nofityOfValue(sa, source, reveal.toString(), null);
|
||||
clashMoveToTopOrBottom(player, pCard, sa);
|
||||
|
||||
@@ -9,6 +9,8 @@ import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -78,7 +80,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
|
||||
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
} else if (sa.hasParam("Defined")) {
|
||||
List<Card> cloneSources = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||
@@ -93,7 +95,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
final boolean optional = sa.hasParam("Optional");
|
||||
if (optional && !host.getController().getController().confirmAction(sa, null, "Do you want to copy " + cardToCopy + "?")) {
|
||||
if (optional && !host.getController().getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantCopy", CardTranslation.getTranslatedName(cardToCopy.getName())))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,9 +36,9 @@ public class ControlExchangeVariantEffect extends SpellAbilityEffect {
|
||||
CardCollectionView list2 = AbilityUtils.filterListByType(player2.getCardsIn(zone), type, sa);
|
||||
int max = Math.min(list1.size(), list2.size());
|
||||
// choose the same number of cards
|
||||
CardCollectionView chosen1 = activator.getController().chooseCardsForEffect(list1, sa, "Choose cards: " + player1, 0, max, true);
|
||||
CardCollectionView chosen1 = activator.getController().chooseCardsForEffect(list1, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player1, 0, max, true);
|
||||
int num = chosen1.size();
|
||||
CardCollectionView chosen2 = activator.getController().chooseCardsForEffect(list2, sa, "Choose cards: " + player2, num, num, true);
|
||||
CardCollectionView chosen2 = activator.getController().chooseCardsForEffect(list2, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player2, num, num, true);
|
||||
// check all cards can be controlled by the other player
|
||||
for (final Card c : chosen1) {
|
||||
if (!c.canBeControlledBy(player2)) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.util.List;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -18,15 +17,14 @@ import forge.game.combat.Combat;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.Ability;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
public class ControlGainEffect extends SpellAbilityEffect {
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.abilityfactory.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
@@ -65,15 +63,17 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
if (null == c || c.hasKeyword("Other players can't gain control of CARDNAME.")) {
|
||||
return;
|
||||
}
|
||||
final Game game = host.getGame();
|
||||
if (c.isInPlay()) {
|
||||
c.removeTempController(tStamp);
|
||||
|
||||
game.getAction().controllerChangeZoneCorrection(c);
|
||||
|
||||
if (tapOnLose) {
|
||||
c.tap();
|
||||
}
|
||||
} // if
|
||||
host.removeGainControlTargets(c);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,11 +82,9 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean bUntap = sa.hasParam("Untap");
|
||||
final boolean bTapOnLose = sa.hasParam("TapOnLose");
|
||||
final boolean bNoRegen = sa.hasParam("NoRegen");
|
||||
final boolean remember = sa.hasParam("RememberControlled");
|
||||
final boolean forget = sa.hasParam("ForgetControlled");
|
||||
final boolean attacking = sa.hasParam("Attacking");
|
||||
final List<String> destroyOn = sa.hasParam("DestroyTgt") ? Arrays.asList(sa.getParam("DestroyTgt").split(",")) : null;
|
||||
final List<String> keywords = sa.hasParam("AddKWs") ? Arrays.asList(sa.getParam("AddKWs").split(" & ")) : null;
|
||||
final List<String> lose = sa.hasParam("LoseControl") ? Arrays.asList(sa.getParam("LoseControl").split(",")) : null;
|
||||
|
||||
@@ -187,18 +185,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyOn != null) {
|
||||
if (destroyOn.contains("LeavesPlay")) {
|
||||
sa.getHostCard().addLeavesPlayCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
if (destroyOn.contains("Untap")) {
|
||||
sa.getHostCard().addUntapCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
if (destroyOn.contains("LoseControl")) {
|
||||
sa.getHostCard().addChangeControllerCommand(getDestroyCommand(tgtC, source, bNoRegen));
|
||||
}
|
||||
}
|
||||
|
||||
if (keywords != null) {
|
||||
// Add keywords only until end of turn
|
||||
final GameCommand untilKeywordEOT = new GameCommand() {
|
||||
@@ -227,7 +213,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
final FCollectionView<GameEntity> e = combat.getDefenders();
|
||||
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
|
||||
"Declare a defender for " + tgtC);
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(tgtC.getName())));
|
||||
|
||||
if (defender != null) {
|
||||
combat.addAttacker(tgtC, defender);
|
||||
@@ -239,43 +225,6 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
} // end foreach target
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDestroyCommand.
|
||||
* </p>
|
||||
*
|
||||
* @param i
|
||||
* a int.
|
||||
* @return a {@link forge.GameCommand} object.
|
||||
*/
|
||||
private static GameCommand getDestroyCommand(final Card c, final Card hostCard, final boolean bNoRegen) {
|
||||
final GameCommand destroy = new GameCommand() {
|
||||
private static final long serialVersionUID = 878543373519872418L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Game game = hostCard.getGame();
|
||||
final Ability ability = new Ability(hostCard, ManaCost.ZERO) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
game.getAction().destroy(c, null, !bNoRegen, null);
|
||||
}
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(hostCard).append(" - destroy ").append(c.getName()).append(".");
|
||||
if (bNoRegen) {
|
||||
sb.append(" It can't be regenerated.");
|
||||
}
|
||||
ability.setStackDescription(sb.toString());
|
||||
ability.setTrigger(true);
|
||||
|
||||
game.getStack().addSimultaneousStackEntry(ability);
|
||||
}
|
||||
|
||||
};
|
||||
return destroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getLoseControlCommand.
|
||||
|
||||
@@ -27,6 +27,8 @@ import forge.util.Aggregates;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.PredicateString.StringOp;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -64,7 +66,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
final long timestamp = game.getNextTimestamp();
|
||||
|
||||
if (sa.hasParam("Optional")) {
|
||||
if (!activator.getController().confirmAction(sa, null, "Copy this permanent?")) {
|
||||
if (!activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblCopyPermanentConfirm"))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -145,7 +147,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||
if (!choices.isEmpty()) {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
||||
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
|
||||
@@ -206,12 +208,12 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
GameEntity defender;
|
||||
if ("True".equals(attacked)) {
|
||||
FCollectionView<GameEntity> defs = game.getCombat().getDefenders();
|
||||
defender = c.getController().getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c, false);
|
||||
defender = c.getController().getController().chooseSingleEntityForEffect(defs, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false);
|
||||
} else {
|
||||
defender = AbilityUtils.getDefinedPlayers(host, sa.getParam("CopyAttacking"), sa).get(0);
|
||||
if (sa.hasParam("ChoosePlayerOrPlaneswalker") && defender != null) {
|
||||
FCollectionView<GameEntity> defs = game.getCombat().getDefendersControlledBy((Player) defender);
|
||||
defender = c.getController().getController().chooseSingleEntityForEffect(defs, sa, "Choose which defender to attack with " + c + " {defender: "+ defender + "}", false);
|
||||
defender = c.getController().getController().chooseSingleEntityForEffect(defs, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())) + " {" + Localizer.getInstance().getMessage("lblDefender") + ": " + defender + "}", false);
|
||||
}
|
||||
}
|
||||
game.getCombat().addAttacker(copyInPlay, defender);
|
||||
@@ -243,7 +245,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
list = CardLists.getValidCards(list, sa.getParam("AttachedTo"), copyInPlay.getController(), copyInPlay);
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
Card attachedTo = activator.getController().chooseSingleEntityForEffect(list, sa, copyInPlay + " - Select a card to attach to.");
|
||||
Card attachedTo = activator.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", copyInPlay.toString()));
|
||||
|
||||
copyInPlay.attachToEntity(attachedTo);
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,8 @@ import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -66,7 +68,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
boolean isOptional = sa.hasParam("Optional");
|
||||
if (isOptional && !controller.getController().confirmAction(sa, null, "Do you want to copy the spell " + card + "?")) {
|
||||
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoyouWantCopyTheSpell", CardTranslation.getTranslatedName(card.getName())))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,7 +86,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
final int spellCount = Integer.parseInt(sa.getParam("CopyMultipleSpells"));
|
||||
|
||||
for (int multi = 0; multi < spellCount && !tgtSpells.isEmpty(); multi++) {
|
||||
String prompt = "Select " + Lang.getOrdinal(multi + 1) + " spell to copy to stack";
|
||||
String prompt = Localizer.getInstance().getMessage("lblSelectMultiSpellCopyToStack", Lang.getOrdinal(multi + 1));
|
||||
SpellAbility chosen = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, prompt,
|
||||
ImmutableMap.of());
|
||||
SpellAbility copiedSpell = CardFactory.copySpellAbilityAndPossiblyHost(card, chosen.getHostCard(), chosen, true);
|
||||
@@ -96,7 +98,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
}
|
||||
else if (sa.hasParam("CopyForEachCanTarget")) {
|
||||
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
||||
"Select a spell to copy", ImmutableMap.of());
|
||||
Localizer.getInstance().getMessage("lblSelectASpellCopy"), ImmutableMap.of());
|
||||
chosenSA.setActivatingPlayer(controller);
|
||||
// Find subability or rootability that has targets
|
||||
SpellAbility targetedSA = chosenSA;
|
||||
@@ -144,7 +146,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
valid.remove(originalTarget);
|
||||
mayChooseNewTargets = false;
|
||||
if (sa.hasParam("ChooseOnlyOne")) {
|
||||
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, "Choose one");
|
||||
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"));
|
||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true);
|
||||
resetFirstTargetOnCopy(copy, choice, targetedSA);
|
||||
copies.add(copy);
|
||||
@@ -164,7 +166,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
}
|
||||
else {
|
||||
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
||||
"Select a spell to copy", ImmutableMap.of());
|
||||
Localizer.getInstance().getMessage("lblSelectASpellCopy"), ImmutableMap.of());
|
||||
chosenSA.setActivatingPlayer(controller);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(
|
||||
|
||||
@@ -12,6 +12,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.SpellPermanent;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -174,7 +175,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
|
||||
if (srcSA.hasParam("DestinationChoice")) {//Hinder
|
||||
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
|
||||
destination = srcSA.getActivatingPlayer().getController().chooseSomeType("a destination to remove", tgtSA, pos, null);
|
||||
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos, null);
|
||||
}
|
||||
if (tgtSA.isAbility()) {
|
||||
// For Ability-targeted counterspells - do not move it anywhere,
|
||||
|
||||
@@ -12,6 +12,8 @@ import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -109,10 +111,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
|
||||
// only select cards if the counterNum is any
|
||||
if (counterNum.equals("Any")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Choose cards to take ").append(cType.getName()).append(" counters from");
|
||||
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, sb.toString(), 0, srcCards.size(), true);
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, Localizer.getInstance().getMessage("lblChooseTakeCountersCard", cType.getName()), 0, srcCards.size(), true);
|
||||
}
|
||||
|
||||
for (Card src : srcCards) {
|
||||
@@ -134,10 +133,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", src);
|
||||
params.put("Target", dest);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Take how many ").append(cType.getName());
|
||||
sb.append(" counters from ").append(src).append("?");
|
||||
cnum = player.getController().chooseNumber(sa, sb.toString(), 0, cmax, params);
|
||||
cnum = player.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard", cType.getName(), CardTranslation.getTranslatedName(src.getName())), 0, cmax, params);
|
||||
} else {
|
||||
cnum = AbilityUtils.calculateAmount(host, counterNum, sa);
|
||||
}
|
||||
@@ -171,12 +167,8 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
tgtCards = CardLists.getValidCards(tgtCards, sa.getParam("ValidDefined"), player, host, sa);
|
||||
|
||||
if (counterNum.equals("Any")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Choose cards to get ").append(cType.getName());
|
||||
sb.append(" counters from ").append(source).append(".");
|
||||
|
||||
tgtCards = player.getController().chooseCardsForEffect(
|
||||
tgtCards, sa, sb.toString(), 0, tgtCards.size(), true);
|
||||
tgtCards = player.getController().chooseCardsForEffect(tgtCards, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseCardToGetCountersFrom", cType.getName(), CardTranslation.getTranslatedName(source.getName())), 0, tgtCards.size(), true);
|
||||
}
|
||||
|
||||
boolean updateSource = false;
|
||||
@@ -200,9 +192,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", source);
|
||||
params.put("Target", cur);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Put how many ").append(cType.getName()).append(" counters on ").append(cur).append("?");
|
||||
int cnum = player.getController().chooseNumber(sa, sb.toString(), 0, source.getCounters(cType), params);
|
||||
int cnum = player.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblPutHowManyTargetCounterOnCard", cType.getName(), CardTranslation.getTranslatedName(cur.getName())), 0, source.getCounters(cType), params);
|
||||
|
||||
if (cnum > 0) {
|
||||
source.subtractCounter(cType, cnum);
|
||||
@@ -260,10 +250,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", source);
|
||||
params.put("Target", cur);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Take how many ").append(cType.getName());
|
||||
sb.append(" counters from ").append(source).append("?");
|
||||
cntToMove = pc.chooseNumber(sa, sb.toString(), 0, cntToMove, params);
|
||||
cntToMove = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard", cType.getName(), CardTranslation.getTranslatedName(source.getName())), 0, cntToMove, params);
|
||||
}
|
||||
|
||||
if (source.getCounters(cType) >= cntToMove) {
|
||||
@@ -289,17 +276,15 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Source", source);
|
||||
params.put("Target", dest);
|
||||
String title = "Select type counters to remove";
|
||||
String title = Localizer.getInstance().getMessage("lblSelectRemoveCounterType");
|
||||
CounterType chosenType = pc.chooseCounterType(typeChoices, sa, title, params);
|
||||
|
||||
params = Maps.newHashMap();
|
||||
params.put("CounterType", chosenType);
|
||||
params.put("Source", source);
|
||||
params.put("Target", dest);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Take how many ").append(chosenType.getName()).append(" counters?");
|
||||
int chosenAmount = pc.chooseNumber(
|
||||
sa, sb.toString(), 0, Math.min(tgtCounters.get(chosenType), cntToMove), params);
|
||||
int chosenAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounters", chosenType.getName()),
|
||||
0, Math.min(tgtCounters.get(chosenType), cntToMove), params);
|
||||
|
||||
if (chosenAmount > 0) {
|
||||
dest.addCounter(chosenType, chosenAmount, player, true, table);
|
||||
|
||||
@@ -14,6 +14,7 @@ import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -42,7 +43,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
|
||||
list.addAll(CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.hasCounters()));
|
||||
|
||||
List<GameEntity> result = pc.chooseEntitiesForEffect(list, 0, list.size(), null, sa,
|
||||
"Choose any number of permanents and/or players for proliferate", p);
|
||||
Localizer.getInstance().getMessage("lblChooseProliferateTarget"), p);
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
for (final GameEntity ge : result) {
|
||||
|
||||
@@ -26,6 +26,8 @@ import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
@@ -33,61 +35,77 @@ import java.util.List;
|
||||
|
||||
public class CountersPutEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Card card = sa.getHostCard();
|
||||
final boolean dividedAsYouChoose = sa.hasParam("DividedAsYouChoose");
|
||||
protected String getStackDescription(SpellAbility spellAbility) {
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
final Card card = spellAbility.getHostCard();
|
||||
final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose");
|
||||
|
||||
|
||||
final int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("CounterNum", "1"), sa);
|
||||
if (sa.hasParam("Bolster")) {
|
||||
sb.append("Bolster ").append(amount);
|
||||
return sb.toString();
|
||||
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
|
||||
if (spellAbility.hasParam("Bolster")) {
|
||||
stringBuilder.append("Bolster ").append(amount);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
if (dividedAsYouChoose) {
|
||||
sb.append("Distribute ");
|
||||
stringBuilder.append("Distribute ");
|
||||
} else {
|
||||
sb.append("Put ");
|
||||
stringBuilder.append("Put ");
|
||||
}
|
||||
if (sa.hasParam("UpTo")) {
|
||||
sb.append("up to ");
|
||||
if (spellAbility.hasParam("UpTo")) {
|
||||
stringBuilder.append("up to ");
|
||||
}
|
||||
|
||||
sb.append(amount).append(" ");
|
||||
stringBuilder.append(amount).append(" ");
|
||||
|
||||
String type = sa.getParam("CounterType");
|
||||
String type = spellAbility.getParam("CounterType");
|
||||
if (type.equals("ExistingCounter")) {
|
||||
sb.append("of an existing counter");
|
||||
stringBuilder.append("of an existing counter");
|
||||
} else {
|
||||
|
||||
sb.append( CounterType.valueOf(type).getName()).append(" counter");
|
||||
stringBuilder.append(CounterType.valueOf(type).getName()).append(" counter");
|
||||
}
|
||||
|
||||
if (amount != 1) {
|
||||
sb.append("s");
|
||||
stringBuilder.append("s");
|
||||
}
|
||||
|
||||
if (dividedAsYouChoose) {
|
||||
sb.append(" among ");
|
||||
stringBuilder.append(" among ");
|
||||
} else {
|
||||
sb.append(" on ");
|
||||
stringBuilder.append(" on ");
|
||||
}
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
|
||||
final Iterator<Card> it = tgtCards.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Card tgtC = it.next();
|
||||
if (tgtC.isFaceDown()) {
|
||||
sb.append("Morph");
|
||||
} else {
|
||||
sb.append(tgtC);
|
||||
// if use targeting we show all targets and corresponding counters
|
||||
if(spellAbility.usesTargeting()) {
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
for(int i = 0; i < targetCards.size(); i++) {
|
||||
Card targetCard = targetCards.get(i);
|
||||
stringBuilder.append(targetCard).append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" counter)");
|
||||
|
||||
if(i == targetCards.size() - 2) {
|
||||
stringBuilder.append(" and ");
|
||||
}
|
||||
else if(i + 1 < targetCards.size()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final List<Card> targetCards = SpellAbilityEffect.getTargetCards(spellAbility);
|
||||
final Iterator<Card> it = targetCards.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Card targetCard = it.next();
|
||||
if (targetCard.isFaceDown()) {
|
||||
stringBuilder.append("Morph");
|
||||
} else {
|
||||
stringBuilder.append(targetCard);
|
||||
}
|
||||
|
||||
if (it.hasNext()) {
|
||||
sb.append(", ");
|
||||
if (it.hasNext()) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(".");
|
||||
stringBuilder.append(".");
|
||||
|
||||
return sb.toString();
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +147,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("Bolster")) {
|
||||
CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense));
|
||||
tgtCards.addAll(pc.chooseCardsForEffect(leastToughness, sa, "Choose a creature with the least toughness", 1, 1, false));
|
||||
tgtCards.addAll(pc.chooseCardsForEffect(leastToughness, sa, Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false));
|
||||
tgtObjects.addAll(tgtCards);
|
||||
} else {
|
||||
tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined"));
|
||||
@@ -154,7 +172,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
if (existingCounter) {
|
||||
final List<CounterType> choices = Lists.newArrayList();
|
||||
if (obj instanceof GameEntity) {
|
||||
GameEntity entity = (GameEntity)obj;
|
||||
GameEntity entity = (GameEntity) obj;
|
||||
// get types of counters
|
||||
for (CounterType ct : entity.getCounters().keySet()) {
|
||||
if (entity.canReceiveCounters(ct)) {
|
||||
@@ -164,7 +182,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (eachExistingCounter) {
|
||||
for(CounterType ct : choices) {
|
||||
for (CounterType ct : choices) {
|
||||
if (obj instanceof Player) {
|
||||
((Player) obj).addCounter(ct, counterAmount, placer, true, table);
|
||||
}
|
||||
@@ -183,13 +201,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", obj);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Select counter type to add to ");
|
||||
sb.append(Localizer.getInstance().getMessage("lblSelectCounterTypeAddTo") + " ");
|
||||
sb.append(obj);
|
||||
counterType = pc.chooseCounterType(choices, sa, sb.toString(), params);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj instanceof Card) {
|
||||
boolean counterAdded = false;
|
||||
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(gameCard) : counterAmount;
|
||||
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
|
||||
if (max != -1) {
|
||||
@@ -199,7 +218,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", obj);
|
||||
params.put("CounterType", counterType);
|
||||
counterAmount = pc.chooseNumber(sa, "How many counters?", 0, counterAmount, params);
|
||||
counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params);
|
||||
}
|
||||
|
||||
// Adapt need extra logic
|
||||
@@ -235,8 +254,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
|
||||
String message = "Do you want to put " + counterAmount + " +1/+1 counters on " + gameCard + " ?";
|
||||
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, "Choose an opponent");
|
||||
String message = Localizer.getInstance().getMessage("lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount), CardTranslation.getTranslatedName(gameCard.getName()));
|
||||
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseAnOpponent"));
|
||||
|
||||
if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) {
|
||||
gameCard.setTributed(true);
|
||||
@@ -244,15 +263,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (rememberCards) {
|
||||
card.addRemembered(gameCard);
|
||||
}
|
||||
final Zone zone = gameCard.getGame().getZoneOf(gameCard);
|
||||
if (zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack)) {
|
||||
if (etbcounter) {
|
||||
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
||||
} else {
|
||||
gameCard.addCounter(counterType, counterAmount, placer, true, table);
|
||||
if (gameCard.addCounter(counterType, counterAmount, placer, true, table) > 0) {
|
||||
counterAdded = true;
|
||||
}
|
||||
}
|
||||
if (remember) {
|
||||
final int value = gameCard.getTotalCountersToAdd();
|
||||
@@ -283,9 +301,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
if (etbcounter) {
|
||||
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
||||
} else {
|
||||
gameCard.addCounter(counterType, counterAmount, placer, false, table);
|
||||
if (gameCard.addCounter(counterType, counterAmount, placer, false, table) > 0) {
|
||||
counterAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rememberCards && counterAdded) {
|
||||
card.addRemembered(gameCard);
|
||||
}
|
||||
game.updateLastStateForCard(gameCard);
|
||||
}
|
||||
} else if (obj instanceof Player) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -97,11 +98,11 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
list = Lists.newArrayList(ctype);
|
||||
}
|
||||
|
||||
String prompt = "Select type of counters to add or remove";
|
||||
String prompt = Localizer.getInstance().getMessage("lblSelectCounterTypeToAddOrRemove");
|
||||
CounterType chosenType = pc.chooseCounterType(list, sa, prompt, params);
|
||||
|
||||
params.put("CounterType", chosenType);
|
||||
prompt = "What to do with that '" + chosenType.getName() + "' counter ";
|
||||
prompt = Localizer.getInstance().getMessage("lblWhatToDoWithTargetCounter", chosenType.getName()) + " ";
|
||||
Boolean putCounter = pc.chooseBinary(sa, prompt, BinaryChoiceType.AddOrRemove, params);
|
||||
|
||||
if (putCounter) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -84,8 +85,8 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("Optional")) {
|
||||
String ctrs = cntToRemove > 1 ? "counters" : num.equals("All") ? "all counters" : "a counter";
|
||||
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, "Remove " + ctrs + "?")) {
|
||||
String ctrs = cntToRemove > 1 ? Localizer.getInstance().getMessage("lblCounters") : num.equals("All") ? Localizer.getInstance().getMessage("lblAllCounters") : Localizer.getInstance().getMessage("lblACounters");
|
||||
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblRemove") + " " + ctrs + "?")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -129,10 +130,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
srcCards = game.getCardsIn(ZoneType.Battlefield);
|
||||
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), player, card, sa);
|
||||
if (num.equals("Any")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Choose cards to take ").append(counterType.getName()).append(" counters from");
|
||||
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, sb.toString(), 0, srcCards.size(), true);
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, Localizer.getInstance().getMessage("lblChooseCardsToTakeTargetCounters", counterType.getName()), 0, srcCards.size(), true);
|
||||
}
|
||||
} else {
|
||||
srcCards = getTargetCards(sa);
|
||||
@@ -172,7 +170,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", gameCard);
|
||||
params.put("CounterType", type);
|
||||
String title = "Select the number of " + type + " counters to remove";
|
||||
String title = Localizer.getInstance().getMessage("lblSelectRemoveCountersNumberOfTarget", type);
|
||||
cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params);
|
||||
}
|
||||
|
||||
@@ -213,10 +211,10 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", entity);
|
||||
|
||||
String prompt = "Select type of counters to remove";
|
||||
String prompt = Localizer.getInstance().getMessage("lblSelectCountersTypeToRemove");
|
||||
CounterType chosenType = pc.chooseCounterType(
|
||||
ImmutableList.copyOf(tgtCounters.keySet()), sa, prompt, params);
|
||||
prompt = "Select the number of " + chosenType.getName() + " counters to remove";
|
||||
prompt = Localizer.getInstance().getMessage("lblSelectRemoveCountersNumberOfTarget", chosenType.getName());
|
||||
int max = Math.min(cntToRemove, tgtCounters.get(chosenType));
|
||||
params = Maps.newHashMap();
|
||||
params.put("Target", entity);
|
||||
|
||||
@@ -122,7 +122,7 @@ public class DamageAllEffect extends DamageBaseEffect {
|
||||
|
||||
if (!usedDamageMap) {
|
||||
preventMap.triggerPreventDamage(false);
|
||||
damageMap.triggerDamageDoneOnce(false, sa);
|
||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
||||
|
||||
preventMap.clear();
|
||||
damageMap.clear();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user