mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Compare commits
1287 Commits
forge-1.5.
...
forge-1.5.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c08718b4b | ||
|
|
2f033f795f | ||
|
|
acd2ca2f23 | ||
|
|
de2731c21f | ||
|
|
5e0a8a6331 | ||
|
|
b34ca7b951 | ||
|
|
1422748447 | ||
|
|
186f7f70e4 | ||
|
|
4b1eb98e5b | ||
|
|
f7e185be3b | ||
|
|
ea6da0b7dc | ||
|
|
ae23652c0c | ||
|
|
54b86b4db2 | ||
|
|
f45779d2c3 | ||
|
|
480ebbb121 | ||
|
|
427e8c7059 | ||
|
|
86cf1b63f4 | ||
|
|
27ee03494b | ||
|
|
2f5799f453 | ||
|
|
7c419d875d | ||
|
|
d7d87b5c5d | ||
|
|
a11e7bbcb8 | ||
|
|
028e4bc196 | ||
|
|
2252b9908b | ||
|
|
02353704ca | ||
|
|
2bde0ac099 | ||
|
|
2543aabbd4 | ||
|
|
d01c22b5f9 | ||
|
|
08896bdb8e | ||
|
|
46d7491c78 | ||
|
|
9437ea5d90 | ||
|
|
184943da83 | ||
|
|
66e53ad1ed | ||
|
|
6595893e48 | ||
|
|
14ac5860f8 | ||
|
|
4554d94f32 | ||
|
|
737b2eb0f7 | ||
|
|
cb318eb8b9 | ||
|
|
557035e340 | ||
|
|
86bd570e2c | ||
|
|
d406114118 | ||
|
|
e4dd0faf5e | ||
|
|
83aa60a77c | ||
|
|
acfe00d6db | ||
|
|
d4a2ae07df | ||
|
|
926bdc1d5a | ||
|
|
ffee3af0ac | ||
|
|
42e6dffda2 | ||
|
|
c9db375d7c | ||
|
|
06de741370 | ||
|
|
a2287aff5b | ||
|
|
444aad2034 | ||
|
|
0de852043e | ||
|
|
9df1102514 | ||
|
|
8eaa535d6b | ||
|
|
b5e7de67a2 | ||
|
|
f4db8078b8 | ||
|
|
21db65e9ab | ||
|
|
1d0b60427d | ||
|
|
1760b10940 | ||
|
|
ce54805981 | ||
|
|
6df75248d8 | ||
|
|
21331d4189 | ||
|
|
c1e72c380b | ||
|
|
b38346e00c | ||
|
|
9e5cbbb9d1 | ||
|
|
f767ca34ce | ||
|
|
d4bc720dd0 | ||
|
|
1d1950d3cb | ||
|
|
038418ca0d | ||
|
|
4aa2dd4471 | ||
|
|
075a01e3ae | ||
|
|
35ea4c2a50 | ||
|
|
bc851d0b51 | ||
|
|
c11d353540 | ||
|
|
32e6da4a78 | ||
|
|
a96f9be77a | ||
|
|
04a38b79ac | ||
|
|
a1886d51f8 | ||
|
|
9cd2bc595d | ||
|
|
e592d3bc56 | ||
|
|
1da213b157 | ||
|
|
3f349d0eb3 | ||
|
|
84cac99a59 | ||
|
|
de1b198f4c | ||
|
|
5954e7053c | ||
|
|
1b4b9ad32b | ||
|
|
394e96d051 | ||
|
|
2c6151996e | ||
|
|
5c195f3cea | ||
|
|
1af6d101e8 | ||
|
|
b6b220496b | ||
|
|
ff92c4cf31 | ||
|
|
12ed04a7cc | ||
|
|
6575000c28 | ||
|
|
43f4e45827 | ||
|
|
4b6e58ddf0 | ||
|
|
0abdf7a3b5 | ||
|
|
53e0a2ad37 | ||
|
|
7e22527164 | ||
|
|
7e6928d700 | ||
|
|
4fa37ac4b9 | ||
|
|
bbe5e3c556 | ||
|
|
4c7f16baf4 | ||
|
|
cec3d26493 | ||
|
|
86edf9fd66 | ||
|
|
0adcfe55aa | ||
|
|
5a22e6a15d | ||
|
|
4d833b6f38 | ||
|
|
d05ecdf840 | ||
|
|
1f66ec432d | ||
|
|
2fcc5943fa | ||
|
|
08e909ca00 | ||
|
|
4c48e3e089 | ||
|
|
d9d32f7508 | ||
|
|
691c52fff9 | ||
|
|
c3d7a9f075 | ||
|
|
da3b2c1dc1 | ||
|
|
2ea69dde48 | ||
|
|
7c66069ee3 | ||
|
|
fd9ee6f4e1 | ||
|
|
6314550377 | ||
|
|
738fc7f52d | ||
|
|
4c129ffee4 | ||
|
|
c1750989b7 | ||
|
|
2b685348ab | ||
|
|
070fd7695a | ||
|
|
7cf1a52405 | ||
|
|
096f41172d | ||
|
|
59665029d2 | ||
|
|
3249e844f9 | ||
|
|
187c90c7df | ||
|
|
d779034da8 | ||
|
|
429d8dbf30 | ||
|
|
c5d35c9596 | ||
|
|
23265d2623 | ||
|
|
4810518d74 | ||
|
|
1e389fdef8 | ||
|
|
f31550732e | ||
|
|
29876aeb28 | ||
|
|
a90c34aac4 | ||
|
|
78456d6333 | ||
|
|
44edc80abc | ||
|
|
3f6e6f54a1 | ||
|
|
1e576cfde6 | ||
|
|
07c4ab24e7 | ||
|
|
915a6a42c2 | ||
|
|
5216b35688 | ||
|
|
3c53eec798 | ||
|
|
db080d8b10 | ||
|
|
04d129b8d8 | ||
|
|
059e7f7920 | ||
|
|
3bbfe87868 | ||
|
|
8cdc76427e | ||
|
|
11976bf964 | ||
|
|
a5f4f78018 | ||
|
|
0621f2e242 | ||
|
|
5538480862 | ||
|
|
07f126cb52 | ||
|
|
a5263b1375 | ||
|
|
6a52bdf10d | ||
|
|
ad38be34b7 | ||
|
|
a7785b0d1b | ||
|
|
26743d0ca0 | ||
|
|
fccc8f129f | ||
|
|
c1a11dd89a | ||
|
|
0730e2d774 | ||
|
|
a8bf257391 | ||
|
|
86f6a9e3f1 | ||
|
|
41f3b22237 | ||
|
|
7ad5bfb581 | ||
|
|
75e5c8d0ca | ||
|
|
1b9437ad32 | ||
|
|
d5bde7570a | ||
|
|
a0d13578f3 | ||
|
|
ed48547f36 | ||
|
|
73b97fe151 | ||
|
|
f21e1c3f8a | ||
|
|
aa230be806 | ||
|
|
26bb8e5009 | ||
|
|
5322d5a9c2 | ||
|
|
0f5eb7d5f8 | ||
|
|
9379f7283c | ||
|
|
29025cce61 | ||
|
|
7fd201d5e3 | ||
|
|
1f947d5781 | ||
|
|
8dea53bcda | ||
|
|
f5d43d0986 | ||
|
|
cf67fb456e | ||
|
|
308814bdca | ||
|
|
149a9bd7b2 | ||
|
|
e16998beb5 | ||
|
|
a9e12e38ef | ||
|
|
9c7d25f49d | ||
|
|
a52a1f5820 | ||
|
|
e0c3244667 | ||
|
|
32bf18781a | ||
|
|
5bd88d468e | ||
|
|
e758e07658 | ||
|
|
eb0ac0dde9 | ||
|
|
c81a4d50dd | ||
|
|
ae28b04615 | ||
|
|
471623cd10 | ||
|
|
4378bfdbea | ||
|
|
8ff6cbd98e | ||
|
|
f4ffe22d61 | ||
|
|
4573e30e73 | ||
|
|
832b40e235 | ||
|
|
aec0bad678 | ||
|
|
aeedde578d | ||
|
|
dfebb08e1e | ||
|
|
dbf77d8548 | ||
|
|
003c252d8a | ||
|
|
1986df1961 | ||
|
|
11c205f0c9 | ||
|
|
7d2cc8fac5 | ||
|
|
863e7733e6 | ||
|
|
e657a1b07d | ||
|
|
2f29cf7755 | ||
|
|
6d34dbc286 | ||
|
|
28f7f7baaf | ||
|
|
f3b78b1aa3 | ||
|
|
ce82a93afe | ||
|
|
fe9a35b706 | ||
|
|
febba22f45 | ||
|
|
2981668172 | ||
|
|
ed7369c98f | ||
|
|
b6409b11c7 | ||
|
|
98c51e45b4 | ||
|
|
9e6b82d10a | ||
|
|
ee49314502 | ||
|
|
29e73367a6 | ||
|
|
80aa655164 | ||
|
|
f8d7d060b9 | ||
|
|
b198e8a40e | ||
|
|
a3fb6de6b3 | ||
|
|
5252cca5c5 | ||
|
|
4cab5da076 | ||
|
|
2cdf7030f8 | ||
|
|
e30f8d0fb6 | ||
|
|
22fe17048d | ||
|
|
3d70e2e9f6 | ||
|
|
f35dc1f30d | ||
|
|
34b36a8ef2 | ||
|
|
7c105ca398 | ||
|
|
2ec4873552 | ||
|
|
aa4f3de30c | ||
|
|
e4fb939e91 | ||
|
|
68acaf77c5 | ||
|
|
308039739f | ||
|
|
7687637783 | ||
|
|
7b799eb531 | ||
|
|
9c37156906 | ||
|
|
32d6eb3ed5 | ||
|
|
faa2cef0bb | ||
|
|
7e999ea4b3 | ||
|
|
69c569c82b | ||
|
|
bcc5f2c526 | ||
|
|
35bff6a89b | ||
|
|
a26d45793e | ||
|
|
d6577d1d72 | ||
|
|
e8aeec7f87 | ||
|
|
0b75a0ee8e | ||
|
|
4163c56502 | ||
|
|
37d45ffbeb | ||
|
|
f03a1397f1 | ||
|
|
410b23974e | ||
|
|
3366843ca1 | ||
|
|
9bcc6cbc64 | ||
|
|
9636e3d2e4 | ||
|
|
140e44a3bd | ||
|
|
9f76a78b0a | ||
|
|
dd6e8d0884 | ||
|
|
66b1a795c1 | ||
|
|
8b8b9f0cf5 | ||
|
|
d5647c9e98 | ||
|
|
5d1d77f4df | ||
|
|
7a193c0618 | ||
|
|
1709ed07c7 | ||
|
|
23586a027e | ||
|
|
97c74ec2b1 | ||
|
|
961461aa19 | ||
|
|
713a9ecb3b | ||
|
|
8a46d38914 | ||
|
|
423033d2ac | ||
|
|
d01925107f | ||
|
|
47a719147c | ||
|
|
556bb2caa7 | ||
|
|
ec3813fb13 | ||
|
|
926152940e | ||
|
|
7cf186952e | ||
|
|
b2ccb5c689 | ||
|
|
3da6e688e4 | ||
|
|
567ffe84cd | ||
|
|
d97495ed41 | ||
|
|
ceab176d9f | ||
|
|
050887c53c | ||
|
|
2bc61a9e68 | ||
|
|
6b1d9356f3 | ||
|
|
1dbc34fe62 | ||
|
|
d93b041397 | ||
|
|
ecd14e1de4 | ||
|
|
923bddc90d | ||
|
|
97297f291f | ||
|
|
84850c76a9 | ||
|
|
ca8c8d35d9 | ||
|
|
18e96a1028 | ||
|
|
f27b3e469a | ||
|
|
7b47938062 | ||
|
|
d9f3bb8bdd | ||
|
|
8919532b84 | ||
|
|
481ac50726 | ||
|
|
731b3c8c36 | ||
|
|
8393bc02e4 | ||
|
|
85e5b0ff13 | ||
|
|
3b8d779b27 | ||
|
|
89dcef5b67 | ||
|
|
6ced3d38a4 | ||
|
|
7fc97562b2 | ||
|
|
80372f40b5 | ||
|
|
d9a5a27391 | ||
|
|
8c73aa93c8 | ||
|
|
59997f5cf2 | ||
|
|
3ab7db6061 | ||
|
|
3d2ba5a182 | ||
|
|
d7cb5165ef | ||
|
|
3a89700d95 | ||
|
|
f0ce722aa4 | ||
|
|
95709401a3 | ||
|
|
06f030ab73 | ||
|
|
bd0dc07c43 | ||
|
|
0d354f679c | ||
|
|
7ed37ec5f8 | ||
|
|
fa40deaa11 | ||
|
|
117d25b686 | ||
|
|
7491eb0076 | ||
|
|
1acb9f8ab1 | ||
|
|
f87b262194 | ||
|
|
6c82cabe3a | ||
|
|
7e7969988a | ||
|
|
4b51c24b66 | ||
|
|
5e0ad38ffc | ||
|
|
56557101f3 | ||
|
|
1d0cf99e48 | ||
|
|
2f5b534de6 | ||
|
|
00ae4d4824 | ||
|
|
af2a87a207 | ||
|
|
72fe5ad996 | ||
|
|
d00da28a3c | ||
|
|
81363bc537 | ||
|
|
1e98cd4939 | ||
|
|
814a8976fb | ||
|
|
86cea92ca1 | ||
|
|
f9178dcff2 | ||
|
|
fcc4b92efe | ||
|
|
44f79a5db2 | ||
|
|
034f235196 | ||
|
|
a1e16543b2 | ||
|
|
8578e2a538 | ||
|
|
092d3dac40 | ||
|
|
e9ddf8400b | ||
|
|
22b78255bb | ||
|
|
2985b48432 | ||
|
|
b249028b8e | ||
|
|
4cb2077e1a | ||
|
|
88b6223820 | ||
|
|
0b4ef6b1e3 | ||
|
|
8921ce16e6 | ||
|
|
f4606da30f | ||
|
|
41a5727a93 | ||
|
|
19cec963d2 | ||
|
|
156bc84c73 | ||
|
|
eb98eaf5af | ||
|
|
fb6d86af8a | ||
|
|
39aa6bfda1 | ||
|
|
ba59dfb01c | ||
|
|
dd044c30e6 | ||
|
|
f1fcc2c26b | ||
|
|
c0e9d82ff6 | ||
|
|
89598ab48c | ||
|
|
586de9321a | ||
|
|
ae7244a10f | ||
|
|
d95fc7352f | ||
|
|
e4b7c92c6a | ||
|
|
a52a88e0cb | ||
|
|
2ec3c08e72 | ||
|
|
b57e0a9848 | ||
|
|
eabcc78815 | ||
|
|
1b7a2730f0 | ||
|
|
caac7dc2ed | ||
|
|
d8eb3a5c7a | ||
|
|
5973d0d944 | ||
|
|
a512a7ab33 | ||
|
|
e44cada50e | ||
|
|
49e734b66d | ||
|
|
5c4efbcd04 | ||
|
|
f131aafa44 | ||
|
|
f4263e943d | ||
|
|
45cd72f7dc | ||
|
|
634289407b | ||
|
|
01ff93375a | ||
|
|
c64ef5deed | ||
|
|
a5a0c510b5 | ||
|
|
d7f8231717 | ||
|
|
f0f7cfc839 | ||
|
|
03870821c5 | ||
|
|
726084bc06 | ||
|
|
cbbc341472 | ||
|
|
664b707330 | ||
|
|
a47bba59d4 | ||
|
|
e84544afe4 | ||
|
|
cd505adc3d | ||
|
|
5b1dd964ae | ||
|
|
86ef4d0820 | ||
|
|
966e593617 | ||
|
|
eb4a355dc7 | ||
|
|
d65b645a64 | ||
|
|
f899c6e58c | ||
|
|
a554599fd7 | ||
|
|
6046860e15 | ||
|
|
1dda7d72a9 | ||
|
|
3e2f3ab1c5 | ||
|
|
286d7d043c | ||
|
|
563c089b9d | ||
|
|
ca103655bd | ||
|
|
666fb6b35c | ||
|
|
d234975cde | ||
|
|
13a6310ac0 | ||
|
|
62bfb66e11 | ||
|
|
b776929fe8 | ||
|
|
273b7286cf | ||
|
|
5bd450e0c9 | ||
|
|
777dff3c4b | ||
|
|
e3ddb00eee | ||
|
|
870ffe3437 | ||
|
|
b8ea837204 | ||
|
|
4a611fcf58 | ||
|
|
91be7b5a32 | ||
|
|
42cf0def0f | ||
|
|
10fd05478d | ||
|
|
9fbc7a17c3 | ||
|
|
3cd105bac0 | ||
|
|
559c96412a | ||
|
|
ee5187c7e7 | ||
|
|
0cb7573494 | ||
|
|
0f514be363 | ||
|
|
ca07a8103a | ||
|
|
4837bb5740 | ||
|
|
fc7cc33111 | ||
|
|
ce341302a3 | ||
|
|
4cb27c40ac | ||
|
|
84e3fc1be9 | ||
|
|
2522450930 | ||
|
|
e3e761ddc0 | ||
|
|
c95ee53137 | ||
|
|
7f9ef96178 | ||
|
|
95cbb2870c | ||
|
|
17891a62fa | ||
|
|
f1ff893962 | ||
|
|
ce6d092b16 | ||
|
|
f6577217ff | ||
|
|
283af44c48 | ||
|
|
1ea839f5d4 | ||
|
|
4a3e07bbae | ||
|
|
9ba8866b19 | ||
|
|
5dbe15e7e0 | ||
|
|
cbf38aed9c | ||
|
|
a8eb34d201 | ||
|
|
496b12913d | ||
|
|
be47a4bad3 | ||
|
|
9d28e88a24 | ||
|
|
d937a397a7 | ||
|
|
c9daed2056 | ||
|
|
804c36e8b7 | ||
|
|
7274d46865 | ||
|
|
feaec2eb6d | ||
|
|
753c0731a3 | ||
|
|
9a6b1822a9 | ||
|
|
db5e8316ec | ||
|
|
513b9db9b5 | ||
|
|
04248f0166 | ||
|
|
c60c041012 | ||
|
|
f10ec1751f | ||
|
|
561d4deb32 | ||
|
|
3e552ac655 | ||
|
|
12d8b289e5 | ||
|
|
bcf2abdbbc | ||
|
|
8747c163ee | ||
|
|
458bd8f964 | ||
|
|
19a34ba730 | ||
|
|
16294fe54a | ||
|
|
76cdfa9759 | ||
|
|
fc508b3a27 | ||
|
|
a4c2fa3797 | ||
|
|
d1575996b4 | ||
|
|
75293299ed | ||
|
|
e50f31fb5f | ||
|
|
e47cdebf14 | ||
|
|
85eaf866d9 | ||
|
|
800a9b6854 | ||
|
|
94eb94508c | ||
|
|
02a49149fa | ||
|
|
80308ab621 | ||
|
|
cb055269d3 | ||
|
|
1c6ccbb20a | ||
|
|
764310e08b | ||
|
|
673fd2dae3 | ||
|
|
5fe57fcb11 | ||
|
|
0a9617b0c4 | ||
|
|
6b06a6f274 | ||
|
|
e5c3d4595c | ||
|
|
ab0a18cc64 | ||
|
|
b534c4bbcd | ||
|
|
0ee95fae76 | ||
|
|
dcb8c54e4c | ||
|
|
802aed63df | ||
|
|
ba7cd330e6 | ||
|
|
adeec5d6a9 | ||
|
|
8b0e20aa9e | ||
|
|
a22a3dc849 | ||
|
|
f220aef52a | ||
|
|
0175cc1a26 | ||
|
|
59b6e092c9 | ||
|
|
ceb7b13a8e | ||
|
|
f68f8ba157 | ||
|
|
19a58c309e | ||
|
|
62479e1de6 | ||
|
|
04dde1c18d | ||
|
|
c7450034f5 | ||
|
|
9140dab9f1 | ||
|
|
8f4d3792c6 | ||
|
|
9d73236812 | ||
|
|
fbc0404f94 | ||
|
|
a2050ed3e6 | ||
|
|
0d2c13bf80 | ||
|
|
39061439dc | ||
|
|
d9e5d93265 | ||
|
|
b3bec906ef | ||
|
|
6d969373b9 | ||
|
|
c8ac6b9f2d | ||
|
|
45d7b43aa6 | ||
|
|
e954b40d3c | ||
|
|
07f94f9d42 | ||
|
|
18809bc918 | ||
|
|
85d32d49e2 | ||
|
|
64459b414e | ||
|
|
f0ffeefe24 | ||
|
|
df022d5df7 | ||
|
|
a2b75c2072 | ||
|
|
e7a9492200 | ||
|
|
3133dbf8b4 | ||
|
|
8fd2ffc93e | ||
|
|
39675d465d | ||
|
|
6e969ddaaa | ||
|
|
a9f4009e43 | ||
|
|
b28b5e1ec9 | ||
|
|
f4b9dd2f34 | ||
|
|
40c651cd04 | ||
|
|
b6d6da88e8 | ||
|
|
7f95e5f583 | ||
|
|
e179aa404c | ||
|
|
4235a6906e | ||
|
|
618cd6415d | ||
|
|
9d458ce8c5 | ||
|
|
311306e986 | ||
|
|
a1688a7404 | ||
|
|
da421bc5f8 | ||
|
|
db8a71cca2 | ||
|
|
c8352e4968 | ||
|
|
1d8aec2c3b | ||
|
|
459c2cfae4 | ||
|
|
32c52578e2 | ||
|
|
eccadbf6fc | ||
|
|
8d99d2032d | ||
|
|
121db288c1 | ||
|
|
b8b3fff684 | ||
|
|
33a08f67ec | ||
|
|
dffc1b2a42 | ||
|
|
55aa4350b9 | ||
|
|
40bfde20fa | ||
|
|
8ceda2b07b | ||
|
|
a592a44647 | ||
|
|
2a9b59f935 | ||
|
|
5ed0549dc4 | ||
|
|
158da7f60f | ||
|
|
58e529097f | ||
|
|
2de5beb6d1 | ||
|
|
cb5df407fd | ||
|
|
8fd0e873c7 | ||
|
|
fe9ff2f4f9 | ||
|
|
bf05d850fb | ||
|
|
266cbd5dde | ||
|
|
e222c0f7a9 | ||
|
|
12e80be6d1 | ||
|
|
5efb2c2e40 | ||
|
|
2e574caac6 | ||
|
|
74b7a40763 | ||
|
|
1ede72b64a | ||
|
|
c45fe42129 | ||
|
|
ddaffcf7a5 | ||
|
|
0d28620ee3 | ||
|
|
bbab149d3c | ||
|
|
d4e193f39a | ||
|
|
bd4dd064be | ||
|
|
2efb70e3b5 | ||
|
|
92668c3a2a | ||
|
|
3915c3ac2c | ||
|
|
51e521fbb3 | ||
|
|
a81a666b81 | ||
|
|
69a9f0fe43 | ||
|
|
9262ad4d82 | ||
|
|
b6270a71bb | ||
|
|
e31d2ae0d1 | ||
|
|
77510ac6e3 | ||
|
|
0f1e88bb25 | ||
|
|
c5a42f03e6 | ||
|
|
de1124c19e | ||
|
|
60ba4bfec6 | ||
|
|
6372242b0b | ||
|
|
9a8c4a1c8c | ||
|
|
891db98700 | ||
|
|
f7d1b73a6c | ||
|
|
7a906fdc03 | ||
|
|
e7046c9bf5 | ||
|
|
8d87b0256b | ||
|
|
802aa167a9 | ||
|
|
b83bd41716 | ||
|
|
7c8cb3053a | ||
|
|
7c9fb9f6b4 | ||
|
|
68989d3e31 | ||
|
|
51a0745348 | ||
|
|
3505a2bcd5 | ||
|
|
0581a5e42b | ||
|
|
7ea839334c | ||
|
|
be7cce4300 | ||
|
|
c78251a90e | ||
|
|
c3c8fd7186 | ||
|
|
3d97768f71 | ||
|
|
f2c93cb5dd | ||
|
|
689ef0cf8a | ||
|
|
77de4abfd1 | ||
|
|
dc16b75c94 | ||
|
|
3a0fdb4d4f | ||
|
|
32b0f8333d | ||
|
|
446dfd2257 | ||
|
|
faa9947974 | ||
|
|
c75971e8c5 | ||
|
|
2c850cbe53 | ||
|
|
a060f5f322 | ||
|
|
5367811a33 | ||
|
|
7d41ae1331 | ||
|
|
f55abfa576 | ||
|
|
79b8340b80 | ||
|
|
e0b7034f97 | ||
|
|
b79876c959 | ||
|
|
e159c25f6d | ||
|
|
d16eedd1b3 | ||
|
|
a062d61c8d | ||
|
|
471e5b5399 | ||
|
|
577f94a67f | ||
|
|
f7e05491da | ||
|
|
2771b4fe1f | ||
|
|
b5830fac6e | ||
|
|
d8511c92c0 | ||
|
|
1ea41edd6a | ||
|
|
4f79f92940 | ||
|
|
e13865a966 | ||
|
|
05e1161632 | ||
|
|
feae947742 | ||
|
|
9ef69149ee | ||
|
|
04ca668807 | ||
|
|
489af75669 | ||
|
|
c8e1aff861 | ||
|
|
373bcd0351 | ||
|
|
c08e984e35 | ||
|
|
5dee59e8e5 | ||
|
|
350dbeb12f | ||
|
|
fdc2eb66aa | ||
|
|
ef332e4568 | ||
|
|
93c7d28714 | ||
|
|
5d8225f041 | ||
|
|
b501c76f7c | ||
|
|
97526110d0 | ||
|
|
f8a4c5db92 | ||
|
|
2093d23e24 | ||
|
|
7e32b752a0 | ||
|
|
4b02708bb0 | ||
|
|
9684d69bd1 | ||
|
|
f6d07944d8 | ||
|
|
46796041c7 | ||
|
|
196a782f8c | ||
|
|
4839d05b70 | ||
|
|
d8b6140692 | ||
|
|
040e51f124 | ||
|
|
a24161db2b | ||
|
|
117501c511 | ||
|
|
1c122b99e9 | ||
|
|
6970c1e7cf | ||
|
|
d56cecdba4 | ||
|
|
8ebf514dab | ||
|
|
1376f0dde2 | ||
|
|
dbacf597da | ||
|
|
87eaf96fa1 | ||
|
|
13db59aac8 | ||
|
|
9a46768de6 | ||
|
|
3c5010cebf | ||
|
|
d25b197c09 | ||
|
|
b892cfa029 | ||
|
|
23a012364b | ||
|
|
ed0f3600f5 | ||
|
|
dd4e250432 | ||
|
|
2ffb71e053 | ||
|
|
be25cf699c | ||
|
|
1cc15fe50c | ||
|
|
a04e5c1da2 | ||
|
|
5b18ba9058 | ||
|
|
753f4d989b | ||
|
|
8960fa20c3 | ||
|
|
38344027ca | ||
|
|
90ff1abab8 | ||
|
|
7fe3799ea6 | ||
|
|
5ed6fba085 | ||
|
|
6c3573d854 | ||
|
|
d00aeffbda | ||
|
|
e247b8e368 | ||
|
|
3d54fd3b7b | ||
|
|
b19b5daf48 | ||
|
|
f48952aa94 | ||
|
|
e8d4433bb3 | ||
|
|
84b0e17572 | ||
|
|
e4ba6f56dd | ||
|
|
bdfd2e9adf | ||
|
|
849655cb2f | ||
|
|
1c391a7c7e | ||
|
|
cf1990ddf7 | ||
|
|
fad3ea497d | ||
|
|
f586525a61 | ||
|
|
832a290778 | ||
|
|
ecbba9f1c0 | ||
|
|
afeb4eb815 | ||
|
|
d0b5e78f7a | ||
|
|
1837234650 | ||
|
|
c65031ea31 | ||
|
|
14a7176325 | ||
|
|
a0c3a1eb05 | ||
|
|
f7af3865b7 | ||
|
|
51a0c08042 | ||
|
|
abc2118a77 | ||
|
|
776ac3ee2b | ||
|
|
63eaec8348 | ||
|
|
ddb8464c1c | ||
|
|
29ef7a7d3b | ||
|
|
49aa0cc459 | ||
|
|
cdf1b44cb0 | ||
|
|
61fe9f2282 | ||
|
|
0b9d254e12 | ||
|
|
77b241eb68 | ||
|
|
b98ae5844c | ||
|
|
507ce6f220 | ||
|
|
4d2a27f7e8 | ||
|
|
a90841a6b6 | ||
|
|
e9bb6e42f6 | ||
|
|
ed4b11d9ef | ||
|
|
c115f01edf | ||
|
|
8fd6bbef3b | ||
|
|
8fa68731d1 | ||
|
|
b6b586a865 | ||
|
|
457ba444f1 | ||
|
|
31a367c434 | ||
|
|
e83eb92948 | ||
|
|
5ee779846d | ||
|
|
7a3ecf8e38 | ||
|
|
9e2c96a18e | ||
|
|
6550fa28ca | ||
|
|
de9ce7ebb9 | ||
|
|
caae78b6ee | ||
|
|
495701c46c | ||
|
|
ed99ce1694 | ||
|
|
51a37e896d | ||
|
|
b431315e88 | ||
|
|
ea59f120da | ||
|
|
9bd30e8d5d | ||
|
|
39d1ef7e13 | ||
|
|
83a1b70efe | ||
|
|
01b4f803a8 | ||
|
|
5c31890066 | ||
|
|
be7f4e5192 | ||
|
|
4124c45875 | ||
|
|
06e785c75a | ||
|
|
2f384c079c | ||
|
|
e4adada542 | ||
|
|
efa86891cc | ||
|
|
bcc20bb774 | ||
|
|
937a095209 | ||
|
|
1661228168 | ||
|
|
b17b9eb4e1 | ||
|
|
2ffa1510ed | ||
|
|
d1ef107b48 | ||
|
|
aa48e9cacc | ||
|
|
3c0bfa7843 | ||
|
|
803b6331f4 | ||
|
|
fe8af8705d | ||
|
|
624365cda3 | ||
|
|
4fb59b6071 | ||
|
|
d15948e44b | ||
|
|
bf629ff518 | ||
|
|
ee9ee7f207 | ||
|
|
311d4091ff | ||
|
|
4e79f508c0 | ||
|
|
90828e6eab | ||
|
|
7d3afc35cc | ||
|
|
6f4c046732 | ||
|
|
503a8da70f | ||
|
|
1cf9a005d9 | ||
|
|
fe04d56568 | ||
|
|
93b6ac15dd | ||
|
|
500632515c | ||
|
|
598d2da9d6 | ||
|
|
05f7a9b30a | ||
|
|
0b6be04428 | ||
|
|
dcb9553807 | ||
|
|
c9c9be68b1 | ||
|
|
53e6671143 | ||
|
|
3d063d8188 | ||
|
|
8d3633fe63 | ||
|
|
ee7d64c533 | ||
|
|
5009ebe0ab | ||
|
|
9b2f3f3108 | ||
|
|
9ca9dc797e | ||
|
|
2ffc18cc89 | ||
|
|
cfeeda3c93 | ||
|
|
a2277bc2d4 | ||
|
|
694e4e7988 | ||
|
|
1c0e786489 | ||
|
|
7420e94876 | ||
|
|
73c921bb95 | ||
|
|
7a22f4f74a | ||
|
|
cdb76d344e | ||
|
|
6cbded92cb | ||
|
|
bc032a074c | ||
|
|
89a4afdc08 | ||
|
|
be6266d633 | ||
|
|
705264ba9a | ||
|
|
a0b181e6fe | ||
|
|
df50a0a38d | ||
|
|
abd182e17f | ||
|
|
312c15d824 | ||
|
|
3f0cdc9b19 | ||
|
|
5d5c42c843 | ||
|
|
1a8fe19c88 | ||
|
|
7e37495b49 | ||
|
|
3210f06931 | ||
|
|
a457ff539d | ||
|
|
afbcd90ffc | ||
|
|
8acdc6c458 | ||
|
|
4ed8f69da1 | ||
|
|
90fe657da6 | ||
|
|
91fe7b516c | ||
|
|
e3851e3f1d | ||
|
|
301014285f | ||
|
|
e467f44bf2 | ||
|
|
e8e5ac2ca7 | ||
|
|
ebb0a4b2bc | ||
|
|
d21e2666ec | ||
|
|
43140b2db3 | ||
|
|
c898e1321f | ||
|
|
4f04cbbcc8 | ||
|
|
f17e857710 | ||
|
|
2401b9b1ff | ||
|
|
e646ff1d1f | ||
|
|
9bb800ce43 | ||
|
|
6859ad2ad9 | ||
|
|
19152790b2 | ||
|
|
05751dbe1b | ||
|
|
f35f6b3edd | ||
|
|
8af7fadbf6 | ||
|
|
9190ce038f | ||
|
|
bda47748f2 | ||
|
|
96084d7b26 | ||
|
|
e96ff73b50 | ||
|
|
fcb8c4bdd6 | ||
|
|
c7c1d2d302 | ||
|
|
21ea8e377c | ||
|
|
e9409d01a6 | ||
|
|
a8c70a903f | ||
|
|
c66ca97dfc | ||
|
|
5b5517ad29 | ||
|
|
c8de886967 | ||
|
|
2ef19583a6 | ||
|
|
9e0ff5e635 | ||
|
|
427cef758f | ||
|
|
1cc1516feb | ||
|
|
daafa3f5c2 | ||
|
|
9e20af7603 | ||
|
|
306a160df0 | ||
|
|
b5686bcd2f | ||
|
|
33dd245131 | ||
|
|
25334c27f1 | ||
|
|
3f20ebb15b | ||
|
|
37849b88e2 | ||
|
|
800b3f8272 | ||
|
|
b2ecb3cb52 | ||
|
|
8a794f2e02 | ||
|
|
3c05e1674f | ||
|
|
a15b3c2a69 | ||
|
|
10c138d64e | ||
|
|
99d1718498 | ||
|
|
515985dead | ||
|
|
e30c4532b8 | ||
|
|
2ab5f19f79 | ||
|
|
f5054558c2 | ||
|
|
b726053164 | ||
|
|
cc433b6708 | ||
|
|
e95e251161 | ||
|
|
75edc751f3 | ||
|
|
b4ba84a0d1 | ||
|
|
38074a1660 | ||
|
|
ca5e20b1a9 | ||
|
|
c4db463338 | ||
|
|
34c2cb604f | ||
|
|
f058e38004 | ||
|
|
b38a8b5830 | ||
|
|
8e76829a27 | ||
|
|
f3bf092898 | ||
|
|
4dd2b87538 | ||
|
|
4d6930e903 | ||
|
|
3fecd16efd | ||
|
|
29a535c71a | ||
|
|
3fd72f35df | ||
|
|
bb3d637738 | ||
|
|
02fe620360 | ||
|
|
ebe0e350ff | ||
|
|
6dbe638a59 | ||
|
|
31b5f4181a | ||
|
|
72d0cfdbbc | ||
|
|
f934764290 | ||
|
|
e28f809a58 | ||
|
|
a88dd00a0d | ||
|
|
5d9de47fd5 | ||
|
|
7add28ad7d | ||
|
|
17b97aca28 | ||
|
|
86b422230e | ||
|
|
0193671453 | ||
|
|
c4be1b8697 | ||
|
|
52f8880627 | ||
|
|
f537e8cf78 | ||
|
|
482af12b67 | ||
|
|
2f85d50778 | ||
|
|
b51663c130 | ||
|
|
7ebc54352e | ||
|
|
6703e62823 | ||
|
|
e860e9e1af | ||
|
|
de3303abc9 | ||
|
|
a0695c08cc | ||
|
|
c5aeb0785e | ||
|
|
21b376848f | ||
|
|
f8fe7e41b4 | ||
|
|
22caa0a408 | ||
|
|
4d3945f5a4 | ||
|
|
0786012ed8 | ||
|
|
6e9a316460 | ||
|
|
fd7d0e1d99 | ||
|
|
65199c7a81 | ||
|
|
851094c523 | ||
|
|
9b31032016 | ||
|
|
a7fa173e52 | ||
|
|
de4d83372c | ||
|
|
c7f24490f3 | ||
|
|
fabc7a3a27 | ||
|
|
e830527f74 | ||
|
|
5fc5feb636 | ||
|
|
384af0001c | ||
|
|
2abc9b3f7c | ||
|
|
9242f3439c | ||
|
|
b1ba8f0aa2 | ||
|
|
20b3775280 | ||
|
|
c2bb321e17 | ||
|
|
b0160287ba | ||
|
|
284e2257f3 | ||
|
|
ec06d07f35 | ||
|
|
5eca8807d8 | ||
|
|
5b3ee63299 | ||
|
|
a6ffa3ae36 | ||
|
|
e021e66387 | ||
|
|
eb70985551 | ||
|
|
cd6dc5158e | ||
|
|
942f30557c | ||
|
|
1486d61e65 | ||
|
|
eb082d2f49 | ||
|
|
7e6e691771 | ||
|
|
8e4962ceb1 | ||
|
|
414d82bd37 | ||
|
|
4091f1d1d9 | ||
|
|
fe0f4ddf99 | ||
|
|
b0845c5795 | ||
|
|
aeddb37d9d | ||
|
|
1ff1c35486 | ||
|
|
6e585a7afa | ||
|
|
e0df16ee17 | ||
|
|
ae5cb7a17a | ||
|
|
8d79ed527f | ||
|
|
d4bc84c6bd | ||
|
|
9743a7d1b7 | ||
|
|
e5cffc623a | ||
|
|
9c0a559bce | ||
|
|
b794bc5ee0 | ||
|
|
55c71d77e1 | ||
|
|
6914e30443 | ||
|
|
1c52e8e38a | ||
|
|
c38f169d1d | ||
|
|
8f4b8dbf88 | ||
|
|
b68f28b621 | ||
|
|
a23eb2f869 | ||
|
|
8dfbf2b3b6 | ||
|
|
70b0fab66a | ||
|
|
082e2ff2cd | ||
|
|
12af50d473 | ||
|
|
b1cde02dd4 | ||
|
|
2d737ab092 | ||
|
|
8cd9220f36 | ||
|
|
838d4a98aa | ||
|
|
d32bfea94e | ||
|
|
181a7f5b9c | ||
|
|
ee00b544f7 | ||
|
|
5548c71bda | ||
|
|
ae7a6b79b7 | ||
|
|
96d2338bea | ||
|
|
21217e030b | ||
|
|
1d63f4b4a1 | ||
|
|
8843e5bfe7 | ||
|
|
48b97f8632 | ||
|
|
20184fb4c0 | ||
|
|
76000a90fb | ||
|
|
4aebd350ee | ||
|
|
13f4a0abb4 | ||
|
|
35ea297090 | ||
|
|
f11909d6da | ||
|
|
5df82652b6 | ||
|
|
8fde8ebe5f | ||
|
|
6bc20a8830 | ||
|
|
0bbd9c103a | ||
|
|
0b68e2ade6 | ||
|
|
1ca79539b6 | ||
|
|
1c2c3d6d51 | ||
|
|
bdb1243409 | ||
|
|
812d4ca519 | ||
|
|
4e7e14cbf3 | ||
|
|
2064b6517e | ||
|
|
c9f491a483 | ||
|
|
be2ec80f0c | ||
|
|
1c13e8dc10 | ||
|
|
4748aa2262 | ||
|
|
a3376b0502 | ||
|
|
c073d39cd6 | ||
|
|
b508f78f04 | ||
|
|
8424da8311 | ||
|
|
cfd9d55668 | ||
|
|
417e3bb104 | ||
|
|
320d4f62d9 | ||
|
|
6c712d86fb | ||
|
|
a0479f9610 | ||
|
|
374a744a44 | ||
|
|
81416d49de | ||
|
|
31ebd62c83 | ||
|
|
93dd4af6ed | ||
|
|
9f0308dd66 | ||
|
|
f9298703d4 | ||
|
|
3992032e8e | ||
|
|
58dacc0789 | ||
|
|
b3280923d9 | ||
|
|
e83da9ff9c | ||
|
|
8b1d0fc759 | ||
|
|
ff869933be | ||
|
|
fb0af4dc18 | ||
|
|
6f5551d78d | ||
|
|
a90efe4939 | ||
|
|
46c37b9d82 | ||
|
|
3e23eb517b | ||
|
|
9cf75ea844 | ||
|
|
5565b4a4d0 | ||
|
|
bde5967ef4 | ||
|
|
f3560238fe | ||
|
|
f97f489aba | ||
|
|
454b7715c0 | ||
|
|
ac1e279235 | ||
|
|
5950162663 | ||
|
|
6a52b46192 | ||
|
|
f5558eec7f | ||
|
|
0c39f596a3 | ||
|
|
ba3c544484 | ||
|
|
e0063727a9 | ||
|
|
7b68821a8c | ||
|
|
4a84b408d4 | ||
|
|
b9aec4ffb8 | ||
|
|
1a958e758f | ||
|
|
74527319aa | ||
|
|
a66e7ad14f | ||
|
|
e4b91416dd | ||
|
|
7735c705c0 | ||
|
|
31ab48d0d4 | ||
|
|
e5cf9da31d | ||
|
|
9eac64ab7b | ||
|
|
7131cebf7d | ||
|
|
bfb4a5ee57 | ||
|
|
21e250bf36 | ||
|
|
f5b1ac970b | ||
|
|
c85f14f27c | ||
|
|
c0cf6198a4 | ||
|
|
4d3012fee7 | ||
|
|
75726dc272 | ||
|
|
260724af80 | ||
|
|
2b37cf217f | ||
|
|
669ff579c5 | ||
|
|
d0e65fe1bc | ||
|
|
4485e46801 | ||
|
|
2265687c75 | ||
|
|
f70523bf14 | ||
|
|
48013b9339 | ||
|
|
e9e1caf9d1 | ||
|
|
5b17e2beda | ||
|
|
19bc74f88d | ||
|
|
ae0069652a | ||
|
|
5ec7f01b7a | ||
|
|
691f798f90 | ||
|
|
d5bef0861b | ||
|
|
87dd938cde | ||
|
|
c4e0d3e7ce | ||
|
|
c705722bdc | ||
|
|
6424fa151a | ||
|
|
0646e49359 | ||
|
|
6ddae9e9da | ||
|
|
efc7be1637 | ||
|
|
1de80be872 | ||
|
|
f8740f1268 | ||
|
|
d6f30c9220 | ||
|
|
f2d7004b02 | ||
|
|
6ecf800bab | ||
|
|
6f92a834da | ||
|
|
a7a85d22d5 | ||
|
|
178024e248 | ||
|
|
798e93af46 | ||
|
|
3cb125ae4a | ||
|
|
30aed49e87 | ||
|
|
d0173a2341 | ||
|
|
fb7a61ab30 | ||
|
|
13e09c73bc | ||
|
|
6fdaf4fda4 | ||
|
|
ba79089bed | ||
|
|
a8670467be | ||
|
|
4dd84cdb2e | ||
|
|
028d6c0a1f | ||
|
|
ca2584f598 | ||
|
|
17e96250f7 | ||
|
|
79fdd21817 | ||
|
|
a0daa7c4be | ||
|
|
000d0d3119 | ||
|
|
3b01b4f3dd | ||
|
|
f0613e9660 | ||
|
|
1c51ed37c2 | ||
|
|
2bfcd92da9 | ||
|
|
0bf676448b | ||
|
|
5bceff414e | ||
|
|
1b65231e30 | ||
|
|
8b39b69bb7 | ||
|
|
4dfab45544 | ||
|
|
f046dbf3cc | ||
|
|
2e5f9350eb | ||
|
|
938784d6e8 | ||
|
|
02c582a695 | ||
|
|
2851d49c3b | ||
|
|
e88af1f6ba | ||
|
|
da3b6bf19e | ||
|
|
da28e10ac7 | ||
|
|
e6b260cc60 | ||
|
|
2cea9debcf | ||
|
|
768a26a5d4 | ||
|
|
0e251ffb6a | ||
|
|
74c04ea561 | ||
|
|
e92cc2735b | ||
|
|
6a94535b78 | ||
|
|
17ad490f97 | ||
|
|
704e4c4ef7 | ||
|
|
36a301275e | ||
|
|
bb2a01512e | ||
|
|
ad73c9391d | ||
|
|
ce33d146d0 | ||
|
|
055695890e | ||
|
|
028579eed4 | ||
|
|
5c836228f7 | ||
|
|
0663eee49a | ||
|
|
4d9205699b | ||
|
|
d8a5993a28 | ||
|
|
6e7e19fd34 | ||
|
|
9e55ee9be1 | ||
|
|
ae93665645 | ||
|
|
7eb84bb6a3 | ||
|
|
3006cc9431 | ||
|
|
480629953b | ||
|
|
c54ed09d38 | ||
|
|
a098b56339 | ||
|
|
7a9f8d9788 | ||
|
|
7bd822b96a | ||
|
|
d20e9d5629 | ||
|
|
9b61a99dc3 | ||
|
|
4c1f670147 | ||
|
|
c05d299fe4 | ||
|
|
0ba8b530fe | ||
|
|
0a6f7afeff | ||
|
|
483f274bac | ||
|
|
1fd36a4623 | ||
|
|
3b1ab80c1c | ||
|
|
abe0cec346 | ||
|
|
6e1e22d6cb | ||
|
|
7650ec9323 | ||
|
|
fe3fedaf64 | ||
|
|
f21f187890 | ||
|
|
41e164d131 | ||
|
|
adb6a0b7f0 | ||
|
|
31618c1611 | ||
|
|
6eba60638b | ||
|
|
e4040d4a47 | ||
|
|
c53cd32291 | ||
|
|
172ca340ae | ||
|
|
a8451abea4 | ||
|
|
cdf67a3865 | ||
|
|
e2f2fc4804 | ||
|
|
db9a451e32 | ||
|
|
a8cf682f71 | ||
|
|
0c721f1776 | ||
|
|
f8194aac7a | ||
|
|
15dd60ff3e | ||
|
|
d54441a9b2 | ||
|
|
4e75bfb4d0 | ||
|
|
3e350d03c6 | ||
|
|
2fa44e1ae2 | ||
|
|
1e5244cee0 | ||
|
|
6043c2738d | ||
|
|
013cf5eb13 | ||
|
|
a413fe2dc1 | ||
|
|
a2bf02eb82 | ||
|
|
229485494a | ||
|
|
4ea3439667 | ||
|
|
4154d31143 | ||
|
|
30b660f802 | ||
|
|
b79cf45778 | ||
|
|
c6e9cbd69b | ||
|
|
7e493ed129 | ||
|
|
c3ed6ebe79 | ||
|
|
27e33ec1b1 | ||
|
|
7601d5a2bd | ||
|
|
a9fb42eb15 | ||
|
|
cd9cdbf1be | ||
|
|
e68a716587 | ||
|
|
bab5672669 | ||
|
|
80f083e141 | ||
|
|
91d37b38cc | ||
|
|
9349fa9217 | ||
|
|
748523f9cf | ||
|
|
e64336abe9 | ||
|
|
15450a7fba | ||
|
|
ad2f63cd8f | ||
|
|
dbd3d956fb | ||
|
|
aabb4399ed | ||
|
|
a99386ac1d | ||
|
|
b400f22d15 | ||
|
|
8843054b89 | ||
|
|
8c8bde0799 | ||
|
|
109a990b28 | ||
|
|
8e9ac2e3e7 | ||
|
|
c0ad9b1b71 | ||
|
|
01609a1f29 | ||
|
|
2f5219615d | ||
|
|
91cd93783f | ||
|
|
2d8e4e9053 | ||
|
|
9500b3a5ae | ||
|
|
84e124bbf5 | ||
|
|
0d0377ae4a | ||
|
|
9e1ec67253 | ||
|
|
4f8ee5ea52 | ||
|
|
56654ffcb1 | ||
|
|
ac48d3634b | ||
|
|
934e4cbb2c | ||
|
|
53e631d65f | ||
|
|
1483d7c969 | ||
|
|
71ddee1033 | ||
|
|
480bba7a8c | ||
|
|
9c4c529c62 |
@@ -1,8 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
|
||||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
|
|
||||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
||||||
|
|||||||
31479
.gitattributes
vendored
31479
.gitattributes
vendored
File diff suppressed because it is too large
Load Diff
27
.gitignore
vendored
27
.gitignore
vendored
@@ -2,20 +2,29 @@
|
|||||||
/*.iml
|
/*.iml
|
||||||
/*.tmp
|
/*.tmp
|
||||||
/.metadata
|
/.metadata
|
||||||
|
forge-ai/forge-ai.iml
|
||||||
|
forge-ai/target
|
||||||
|
forge-core/forge-core.iml
|
||||||
|
forge-core/target
|
||||||
|
forge-game/target
|
||||||
|
forge-gui/forge-gui.iml
|
||||||
|
forge-gui/forge.profile.properties
|
||||||
|
forge-gui/res/*.log
|
||||||
|
forge-gui/res/PerSetTrackingResults
|
||||||
|
forge-gui/res/cardsfolder/*.bat
|
||||||
|
forge-gui/res/decks
|
||||||
|
forge-gui/res/layouts
|
||||||
|
forge-gui/res/pics*
|
||||||
|
forge-gui/res/pics_product
|
||||||
|
forge-gui/target
|
||||||
|
forge-gui/tools/PerSetTrackingResults
|
||||||
|
forge-gui/tools/oracleScript.log
|
||||||
|
forge-net/target
|
||||||
/forge.profile.properties
|
/forge.profile.properties
|
||||||
/nbactions.xml
|
/nbactions.xml
|
||||||
/pom.xml.next
|
/pom.xml.next
|
||||||
/pom.xml.releaseBackup
|
/pom.xml.releaseBackup
|
||||||
/pom.xml.tag
|
/pom.xml.tag
|
||||||
/release.properties
|
/release.properties
|
||||||
res/*.log
|
|
||||||
res/PerSetTrackingResults
|
|
||||||
res/cardsfolder/*.bat
|
|
||||||
res/decks
|
|
||||||
res/layouts
|
|
||||||
res/pics*
|
|
||||||
res/pics_product
|
|
||||||
/target
|
/target
|
||||||
/test-output
|
/test-output
|
||||||
tools/PerSetTrackingResults
|
|
||||||
tools/oracleScript.log
|
|
||||||
|
|||||||
29
.project
29
.project
@@ -4,36 +4,7 @@
|
|||||||
<comment></comment>
|
<comment></comment>
|
||||||
<projects>
|
<projects>
|
||||||
</projects>
|
</projects>
|
||||||
<buildSpec>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
</buildSpec>
|
|
||||||
<natures>
|
<natures>
|
||||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
|
||||||
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
|
|
||||||
</natures>
|
</natures>
|
||||||
</projectDescription>
|
</projectDescription>
|
||||||
|
|||||||
101
CHANGES.txt
101
CHANGES.txt
@@ -1,101 +0,0 @@
|
|||||||
Forge Beta: 10-01-2013 ver 1.5.1
|
|
||||||
|
|
||||||
|
|
||||||
13328 cards in total.
|
|
||||||
|
|
||||||
|
|
||||||
-------------
|
|
||||||
Release Notes
|
|
||||||
-------------
|
|
||||||
|
|
||||||
- Forge freezing during a match bug -
|
|
||||||
A number of people have reported this bug and we now feel that it may have been fixed in this version. Please play test this version and let us know.
|
|
||||||
|
|
||||||
|
|
||||||
- Skinned titlebar for main window -
|
|
||||||
Titlebar is now skinned instead of displaying using standard OS window titlebar
|
|
||||||
Maximizing window now displays full-screen
|
|
||||||
Can use Layout > View > Titlebar (F11) to toggle visibility of titlebar (will also open full-screen if hiding titlebar)
|
|
||||||
|
|
||||||
|
|
||||||
- Forge now requires Java 7 -
|
|
||||||
Please update your Java runtime environment. At this point Forge versions 1.4.2 and above will no longer run under Java 6. Those who are using Mac OS should install the JDK version rather than the JRE version.
|
|
||||||
|
|
||||||
|
|
||||||
- The Mac OS X application -
|
|
||||||
At this time Forge now requires Java 7 and will no longer run under Java 6.
|
|
||||||
|
|
||||||
Unfortunately, the Mac OS X builder that we were using does not support Java 7. We hope to find and to use a different Mac OS X builder in order to continue releasing a Mac OS bundled application like we have in the past.
|
|
||||||
|
|
||||||
Currently, the windows/unix release of Forge includes a launcher file named "forge.command". Double click on the "forge.command" launcher command file and this will in turn launch the Forge jar file via the terminal application while increasing the Java heap space. This should be a temporary inconvenience.
|
|
||||||
|
|
||||||
|
|
||||||
---------
|
|
||||||
New Cards
|
|
||||||
---------
|
|
||||||
|
|
||||||
Chaos Moon
|
|
||||||
Deep Water
|
|
||||||
Infernal Darkness
|
|
||||||
Mana Reflection
|
|
||||||
Mausoleum Turnkey
|
|
||||||
Naked Singularity
|
|
||||||
Pale Moon
|
|
||||||
Pulse of Llanowar
|
|
||||||
Reality Twist
|
|
||||||
Ritual of Subdual
|
|
||||||
|
|
||||||
|
|
||||||
-----------
|
|
||||||
New Schemes
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Nature Demands an Offering
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
New Vanguard Avatars
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Mirri
|
|
||||||
|
|
||||||
|
|
||||||
------------
|
|
||||||
Known Issues
|
|
||||||
------------
|
|
||||||
|
|
||||||
Several people have noticed that the cards displayed on the battlefield will fail to be displayed when the number of cards on the battlefield increases. Maximizing the human panel can help to re-display the cards.
|
|
||||||
|
|
||||||
Some time was spent turning the static ETB triggers into the proper ETB replacement effects they should be, mainly to interact correctly with each other. This work is not yet finished. As a result there is currently some inconsistencies with "Enters the battlefield with counters" (Not incredibly noticeable).
|
|
||||||
|
|
||||||
A recent contribution to the code base should fix some of the bugs that people noticed with cloning type abilities. At this time there is one remaining issue that we hope will be addressed in the near future:
|
|
||||||
Copies of cards that setup Zone Change triggers via addComesIntoPlayCommand and addLeavesPlayCommand will not function correctly.
|
|
||||||
|
|
||||||
The Forge archive includes a readme.txt file and we ask that you spend a few minutes reading this file as it contains some information that may prove useful. We do tend to update this file at times and you should quickly read this file and look for new information for each and every new release. Thank you.
|
|
||||||
|
|
||||||
The archive format used for the Forge distribution is ".tar.bz2". There are utilities for Windows, Mac OS and the various *nix's that can be used to extract/decompress these ".tar.bz2" archives. We recommend that you extract/decompress the Forge archive into a new and unused folder.
|
|
||||||
|
|
||||||
Some people use the Windows application 7zip. This utility can be found at http://www.7-zip.org/download.html. Mac users can double click on the archive and the application Archive Utility will launch and extract the archive. Mac users do not need to download a separate utility.
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------
|
|
||||||
Contributors to This Release
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
DrDev
|
|
||||||
Dripton
|
|
||||||
Gos
|
|
||||||
Hellfish
|
|
||||||
Max
|
|
||||||
Sloth
|
|
||||||
spr
|
|
||||||
Swordshine
|
|
||||||
Chris H
|
|
||||||
|
|
||||||
|
|
||||||
(Quest icons used created by Teekatas, from his Legendora set http://raindropmemory.deviantart.com)
|
|
||||||
(Thanks to the MAGE team for permission to use their targeting arrows.)
|
|
||||||
(Thanks to http://www.freesound.org/browse/ for providing some sound files.)
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
9
forge-ai/.classpath
Normal file
9
forge-ai/.classpath
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
23
forge-ai/.project
Normal file
23
forge-ai/.project
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>forge-ai</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
4
forge-ai/.settings/org.eclipse.core.resources.prefs
Normal file
4
forge-ai/.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding//src/main/java=ISO-8859-1
|
||||||
|
encoding//src/test/java=ISO-8859-1
|
||||||
|
encoding/<project>=ISO-8859-1
|
||||||
5
forge-ai/.settings/org.eclipse.jdt.core.prefs
Normal file
5
forge-ai/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.7
|
||||||
4
forge-ai/.settings/org.eclipse.m2e.core.prefs
Normal file
4
forge-ai/.settings/org.eclipse.m2e.core.prefs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
||||||
28
forge-ai/pom.xml
Normal file
28
forge-ai/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>forge</artifactId>
|
||||||
|
<groupId>forge</groupId>
|
||||||
|
<version>1.5.12</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>forge-ai</artifactId>
|
||||||
|
<name>Forge AI</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>forge</groupId>
|
||||||
|
<artifactId>forge-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>forge</groupId>
|
||||||
|
<artifactId>forge-game</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
9
forge-core/.classpath
Normal file
9
forge-core/.classpath
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
23
forge-core/.project
Normal file
23
forge-core/.project
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>forge-core</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
4
forge-core/.settings/org.eclipse.core.resources.prefs
Normal file
4
forge-core/.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding//src/main/java=ISO-8859-1
|
||||||
|
encoding//src/test/java=ISO-8859-1
|
||||||
|
encoding/<project>=ISO-8859-1
|
||||||
5
forge-core/.settings/org.eclipse.jdt.core.prefs
Normal file
5
forge-core/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.7
|
||||||
4
forge-core/.settings/org.eclipse.m2e.core.prefs
Normal file
4
forge-core/.settings/org.eclipse.m2e.core.prefs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
||||||
27
forge-core/pom.xml
Normal file
27
forge-core/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>forge</artifactId>
|
||||||
|
<groupId>forge</groupId>
|
||||||
|
<version>1.5.12</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>forge-core</artifactId>
|
||||||
|
<name>Forge Core</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>15.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
375
forge-core/src/main/java/forge/CardStorageReader.java
Normal file
375
forge-core/src/main/java/forge/CardStorageReader.java
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.time.StopWatch;
|
||||||
|
|
||||||
|
import forge.card.CardRules;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
import forge.util.ThreadUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* CardReader class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardStorageReader.java 23742 2013-11-22 16:32:56Z Max mtg $
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CardStorageReader {
|
||||||
|
public interface Observer {
|
||||||
|
public void cardLoaded(CardRules rules, List<String> lines, File fileOnDisk);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ProgressObserver{
|
||||||
|
void setOperationName(String name, boolean usePercents);
|
||||||
|
void report(int current, int total);
|
||||||
|
|
||||||
|
// does nothing, used when they pass null instead of an instance
|
||||||
|
public final static ProgressObserver emptyObserver = new ProgressObserver() {
|
||||||
|
@Override public void setOperationName(String name, boolean usePercents) {}
|
||||||
|
@Override public void report(int current, int total) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String CARD_FILE_DOT_EXTENSION = ".txt";
|
||||||
|
|
||||||
|
/** Default charset when loading from files. */
|
||||||
|
public static final String DEFAULT_CHARSET_NAME = "US-ASCII";
|
||||||
|
|
||||||
|
private final boolean useThreadPool = ThreadUtil.isMultiCoreSystem();
|
||||||
|
private final static int NUMBER_OF_PARTS = 25;
|
||||||
|
|
||||||
|
private final ProgressObserver progressObserver;
|
||||||
|
|
||||||
|
private transient File cardsfolder;
|
||||||
|
|
||||||
|
private transient ZipFile zip;
|
||||||
|
private final transient Charset charset;
|
||||||
|
|
||||||
|
private final Observer observer;
|
||||||
|
|
||||||
|
|
||||||
|
// 8/18/11 10:56 PM
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor for CardReader.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param theCardsFolder
|
||||||
|
* indicates location of the cardsFolder
|
||||||
|
* @param useZip
|
||||||
|
* if true, attempts to load cards from a zip file, if one
|
||||||
|
* exists.
|
||||||
|
*/
|
||||||
|
public CardStorageReader(String cardDataDir, CardStorageReader.ProgressObserver progressObserver, Observer observer) {
|
||||||
|
this.progressObserver = progressObserver != null ? progressObserver : CardStorageReader.ProgressObserver.emptyObserver;
|
||||||
|
this.cardsfolder = new File(cardDataDir);
|
||||||
|
this.observer = observer;
|
||||||
|
|
||||||
|
// These read data for lightweight classes.
|
||||||
|
if (!cardsfolder.exists()) {
|
||||||
|
throw new RuntimeException("CardReader : constructor error -- " + cardsfolder.getAbsolutePath() + " file/folder not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cardsfolder.isDirectory()) {
|
||||||
|
throw new RuntimeException("CardReader : constructor error -- not a directory -- " + cardsfolder.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
final File zipFile = new File(cardsfolder, "cardsfolder.zip");
|
||||||
|
|
||||||
|
if (zipFile.exists()) {
|
||||||
|
try {
|
||||||
|
this.zip = new ZipFile(zipFile);
|
||||||
|
} catch (final Exception exn) {
|
||||||
|
System.err.printf("Error reading zip file \"%s\": %s. Defaulting to txt files in \"%s\".%n", zipFile.getAbsolutePath(), exn, cardsfolder.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.charset = Charset.forName(CardStorageReader.DEFAULT_CHARSET_NAME);
|
||||||
|
|
||||||
|
} // CardReader()
|
||||||
|
|
||||||
|
private final List<CardRules> loadCardsInRange(final List<File> files, int from, int to) {
|
||||||
|
CardRules.Reader rulesReader = new CardRules.Reader();
|
||||||
|
|
||||||
|
List<CardRules> result = new ArrayList<CardRules>();
|
||||||
|
for(int i = from; i < to; i++) {
|
||||||
|
File cardTxtFile = files.get(i);
|
||||||
|
result.add(this.loadCard(rulesReader, cardTxtFile));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<CardRules> loadCardsInRangeFromZip(final List<ZipEntry> files, int from, int to) {
|
||||||
|
CardRules.Reader rulesReader = new CardRules.Reader();
|
||||||
|
|
||||||
|
List<CardRules> result = new ArrayList<CardRules>();
|
||||||
|
for(int i = from; i < to; i++) {
|
||||||
|
ZipEntry ze = files.get(i);
|
||||||
|
// if (ze.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION)) // already filtered!
|
||||||
|
result.add(this.loadCard(rulesReader, ze));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts reading cards into memory until the given card is found.
|
||||||
|
*
|
||||||
|
* After that, we save our place in the list of cards (on disk) in case we
|
||||||
|
* need to load more.
|
||||||
|
*
|
||||||
|
* @return the Card or null if it was not found.
|
||||||
|
*/
|
||||||
|
public final Iterable<CardRules> loadCards() {
|
||||||
|
progressObserver.setOperationName("Loading cards, examining folder", true);
|
||||||
|
|
||||||
|
// Iterate through txt files or zip archive.
|
||||||
|
// Report relevant numbers to progress monitor model.
|
||||||
|
|
||||||
|
|
||||||
|
Set<CardRules> result = new TreeSet<CardRules>(new Comparator<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public int compare(CardRules o1, CardRules o2) {
|
||||||
|
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<File> allFiles = collectCardFiles(new ArrayList<File>(), this.cardsfolder);
|
||||||
|
if(!allFiles.isEmpty()) {
|
||||||
|
int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3;
|
||||||
|
if( allFiles.size() < fileParts * 100)
|
||||||
|
fileParts = allFiles.size() / 100; // to avoid creation of many threads for a dozen of files
|
||||||
|
final CountDownLatch cdlFiles = new CountDownLatch(fileParts);
|
||||||
|
List<Callable<List<CardRules>>> taskFiles = makeTaskListForFiles(allFiles, cdlFiles);
|
||||||
|
progressObserver.setOperationName("Loading cards from folders", true);
|
||||||
|
progressObserver.report(0, taskFiles.size());
|
||||||
|
StopWatch sw = new StopWatch();
|
||||||
|
sw.start();
|
||||||
|
executeLoadTask(result, taskFiles, cdlFiles);
|
||||||
|
sw.stop();
|
||||||
|
final long timeOnParse = sw.getTime();
|
||||||
|
System.out.printf("Read cards: %s files in %d ms (%d parts) %s%n", allFiles.size(), timeOnParse, taskFiles.size(), useThreadPool ? "using thread pool" : "in same thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( this.zip != null ) {
|
||||||
|
final CountDownLatch cdlZip = new CountDownLatch(NUMBER_OF_PARTS);
|
||||||
|
List<Callable<List<CardRules>>> taskZip = new ArrayList<>();
|
||||||
|
|
||||||
|
ZipEntry entry;
|
||||||
|
List<ZipEntry> entries = new ArrayList<ZipEntry>();
|
||||||
|
// zipEnum was initialized in the constructor.
|
||||||
|
Enumeration<? extends ZipEntry> zipEnum = this.zip.entries();
|
||||||
|
while (zipEnum.hasMoreElements()) {
|
||||||
|
entry = zipEnum.nextElement();
|
||||||
|
if (entry.isDirectory() || !entry.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION))
|
||||||
|
continue;
|
||||||
|
entries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
taskZip = makeTaskListForZip(entries, cdlZip);
|
||||||
|
progressObserver.setOperationName("Loading cards from archive", true);
|
||||||
|
progressObserver.report(0, taskZip.size());
|
||||||
|
StopWatch sw = new StopWatch();
|
||||||
|
sw.start();
|
||||||
|
executeLoadTask(result, taskZip, cdlZip);
|
||||||
|
sw.stop();
|
||||||
|
final long timeOnParse = sw.getTime();
|
||||||
|
System.out.printf("Read cards: %s archived files in %d ms (%d parts) %s%n", this.zip.size(), timeOnParse, taskZip.size(), useThreadPool ? "using thread pool" : "in same thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} // loadCardsUntilYouFind(String)
|
||||||
|
|
||||||
|
private void executeLoadTask(Collection<CardRules> result, final List<Callable<List<CardRules>>> tasks, CountDownLatch cdl) {
|
||||||
|
try {
|
||||||
|
if ( useThreadPool ) {
|
||||||
|
final ExecutorService executor = ThreadUtil.getComputingPool(0.5f);
|
||||||
|
final List<Future<List<CardRules>>> parts = executor.invokeAll(tasks);
|
||||||
|
executor.shutdown();
|
||||||
|
cdl.await();
|
||||||
|
for(Future<List<CardRules>> pp : parts) {
|
||||||
|
result.addAll(pp.get());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(Callable<List<CardRules>> c : tasks) {
|
||||||
|
result.addAll(c.call());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) { // this clause comes from non-threaded branch
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Callable<List<CardRules>>> makeTaskListForZip(final List<ZipEntry> entries, final CountDownLatch cdl) {
|
||||||
|
int totalFiles = entries.size();
|
||||||
|
final int maxParts = (int) cdl.getCount();
|
||||||
|
int filesPerPart = totalFiles / maxParts;
|
||||||
|
final List<Callable<List<CardRules>>> tasks = new ArrayList<Callable<List<CardRules>>>();
|
||||||
|
for (int iPart = 0; iPart < maxParts; iPart++) {
|
||||||
|
final int from = iPart * filesPerPart;
|
||||||
|
final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart;
|
||||||
|
tasks.add(new Callable<List<CardRules>>() {
|
||||||
|
@Override
|
||||||
|
public List<CardRules> call() throws Exception{
|
||||||
|
List<CardRules> res = loadCardsInRangeFromZip(entries, from, till);
|
||||||
|
cdl.countDown();
|
||||||
|
progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Callable<List<CardRules>>> makeTaskListForFiles(final List<File> allFiles, final CountDownLatch cdl) {
|
||||||
|
int totalFiles = allFiles.size();
|
||||||
|
final int maxParts = (int) cdl.getCount();
|
||||||
|
int filesPerPart = totalFiles / maxParts;
|
||||||
|
final List<Callable<List<CardRules>>> tasks = new ArrayList<Callable<List<CardRules>>>();
|
||||||
|
for (int iPart = 0; iPart < maxParts; iPart++) {
|
||||||
|
final int from = iPart * filesPerPart;
|
||||||
|
final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart;
|
||||||
|
tasks.add(new Callable<List<CardRules>>() {
|
||||||
|
@Override
|
||||||
|
public List<CardRules> call() throws Exception{
|
||||||
|
List<CardRules> res = loadCardsInRange(allFiles, from, till);
|
||||||
|
cdl.countDown();
|
||||||
|
progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<File> collectCardFiles(List<File> accumulator, File startDir) {
|
||||||
|
String[] list = startDir.list();
|
||||||
|
for (String filename : list) {
|
||||||
|
File entry = new File(startDir, filename);
|
||||||
|
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
if (entry.getName().endsWith(CardStorageReader.CARD_FILE_DOT_EXTENSION))
|
||||||
|
accumulator.add(entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (filename.startsWith(".")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
collectCardFiles(accumulator, entry);
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<String> readScript(final InputStream inputStream) {
|
||||||
|
return FileUtil.readAllLines(new InputStreamReader(inputStream, this.charset), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a card from a txt file.
|
||||||
|
*
|
||||||
|
* @param pathToTxtFile
|
||||||
|
* the full or relative path to the file to load
|
||||||
|
*
|
||||||
|
* @return a new Card instance
|
||||||
|
*/
|
||||||
|
protected final CardRules loadCard(final CardRules.Reader reader, final File file) {
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
try {
|
||||||
|
fileInputStream = new FileInputStream(file);
|
||||||
|
reader.reset();
|
||||||
|
List<String> lines = readScript(fileInputStream);
|
||||||
|
CardRules rules = reader.readCard(lines);
|
||||||
|
if ( null != observer )
|
||||||
|
observer.cardLoaded(rules, lines, file);
|
||||||
|
return rules;
|
||||||
|
} catch (final FileNotFoundException ex) {
|
||||||
|
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (final IOException ignored) {
|
||||||
|
// 11:08
|
||||||
|
// PM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a card from an entry in a zip file.
|
||||||
|
*
|
||||||
|
* @param entry
|
||||||
|
* to load from
|
||||||
|
*
|
||||||
|
* @return a new Card instance
|
||||||
|
*/
|
||||||
|
protected final CardRules loadCard(final CardRules.Reader rulesReader, final ZipEntry entry) {
|
||||||
|
InputStream zipInputStream = null;
|
||||||
|
try {
|
||||||
|
zipInputStream = this.zip.getInputStream(entry);
|
||||||
|
rulesReader.reset();
|
||||||
|
CardRules rules = rulesReader.readCard(readScript(zipInputStream));
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
} catch (final IOException exn) {
|
||||||
|
throw new RuntimeException(exn);
|
||||||
|
// PM
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (zipInputStream != null) {
|
||||||
|
zipInputStream.close();
|
||||||
|
}
|
||||||
|
} catch (final IOException ignored) {
|
||||||
|
// 11:08
|
||||||
|
// PM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
102
forge-core/src/main/java/forge/StaticData.java
Normal file
102
forge-core/src/main/java/forge/StaticData.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package forge;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import forge.card.CardDb;
|
||||||
|
import forge.card.CardEdition;
|
||||||
|
import forge.card.CardRules;
|
||||||
|
import forge.card.PrintSheet;
|
||||||
|
import forge.item.FatPack;
|
||||||
|
import forge.item.SealedProduct;
|
||||||
|
import forge.util.storage.IStorage;
|
||||||
|
import forge.util.storage.StorageBase;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class holding game invariants, such as cards, editions, game formats. All that data, which is not supposed to be changed by player
|
||||||
|
*
|
||||||
|
* @author Max
|
||||||
|
*/
|
||||||
|
public class StaticData {
|
||||||
|
private final CardDb commonCards;
|
||||||
|
private final CardDb variantCards;
|
||||||
|
private final CardEdition.Collection editions;
|
||||||
|
private final IStorage<SealedProduct.Template> boosters;
|
||||||
|
private final IStorage<SealedProduct.Template> specialBoosters;
|
||||||
|
private final IStorage<SealedProduct.Template> tournaments;
|
||||||
|
private final IStorage<FatPack.Template> fatPacks;
|
||||||
|
private final IStorage<PrintSheet> printSheets;
|
||||||
|
|
||||||
|
private static StaticData lastInstance = null;
|
||||||
|
|
||||||
|
public StaticData(CardStorageReader reader, String editionFolder, String blockDataFolder) {
|
||||||
|
this.editions = new CardEdition.Collection(new CardEdition.Reader(new File(editionFolder)));
|
||||||
|
lastInstance = this;
|
||||||
|
|
||||||
|
final Map<String, CardRules> regularCards = new TreeMap<String, CardRules>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
final Map<String, CardRules> variantsCards = new TreeMap<String, CardRules>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
|
for (CardRules card : reader.loadCards()) {
|
||||||
|
if (null == card) continue;
|
||||||
|
|
||||||
|
final String cardName = card.getName();
|
||||||
|
if ( card.isVariant() ) {
|
||||||
|
variantsCards.put(cardName, card);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
regularCards.put(cardName, card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commonCards = new CardDb(regularCards, editions, false, false);
|
||||||
|
variantCards = new CardDb(variantsCards, editions, false, false);
|
||||||
|
|
||||||
|
this.boosters = new StorageBase<SealedProduct.Template>("Boosters", editions.getBoosterGenerator());
|
||||||
|
this.specialBoosters = new StorageBase<SealedProduct.Template>("Special boosters", new SealedProduct.Template.Reader(new File(blockDataFolder, "boosters-special.txt")));
|
||||||
|
this.tournaments = new StorageBase<SealedProduct.Template>("Starter sets", new SealedProduct.Template.Reader(new File(blockDataFolder, "starters.txt")));
|
||||||
|
this.fatPacks = new StorageBase<FatPack.Template>("Fat packs", new FatPack.Template.Reader("res/blockdata/fatpacks.txt"));
|
||||||
|
this.printSheets = new StorageBase<PrintSheet>("Special print runs", new PrintSheet.Reader(new File(blockDataFolder, "printsheets.txt")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static StaticData instance() {
|
||||||
|
return lastInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final CardEdition.Collection getEditions() {
|
||||||
|
return this.editions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {@link forge.util.storage.IStorageView}<{@link forge.item.FatPackTemplate}> */
|
||||||
|
public IStorage<FatPack.Template> getFatPacks() {
|
||||||
|
return fatPacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.BoosterTemplate}> */
|
||||||
|
public final IStorage<SealedProduct.Template> getTournamentPacks() {
|
||||||
|
return tournaments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.BoosterTemplate}> */
|
||||||
|
public final IStorage<SealedProduct.Template> getBoosters() {
|
||||||
|
return boosters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final IStorage<SealedProduct.Template> getSpecialBoosters() {
|
||||||
|
return specialBoosters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IStorage<PrintSheet> getPrintSheets() {
|
||||||
|
return printSheets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardDb getCommonCards() {
|
||||||
|
return commonCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardDb getVariantCards() {
|
||||||
|
return variantCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -34,10 +34,10 @@ import com.google.common.base.Predicates;
|
|||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.Singletons;
|
import forge.StaticData;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.item.IPaperCard;
|
import forge.item.IPaperCard;
|
||||||
import forge.item.PrintSheet;
|
import forge.item.SealedProduct;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,26 +50,15 @@ import forge.util.TextUtil;
|
|||||||
*/
|
*/
|
||||||
public class BoosterGenerator {
|
public class BoosterGenerator {
|
||||||
|
|
||||||
private static final String LAND = "Land";
|
|
||||||
public static final String ANY = "Any";
|
|
||||||
public static final String COMMON = "Common";
|
|
||||||
public static final String UNCOMMON = "Uncommon";
|
|
||||||
public static final String UNCOMMON_RARE = "UncommonRare";
|
|
||||||
public static final String RARE = "Rare";
|
|
||||||
public static final String RARE_MYTHIC = "RareMythic";
|
|
||||||
public static final String MYTHIC = "Mythic";
|
|
||||||
public static final String BASIC_LAND = "BasicLand";
|
|
||||||
public static final String TIME_SHIFTED = "TimeShifted";
|
|
||||||
|
|
||||||
|
|
||||||
private final static Map<String, PrintSheet> cachedSheets = new TreeMap<String, PrintSheet>(String.CASE_INSENSITIVE_ORDER);
|
private final static Map<String, PrintSheet> cachedSheets = new TreeMap<String, PrintSheet>(String.CASE_INSENSITIVE_ORDER);
|
||||||
private static final synchronized PrintSheet getPrintSheet(String key) {
|
private static final synchronized PrintSheet getPrintSheet(String key) {
|
||||||
if( !cachedSheets.containsKey(key) )
|
if( !cachedSheets.containsKey(key) )
|
||||||
cachedSheets.put(key, makeSheet(key, CardDb.instance().getAllCards()));
|
cachedSheets.put(key, makeSheet(key, StaticData.instance().getCommonCards().getAllCards()));
|
||||||
return cachedSheets.get(key);
|
return cachedSheets.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final List<PaperCard> getBoosterPack(SealedProductTemplate booster) {
|
public static final List<PaperCard> getBoosterPack(SealedProduct.Template booster) {
|
||||||
List<PaperCard> result = new ArrayList<PaperCard>();
|
List<PaperCard> result = new ArrayList<PaperCard>();
|
||||||
for(Pair<String, Integer> slot : booster.getSlots()) {
|
for(Pair<String, Integer> slot : booster.getSlots()) {
|
||||||
String slotType = slot.getLeft(); // add expansion symbol here?
|
String slotType = slot.getLeft(); // add expansion symbol here?
|
||||||
@@ -77,7 +66,7 @@ public class BoosterGenerator {
|
|||||||
|
|
||||||
String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
|
String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
|
||||||
String setCode = sType.length == 1 && booster.getEdition() != null ? booster.getEdition() : null;
|
String setCode = sType.length == 1 && booster.getEdition() != null ? booster.getEdition() : null;
|
||||||
String sheetKey = Singletons.getModel().getEditions().contains(setCode) ? slotType.trim() + " " + setCode: slotType.trim();
|
String sheetKey = StaticData.instance().getEditions().contains(setCode) ? slotType.trim() + " " + setCode: slotType.trim();
|
||||||
|
|
||||||
PrintSheet ps = getPrintSheet(sheetKey);
|
PrintSheet ps = getPrintSheet(sheetKey);
|
||||||
result.addAll(ps.random(numCards, true));
|
result.addAll(ps.random(numCards, true));
|
||||||
@@ -100,7 +89,7 @@ public class BoosterGenerator {
|
|||||||
String mainCode = itMod.next();
|
String mainCode = itMod.next();
|
||||||
if ( mainCode.regionMatches(true, 0, "fromSheet", 0, 9)) { // custom print sheet
|
if ( mainCode.regionMatches(true, 0, "fromSheet", 0, 9)) { // custom print sheet
|
||||||
String sheetName = StringUtils.strip(mainCode.substring(9), "()\" ");
|
String sheetName = StringUtils.strip(mainCode.substring(9), "()\" ");
|
||||||
src = Singletons.getModel().getPrintSheets().get(sheetName).toFlatList();
|
src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||||
setPred = Predicates.alwaysTrue();
|
setPred = Predicates.alwaysTrue();
|
||||||
|
|
||||||
} else if (mainCode.startsWith("promo")) { // get exactly the named cards, that's a tiny inlined print sheet
|
} else if (mainCode.startsWith("promo")) { // get exactly the named cards, that's a tiny inlined print sheet
|
||||||
@@ -108,7 +97,7 @@ public class BoosterGenerator {
|
|||||||
String[] cardNames = TextUtil.splitWithParenthesis(list, ',', '"', '"');
|
String[] cardNames = TextUtil.splitWithParenthesis(list, ',', '"', '"');
|
||||||
List<PaperCard> srcList = new ArrayList<PaperCard>();
|
List<PaperCard> srcList = new ArrayList<PaperCard>();
|
||||||
for(String cardName: cardNames)
|
for(String cardName: cardNames)
|
||||||
srcList.add(CardDb.instance().getCard(cardName));
|
srcList.add(StaticData.instance().getCommonCards().getCard(cardName));
|
||||||
src = srcList;
|
src = srcList;
|
||||||
setPred = Predicates.alwaysTrue();
|
setPred = Predicates.alwaysTrue();
|
||||||
|
|
||||||
@@ -121,18 +110,18 @@ public class BoosterGenerator {
|
|||||||
// only special operators should remain by now - the ones that could not be turned into one predicate
|
// only special operators should remain by now - the ones that could not be turned into one predicate
|
||||||
String mainCode = operators.isEmpty() ? null : operators.get(0).trim();
|
String mainCode = operators.isEmpty() ? null : operators.get(0).trim();
|
||||||
|
|
||||||
if( null == mainCode || mainCode.equalsIgnoreCase(ANY) ) { // no restriction on rarity
|
if( null == mainCode || mainCode.equalsIgnoreCase(BoosterSlots.ANY) ) { // no restriction on rarity
|
||||||
Predicate<PaperCard> predicate = Predicates.and(setPred, extraPred);
|
Predicate<PaperCard> predicate = Predicates.and(setPred, extraPred);
|
||||||
ps.addAll(Iterables.filter(src, predicate));
|
ps.addAll(Iterables.filter(src, predicate));
|
||||||
|
|
||||||
} else if ( mainCode.equalsIgnoreCase(UNCOMMON_RARE) ) { // for sets like ARN, where U1 cards are considered rare and U3 are uncommon
|
} else if ( mainCode.equalsIgnoreCase(BoosterSlots.UNCOMMON_RARE) ) { // for sets like ARN, where U1 cards are considered rare and U3 are uncommon
|
||||||
Predicate<PaperCard> predicateRares = Predicates.and(setPred, IPaperCard.Predicates.Presets.IS_RARE, extraPred);
|
Predicate<PaperCard> predicateRares = Predicates.and(setPred, IPaperCard.Predicates.Presets.IS_RARE, extraPred);
|
||||||
ps.addAll(Iterables.filter(src, predicateRares));
|
ps.addAll(Iterables.filter(src, predicateRares));
|
||||||
|
|
||||||
Predicate<PaperCard> predicateUncommon = Predicates.and( setPred, IPaperCard.Predicates.Presets.IS_UNCOMMON, extraPred);
|
Predicate<PaperCard> predicateUncommon = Predicates.and( setPred, IPaperCard.Predicates.Presets.IS_UNCOMMON, extraPred);
|
||||||
ps.addAll(Iterables.filter(src, predicateUncommon), 3);
|
ps.addAll(Iterables.filter(src, predicateUncommon), 3);
|
||||||
|
|
||||||
} else if ( mainCode.equalsIgnoreCase(RARE_MYTHIC) ) {
|
} else if ( mainCode.equalsIgnoreCase(BoosterSlots.RARE_MYTHIC) ) {
|
||||||
// Typical ratio of rares to mythics is 53:15, changing to 35:10 in smaller sets.
|
// Typical ratio of rares to mythics is 53:15, changing to 35:10 in smaller sets.
|
||||||
// To achieve the desired 1:8 are all mythics are added once, and all rares added twice per print sheet.
|
// To achieve the desired 1:8 are all mythics are added once, and all rares added twice per print sheet.
|
||||||
|
|
||||||
@@ -168,13 +157,13 @@ public class BoosterGenerator {
|
|||||||
|
|
||||||
Predicate<PaperCard> toAdd = null;
|
Predicate<PaperCard> toAdd = null;
|
||||||
if( operator.equalsIgnoreCase("dfc") ) { toAdd = Predicates.compose(CardRulesPredicates.splitType(CardSplitType.Transform), PaperCard.FN_GET_RULES);
|
if( operator.equalsIgnoreCase("dfc") ) { toAdd = Predicates.compose(CardRulesPredicates.splitType(CardSplitType.Transform), PaperCard.FN_GET_RULES);
|
||||||
} else if ( operator.equalsIgnoreCase(LAND) ) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES);
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.LAND) ) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES);
|
||||||
} else if ( operator.equalsIgnoreCase(BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND;
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND;
|
||||||
} else if ( operator.equalsIgnoreCase(TIME_SHIFTED)) { toAdd = IPaperCard.Predicates.Presets.IS_SPECIAL;
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.TIME_SHIFTED)) { toAdd = IPaperCard.Predicates.Presets.IS_SPECIAL;
|
||||||
} else if ( operator.equalsIgnoreCase(MYTHIC)) { toAdd = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE;
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.MYTHIC)) { toAdd = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE;
|
||||||
} else if ( operator.equalsIgnoreCase(RARE)) { toAdd = IPaperCard.Predicates.Presets.IS_RARE;
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.RARE)) { toAdd = IPaperCard.Predicates.Presets.IS_RARE;
|
||||||
} else if ( operator.equalsIgnoreCase(UNCOMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_UNCOMMON;
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.UNCOMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_UNCOMMON;
|
||||||
} else if ( operator.equalsIgnoreCase(COMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_COMMON;
|
} else if ( operator.equalsIgnoreCase(BoosterSlots.COMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_COMMON;
|
||||||
} else if ( operator.startsWith("name(") ) {
|
} else if ( operator.startsWith("name(") ) {
|
||||||
operator = StringUtils.strip(operator.substring(4), "() ");
|
operator = StringUtils.strip(operator.substring(4), "() ");
|
||||||
String[] cardNames = TextUtil.splitWithParenthesis(operator, ',', '"', '"');
|
String[] cardNames = TextUtil.splitWithParenthesis(operator, ',', '"', '"');
|
||||||
14
forge-core/src/main/java/forge/card/BoosterSlots.java
Normal file
14
forge-core/src/main/java/forge/card/BoosterSlots.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
public class BoosterSlots {
|
||||||
|
public static final String LAND = "Land";
|
||||||
|
public static final String ANY = "Any";
|
||||||
|
public static final String COMMON = "Common";
|
||||||
|
public static final String UNCOMMON = "Uncommon";
|
||||||
|
public static final String UNCOMMON_RARE = "UncommonRare";
|
||||||
|
public static final String RARE = "Rare";
|
||||||
|
public static final String RARE_MYTHIC = "RareMythic";
|
||||||
|
public static final String MYTHIC = "Mythic";
|
||||||
|
public static final String BASIC_LAND = "BasicLand";
|
||||||
|
public static final String TIME_SHIFTED = "TimeShifted";
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package forge;
|
package forge.card;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
511
forge-core/src/main/java/forge/card/CardDb.java
Normal file
511
forge-core/src/main/java/forge/card/CardDb.java
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Multimaps;
|
||||||
|
|
||||||
|
import forge.card.CardEdition.CardInSet;
|
||||||
|
import forge.card.CardEdition.Type;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
import forge.util.CollectionSuppliers;
|
||||||
|
import forge.util.Lang;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
|
public final class CardDb implements ICardDatabase {
|
||||||
|
public final static String foilSuffix = "+";
|
||||||
|
public final static char NameSetSeparator = '|';
|
||||||
|
|
||||||
|
// need this to obtain cardReference by name+set+artindex
|
||||||
|
private final ListMultimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<String,Collection<PaperCard>>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.<PaperCard>arrayLists());
|
||||||
|
private final Map<String, PaperCard> uniqueCardsByName = new TreeMap<String, PaperCard>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
private final Map<String, CardRules> rulesByName;
|
||||||
|
|
||||||
|
private final List<PaperCard> allCards = new ArrayList<PaperCard>();
|
||||||
|
private final List<PaperCard> roAllCards = Collections.unmodifiableList(allCards);
|
||||||
|
private final Collection<PaperCard> roUniqueCards = Collections.unmodifiableCollection(uniqueCardsByName.values());
|
||||||
|
private final CardEdition.Collection editions;
|
||||||
|
|
||||||
|
public enum SetPreference {
|
||||||
|
Latest(false),
|
||||||
|
LatestCoreExp(true),
|
||||||
|
Earliest(false),
|
||||||
|
EarliestCoreExp(true),
|
||||||
|
Random(false);
|
||||||
|
|
||||||
|
final boolean filterSets;
|
||||||
|
private SetPreference(boolean filterIrregularSets) {
|
||||||
|
filterSets = filterIrregularSets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean accept(CardEdition ed) {
|
||||||
|
return !filterSets || ed.getType() == Type.CORE || ed.getType() == Type.EXPANSION || ed.getType() == Type.REPRINT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NO GETTERS/SETTERS HERE!
|
||||||
|
private static class CardRequest {
|
||||||
|
public String cardName;
|
||||||
|
public String edition;
|
||||||
|
public int artIndex;
|
||||||
|
public boolean isFoil;
|
||||||
|
|
||||||
|
public CardRequest(String name, String edition, int artIndex, boolean isFoil) {
|
||||||
|
cardName = name;
|
||||||
|
this.edition = edition;
|
||||||
|
this.artIndex = artIndex;
|
||||||
|
this.isFoil = isFoil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CardRequest fromString(String name) {
|
||||||
|
boolean isFoil = name.endsWith(foilSuffix);
|
||||||
|
if( isFoil )
|
||||||
|
name = name.substring(0, name.length() - foilSuffix.length());
|
||||||
|
|
||||||
|
String[] nameParts = TextUtil.split(name, NameSetSeparator);
|
||||||
|
|
||||||
|
int setPos = nameParts.length >= 2 && !StringUtils.isNumeric(nameParts[1]) ? 1 : -1;
|
||||||
|
int artPos = nameParts.length >= 2 && StringUtils.isNumeric(nameParts[1]) ? 1 : nameParts.length >= 3 && StringUtils.isNumeric(nameParts[2]) ? 2 : -1;
|
||||||
|
|
||||||
|
String cardName = nameParts[0];
|
||||||
|
if( cardName.endsWith(foilSuffix)) {
|
||||||
|
cardName = cardName.substring(0, cardName.length() - foilSuffix.length());
|
||||||
|
isFoil = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int artIndex = artPos > 0 ? Integer.parseInt(nameParts[artPos]) : 0;
|
||||||
|
String setName = setPos > 0 ? nameParts[setPos] : null;
|
||||||
|
if( "???".equals(setName) )
|
||||||
|
setName = null;
|
||||||
|
|
||||||
|
return new CardRequest(cardName, setName, artIndex, isFoil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardDb(Map<String, CardRules> rules, CardEdition.Collection editions0, boolean logMissingPerEdition, boolean logMissingSummary) {
|
||||||
|
this.rulesByName = rules;
|
||||||
|
this.editions = editions0;
|
||||||
|
Set<String> allMissingCards = new LinkedHashSet<String>();
|
||||||
|
List<String> missingCards = new ArrayList<String>();
|
||||||
|
for(CardEdition e : editions.getOrderedEditions()) {
|
||||||
|
boolean isCoreExpSet = e.getType() == CardEdition.Type.CORE || e.getType() == CardEdition.Type.EXPANSION || e.getType() == CardEdition.Type.REPRINT;
|
||||||
|
if(logMissingPerEdition && isCoreExpSet)
|
||||||
|
System.out.print(e.getName() + " (" + e.getCards().length + " cards)");
|
||||||
|
String lastCardName = null;
|
||||||
|
int artIdx = 1;
|
||||||
|
for(CardEdition.CardInSet cis : e.getCards()) {
|
||||||
|
if ( cis.name.equals(lastCardName) )
|
||||||
|
artIdx++;
|
||||||
|
else {
|
||||||
|
artIdx = 1;
|
||||||
|
lastCardName = cis.name;
|
||||||
|
}
|
||||||
|
CardRules cr = rulesByName.get(lastCardName);
|
||||||
|
if( cr != null )
|
||||||
|
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx));
|
||||||
|
else
|
||||||
|
missingCards.add(cis.name);
|
||||||
|
}
|
||||||
|
if(isCoreExpSet && logMissingPerEdition) {
|
||||||
|
if(missingCards.isEmpty())
|
||||||
|
System.out.println(" ... 100% ");
|
||||||
|
else {
|
||||||
|
int missing = (e.getCards().length - missingCards.size()) * 10000 / e.getCards().length;
|
||||||
|
System.out.printf(" ... %.2f%% (%s missing: %s )%n", missing * 0.01f, Lang.nounWithAmount(missingCards.size(), "card"), StringUtils.join(missingCards, " | ") );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( isCoreExpSet && logMissingSummary )
|
||||||
|
allMissingCards.addAll(missingCards);
|
||||||
|
missingCards.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( logMissingSummary ) {
|
||||||
|
System.out.printf("Totally %d cards not implemented: %s\n", allMissingCards.size(), StringUtils.join(allMissingCards, " | "));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(CardRules cr : rulesByName.values()) {
|
||||||
|
if( !allCardsByName.containsKey(cr.getName()) )
|
||||||
|
{
|
||||||
|
System.err.println("The card " + cr.getName() + " was not assigned to any set. Adding it to UNKNOWN set... to fix see res/cardeditions/ folder. ");
|
||||||
|
addCard(new PaperCard(cr, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCard(PaperCard paperCard) {
|
||||||
|
allCardsByName.put(paperCard.getName(), paperCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reIndex() {
|
||||||
|
uniqueCardsByName.clear();
|
||||||
|
allCards.clear();
|
||||||
|
for(Entry<String, Collection<PaperCard>> kv : allCardsByName.asMap().entrySet()) {
|
||||||
|
uniqueCardsByName.put(kv.getKey(), Iterables.getFirst(kv.getValue(), null));
|
||||||
|
allCards.addAll(kv.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String cardName) {
|
||||||
|
CardRequest request = CardRequest.fromString(cardName);
|
||||||
|
return tryGetCard(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String cardName, String setName) {
|
||||||
|
CardRequest request = CardRequest.fromString(cardName);
|
||||||
|
if(setName != null)
|
||||||
|
request.edition = setName;
|
||||||
|
return tryGetCard(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String cardName, String setName, int artIndex) {
|
||||||
|
CardRequest request = CardRequest.fromString(cardName);
|
||||||
|
if(setName != null)
|
||||||
|
request.edition = setName;
|
||||||
|
if(artIndex > 0)
|
||||||
|
request.artIndex = artIndex;
|
||||||
|
return tryGetCard(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PaperCard tryGetCard(CardRequest request) {
|
||||||
|
Collection<PaperCard> cards = allCardsByName.get(request.cardName);
|
||||||
|
if ( null == cards ) return null;
|
||||||
|
|
||||||
|
PaperCard result = null;
|
||||||
|
|
||||||
|
String reqEdition = request.edition;
|
||||||
|
if(reqEdition != null && !editions.contains(reqEdition)) {
|
||||||
|
CardEdition edition = editions.get(reqEdition);
|
||||||
|
if( edition != null )
|
||||||
|
reqEdition = edition.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( request.artIndex <= 0 ) { // this stands for 'random art'
|
||||||
|
List<PaperCard> candidates = new ArrayList<PaperCard>(9); // 9 cards with same name per set is a maximum of what has been printed (Arnchenemy)
|
||||||
|
for( PaperCard pc : cards ) {
|
||||||
|
if( pc.getEdition().equalsIgnoreCase(reqEdition) || reqEdition == null )
|
||||||
|
candidates.add(pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.isEmpty())
|
||||||
|
return null;
|
||||||
|
result = Aggregates.random(candidates);
|
||||||
|
} else {
|
||||||
|
for( PaperCard pc : cards ) {
|
||||||
|
if( pc.getEdition().equalsIgnoreCase(reqEdition) && request.artIndex == pc.getArtIndex() ) {
|
||||||
|
result = pc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( result == null ) return null;
|
||||||
|
|
||||||
|
return request.isFoil ? getFoiled(result) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCardFromEdition(final String cardName, SetPreference fromSet) {
|
||||||
|
return getCardFromEdition(cardName, null, fromSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final SetPreference fromSet) {
|
||||||
|
return getCardFromEdition(cardName, printedBefore, fromSet, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final SetPreference fromSet, int artIndex) {
|
||||||
|
List<PaperCard> cards = this.allCardsByName.get(cardName);
|
||||||
|
|
||||||
|
int sz = cards.size();
|
||||||
|
if( fromSet == SetPreference.Earliest || fromSet == SetPreference.EarliestCoreExp) {
|
||||||
|
for(int i = sz - 1 ; i >= 0 ; i--) {
|
||||||
|
PaperCard pc = cards.get(i);
|
||||||
|
CardEdition ed = editions.get(pc.getEdition());
|
||||||
|
if(!fromSet.accept(ed))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if((artIndex <= 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore)))
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} else if( fromSet == SetPreference.LatestCoreExp || fromSet == SetPreference.Latest || fromSet == null || fromSet == SetPreference.Random ) {
|
||||||
|
for(int i = 0 ; i < sz ; i++) {
|
||||||
|
PaperCard pc = cards.get(i);
|
||||||
|
CardEdition ed = editions.get(pc.getEdition());
|
||||||
|
if(!fromSet.accept(ed))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if((artIndex < 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore))) {
|
||||||
|
if( fromSet == SetPreference.LatestCoreExp || fromSet == SetPreference.Latest )
|
||||||
|
return pc;
|
||||||
|
return cards.get(i + MyRandom.getRandom().nextInt(sz-i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaperCard getFoiled(PaperCard card0) {
|
||||||
|
// Here - I am still unsure if there should be a cache Card->Card from unfoiled to foiled, to avoid creation of N instances of single plains
|
||||||
|
return new PaperCard(card0.getRules(), card0.getEdition(), card0.getRarity(), card0.getArtIndex(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPrintCount(String cardName, String edition) {
|
||||||
|
int cnt = 0;
|
||||||
|
for( PaperCard pc : allCardsByName.get(cardName) ) {
|
||||||
|
if( pc.getEdition().equals(edition) )
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxPrintCount(String cardName) {
|
||||||
|
int max = -1;
|
||||||
|
for( PaperCard pc : allCardsByName.get(cardName) ) {
|
||||||
|
if ( max < pc.getArtIndex() )
|
||||||
|
max = pc.getArtIndex();
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArtCount(String cardName, String setName) {
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
Collection<PaperCard> cards = allCardsByName.get(cardName);
|
||||||
|
if ( null == cards ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( PaperCard pc : cards ) {
|
||||||
|
if ( pc.getEdition().equalsIgnoreCase(setName) ) {
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a list of all cards from their respective latest editions
|
||||||
|
@Override
|
||||||
|
public Collection<PaperCard> getUniqueCards() {
|
||||||
|
return roUniqueCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PaperCard> getAllCards() {
|
||||||
|
return roAllCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a modifiable list of cards matching the given predicate */
|
||||||
|
@Override
|
||||||
|
public List<PaperCard> getAllCards(Predicate<PaperCard> predicate) {
|
||||||
|
return Lists.newArrayList(Iterables.filter(this.roAllCards, predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<PaperCard> iterator() {
|
||||||
|
return this.roAllCards.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Predicate<? super PaperCard> wasPrintedInSets(List<String> setCodes) {
|
||||||
|
return new PredicateExistsInSets(setCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PredicateExistsInSets implements Predicate<PaperCard> {
|
||||||
|
private final List<String> sets;
|
||||||
|
|
||||||
|
public PredicateExistsInSets(final List<String> wantSets) {
|
||||||
|
this.sets = wantSets; // maybe should make a copy here?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(final PaperCard subject) {
|
||||||
|
Collection<PaperCard> cc = allCardsByName.get(subject.getName());
|
||||||
|
for(PaperCard c : cc)
|
||||||
|
if (sets.contains(c.getEdition()))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuilder appendCardToStringBuilder(PaperCard card, StringBuilder sb) {
|
||||||
|
final boolean hasBadSetInfo = "???".equals(card.getEdition()) || StringUtils.isBlank(card.getEdition());
|
||||||
|
sb.append(card.getName());
|
||||||
|
|
||||||
|
if (!hasBadSetInfo) {
|
||||||
|
int artCount = getArtCount(card.getName(), card.getEdition());
|
||||||
|
sb.append(CardDb.NameSetSeparator).append(card.getEdition());
|
||||||
|
if (artCount > 1) {
|
||||||
|
sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(card.isFoil()) {
|
||||||
|
sb.append(CardDb.foilSuffix);
|
||||||
|
}
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String cardToString(PaperCard pc) {
|
||||||
|
return appendCardToStringBuilder(pc, new StringBuilder()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaperCard createUnsuportedCard(String cardName) {
|
||||||
|
CardRequest request = CardRequest.fromString(cardName);
|
||||||
|
CardEdition cE = CardEdition.UNKNOWN;
|
||||||
|
CardRarity cR = CardRarity.Unknown;
|
||||||
|
|
||||||
|
// May iterate over editions and find out if there is any card named 'cardName' but not implemented with Forge script.
|
||||||
|
if( StringUtils.isBlank(request.edition) ) {
|
||||||
|
for(CardEdition e : editions) {
|
||||||
|
for(CardInSet cs : e.getCards() ) {
|
||||||
|
if( cs.name.equals(request.cardName)) {
|
||||||
|
cE = e;
|
||||||
|
cR = cs.rarity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cE != CardEdition.UNKNOWN)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cE = editions.get(request.edition);
|
||||||
|
if ( cE != null )
|
||||||
|
for(CardInSet cs : cE.getCards() ) {
|
||||||
|
if( cs.name.equals(request.cardName)) {
|
||||||
|
cR = cs.rarity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cE = CardEdition.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Write to log that attempt,
|
||||||
|
if (cR == CardRarity.Unknown )
|
||||||
|
System.err.println(String.format("An unknown card found when loading Forge decks: \"%s\" Forge does not know of such a card's existence. Have you mistyped the card name?", cardName));
|
||||||
|
else
|
||||||
|
System.err.println(String.format("An unsupported card was requested: \"%s\" from \"%s\" set. We're sorry, but you cannot use this card yet.", request.cardName, cE.getName()));
|
||||||
|
|
||||||
|
return new PaperCard(CardRules.getUnsupportedCardNamed(request.cardName), cE.getCode(), cR, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Editor editor = new Editor();
|
||||||
|
public Editor getEditor() { return editor; }
|
||||||
|
public class Editor {
|
||||||
|
private boolean immediateReindex = true;
|
||||||
|
public CardRules putCard(CardRules rules) { return putCard(rules, null); /* will use data from editions folder */ }
|
||||||
|
public CardRules putCard(CardRules rules, List<Pair<String, CardRarity>> whenItWasPrinted){ // works similarly to Map<K,V>, returning prev. value
|
||||||
|
String cardName = rules.getName();
|
||||||
|
|
||||||
|
CardRules result = rulesByName.get(cardName);
|
||||||
|
if (result != null && result.getName().equals(cardName)){ // change properties only
|
||||||
|
result.reinitializeFromRules(rules);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = rulesByName.put(cardName, rules);
|
||||||
|
|
||||||
|
// 1. generate all paper cards from edition data we have (either explicit, or found in res/editions, or add to unknown edition)
|
||||||
|
List<PaperCard> paperCards = new ArrayList<PaperCard>();
|
||||||
|
if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) {
|
||||||
|
for(CardEdition e : editions.getOrderedEditions()) {
|
||||||
|
int artIdx = 1;
|
||||||
|
for(CardInSet cis : e.getCards()) {
|
||||||
|
if( !cis.name.equals(cardName) )
|
||||||
|
continue;
|
||||||
|
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String lastEdition = null;
|
||||||
|
int artIdx = 0;
|
||||||
|
for(Pair<String, CardRarity> tuple : whenItWasPrinted){
|
||||||
|
if(!tuple.getKey().equals(lastEdition)) {
|
||||||
|
artIdx = 1;
|
||||||
|
lastEdition = tuple.getKey();
|
||||||
|
}
|
||||||
|
CardEdition ed = editions.get(lastEdition);
|
||||||
|
if(null == ed)
|
||||||
|
continue;
|
||||||
|
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(paperCards.isEmpty())
|
||||||
|
paperCards.add(new PaperCard(rules, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1));
|
||||||
|
|
||||||
|
// 2. add them to db
|
||||||
|
for (PaperCard paperCard : paperCards)
|
||||||
|
addCard(paperCard);
|
||||||
|
// 3. reindex can be temporary disabled and run after the whole batch of rules is added to db.
|
||||||
|
if(immediateReindex)
|
||||||
|
reIndex();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public void removeCard(String name) {
|
||||||
|
allCardsByName.removeAll(name);
|
||||||
|
uniqueCardsByName.remove(name);
|
||||||
|
rulesByName.remove(name);
|
||||||
|
Iterator<PaperCard> it = allCards.iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
PaperCard pc = it.next();
|
||||||
|
if( pc.getName().equalsIgnoreCase(name))
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void rebuildIndex() { reIndex(); }
|
||||||
|
|
||||||
|
public boolean isImmediateReindex() {
|
||||||
|
return immediateReindex;
|
||||||
|
}
|
||||||
|
public void setImmediateReindex(boolean immediateReindex) {
|
||||||
|
this.immediateReindex = immediateReindex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
486
forge-core/src/main/java/forge/card/CardEdition.java
Normal file
486
forge-core/src/main/java/forge/card/CardEdition.java
Normal file
@@ -0,0 +1,486 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.card.CardDb.SetPreference;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
import forge.item.SealedProduct;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
import forge.util.FileSection;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
import forge.util.IItemReader;
|
||||||
|
import forge.util.storage.StorageBase;
|
||||||
|
import forge.util.storage.StorageReaderBase;
|
||||||
|
import forge.util.storage.StorageReaderFolder;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* CardSet class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardSet.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardEdition implements Comparable<CardEdition> { // immutable
|
||||||
|
public enum Type {
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
|
CORE,
|
||||||
|
EXPANSION,
|
||||||
|
|
||||||
|
REPRINT,
|
||||||
|
STARTER,
|
||||||
|
|
||||||
|
DUEL_DECKS,
|
||||||
|
PREMIUM_DECK_SERIES,
|
||||||
|
FROM_THE_VAULT,
|
||||||
|
|
||||||
|
OTHER,
|
||||||
|
THIRDPARTY // custom sets
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FoilType {
|
||||||
|
NOT_SUPPORTED, // sets before Urza's Legacy
|
||||||
|
OLD_STYLE, // sets between Urza's Legacy and 8th Edition
|
||||||
|
MODERN // 8th Edition and newer
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CardInSet {
|
||||||
|
public final CardRarity rarity;
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
public CardInSet(final String name, final CardRarity rarity) {
|
||||||
|
this.rarity = rarity;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** The Constant unknown. */
|
||||||
|
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
|
||||||
|
public static final CardEdition UNKNOWN = new CardEdition("1990-01-01", "??", "???", Type.UNKNOWN, "Undefined", FoilType.NOT_SUPPORTED, new CardInSet[]{});
|
||||||
|
|
||||||
|
private Date date;
|
||||||
|
private String code2;
|
||||||
|
private String code;
|
||||||
|
private Type type;
|
||||||
|
private String name;
|
||||||
|
private String alias = null;
|
||||||
|
private boolean whiteBorder = false;
|
||||||
|
private FoilType foilType = FoilType.NOT_SUPPORTED;
|
||||||
|
private int foilChanceInBooster = 0;
|
||||||
|
private boolean foilAlwaysInCommonSlot = false;
|
||||||
|
private final CardInSet[] cards;
|
||||||
|
|
||||||
|
|
||||||
|
private int boosterArts = 1;
|
||||||
|
private SealedProduct.Template boosterTpl = null;
|
||||||
|
|
||||||
|
private CardEdition(CardInSet[] cards) {
|
||||||
|
this.cards = cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new card set.
|
||||||
|
*
|
||||||
|
* @param index indicates order of set release date
|
||||||
|
* @param code2 the 2 (usually) letter code used for image filenames/URLs distributed by the HQ pics team that
|
||||||
|
* use Magic Workstation-type edition codes. Older sets only had 2-letter codes, and some of the 3-letter
|
||||||
|
* codes they use now aren't the same as the official list of 3-letter codes. When Forge downloads set-pics,
|
||||||
|
* it uses the 3-letter codes for the folder no matter the age of the set.
|
||||||
|
* @param code the MTG 3-letter set code
|
||||||
|
* @param type the set type
|
||||||
|
* @param name the name of the set
|
||||||
|
* @param an optional secondary code alias for the set
|
||||||
|
*/
|
||||||
|
private CardEdition(String date, String code2, String code, Type type, String name, FoilType foil, CardInSet[] cards) {
|
||||||
|
this(cards);
|
||||||
|
this.code2 = code2;
|
||||||
|
this.code = code;
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
this.date = parseDate(date);
|
||||||
|
this.foilType = foil;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Date parseDate(String date) {
|
||||||
|
if( date.length() <= 7 )
|
||||||
|
date = date + "-01";
|
||||||
|
try {
|
||||||
|
return formatter.parse(date);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
return new Date();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() { return date; }
|
||||||
|
public String getCode2() { return code2; }
|
||||||
|
public String getCode() { return code; }
|
||||||
|
public Type getType() { return type; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public String getAlias() { return alias; }
|
||||||
|
public FoilType getFoilType() { return foilType; }
|
||||||
|
public int getFoilChanceInBooster() { return foilChanceInBooster; }
|
||||||
|
public boolean getFoilAlwaysInCommonSlot() { return foilAlwaysInCommonSlot; }
|
||||||
|
public CardInSet[] getCards() { return cards; }
|
||||||
|
|
||||||
|
/** The Constant fnGetName. */
|
||||||
|
public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(final CardEdition arg1) {
|
||||||
|
return arg1.getCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardEdition o) {
|
||||||
|
if (o == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return date.compareTo(o.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (this.code.hashCode() * 17) + this.name.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CardEdition other = (CardEdition) obj;
|
||||||
|
return other.name.equals(this.name) && this.code.equals(other.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name + " (set)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the whiteBorder
|
||||||
|
*/
|
||||||
|
public boolean isWhiteBorder() {
|
||||||
|
return whiteBorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCntBoosterPictures() {
|
||||||
|
return boosterArts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SealedProduct.Template getBoosterTemplate() {
|
||||||
|
return boosterTpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasBoosterTemplate() {
|
||||||
|
return boosterTpl != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Reader extends StorageReaderFolder<CardEdition> {
|
||||||
|
public Reader(File path) {
|
||||||
|
super(path, CardEdition.FN_GET_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static CardInSet[] arrCards = new CardInSet[] {};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CardEdition read(File file) {
|
||||||
|
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
|
||||||
|
|
||||||
|
List<CardEdition.CardInSet> processedCards = new ArrayList<CardEdition.CardInSet>();
|
||||||
|
for(String line : contents.get("cards")) {
|
||||||
|
if (StringUtils.isBlank(line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// You may omit rarity for early development
|
||||||
|
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
|
||||||
|
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
|
||||||
|
String cardName = hadRarity ? line.substring(2) : line;
|
||||||
|
CardInSet cis = new CardInSet(cardName, r);
|
||||||
|
processedCards.add(cis);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardEdition res = new CardEdition(processedCards.toArray(arrCards));
|
||||||
|
|
||||||
|
|
||||||
|
FileSection section = FileSection.parse(contents.get("metadata"), "=");
|
||||||
|
res.name = section.get("name");
|
||||||
|
res.date = parseDate(section.get("date"));
|
||||||
|
res.code = section.get("code");
|
||||||
|
res.code2 = section.get("code2");
|
||||||
|
if( res.code2 == null )
|
||||||
|
res.code2 = res.code;
|
||||||
|
|
||||||
|
res.boosterArts = section.getInt("BoosterCovers", 1);
|
||||||
|
String boosterDesc = section.get("Booster");
|
||||||
|
res.boosterTpl = boosterDesc == null ? null : new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(boosterDesc));
|
||||||
|
|
||||||
|
res.alias = section.get("alias");
|
||||||
|
res.whiteBorder = "white".equalsIgnoreCase(section.get("border"));
|
||||||
|
String type = section.get("type");
|
||||||
|
Type enumType = Type.UNKNOWN;
|
||||||
|
if (null != type && !type.isEmpty()) {
|
||||||
|
try {
|
||||||
|
enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// ignore; type will get UNKNOWN
|
||||||
|
System.err.println(String.format("Ignoring unknown type in set definitions: name: %s; type: %s", res.name, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.type = enumType;
|
||||||
|
|
||||||
|
switch(section.get("foil", "newstyle").toLowerCase()) {
|
||||||
|
case "notsupported":
|
||||||
|
res.foilType = FoilType.NOT_SUPPORTED;
|
||||||
|
break;
|
||||||
|
case "oldstyle":
|
||||||
|
case "classic":
|
||||||
|
res.foilType = FoilType.OLD_STYLE;
|
||||||
|
break;
|
||||||
|
case "newstyle":
|
||||||
|
case "modern":
|
||||||
|
res.foilType = FoilType.MODERN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res.foilType = FoilType.NOT_SUPPORTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res.foilChanceInBooster = section.getInt("FoilChanceInBooster", 16);
|
||||||
|
res.foilAlwaysInCommonSlot = section.getBoolean("FoilAlwaysInCommonSlot", false);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FilenameFilter getFileFilter() {
|
||||||
|
return TXT_FILE_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final FilenameFilter TXT_FILE_FILTER = new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(final File dir, final String name) {
|
||||||
|
return name.endsWith(".txt");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Collection extends StorageBase<CardEdition> {
|
||||||
|
|
||||||
|
private final Map<String, CardEdition> aliasToEdition = new TreeMap<String, CardEdition>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
|
public Collection(IItemReader<CardEdition> reader) {
|
||||||
|
super("Card editions", reader);
|
||||||
|
|
||||||
|
for (CardEdition ee : this) {
|
||||||
|
String alias = ee.getAlias();
|
||||||
|
if (null != alias) {
|
||||||
|
aliasToEdition.put(alias, ee);
|
||||||
|
}
|
||||||
|
aliasToEdition.put(ee.getCode2(), ee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a sets by code. It will search first by three letter codes, then by aliases and two-letter codes.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* the code
|
||||||
|
* @return the sets the by code
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CardEdition get(final String code) {
|
||||||
|
CardEdition baseResult = super.get(code);
|
||||||
|
return baseResult == null ? aliasToEdition.get(code) : baseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Iterable<CardEdition> getOrderedEditions() {
|
||||||
|
List<CardEdition> res = Lists.newArrayList(this);
|
||||||
|
Collections.sort(res);
|
||||||
|
Collections.reverse(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sets by code or throw.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* the code
|
||||||
|
* @return the sets the by code or throw
|
||||||
|
*/
|
||||||
|
public CardEdition getEditionByCodeOrThrow(final String code) {
|
||||||
|
final CardEdition set = this.get(code);
|
||||||
|
if (null == set) {
|
||||||
|
throw new RuntimeException(String.format("Edition with code '%s' not found", code));
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
// used by image generating code
|
||||||
|
/**
|
||||||
|
* Gets the code2 by code.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* the code
|
||||||
|
* @return the code2 by code
|
||||||
|
*/
|
||||||
|
public String getCode2ByCode(final String code) {
|
||||||
|
final CardEdition set = this.get(code);
|
||||||
|
return set == null ? "" : set.getCode2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Function<String, CardEdition> FN_EDITION_BY_CODE = new Function<String, CardEdition>() {
|
||||||
|
@Override
|
||||||
|
public CardEdition apply(String code) {
|
||||||
|
return Collection.this.get(code);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public IItemReader<SealedProduct.Template> getBoosterGenerator() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return new StorageReaderBase<SealedProduct.Template>(null) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, SealedProduct.Template> readAll() {
|
||||||
|
Map<String, SealedProduct.Template> map = new TreeMap<String, SealedProduct.Template>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
for(CardEdition ce : Collection.this) {
|
||||||
|
map.put(ce.getCode(), ce.getBoosterTemplate());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemKey(SealedProduct.Template item) {
|
||||||
|
return item.getEdition();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardEdition getEarliestEditionWithAllCards(CardPool cards) {
|
||||||
|
Set<String> minEditions = new HashSet<String>();
|
||||||
|
|
||||||
|
SetPreference strictness = SetPreference.EarliestCoreExp;
|
||||||
|
|
||||||
|
for(Entry<PaperCard, Integer> k : cards) {
|
||||||
|
PaperCard cp = StaticData.instance().getCommonCards().getCardFromEdition(k.getKey().getName(), strictness);
|
||||||
|
if( cp == null && strictness == SetPreference.EarliestCoreExp) {
|
||||||
|
strictness = SetPreference.Earliest; // card is not found in core and expansions only (probably something CMD or C13)
|
||||||
|
cp = StaticData.instance().getCommonCards().getCardFromEdition(k.getKey().getName(), strictness);
|
||||||
|
}
|
||||||
|
if ( cp == null )
|
||||||
|
cp = k.getKey(); // it's unlikely, this code will ever run
|
||||||
|
|
||||||
|
minEditions.add(cp.getEdition());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(CardEdition ed : getOrderedEditions()) {
|
||||||
|
if(minEditions.contains(ed.getCode()))
|
||||||
|
return ed;
|
||||||
|
}
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class Predicates {
|
||||||
|
|
||||||
|
/** The Constant canMakeBooster. */
|
||||||
|
public static final Predicate<CardEdition> CAN_MAKE_BOOSTER = new CanMakeBooster();
|
||||||
|
|
||||||
|
private static class CanMakeBooster implements Predicate<CardEdition> {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardEdition subject) {
|
||||||
|
return subject.hasBoosterTemplate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public final static CardEdition getRandomSetWithAllBasicLands(Iterable<CardEdition> allEditions) {
|
||||||
|
return Aggregates.random(Iterables.filter(allEditions, hasBasicLands));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Predicate<CardEdition> HAS_TOURNAMENT_PACK = new CanMakeStarter();
|
||||||
|
private static class CanMakeStarter implements Predicate<CardEdition> {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardEdition subject) {
|
||||||
|
return StaticData.instance().getTournamentPacks().contains(subject.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Predicate<CardEdition> HAS_FAT_PACK = new CanMakeFatPack();
|
||||||
|
private static class CanMakeFatPack implements Predicate<CardEdition> {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardEdition subject) {
|
||||||
|
return StaticData.instance().getFatPacks().contains(subject.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final Predicate<CardEdition> hasBasicLands = new Predicate<CardEdition>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(CardEdition ed) {
|
||||||
|
for(String landName : MagicColor.Constant.BASIC_LANDS) {
|
||||||
|
if (null == StaticData.instance().getCommonCards().getCard(landName, ed.getCode(), 0))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
124
forge-core/src/main/java/forge/card/CardFace.java
Normal file
124
forge-core/src/main/java/forge/card/CardFace.java
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
|
||||||
|
//
|
||||||
|
// DO NOT AUTOFORMAT / CHECKSTYLE THIS FILE
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single side or part of a magic card with its original characteristics.
|
||||||
|
* <br><br>
|
||||||
|
* <i>Do not use reference to class except for card parsing.<br>Always use reference to interface type outside of package.</i>
|
||||||
|
*/
|
||||||
|
final class CardFace implements ICardFace {
|
||||||
|
|
||||||
|
public enum FaceSelectionMethod { //
|
||||||
|
USE_ACTIVE_FACE,
|
||||||
|
USE_PRIMARY_FACE,
|
||||||
|
COMBINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<String>());
|
||||||
|
private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<String, String>());
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private CardType type = null;
|
||||||
|
private ManaCost manaCost = ManaCost.NO_COST;
|
||||||
|
private ColorSet color = null;
|
||||||
|
|
||||||
|
private String oracleText = null;
|
||||||
|
private int iPower = -1;
|
||||||
|
private int iToughness = -1;
|
||||||
|
private String power = null;
|
||||||
|
private String toughness = null;
|
||||||
|
private int initialLoyalty = -1;
|
||||||
|
|
||||||
|
private String nonAbilityText = null;
|
||||||
|
private List<String> keywords = null;
|
||||||
|
private List<String> abilities = null;
|
||||||
|
private List<String> staticAbilities = null;
|
||||||
|
private List<String> triggers = null;
|
||||||
|
private List<String> replacements = null;
|
||||||
|
private Map<String, String> variables = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// these implement ICardCharacteristics
|
||||||
|
@Override public String getOracleText() { return oracleText; }
|
||||||
|
@Override public int getIntPower() { return iPower; }
|
||||||
|
@Override public int getIntToughness() { return iToughness; }
|
||||||
|
@Override public String getPower() { return power; }
|
||||||
|
@Override public String getToughness() { return toughness; }
|
||||||
|
@Override public int getInitialLoyalty() { return initialLoyalty; }
|
||||||
|
@Override public String getName() { return this.name; }
|
||||||
|
@Override public CardType getType() { return this.type; }
|
||||||
|
@Override public ManaCost getManaCost() { return this.manaCost; }
|
||||||
|
@Override public ColorSet getColor() { return this.color; }
|
||||||
|
|
||||||
|
// these are raw and unparsed used for Card creation
|
||||||
|
@Override public Iterable<String> getKeywords() { return keywords; }
|
||||||
|
@Override public Iterable<String> getAbilities() { return abilities; }
|
||||||
|
@Override public Iterable<String> getStaticAbilities() { return staticAbilities; }
|
||||||
|
@Override public Iterable<String> getTriggers() { return triggers; }
|
||||||
|
@Override public Iterable<String> getReplacements() { return replacements; }
|
||||||
|
@Override public String getNonAbilityText() { return nonAbilityText; }
|
||||||
|
@Override public Iterable<Entry<String, String>> getVariables() { return variables.entrySet(); }
|
||||||
|
|
||||||
|
public CardFace(String name0) {
|
||||||
|
this.name = name0;
|
||||||
|
if ( StringUtils.isBlank(name0) )
|
||||||
|
throw new RuntimeException("Card name is empty");
|
||||||
|
}
|
||||||
|
// Here come setters to allow parser supply values
|
||||||
|
void setType(CardType type0) { this.type = type0; }
|
||||||
|
void setManaCost(ManaCost manaCost0) { this.manaCost = manaCost0; }
|
||||||
|
void setColor(ColorSet color0) { this.color = color0; }
|
||||||
|
void setOracleText(String text) { this.oracleText = text; }
|
||||||
|
void setInitialLoyalty(int value) { this.initialLoyalty = value; }
|
||||||
|
|
||||||
|
void setPtText(String value) {
|
||||||
|
final int slashPos = value.indexOf('/');
|
||||||
|
if (slashPos == -1) {
|
||||||
|
throw new RuntimeException(String.format("Creature '%s' has bad p/t stats", this.getName()));
|
||||||
|
}
|
||||||
|
this.power = value.substring(0, slashPos);
|
||||||
|
this.toughness = value.substring(slashPos + 1);
|
||||||
|
this.iPower = StringUtils.isNumeric(this.power) ? Integer.parseInt(this.power) : 0;
|
||||||
|
this.iToughness = StringUtils.isNumeric(this.toughness) ? Integer.parseInt(this.toughness) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw fields used for Card creation
|
||||||
|
void setNonAbilityText(String value) { this.nonAbilityText = value; }
|
||||||
|
void addKeyword(String value) { if (null == this.keywords) { this.keywords = new ArrayList<String>(); } this.keywords.add(value); }
|
||||||
|
void addAbility(String value) { if (null == this.abilities) { this.abilities = new ArrayList<String>(); } this.abilities.add(value);}
|
||||||
|
void addTrigger(String value) { if (null == this.triggers) { this.triggers = new ArrayList<String>(); } this.triggers.add(value);}
|
||||||
|
void addStaticAbility(String value) { if (null == this.staticAbilities) { this.staticAbilities = new ArrayList<String>(); } this.staticAbilities.add(value);}
|
||||||
|
void addReplacementEffect(String value) { if (null == this.replacements) { this.replacements = new ArrayList<String>(); } this.replacements.add(value);}
|
||||||
|
void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); }
|
||||||
|
|
||||||
|
|
||||||
|
void assignMissingFields() { // Most scripts do not specify color explicitly
|
||||||
|
if ( null == oracleText ) { System.err.println(name + " has no Oracle text."); oracleText = ""; }
|
||||||
|
if ( manaCost == null && color == null ) System.err.println(name + " has neither ManaCost nor Color");
|
||||||
|
if ( color == null ) color = ColorSet.fromManaCost(manaCost);
|
||||||
|
|
||||||
|
if ( keywords == null ) keywords = emptyList;
|
||||||
|
if ( abilities == null ) abilities = emptyList;
|
||||||
|
if ( staticAbilities == null ) staticAbilities = emptyList;
|
||||||
|
if ( triggers == null ) triggers = emptyList;
|
||||||
|
if ( replacements == null ) replacements = emptyList;
|
||||||
|
if ( variables == null ) variables = emptyMap;
|
||||||
|
if ( null == nonAbilityText ) nonAbilityText = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
506
forge-core/src/main/java/forge/card/CardRules.java
Normal file
506
forge-core/src/main/java/forge/card/CardRules.java
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.card.mana.IParserManaCost;
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
import forge.card.mana.ManaCostShard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of methods containing full
|
||||||
|
* meta and gameplay properties of a card.
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public final class CardRules implements ICardCharacteristics {
|
||||||
|
private CardSplitType splitType;
|
||||||
|
private ICardFace mainPart;
|
||||||
|
private ICardFace otherPart;
|
||||||
|
private CardAiHints aiHints;
|
||||||
|
private ColorSet colorIdentity;
|
||||||
|
|
||||||
|
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
|
||||||
|
splitType = altMode;
|
||||||
|
mainPart = faces[0];
|
||||||
|
otherPart = faces[1];
|
||||||
|
aiHints = cah;
|
||||||
|
|
||||||
|
//calculate color identity
|
||||||
|
byte colMask = calculateColorIdentity(mainPart);
|
||||||
|
|
||||||
|
if (otherPart != null) {
|
||||||
|
colMask |= calculateColorIdentity(otherPart);
|
||||||
|
}
|
||||||
|
colorIdentity = ColorSet.fromMask(colMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reinitializeFromRules(CardRules newRules) {
|
||||||
|
if(!newRules.getName().equals(this.getName()))
|
||||||
|
throw new UnsupportedOperationException("You cannot rename the card using the same CardRules object");
|
||||||
|
|
||||||
|
splitType = newRules.splitType;
|
||||||
|
mainPart = newRules.mainPart;
|
||||||
|
otherPart = newRules.otherPart;
|
||||||
|
aiHints = newRules.aiHints;
|
||||||
|
colorIdentity = newRules.colorIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte calculateColorIdentity(ICardFace face) {
|
||||||
|
byte res = face.getColor().getColor();
|
||||||
|
boolean isReminder = false;
|
||||||
|
boolean isSymbol = false;
|
||||||
|
String oracleText = face.getOracleText();
|
||||||
|
int len = oracleText.length();
|
||||||
|
for(int i = 0; i < len; i++) {
|
||||||
|
char c = oracleText.charAt(i); // This is to avoid needless allocations performed by toCharArray()
|
||||||
|
switch(c) {
|
||||||
|
case('('): isReminder = i > 0; break; // if oracle has only reminder, consider it valid rules (basic and true lands need this)
|
||||||
|
case(')'): isReminder = false; break;
|
||||||
|
case('{'): isSymbol = true; break;
|
||||||
|
case('}'): isSymbol = false; break;
|
||||||
|
default:
|
||||||
|
if(isSymbol && !isReminder) {
|
||||||
|
switch(c) {
|
||||||
|
case('W'): res |= MagicColor.WHITE; break;
|
||||||
|
case('U'): res |= MagicColor.BLUE; break;
|
||||||
|
case('B'): res |= MagicColor.BLACK; break;
|
||||||
|
case('R'): res |= MagicColor.RED; break;
|
||||||
|
case('G'): res |= MagicColor.GREEN; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVariant() {
|
||||||
|
CardType t = getType();
|
||||||
|
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardSplitType getSplitType() {
|
||||||
|
return splitType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICardFace getMainPart() {
|
||||||
|
return mainPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICardFace getOtherPart() {
|
||||||
|
return otherPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
switch(splitType.getAggregationMethod()) {
|
||||||
|
case COMBINE:
|
||||||
|
return mainPart.getName() + " // " + otherPart.getName();
|
||||||
|
default:
|
||||||
|
return mainPart.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardAiHints getAiHints() {
|
||||||
|
return aiHints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CardType getType() {
|
||||||
|
switch(splitType.getAggregationMethod()) {
|
||||||
|
case COMBINE: // no cards currently have different types
|
||||||
|
return CardType.combine(mainPart.getType(), otherPart.getType());
|
||||||
|
default:
|
||||||
|
return mainPart.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ManaCost getManaCost() {
|
||||||
|
switch(splitType.getAggregationMethod()) {
|
||||||
|
case COMBINE:
|
||||||
|
return ManaCost.combine(mainPart.getManaCost(), otherPart.getManaCost());
|
||||||
|
default:
|
||||||
|
return mainPart.getManaCost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSet getColor() {
|
||||||
|
switch(splitType.getAggregationMethod()) {
|
||||||
|
case COMBINE:
|
||||||
|
return ColorSet.fromMask(mainPart.getColor().getColor() | otherPart.getColor().getColor());
|
||||||
|
default:
|
||||||
|
return mainPart.getColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canCastFace(ICardFace face, byte colorCode) {
|
||||||
|
if (face.getManaCost().isNoCost()) {
|
||||||
|
//if card face has no cost, assume castable only by mana of its defined color
|
||||||
|
return face.getColor().hasNoColorsExcept(colorCode);
|
||||||
|
}
|
||||||
|
return face.getManaCost().canBePaidWithAvaliable(colorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canCastWithAvailable(byte colorCode) {
|
||||||
|
switch(splitType.getAggregationMethod()) {
|
||||||
|
case COMBINE:
|
||||||
|
return canCastFace(mainPart, colorCode) || canCastFace(otherPart, colorCode);
|
||||||
|
default:
|
||||||
|
return canCastFace(mainPart, colorCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int getIntPower() { return mainPart.getIntPower(); }
|
||||||
|
@Override public int getIntToughness() { return mainPart.getIntToughness(); }
|
||||||
|
@Override public String getPower() { return mainPart.getPower(); }
|
||||||
|
@Override public String getToughness() { return mainPart.getToughness(); }
|
||||||
|
@Override public int getInitialLoyalty() { return mainPart.getInitialLoyalty(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOracleText() {
|
||||||
|
switch(splitType.getAggregationMethod()) {
|
||||||
|
case COMBINE:
|
||||||
|
return mainPart.getOracleText() + "\r\n\r\n" + otherPart.getOracleText();
|
||||||
|
default:
|
||||||
|
return mainPart.getOracleText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Set<String> getSets() { return this.setsPrinted.keySet(); }
|
||||||
|
// public CardInSet getEditionInfo(final String setCode) {
|
||||||
|
// final CardInSet result = this.setsPrinted.get(setCode);
|
||||||
|
// return result; // if returns null, String.format("Card '%s' was never printed in set '%s'", this.getName(), setCode);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// vanguard card fields, they don't use sides.
|
||||||
|
private int deltaHand;
|
||||||
|
private int deltaLife;
|
||||||
|
|
||||||
|
public int getHand() { return deltaHand; }
|
||||||
|
public int getLife() { return deltaLife; }
|
||||||
|
public void setVanguardProperties(String pt) {
|
||||||
|
final int slashPos = pt == null ? -1 : pt.indexOf('/');
|
||||||
|
if (slashPos == -1) {
|
||||||
|
throw new RuntimeException(String.format("Vanguard '%s' has bad hand/life stats", this.getName()));
|
||||||
|
}
|
||||||
|
this.deltaHand = Integer.parseInt(pt.substring(0, slashPos).replace("+", ""));
|
||||||
|
this.deltaLife = Integer.parseInt(pt.substring(slashPos+1).replace("+", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downloadable image
|
||||||
|
private String dlUrl;
|
||||||
|
private String dlUrlOtherSide;
|
||||||
|
public String getPictureUrl(boolean backface ) { return backface ? dlUrlOtherSide : dlUrl; }
|
||||||
|
public void setDlUrls(String[] dlUrls) { this.dlUrl = dlUrls[0]; this.dlUrlOtherSide = dlUrls[1]; }
|
||||||
|
|
||||||
|
public final List<String> getReplacements() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<String> getTriggers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<String> getStaticAbilities() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<String> getAbilities() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorSet getColorIdentity() {
|
||||||
|
return colorIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Instantiates class, reads a card. For batch operations better create you own reader instance. */
|
||||||
|
public static CardRules fromScript(Iterable<String> script) {
|
||||||
|
Reader crr = new Reader();
|
||||||
|
for(String line : script) {
|
||||||
|
crr.parseLine(line);
|
||||||
|
}
|
||||||
|
return crr.getCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads cardname.txt
|
||||||
|
public static class Reader {
|
||||||
|
// fields to build
|
||||||
|
private CardFace[] faces = new CardFace[] { null, null };
|
||||||
|
private String[] pictureUrl = new String[] { null, null };
|
||||||
|
private int curFace = 0;
|
||||||
|
private CardSplitType altMode = CardSplitType.None;
|
||||||
|
private String handLife = null;
|
||||||
|
|
||||||
|
// fields to build CardAiHints
|
||||||
|
private boolean removedFromAIDecks = false;
|
||||||
|
private boolean removedFromRandomDecks = false;
|
||||||
|
private DeckHints hints = null;
|
||||||
|
private DeckHints needs = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all fields to parse next card (to avoid allocating new CardRulesReader N times)
|
||||||
|
*/
|
||||||
|
public final void reset() {
|
||||||
|
this.curFace = 0;
|
||||||
|
this.faces[0] = null;
|
||||||
|
this.faces[1] = null;
|
||||||
|
this.pictureUrl[0] = null;
|
||||||
|
this.pictureUrl[1] = null;
|
||||||
|
|
||||||
|
this.handLife = null;
|
||||||
|
this.altMode = CardSplitType.None;
|
||||||
|
|
||||||
|
this.removedFromAIDecks = false;
|
||||||
|
this.removedFromRandomDecks = false;
|
||||||
|
this.needs = null;
|
||||||
|
this.hints = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the card.
|
||||||
|
*
|
||||||
|
* @return the card
|
||||||
|
*/
|
||||||
|
public final CardRules getCard() {
|
||||||
|
CardAiHints cah = new CardAiHints(removedFromAIDecks, removedFromRandomDecks, hints, needs );
|
||||||
|
faces[0].assignMissingFields();
|
||||||
|
if (null != faces[1]) faces[1].assignMissingFields();
|
||||||
|
final CardRules result = new CardRules(faces, altMode, cah);
|
||||||
|
result.setDlUrls(pictureUrl);
|
||||||
|
if (StringUtils.isNotBlank(handLife))
|
||||||
|
result.setVanguardProperties(handLife);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final CardRules readCard(final Iterable<String> script) {
|
||||||
|
this.reset();
|
||||||
|
for (String line : script) {
|
||||||
|
if (line.isEmpty() || line.charAt(0) == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.parseLine(line);
|
||||||
|
}
|
||||||
|
return this.getCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the line.
|
||||||
|
*
|
||||||
|
* @param line
|
||||||
|
* the line
|
||||||
|
*/
|
||||||
|
public final void parseLine(final String line) {
|
||||||
|
int colonPos = line.indexOf(':');
|
||||||
|
String key = colonPos > 0 ? line.substring(0, colonPos) : line;
|
||||||
|
String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null;
|
||||||
|
|
||||||
|
switch(key.charAt(0)) {
|
||||||
|
case 'A':
|
||||||
|
if ("A".equals(key))
|
||||||
|
this.faces[curFace].addAbility(value);
|
||||||
|
else if ("AlternateMode".equals(key)) {
|
||||||
|
//System.out.println(faces[curFace].getName());
|
||||||
|
this.altMode = CardSplitType.smartValueOf(value);
|
||||||
|
} else if ("ALTERNATE".equals(key)) {
|
||||||
|
this.curFace = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
if ("Colors".equals(key)) {
|
||||||
|
// This is forge.card.CardColor not forge.CardColor.
|
||||||
|
// Why do we have two classes with the same name?
|
||||||
|
ColorSet newCol = ColorSet.fromNames(value.split(","));
|
||||||
|
this.faces[this.curFace].setColor(newCol);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
if ("DeckHints".equals(key)) {
|
||||||
|
hints = new DeckHints(value);
|
||||||
|
} else if ("DeckNeeds".equals(key)) {
|
||||||
|
needs = new DeckHints(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'H':
|
||||||
|
if ("HandLifeModifier".equals(key)) {
|
||||||
|
handLife = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'K':
|
||||||
|
if ("K".equals(key)) {
|
||||||
|
this.faces[this.curFace].addKeyword(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'L':
|
||||||
|
if ("Loyalty".equals(key)) {
|
||||||
|
this.faces[this.curFace].setInitialLoyalty(Integer.valueOf(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
if ("ManaCost".equals(key)) {
|
||||||
|
this.faces[this.curFace].setManaCost("no cost".equals(value) ? ManaCost.NO_COST
|
||||||
|
: new ManaCost(new ManaCostParser(value)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
if ("Name".equals(key)) {
|
||||||
|
this.faces[this.curFace] = new CardFace(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'O':
|
||||||
|
if ("Oracle".equals(key)) {
|
||||||
|
this.faces[this.curFace].setOracleText(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
if ("PT".equals(key)) {
|
||||||
|
this.faces[this.curFace].setPtText(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'R':
|
||||||
|
if ("R".equals(key)) {
|
||||||
|
this.faces[this.curFace].addReplacementEffect(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
if ("S".equals(key)) {
|
||||||
|
this.faces[this.curFace].addStaticAbility(value);
|
||||||
|
} else if ( "SVar".equals(key) ) {
|
||||||
|
if ( null == value ) throw new IllegalArgumentException("SVar has no variable name");
|
||||||
|
|
||||||
|
colonPos = value.indexOf(':');
|
||||||
|
String variable = colonPos > 0 ? value.substring(0, colonPos) : value;
|
||||||
|
value = colonPos > 0 ? value.substring(1+colonPos) : null;
|
||||||
|
|
||||||
|
if ( "RemAIDeck".equals(variable) ) {
|
||||||
|
this.removedFromAIDecks = "True".equalsIgnoreCase(value);
|
||||||
|
} else if ( "RemRandomDeck".equals(variable) ) {
|
||||||
|
this.removedFromRandomDecks = "True".equalsIgnoreCase(value);
|
||||||
|
} else if ( "Picture".equals(variable) ) {
|
||||||
|
this.pictureUrl[this.curFace] = value;
|
||||||
|
} else if ( "Rarity".equals(variable) ) {
|
||||||
|
// discard that, they should supply it in SetInfo
|
||||||
|
} else
|
||||||
|
this.faces[curFace].addSVar(variable, value);
|
||||||
|
} else if ("SetInfo".equals(key)) {
|
||||||
|
// deprecated
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
if ("T".equals(key)) {
|
||||||
|
this.faces[this.curFace].addTrigger(value);
|
||||||
|
} else if ("Types".equals(key)) {
|
||||||
|
this.faces[this.curFace].setType(CardType.parse(value));
|
||||||
|
} else if ("Text".equals(key) && !"no text".equals(value) && StringUtils.isNotBlank(value)) {
|
||||||
|
this.faces[this.curFace].setNonAbilityText(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class ParserCardnameTxtManaCost.
|
||||||
|
*/
|
||||||
|
private static class ManaCostParser implements IParserManaCost {
|
||||||
|
private final StringTokenizer st;
|
||||||
|
private int colorlessCost;
|
||||||
|
|
||||||
|
public ManaCostParser(final String cost) {
|
||||||
|
st = new StringTokenizer(cost, " ");
|
||||||
|
this.colorlessCost = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getTotalColorlessCost() {
|
||||||
|
if (this.hasNext()) {
|
||||||
|
throw new RuntimeException("Colorless cost should be obtained after iteration is complete");
|
||||||
|
}
|
||||||
|
return this.colorlessCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.util.Iterator#hasNext()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final boolean hasNext() {
|
||||||
|
return st.hasMoreTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.util.Iterator#next()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final ManaCostShard next() {
|
||||||
|
final String unparsed = st.nextToken();
|
||||||
|
// System.out.println(unparsed);
|
||||||
|
try {
|
||||||
|
int iVal = Integer.parseInt(unparsed);
|
||||||
|
this.colorlessCost += iVal;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (NumberFormatException nex) { }
|
||||||
|
|
||||||
|
return ManaCostShard.parseNonGeneric(unparsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.util.Iterator#remove()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
} // unsuported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CardRules getUnsupportedCardNamed(String name) {
|
||||||
|
CardAiHints cah = new CardAiHints(true, true, null, null);
|
||||||
|
CardFace[] faces = { new CardFace(name), null};
|
||||||
|
faces[0].setColor(ColorSet.fromMask(0));
|
||||||
|
faces[0].setType(CardType.parse(""));
|
||||||
|
faces[0].setOracleText("This card is not supported by Forge. Whenever you start a game with this card, it will be bugged.");
|
||||||
|
faces[0].setNonAbilityText("This card is not supported by Forge.\nWhenever you start a game with this card, it will be bugged.");
|
||||||
|
faces[0].assignMissingFields();
|
||||||
|
final CardRules result = new CardRules(faces, CardSplitType.None, cah);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import java.util.List;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.util.ComparableOp;
|
import forge.util.ComparableOp;
|
||||||
import forge.util.PredicateString;
|
import forge.util.PredicateString;
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ public final class CardRulesPredicates {
|
|||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
|
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
|
||||||
try {
|
try {
|
||||||
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardCoreType.class, what));
|
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardType.CoreType.class, what));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
return com.google.common.base.Predicates.alwaysFalse();
|
return com.google.common.base.Predicates.alwaysFalse();
|
||||||
}
|
}
|
||||||
@@ -185,7 +186,7 @@ public final class CardRulesPredicates {
|
|||||||
* the type
|
* the type
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> coreType(final boolean isEqual, final CardCoreType type) {
|
public static Predicate<CardRules> coreType(final boolean isEqual, final CardType.CoreType type) {
|
||||||
return new PredicateCoreType(type, isEqual);
|
return new PredicateCoreType(type, isEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +201,7 @@ public final class CardRulesPredicates {
|
|||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
|
public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
|
||||||
try {
|
try {
|
||||||
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardSuperType.class, what));
|
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardType.SuperType.class, what));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
return com.google.common.base.Predicates.alwaysFalse();
|
return com.google.common.base.Predicates.alwaysFalse();
|
||||||
}
|
}
|
||||||
@@ -215,7 +216,7 @@ public final class CardRulesPredicates {
|
|||||||
* the type
|
* the type
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> superType(final boolean isEqual, final CardSuperType type) {
|
public static Predicate<CardRules> superType(final boolean isEqual, final CardType.SuperType type) {
|
||||||
return new PredicateSuperType(type, isEqual);
|
return new PredicateSuperType(type, isEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ public final class CardRulesPredicates {
|
|||||||
* Checks for color.
|
* Checks for color.
|
||||||
*
|
*
|
||||||
* @param thatColor
|
* @param thatColor
|
||||||
* the that color
|
* color to check
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> hasColor(final byte thatColor) {
|
public static Predicate<CardRules> hasColor(final byte thatColor) {
|
||||||
@@ -235,13 +236,24 @@ public final class CardRulesPredicates {
|
|||||||
* Checks if is color.
|
* Checks if is color.
|
||||||
*
|
*
|
||||||
* @param thatColor
|
* @param thatColor
|
||||||
* the that color
|
* color to check
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> isColor(final byte thatColor) {
|
public static Predicate<CardRules> isColor(final byte thatColor) {
|
||||||
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
|
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if card can be cast with unlimited mana of given color set.
|
||||||
|
*
|
||||||
|
* @param thatColor
|
||||||
|
* color to check
|
||||||
|
* @return the predicate
|
||||||
|
*/
|
||||||
|
public static Predicate<CardRules> canCastWithAvailable(final byte thatColor) {
|
||||||
|
return new LeafColor(LeafColor.ColorOperator.CanCast, thatColor);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if is exactly that color.
|
* Checks if is exactly that color.
|
||||||
*
|
*
|
||||||
@@ -310,7 +322,7 @@ public final class CardRulesPredicates {
|
|||||||
|
|
||||||
private static class LeafColor implements Predicate<CardRules> {
|
private static class LeafColor implements Predicate<CardRules> {
|
||||||
public enum ColorOperator {
|
public enum ColorOperator {
|
||||||
CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals
|
CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals, CanCast
|
||||||
}
|
}
|
||||||
|
|
||||||
private final LeafColor.ColorOperator op;
|
private final LeafColor.ColorOperator op;
|
||||||
@@ -337,6 +349,8 @@ public final class CardRulesPredicates {
|
|||||||
return subject.getColor().hasAllColors(this.color);
|
return subject.getColor().hasAllColors(this.color);
|
||||||
case HasAnyOf:
|
case HasAnyOf:
|
||||||
return subject.getColor().hasAnyColor(this.color);
|
return subject.getColor().hasAnyColor(this.color);
|
||||||
|
case CanCast:
|
||||||
|
return subject.canCastWithAvailable(this.color);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -396,7 +410,7 @@ public final class CardRulesPredicates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class PredicateCoreType implements Predicate<CardRules> {
|
private static class PredicateCoreType implements Predicate<CardRules> {
|
||||||
private final CardCoreType operand;
|
private final CardType.CoreType operand;
|
||||||
private final boolean shouldBeEqual;
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -407,14 +421,14 @@ public final class CardRulesPredicates {
|
|||||||
return this.shouldBeEqual == card.getType().typeContains(this.operand);
|
return this.shouldBeEqual == card.getType().typeContains(this.operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PredicateCoreType(final CardCoreType type, final boolean wantEqual) {
|
public PredicateCoreType(final CardType.CoreType type, final boolean wantEqual) {
|
||||||
this.operand = type;
|
this.operand = type;
|
||||||
this.shouldBeEqual = wantEqual;
|
this.shouldBeEqual = wantEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PredicateSuperType implements Predicate<CardRules> {
|
private static class PredicateSuperType implements Predicate<CardRules> {
|
||||||
private final CardSuperType operand;
|
private final CardType.SuperType operand;
|
||||||
private final boolean shouldBeEqual;
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -422,7 +436,7 @@ public final class CardRulesPredicates {
|
|||||||
return this.shouldBeEqual == card.getType().superTypeContains(this.operand);
|
return this.shouldBeEqual == card.getType().superTypeContains(this.operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PredicateSuperType(final CardSuperType type, final boolean wantEqual) {
|
public PredicateSuperType(final CardType.SuperType type, final boolean wantEqual) {
|
||||||
this.operand = type;
|
this.operand = type;
|
||||||
this.shouldBeEqual = wantEqual;
|
this.shouldBeEqual = wantEqual;
|
||||||
}
|
}
|
||||||
@@ -448,21 +462,21 @@ public final class CardRulesPredicates {
|
|||||||
|
|
||||||
/** The Constant isCreature. */
|
/** The Constant isCreature. */
|
||||||
public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates
|
public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates
|
||||||
.coreType(true, CardCoreType.Creature);
|
.coreType(true, CardType.CoreType.Creature);
|
||||||
|
|
||||||
public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates
|
public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates
|
||||||
.superType(true, CardSuperType.Legendary);
|
.superType(true, CardType.SuperType.Legendary);
|
||||||
|
|
||||||
/** The Constant isArtifact. */
|
/** The Constant isArtifact. */
|
||||||
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
|
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
|
||||||
.coreType(true, CardCoreType.Artifact);
|
.coreType(true, CardType.CoreType.Artifact);
|
||||||
|
|
||||||
/** The Constant isEquipment. */
|
/** The Constant isEquipment. */
|
||||||
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
|
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
|
||||||
.subType("Equipment");
|
.subType("Equipment");
|
||||||
|
|
||||||
/** The Constant isLand. */
|
/** The Constant isLand. */
|
||||||
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardCoreType.Land);
|
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardType.CoreType.Land);
|
||||||
|
|
||||||
/** The Constant isBasicLand. */
|
/** The Constant isBasicLand. */
|
||||||
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
|
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
|
||||||
@@ -480,31 +494,17 @@ public final class CardRulesPredicates {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The Constant isPlaneswalker. */
|
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
|
||||||
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true,
|
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
|
||||||
CardCoreType.Planeswalker);
|
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardType.CoreType.Sorcery);
|
||||||
|
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardType.CoreType.Enchantment);
|
||||||
/** The Constant isInstant. */
|
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardType.CoreType.Plane);
|
||||||
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardCoreType.Instant);
|
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardType.CoreType.Phenomenon);
|
||||||
|
|
||||||
/** The Constant isSorcery. */
|
|
||||||
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardCoreType.Sorcery);
|
|
||||||
|
|
||||||
/** The Constant isEnchantment. */
|
|
||||||
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardCoreType.Enchantment);
|
|
||||||
|
|
||||||
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardCoreType.Plane);
|
|
||||||
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardCoreType.Phenomenon);
|
|
||||||
public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON);
|
public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON);
|
||||||
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardCoreType.Scheme);
|
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardType.CoreType.Scheme);
|
||||||
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardCoreType.Vanguard);
|
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
||||||
|
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||||
/** The Constant isNonLand. */
|
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
|
||||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardCoreType.Land);
|
|
||||||
|
|
||||||
/** The Constant isNonCreatureSpell. */
|
|
||||||
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(
|
|
||||||
Presets.IS_CREATURE, Presets.IS_LAND));
|
|
||||||
|
|
||||||
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -532,7 +532,8 @@ public final class CardRulesPredicates {
|
|||||||
|
|
||||||
/** The Constant isMulticolor. */
|
/** The Constant isMulticolor. */
|
||||||
public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2);
|
public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2);
|
||||||
|
|
||||||
|
/** The Constant isMonocolor. */
|
||||||
public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1);
|
public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1);
|
||||||
|
|
||||||
/** The Constant colors. */
|
/** The Constant colors. */
|
||||||
36
forge-core/src/main/java/forge/card/CardSplitType.java
Normal file
36
forge-core/src/main/java/forge/card/CardSplitType.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import forge.card.CardFace.FaceSelectionMethod;
|
||||||
|
|
||||||
|
public enum CardSplitType
|
||||||
|
{
|
||||||
|
None(FaceSelectionMethod.USE_PRIMARY_FACE, null),
|
||||||
|
Transform(FaceSelectionMethod.USE_ACTIVE_FACE, CardCharacteristicName.Transformed),
|
||||||
|
Split(FaceSelectionMethod.COMBINE, CardCharacteristicName.RightSplit),
|
||||||
|
Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardCharacteristicName.Flipped),
|
||||||
|
// used by 12 licid creatures to switch type into enchantment aura
|
||||||
|
Licid(FaceSelectionMethod.USE_PRIMARY_FACE, CardCharacteristicName.Licid);
|
||||||
|
|
||||||
|
private CardSplitType(FaceSelectionMethod calcMode, CardCharacteristicName stateName) {
|
||||||
|
method = calcMode;
|
||||||
|
this.changedStateName = stateName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FaceSelectionMethod getAggregationMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FaceSelectionMethod method;
|
||||||
|
private final CardCharacteristicName changedStateName;
|
||||||
|
|
||||||
|
public static CardSplitType smartValueOf(String text) {
|
||||||
|
if ("DoubleFaced".equals(text)) return Transform;
|
||||||
|
// Will throw exceptions here if bad text passed
|
||||||
|
CardSplitType res = CardSplitType.valueOf(text);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardCharacteristicName getChangedStateName() {
|
||||||
|
return changedStateName;
|
||||||
|
}
|
||||||
|
}
|
||||||
405
forge-core/src/main/java/forge/card/CardType.java
Normal file
405
forge-core/src/main/java/forge/card/CardType.java
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Immutable Card type. Can be built only from parsing a string.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: CardType.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class CardType implements Comparable<CardType> {
|
||||||
|
|
||||||
|
public enum CoreType {
|
||||||
|
|
||||||
|
Artifact(true),
|
||||||
|
Creature(true),
|
||||||
|
Enchantment(true),
|
||||||
|
Instant(false),
|
||||||
|
Land(true),
|
||||||
|
Plane(false),
|
||||||
|
Planeswalker(true),
|
||||||
|
Scheme(false),
|
||||||
|
Sorcery(false),
|
||||||
|
Tribal(false),
|
||||||
|
Vanguard(false),
|
||||||
|
Phenomenon(false);
|
||||||
|
|
||||||
|
public final boolean isPermanent;
|
||||||
|
|
||||||
|
private CoreType(final boolean permanent) {
|
||||||
|
isPermanent = permanent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CoreType smartValueOf(final String value) { return smartValueOf(value, true); }
|
||||||
|
public static CoreType smartValueOf(final String value, boolean throwIfNotFound) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String valToCompate = value.trim();
|
||||||
|
for (final CoreType v : CoreType.values()) {
|
||||||
|
if (v.name().equalsIgnoreCase(valToCompate)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (throwIfNotFound)
|
||||||
|
throw new IllegalArgumentException("No element named " + value + " in enum CoreType");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAPermanentType(final String cardType) {
|
||||||
|
CoreType ct = smartValueOf(cardType, false);
|
||||||
|
return ct != null && ct.isPermanent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SuperType {
|
||||||
|
|
||||||
|
/** The Basic. */
|
||||||
|
Basic,
|
||||||
|
/** The Legendary. */
|
||||||
|
Legendary,
|
||||||
|
/** The Snow. */
|
||||||
|
Snow,
|
||||||
|
/** The Ongoing. */
|
||||||
|
Ongoing,
|
||||||
|
/** The World. */
|
||||||
|
World
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final List<String> subType = new ArrayList<String>();
|
||||||
|
private final EnumSet<CardType.CoreType> coreType = EnumSet.noneOf(CardType.CoreType.class);
|
||||||
|
private final EnumSet<CardType.SuperType> superType = EnumSet.noneOf(CardType.SuperType.class);
|
||||||
|
private String calculatedType = null; // since obj is immutable, this is
|
||||||
|
// calc'd once
|
||||||
|
|
||||||
|
// This will be useful for faster parses
|
||||||
|
private static HashMap<String, CardType.CoreType> stringToCoreType = new HashMap<String, CardType.CoreType>();
|
||||||
|
private static HashMap<String, CardType.SuperType> stringToSuperType = new HashMap<String, CardType.SuperType>();
|
||||||
|
static {
|
||||||
|
for (final CardType.SuperType st : CardType.SuperType.values()) {
|
||||||
|
CardType.stringToSuperType.put(st.name(), st);
|
||||||
|
}
|
||||||
|
for (final CardType.CoreType ct : CardType.CoreType.values()) {
|
||||||
|
CardType.stringToCoreType.put(ct.name(), ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardType() { }
|
||||||
|
|
||||||
|
// TODO: Debug this code
|
||||||
|
public static CardType parse(final String typeText) {
|
||||||
|
// Most types and subtypes, except "Serra's Realm" and
|
||||||
|
// "Bolas's Meditation Realm" consist of only one word
|
||||||
|
final char space = ' ';
|
||||||
|
final CardType result = new CardType();
|
||||||
|
|
||||||
|
int iTypeStart = 0;
|
||||||
|
int iSpace = typeText.indexOf(space);
|
||||||
|
boolean hasMoreTypes = typeText.length() > 0;
|
||||||
|
while (hasMoreTypes) {
|
||||||
|
final String type = typeText.substring(iTypeStart, iSpace == -1 ? typeText.length() : iSpace);
|
||||||
|
hasMoreTypes = iSpace != -1;
|
||||||
|
if (!CardType.isMultiwordType(type) || !hasMoreTypes) {
|
||||||
|
iTypeStart = iSpace + 1;
|
||||||
|
result.parseAndAdd(type);
|
||||||
|
}
|
||||||
|
iSpace = typeText.indexOf(space, iSpace + 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CardType combine(final CardType a, final CardType b) {
|
||||||
|
CardType result = new CardType();
|
||||||
|
result.superType.addAll(a.superType);
|
||||||
|
result.superType.addAll(b.superType);
|
||||||
|
result.coreType.addAll(a.coreType);
|
||||||
|
result.coreType.addAll(b.coreType);
|
||||||
|
result.subType.addAll(a.subType);
|
||||||
|
result.subType.addAll(b.subType);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMultiwordType(final String type) {
|
||||||
|
final String[] multiWordTypes = { "Serra's Realm", "Bolas's Meditation Realm" };
|
||||||
|
// no need to loop for only 2 exceptions!
|
||||||
|
if (multiWordTypes[0].startsWith(type) && !multiWordTypes[0].equals(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (multiWordTypes[1].startsWith(type) && !multiWordTypes[1].equals(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseAndAdd(final String type) {
|
||||||
|
if ("-".equals(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CardType.CoreType ct = CardType.stringToCoreType.get(type);
|
||||||
|
if (ct != null) {
|
||||||
|
this.coreType.add(ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CardType.SuperType st = CardType.stringToSuperType.get(type);
|
||||||
|
if (st != null) {
|
||||||
|
this.superType.add(st);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not recognized by super- and core- this must be subtype
|
||||||
|
this.subType.add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean subTypeContains(final String operand) {
|
||||||
|
return this.subType.contains(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean typeContains(final CardType.CoreType operand) {
|
||||||
|
return this.coreType.contains(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean superTypeContains(final CardType.SuperType operand) {
|
||||||
|
return this.superType.contains(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreature() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlaneswalker() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Planeswalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLand() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Land);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isArtifact() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInstant() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSorcery() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Sorcery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVanguard() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Vanguard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isScheme() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnchantment() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Enchantment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBasic() {
|
||||||
|
return this.superType.contains(CardType.SuperType.Basic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLegendary() {
|
||||||
|
return this.superType.contains(CardType.SuperType.Legendary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBasicLand() {
|
||||||
|
return this.isBasic() && this.isLand();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (null == this.calculatedType) {
|
||||||
|
this.calculatedType = this.toStringImpl();
|
||||||
|
}
|
||||||
|
return this.calculatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toStringImpl() {
|
||||||
|
if (this.subType.isEmpty()) {
|
||||||
|
return StringUtils.join(this.getTypesBeforeDash(), ' ');
|
||||||
|
} else {
|
||||||
|
return String.format("%s - %s", StringUtils.join(this.getTypesBeforeDash(), ' '), StringUtils.join(this.subType, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getTypesBeforeDash() {
|
||||||
|
final ArrayList<String> types = new ArrayList<String>();
|
||||||
|
for (final CardType.SuperType st : this.superType) {
|
||||||
|
types.add(st.name());
|
||||||
|
}
|
||||||
|
for (final CardType.CoreType ct : this.coreType) {
|
||||||
|
types.add(ct.name());
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CardType o) {
|
||||||
|
return this.toString().compareTo(o.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSubTypes() {
|
||||||
|
return this.subType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sharesSubTypeWith(CardType ctOther) {
|
||||||
|
for (String t : ctOther.getSubTypes()) {
|
||||||
|
if (this.subTypeContains(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlane() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPhenomenon() {
|
||||||
|
return this.coreType.contains(CardType.CoreType.Phenomenon);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Interface CardTypes.
|
||||||
|
*/
|
||||||
|
public static class Constant {
|
||||||
|
|
||||||
|
/** The loaded. */
|
||||||
|
public static final boolean[] LOADED = { false };
|
||||||
|
|
||||||
|
/** The card types. */
|
||||||
|
public static final List<String> CARD_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The super types. */
|
||||||
|
public static final List<String> SUPER_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The basic types. */
|
||||||
|
public static final List<String> BASIC_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The land types. */
|
||||||
|
public static final List<String> LAND_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The creature types. */
|
||||||
|
public static final List<String> CREATURE_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The instant types. */
|
||||||
|
public static final List<String> INSTANT_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The sorcery types. */
|
||||||
|
public static final List<String> SORCERY_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The enchantment types. */
|
||||||
|
public static final List<String> ENCHANTMENT_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The artifact types. */
|
||||||
|
public static final List<String> ARTIFACT_TYPES = new ArrayList<String>();
|
||||||
|
|
||||||
|
/** The walker types. */
|
||||||
|
public static final List<String> WALKER_TYPES = new ArrayList<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// Utility methods
|
||||||
|
public static boolean isACardType(final String cardType) {
|
||||||
|
return CardType.getAllCardTypes().contains(cardType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> getAllCardTypes() {
|
||||||
|
final ArrayList<String> types = new ArrayList<String>();
|
||||||
|
|
||||||
|
// types.addAll(getCardTypes());
|
||||||
|
types.addAll(Constant.CARD_TYPES);
|
||||||
|
|
||||||
|
// not currently used by Forge
|
||||||
|
types.add("Plane");
|
||||||
|
types.add("Scheme");
|
||||||
|
types.add("Vanguard");
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> getBasicTypes() {
|
||||||
|
final ArrayList<String> types = new ArrayList<String>();
|
||||||
|
|
||||||
|
types.addAll(Constant.BASIC_TYPES);
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> getLandTypes() {
|
||||||
|
final ArrayList<String> types = new ArrayList<String>();
|
||||||
|
|
||||||
|
types.addAll(Constant.BASIC_TYPES);
|
||||||
|
types.addAll(Constant.LAND_TYPES);
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> getCreatureTypes() {
|
||||||
|
final ArrayList<String> types = new ArrayList<String>();
|
||||||
|
|
||||||
|
types.addAll(Constant.CREATURE_TYPES);
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isASuperType(final String cardType) {
|
||||||
|
return (Constant.SUPER_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isASubType(final String cardType) {
|
||||||
|
return (!CardType.isASuperType(cardType) && !CardType.isACardType(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isACreatureType(final String cardType) {
|
||||||
|
return (Constant.CREATURE_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isALandType(final String cardType) {
|
||||||
|
return (Constant.LAND_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAPlaneswalkerType(final String cardType) {
|
||||||
|
return (Constant.WALKER_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isABasicLandType(final String cardType) {
|
||||||
|
return (Constant.BASIC_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package forge.card;
|
package forge.card;
|
||||||
|
|
||||||
import forge.Constant;
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import com.google.common.collect.UnmodifiableIterator;
|
||||||
|
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.util.BinaryUtil;
|
import forge.util.BinaryUtil;
|
||||||
|
|
||||||
@@ -31,10 +35,10 @@ import forge.util.BinaryUtil;
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public final class ColorSet implements Comparable<ColorSet> {
|
public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte> {
|
||||||
|
|
||||||
private final byte myColor;
|
private final byte myColor;
|
||||||
private final int orderWeight;
|
private final float orderWeight;
|
||||||
|
|
||||||
private static ColorSet[] allColors = new ColorSet[32];
|
private static ColorSet[] allColors = new ColorSet[32];
|
||||||
private static final ColorSet noColor = new ColorSet();
|
private static final ColorSet noColor = new ColorSet();
|
||||||
@@ -55,11 +59,10 @@ public final class ColorSet implements Comparable<ColorSet> {
|
|||||||
private ColorSet(final byte mask) {
|
private ColorSet(final byte mask) {
|
||||||
this.myColor = mask;
|
this.myColor = mask;
|
||||||
this.orderWeight = this.getOrderWeight();
|
this.orderWeight = this.getOrderWeight();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ColorSet fromMask(final int mask) {
|
public static ColorSet fromMask(final int mask) {
|
||||||
int mask32 = (mask & MagicColor.ALL_COLORS) >> 1;
|
int mask32 = mask & MagicColor.ALL_COLORS;
|
||||||
if (allColors[mask32] == null) {
|
if (allColors[mask32] == null) {
|
||||||
allColors[mask32] = new ColorSet((byte) mask);
|
allColors[mask32] = new ColorSet((byte) mask);
|
||||||
}
|
}
|
||||||
@@ -134,8 +137,14 @@ public final class ColorSet implements Comparable<ColorSet> {
|
|||||||
*
|
*
|
||||||
* @return the order weight
|
* @return the order weight
|
||||||
*/
|
*/
|
||||||
public int getOrderWeight() {
|
public float getOrderWeight() {
|
||||||
return this.myColor == 0 ? 0x400 : (this.countColors() == 1 ? this.myColor : 0x200);
|
float res = this.countColors();
|
||||||
|
if(hasWhite()) res += 0.0005f;
|
||||||
|
if(hasBlue()) res += 0.0020f;
|
||||||
|
if(hasBlack()) res += 0.0080f;
|
||||||
|
if(hasRed()) res += 0.0320f;
|
||||||
|
if(hasGreen()) res += 0.1280f;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,7 +192,7 @@ public final class ColorSet implements Comparable<ColorSet> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(final ColorSet other) {
|
public int compareTo(final ColorSet other) {
|
||||||
return this.orderWeight - other.orderWeight;
|
return Float.compare(this.orderWeight, other.orderWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Presets
|
// Presets
|
||||||
@@ -253,7 +262,7 @@ public final class ColorSet implements Comparable<ColorSet> {
|
|||||||
return "n/a";
|
return "n/a";
|
||||||
}
|
}
|
||||||
String toReturn = MagicColor.toLongString(myColor);
|
String toReturn = MagicColor.toLongString(myColor);
|
||||||
if (toReturn == Constant.Color.COLORLESS && myColor != 0) {
|
if (toReturn == MagicColor.Constant.COLORLESS && myColor != 0) {
|
||||||
return "multi";
|
return "multi";
|
||||||
}
|
}
|
||||||
return toReturn;
|
return toReturn;
|
||||||
@@ -281,4 +290,37 @@ public final class ColorSet implements Comparable<ColorSet> {
|
|||||||
public ColorSet getOffColors(ColorSet ccOther) {
|
public ColorSet getOffColors(ColorSet ccOther) {
|
||||||
return ColorSet.fromMask(~this.myColor & ccOther.myColor);
|
return ColorSet.fromMask(~this.myColor & ccOther.myColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Byte> iterator() {
|
||||||
|
return new ColorIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ColorIterator extends UnmodifiableIterator<Byte> {
|
||||||
|
int currentBit = -1;
|
||||||
|
|
||||||
|
private int getIndexOfNextColor(){
|
||||||
|
int nextBit = currentBit + 1;
|
||||||
|
while(nextBit < MagicColor.NUMBER_OR_COLORS) {
|
||||||
|
if((myColor & MagicColor.WUBRG[nextBit]) != 0)
|
||||||
|
break;
|
||||||
|
nextBit++;
|
||||||
|
}
|
||||||
|
return nextBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return getIndexOfNextColor() < MagicColor.NUMBER_OR_COLORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Byte next() {
|
||||||
|
currentBit = getIndexOfNextColor();
|
||||||
|
if (currentBit >= MagicColor.NUMBER_OR_COLORS)
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
|
return MagicColor.WUBRG[currentBit];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
33
forge-core/src/main/java/forge/card/ICardDatabase.java
Normal file
33
forge-core/src/main/java/forge/card/ICardDatabase.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
|
import forge.card.CardDb.SetPreference;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
public interface ICardDatabase extends Iterable<PaperCard> {
|
||||||
|
PaperCard getCard(String cardName);
|
||||||
|
PaperCard getCard(String cardName, String edition);
|
||||||
|
PaperCard getCard(String cardName, String edition, int artIndex);
|
||||||
|
PaperCard getCardFromEdition(String cardName, SetPreference fromSet);
|
||||||
|
PaperCard getCardFromEdition(String cardName, Date printedBefore, SetPreference fromSet);
|
||||||
|
PaperCard getCardFromEdition(String cardName, Date printedBefore, SetPreference fromSet, int artIndex);
|
||||||
|
|
||||||
|
PaperCard getFoiled(PaperCard cpi);
|
||||||
|
|
||||||
|
int getPrintCount(String cardName, String edition);
|
||||||
|
int getMaxPrintCount(String cardName);
|
||||||
|
|
||||||
|
int getArtCount(String cardName, String edition);
|
||||||
|
|
||||||
|
Collection<PaperCard> getUniqueCards();
|
||||||
|
List<PaperCard> getAllCards();
|
||||||
|
List<PaperCard> getAllCards(Predicate<PaperCard> predicate);
|
||||||
|
|
||||||
|
Predicate<? super PaperCard> wasPrintedInSets(List<String> allowedSetCodes);
|
||||||
|
|
||||||
|
}
|
||||||
122
forge-core/src/main/java/forge/card/MagicColor.java
Normal file
122
forge-core/src/main/java/forge/card/MagicColor.java
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds byte values for each color magic has.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MagicColor {
|
||||||
|
|
||||||
|
public static final byte COLORLESS = 0;
|
||||||
|
public static final byte WHITE = 1 << 0;
|
||||||
|
public static final byte BLUE = 1 << 1;
|
||||||
|
public static final byte BLACK = 1 << 2;
|
||||||
|
public static final byte RED = 1 << 3;
|
||||||
|
public static final byte GREEN = 1 << 4;
|
||||||
|
|
||||||
|
public static final byte ALL_COLORS = BLACK | BLUE | WHITE | RED | GREEN;
|
||||||
|
public static final int NUMBER_OR_COLORS = 5;
|
||||||
|
|
||||||
|
public static final byte[] WUBRG = new byte[] { WHITE, BLUE, BLACK, RED, GREEN };
|
||||||
|
|
||||||
|
public static byte fromName(String s) {
|
||||||
|
if( s == null ) return 0;
|
||||||
|
if (s.equalsIgnoreCase(Constant.WHITE) || s.equalsIgnoreCase("w")) {
|
||||||
|
return MagicColor.WHITE;
|
||||||
|
}
|
||||||
|
if (s.equalsIgnoreCase(Constant.BLUE) || s.equalsIgnoreCase("u")) {
|
||||||
|
return MagicColor.BLUE;
|
||||||
|
}
|
||||||
|
if (s.equalsIgnoreCase(Constant.BLACK) || s.equalsIgnoreCase("b")) {
|
||||||
|
return MagicColor.BLACK;
|
||||||
|
}
|
||||||
|
if (s.equalsIgnoreCase(Constant.RED) || s.equalsIgnoreCase("r")) {
|
||||||
|
return MagicColor.RED;
|
||||||
|
}
|
||||||
|
if (s.equalsIgnoreCase(Constant.GREEN) || s.equalsIgnoreCase("g")) {
|
||||||
|
return MagicColor.GREEN;
|
||||||
|
}
|
||||||
|
return 0; // colorless
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte fromName(char c) {
|
||||||
|
switch(Character.toLowerCase(c)) {
|
||||||
|
case 'w': return MagicColor.WHITE;
|
||||||
|
case 'u': return MagicColor.BLUE;
|
||||||
|
case 'b': return MagicColor.BLACK;
|
||||||
|
case 'r': return MagicColor.RED;
|
||||||
|
case 'g': return MagicColor.GREEN;
|
||||||
|
}
|
||||||
|
return 0; // unknown means 'colorless'
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toShortString(String color) {
|
||||||
|
if (color.equalsIgnoreCase(Constant.SNOW)) return "S"; // compatibility
|
||||||
|
return toShortString(fromName(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toLongString(String color) {
|
||||||
|
if (color.equalsIgnoreCase("s")) return Constant.SNOW; // compatibility
|
||||||
|
return toLongString(fromName(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toShortString(byte color) {
|
||||||
|
switch(color){
|
||||||
|
case GREEN: return "G";
|
||||||
|
case RED: return "R";
|
||||||
|
case BLUE: return "U";
|
||||||
|
case BLACK: return "B";
|
||||||
|
case WHITE: return "W";
|
||||||
|
default: return "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toLongString(byte color) {
|
||||||
|
switch(color){
|
||||||
|
case GREEN: return Constant.GREEN ;
|
||||||
|
case RED: return Constant.RED;
|
||||||
|
case BLUE: return Constant.BLUE;
|
||||||
|
case BLACK: return Constant.BLACK;
|
||||||
|
case WHITE: return Constant.WHITE;
|
||||||
|
default: return Constant.COLORLESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Interface Color.
|
||||||
|
*/
|
||||||
|
public static class Constant {
|
||||||
|
|
||||||
|
/** The Black. */
|
||||||
|
public static final String BLACK = "black";
|
||||||
|
|
||||||
|
/** The Blue. */
|
||||||
|
public static final String BLUE = "blue";
|
||||||
|
|
||||||
|
/** The Green. */
|
||||||
|
public static final String GREEN = "green";
|
||||||
|
|
||||||
|
/** The Red. */
|
||||||
|
public static final String RED = "red";
|
||||||
|
|
||||||
|
/** The White. */
|
||||||
|
public static final String WHITE = "white";
|
||||||
|
|
||||||
|
/** The Colorless. */
|
||||||
|
public static final String COLORLESS = "colorless";
|
||||||
|
// color order "wubrg"
|
||||||
|
|
||||||
|
/** The only colors. */
|
||||||
|
public static final ImmutableList<String> ONLY_COLORS = ImmutableList.of(WHITE, BLUE, BLACK, RED, GREEN);
|
||||||
|
|
||||||
|
/** The Snow. */
|
||||||
|
public static final String SNOW = "snow";
|
||||||
|
|
||||||
|
/** The Basic lands. */
|
||||||
|
public static final List<String> BASIC_LANDS = ImmutableList.of("Plains", "Island", "Swamp", "Mountain", "Forest");
|
||||||
|
public static final List<String> SNOW_LANDS = ImmutableList.of("Snow-Covered Plains", "Snow-Covered Island", "Snow-Covered Swamp", "Snow-Covered Mountain", "Snow-Covered Forest");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.item;
|
package forge.card;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -7,7 +8,9 @@ import java.util.Collection;
|
|||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.util.ItemPool;
|
||||||
import forge.deck.CardPool;
|
import forge.deck.CardPool;
|
||||||
|
import forge.item.PaperCard;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.storage.StorageReaderFileSections;
|
import forge.util.storage.StorageReaderFileSections;
|
||||||
|
|
||||||
@@ -30,7 +33,7 @@ public class PrintSheet {
|
|||||||
this(name0, null);
|
this(name0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PrintSheet(String name0, ItemPool<PaperCard> pool) {
|
public PrintSheet(String name0, ItemPool<PaperCard> pool) {
|
||||||
name = name0;
|
name = name0;
|
||||||
cardsWithWeights = pool != null ? pool : new ItemPool<PaperCard>(PaperCard.class);
|
cardsWithWeights = pool != null ? pool : new ItemPool<PaperCard>(PaperCard.class);
|
||||||
}
|
}
|
||||||
@@ -105,18 +108,6 @@ public class PrintSheet {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Reader extends StorageReaderFileSections<PrintSheet> {
|
|
||||||
public Reader(String fileName) {
|
|
||||||
super(fileName, PrintSheet.FN_GET_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected PrintSheet read(String title, Iterable<String> body, int idx) {
|
|
||||||
return new PrintSheet(title, CardPool.fromCardList(body));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return cardsWithWeights.isEmpty();
|
return cardsWithWeights.isEmpty();
|
||||||
}
|
}
|
||||||
@@ -125,5 +116,16 @@ public class PrintSheet {
|
|||||||
return cardsWithWeights.toFlatList();
|
return cardsWithWeights.toFlatList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Reader extends StorageReaderFileSections<PrintSheet> {
|
||||||
|
public Reader(File file) {
|
||||||
|
super(file, PrintSheet.FN_GET_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PrintSheet read(String title, Iterable<String> body, int idx) {
|
||||||
|
return new PrintSheet(title, CardPool.fromCardList(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,13 +10,15 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.item.ItemPoolView;
|
import forge.item.SealedProduct;
|
||||||
import forge.item.PrintSheet;
|
import forge.util.ItemPool;
|
||||||
|
|
||||||
|
|
||||||
public class UnOpenedProduct implements IUnOpenedProduct {
|
public class UnOpenedProduct implements IUnOpenedProduct {
|
||||||
|
|
||||||
private final SealedProductTemplate tpl;
|
private final SealedProduct.Template tpl;
|
||||||
private final Map<String, PrintSheet> sheets;
|
private final Map<String, PrintSheet> sheets;
|
||||||
private boolean poolLimited = false; // if true after successful generation cards are removed from printsheets.
|
private boolean poolLimited = false; // if true after successful generation cards are removed from printsheets.
|
||||||
|
|
||||||
@@ -30,24 +32,24 @@ public class UnOpenedProduct implements IUnOpenedProduct {
|
|||||||
|
|
||||||
|
|
||||||
// Means to select from all unique cards (from base game, ie. no schemes or avatars)
|
// Means to select from all unique cards (from base game, ie. no schemes or avatars)
|
||||||
public UnOpenedProduct(SealedProductTemplate template) {
|
public UnOpenedProduct(SealedProduct.Template template) {
|
||||||
tpl = template;
|
tpl = template;
|
||||||
sheets = null;
|
sheets = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke this constructor only if you are sure that the pool is not equal to deafult carddb
|
// Invoke this constructor only if you are sure that the pool is not equal to deafult carddb
|
||||||
public UnOpenedProduct(SealedProductTemplate template, ItemPoolView<PaperCard> pool) {
|
public UnOpenedProduct(SealedProduct.Template template, ItemPool<PaperCard> pool) {
|
||||||
this(template, pool.toFlatList());
|
this(template, pool.toFlatList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnOpenedProduct(SealedProductTemplate template, Iterable<PaperCard> cards) {
|
public UnOpenedProduct(SealedProduct.Template template, Iterable<PaperCard> cards) {
|
||||||
tpl = template;
|
tpl = template;
|
||||||
sheets = new TreeMap<String, PrintSheet>();
|
sheets = new TreeMap<String, PrintSheet>();
|
||||||
prebuildSheets(cards);
|
prebuildSheets(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnOpenedProduct(SealedProductTemplate sealedProductTemplate, Predicate<PaperCard> filterPrinted) {
|
public UnOpenedProduct(SealedProduct.Template sealedProductTemplate, Predicate<PaperCard> filterPrinted) {
|
||||||
this(sealedProductTemplate, Iterables.filter(CardDb.instance().getAllCards(), filterPrinted));
|
this(sealedProductTemplate, Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), filterPrinted));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prebuildSheets(Iterable<PaperCard> sourceList) {
|
private void prebuildSheets(Iterable<PaperCard> sourceList) {
|
||||||
35
forge-core/src/main/java/forge/card/mana/ManaAtom.java
Normal file
35
forge-core/src/main/java/forge/card/mana/ManaAtom.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package forge.card.mana;
|
||||||
|
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
|
||||||
|
/** A bitmask to represent any mana symbol as an integer. */
|
||||||
|
public abstract class ManaAtom {
|
||||||
|
public static final int COLORLESS = 1 << 7;
|
||||||
|
|
||||||
|
/** The Constant WHITE. */
|
||||||
|
public static final int WHITE = MagicColor.WHITE;
|
||||||
|
|
||||||
|
/** The Constant BLUE. */
|
||||||
|
public static final int BLUE = MagicColor.BLUE;
|
||||||
|
|
||||||
|
/** The Constant BLACK. */
|
||||||
|
public static final int BLACK = MagicColor.BLACK;
|
||||||
|
|
||||||
|
/** The Constant RED. */
|
||||||
|
public static final int RED = MagicColor.RED;
|
||||||
|
|
||||||
|
/** The Constant GREEN. */
|
||||||
|
public static final int GREEN = MagicColor.GREEN;
|
||||||
|
|
||||||
|
/** The Constant IS_X. */
|
||||||
|
public static final int IS_X = 1 << 8;
|
||||||
|
|
||||||
|
/** The Constant OR_2_COLORLESS. */
|
||||||
|
public static final int OR_2_COLORLESS = 1 << 9;
|
||||||
|
|
||||||
|
/** The Constant OR_2_LIFE. */
|
||||||
|
public static final int OR_2_LIFE = 1 << 10;
|
||||||
|
|
||||||
|
/** The Constant IS_SNOW. */
|
||||||
|
public static final int IS_SNOW = 1 << 11;
|
||||||
|
}
|
||||||
@@ -19,10 +19,9 @@ package forge.card.mana;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.card.ColorSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* CardManaCost class.
|
* CardManaCost class.
|
||||||
@@ -32,7 +31,7 @@ import forge.card.ColorSet;
|
|||||||
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
|
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final class ManaCost implements Comparable<ManaCost> {
|
public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostShard> {
|
||||||
private List<ManaCostShard> shards;
|
private List<ManaCostShard> shards;
|
||||||
private final int genericCost;
|
private final int genericCost;
|
||||||
private final boolean hasNoCost; // lands cost
|
private final boolean hasNoCost; // lands cost
|
||||||
@@ -49,7 +48,7 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
public static final ManaCost FOUR = new ManaCost(4);
|
public static final ManaCost FOUR = new ManaCost(4);
|
||||||
|
|
||||||
public static ManaCost get(int cntColorless) {
|
public static ManaCost get(int cntColorless) {
|
||||||
switch (cntColorless) {
|
switch (cntColorless) {
|
||||||
case 0: return ZERO;
|
case 0: return ZERO;
|
||||||
case 1: return ONE;
|
case 1: return ONE;
|
||||||
case 2: return TWO;
|
case 2: return TWO;
|
||||||
@@ -58,14 +57,14 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
}
|
}
|
||||||
return cntColorless > 0 ? new ManaCost(cntColorless) : NO_COST;
|
return cntColorless > 0 ? new ManaCost(cntColorless) : NO_COST;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pass mana cost parser here
|
// pass mana cost parser here
|
||||||
private ManaCost(int cmc) {
|
private ManaCost(int cmc) {
|
||||||
this.hasNoCost = cmc < 0;
|
this.hasNoCost = cmc < 0;
|
||||||
this.genericCost = cmc < 0 ? 0 : cmc;
|
this.genericCost = cmc < 0 ? 0 : cmc;
|
||||||
sealClass(new ArrayList<ManaCostShard>());
|
sealClass(new ArrayList<ManaCostShard>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sealClass(List<ManaCostShard> shards0) {
|
private void sealClass(List<ManaCostShard> shards0) {
|
||||||
this.shards = Collections.unmodifiableList(shards0);
|
this.shards = Collections.unmodifiableList(shards0);
|
||||||
this.stringValue = this.getSimpleString();
|
this.stringValue = this.getSimpleString();
|
||||||
@@ -97,22 +96,19 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
return "no cost";
|
return "no cost";
|
||||||
}
|
}
|
||||||
if (this.shards.isEmpty()) {
|
if (this.shards.isEmpty()) {
|
||||||
return Integer.toString(this.genericCost);
|
return "{" + this.genericCost + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
boolean isFirst = true;
|
|
||||||
if (this.genericCost > 0) {
|
if (this.genericCost > 0) {
|
||||||
sb.append(this.genericCost);
|
sb.append("{" + this.genericCost + "}");
|
||||||
isFirst = false;
|
|
||||||
}
|
}
|
||||||
for (final ManaCostShard s : this.shards) {
|
for (final ManaCostShard s : this.shards) {
|
||||||
if (!isFirst) {
|
if (s == ManaCostShard.X) {
|
||||||
sb.append(' ');
|
sb.insert(0, s.toString());
|
||||||
} else {
|
} else {
|
||||||
isFirst = false;
|
sb.append(s.toString());
|
||||||
}
|
}
|
||||||
sb.append(s.toString());
|
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
@@ -143,15 +139,6 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the shards.
|
|
||||||
*
|
|
||||||
* @return the shards
|
|
||||||
*/
|
|
||||||
public List<ManaCostShard> getShards() {
|
|
||||||
return this.shards;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getShardCount(ManaCostShard which) {
|
public int getShardCount(ManaCostShard which) {
|
||||||
if (which == ManaCostShard.COLORLESS) {
|
if (which == ManaCostShard.COLORLESS) {
|
||||||
return genericCost;
|
return genericCost;
|
||||||
@@ -196,7 +183,7 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
public boolean isZero() {
|
public boolean isZero() {
|
||||||
return genericCost == 0 && isPureGeneric();
|
return genericCost == 0 && isPureGeneric();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
@@ -260,13 +247,12 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Can this mana cost be paid with unlimited mana of given color set.
|
* Can this mana cost be paid with unlimited mana of given color set.
|
||||||
* @param color
|
* @param colorCode
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
public boolean canBePaidWithAvaliable(byte colorCode) {
|
||||||
public boolean canBePaidWithAvaliable(ColorSet color) {
|
|
||||||
for (ManaCostShard shard : shards) {
|
for (ManaCostShard shard : shards) {
|
||||||
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(color.getColor())) {
|
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(colorCode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,4 +274,16 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ManaCostShard> iterator() {
|
||||||
|
return this.shards.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGlyphCount() { // counts all colored shards or 1 for {0} costs
|
||||||
|
int width = shards.size();
|
||||||
|
if (genericCost > 0 || (genericCost == 0 && width == 0)) {
|
||||||
|
width++;
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,17 @@ public class ManaCostParser implements IParserManaCost {
|
|||||||
private int nextToken;
|
private int nextToken;
|
||||||
private int colorlessCost;
|
private int colorlessCost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given cost and output formatted cost string
|
||||||
|
*
|
||||||
|
* @param cost
|
||||||
|
*/
|
||||||
|
public static String parse(final String cost) {
|
||||||
|
final ManaCostParser parser = new ManaCostParser(cost);
|
||||||
|
final ManaCost manaCost = new ManaCost(parser);
|
||||||
|
return manaCost.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new parser cardname txt mana cost.
|
* Instantiates a new parser cardname txt mana cost.
|
||||||
*
|
*
|
||||||
@@ -54,7 +65,6 @@ public class ManaCostParser implements IParserManaCost {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final ManaCostShard next() {
|
public final ManaCostShard next() {
|
||||||
|
|
||||||
final String unparsed = this.cost[this.nextToken++];
|
final String unparsed = this.cost[this.nextToken++];
|
||||||
// System.out.println(unparsed);
|
// System.out.println(unparsed);
|
||||||
if (StringUtils.isNumeric(unparsed)) {
|
if (StringUtils.isNumeric(unparsed)) {
|
||||||
@@ -23,7 +23,7 @@ import forge.util.BinaryUtil;
|
|||||||
* The Class CardManaCostShard.
|
* The Class CardManaCostShard.
|
||||||
*/
|
*/
|
||||||
public enum ManaCostShard implements Comparable<ManaCostShard> {
|
public enum ManaCostShard implements Comparable<ManaCostShard> {
|
||||||
// declaration order matters! Place the shards that offer least ways to be paid for first
|
// declaration order matters! Place the shards that offer least ways to be paid for first
|
||||||
|
|
||||||
/* Pure colors */
|
/* Pure colors */
|
||||||
WHITE(ManaAtom.WHITE, "W"),
|
WHITE(ManaAtom.WHITE, "W"),
|
||||||
@@ -35,14 +35,14 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
/* Hybrid */
|
/* Hybrid */
|
||||||
WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"),
|
WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"),
|
||||||
WB(ManaAtom.WHITE | ManaAtom.BLACK, "W/B", "WB"),
|
WB(ManaAtom.WHITE | ManaAtom.BLACK, "W/B", "WB"),
|
||||||
WR(ManaAtom.WHITE | ManaAtom.RED, "W/R", "RW"),
|
|
||||||
WG(ManaAtom.WHITE | ManaAtom.GREEN, "W/G", "GW"),
|
|
||||||
UB(ManaAtom.BLUE | ManaAtom.BLACK, "U/B", "UB"),
|
UB(ManaAtom.BLUE | ManaAtom.BLACK, "U/B", "UB"),
|
||||||
UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"),
|
UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"),
|
||||||
UG(ManaAtom.BLUE | ManaAtom.GREEN, "U/G", "GU"),
|
|
||||||
BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"),
|
BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"),
|
||||||
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
|
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
|
||||||
|
RW(ManaAtom.RED | ManaAtom.WHITE, "R/W", "RW"),
|
||||||
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
|
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
|
||||||
|
GW(ManaAtom.GREEN | ManaAtom.WHITE, "G/W", "GW"),
|
||||||
|
GU(ManaAtom.GREEN | ManaAtom.BLUE, "G/U", "GU"),
|
||||||
|
|
||||||
/* Or 2 colorless */
|
/* Or 2 colorless */
|
||||||
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
||||||
@@ -55,7 +55,7 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
S(ManaAtom.IS_SNOW, "S"),
|
S(ManaAtom.IS_SNOW, "S"),
|
||||||
COLORLESS(ManaAtom.COLORLESS, "1"),
|
COLORLESS(ManaAtom.COLORLESS, "1"),
|
||||||
|
|
||||||
/* Phyrexian */
|
/* Phyrexian */
|
||||||
PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"),
|
PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"),
|
||||||
PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"),
|
PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"),
|
||||||
PB(ManaAtom.BLACK | ManaAtom.OR_2_LIFE, "B/P", "PB"),
|
PB(ManaAtom.BLACK | ManaAtom.OR_2_LIFE, "B/P", "PB"),
|
||||||
@@ -102,15 +102,12 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
this.shard = value;
|
this.shard = value;
|
||||||
this.cmc = this.getCMC();
|
this.cmc = this.getCMC();
|
||||||
this.cmpc = this.getCmpCost();
|
this.cmpc = this.getCmpCost();
|
||||||
this.stringValue = sValue;
|
this.stringValue = "{" + sValue + "}";
|
||||||
this.imageKey = imgKey;
|
this.imageKey = imgKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN;
|
public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN;
|
||||||
|
|
||||||
|
|
||||||
private int getCMC() {
|
private int getCMC() {
|
||||||
if (0 != (this.shard & ManaAtom.IS_X)) {
|
if (0 != (this.shard & ManaAtom.IS_X)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -267,7 +264,6 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
|
|
||||||
public boolean isMonoColor() {
|
public boolean isMonoColor() {
|
||||||
return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 1;
|
return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOr2Colorless() {
|
public boolean isOr2Colorless() {
|
||||||
199
forge-core/src/main/java/forge/deck/CardPool.java
Normal file
199
forge-core/src/main/java/forge/deck/CardPool.java
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.card.CardDb;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
import forge.util.ItemPool;
|
||||||
|
import forge.util.ItemPoolSorter;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deck section.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CardPool extends ItemPool<PaperCard> {
|
||||||
|
|
||||||
|
public CardPool() {
|
||||||
|
super(PaperCard.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardPool(final Iterable<Entry<PaperCard, Integer>> cards) {
|
||||||
|
this();
|
||||||
|
this.addAll(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final String cardName, final int amount) {
|
||||||
|
this.add(cardName, null, -1, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final String cardName, final String setCode) {
|
||||||
|
this.add(cardName, setCode, -1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final String cardName, final String setCode, final int amount) {
|
||||||
|
this.add(cardName, setCode, -1, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: ART indices are "1" -based
|
||||||
|
public void add(String cardName, String setCode, final int artIndex, final int amount) {
|
||||||
|
PaperCard cp = StaticData.instance().getCommonCards().getCard(cardName, setCode, artIndex);
|
||||||
|
boolean isCommonCard = cp != null;
|
||||||
|
if ( !isCommonCard ) {
|
||||||
|
cp = StaticData.instance().getVariantCards().getCard(cardName, setCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean artIndexExplicitlySet = artIndex > 0 || Character.isDigit(cardName.charAt(cardName.length()-1)) && cardName.charAt(cardName.length()-2) == CardDb.NameSetSeparator;
|
||||||
|
int artCount = 1;
|
||||||
|
|
||||||
|
if (cp != null ) {
|
||||||
|
setCode = cp.getEdition();
|
||||||
|
cardName = cp.getName();
|
||||||
|
artCount = isCommonCard ? StaticData.instance().getCommonCards().getArtCount(cardName, setCode) : 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cp = StaticData.instance().getCommonCards().createUnsuportedCard(cardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artIndexExplicitlySet || artCount <= 1) {
|
||||||
|
// either a specific art index is specified, or there is only one art, so just add the card
|
||||||
|
this.add(cp, amount);
|
||||||
|
} else {
|
||||||
|
// random art index specified, make sure we get different groups of cards with different art
|
||||||
|
int[] artGroups = MyRandom.splitIntoRandomGroups(amount, artCount);
|
||||||
|
for (int i = 1; i <= artGroups.length; i++) {
|
||||||
|
int cnt = artGroups[i-1];
|
||||||
|
if (cnt <= 0)
|
||||||
|
continue;
|
||||||
|
PaperCard cp_random = isCommonCard
|
||||||
|
? StaticData.instance().getCommonCards().getCard(cardName, setCode, i)
|
||||||
|
: StaticData.instance().getVariantCards().getCard(cardName, setCode, i);
|
||||||
|
this.add(cp_random, cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all from a List of CardPrinted.
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* CardPrinteds to add
|
||||||
|
*/
|
||||||
|
public void add(final Iterable<PaperCard> list) {
|
||||||
|
for (PaperCard cp : list) {
|
||||||
|
this.add(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns n-th card from this DeckSection. LINEAR time. No fixed order between changes
|
||||||
|
* @param i
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public PaperCard get(int n) {
|
||||||
|
for (Entry<PaperCard, Integer> e : this) {
|
||||||
|
n -= e.getValue();
|
||||||
|
if ( n <= 0 ) return e.getKey();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (this.isEmpty()) { return "[]"; }
|
||||||
|
|
||||||
|
boolean isFirst = true;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append('[');
|
||||||
|
for (Entry<PaperCard, Integer> e : this) {
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(e.getValue()).append(" x ").append(e.getKey().getName());
|
||||||
|
}
|
||||||
|
return sb.append(']').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Pattern p = Pattern.compile("((\\d+)\\s+)?(.*?)");
|
||||||
|
public static CardPool fromCardList(final Iterable<String> lines) {
|
||||||
|
CardPool pool = new CardPool();
|
||||||
|
|
||||||
|
|
||||||
|
if (lines == null) {
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator<String> lineIterator = lines.iterator();
|
||||||
|
while (lineIterator.hasNext()) {
|
||||||
|
final String line = lineIterator.next();
|
||||||
|
if (line.startsWith(";") || line.startsWith("#")) { continue; } // that is a comment or not-yet-supported card
|
||||||
|
|
||||||
|
final Matcher m = p.matcher(line.trim());
|
||||||
|
m.matches();
|
||||||
|
final String sCnt = m.group(2);
|
||||||
|
final String cardName = m.group(3);
|
||||||
|
if (StringUtils.isBlank(cardName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int count = sCnt == null ? 1 : Integer.parseInt(sCnt);
|
||||||
|
pool.add(cardName, count);
|
||||||
|
}
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toCardList(String separator) {
|
||||||
|
List<Entry<PaperCard, Integer>> main2sort = Lists.newArrayList(this);
|
||||||
|
Collections.sort(main2sort, ItemPoolSorter.BY_NAME_THEN_SET);
|
||||||
|
final CardDb commonDb = StaticData.instance().getCommonCards();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
boolean isFirst = true;
|
||||||
|
|
||||||
|
for (final Entry<PaperCard, Integer> e : main2sort) {
|
||||||
|
if(!isFirst)
|
||||||
|
sb.append(separator);
|
||||||
|
else
|
||||||
|
isFirst = false;
|
||||||
|
|
||||||
|
CardDb db = !e.getKey().getRules().isVariant() ? commonDb : StaticData.instance().getVariantCards();
|
||||||
|
sb.append(e.getValue()).append(" ");
|
||||||
|
db.appendCardToStringBuilder(e.getKey(), sb);
|
||||||
|
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
212
forge-core/src/main/java/forge/deck/Deck.java
Normal file
212
forge-core/src/main/java/forge/deck/Deck.java
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import forge.StaticData;
|
||||||
|
|
||||||
|
import forge.card.CardEdition;
|
||||||
|
import forge.card.CardDb.SetPreference;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Deck class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* The set of MTG legal cards that become player's library when the game starts.
|
||||||
|
* Any other data is not part of a deck and should be stored elsewhere. Current
|
||||||
|
* fields allowed for deck metadata are Name, Title, Description and Deck Type.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPool>> {
|
||||||
|
private final Map<DeckSection, CardPool> parts = new EnumMap<DeckSection, CardPool>(DeckSection.class);
|
||||||
|
private final Set<String> tags = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
|
// gameType is from Constant.GameType, like GameType.Regular
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Decks have their named finalled.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public Deck() {
|
||||||
|
this("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new deck.
|
||||||
|
*
|
||||||
|
* @param name0 the name0
|
||||||
|
*/
|
||||||
|
public Deck(final String name0) {
|
||||||
|
super(name0);
|
||||||
|
getOrCreate(DeckSection.Main);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType() {
|
||||||
|
return "Deck";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.getName().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardPool getMain() {
|
||||||
|
return this.parts.get(DeckSection.Main);
|
||||||
|
}
|
||||||
|
|
||||||
|
// may return nulls
|
||||||
|
public CardPool get(DeckSection deckSection) {
|
||||||
|
return this.parts.get(deckSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(DeckSection deckSection) {
|
||||||
|
final CardPool cp = get(deckSection);
|
||||||
|
return cp != null && !cp.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// will return new if it was absent
|
||||||
|
public CardPool getOrCreate(DeckSection deckSection) {
|
||||||
|
CardPool p = get(deckSection);
|
||||||
|
if (p != null) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
p = new CardPool();
|
||||||
|
this.parts.put(deckSection, p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putSection(DeckSection section, CardPool pool) {
|
||||||
|
this.parts.put(section, pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.deck.DeckBase#cloneFieldsTo(forge.deck.DeckBase)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void cloneFieldsTo(final DeckBase clone) {
|
||||||
|
super.cloneFieldsTo(clone);
|
||||||
|
final Deck result = (Deck) clone;
|
||||||
|
for (Entry<DeckSection, CardPool> kv : parts.entrySet()) {
|
||||||
|
CardPool cp = new CardPool();
|
||||||
|
result.parts.put(kv.getKey(), cp);
|
||||||
|
cp.addAll(kv.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see forge.deck.DeckBase#newInstance(java.lang.String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected DeckBase newInstance(final String name0) {
|
||||||
|
return new Deck(name0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void convertByXitaxMethod() {
|
||||||
|
CardEdition earliestSet = StaticData.instance().getEditions().getEarliestEditionWithAllCards(getAllCardsInASinglePool());
|
||||||
|
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(earliestSet.getDate());
|
||||||
|
cal.add(Calendar.DATE, 1);
|
||||||
|
Date dayAfterNewestSetRelease = cal.getTime();
|
||||||
|
|
||||||
|
for(Entry<DeckSection, CardPool> p : parts.entrySet()) {
|
||||||
|
if( p.getKey() == DeckSection.Planes || p.getKey() == DeckSection.Schemes || p.getKey() == DeckSection.Avatar)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CardPool newPool = new CardPool();
|
||||||
|
|
||||||
|
for(Entry<PaperCard, Integer> cp : p.getValue()){
|
||||||
|
String cardName = cp.getKey().getName();
|
||||||
|
int artIndex = cp.getKey().getArtIndex();
|
||||||
|
|
||||||
|
PaperCard c = StaticData.instance().getCommonCards().getCardFromEdition(cardName, dayAfterNewestSetRelease, SetPreference.LatestCoreExp, artIndex);
|
||||||
|
if( null == c ) {
|
||||||
|
c = StaticData.instance().getCommonCards().getCardFromEdition(cardName, dayAfterNewestSetRelease, SetPreference.LatestCoreExp, -1);
|
||||||
|
if( c == null)
|
||||||
|
c = StaticData.instance().getCommonCards().getCardFromEdition(cardName, dayAfterNewestSetRelease, SetPreference.Latest, -1);
|
||||||
|
|
||||||
|
if( c != null ) {
|
||||||
|
newPool.add(cardName, c.getEdition(), cp.getValue()); // this is to randomize art of all those cards
|
||||||
|
} else // I give up!
|
||||||
|
newPool.add(cp.getKey(), cp.getValue());
|
||||||
|
} else
|
||||||
|
newPool.add(c, cp.getValue());
|
||||||
|
}
|
||||||
|
parts.put(p.getKey(), newPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Function<Deck, String> FN_NAME_SELECTOR = new Function<Deck, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(Deck arg1) {
|
||||||
|
return arg1.getName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see java.lang.Iterable#iterator()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<DeckSection, CardPool>> iterator() {
|
||||||
|
return parts.entrySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the associated tags, a writable set
|
||||||
|
*/
|
||||||
|
public Set<String> getTags() {
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardPool getAllCardsInASinglePool() {
|
||||||
|
CardPool allCards = new CardPool(); // will count cards in this pool to enforce restricted
|
||||||
|
allCards.addAll(this.getMain());
|
||||||
|
if (this.has(DeckSection.Sideboard))
|
||||||
|
allCards.addAll(this.get(DeckSection.Sideboard));
|
||||||
|
if (this.has(DeckSection.Commander))
|
||||||
|
allCards.addAll(this.get(DeckSection.Commander));
|
||||||
|
// do not include schemes / avatars and any non-regular cards
|
||||||
|
return allCards;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,13 +19,13 @@ package forge.deck;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import forge.util.IHasName;
|
import forge.item.InventoryItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IHasName {
|
public abstract class DeckBase implements Serializable, Comparable<DeckBase>, InventoryItem {
|
||||||
private static final long serialVersionUID = -7538150536939660052L;
|
private static final long serialVersionUID = -7538150536939660052L;
|
||||||
// gameType is from Constant.GameType, like GameType.Regular
|
// gameType is from Constant.GameType, like GameType.Regular
|
||||||
|
|
||||||
@@ -52,8 +52,8 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IH
|
|||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object o) {
|
public boolean equals(final Object o) {
|
||||||
if (o instanceof Deck) {
|
if (o instanceof DeckBase) {
|
||||||
final Deck d = (Deck) o;
|
final DeckBase d = (DeckBase) o;
|
||||||
return this.getName().equals(d.getName());
|
return this.getName().equals(d.getName());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -76,6 +76,11 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IH
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the comment.
|
* Sets the comment.
|
||||||
*
|
*
|
||||||
@@ -133,5 +138,4 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IH
|
|||||||
public final String getBestFileName() {
|
public final String getBestFileName() {
|
||||||
return this.getName().replaceAll("[^-_$#@.,{[()]} a-zA-Z0-9]", "");
|
return this.getName().replaceAll("[^-_$#@.,{[()]} a-zA-Z0-9]", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
245
forge-core/src/main/java/forge/deck/DeckFormat.java
Normal file
245
forge-core/src/main/java/forge/deck/DeckFormat.java
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Range;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.card.CardType;
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
import forge.item.IPaperCard;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GameType is an enum to determine the type of current game. :)
|
||||||
|
*/
|
||||||
|
public enum DeckFormat {
|
||||||
|
|
||||||
|
// Main board: allowed size SB: restriction Max distinct non basic cards
|
||||||
|
Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||||
|
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||||
|
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
|
||||||
|
Commander ( Range.is(99), Range.between(0, 10), 1),
|
||||||
|
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
|
Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
|
Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4);
|
||||||
|
|
||||||
|
private final Range<Integer> mainRange;
|
||||||
|
private final Range<Integer> sideRange; // null => no check
|
||||||
|
private final int maxCardCopies;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DeckFormat(Range<Integer> main, Range<Integer> side, int maxCopies) {
|
||||||
|
mainRange = main;
|
||||||
|
sideRange = side;
|
||||||
|
maxCardCopies = maxCopies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smart value of.
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
* @param defaultValue the default value
|
||||||
|
* @return the game type
|
||||||
|
*/
|
||||||
|
public static DeckFormat smartValueOf(final String value, DeckFormat defaultValue) {
|
||||||
|
if (null == value) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String valToCompate = value.trim();
|
||||||
|
for (final DeckFormat v : DeckFormat.values()) {
|
||||||
|
if (v.name().compareToIgnoreCase(valToCompate) == 0) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("No element named " + value + " in enum GameType");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the sideRange
|
||||||
|
*/
|
||||||
|
public Range<Integer> getSideRange() {
|
||||||
|
return sideRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the mainRange
|
||||||
|
*/
|
||||||
|
public Range<Integer> getMainRange() {
|
||||||
|
return mainRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maxCardCopies
|
||||||
|
*/
|
||||||
|
public int getMaxCardCopies() {
|
||||||
|
return maxCardCopies;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("incomplete-switch")
|
||||||
|
public String getDeckConformanceProblem(Deck deck) {
|
||||||
|
if(deck == null) {
|
||||||
|
return "is not selected";
|
||||||
|
}
|
||||||
|
|
||||||
|
int deckSize = deck.getMain().countAll();
|
||||||
|
|
||||||
|
int min = getMainRange().getMinimum();
|
||||||
|
int max = getMainRange().getMaximum();
|
||||||
|
|
||||||
|
if (deckSize < min) {
|
||||||
|
return String.format("should have a minimum of %d cards", min);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deckSize > max) {
|
||||||
|
return String.format("should not exceed a maximum of %d cards", max);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(this) {
|
||||||
|
case Commander: //Must contain exactly 1 legendary Commander and a sideboard of 10 or zero cards.
|
||||||
|
|
||||||
|
final CardPool cmd = deck.get(DeckSection.Commander);
|
||||||
|
if (null == cmd || cmd.isEmpty()) {
|
||||||
|
return "is missing a commander";
|
||||||
|
}
|
||||||
|
if (!cmd.get(0).getRules().getType().isLegendary()
|
||||||
|
|| !cmd.get(0).getRules().getType().isCreature()) {
|
||||||
|
return "has a commander that is not a legendary creature";
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorSet cmdCI = cmd.get(0).getRules().getColorIdentity();
|
||||||
|
List<PaperCard> erroneousCI = new ArrayList<PaperCard>();
|
||||||
|
|
||||||
|
for(Entry<PaperCard, Integer> cp : deck.get(DeckSection.Main)) {
|
||||||
|
if(!cp.getKey().getRules().getColorIdentity().hasNoColorsExcept(cmdCI.getColor()))
|
||||||
|
{
|
||||||
|
erroneousCI.add(cp.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(deck.has(DeckSection.Sideboard))
|
||||||
|
{
|
||||||
|
for(Entry<PaperCard, Integer> cp : deck.get(DeckSection.Sideboard)) {
|
||||||
|
if(!cp.getKey().getRules().getColorIdentity().hasNoColorsExcept(cmdCI.getColor()))
|
||||||
|
{
|
||||||
|
erroneousCI.add(cp.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(erroneousCI.size() > 0)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder("contains card that do not match the commanders color identity:");
|
||||||
|
|
||||||
|
for(PaperCard cp : erroneousCI)
|
||||||
|
{
|
||||||
|
sb.append("\n").append(cp.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Archenemy: //Must contain at least 20 schemes, max 2 of each.
|
||||||
|
final CardPool schemes = deck.get(DeckSection.Schemes);
|
||||||
|
if (schemes == null || schemes.countAll() < 20) {
|
||||||
|
return "must contain at least 20 schemes";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry<PaperCard, Integer> cp : schemes) {
|
||||||
|
if (cp.getValue() > 2) {
|
||||||
|
return String.format("must not contain more than 2 copies of any Scheme, but has %d of '%s'", cp.getValue(), cp.getKey().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxCopies = getMaxCardCopies();
|
||||||
|
if (maxCopies < Integer.MAX_VALUE) {
|
||||||
|
//Must contain no more than 4 of the same card
|
||||||
|
//shared among the main deck and sideboard, except
|
||||||
|
//basic lands, Shadowborn Apostle and Relentless Rats
|
||||||
|
|
||||||
|
CardPool tmp = new CardPool(deck.getMain());
|
||||||
|
if ( deck.has(DeckSection.Sideboard))
|
||||||
|
tmp.addAll(deck.get(DeckSection.Sideboard));
|
||||||
|
if ( deck.has(DeckSection.Commander) && this == Commander)
|
||||||
|
tmp.addAll(deck.get(DeckSection.Commander));
|
||||||
|
|
||||||
|
List<String> limitExceptions = Arrays.asList(new String[]{"Relentless Rats", "Shadowborn Apostle"});
|
||||||
|
|
||||||
|
// should group all cards by name, so that different editions of same card are really counted as the same card
|
||||||
|
for (Entry<String, Integer> cp : Aggregates.groupSumBy(tmp, PaperCard.FN_GET_NAME)) {
|
||||||
|
|
||||||
|
IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
|
||||||
|
boolean canHaveMultiple = simpleCard.getRules().getType().isBasicLand() || limitExceptions.contains(cp.getKey());
|
||||||
|
|
||||||
|
if (!canHaveMultiple && cp.getValue() > maxCopies) {
|
||||||
|
return String.format("must not contain more than %d of '%s' card", maxCopies, cp.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sideboard must contain either 0 or 15 cards
|
||||||
|
int sideboardSize = deck.has(DeckSection.Sideboard) ? deck.get(DeckSection.Sideboard).countAll() : 0;
|
||||||
|
Range<Integer> sbRange = getSideRange();
|
||||||
|
if (sbRange != null && sideboardSize > 0 && !sbRange.contains(sideboardSize)) {
|
||||||
|
return sbRange.getMinimum() == sbRange.getMaximum()
|
||||||
|
? String.format("must have a sideboard of %d cards or no sideboard at all", sbRange.getMaximum())
|
||||||
|
: String.format("must have a sideboard of %d to %d cards or no sideboard at all", sbRange.getMinimum(), sbRange.getMaximum());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlaneSectionConformanceProblem(CardPool planes) {
|
||||||
|
//Must contain at least 10 planes/phenomenons, but max 2 phenomenons. Singleton.
|
||||||
|
if (planes == null || planes.countAll() < 10) {
|
||||||
|
return "should have at least 10 planes";
|
||||||
|
}
|
||||||
|
int phenoms = 0;
|
||||||
|
for (Entry<PaperCard, Integer> cp : planes) {
|
||||||
|
if (cp.getKey().getRules().getType().typeContains(CardType.CoreType.Phenomenon)) {
|
||||||
|
phenoms++;
|
||||||
|
}
|
||||||
|
if (cp.getValue() > 1) {
|
||||||
|
return "must not contain multiple copies of any Plane or Phenomena";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (phenoms > 2) {
|
||||||
|
return "must not contain more than 2 Phenomena";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,11 @@ public class DeckGroup extends DeckBase {
|
|||||||
}
|
}
|
||||||
Collections.sort(aiDecks, comparator);
|
Collections.sort(aiDecks, comparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType() {
|
||||||
|
return "Group of decks";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cloneFieldsTo(final DeckBase clone) {
|
protected void cloneFieldsTo(final DeckBase clone) {
|
||||||
@@ -134,5 +139,12 @@ public class DeckGroup extends DeckBase {
|
|||||||
return arg1.getName();
|
return arg1.getName();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static final Function<DeckGroup, Deck> FN_HUMAN_DECK = new Function<DeckGroup, Deck>() {
|
||||||
|
@Override
|
||||||
|
public Deck apply(DeckGroup arg1) {
|
||||||
|
return arg1.getHumanDeck();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
|
import forge.card.CardDb.SetPreference;
|
||||||
import forge.card.ICardDatabase;
|
import forge.card.ICardDatabase;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
@@ -163,13 +164,19 @@ public class DeckRecognizer {
|
|||||||
//private static final Pattern READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)");
|
//private static final Pattern READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)");
|
||||||
|
|
||||||
|
|
||||||
private final boolean useLastSet;
|
private final SetPreference useLastSet;
|
||||||
private final ICardDatabase db;
|
private final ICardDatabase db;
|
||||||
private Date recognizeCardsPrintedBefore = null;
|
private Date recognizeCardsPrintedBefore = null;
|
||||||
|
|
||||||
public DeckRecognizer(boolean fromLatestSet) {
|
public DeckRecognizer(boolean fromLatestSet, boolean onlyCoreAndExp, CardDb db) {
|
||||||
useLastSet = fromLatestSet;
|
if(!fromLatestSet)
|
||||||
db = CardDb.instance();
|
useLastSet = null;
|
||||||
|
else if (onlyCoreAndExp)
|
||||||
|
useLastSet = SetPreference.LatestCoreExp;
|
||||||
|
else
|
||||||
|
useLastSet = SetPreference.Latest;
|
||||||
|
|
||||||
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token recognizeLine(final String rawLine) {
|
public Token recognizeLine(final String rawLine) {
|
||||||
@@ -202,9 +209,7 @@ public class DeckRecognizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PaperCard tryGetCard(String text) {
|
private PaperCard tryGetCard(String text) {
|
||||||
if(recognizeCardsPrintedBefore != null )
|
return db.getCardFromEdition(text, recognizeCardsPrintedBefore, useLastSet);
|
||||||
return db.tryGetCardPrintedByDate(text, useLastSet, recognizeCardsPrintedBefore);
|
|
||||||
return db.tryGetCard(text, useLastSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token recognizePossibleNameAndNumber(final String name, final int n) {
|
private Token recognizePossibleNameAndNumber(final String name, final int n) {
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck.generation;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.card.ICardDatabase;
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Generate2ColorDeck class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DeckGenerator2Color extends DeckGeneratorBase {
|
||||||
|
@Override protected final float getLandsPercentage() { return 0.39f; }
|
||||||
|
@Override protected final float getCreatPercentage() { return 0.36f; }
|
||||||
|
@Override protected final float getSpellPercentage() { return 0.25f; }
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<ImmutablePair<FilterCMC, Integer>> cmcRelativeWeights = Lists.newArrayList(
|
||||||
|
ImmutablePair.of(new FilterCMC(0, 2), 6),
|
||||||
|
ImmutablePair.of(new FilterCMC(3, 4), 4),
|
||||||
|
ImmutablePair.of(new FilterCMC(5, 6), 2),
|
||||||
|
ImmutablePair.of(new FilterCMC(7, 20), 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// mana curve of the card pool
|
||||||
|
// 20x 0 - 2
|
||||||
|
// 16x 3 - 4
|
||||||
|
// 12x 5 - 6
|
||||||
|
// 4x 7 - 20
|
||||||
|
// = 52x - card pool (before further random filtering)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor for Generate2ColorDeck.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param clr1
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
* @param clr2
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
*/
|
||||||
|
public DeckGenerator2Color(ICardDatabase cardDb, final String clr1, final String clr2) {
|
||||||
|
super(cardDb);
|
||||||
|
int c1 = MagicColor.fromName(clr1);
|
||||||
|
int c2 = MagicColor.fromName(clr2);
|
||||||
|
|
||||||
|
if( c1 == 0 && c2 == 0) {
|
||||||
|
int color1 = r.nextInt(5);
|
||||||
|
int color2 = (color1 + 1 + r.nextInt(4)) % 5;
|
||||||
|
colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2);
|
||||||
|
} else if ( c1 == 0 || c2 == 0 ) {
|
||||||
|
byte knownColor = (byte) (c1 | c2);
|
||||||
|
int color1 = Arrays.binarySearch(MagicColor.WUBRG, knownColor);
|
||||||
|
int color2 = (color1 + 1 + r.nextInt(4)) % 5;
|
||||||
|
colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2);
|
||||||
|
} else {
|
||||||
|
colors = ColorSet.fromMask(c1 | c2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final CardPool getDeck(final int size, final boolean forAi) {
|
||||||
|
addCreaturesAndSpells(size, cmcRelativeWeights, forAi);
|
||||||
|
|
||||||
|
// Add lands
|
||||||
|
int numLands = Math.round(size * getLandsPercentage());
|
||||||
|
adjustDeckSize(size - numLands);
|
||||||
|
tmpDeck.append(String.format("Adjusted deck size to: %d, should add %d land(s)%n", size - numLands, numLands));
|
||||||
|
|
||||||
|
// Add dual lands
|
||||||
|
List<String> duals = getDualLandList();
|
||||||
|
for (String s : duals) {
|
||||||
|
this.cardCounts.put(s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dblsAdded = addSomeStr((numLands / 6), duals);
|
||||||
|
numLands -= dblsAdded;
|
||||||
|
|
||||||
|
addBasicLand(numLands);
|
||||||
|
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
|
||||||
|
|
||||||
|
//System.out.println(tmpDeck.toString());
|
||||||
|
|
||||||
|
return tDeck;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck.generation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.card.ICardDatabase;
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Generate3ColorDeck class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DeckGenerator3Color extends DeckGeneratorBase {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<ImmutablePair<FilterCMC, Integer>> cmcLevels = Lists.newArrayList(
|
||||||
|
ImmutablePair.of(new FilterCMC(0, 2), 12),
|
||||||
|
ImmutablePair.of(new FilterCMC(3, 5), 9),
|
||||||
|
ImmutablePair.of(new FilterCMC(6, 20), 3)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor for Generate3ColorDeck.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param clr1
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
* @param clr2
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
* @param clr3
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
*/
|
||||||
|
public DeckGenerator3Color(ICardDatabase cardDb, final String clr1, final String clr2, final String clr3) {
|
||||||
|
super(cardDb);
|
||||||
|
int c1 = MagicColor.fromName(clr1);
|
||||||
|
int c2 = MagicColor.fromName(clr2);
|
||||||
|
int c3 = MagicColor.fromName(clr3);
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
int combo = c1 | c2 | c3;
|
||||||
|
|
||||||
|
ColorSet param = ColorSet.fromMask(combo);
|
||||||
|
switch(param.countColors()) {
|
||||||
|
case 3:
|
||||||
|
colors = param;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
int color1 = r.nextInt(5);
|
||||||
|
int color2 = (color1 + 1 + r.nextInt(4)) % 5;
|
||||||
|
colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2).inverse();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
do {
|
||||||
|
rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5);
|
||||||
|
} while ( rc == combo );
|
||||||
|
combo |= rc;
|
||||||
|
// fall-through
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
do {
|
||||||
|
rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5);
|
||||||
|
} while ( (rc & combo) != 0 );
|
||||||
|
combo |= rc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
colors = ColorSet.fromMask(combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final CardPool getDeck(final int size, final boolean forAi) {
|
||||||
|
addCreaturesAndSpells(size, cmcLevels, forAi);
|
||||||
|
|
||||||
|
// Add lands
|
||||||
|
int numLands = Math.round(size * getLandsPercentage());
|
||||||
|
adjustDeckSize(size - numLands);
|
||||||
|
tmpDeck.append("numLands:").append(numLands).append("\n");
|
||||||
|
|
||||||
|
// Add dual lands
|
||||||
|
List<String> duals = getDualLandList();
|
||||||
|
for (String s : duals) {
|
||||||
|
this.cardCounts.put(s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dblsAdded = addSomeStr((numLands / 4), duals);
|
||||||
|
numLands -= dblsAdded;
|
||||||
|
|
||||||
|
addBasicLand(numLands);
|
||||||
|
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
|
||||||
|
return tDeck;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck.generation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.card.ICardDatabase;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Generate5ColorDeck class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DeckGenerator5Color extends DeckGeneratorBase {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<ImmutablePair<FilterCMC, Integer>> cmcLevels = Lists.newArrayList(
|
||||||
|
ImmutablePair.of(new FilterCMC(0, 2), 3),
|
||||||
|
ImmutablePair.of(new FilterCMC(3, 5), 2),
|
||||||
|
ImmutablePair.of(new FilterCMC(6, 20), 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// resulting mana curve of the card pool
|
||||||
|
// 30x 0 - 2
|
||||||
|
// 20x 3 - 5
|
||||||
|
// 10x 6 - 20
|
||||||
|
// =60x - card pool
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new generate5 color deck.
|
||||||
|
*/
|
||||||
|
public DeckGenerator5Color(ICardDatabase cardDb) {
|
||||||
|
super(cardDb);
|
||||||
|
colors = ColorSet.fromMask(0).inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final CardPool getDeck(final int size, final boolean forAi) {
|
||||||
|
addCreaturesAndSpells(size, cmcLevels, forAi);
|
||||||
|
|
||||||
|
// Add lands
|
||||||
|
int numLands = Math.round(size * getLandsPercentage());
|
||||||
|
adjustDeckSize(size - numLands);
|
||||||
|
tmpDeck.append("numLands:").append(numLands).append("\n");
|
||||||
|
|
||||||
|
// Add dual lands
|
||||||
|
List<String> duals = getDualLandList();
|
||||||
|
for (String s : duals) {
|
||||||
|
this.cardCounts.put(s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dblsAdded = addSomeStr((numLands / 4), duals);
|
||||||
|
numLands -= dblsAdded;
|
||||||
|
|
||||||
|
addBasicLand(numLands);
|
||||||
|
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
|
||||||
|
return tDeck;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck.generation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.card.CardRules;
|
||||||
|
import forge.card.CardRulesPredicates;
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.card.ICardDatabase;
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
import forge.util.ItemPool;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Generate2ColorDeck class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: Generate2ColorDeck.java 14959 2012-03-28 14:03:43Z Chris H. $
|
||||||
|
*/
|
||||||
|
public abstract class DeckGeneratorBase {
|
||||||
|
protected final Random r = MyRandom.getRandom();
|
||||||
|
protected final Map<String, Integer> cardCounts = new HashMap<String, Integer>();
|
||||||
|
protected int maxDuplicates = 4;
|
||||||
|
protected boolean useArtifacts = true;
|
||||||
|
|
||||||
|
protected ColorSet colors;
|
||||||
|
protected final CardPool tDeck = new CardPool();
|
||||||
|
protected final ICardDatabase cardDb;
|
||||||
|
|
||||||
|
// 2-colored deck generator has its own constants. The rest works fine with these ones
|
||||||
|
protected float getLandsPercentage() { return 0.44f; }
|
||||||
|
protected float getCreatPercentage() { return 0.34f; }
|
||||||
|
protected float getSpellPercentage() { return 0.22f; }
|
||||||
|
|
||||||
|
StringBuilder tmpDeck = new StringBuilder();
|
||||||
|
|
||||||
|
public DeckGeneratorBase(ICardDatabase cardDb) {
|
||||||
|
this.cardDb = cardDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSingleton(boolean singleton){
|
||||||
|
this.maxDuplicates = singleton ? 1 : 4;
|
||||||
|
}
|
||||||
|
public void setUseArtifacts(boolean value) {
|
||||||
|
this.useArtifacts = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addCreaturesAndSpells(int size, List<ImmutablePair<FilterCMC, Integer>> cmcLevels, boolean forAi) {
|
||||||
|
tmpDeck.append("Building deck of ").append(size).append("cards\n");
|
||||||
|
|
||||||
|
final Iterable<PaperCard> cards = selectCardsOfMatchingColorForPlayer(forAi);
|
||||||
|
// build subsets based on type
|
||||||
|
|
||||||
|
final Iterable<PaperCard> creatures = Iterables.filter(cards, Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES));
|
||||||
|
final int creatCnt = (int) Math.ceil(getCreatPercentage() * size);
|
||||||
|
tmpDeck.append("Creatures to add:").append(creatCnt).append("\n");
|
||||||
|
addCmcAdjusted(creatures, creatCnt, cmcLevels);
|
||||||
|
|
||||||
|
Predicate<PaperCard> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NONCREATURE_SPELL_FOR_GENERATOR, PaperCard.FN_GET_RULES);
|
||||||
|
final Iterable<PaperCard> spells = Iterables.filter(cards, preSpells);
|
||||||
|
final int spellCnt = (int) Math.ceil(getSpellPercentage() * size);
|
||||||
|
tmpDeck.append("Spells to add:").append(spellCnt).append("\n");
|
||||||
|
addCmcAdjusted(spells, spellCnt, cmcLevels);
|
||||||
|
|
||||||
|
tmpDeck.append(String.format("Current deck size: %d... should be %f%n", tDeck.countAll(), size * (getCreatPercentage() + getSpellPercentage())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardPool getDeck(final int size, final boolean forAi) {
|
||||||
|
return null; // all but theme deck do override this method
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addSome(int cnt, List<PaperCard> source) {
|
||||||
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
PaperCard cp;
|
||||||
|
int lc = 0;
|
||||||
|
int srcLen = source.size();
|
||||||
|
do {
|
||||||
|
cp = source.get(this.r.nextInt(srcLen));
|
||||||
|
lc++;
|
||||||
|
} while (this.cardCounts.get(cp.getName()) > this.maxDuplicates - 1 && lc <= 100);
|
||||||
|
|
||||||
|
if (lc > 100) {
|
||||||
|
throw new RuntimeException("Generate2ColorDeck : get2ColorDeck -- looped too much -- Cr12");
|
||||||
|
}
|
||||||
|
|
||||||
|
tDeck.add(cardDb.getCard(cp.getName()));
|
||||||
|
|
||||||
|
final int n = this.cardCounts.get(cp.getName());
|
||||||
|
this.cardCounts.put(cp.getName(), n + 1);
|
||||||
|
if( n + 1 == this.maxDuplicates )
|
||||||
|
source.remove(cp);
|
||||||
|
tmpDeck.append(String.format("(%d) %s [%s]%n", cp.getRules().getManaCost().getCMC(), cp.getName(), cp.getRules().getManaCost()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int addSomeStr(int cnt, List<String> source) {
|
||||||
|
int res = 0;
|
||||||
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
String s;
|
||||||
|
int lc = 0;
|
||||||
|
do {
|
||||||
|
s = source.get(this.r.nextInt(source.size()));
|
||||||
|
lc++;
|
||||||
|
} while ((this.cardCounts.get(s) > 3) && (lc <= 20));
|
||||||
|
// not an error if looped too much - could play singleton mode, with 6 slots for 3 non-basic lands.
|
||||||
|
|
||||||
|
tDeck.add(cardDb.getCard(s));
|
||||||
|
|
||||||
|
final int n = this.cardCounts.get(s);
|
||||||
|
this.cardCounts.put(s, n + 1);
|
||||||
|
tmpDeck.append(s + "\n");
|
||||||
|
res++;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addBasicLand(int cnt) {
|
||||||
|
tmpDeck.append(cnt).append(" basic lands remain").append("\n");
|
||||||
|
|
||||||
|
// attempt to optimize basic land counts according to colors of picked cards
|
||||||
|
final Map<String, Integer> clrCnts = countLands(tDeck);
|
||||||
|
// total of all ClrCnts
|
||||||
|
float totalColor = 0;
|
||||||
|
for (Entry<String, Integer> c : clrCnts.entrySet()) {
|
||||||
|
totalColor += c.getValue();
|
||||||
|
tmpDeck.append(c.getKey()).append(":").append(c.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDeck.append("totalColor:").append(totalColor).append("\n");
|
||||||
|
|
||||||
|
int landsLeft = cnt;
|
||||||
|
for (Entry<String, Integer> c : clrCnts.entrySet()) {
|
||||||
|
String basicLandName = c.getKey();
|
||||||
|
|
||||||
|
|
||||||
|
// calculate number of lands for each color
|
||||||
|
final int nLand = Math.min(landsLeft, Math.round(cnt * c.getValue() / totalColor));
|
||||||
|
tmpDeck.append("nLand-").append(basicLandName).append(":").append(nLand).append("\n");
|
||||||
|
|
||||||
|
// just to prevent a null exception by the deck size fixing code
|
||||||
|
this.cardCounts.put(basicLandName, nLand);
|
||||||
|
|
||||||
|
PaperCard cp = cardDb.getCard(basicLandName);
|
||||||
|
String basicLandSet = cp.getEdition();
|
||||||
|
|
||||||
|
for (int i=0; i < nLand; i++) {
|
||||||
|
tDeck.add(cardDb.getCard(cp.getName(), basicLandSet, -1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
landsLeft -= nLand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void adjustDeckSize(int targetSize) {
|
||||||
|
// fix under-sized or over-sized decks, due to integer arithmetic
|
||||||
|
int actualSize = tDeck.countAll();
|
||||||
|
if (actualSize < targetSize) {
|
||||||
|
final int diff = targetSize - actualSize;
|
||||||
|
addSome(diff, tDeck.toFlatList());
|
||||||
|
} else if (actualSize > targetSize) {
|
||||||
|
|
||||||
|
Predicate<PaperCard> exceptBasicLand = Predicates.not(Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
|
||||||
|
|
||||||
|
for (int i = 0; i < 3 && actualSize > targetSize; i++) {
|
||||||
|
Iterable<PaperCard> matchingCards = Iterables.filter(tDeck.toFlatList(), exceptBasicLand);
|
||||||
|
List<PaperCard> toRemove = Aggregates.random(matchingCards, actualSize - targetSize);
|
||||||
|
tDeck.removeAllFlat(toRemove);
|
||||||
|
|
||||||
|
for (PaperCard c : toRemove) {
|
||||||
|
tmpDeck.append("Removed:").append(c.getName()).append("\n");
|
||||||
|
}
|
||||||
|
actualSize = tDeck.countAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addCmcAdjusted(Iterable<PaperCard> source, int cnt, List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
|
||||||
|
int totalWeight = 0;
|
||||||
|
for (ImmutablePair<FilterCMC, Integer> pair : cmcLevels) {
|
||||||
|
totalWeight += pair.getRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
float variability = 0.6f; // if set to 1, you'll get minimum cards to choose from
|
||||||
|
float desiredWeight = (float)cnt / ( maxDuplicates * variability );
|
||||||
|
float desiredOverTotal = desiredWeight / totalWeight;
|
||||||
|
float requestedOverTotal = (float)cnt / totalWeight;
|
||||||
|
|
||||||
|
for (ImmutablePair<FilterCMC, Integer> pair : cmcLevels) {
|
||||||
|
Iterable<PaperCard> matchingCards = Iterables.filter(source, Predicates.compose(pair.getLeft(), PaperCard.FN_GET_RULES));
|
||||||
|
int cmcCountForPool = (int) Math.ceil(pair.getRight().intValue() * desiredOverTotal);
|
||||||
|
|
||||||
|
int addOfThisCmc = Math.round(pair.getRight().intValue() * requestedOverTotal);
|
||||||
|
tmpDeck.append(String.format("Adding %d cards for cmc range from a pool with %d cards:%n", addOfThisCmc, cmcCountForPool));
|
||||||
|
|
||||||
|
final List<PaperCard> curved = Aggregates.random(matchingCards, cmcCountForPool);
|
||||||
|
final List<PaperCard> curvedRandomized = Lists.newArrayList();
|
||||||
|
for (PaperCard c : curved) {
|
||||||
|
this.cardCounts.put(c.getName(), 0);
|
||||||
|
curvedRandomized.add(cardDb.getCard(c.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
addSome(addOfThisCmc, curvedRandomized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Iterable<PaperCard> selectCardsOfMatchingColorForPlayer(boolean forAi) {
|
||||||
|
|
||||||
|
// start with all cards
|
||||||
|
// remove cards that generated decks don't like
|
||||||
|
Predicate<CardRules> canPlay = forAi ? AI_CAN_PLAY : HUMAN_CAN_PLAY;
|
||||||
|
Predicate<CardRules> hasColor = new MatchColorIdentity(colors);
|
||||||
|
|
||||||
|
if (useArtifacts) {
|
||||||
|
hasColor = Predicates.or(hasColor, COLORLESS_CARDS);
|
||||||
|
}
|
||||||
|
return Iterables.filter(cardDb.getAllCards(), Predicates.compose(Predicates.and(canPlay, hasColor), PaperCard.FN_GET_RULES));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Map<String, Integer> countLands(ItemPool<PaperCard> outList) {
|
||||||
|
// attempt to optimize basic land counts according
|
||||||
|
// to color representation
|
||||||
|
|
||||||
|
Map<String, Integer> res = new TreeMap<String, Integer>();
|
||||||
|
// count each card color using mana costs
|
||||||
|
// TODO: count hybrid mana differently?
|
||||||
|
for (Entry<PaperCard, Integer> cpe : outList) {
|
||||||
|
|
||||||
|
int profile = cpe.getKey().getRules().getManaCost().getColorProfile();
|
||||||
|
|
||||||
|
if ((profile & MagicColor.WHITE) != 0) {
|
||||||
|
increment(res, MagicColor.Constant.BASIC_LANDS.get(0), cpe.getValue());
|
||||||
|
} else if ((profile & MagicColor.BLUE) != 0) {
|
||||||
|
increment(res, MagicColor.Constant.BASIC_LANDS.get(1), cpe.getValue());
|
||||||
|
} else if ((profile & MagicColor.BLACK) != 0) {
|
||||||
|
increment(res, MagicColor.Constant.BASIC_LANDS.get(2), cpe.getValue());
|
||||||
|
} else if ((profile & MagicColor.RED) != 0) {
|
||||||
|
increment(res, MagicColor.Constant.BASIC_LANDS.get(3), cpe.getValue());
|
||||||
|
} else if ((profile & MagicColor.GREEN) != 0) {
|
||||||
|
increment(res, MagicColor.Constant.BASIC_LANDS.get(4), cpe.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void increment(Map<String, Integer> map, String key, int delta)
|
||||||
|
{
|
||||||
|
final Integer boxed = map.get(key);
|
||||||
|
map.put(key, boxed == null ? delta : boxed.intValue() + delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Predicate<CardRules> AI_CAN_PLAY = new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(CardRules c) {
|
||||||
|
return !c.getAiHints().getRemAIDecks() && !c.getAiHints().getRemRandomDecks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Predicate<CardRules> HUMAN_CAN_PLAY = new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(CardRules c) {
|
||||||
|
return !c.getAiHints().getRemRandomDecks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Predicate<CardRules> COLORLESS_CARDS = new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(CardRules c) {
|
||||||
|
ManaCost mc = c.getManaCost();
|
||||||
|
return c.getColorIdentity().isColorless() && !mc.isNoCost();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static class MatchColorIdentity implements Predicate<CardRules> {
|
||||||
|
private final ColorSet allowedColor;
|
||||||
|
|
||||||
|
public MatchColorIdentity(ColorSet color) {
|
||||||
|
allowedColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(CardRules subject) {
|
||||||
|
ManaCost mc = subject.getManaCost();
|
||||||
|
return !mc.isPureGeneric() && allowedColor.containsAllColorsFrom(subject.getColorIdentity().getColor());
|
||||||
|
//return mc.canBePaidWithAvaliable(allowedColor);
|
||||||
|
// return allowedColor.containsAllColorsFrom(mc.getColorProfile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FilterCMC implements Predicate<CardRules> {
|
||||||
|
private final int min;
|
||||||
|
private final int max;
|
||||||
|
|
||||||
|
public FilterCMC(int from, int to) {
|
||||||
|
min = from;
|
||||||
|
max = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(CardRules c) {
|
||||||
|
ManaCost mc = c.getManaCost();
|
||||||
|
int cmc = mc.getCMC();
|
||||||
|
return cmc >= min && cmc <= max && !mc.isNoCost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Integer, String[]> dualLands = new HashMap<Integer, String[]>();
|
||||||
|
static {
|
||||||
|
dualLands.put(MagicColor.WHITE | MagicColor.BLUE, new String[] { "Tundra", "Hallowed Fountain", "Flooded Strand" });
|
||||||
|
dualLands.put(MagicColor.BLACK | MagicColor.BLUE, new String[] { "Underground Sea", "Watery Grave", "Polluted Delta" });
|
||||||
|
dualLands.put(MagicColor.BLACK | MagicColor.RED, new String[] { "Badlands", "Blood Crypt", "Bloodstained Mire" });
|
||||||
|
dualLands.put(MagicColor.GREEN | MagicColor.RED, new String[] { "Taiga", "Stomping Ground", "Wooded Foothills" });
|
||||||
|
dualLands.put(MagicColor.GREEN | MagicColor.WHITE, new String[] { "Savannah", "Temple Garden", "Windswept Heath" });
|
||||||
|
|
||||||
|
dualLands.put(MagicColor.WHITE | MagicColor.BLACK, new String[] { "Scrubland", "Godless Shrine", "Marsh Flats" });
|
||||||
|
dualLands.put(MagicColor.BLUE | MagicColor.RED, new String[] { "Volcanic Island", "Steam Vents", "Scalding Tarn" });
|
||||||
|
dualLands.put(MagicColor.BLACK | MagicColor.GREEN, new String[] { "Bayou", "Overgrown Tomb", "Verdant Catacombs" });
|
||||||
|
dualLands.put(MagicColor.WHITE | MagicColor.RED, new String[] { "Plateau", "Sacred Foundry", "Arid Mesa" });
|
||||||
|
dualLands.put(MagicColor.GREEN | MagicColor.BLUE, new String[] { "Tropical Island", "Breeding Pool", "Misty Rainforest" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of dual lands for this color combo.
|
||||||
|
*
|
||||||
|
* @param color
|
||||||
|
* the color
|
||||||
|
* @return dual land names
|
||||||
|
*/
|
||||||
|
protected List<String> getDualLandList() {
|
||||||
|
|
||||||
|
final List<String> dLands = new ArrayList<String>();
|
||||||
|
|
||||||
|
if (colors.countColors() > 3) {
|
||||||
|
dLands.add("Rupture Spire");
|
||||||
|
dLands.add("Undiscovered Paradise");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colors.countColors() > 2) {
|
||||||
|
dLands.add("Evolving Wilds");
|
||||||
|
dLands.add("Terramorphic Expanse");
|
||||||
|
}
|
||||||
|
for (Entry<Integer, String[]> dual : dualLands.entrySet()) {
|
||||||
|
if (colors.hasAllColors(dual.getKey())) {
|
||||||
|
for (String s : dual.getValue()) {
|
||||||
|
dLands.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dLands;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all dual lands that do not match this color combo.
|
||||||
|
*
|
||||||
|
* @param color
|
||||||
|
* the color
|
||||||
|
* @return dual land names
|
||||||
|
*/
|
||||||
|
protected List<String> getInverseDualLandList() {
|
||||||
|
|
||||||
|
final List<String> dLands = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (Entry<Integer, String[]> dual : dualLands.entrySet()) {
|
||||||
|
if (!colors.hasAllColors(dual.getKey())) {
|
||||||
|
for (String s : dual.getValue()) {
|
||||||
|
dLands.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dLands;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck.generation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.card.ICardDatabase;
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Generate2ColorDeck class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: Generate2ColorDeck.java 19765 2013-02-20 03:01:37Z myk $
|
||||||
|
*/
|
||||||
|
public class DeckGeneratorMonoColor extends DeckGeneratorBase {
|
||||||
|
@Override protected final float getLandsPercentage() { return 0.39f; }
|
||||||
|
@Override protected final float getCreatPercentage() { return 0.36f; }
|
||||||
|
@Override protected final float getSpellPercentage() { return 0.25f; }
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<ImmutablePair<FilterCMC, Integer>> cmcLevels = Lists.newArrayList(
|
||||||
|
ImmutablePair.of(new FilterCMC(0, 2), 10),
|
||||||
|
ImmutablePair.of(new FilterCMC(3, 4), 8),
|
||||||
|
ImmutablePair.of(new FilterCMC(5, 6), 5),
|
||||||
|
ImmutablePair.of(new FilterCMC(7, 20), 3)
|
||||||
|
);
|
||||||
|
|
||||||
|
// mana curve of the card pool
|
||||||
|
// 20x 0 - 2
|
||||||
|
// 16x 3 - 4
|
||||||
|
// 12x 5 - 6
|
||||||
|
// 4x 7 - 20
|
||||||
|
// = 52x - card pool (before further random filtering)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor for Generate2ColorDeck.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param clr1
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
* @param clr2
|
||||||
|
* a {@link java.lang.String} object.
|
||||||
|
*/
|
||||||
|
public DeckGeneratorMonoColor(ICardDatabase cardDb, final String clr1) {
|
||||||
|
super(cardDb);
|
||||||
|
if (MagicColor.fromName(clr1) == 0) {
|
||||||
|
int color1 = r.nextInt(5);
|
||||||
|
colors = ColorSet.fromMask(MagicColor.WHITE << color1);
|
||||||
|
} else {
|
||||||
|
colors = ColorSet.fromNames(clr1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final CardPool getDeck(final int size, final boolean forAi) {
|
||||||
|
addCreaturesAndSpells(size, cmcLevels, forAi);
|
||||||
|
|
||||||
|
// Add lands
|
||||||
|
int numLands = (int) (getLandsPercentage() * size);
|
||||||
|
|
||||||
|
tmpDeck.append("numLands:").append(numLands).append("\n");
|
||||||
|
|
||||||
|
addBasicLand(numLands);
|
||||||
|
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
|
||||||
|
|
||||||
|
adjustDeckSize(size);
|
||||||
|
tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n");
|
||||||
|
|
||||||
|
return tDeck;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
/** Forge Card Game. */
|
||||||
|
package forge.deck.generation;
|
||||||
|
|
||||||
@@ -23,7 +23,6 @@ import java.util.TreeSet;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import forge.deck.DeckFormat;
|
import forge.deck.DeckFormat;
|
||||||
import forge.game.player.PlayerType;
|
|
||||||
import forge.util.FileSection;
|
import forge.util.FileSection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +47,6 @@ public class DeckFileHeader {
|
|||||||
private static final String PLAYER_TYPE = "PlayerType";
|
private static final String PLAYER_TYPE = "PlayerType";
|
||||||
|
|
||||||
private final DeckFormat deckType;
|
private final DeckFormat deckType;
|
||||||
private final PlayerType playerType;
|
|
||||||
private final boolean customPool;
|
private final boolean customPool;
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
@@ -56,6 +54,15 @@ public class DeckFileHeader {
|
|||||||
|
|
||||||
private final Set<String> tags;
|
private final Set<String> tags;
|
||||||
|
|
||||||
|
private final boolean intendedForAi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the intendedForAi
|
||||||
|
*/
|
||||||
|
public boolean isIntendedForAi() {
|
||||||
|
return intendedForAi;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for Constructor.
|
* TODO: Write javadoc for Constructor.
|
||||||
*
|
*
|
||||||
@@ -67,8 +74,7 @@ public class DeckFileHeader {
|
|||||||
this.comment = kvPairs.get(DeckFileHeader.COMMENT);
|
this.comment = kvPairs.get(DeckFileHeader.COMMENT);
|
||||||
this.deckType = DeckFormat.smartValueOf(kvPairs.get(DeckFileHeader.DECK_TYPE), DeckFormat.Constructed);
|
this.deckType = DeckFormat.smartValueOf(kvPairs.get(DeckFileHeader.DECK_TYPE), DeckFormat.Constructed);
|
||||||
this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL);
|
this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL);
|
||||||
boolean isForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE));
|
this.intendedForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE));
|
||||||
this.playerType = isForAi ? PlayerType.COMPUTER : PlayerType.HUMAN;
|
|
||||||
this.tags = new TreeSet<String>();
|
this.tags = new TreeSet<String>();
|
||||||
|
|
||||||
String rawTags = kvPairs.get(DeckFileHeader.TAGS);
|
String rawTags = kvPairs.get(DeckFileHeader.TAGS);
|
||||||
@@ -81,15 +87,6 @@ public class DeckFileHeader {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the player type.
|
|
||||||
*
|
|
||||||
* @return the player type
|
|
||||||
*/
|
|
||||||
public final PlayerType getPlayerType() {
|
|
||||||
return this.playerType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if is custom pool.
|
* Checks if is custom pool.
|
||||||
*
|
*
|
||||||
114
forge-core/src/main/java/forge/deck/io/DeckSerializer.java
Normal file
114
forge-core/src/main/java/forge/deck/io/DeckSerializer.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package forge.deck.io;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.card.CardDb;
|
||||||
|
import forge.deck.CardPool;
|
||||||
|
import forge.deck.Deck;
|
||||||
|
import forge.deck.DeckSection;
|
||||||
|
import forge.item.IPaperCard;
|
||||||
|
import forge.util.FileSection;
|
||||||
|
import forge.util.FileSectionManual;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
|
||||||
|
public class DeckSerializer {
|
||||||
|
|
||||||
|
public static void writeDeck(final Deck d, final File f) {
|
||||||
|
FileUtil.writeFile(f, serializeDeck(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeckFileHeader readDeckMetadata(final Map<String, List<String>> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final List<String> metadata = map.get("metadata");
|
||||||
|
if (metadata != null) {
|
||||||
|
return new DeckFileHeader(FileSection.parse(metadata, "="));
|
||||||
|
}
|
||||||
|
final List<String> general = map.get("general");
|
||||||
|
if (general != null) {
|
||||||
|
final FileSectionManual fs = new FileSectionManual();
|
||||||
|
fs.put(DeckFileHeader.NAME, StringUtils.join(map.get(""), " "));
|
||||||
|
fs.put(DeckFileHeader.DECK_TYPE, StringUtils.join(general, " "));
|
||||||
|
return new DeckFileHeader(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> serializeDeck(Deck d) {
|
||||||
|
final List<String> out = new ArrayList<String>();
|
||||||
|
out.add(String.format("[metadata]"));
|
||||||
|
|
||||||
|
out.add(String.format("%s=%s", DeckFileHeader.NAME, d.getName().replaceAll("\n", "")));
|
||||||
|
// these are optional
|
||||||
|
if (d.getComment() != null) {
|
||||||
|
out.add(String.format("%s=%s", DeckFileHeader.COMMENT, d.getComment().replaceAll("\n", "")));
|
||||||
|
}
|
||||||
|
if (!d.getTags().isEmpty()) {
|
||||||
|
out.add(String.format("%s=%s", DeckFileHeader.TAGS, StringUtils.join(d.getTags(), DeckFileHeader.TAGS_SEPARATOR)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Entry<DeckSection, CardPool> s : d) {
|
||||||
|
out.add(String.format("[%s]", s.getKey().toString()));
|
||||||
|
out.add(s.getValue().toCardList(System.getProperty("line.separator")));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Deck fromFile(final File deckFile) {
|
||||||
|
return fromSections(FileSection.parseSections(FileUtil.readFile(deckFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Deck fromSections(final Map<String, List<String>> sections) {
|
||||||
|
if (sections == null || sections.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final DeckFileHeader dh = readDeckMetadata(sections);
|
||||||
|
if (dh == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Deck d = new Deck(dh.getName());
|
||||||
|
d.setComment(dh.getComment());
|
||||||
|
d.getTags().addAll(dh.getTags());
|
||||||
|
|
||||||
|
boolean hasExplicitlySpecifiedSet = false;
|
||||||
|
|
||||||
|
for (Entry<String, List<String>> s : sections.entrySet()) {
|
||||||
|
DeckSection sec = DeckSection.smartValueOf(s.getKey());
|
||||||
|
if (sec == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String k : s.getValue())
|
||||||
|
if ( k.indexOf(CardDb.NameSetSeparator) > 0 )
|
||||||
|
hasExplicitlySpecifiedSet = true;
|
||||||
|
|
||||||
|
CardPool pool = CardPool.fromCardList(s.getValue());
|
||||||
|
// I used to store planes and schemes under sideboard header, so this will assign them to a correct section
|
||||||
|
IPaperCard sample = pool.get(0);
|
||||||
|
if (sample != null && ( sample.getRules().getType().isPlane() || sample.getRules().getType().isPhenomenon())) {
|
||||||
|
sec = DeckSection.Planes;
|
||||||
|
}
|
||||||
|
if (sample != null && sample.getRules().getType().isScheme()) {
|
||||||
|
sec = DeckSection.Schemes;
|
||||||
|
}
|
||||||
|
|
||||||
|
d.putSection(sec, pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasExplicitlySpecifiedSet) {
|
||||||
|
d.convertByXitaxMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
109
forge-core/src/main/java/forge/deck/io/DeckStorage.java
Normal file
109
forge-core/src/main/java/forge/deck/io/DeckStorage.java
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.deck.io;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import forge.deck.Deck;
|
||||||
|
import forge.util.FileSection;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
import forge.util.IItemReader;
|
||||||
|
import forge.util.IItemSerializer;
|
||||||
|
import forge.util.storage.StorageReaderFolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class knows how to make a file out of a deck object and vice versa.
|
||||||
|
*/
|
||||||
|
public class DeckStorage extends StorageReaderFolder<Deck> implements IItemSerializer<Deck> {
|
||||||
|
private final boolean moveWronglyNamedDecks;
|
||||||
|
public static final String FILE_EXTENSION = ".dck";
|
||||||
|
|
||||||
|
/** Constant <code>DCKFileFilter</code>. */
|
||||||
|
public static final FilenameFilter DCK_FILE_FILTER = new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(final File dir, final String name) {
|
||||||
|
return name.endsWith(FILE_EXTENSION);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public DeckStorage(final File deckDir0) {
|
||||||
|
this(deckDir0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeckStorage(final File deckDir0, boolean moveWrongDecks) {
|
||||||
|
super(deckDir0, Deck.FN_NAME_SELECTOR);
|
||||||
|
moveWronglyNamedDecks = moveWrongDecks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.util.storage.StorageReaderBase#getReaderForFolder(java.io.File)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IItemReader<Deck> getReaderForFolder(File subfolder) {
|
||||||
|
if ( !subfolder.getParentFile().equals(directory) )
|
||||||
|
throw new UnsupportedOperationException("Only child folders of " + directory + " may be processed");
|
||||||
|
return new DeckStorage(subfolder, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(final Deck unit) {
|
||||||
|
DeckSerializer.writeDeck(unit, this.makeFileFor(unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void erase(final Deck unit) {
|
||||||
|
this.makeFileFor(unit).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File makeFileFor(final Deck deck) {
|
||||||
|
return new File(this.directory, deck.getBestFileName() + FILE_EXTENSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Deck read(final File file) {
|
||||||
|
final Map<String, List<String>> sections = FileSection.parseSections(FileUtil.readFile(file));
|
||||||
|
Deck result = DeckSerializer.fromSections(sections);
|
||||||
|
|
||||||
|
if (moveWronglyNamedDecks) {
|
||||||
|
adjustFileLocation(file, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustFileLocation(final File file, final Deck result) {
|
||||||
|
if (result == null) {
|
||||||
|
file.delete();
|
||||||
|
} else {
|
||||||
|
String destFilename = result.getBestFileName() + FILE_EXTENSION;
|
||||||
|
if (!file.getName().equals(destFilename)) {
|
||||||
|
file.renameTo(new File(file.getParentFile().getParentFile(), destFilename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FilenameFilter getFileFilter() {
|
||||||
|
return DCK_FILE_FILTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
8
forge-core/src/main/java/forge/deck/io/package-info.java
Normal file
8
forge-core/src/main/java/forge/deck/io/package-info.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Max
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package forge.deck.io;
|
||||||
8
forge-core/src/main/java/forge/deck/package-info.java
Normal file
8
forge-core/src/main/java/forge/deck/package-info.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Max
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package forge.deck;
|
||||||
@@ -20,26 +20,25 @@ package forge.item;
|
|||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
import forge.Singletons;
|
import forge.StaticData;
|
||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.card.SealedProductTemplate;
|
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
public class BoosterPack extends OpenablePack {
|
public class BoosterPack extends SealedProduct {
|
||||||
private final int artIndex;
|
private final int artIndex;
|
||||||
private final int hash;
|
private final int hash;
|
||||||
|
|
||||||
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() {
|
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() {
|
||||||
@Override
|
@Override
|
||||||
public BoosterPack apply(final CardEdition arg1) {
|
public BoosterPack apply(final CardEdition arg1) {
|
||||||
SealedProductTemplate d = Singletons.getModel().getBoosters().get(arg1.getCode());
|
Template d = StaticData.instance().getBoosters().get(arg1.getCode());
|
||||||
return new BoosterPack(arg1.getName(), d);
|
return new BoosterPack(arg1.getName(), d);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BoosterPack(final String name0, final SealedProductTemplate boosterData) {
|
public BoosterPack(final String name0, final Template boosterData) {
|
||||||
super(name0, boosterData);
|
super(name0, boosterData);
|
||||||
int maxIdx = Singletons.getModel().getEditions().get(boosterData.getEdition()).getCntBoosterPictures();
|
int maxIdx = StaticData.instance().getEditions().get(boosterData.getEdition()).getCntBoosterPictures();
|
||||||
artIndex = MyRandom.getRandom().nextInt(maxIdx) + 1;
|
artIndex = MyRandom.getRandom().nextInt(maxIdx) + 1;
|
||||||
hash = super.hashCode() ^ artIndex;
|
hash = super.hashCode() ^ artIndex;
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ public class BoosterPack extends OpenablePack {
|
|||||||
return new BoosterPack(name, contents);
|
return new BoosterPack(name, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SealedProductTemplate getBoosterData() {
|
public Template getBoosterData() {
|
||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
141
forge-core/src/main/java/forge/item/FatPack.java
Normal file
141
forge-core/src/main/java/forge/item/FatPack.java
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package forge.item;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.card.BoosterGenerator;
|
||||||
|
import forge.card.CardEdition;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
import forge.util.storage.StorageReaderFile;
|
||||||
|
|
||||||
|
public class FatPack extends SealedProduct {
|
||||||
|
public static final Function<CardEdition, FatPack> FN_FROM_SET = new Function<CardEdition, FatPack>() {
|
||||||
|
@Override
|
||||||
|
public FatPack apply(final CardEdition arg1) {
|
||||||
|
FatPack.Template d = StaticData.instance().getFatPacks().get(arg1.getCode());
|
||||||
|
return new FatPack(arg1.getName(), d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final FatPack.Template fpData;
|
||||||
|
|
||||||
|
public FatPack(final String name0, final FatPack.Template fpData0) {
|
||||||
|
super(name0, StaticData.instance().getBoosters().get(fpData0.getEdition()));
|
||||||
|
fpData = fpData0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return fpData.toString() + contents.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getItemType() {
|
||||||
|
return "Fat Pack";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<PaperCard> generate() {
|
||||||
|
List<PaperCard> result = new ArrayList<PaperCard>();
|
||||||
|
for (int i = 0; i < fpData.getCntBoosters(); i++) {
|
||||||
|
result.addAll(super.generate());
|
||||||
|
}
|
||||||
|
result.addAll(BoosterGenerator.getBoosterPack(fpData));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object clone() {
|
||||||
|
return new FatPack(name, fpData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalCards() {
|
||||||
|
return super.getTotalCards() * fpData.getCntBoosters() + fpData.getNumberOfCardsExpected();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Template extends SealedProduct.Template {
|
||||||
|
private final int cntBoosters;
|
||||||
|
|
||||||
|
|
||||||
|
public int getCntBoosters() { return cntBoosters; }
|
||||||
|
|
||||||
|
private Template(String edition, int boosters, Iterable<Pair<String, Integer>> itrSlots)
|
||||||
|
{
|
||||||
|
super(edition, itrSlots);
|
||||||
|
cntBoosters = boosters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Reader extends StorageReaderFile<Template> {
|
||||||
|
public Reader(String pathname) {
|
||||||
|
super(pathname, Template.FN_GET_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Template read(String line, int i) {
|
||||||
|
String[] headAndData = TextUtil.split(line, ':', 2);
|
||||||
|
final String edition = headAndData[0];
|
||||||
|
final String[] data = TextUtil.splitWithParenthesis(headAndData[1], ',');
|
||||||
|
int nBoosters = 6;
|
||||||
|
|
||||||
|
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
|
||||||
|
for(String slotDesc : data) {
|
||||||
|
String[] kv = TextUtil.split(slotDesc, ' ', 2);
|
||||||
|
if (kv[1].startsWith("Booster"))
|
||||||
|
nBoosters = Integer.parseInt(kv[0]);
|
||||||
|
else
|
||||||
|
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FatPack.Template(edition, nBoosters, slots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (0 >= cntBoosters) {
|
||||||
|
return "no cards";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
for(Pair<String, Integer> p : slots) {
|
||||||
|
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
|
||||||
|
}
|
||||||
|
// trim the last comma and space
|
||||||
|
if( s.length() > 0 )
|
||||||
|
s.replace(s.length() - 2, s.length(), "");
|
||||||
|
|
||||||
|
if (0 < cntBoosters) {
|
||||||
|
if( s.length() > 0 )
|
||||||
|
s.append(" and ");
|
||||||
|
|
||||||
|
s.append(cntBoosters).append(" booster packs ");
|
||||||
|
}
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,9 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.Card;
|
//import forge.Card;
|
||||||
import forge.card.CardRarity;
|
import forge.card.CardRarity;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
import forge.game.player.Player;
|
|
||||||
import forge.util.PredicateString;
|
import forge.util.PredicateString;
|
||||||
|
|
||||||
public interface IPaperCard extends InventoryItem {
|
public interface IPaperCard extends InventoryItem {
|
||||||
@@ -161,7 +160,4 @@ public interface IPaperCard extends InventoryItem {
|
|||||||
|
|
||||||
public abstract String getItemType();
|
public abstract String getItemType();
|
||||||
|
|
||||||
public abstract Card getMatchingForgeCard();
|
|
||||||
public abstract Card toForgeCard(Player owner);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,6 @@ public abstract class ItemPredicate {
|
|||||||
public static class Presets {
|
public static class Presets {
|
||||||
/** The Item IsPack. */
|
/** The Item IsPack. */
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static final Predicate<InventoryItem> IS_PACK = Predicates.or(IsBoosterPack, IsFatPack, IsTournamentPack);
|
public static final Predicate<InventoryItem> IS_PACK_OR_DECK = Predicates.or(IsBoosterPack, IsFatPack, IsTournamentPack, IsStarterDeck, IsPrebuiltDeck);
|
||||||
|
|
||||||
/** The Item IsDeck. */
|
|
||||||
public static final Predicate<InventoryItem> IS_DECK = Predicates.or(IsStarterDeck, IsPrebuiltDeck);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
193
forge-core/src/main/java/forge/item/PaperCard.java
Normal file
193
forge-core/src/main/java/forge/item/PaperCard.java
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.item;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.card.CardRarity;
|
||||||
|
import forge.card.CardRules;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade).
|
||||||
|
* <br><br>
|
||||||
|
* The full set of rules is in the CardRules class.
|
||||||
|
*
|
||||||
|
* @author Forge
|
||||||
|
*/
|
||||||
|
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
|
||||||
|
// Reference to rules
|
||||||
|
private final transient CardRules rules;
|
||||||
|
|
||||||
|
// These fields are kinda PK for PrintedCard
|
||||||
|
private final String name;
|
||||||
|
private final String edition;
|
||||||
|
private final int artIndex;
|
||||||
|
private final boolean foil;
|
||||||
|
|
||||||
|
// Calculated fields are below:
|
||||||
|
private final transient CardRarity rarity; // rarity is given in ctor when set is assigned
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEdition() {
|
||||||
|
return this.edition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArtIndex() {
|
||||||
|
return this.artIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFoil() {
|
||||||
|
return this.foil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isToken() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CardRules getRules() {
|
||||||
|
return this.rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CardRarity getRarity() {
|
||||||
|
return this.rarity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public String getImageKey() {
|
||||||
|
// return getImageLocator(getImageName(), getArtIndex(), true, false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType() {
|
||||||
|
return "Card";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lambda to get rules for selects from list of printed cards.
|
||||||
|
*/
|
||||||
|
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
|
||||||
|
@Override
|
||||||
|
public CardRules apply(final PaperCard from) {
|
||||||
|
return from.rules;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(final PaperCard from) {
|
||||||
|
return from.getName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index) {
|
||||||
|
this(c, edition0, rare, index, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
|
||||||
|
if ( edition0 == null || c == null || rare == null )
|
||||||
|
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
|
||||||
|
this.rules = c;
|
||||||
|
this.name = c.getName();
|
||||||
|
this.edition = edition0;
|
||||||
|
this.artIndex = index;
|
||||||
|
this.foil = foil;
|
||||||
|
this.rarity = rare;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Want this class to be a key for HashTable
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PaperCard other = (PaperCard) obj;
|
||||||
|
if (!this.name.equals(other.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.edition.equals(other.edition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((other.foil != this.foil) || (other.artIndex != this.artIndex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#hashCode()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int code = (this.name.hashCode() * 11) + (this.edition.hashCode() * 59) + (this.artIndex * 2);
|
||||||
|
if (this.foil) {
|
||||||
|
return code + 1;
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Object#toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name;
|
||||||
|
// cannot still decide, if this "name|set" format is needed anymore
|
||||||
|
// return String.format("%s|%s", name, cardSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compareTo(final IPaperCard o) {
|
||||||
|
final int nameCmp = this.getName().compareToIgnoreCase(o.getName());
|
||||||
|
if (0 != nameCmp) {
|
||||||
|
return nameCmp;
|
||||||
|
}
|
||||||
|
// TODO compare sets properly
|
||||||
|
int setDiff = this.edition.compareTo(o.getEdition());
|
||||||
|
if ( 0 != setDiff )
|
||||||
|
return setDiff;
|
||||||
|
|
||||||
|
return Integer.compare(artIndex, o.getArtIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,10 @@ package forge.item;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import forge.Card;
|
|
||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.card.CardRarity;
|
import forge.card.CardRarity;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
import forge.card.cardfactory.CardFactory;
|
|
||||||
import forge.game.player.Player;
|
|
||||||
|
|
||||||
public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
||||||
private String name;
|
private String name;
|
||||||
@@ -49,8 +47,8 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
|||||||
|
|
||||||
@Override public String getName() { return name; }
|
@Override public String getName() { return name; }
|
||||||
|
|
||||||
|
@Override public String toString() { return name; }
|
||||||
@Override public String getEdition() { return edition.getCode(); }
|
@Override public String getEdition() { return edition.getCode(); }
|
||||||
|
|
||||||
@Override public int getArtIndex() { return 0; } // This might change however
|
@Override public int getArtIndex() { return 0; } // This might change however
|
||||||
@Override public boolean isFoil() { return false; }
|
@Override public boolean isFoil() { return false; }
|
||||||
@Override public CardRules getRules() { return card; }
|
@Override public CardRules getRules() { return card; }
|
||||||
@@ -61,13 +59,6 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
|||||||
public String getImageFilename() { return imageFileName; }
|
public String getImageFilename() { return imageFileName; }
|
||||||
|
|
||||||
@Override public String getItemType() { return "Token"; }
|
@Override public String getItemType() { return "Token"; }
|
||||||
@Override public Card getMatchingForgeCard() { return toForgeCard(null); } // hope this won't be queried too frequently, so no cache
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Card toForgeCard(Player owner) {
|
|
||||||
final Card c = CardFactory.getCard(this, owner);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean isToken() { return true; }
|
@Override public boolean isToken() { return true; }
|
||||||
}
|
}
|
||||||
156
forge-core/src/main/java/forge/item/PreconDeck.java
Normal file
156
forge-core/src/main/java/forge/item/PreconDeck.java
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Nate
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.item;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.deck.Deck;
|
||||||
|
import forge.deck.io.DeckSerializer;
|
||||||
|
import forge.deck.io.DeckStorage;
|
||||||
|
import forge.util.FileSection;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
import forge.util.storage.StorageReaderFolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PreconDeck implements InventoryItemFromSet {
|
||||||
|
private final Deck deck;
|
||||||
|
private final String set;
|
||||||
|
private final String description;
|
||||||
|
private String imageFilename;
|
||||||
|
|
||||||
|
// private final SellRules recommendedDeals;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see forge.item.InventoryItemFromSet#getName()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.deck.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see forge.item.InventoryItem#getType()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getItemType() {
|
||||||
|
return "Prebuilt Deck";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.deck.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreconDeck(final Deck d, String set, String description) {
|
||||||
|
deck = d;
|
||||||
|
this.set = set;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Deck getDeck() {
|
||||||
|
return this.deck;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the recommended deals.
|
||||||
|
*
|
||||||
|
* @return the recommended deals
|
||||||
|
*/
|
||||||
|
// public final SellRules getRecommendedDeals() {
|
||||||
|
// return this.recommendedDeals;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public final String getImageFilename() {
|
||||||
|
return imageFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see forge.item.InventoryItemFromSet#getSet()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getEdition() {
|
||||||
|
return this.set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description.
|
||||||
|
*
|
||||||
|
* @return the description
|
||||||
|
*/
|
||||||
|
public final String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Function<PreconDeck, String> FN_NAME_SELECTOR = new Function<PreconDeck, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(PreconDeck arg1) {
|
||||||
|
return arg1.getName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static class Reader extends StorageReaderFolder<PreconDeck> {
|
||||||
|
public Reader(final File deckDir0) {
|
||||||
|
super(deckDir0, PreconDeck.FN_NAME_SELECTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PreconDeck read(final File file) {
|
||||||
|
return getPreconDeckFromSections(FileSection.parseSections(FileUtil.readFile(file)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be able to read "shops" section in overloads
|
||||||
|
protected PreconDeck getPreconDeckFromSections(final Map<String, List<String>> sections) {
|
||||||
|
FileSection kv = FileSection.parse(sections.get("metadata"), "=");
|
||||||
|
String imageFilename = kv.get("Image");
|
||||||
|
String description = kv.get("Description");
|
||||||
|
String deckEdition = kv.get("set");
|
||||||
|
String set = deckEdition == null || StaticData.instance().getEditions().get(deckEdition.toUpperCase()) == null ? "n/a" : deckEdition;
|
||||||
|
PreconDeck result = new PreconDeck(DeckSerializer.fromSections(sections), set, description);
|
||||||
|
result.imageFilename = imageFilename;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FilenameFilter getFileFilter() {
|
||||||
|
return DeckStorage.DCK_FILE_FILTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Function<PreconDeck, Deck> FN_GET_DECK = new Function<PreconDeck, Deck>() {
|
||||||
|
@Override
|
||||||
|
public Deck apply(PreconDeck arg1) {
|
||||||
|
return arg1.getDeck();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
218
forge-core/src/main/java/forge/item/SealedProduct.java
Normal file
218
forge-core/src/main/java/forge/item/SealedProduct.java
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package forge.item;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.StaticData;
|
||||||
|
import forge.card.BoosterGenerator;
|
||||||
|
import forge.card.BoosterSlots;
|
||||||
|
import forge.card.CardRulesPredicates;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
import forge.util.storage.StorageReaderFile;
|
||||||
|
|
||||||
|
public abstract class SealedProduct implements InventoryItemFromSet {
|
||||||
|
protected final Template contents;
|
||||||
|
protected final String name;
|
||||||
|
private final int hash;
|
||||||
|
private List<PaperCard> cards = null;
|
||||||
|
|
||||||
|
public SealedProduct(String name0, Template boosterData) {
|
||||||
|
if (null == name0) { throw new IllegalArgumentException("name0 must not be null"); }
|
||||||
|
if (null == boosterData) { throw new IllegalArgumentException("boosterData must not be null"); }
|
||||||
|
contents = boosterData;
|
||||||
|
name = name0;
|
||||||
|
hash = name.hashCode() ^ getClass().hashCode() ^ contents.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getName() {
|
||||||
|
return name + " " + getItemType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return contents.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getEdition() {
|
||||||
|
return contents.getEdition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<PaperCard> getCards() {
|
||||||
|
if (null == cards) {
|
||||||
|
cards = generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalCards() {
|
||||||
|
return contents.getNumberOfCardsExpected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SealedProduct other = (SealedProduct)obj;
|
||||||
|
return name.equals(other.name) && contents.equals(other.contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<PaperCard> generate() {
|
||||||
|
return BoosterGenerator.getBoosterPack(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PaperCard getRandomBasicLand(final String setCode) {
|
||||||
|
return this.getRandomBasicLands(setCode, 1).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<PaperCard> getRandomBasicLands(final String setCode, final int count) {
|
||||||
|
Predicate<PaperCard> cardsRule = Predicates.and(
|
||||||
|
IPaperCard.Predicates.printedInSet(setCode),
|
||||||
|
Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES));
|
||||||
|
return Aggregates.random(Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), cardsRule), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Template {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final static Template genericBooster = new Template(null, Lists.newArrayList(
|
||||||
|
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
|
||||||
|
Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1)
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
protected final List<Pair<String, Integer>> slots;
|
||||||
|
protected final String name;
|
||||||
|
|
||||||
|
|
||||||
|
public final List<Pair<String, Integer>> getSlots() {
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getEdition() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public Template(Iterable<Pair<String, Integer>> itrSlots)
|
||||||
|
{
|
||||||
|
this(null, itrSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template(String name0, Iterable<Pair<String, Integer>> itrSlots)
|
||||||
|
{
|
||||||
|
slots = Lists.newArrayList(itrSlots);
|
||||||
|
name = name0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template(String code, String boosterDesc) {
|
||||||
|
this(code, Reader.parseSlots(boosterDesc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfCardsExpected() {
|
||||||
|
int sum = 0;
|
||||||
|
for(Pair<String, Integer> p : slots) {
|
||||||
|
sum += p.getRight().intValue();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Function<? super Template, String> FN_GET_NAME = new Function<Template, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(Template arg1) {
|
||||||
|
return arg1.name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
s.append("consisting of ");
|
||||||
|
for(Pair<String, Integer> p : slots) {
|
||||||
|
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim the last comma and space
|
||||||
|
s.replace(s.length() - 2, s.length(), "");
|
||||||
|
|
||||||
|
// put an 'and' before the previous comma
|
||||||
|
int lastCommaIdx = s.lastIndexOf(",");
|
||||||
|
if (0 < lastCommaIdx) {
|
||||||
|
s.replace(lastCommaIdx+1, lastCommaIdx+1, " and");
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static class Reader extends StorageReaderFile<Template> {
|
||||||
|
public Reader(File file) {
|
||||||
|
super(file, Template.FN_GET_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Pair<String, Integer>> parseSlots(String data) {
|
||||||
|
final String[] dataz = TextUtil.splitWithParenthesis(data, ',');
|
||||||
|
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
|
||||||
|
for(String slotDesc : dataz) {
|
||||||
|
String[] kv = TextUtil.splitWithParenthesis(slotDesc, ' ', 2);
|
||||||
|
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
|
||||||
|
}
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Template read(String line, int i) {
|
||||||
|
String[] headAndData = TextUtil.split(line, ':', 2);
|
||||||
|
return new Template(headAndData[0], parseSlots(headAndData[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,23 +21,22 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
import forge.Singletons;
|
import forge.StaticData;
|
||||||
import forge.card.BoosterGenerator;
|
import forge.card.BoosterGenerator;
|
||||||
import forge.card.CardEdition;
|
import forge.card.CardEdition;
|
||||||
import forge.card.SealedProductTemplate;
|
|
||||||
|
|
||||||
public class TournamentPack extends OpenablePack {
|
public class TournamentPack extends SealedProduct {
|
||||||
|
|
||||||
/** The Constant fnFromSet. */
|
/** The Constant fnFromSet. */
|
||||||
public static final Function<CardEdition, TournamentPack> FN_FROM_SET = new Function<CardEdition, TournamentPack>() {
|
public static final Function<CardEdition, TournamentPack> FN_FROM_SET = new Function<CardEdition, TournamentPack>() {
|
||||||
@Override
|
@Override
|
||||||
public TournamentPack apply(final CardEdition arg1) {
|
public TournamentPack apply(final CardEdition arg1) {
|
||||||
SealedProductTemplate d = Singletons.getModel().getTournamentPacks().get(arg1.getCode());
|
Template d = StaticData.instance().getTournamentPacks().get(arg1.getCode());
|
||||||
return new TournamentPack(arg1.getName(), d);
|
return new TournamentPack(arg1.getName(), d);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public TournamentPack(final String name0, final SealedProductTemplate boosterData) {
|
public TournamentPack(final String name0, final Template boosterData) {
|
||||||
super(name0, boosterData);
|
super(name0, boosterData);
|
||||||
}
|
}
|
||||||
|
|
||||||
8
forge-core/src/main/java/forge/item/package-info.java
Normal file
8
forge-core/src/main/java/forge/item/package-info.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Max
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package forge.item;
|
||||||
8
forge-core/src/main/java/forge/package-info.java
Normal file
8
forge-core/src/main/java/forge/package-info.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Max
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package forge;
|
||||||
@@ -79,8 +79,7 @@ public class Aggregates {
|
|||||||
if( null == source )
|
if( null == source )
|
||||||
return null;
|
return null;
|
||||||
Random rnd = MyRandom.getRandom();
|
Random rnd = MyRandom.getRandom();
|
||||||
if ( source instanceof List<?> )
|
if ( source instanceof List<?> ) {
|
||||||
{
|
|
||||||
List<T> src = (List<T>)source;
|
List<T> src = (List<T>)source;
|
||||||
int len = src.size();
|
int len = src.size();
|
||||||
switch(len) {
|
switch(len) {
|
||||||
@@ -88,33 +87,43 @@ public class Aggregates {
|
|||||||
case 1: return src.get(0);
|
case 1: return src.get(0);
|
||||||
default: return src.get(rnd.nextInt(len));
|
default: return src.get(rnd.nextInt(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
T candidate = null;
|
T candidate = null;
|
||||||
|
int lowest = Integer.MAX_VALUE;
|
||||||
for (final T item : source) {
|
for (final T item : source) {
|
||||||
if ((rnd.nextDouble() * ++n) < 1) {
|
int next = rnd.nextInt();
|
||||||
|
if(next < lowest) {
|
||||||
|
lowest = next;
|
||||||
candidate = item;
|
candidate = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get several random values
|
|
||||||
// should improve to make 1 pass over source and track N candidates at once
|
|
||||||
public static final <T> List<T> random(final Iterable<T> source, final int count) {
|
public static final <T> List<T> random(final Iterable<T> source, final int count) {
|
||||||
final List<T> result = new ArrayList<T>();
|
final List<T> result = new ArrayList<T>();
|
||||||
for (int i = 0; i < count; ++i) {
|
final int[] randoms = new int[count];
|
||||||
final T toAdd = Aggregates.random(source);
|
for(int i = 0; i < count; i++) {
|
||||||
if (toAdd == null) {
|
randoms[i] = Integer.MAX_VALUE;
|
||||||
break;
|
result.add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Random rnd = MyRandom.getRandom();
|
||||||
|
for(T item : source) {
|
||||||
|
int next = rnd.nextInt();
|
||||||
|
for(int i = 0; i < count; i++) {
|
||||||
|
if(next < randoms[i]) {
|
||||||
|
randoms[i] = next;
|
||||||
|
result.set(i, item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result.add(toAdd);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic
|
public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic
|
||||||
final Map<K, U> uniques = new Hashtable<K, U>();
|
final Map<K, U> uniques = new Hashtable<K, U>();
|
||||||
for (final U c : source) {
|
for (final U c : source) {
|
||||||
@@ -123,7 +132,6 @@ public class Aggregates {
|
|||||||
return uniques.values();
|
return uniques.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
|
public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
|
||||||
if (source == null) { return null; }
|
if (source == null) { return null; }
|
||||||
int max = Integer.MAX_VALUE;
|
int max = Integer.MAX_VALUE;
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package forge.model;
|
package forge.util;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package forge.util.maps;
|
package forge.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -31,8 +31,6 @@ import java.util.regex.Pattern;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import forge.error.BugReporter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* FileUtil class.
|
* FileUtil class.
|
||||||
@@ -109,17 +107,12 @@ public final class FileUtil {
|
|||||||
}
|
}
|
||||||
p.close();
|
p.close();
|
||||||
} catch (final Exception ex) {
|
} catch (final Exception ex) {
|
||||||
BugReporter.reportException(ex);
|
|
||||||
throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + file + " : " + ex);
|
throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + file + " : " + ex);
|
||||||
}
|
}
|
||||||
} // writeAllDecks()
|
} // writeAllDecks()
|
||||||
|
|
||||||
public static String readFileToString(String filename) {
|
public static String readFileToString(String filename) {
|
||||||
StringBuilder s = new StringBuilder();
|
return TextUtil.join(readFile(filename), "\n");
|
||||||
for (String line : readFile(filename)) {
|
|
||||||
s.append(line).append('\n');
|
|
||||||
}
|
|
||||||
return s.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> readFile(final String filename) {
|
public static List<String> readFile(final String filename) {
|
||||||
@@ -145,7 +138,6 @@ public final class FileUtil {
|
|||||||
}
|
}
|
||||||
return FileUtil.readAllLines(new FileReader(file), false);
|
return FileUtil.readAllLines(new FileReader(file), false);
|
||||||
} catch (final Exception ex) {
|
} catch (final Exception ex) {
|
||||||
BugReporter.reportException(ex);
|
|
||||||
throw new RuntimeException("FileUtil : readFile() error, " + ex);
|
throw new RuntimeException("FileUtil : readFile() error, " + ex);
|
||||||
}
|
}
|
||||||
} // readFile()
|
} // readFile()
|
||||||
@@ -180,7 +172,6 @@ public final class FileUtil {
|
|||||||
}
|
}
|
||||||
in.close();
|
in.close();
|
||||||
} catch (final IOException ex) {
|
} catch (final IOException ex) {
|
||||||
BugReporter.reportException(ex);
|
|
||||||
throw new RuntimeException("FileUtil : readAllLines() error, " + ex);
|
throw new RuntimeException("FileUtil : readAllLines() error, " + ex);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
381
forge-core/src/main/java/forge/util/ItemPool.java
Normal file
381
forge-core/src/main/java/forge/util/ItemPool.java
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
/*
|
||||||
|
* Forge: Play Magic: the Gathering.
|
||||||
|
* Copyright (C) 2011 Forge Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package forge.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
|
import forge.item.InventoryItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* ItemPool class.
|
||||||
|
* </p>
|
||||||
|
* Represents a list of items with amount of each
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* an Object
|
||||||
|
*/
|
||||||
|
public class ItemPool<T extends InventoryItem> implements Iterable<Entry<T, Integer>> {
|
||||||
|
|
||||||
|
/** The fn to printed. */
|
||||||
|
public final transient Function<Entry<T, Integer>, T> FN_GET_KEY = new Function<Entry<T, Integer>, T>() {
|
||||||
|
@Override
|
||||||
|
public T apply(final Entry<T, Integer> from) {
|
||||||
|
return from.getKey();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The fn to item name. */
|
||||||
|
public final transient Function<Entry<T, Integer>, String> FN_GET_NAME = new Function<Entry<T, Integer>, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(final Entry<T, Integer> from) {
|
||||||
|
return from.getKey().getName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The fn to count. */
|
||||||
|
public final transient Function<Entry<T, Integer>, Integer> FN_GET_COUNT = new Function<Entry<T, Integer>, Integer>() {
|
||||||
|
@Override
|
||||||
|
public Integer apply(final Entry<T, Integer> from) {
|
||||||
|
return from.getValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructors here
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ItemPool Constructor.
|
||||||
|
*
|
||||||
|
* @param cls
|
||||||
|
* a T
|
||||||
|
*/
|
||||||
|
public ItemPool(final Class<T> cls) {
|
||||||
|
this(new Hashtable<T, Integer>(), cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final ItemPool<Tin> from, final Class<Tout> clsHint) {
|
||||||
|
final ItemPool<Tout> result = new ItemPool<Tout>(clsHint);
|
||||||
|
if (from != null) {
|
||||||
|
for (final Entry<Tin, Integer> e : from) {
|
||||||
|
final Tin srcKey = e.getKey();
|
||||||
|
if (clsHint.isInstance(srcKey)) {
|
||||||
|
result.add((Tout) srcKey, e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final Iterable<Tin> from, final Class<Tout> clsHint) {
|
||||||
|
final ItemPool<Tout> result = new ItemPool<Tout>(clsHint);
|
||||||
|
if (from != null) {
|
||||||
|
for (final Tin srcKey : from) {
|
||||||
|
if (clsHint.isInstance(srcKey)) {
|
||||||
|
result.add((Tout) srcKey, Integer.valueOf(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ItemPool(final Map<T, Integer> inMap, final Class<T> cls) {
|
||||||
|
this.items = inMap;
|
||||||
|
this.myClass = cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data members
|
||||||
|
/** The items. */
|
||||||
|
protected final Map<T, Integer> items;
|
||||||
|
|
||||||
|
/** The my class. */
|
||||||
|
private final Class<T> myClass; // class does not keep this in runtime by
|
||||||
|
// itself
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterator.
|
||||||
|
*
|
||||||
|
* @return Iterator<Entry<T, Integer>>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final Iterator<Entry<T, Integer>> iterator() {
|
||||||
|
return this.items.entrySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items read only operations
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* contains.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* a T
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public final boolean contains(final T item) {
|
||||||
|
if (this.items == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.items.containsKey(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* count.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* a T
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public final int count(final T item) {
|
||||||
|
if (this.items == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final Integer boxed = this.items.get(item);
|
||||||
|
return boxed == null ? 0 : boxed.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* countAll.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public final int countAll() {
|
||||||
|
return countAll(null, myClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int countAll(Predicate<T> condition) {
|
||||||
|
return countAll(condition, myClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final <U extends InventoryItem> int countAll(Predicate<U> condition, Class<U> cls) {
|
||||||
|
int result = 0;
|
||||||
|
if (this.items != null) {
|
||||||
|
final boolean isSameClass = cls == myClass;
|
||||||
|
for (final Entry<T, Integer> kv : this) {
|
||||||
|
final T key = kv.getKey();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final U castKey = isSameClass || cls.isInstance(key) ? (U)key : null;
|
||||||
|
if (null == condition || castKey != null && condition.apply(castKey))
|
||||||
|
result += kv.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* countDistinct.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public final int countDistinct() {
|
||||||
|
return this.items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isEmpty.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public final boolean isEmpty() {
|
||||||
|
return (this.items == null) || this.items.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* toFlatList.
|
||||||
|
*
|
||||||
|
* @return List<T>
|
||||||
|
*/
|
||||||
|
public final List<T> toFlatList() {
|
||||||
|
final List<T> result = new ArrayList<T>();
|
||||||
|
for (final Entry<T, Integer> e : this) {
|
||||||
|
for (int i = 0; i < e.getValue(); i++) {
|
||||||
|
result.add(e.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the my class.
|
||||||
|
*
|
||||||
|
* @return the myClass
|
||||||
|
*/
|
||||||
|
public Class<T> getMyClass() {
|
||||||
|
return this.myClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Get item view.
|
||||||
|
*
|
||||||
|
* @return a ItemPoolView
|
||||||
|
*/
|
||||||
|
public ItemPool<T> getView() {
|
||||||
|
return new ItemPool<T>(Collections.unmodifiableMap(this.items), this.getMyClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items manipulation
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Add a single item.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* a T
|
||||||
|
*/
|
||||||
|
public void add(final T item) {
|
||||||
|
this.add(item, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Add multiple items.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* a T
|
||||||
|
* @param amount
|
||||||
|
* a int
|
||||||
|
*/
|
||||||
|
public void add(final T item, final int amount) {
|
||||||
|
if (amount <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.items.put(item, Integer.valueOf(this.count(item) + amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addAllFlat.
|
||||||
|
*
|
||||||
|
* @param <U>
|
||||||
|
* a InventoryItem
|
||||||
|
* @param items
|
||||||
|
* a Iterable<U>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <U extends InventoryItem> void addAllFlat(final Iterable<U> items) {
|
||||||
|
for (final U cr : items) {
|
||||||
|
if (this.getMyClass().isInstance(cr)) {
|
||||||
|
this.add((T) cr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addAll.
|
||||||
|
*
|
||||||
|
* @param <U>
|
||||||
|
* an InventoryItem
|
||||||
|
* @param map
|
||||||
|
* a Iterable<Entry<U, Integer>>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <U extends InventoryItem> void addAll(final Iterable<Entry<U, Integer>> map) {
|
||||||
|
Class<T> myClass = this.getMyClass();
|
||||||
|
for (final Entry<U, Integer> e : map) {
|
||||||
|
if (myClass.isInstance(e.getKey())) {
|
||||||
|
this.add((T) e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Remove.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* a T
|
||||||
|
*/
|
||||||
|
public boolean remove(final T item) {
|
||||||
|
return this.remove(item, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Remove.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* a T
|
||||||
|
* @param amount
|
||||||
|
* a int
|
||||||
|
*/
|
||||||
|
public boolean remove(final T item, final int amount) {
|
||||||
|
final int count = this.count(item);
|
||||||
|
if ((count == 0) || (amount <= 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (count <= amount) {
|
||||||
|
this.items.remove(item);
|
||||||
|
} else {
|
||||||
|
this.items.put(item, count - amount);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAll(final T item) {
|
||||||
|
return this.items.remove(item) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* RemoveAll.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* a T
|
||||||
|
*/
|
||||||
|
public void removeAll(final Iterable<Entry<T, Integer>> map) {
|
||||||
|
for (final Entry<T, Integer> e : map) {
|
||||||
|
this.remove(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
// need not set out-of-sync: either remove did set, or nothing was removed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param flat Iterable<T>
|
||||||
|
*/
|
||||||
|
public void removeAllFlat(final Iterable<T> flat) {
|
||||||
|
for (final T e : flat) {
|
||||||
|
this.remove(e);
|
||||||
|
}
|
||||||
|
// need not set out-of-sync: either remove did set, or nothing was removed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Clear.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
this.items.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,13 +15,15 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package forge.item;
|
package forge.util;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -31,13 +31,13 @@ public class Lang {
|
|||||||
return position + sufixes[position % 10];
|
return position + sufixes[position % 10];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> String joinHomogenous(String s1, String s2) {
|
public static <T> String joinHomogenous(String s1, String s2) {
|
||||||
boolean has1 = StringUtils.isNotBlank(s1);
|
boolean has1 = StringUtils.isNotBlank(s1);
|
||||||
boolean has2 = StringUtils.isNotBlank(s2);
|
boolean has2 = StringUtils.isNotBlank(s2);
|
||||||
return has1 ? (has2 ? s1 + " and " + s2 : s1) : (has2 ? s2 : "");
|
return has1 ? (has2 ? s1 + " and " + s2 : s1) : (has2 ? s2 : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> String joinHomogenous(Iterable<T> objects) { return joinHomogenous(Lists.newArrayList(objects)); }
|
public static <T> String joinHomogenous(Iterable<T> objects) { return joinHomogenous(Lists.newArrayList(objects)); }
|
||||||
public static <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null, "and"); }
|
public static <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null, "and"); }
|
||||||
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor) {
|
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor) {
|
||||||
@@ -46,85 +46,96 @@ public class Lang {
|
|||||||
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor, String lastUnion) {
|
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor, String lastUnion) {
|
||||||
int remaining = objects.size();
|
int remaining = objects.size();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for(T obj : objects) {
|
for (T obj : objects) {
|
||||||
remaining--;
|
remaining--;
|
||||||
if( accessor != null )
|
if (accessor != null) {
|
||||||
sb.append(accessor.apply(obj));
|
sb.append(accessor.apply(obj));
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
sb.append(obj);
|
sb.append(obj);
|
||||||
if( remaining > 1 ) sb.append(", ");
|
}
|
||||||
if( remaining == 1 ) sb.append(" ").append(lastUnion).append(" ");
|
if (remaining > 1) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
else if (remaining == 1) {
|
||||||
|
sb.append(" ").append(lastUnion).append(" ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static <T> String joinVerb(List<T> subjects, String verb) {
|
public static <T> String joinVerb(List<T> subjects, String verb) {
|
||||||
return subjects.size() > 1 || !subjectIsSingle3rdPerson(Iterables.getFirst(subjects, "it").toString()) ? verb : verbs3rdPersonSingular(verb);
|
return subjects.size() > 1 || !subjectIsSingle3rdPerson(Iterables.getFirst(subjects, "it").toString()) ? verb : verbs3rdPersonSingular(verb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String joinVerb(String subject, String verb) {
|
public static String joinVerb(String subject, String verb) {
|
||||||
return !Lang.subjectIsSingle3rdPerson(subject) ? verb : verbs3rdPersonSingular(verb);
|
return !Lang.subjectIsSingle3rdPerson(subject) ? verb : verbs3rdPersonSingular(verb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean subjectIsSingle3rdPerson(String subject) {
|
public static boolean subjectIsSingle3rdPerson(String subject) {
|
||||||
// Will be most simple
|
// Will be most simple
|
||||||
return !"You".equalsIgnoreCase(subject);
|
return !"You".equalsIgnoreCase(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String verbs3rdPersonSingular(String verb) {
|
public static String verbs3rdPersonSingular(String verb) {
|
||||||
// English is simple - just add (s) for multiple objects.
|
// English is simple - just add (s) for multiple objects.
|
||||||
return verb + "s";
|
return verb + "s";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getPlural(String noun) {
|
public static String getPlural(String noun) {
|
||||||
return noun + ( noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
|
return noun + (noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> String nounWithAmount(int cnt, String noun) {
|
public static <T> String nounWithAmount(int cnt, String noun) {
|
||||||
String countedForm = cnt <= 1 ? noun : getPlural(noun);
|
String countedForm = cnt == 1 ? noun : getPlural(noun);
|
||||||
final String strCount;
|
final String strCount;
|
||||||
if( cnt == 1 )
|
if (cnt == 1) {
|
||||||
strCount = startsWithVowel(noun) ? "an " : "a ";
|
strCount = startsWithVowel(noun) ? "an " : "a ";
|
||||||
else
|
}
|
||||||
strCount = String.valueOf(cnt) + " ";
|
else {
|
||||||
|
strCount = String.valueOf(cnt) + " ";
|
||||||
|
}
|
||||||
return strCount + countedForm;
|
return strCount + countedForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> String nounWithNumeral(int cnt, String noun) {
|
public static <T> String nounWithNumeral(int cnt, String noun) {
|
||||||
String countedForm = cnt <= 1 ? noun : getPlural(noun);
|
String countedForm = cnt <= 1 ? noun : getPlural(noun);
|
||||||
return getNumeral(cnt) + " " + countedForm;
|
return getNumeral(cnt) + " " + countedForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getPossesive(String name) {
|
public static String getPossesive(String name) {
|
||||||
if ("You".equalsIgnoreCase(name)) return name + "r"; // to get "your"
|
if ("You".equalsIgnoreCase(name)) return name + "r"; // to get "your"
|
||||||
return name.endsWith("s") ? name + "'" : name + "'s";
|
return name.endsWith("s") ? name + "'" : name + "'s";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getPossessedObject(String owner, String object) {
|
||||||
|
return getPossesive(owner) + " " + object;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean startsWithVowel(String word) {
|
public static boolean startsWithVowel(String word) {
|
||||||
return isVowel(word.trim().charAt(0));
|
return isVowel(word.trim().charAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final char[] vowels = { 'a', 'i', 'e', 'o', 'u' };
|
private static final char[] vowels = { 'a', 'i', 'e', 'o', 'u' };
|
||||||
public static boolean isVowel(char letter) {
|
public static boolean isVowel(char letter) {
|
||||||
char l = Character.toLowerCase(letter);
|
char l = Character.toLowerCase(letter);
|
||||||
for(char c : vowels)
|
for (char c : vowels) {
|
||||||
if ( c == l ) return true;
|
if (c == l) return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static String[] numbers0 = new String[] {
|
public final static String[] numbers0 = new String[] {
|
||||||
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eightteen", "nineteen" };
|
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eightteen", "nineteen" };
|
||||||
public final static String[] numbers20 = new String[] {"twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" };
|
public final static String[] numbers20 = new String[] {"twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" };
|
||||||
|
|
||||||
public static String getNumeral(int n) {
|
public static String getNumeral(int n) {
|
||||||
String prefix = n < 0 ? "minus " : "";
|
String prefix = n < 0 ? "minus " : "";
|
||||||
n = Math.abs(n);
|
n = Math.abs(n);
|
||||||
if ( n >= 0 && n < 20 )
|
if (n >= 0 && n < 20)
|
||||||
return prefix + numbers0[n];
|
return prefix + numbers0[n];
|
||||||
if ( n < 100 ) {
|
if (n < 100) {
|
||||||
int n1 = n % 10;
|
int n1 = n % 10;
|
||||||
String ones = n1 == 0 ? "" : numbers0[n1];
|
String ones = n1 == 0 ? "" : numbers0[n1];
|
||||||
return prefix + numbers20[(n / 10) - 2] + " " + ones;
|
return prefix + numbers20[(n / 10) - 2] + " " + ones;
|
||||||
@@ -55,4 +55,14 @@ public class MyRandom {
|
|||||||
public static Random getRandom() {
|
public static Random getRandom() {
|
||||||
return MyRandom.random;
|
return MyRandom.random;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int[] splitIntoRandomGroups(final int value, final int numGroups) {
|
||||||
|
int[] groups = new int[numGroups];
|
||||||
|
|
||||||
|
for (int i = 0; i < value; i++) {
|
||||||
|
groups[random.nextInt(numGroups)]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
44
forge-core/src/main/java/forge/util/ReflectionUtil.java
Normal file
44
forge-core/src/main/java/forge/util/ReflectionUtil.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package forge.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ReflectionUtil {
|
||||||
|
|
||||||
|
public static <T> T makeDefaultInstanceOf(Class<? extends T> cls) {
|
||||||
|
if ( null == cls )
|
||||||
|
throw new IllegalArgumentException("Class<? extends T> cls must not be null");
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Constructor<? extends T>[] cc = (Constructor<? extends T>[]) cls.getConstructors();
|
||||||
|
for (Constructor<? extends T> c : cc) {
|
||||||
|
Class<?>[] pp = c.getParameterTypes();
|
||||||
|
if (pp.length == 0) {
|
||||||
|
try {
|
||||||
|
T res = c.newInstance();
|
||||||
|
return res;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("No default constructor found in class " + cls.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast object to a given type if possible, returning null if not possible
|
||||||
|
* @param obj
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T safeCast(Object obj, Class<T> type) {
|
||||||
|
if (type.isInstance(obj)) {
|
||||||
|
return (T) obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
180
forge-core/src/main/java/forge/util/TextUtil.java
Normal file
180
forge-core/src/main/java/forge/util/TextUtil.java
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package forge.util;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TextUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely converts an object to a String.
|
||||||
|
*
|
||||||
|
* @param obj
|
||||||
|
* to convert; may be null
|
||||||
|
*
|
||||||
|
* @return "null" if obj is null, obj.toString() otherwise
|
||||||
|
*/
|
||||||
|
public static String safeToString(final Object obj) {
|
||||||
|
return obj == null ? "null" : obj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String mapToString(Map<String, ?> map) {
|
||||||
|
StringBuilder mapAsString = new StringBuilder();
|
||||||
|
boolean isFirst = true;
|
||||||
|
for (Entry<String, ?> p : map.entrySet()) {
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
} else {
|
||||||
|
mapAsString.append("; ");
|
||||||
|
}
|
||||||
|
mapAsString.append(p.getKey() + " => " + (p.getValue() == null ? "(null)" : p.getValue().toString()));
|
||||||
|
}
|
||||||
|
return mapAsString.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] split(CharSequence input, char delimiter) {
|
||||||
|
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '\0', '\0', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] split(CharSequence input, char delimiter, int limit) {
|
||||||
|
return splitWithParenthesis(input, delimiter, limit, '\0', '\0', true);
|
||||||
|
}
|
||||||
|
public static String[] splitWithParenthesis(CharSequence input, char delimiter) {
|
||||||
|
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '(', ')', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] splitWithParenthesis(CharSequence input, char delimiter, char openPar, char closePar) {
|
||||||
|
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, openPar, closePar, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int limit) {
|
||||||
|
return splitWithParenthesis(input, delimiter, limit, '(', ')', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] splitWithParenthesis(CharSequence input, char delimiter, char openPar, char closePar, int limit) {
|
||||||
|
return splitWithParenthesis(input, delimiter, limit, openPar, closePar, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split string separated by a single char delimiter, can take parenthesis in account
|
||||||
|
* It's faster than String.split, and allows parenthesis
|
||||||
|
*/
|
||||||
|
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int maxEntries, char openPar, char closePar, boolean skipEmpty) {
|
||||||
|
List<String> result = new ArrayList<String>();
|
||||||
|
// Assume that when equal non-zero parenthesis are passed, they need to be discarded
|
||||||
|
boolean trimParenthesis = openPar == closePar && openPar > 0;
|
||||||
|
int nPar = 0;
|
||||||
|
int len = input.length();
|
||||||
|
int start = 0;
|
||||||
|
int idx = 1;
|
||||||
|
for (int iC = 0; iC < len; iC++) {
|
||||||
|
char c = input.charAt(iC);
|
||||||
|
if (closePar > 0 && c == closePar && nPar > 0) { nPar--; }
|
||||||
|
else if (openPar > 0 && c == openPar) nPar++;
|
||||||
|
|
||||||
|
if (c == delimiter && nPar == 0 && idx < maxEntries) {
|
||||||
|
if (iC > start || !skipEmpty) {
|
||||||
|
result.add(input.subSequence(start, iC).toString());
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
start = iC + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > start || !skipEmpty)
|
||||||
|
result.add(input.subSequence(start, len).toString());
|
||||||
|
|
||||||
|
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
||||||
|
return trimParenthesis ? StringUtils.stripAll(toReturn, String.valueOf(openPar)) : toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String join(Iterable<String> strs, String delim) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String str : strs) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append(delim);
|
||||||
|
}
|
||||||
|
sb.append(str);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an enum value to a printable label but upcasing the first letter
|
||||||
|
* and lcasing all subsequent letters
|
||||||
|
*/
|
||||||
|
public static String enumToLabel(Enum<?> val) {
|
||||||
|
return val.toString().substring(0, 1).toUpperCase(Locale.ENGLISH) +
|
||||||
|
val.toString().substring(1).toLowerCase(Locale.ENGLISH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildFourColumnList(String firstLine, Iterable<PaperCard> cAnteRemoved) {
|
||||||
|
StringBuilder sb = new StringBuilder(firstLine);
|
||||||
|
int i = 0;
|
||||||
|
for (PaperCard cp: cAnteRemoved) {
|
||||||
|
if (i != 0) { sb.append(", "); }
|
||||||
|
if (i % 4 == 0) { sb.append("\n"); }
|
||||||
|
sb.append(cp);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPrintableChar(char c) {
|
||||||
|
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
|
||||||
|
return (!Character.isISOControl(c)) &&
|
||||||
|
c != KeyEvent.CHAR_UNDEFINED &&
|
||||||
|
block != null &&
|
||||||
|
block != Character.UnicodeBlock.SPECIALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PhraseCase {
|
||||||
|
Title,
|
||||||
|
Sentence,
|
||||||
|
Lower
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String splitCompoundWord(String word, PhraseCase phraseCase) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < word.length(); i++) {
|
||||||
|
char ch = word.charAt(i);
|
||||||
|
if (Character.isUpperCase(ch)) {
|
||||||
|
if (i > 0) {
|
||||||
|
builder.append(" ");
|
||||||
|
}
|
||||||
|
switch (phraseCase) {
|
||||||
|
case Title:
|
||||||
|
builder.append(ch);
|
||||||
|
break;
|
||||||
|
case Sentence:
|
||||||
|
if (i > 0) {
|
||||||
|
builder.append(ch);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.append(Character.toLowerCase(ch));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Lower:
|
||||||
|
builder.append(Character.toLowerCase(ch));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
52
forge-core/src/main/java/forge/util/ThreadUtil.java
Normal file
52
forge-core/src/main/java/forge/util/ThreadUtil.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package forge.util;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ThreadUtil {
|
||||||
|
static {
|
||||||
|
System.out.printf("(ThreadUtil first call): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WorkerThreadFactory implements ThreadFactory {
|
||||||
|
private int countr = 0;
|
||||||
|
private String prefix = "";
|
||||||
|
|
||||||
|
public WorkerThreadFactory(String prefix) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread newThread(Runnable r) {
|
||||||
|
return new Thread(r, prefix + "-" + countr++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static ExecutorService gameThreadPool = Executors.newCachedThreadPool(new WorkerThreadFactory("Game"));
|
||||||
|
private static ExecutorService getGameThreadPool() { return gameThreadPool; }
|
||||||
|
private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2, new WorkerThreadFactory("Delayed"));
|
||||||
|
private static ScheduledExecutorService getScheduledPool() { return scheduledPool; }
|
||||||
|
|
||||||
|
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
|
||||||
|
public final static ExecutorService getComputingPool(float loadFactor) {
|
||||||
|
return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMultiCoreSystem() {
|
||||||
|
return Runtime.getRuntime().availableProcessors() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invokeInGameThread(Runnable toRun) {
|
||||||
|
getGameThreadPool().execute(toRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void delay(int milliseconds, Runnable inputUpdater) {
|
||||||
|
getScheduledPool().schedule(inputUpdater, milliseconds, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isGameThread() {
|
||||||
|
return Thread.currentThread().getName().startsWith("Game");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package forge.util.maps;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class EnumMapToAmount<T extends Enum<T>> extends EnumMap<T, Integer> implements MapToAmount<T> {
|
||||||
|
private static final long serialVersionUID = -4749796492075359368L;
|
||||||
|
|
||||||
|
public EnumMapToAmount(Class<T> keyType) {
|
||||||
|
super(keyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumMapToAmount(EnumMap<T, ? extends Integer> m) {
|
||||||
|
super(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumMapToAmount(Map<T, ? extends Integer> m) {
|
||||||
|
super(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T item) {
|
||||||
|
add(item, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T item, int amount) {
|
||||||
|
if (amount <= 0) { return; } // throw an exception maybe?
|
||||||
|
Integer cur = get(item);
|
||||||
|
int newVal = cur == null ? amount : amount + cur.intValue();
|
||||||
|
put(item, Integer.valueOf(newVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAll(Iterable<T> items) {
|
||||||
|
for (T i : items) {
|
||||||
|
add(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean substract(T item) {
|
||||||
|
return substract(item, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean substract(T item, int amount) {
|
||||||
|
Integer cur = get(item);
|
||||||
|
if (cur == null) { return false; }
|
||||||
|
int newVal = cur.intValue() - amount;
|
||||||
|
if (newVal > 0) {
|
||||||
|
put(item, Integer.valueOf(newVal));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
remove(item);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void substractAll(Iterable<T> items) {
|
||||||
|
for (T i : items) {
|
||||||
|
substract(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int countAll() {
|
||||||
|
int c = 0;
|
||||||
|
for (java.util.Map.Entry<T, Integer> kv : this.entrySet()) {
|
||||||
|
c += kv.getValue().intValue();
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int count(T item) {
|
||||||
|
Integer cur = get(item);
|
||||||
|
return cur == null ? 0 : cur.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user