mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Compare commits
1877 Commits
forge-1.4.
...
forge-1.5.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2df834804e | ||
|
|
fb5fab037e | ||
|
|
fc2e170d09 | ||
|
|
77602a2aa2 | ||
|
|
dd668cb0fe | ||
|
|
2a7b1f3c68 | ||
|
|
05b4a064ab | ||
|
|
e6f64aebd1 | ||
|
|
e2d9006e5c | ||
|
|
d991861421 | ||
|
|
c04d43ac66 | ||
|
|
9cd7e8e034 | ||
|
|
ee198870f7 | ||
|
|
5138ba7605 | ||
|
|
426fab34bc | ||
|
|
72b31ac9d0 | ||
|
|
c957dab3d2 | ||
|
|
f8e522fd1e | ||
|
|
5ace15ac38 | ||
|
|
4f8ef1ef34 | ||
|
|
8c580acc42 | ||
|
|
3452039d42 | ||
|
|
20202b08f9 | ||
|
|
fdf0a30843 | ||
|
|
f807986e44 | ||
|
|
358602ad21 | ||
|
|
321805e786 | ||
|
|
141025a02c | ||
|
|
526b86ac16 | ||
|
|
0d3eb11aa3 | ||
|
|
62cf0b5a5e | ||
|
|
0b7238d93b | ||
|
|
e4e62a8267 | ||
|
|
57f95c3c5c | ||
|
|
28351a1d18 | ||
|
|
9b6e34a33f | ||
|
|
faad09f594 | ||
|
|
4b687603f8 | ||
|
|
1084d89cff | ||
|
|
586f6ce5d9 | ||
|
|
059edfe381 | ||
|
|
776f4b03ff | ||
|
|
3be5ff1e17 | ||
|
|
90d51c6dbe | ||
|
|
f792bce232 | ||
|
|
94f937ad7e | ||
|
|
0aacb10d96 | ||
|
|
07b26dd7f3 | ||
|
|
6fd8e8de9a | ||
|
|
d38898b1dd | ||
|
|
12fd77953f | ||
|
|
f1db96ddce | ||
|
|
537fd9a076 | ||
|
|
25c18e8f48 | ||
|
|
064d8a8288 | ||
|
|
8e9fecaf8f | ||
|
|
ec75e4d9d9 | ||
|
|
4ccf39517b | ||
|
|
f26c734c4d | ||
|
|
655c96361e | ||
|
|
3f4adfbc07 | ||
|
|
4d2a2b6ce2 | ||
|
|
d716493bcd | ||
|
|
919de9b0fa | ||
|
|
a97398e0b3 | ||
|
|
2cf046de5e | ||
|
|
a28e444680 | ||
|
|
03a3ef8422 | ||
|
|
f99d1ce51d | ||
|
|
bf46bd9d96 | ||
|
|
5620552344 | ||
|
|
3898afe81b | ||
|
|
ba9d49d0f1 | ||
|
|
a7f196999f | ||
|
|
7493cf2726 | ||
|
|
ffcfd24781 | ||
|
|
bb4f967353 | ||
|
|
8e28ce296a | ||
|
|
d3b9f4c8d8 | ||
|
|
5ebdf212e2 | ||
|
|
1bc4baffec | ||
|
|
37661ac0d0 | ||
|
|
bb7b4c6252 | ||
|
|
aeb6994974 | ||
|
|
892ed7df24 | ||
|
|
9a60203313 | ||
|
|
5ce9f79c66 | ||
|
|
0adee5121b | ||
|
|
13070aa96c | ||
|
|
b75012a0a1 | ||
|
|
09c11d1f3f | ||
|
|
b51bf8f952 | ||
|
|
9db08d508b | ||
|
|
015aa8f343 | ||
|
|
5ed5c9afbe | ||
|
|
f45174318d | ||
|
|
a59f5b4d11 | ||
|
|
2e122714b0 | ||
|
|
5beba3101e | ||
|
|
c4e0bc0a1a | ||
|
|
2a0cb56520 | ||
|
|
9e21987039 | ||
|
|
e4fc9164c1 | ||
|
|
f4ce2a9f37 | ||
|
|
21d7a8fe3f | ||
|
|
af8ae84eb5 | ||
|
|
d73699bcaa | ||
|
|
1e55407060 | ||
|
|
c9987e5efc | ||
|
|
4e90eac764 | ||
|
|
24e320237f | ||
|
|
0ca16e5996 | ||
|
|
5125ef32ab | ||
|
|
9c2c231b93 | ||
|
|
35f3949fbc | ||
|
|
03fb8b24d1 | ||
|
|
1ece44afc7 | ||
|
|
0b951527fa | ||
|
|
b2a86015f1 | ||
|
|
f0f5b97e03 | ||
|
|
ba8ec270d1 | ||
|
|
d3b94b0dd9 | ||
|
|
7f3a641629 | ||
|
|
61073dcd09 | ||
|
|
40c840446d | ||
|
|
6426106e0c | ||
|
|
799387b09b | ||
|
|
a9e0e660d2 | ||
|
|
732beaa9fd | ||
|
|
eee4322173 | ||
|
|
727920679c | ||
|
|
7ec2d0f8cb | ||
|
|
eb4b25b9e4 | ||
|
|
a3afd4c35b | ||
|
|
cfccacd21d | ||
|
|
b40302a3aa | ||
|
|
75cd2cdcab | ||
|
|
2f37ca57b4 | ||
|
|
e4446b5fa0 | ||
|
|
9519eb3a17 | ||
|
|
a1d99ba2ba | ||
|
|
5c1219f649 | ||
|
|
e2c773c416 | ||
|
|
98d16ba6e1 | ||
|
|
a8f3360c40 | ||
|
|
c5aa1d3d7e | ||
|
|
80661d3055 | ||
|
|
d4e5a1ffba | ||
|
|
7b88caee8a | ||
|
|
17f4bbdda1 | ||
|
|
6dacae86f8 | ||
|
|
55b182687e | ||
|
|
2966504fae | ||
|
|
9df426e427 | ||
|
|
f96711fc9a | ||
|
|
7d170f4f24 | ||
|
|
30350041bb | ||
|
|
bf205b0998 | ||
|
|
a065c8573f | ||
|
|
22c54a488b | ||
|
|
4b14ff7231 | ||
|
|
1bdbfdc7bf | ||
|
|
7b4812edca | ||
|
|
a6a08bb970 | ||
|
|
5b47dfbcb7 | ||
|
|
d4c54cab89 | ||
|
|
358f05ba01 | ||
|
|
71040279a4 | ||
|
|
e10843e5cb | ||
|
|
98901c4d06 | ||
|
|
12ddd7540b | ||
|
|
565971ac7f | ||
|
|
a93ae5b3e9 | ||
|
|
b662b0a7ba | ||
|
|
c26e23539b | ||
|
|
486a965624 | ||
|
|
f3f44dd65e | ||
|
|
2431e1dd20 | ||
|
|
d9eea5d5f0 | ||
|
|
9499207bae | ||
|
|
6c6ba5742f | ||
|
|
fcf1ac425e | ||
|
|
dba19e6cb7 | ||
|
|
82bb71a34b | ||
|
|
cd061cd660 | ||
|
|
ea6795df35 | ||
|
|
35edfd936b | ||
|
|
e18b3b3537 | ||
|
|
f9d749e2cb | ||
|
|
0baa952db7 | ||
|
|
fa443f0c7f | ||
|
|
98b8c2b43a | ||
|
|
f5b0fa0467 | ||
|
|
67608ae02a | ||
|
|
73495c4a76 | ||
|
|
3ae5b4cd80 | ||
|
|
6b4de9833f | ||
|
|
4531658dd8 | ||
|
|
11e738e6c6 | ||
|
|
f531baf7d3 | ||
|
|
f4e791b6f1 | ||
|
|
c05cd7b2f6 | ||
|
|
9778fe7dfd | ||
|
|
00208b69a7 | ||
|
|
3df8397c9a | ||
|
|
b480c2895c | ||
|
|
928b4fa887 | ||
|
|
4a8ff10965 | ||
|
|
23df025313 | ||
|
|
aa4ee6ec6b | ||
|
|
a0ca0c47c9 | ||
|
|
775879096f | ||
|
|
8969d07d45 | ||
|
|
b1fd15c083 | ||
|
|
b9f4a9abda | ||
|
|
fc5230a9b9 | ||
|
|
9f6f9ce7bb | ||
|
|
b9d5cc4ae7 | ||
|
|
cfed3ee53a | ||
|
|
a242fbd8a2 | ||
|
|
b1a8accfd0 | ||
|
|
905d64cb51 | ||
|
|
ab2d7ae029 | ||
|
|
2b33f588bd | ||
|
|
3c86a85cd3 | ||
|
|
b4ae3c97a3 | ||
|
|
80dd6a2efa | ||
|
|
5069854924 | ||
|
|
29d4875778 | ||
|
|
df44f00eb8 | ||
|
|
1a08cd6ae4 | ||
|
|
2f39539135 | ||
|
|
8350c398f3 | ||
|
|
3591759cdb | ||
|
|
f352704fcd | ||
|
|
679c0fea03 | ||
|
|
370927e489 | ||
|
|
d675097da5 | ||
|
|
e4c7797102 | ||
|
|
57a9c54630 | ||
|
|
9c885af7ec | ||
|
|
242d293f5a | ||
|
|
bc44426d9d | ||
|
|
e4441f9126 | ||
|
|
97855b563d | ||
|
|
7ef5462969 | ||
|
|
714dbdc930 | ||
|
|
f1366a5e8a | ||
|
|
5af7659883 | ||
|
|
05df1adb11 | ||
|
|
05066496d8 | ||
|
|
599821c018 | ||
|
|
52cae0e723 | ||
|
|
ceb9180bbe | ||
|
|
657beeb63d | ||
|
|
16a915faa1 | ||
|
|
2ca3d67eb5 | ||
|
|
309cf6658d | ||
|
|
e21aac63f4 | ||
|
|
9a0b240e73 | ||
|
|
046f3a6458 | ||
|
|
398a3ed1ea | ||
|
|
f1ab723957 | ||
|
|
9380a06a35 | ||
|
|
73131ba99e | ||
|
|
12e813a5a1 | ||
|
|
de87fcb123 | ||
|
|
26b4eb000a | ||
|
|
4c9dacbcbe | ||
|
|
29a959fb59 | ||
|
|
b5049bbb8d | ||
|
|
42ab52e791 | ||
|
|
999070fd9b | ||
|
|
03343276c2 | ||
|
|
02e7a8920a | ||
|
|
fbc2d6ad7e | ||
|
|
7ce642fa7b | ||
|
|
8c984d6dbe | ||
|
|
9c0dad1342 | ||
|
|
dc69975b87 | ||
|
|
da06932d85 | ||
|
|
4cfed1e438 | ||
|
|
8c461edc1d | ||
|
|
ed7d5aa8af | ||
|
|
66a4b444e3 | ||
|
|
cebcd25f81 | ||
|
|
48ba246fc4 | ||
|
|
16a2e75304 | ||
|
|
eb1d4a6354 | ||
|
|
490abc5826 | ||
|
|
90c78a469d | ||
|
|
3225112fc7 | ||
|
|
20732ab927 | ||
|
|
501a0ce009 | ||
|
|
95af0e1ef5 | ||
|
|
92a7c1a20a | ||
|
|
c51abaaaf4 | ||
|
|
1df24c0287 | ||
|
|
22e6603a5b | ||
|
|
8c07e0b89f | ||
|
|
4be5d02fd7 | ||
|
|
d6176eec0d | ||
|
|
2279087023 | ||
|
|
59197f2ac9 | ||
|
|
2af8c51798 | ||
|
|
a05fbe5cd8 | ||
|
|
30f12050dc | ||
|
|
fdc97228e7 | ||
|
|
ca9266f772 | ||
|
|
d1eeeedca1 | ||
|
|
39486f078c | ||
|
|
c6904e740c | ||
|
|
626279667f | ||
|
|
c1d8cccdb3 | ||
|
|
db8a191afb | ||
|
|
e8099b8492 | ||
|
|
5bcea269de | ||
|
|
9fe331d11a | ||
|
|
cbeb5ced09 | ||
|
|
03ef46b316 | ||
|
|
66de7141d9 | ||
|
|
8f80f4d439 | ||
|
|
4b30a7eca1 | ||
|
|
5ee58d727a | ||
|
|
f8f03a57ba | ||
|
|
34618e957c | ||
|
|
f745ba9f5d | ||
|
|
1677e1eeb1 | ||
|
|
eebbb45b2c | ||
|
|
ada5fa5056 | ||
|
|
df6860294d | ||
|
|
8b97275195 | ||
|
|
7dfde0aba3 | ||
|
|
517b1ae328 | ||
|
|
7e92f73a30 | ||
|
|
1514ad0694 | ||
|
|
48bebfef05 | ||
|
|
19cb78e529 | ||
|
|
ddb66257ce | ||
|
|
fded944458 | ||
|
|
e55d5f8199 | ||
|
|
e8dc32489d | ||
|
|
f8ec884788 | ||
|
|
fa0fb3e519 | ||
|
|
023bc56986 | ||
|
|
c52050e37c | ||
|
|
a523f3c130 | ||
|
|
0126e6dae3 | ||
|
|
2a991e219f | ||
|
|
3c31e29a48 | ||
|
|
e7a143e181 | ||
|
|
03b37b049b | ||
|
|
1563cb4693 | ||
|
|
82f32154fd | ||
|
|
37b13aa484 | ||
|
|
cf00e02c12 | ||
|
|
4bfb8a840a | ||
|
|
490efdbd5a | ||
|
|
d2335d2382 | ||
|
|
7e8a729fed | ||
|
|
d6274b42af | ||
|
|
699a502cc5 | ||
|
|
292bfed82a | ||
|
|
a34847ab7b | ||
|
|
9e5f7f943b | ||
|
|
7dc9afddb3 | ||
|
|
f174d73098 | ||
|
|
15c90a00cd | ||
|
|
1ecb6728b1 | ||
|
|
f13c38e135 | ||
|
|
7c34056281 | ||
|
|
0fb1c82dc0 | ||
|
|
ecd8e8838f | ||
|
|
cd969babff | ||
|
|
116a24ac23 | ||
|
|
a951e4eaa0 | ||
|
|
fa820d4e07 | ||
|
|
f0ab756a5d | ||
|
|
6545960635 | ||
|
|
ef8bb689d7 | ||
|
|
ce988a66df | ||
|
|
54d98f710c | ||
|
|
c1ab52e38f | ||
|
|
42f6c88463 | ||
|
|
d85e43a427 | ||
|
|
d74f571ddb | ||
|
|
cfcbde62b2 | ||
|
|
f3fbb82dbd | ||
|
|
0a0c9d8070 | ||
|
|
27e22ace02 | ||
|
|
5b7197fbd6 | ||
|
|
a1764bf043 | ||
|
|
cfc280274f | ||
|
|
258953ec53 | ||
|
|
236d528923 | ||
|
|
3fd6c7b688 | ||
|
|
3306fb4352 | ||
|
|
c0960c2250 | ||
|
|
651fd5e5a2 | ||
|
|
4d72113af4 | ||
|
|
ca6fe5105c | ||
|
|
27c19645ac | ||
|
|
259086a979 | ||
|
|
9f4ba4b50c | ||
|
|
21ad3cfc5e | ||
|
|
bfba7a5102 | ||
|
|
0119fcd24a | ||
|
|
39edeacc5b | ||
|
|
e0dcec7d5b | ||
|
|
3ec7071889 | ||
|
|
eeb411bacb | ||
|
|
03d13ebbc1 | ||
|
|
46bb7df90e | ||
|
|
69ed6496a6 | ||
|
|
b100daea1b | ||
|
|
ec38900386 | ||
|
|
a523cdf1e6 | ||
|
|
c552ef1616 | ||
|
|
752a4413f3 | ||
|
|
186bd2d7da | ||
|
|
610c2f98ce | ||
|
|
fdac56a9a7 | ||
|
|
2bb8c97a5a | ||
|
|
ad3b034fd2 | ||
|
|
b2b1657e31 | ||
|
|
3a4087a2e1 | ||
|
|
3fe7d3adf3 | ||
|
|
f64566dbae | ||
|
|
98f3a50fbd | ||
|
|
ef5234d734 | ||
|
|
5b34571cf2 | ||
|
|
edac489791 | ||
|
|
acb01268da | ||
|
|
9b19a88a14 | ||
|
|
555a8bfed7 | ||
|
|
b4efa372b2 | ||
|
|
8ee5a5fa41 | ||
|
|
c77f70b371 | ||
|
|
c4f1721250 | ||
|
|
32d2f98bef | ||
|
|
1f40291c1b | ||
|
|
8b3dda264b | ||
|
|
1a5c749b15 | ||
|
|
0afd72195b | ||
|
|
fef2c873d5 | ||
|
|
7d0fdfff24 | ||
|
|
2d966bc535 | ||
|
|
4b3c8b555e | ||
|
|
c0de6f7721 | ||
|
|
70323eccd0 | ||
|
|
b259c66530 | ||
|
|
84bbd884ae | ||
|
|
fb3867c21c | ||
|
|
0b61e6b4b6 | ||
|
|
fbad22131a | ||
|
|
884232bd33 | ||
|
|
70acd483d1 | ||
|
|
018699cc39 | ||
|
|
61d1786a00 | ||
|
|
07c38892f9 | ||
|
|
f689d44f01 | ||
|
|
ce53c8d84a | ||
|
|
d1030df71a | ||
|
|
d9be047a5c | ||
|
|
2966a22521 | ||
|
|
79d5f9b46d | ||
|
|
9f387d1ddf | ||
|
|
f32a1455fd | ||
|
|
1dcc84a894 | ||
|
|
1309a14ea0 | ||
|
|
04010be62a | ||
|
|
d8319fd89e | ||
|
|
0dbd8155ae | ||
|
|
f426c6be3d | ||
|
|
88d32d6c4a | ||
|
|
784e8ba0eb | ||
|
|
196ea025a9 | ||
|
|
654f64b604 | ||
|
|
b568fdd47b | ||
|
|
45e72f9202 | ||
|
|
4f71f79bb4 | ||
|
|
9aa52d7094 | ||
|
|
267dfa7545 | ||
|
|
f8a086a26b | ||
|
|
c78689db89 | ||
|
|
44b4e699aa | ||
|
|
e3a88484c6 | ||
|
|
f2e710f94d | ||
|
|
7df35a6203 | ||
|
|
5558692821 | ||
|
|
47db405dd5 | ||
|
|
47faabd7f6 | ||
|
|
409dd8c250 | ||
|
|
1b995b1c0d | ||
|
|
1a48640042 | ||
|
|
9d3a944033 | ||
|
|
f1ea2abcc5 | ||
|
|
108c0662ca | ||
|
|
4ad15366ae | ||
|
|
1c53168e12 | ||
|
|
df5ded6050 | ||
|
|
52dfa47d6d | ||
|
|
2df22b5a42 | ||
|
|
a6a151f98f | ||
|
|
ec645d7f0e | ||
|
|
1d2666c598 | ||
|
|
85a9990f50 | ||
|
|
6a681094f8 | ||
|
|
c8ee474025 | ||
|
|
8b4da360c1 | ||
|
|
313ede1465 | ||
|
|
2bc8120186 | ||
|
|
068dce1b56 | ||
|
|
7a110a80b5 | ||
|
|
a1da50a1dd | ||
|
|
8bfe0746dd | ||
|
|
5e625dc08d | ||
|
|
e16d885d30 | ||
|
|
369405e1f5 | ||
|
|
d34e40ea29 | ||
|
|
759a9bed7a | ||
|
|
a7e0522cf9 | ||
|
|
44f819e9be | ||
|
|
f55566956e | ||
|
|
9d9ae41006 | ||
|
|
b19a6ae0d4 | ||
|
|
d4996103c5 | ||
|
|
ef4ef398ed | ||
|
|
82b6055a6d | ||
|
|
994dfe1366 | ||
|
|
6e71b2f120 | ||
|
|
26bea083af | ||
|
|
800786f314 | ||
|
|
9ff7adab79 | ||
|
|
aa4c4acd6e | ||
|
|
345cece010 | ||
|
|
ac00afd142 | ||
|
|
585e97538c | ||
|
|
cfcd4e9404 | ||
|
|
5865604e0f | ||
|
|
398c8026df | ||
|
|
594c22b9f9 | ||
|
|
b91d3a38f3 | ||
|
|
d4ddd8114c | ||
|
|
02bf1b283d | ||
|
|
aa846eed7d | ||
|
|
487900ea58 | ||
|
|
6d93668df5 | ||
|
|
4c73af527c | ||
|
|
73f02b8c30 | ||
|
|
1d51805181 | ||
|
|
b41dd6ff64 | ||
|
|
e3f9bf17f6 | ||
|
|
7a6ea2f2db | ||
|
|
ffe6d0b406 | ||
|
|
3bff036d04 | ||
|
|
b4ee92ea28 | ||
|
|
e49147f5c2 | ||
|
|
7aa3910911 | ||
|
|
16f0be2758 | ||
|
|
732bc9be73 | ||
|
|
235abb58f4 | ||
|
|
5ff00b2c5c | ||
|
|
a0d37f7edd | ||
|
|
04a0d0c04c | ||
|
|
49beec26c5 | ||
|
|
7686ef0d73 | ||
|
|
c2d8fb9da2 | ||
|
|
9d6afb9f55 | ||
|
|
de805f68c5 | ||
|
|
79a1ed3d03 | ||
|
|
5a451977a4 | ||
|
|
50e482df9a | ||
|
|
576697993e | ||
|
|
7adc31890c | ||
|
|
afa42a53cd | ||
|
|
3cb2b09dd6 | ||
|
|
35213d720e | ||
|
|
dd0b8b94fa | ||
|
|
152587e5c4 | ||
|
|
70c98f6cea | ||
|
|
288c1d8867 | ||
|
|
a9ab0c7fc6 | ||
|
|
af89ddf40e | ||
|
|
57c7e32361 | ||
|
|
00779c550c | ||
|
|
6f128f61e1 | ||
|
|
9ad1c5d2e7 | ||
|
|
d06d797ab1 | ||
|
|
14ae68b38c | ||
|
|
85a31e7f04 | ||
|
|
003081508c | ||
|
|
e62d8e2bdc | ||
|
|
2cacb05e66 | ||
|
|
8c4f6650c2 | ||
|
|
5da85b079a | ||
|
|
0c9a29000e | ||
|
|
412b7501e5 | ||
|
|
87ffcf737f | ||
|
|
10a83ed994 | ||
|
|
b9da0fa6bd | ||
|
|
b2957a56a8 | ||
|
|
e54cef963b | ||
|
|
410e562888 | ||
|
|
58a2cb6258 | ||
|
|
c658c937a0 | ||
|
|
217f78a5e8 | ||
|
|
8d3c817195 | ||
|
|
9ce5fc4f58 | ||
|
|
b8eff3db00 | ||
|
|
cbe7be7d20 | ||
|
|
021f482aea | ||
|
|
24b371d58a | ||
|
|
010edda2cd | ||
|
|
a2ecdabe8d | ||
|
|
deda7f13d1 | ||
|
|
1152f3f490 | ||
|
|
89151422ba | ||
|
|
0d6ae53860 | ||
|
|
55d0eb904d | ||
|
|
ec1f2aa35a | ||
|
|
f3a6b9f536 | ||
|
|
4f60bcc16d | ||
|
|
2255ebd533 | ||
|
|
1ebf2f5003 | ||
|
|
2a55d1ea03 | ||
|
|
4d9c988059 | ||
|
|
ef0225c1bd | ||
|
|
633fec0094 | ||
|
|
d5c9a9c34a | ||
|
|
a547bbc815 | ||
|
|
2af5a26ca3 | ||
|
|
24b61319ca | ||
|
|
560b18ab14 | ||
|
|
2bee73487a | ||
|
|
767df4ba4f | ||
|
|
93522443a4 | ||
|
|
af20f7b37c | ||
|
|
795c0c2673 | ||
|
|
b97e89754b | ||
|
|
80ad9ec410 | ||
|
|
ba9fb93a7b | ||
|
|
cb461a2304 | ||
|
|
b4f6dbdeb6 | ||
|
|
ab9092d53e | ||
|
|
b741c594e2 | ||
|
|
01973b0712 | ||
|
|
00ffcc50d8 | ||
|
|
e1503691f9 | ||
|
|
f8ecb5298f | ||
|
|
f83c4a36b5 | ||
|
|
f839c1c41c | ||
|
|
f94b44b65f | ||
|
|
928ac60b42 | ||
|
|
9bf9c167a7 | ||
|
|
fd63bda12a | ||
|
|
bde583d381 | ||
|
|
6a6050dea2 | ||
|
|
355ab88ba9 | ||
|
|
adc22f97ff | ||
|
|
fdd53c2668 | ||
|
|
37e3dac97f | ||
|
|
ac99a905ec | ||
|
|
5b91a68b2f | ||
|
|
5b2ceef0aa | ||
|
|
2c54c811d5 | ||
|
|
55a8d9a552 | ||
|
|
93227be07e | ||
|
|
42adaacb04 | ||
|
|
0b6cbb0c43 | ||
|
|
37568e0410 | ||
|
|
eba08dd086 | ||
|
|
3472ce4675 | ||
|
|
0465095bbc | ||
|
|
6134d2585c | ||
|
|
344643ffdd | ||
|
|
4bfc8bb117 | ||
|
|
cce1d2f361 | ||
|
|
ab68c32549 | ||
|
|
0c6275777e | ||
|
|
0ae4cd25b6 | ||
|
|
8d030f3fac | ||
|
|
e6df31e338 | ||
|
|
6a09f78fc5 | ||
|
|
57844e8c7c | ||
|
|
845d67db52 | ||
|
|
4f5de217e7 | ||
|
|
39d18a48c8 | ||
|
|
0bea980aa7 | ||
|
|
b0e09f8cff | ||
|
|
299ab754b3 | ||
|
|
cada68dee5 | ||
|
|
70c2d18abe | ||
|
|
dd34b9bf0f | ||
|
|
4c52a82e85 | ||
|
|
99f30eb07e | ||
|
|
7f67bfb2df | ||
|
|
4a2c0a2536 | ||
|
|
019685ef9b | ||
|
|
33b29ca55d | ||
|
|
e9d962388d | ||
|
|
913a6ec653 | ||
|
|
d2c2138be6 | ||
|
|
4bb0b15adc | ||
|
|
6aa57212a9 | ||
|
|
5a30bc849e | ||
|
|
995c5705e5 | ||
|
|
c580b15643 | ||
|
|
9a97bc6824 | ||
|
|
9c58d20320 | ||
|
|
210e06fd62 | ||
|
|
cb085223b3 | ||
|
|
b8954ef27b | ||
|
|
b7ca99dd55 | ||
|
|
191783d30c | ||
|
|
0ba70bb4f7 | ||
|
|
277f63609c | ||
|
|
cd01b7ba6b | ||
|
|
18a25447a6 | ||
|
|
93468a849c | ||
|
|
bc864fa71e | ||
|
|
63e46dcf12 | ||
|
|
3ac1ddb51e | ||
|
|
18d3b1ffa8 | ||
|
|
3c713fb362 | ||
|
|
c74a577030 | ||
|
|
a849c03651 | ||
|
|
bf8be3096d | ||
|
|
34a5375a21 | ||
|
|
2303f414bb | ||
|
|
8331e86624 | ||
|
|
c591902722 | ||
|
|
36a478d78d | ||
|
|
6f278cead4 | ||
|
|
6fe7063664 | ||
|
|
c1f903afcd | ||
|
|
2344bf696e | ||
|
|
ba93f2a431 | ||
|
|
15b3698619 | ||
|
|
10060a66f8 | ||
|
|
3ccc53a63c | ||
|
|
cd120bcad4 | ||
|
|
d282f07720 | ||
|
|
1d66df366f | ||
|
|
3a0f0cdfa8 | ||
|
|
ed291cfe4d | ||
|
|
eec5a085e8 | ||
|
|
44ebba25e5 | ||
|
|
eb484df801 | ||
|
|
a3b84f0876 | ||
|
|
3aaa7345f6 | ||
|
|
7559f7c2f9 | ||
|
|
e48e7aa544 | ||
|
|
55a71ea46a | ||
|
|
6879729b01 | ||
|
|
ad5f9232f7 | ||
|
|
45c44fd936 | ||
|
|
f7cc0dbd00 | ||
|
|
0bf2cc8442 | ||
|
|
23c8590123 | ||
|
|
5ad15655a9 | ||
|
|
59249a7155 | ||
|
|
7f545935b4 | ||
|
|
fe9001a550 | ||
|
|
313bed630a | ||
|
|
d030b09c51 | ||
|
|
454557a468 | ||
|
|
1c7a1e3d8f | ||
|
|
e3e937354b | ||
|
|
83936a924e | ||
|
|
0c74043668 | ||
|
|
4a7cee412c | ||
|
|
b7972342dc | ||
|
|
7e0f5e1401 | ||
|
|
d55ced465d | ||
|
|
8d2bf7571a | ||
|
|
764da32cba | ||
|
|
1d6a015939 | ||
|
|
68a9f55f53 | ||
|
|
e638e7f2c9 | ||
|
|
d3aef40bb3 | ||
|
|
e081e66a13 | ||
|
|
7f055ddb4b | ||
|
|
79f8704a00 | ||
|
|
df3b938ed0 | ||
|
|
cf27269f08 | ||
|
|
4d0b02e049 | ||
|
|
f8bb07ec35 | ||
|
|
601d789662 | ||
|
|
54b7e8f1b1 | ||
|
|
33df962e70 | ||
|
|
ab894eeca3 | ||
|
|
bf4e21933c | ||
|
|
0ea254e94c | ||
|
|
94ae69813f | ||
|
|
f6782893e7 | ||
|
|
54ba881dd2 | ||
|
|
2cd7cece85 | ||
|
|
74e2bcfc37 | ||
|
|
d3f5758df8 | ||
|
|
da052c02e2 | ||
|
|
f063f52981 | ||
|
|
2646d7e656 | ||
|
|
0e22f73d11 | ||
|
|
2d7ada5106 | ||
|
|
c8524485bb | ||
|
|
191a8e4e43 | ||
|
|
4f3e058d64 | ||
|
|
da33b4f406 | ||
|
|
d5851fc4ed | ||
|
|
de9adadf04 | ||
|
|
08f1ba8186 | ||
|
|
135239eabe | ||
|
|
ec838bb8a1 | ||
|
|
52bc9b6326 | ||
|
|
e7eff63585 | ||
|
|
21222511dd | ||
|
|
ffb7924585 | ||
|
|
990f7ab9dc | ||
|
|
30f85737c8 | ||
|
|
f1f196585d | ||
|
|
7bbeed304f | ||
|
|
bdbe7b25eb | ||
|
|
d6e3576203 | ||
|
|
c0411273a9 | ||
|
|
789cae29eb | ||
|
|
d668d9c3bb | ||
|
|
4b8effe250 | ||
|
|
896a9a0a23 | ||
|
|
a28929bc36 | ||
|
|
59606709a6 | ||
|
|
5398d53408 | ||
|
|
31e272711f | ||
|
|
b12abc4533 | ||
|
|
628430f0e9 | ||
|
|
1f3501d8de | ||
|
|
7e3858ef31 | ||
|
|
b8613c5cdf | ||
|
|
8b8bb25717 | ||
|
|
3871508385 | ||
|
|
32a5f4389b | ||
|
|
fadaedc382 | ||
|
|
1772faae3f | ||
|
|
37d4245363 | ||
|
|
2e8bb882eb | ||
|
|
73834a4455 | ||
|
|
abf7382520 | ||
|
|
6ebfe9b630 | ||
|
|
310577f0c2 | ||
|
|
2fe6482730 | ||
|
|
10c285f081 | ||
|
|
9d1b5833a9 | ||
|
|
eb1234832e | ||
|
|
3eac899af5 | ||
|
|
5195708028 | ||
|
|
00977986df | ||
|
|
a5887f7dd6 | ||
|
|
05cc118282 | ||
|
|
cb42cf6ceb | ||
|
|
8f4dd0225b | ||
|
|
e15e7d5ecc | ||
|
|
02d9eaa21b | ||
|
|
982a1c1fd3 | ||
|
|
d2067be9d1 | ||
|
|
2a46a6a9d0 | ||
|
|
816c5bb040 | ||
|
|
356d9170f0 | ||
|
|
0d5afe48a8 | ||
|
|
0eac173451 | ||
|
|
355c2e78ec | ||
|
|
4722471b08 | ||
|
|
1eed542f46 | ||
|
|
bb1444b7f9 | ||
|
|
4c7ab1a47f | ||
|
|
b67a84800b | ||
|
|
7d3ffe324a | ||
|
|
f6ef39867c | ||
|
|
7a3b82e40b | ||
|
|
c3c847c5ff | ||
|
|
2e4ec0e402 | ||
|
|
cca6e94a71 | ||
|
|
20addc10ac | ||
|
|
2a49c009c0 | ||
|
|
077e4916fc | ||
|
|
ae21ba28c7 | ||
|
|
d5cf59cb16 | ||
|
|
ba3bb79b52 | ||
|
|
4aed00d954 | ||
|
|
1052537fc6 | ||
|
|
6d30afb166 | ||
|
|
60c2ab84d8 | ||
|
|
4d9b270d3a | ||
|
|
86bf9f2a28 | ||
|
|
ed1e99353f | ||
|
|
4d7749e265 | ||
|
|
368e3c36b9 | ||
|
|
ef44922aa6 | ||
|
|
84abcc565d | ||
|
|
b3b54ffe00 | ||
|
|
43dae4915f | ||
|
|
990f93a34e | ||
|
|
17b1d4004a | ||
|
|
41f0012dc1 | ||
|
|
9a3282285c | ||
|
|
2ad63e7d7f | ||
|
|
abaf65e185 | ||
|
|
a16b8e3ffc | ||
|
|
fdebee9011 | ||
|
|
00d76439b2 | ||
|
|
b90694142c | ||
|
|
3dac462afd | ||
|
|
a8ffebce60 | ||
|
|
4a86221e8a | ||
|
|
7679b05c55 | ||
|
|
1efd93e947 | ||
|
|
9641af3973 | ||
|
|
953c62f36c | ||
|
|
7620b42bef | ||
|
|
f35d57b534 | ||
|
|
10aadd4e66 | ||
|
|
0af795d2e0 | ||
|
|
80b403c881 | ||
|
|
5d3db0dbd3 | ||
|
|
d015c35912 | ||
|
|
c811662d6f | ||
|
|
8e1b3185fe | ||
|
|
d892f396fb | ||
|
|
64dfce26b1 | ||
|
|
d375d5588c | ||
|
|
e1fb9121af | ||
|
|
7101dc02c4 | ||
|
|
8d3067a625 | ||
|
|
5b85dacfa4 | ||
|
|
4ea3851fb4 | ||
|
|
6071121323 | ||
|
|
e1fe8c8e28 | ||
|
|
708b41b8da | ||
|
|
ec730735bc | ||
|
|
0e2c6131f6 | ||
|
|
1172a542fd | ||
|
|
4b71ea18b8 | ||
|
|
4d8d51facc | ||
|
|
33cbbbd162 | ||
|
|
71fb359aec | ||
|
|
879ce2a195 | ||
|
|
e3d57641ca | ||
|
|
6bd754e6d6 | ||
|
|
c419b8d2a6 | ||
|
|
77191443ec | ||
|
|
0c1b3939ea | ||
|
|
088dc4eb99 | ||
|
|
3019641d9f | ||
|
|
82bb17ebf9 | ||
|
|
67ee42dcda | ||
|
|
0387d9a82e | ||
|
|
bb22cc6ba9 | ||
|
|
9d78336096 | ||
|
|
07590fba08 | ||
|
|
f280ba4e05 | ||
|
|
6e111aab3a | ||
|
|
5fad51e876 | ||
|
|
df11323434 | ||
|
|
2c98474e16 | ||
|
|
6e2803a614 | ||
|
|
6ea8469189 | ||
|
|
0a8064d7b5 | ||
|
|
ad294fa15a | ||
|
|
bebeb43a25 | ||
|
|
72600d2e84 | ||
|
|
193d740133 | ||
|
|
2e4278a89e | ||
|
|
2717c0d494 | ||
|
|
cf3dac5a7d | ||
|
|
56ca218826 | ||
|
|
7cbef74401 | ||
|
|
a228e18669 | ||
|
|
3f24909423 | ||
|
|
72c7cb38b6 | ||
|
|
54983e52c5 | ||
|
|
6ddf9c6817 | ||
|
|
3c20f384f5 | ||
|
|
f9f4a27c6a | ||
|
|
ef8af1a2b6 | ||
|
|
3f4839cfe5 | ||
|
|
7be40800c1 | ||
|
|
78938459b5 | ||
|
|
e2ef4b497c | ||
|
|
d78b59b9cd | ||
|
|
1908ad7583 | ||
|
|
b20da90917 | ||
|
|
69d5be5bd9 | ||
|
|
8e8ee4bb5c | ||
|
|
7bb5f45c82 | ||
|
|
b074af6d41 | ||
|
|
5038d5e838 | ||
|
|
da234f5fcf | ||
|
|
6ff8ba6682 | ||
|
|
dfc630aa23 | ||
|
|
74059bda82 | ||
|
|
394a322fbe | ||
|
|
46786907b6 | ||
|
|
b11179bc84 | ||
|
|
d8115e1cec | ||
|
|
52a26ba4a5 | ||
|
|
72c7a54879 | ||
|
|
9c66737813 | ||
|
|
4f159471d7 | ||
|
|
e63d418b03 | ||
|
|
5f3f859b81 | ||
|
|
90cffa7b37 | ||
|
|
770a7d20a8 | ||
|
|
6b04ca78f9 | ||
|
|
e5ad11748f | ||
|
|
fa38216eaa | ||
|
|
c94e8a395f | ||
|
|
dddd7972fb | ||
|
|
3e4323d10c | ||
|
|
197ca7cf4a | ||
|
|
41827bb653 | ||
|
|
e7656f3be5 | ||
|
|
17f10cafbc | ||
|
|
62239488c9 | ||
|
|
615abebe99 | ||
|
|
b703546c80 | ||
|
|
c862679aac | ||
|
|
2b2bfa6389 | ||
|
|
8e50d42099 | ||
|
|
2fd6fde9b5 | ||
|
|
47606cf46c | ||
|
|
2ab15ba0d1 | ||
|
|
0bd68b9b3e | ||
|
|
6cfb6f60f5 | ||
|
|
be1886179f | ||
|
|
65f2368050 | ||
|
|
90a8a2e9da | ||
|
|
9afd057ee2 | ||
|
|
0440c2a89c | ||
|
|
7245506564 | ||
|
|
8c7f10b976 | ||
|
|
cb2fb9e1c1 | ||
|
|
1316cc6c32 | ||
|
|
ddd503fc31 | ||
|
|
ded1fe3db5 | ||
|
|
f6dc60a9f0 | ||
|
|
62e018aced | ||
|
|
799b31f2aa | ||
|
|
89725523d0 | ||
|
|
c14fff73b2 | ||
|
|
81b4156744 | ||
|
|
35a02524c9 | ||
|
|
a0b2bbcb1c | ||
|
|
baf16a2719 | ||
|
|
de15debd11 | ||
|
|
5253f90284 | ||
|
|
46f4aa8e35 | ||
|
|
8d58e317aa | ||
|
|
046836a63a | ||
|
|
92930d7a6f | ||
|
|
b49d1f3f39 | ||
|
|
cba2fb93c9 | ||
|
|
4c40598c79 | ||
|
|
69c36da984 | ||
|
|
a75835f664 | ||
|
|
06eb8e3e43 | ||
|
|
68d3a7cdb2 | ||
|
|
fa443cea0d | ||
|
|
da1645c353 | ||
|
|
1d498a5ba3 | ||
|
|
f3876079a9 | ||
|
|
dc390b796d | ||
|
|
09edb071ff | ||
|
|
13d28d6ecb | ||
|
|
b92eaa14a5 | ||
|
|
10c41c9dfa | ||
|
|
6c6e26bcaa | ||
|
|
a75ad8080d | ||
|
|
1834182d2e | ||
|
|
a395adc2b6 | ||
|
|
5fe795059b | ||
|
|
292df0c8de | ||
|
|
c3e752f98f | ||
|
|
56737375da | ||
|
|
5fe182f3c4 | ||
|
|
3242d396a9 | ||
|
|
390a23d88e | ||
|
|
00ddfed039 | ||
|
|
775588b300 | ||
|
|
8a50df0e06 | ||
|
|
9c5a38ac70 | ||
|
|
ce7a8bb1f1 | ||
|
|
77dcb26372 | ||
|
|
0aecaec778 | ||
|
|
90b48b5447 | ||
|
|
89be097c3f | ||
|
|
4fdb9ef7f9 | ||
|
|
68dba80b9c | ||
|
|
13615dc15a | ||
|
|
8820a45312 | ||
|
|
b5186a367a | ||
|
|
24ee2cba60 | ||
|
|
ade7bed52f | ||
|
|
3c3ab8138b | ||
|
|
85c1fc457c | ||
|
|
9525581631 | ||
|
|
9856588fa8 | ||
|
|
eb85f56534 | ||
|
|
8cce641e43 | ||
|
|
be237b402c | ||
|
|
26b9fa20be | ||
|
|
9c7efa9037 | ||
|
|
2fa0e11ebb | ||
|
|
db40ad80ae | ||
|
|
11262acafc | ||
|
|
b41147b7f6 | ||
|
|
de9c9ff3f3 | ||
|
|
7a0d57997d | ||
|
|
74aa0a4917 | ||
|
|
c39bf4ee34 | ||
|
|
25edd60ac5 | ||
|
|
28a94982fe | ||
|
|
7c1adf1c2a | ||
|
|
f6dc001e3a | ||
|
|
bff37b60d5 | ||
|
|
f8b5e238f4 | ||
|
|
56de452158 | ||
|
|
0d18289699 | ||
|
|
7819893f81 | ||
|
|
7073eb5494 | ||
|
|
9996b26eba | ||
|
|
d2e205a3fc | ||
|
|
ae1e2dbbcb | ||
|
|
4105614645 | ||
|
|
3753d989b7 | ||
|
|
fb2d08032d | ||
|
|
1dcd260feb | ||
|
|
fc3ae7bfe9 | ||
|
|
dd9715761c | ||
|
|
2b35f0d714 | ||
|
|
18a84c0c6e | ||
|
|
39a88cd4d1 | ||
|
|
502a087962 | ||
|
|
9de9d4ca53 | ||
|
|
d5a2ef88df | ||
|
|
abcb758a56 | ||
|
|
2c88d952d2 | ||
|
|
373ce84268 | ||
|
|
9aff5237ea | ||
|
|
58a333ef99 | ||
|
|
5506af61ab | ||
|
|
56df11005b | ||
|
|
f6f80da995 | ||
|
|
8d5f633884 | ||
|
|
15b3599a05 | ||
|
|
f089ef0c60 | ||
|
|
c513f93329 | ||
|
|
00f55f4044 | ||
|
|
7741d9f65d | ||
|
|
b6418401b3 | ||
|
|
b7d6f32b89 | ||
|
|
789af07ebe | ||
|
|
33981efab1 | ||
|
|
0d05da4e0a | ||
|
|
97c75209f3 | ||
|
|
bd7d643f07 | ||
|
|
ee8049c3bf | ||
|
|
6821546d51 | ||
|
|
c188296a82 | ||
|
|
19d93f9e34 | ||
|
|
a8b9c92207 | ||
|
|
eaa5a35f47 | ||
|
|
c12c5939c0 | ||
|
|
4174317f57 | ||
|
|
5f508e5d17 | ||
|
|
8eedf4b3f1 | ||
|
|
786650f660 | ||
|
|
dea4a38a6c | ||
|
|
7298cbbe0d | ||
|
|
a0f4ac195d | ||
|
|
7ee57a381a |
@@ -1,9 +1,6 @@
|
|||||||
<?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 kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||||
<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.6"/>
|
|
||||||
<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"/>
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
|||||||
30645
.gitattributes
vendored
30645
.gitattributes
vendored
File diff suppressed because it is too large
Load Diff
26
.gitignore
vendored
26
.gitignore
vendored
@@ -2,20 +2,28 @@
|
|||||||
/*.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.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>
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#Wed Jul 27 18:40:11 EDT 2011
|
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
org.eclipse.jdt.core.compiler.source=1.6
|
org.eclipse.jdt.core.compiler.source=1.7
|
||||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
||||||
|
|||||||
219
CHANGES.txt
219
CHANGES.txt
@@ -1,219 +0,0 @@
|
|||||||
Forge Beta: 06-14-2013 ver 1.4.1
|
|
||||||
|
|
||||||
|
|
||||||
12819 cards in total.
|
|
||||||
|
|
||||||
|
|
||||||
-------------
|
|
||||||
Release Notes
|
|
||||||
-------------
|
|
||||||
|
|
||||||
- Java 7 -
|
|
||||||
The devs are discussing a plan to update the battlefield display code with Java FX 2.2 and this should help to improve a few things. Java FX 2.2 requires Java 7 so please update your Java runtime environment in the nearest future. At some point a new version of Forge will no longer run under Java 6.
|
|
||||||
|
|
||||||
|
|
||||||
- New M14 cards -
|
|
||||||
We have added a branch to our SVN for the new cards that are currently being scripted. These cards are not yet available in this build of forge. Please be patient and they will soon become available.
|
|
||||||
|
|
||||||
|
|
||||||
- Match and Deck Editor Layout problems -
|
|
||||||
The match and deck editor windows contain panels that can be moved and/or resized. The changes that you make are saved to files that are named "editor.xml" and "match.xml". These files can be found in your userDir/preferences/ directory.
|
|
||||||
|
|
||||||
Sometimes people will decide that they do not like the changes that they made and wish to go back to the original layout. To reset layouts to deafult, go to the Game Settings -> Preferences -> Troubleshooting section. You will find at this location two buttons that will reset the match layout and the deck editor layouts.
|
|
||||||
|
|
||||||
Also use the mentioned measure if your match or deckeditor won't start - it would help in 90% of the cases.
|
|
||||||
|
|
||||||
|
|
||||||
- The AI Drafting has been improved -
|
|
||||||
The AI evaluated the basic lands higher than anything else. Fixed. The AI would pick cards with RemAIDeck but only at a much lowered pick rate. For example the best pick in a 250 card set would become the 75th best pick, the 20th best pick would become the 95th and so on. Divided this factor by 3 (so the first pick would become the 25th pick). Please test whether this has improved the draft experience.
|
|
||||||
|
|
||||||
|
|
||||||
---------
|
|
||||||
New Cards
|
|
||||||
---------
|
|
||||||
|
|
||||||
Amulet of Quoz
|
|
||||||
Aphetto Dredging
|
|
||||||
Archive Trap
|
|
||||||
Battlefield Scrounger
|
|
||||||
Chain Stasis
|
|
||||||
Chancellor of the Annex
|
|
||||||
Chisei, Heart of Oceans
|
|
||||||
Choking Vines
|
|
||||||
Cobra Trap
|
|
||||||
Defensive Formation
|
|
||||||
Dream Chisel
|
|
||||||
Dream Leash
|
|
||||||
Exiled Doomsayer
|
|
||||||
Fossil Find
|
|
||||||
Gemstone Caverns
|
|
||||||
Grave Consequences
|
|
||||||
Grimoire Thief
|
|
||||||
Hankyu
|
|
||||||
Hibernation's End
|
|
||||||
Indentured Djinn
|
|
||||||
Ion Storm
|
|
||||||
Jester's Scepter
|
|
||||||
Jetting Glasskite
|
|
||||||
Jotun Grunt
|
|
||||||
Kira, Great Glass Spinner
|
|
||||||
Kithkin Armor
|
|
||||||
Krark's Thumb
|
|
||||||
Leashling
|
|
||||||
Liquid Fire
|
|
||||||
Martyr of Bones
|
|
||||||
Master Warcraft
|
|
||||||
Melee
|
|
||||||
Minion of Leshrac
|
|
||||||
Odric, Master Tactician
|
|
||||||
Patron of the Akki
|
|
||||||
Patron of the Kitsune
|
|
||||||
Patron of the Moon
|
|
||||||
Patron of the Nezumi
|
|
||||||
Patron of the Orochi
|
|
||||||
Penance
|
|
||||||
Power Conduit
|
|
||||||
Prowling Pangolin
|
|
||||||
Psychic Vortex
|
|
||||||
Research // Development
|
|
||||||
Search for Survivors
|
|
||||||
Shimmering Glasskite
|
|
||||||
Spinning Darkness
|
|
||||||
Summoning Trap
|
|
||||||
Tainted Specter
|
|
||||||
Teferi's Curse
|
|
||||||
Temporary Truce
|
|
||||||
Thelon's Chant
|
|
||||||
Thought Lash
|
|
||||||
Thran Turbine
|
|
||||||
Tidal Influence
|
|
||||||
Time and Tide
|
|
||||||
Tourach's Chant
|
|
||||||
Truce
|
|
||||||
Uba Mask
|
|
||||||
Void Maw
|
|
||||||
|
|
||||||
|
|
||||||
----------
|
|
||||||
New Planes
|
|
||||||
----------
|
|
||||||
|
|
||||||
Furnace Layer
|
|
||||||
Kharasha Foothills
|
|
||||||
Mimano
|
|
||||||
Mirrored Depths
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
New M14 branch Cards
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Accursed Spirit
|
|
||||||
Advocate of the Beast
|
|
||||||
Ajani's Chosen
|
|
||||||
Archangel of Thune
|
|
||||||
Awaken the Ancient
|
|
||||||
Battle Sliver
|
|
||||||
Blur Sliver
|
|
||||||
Bonescythe Sliver
|
|
||||||
Charging Grffin
|
|
||||||
Corpse Hauler
|
|
||||||
Dawnstrike Paladin
|
|
||||||
Deathgaze Cockatrice
|
|
||||||
Devout Invocation
|
|
||||||
Elvish Mystic
|
|
||||||
Enlarge
|
|
||||||
Fleshpulper Giant
|
|
||||||
Glimpse the Future
|
|
||||||
Grim Return
|
|
||||||
Groundshaker Sliver
|
|
||||||
Guardian of the Ages
|
|
||||||
Hive Stirrings
|
|
||||||
Hunt the Weak
|
|
||||||
Into the Wilds
|
|
||||||
Jace's Mindseeker
|
|
||||||
Kalonian Tusker
|
|
||||||
Liliana's Reaver
|
|
||||||
Marauding Maulhorn
|
|
||||||
Master of Diversion
|
|
||||||
Megantic Sliver
|
|
||||||
Molten Birth
|
|
||||||
Ogre Battledriver
|
|
||||||
Predatory Sliver
|
|
||||||
Primeval Bounty
|
|
||||||
Regathan Firecat
|
|
||||||
Ring of Three Wishes
|
|
||||||
Rise of the Dark Realms
|
|
||||||
Scourge of Valkas
|
|
||||||
Sentinel Sliver
|
|
||||||
Seraph of the Sword
|
|
||||||
Shadowborn Apostle
|
|
||||||
Shadowborn Demon
|
|
||||||
Sliver Construct
|
|
||||||
Soulmender
|
|
||||||
Sporemound
|
|
||||||
Staff of the Death Magus
|
|
||||||
Staff of the Flame Magus
|
|
||||||
Staff of the Mind Magus
|
|
||||||
Staff of the Wild Magus
|
|
||||||
Steelform Sliver
|
|
||||||
Stonehorn Chanter
|
|
||||||
Striking Sliver
|
|
||||||
Thorncaster Sliver
|
|
||||||
Undead Minotaur
|
|
||||||
Vampire Warlord
|
|
||||||
Vastwood Hydra
|
|
||||||
Vial of Poison
|
|
||||||
Windreader Sphinx
|
|
||||||
Woodborn Behemoth
|
|
||||||
Young Pyromancer
|
|
||||||
|
|
||||||
|
|
||||||
------------
|
|
||||||
Known Issues
|
|
||||||
------------
|
|
||||||
|
|
||||||
A small number of cards including Wall of Corpses, Abu Ja'far and others with a similar ability are not functional. The common issue here is actually that they check the blocker/attacker after being removed from the battlefield. Probably need to use LKI. Regular destroy/destroy all effects work just fine. The problem cards are caring about battlefield specific properties, that no longer apply when the base card is no longer on the battlefield.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Forge is likely to be compatible with Java 7 at this time. Some people have used forge with Java 7 and have not reported any problems that are related to Java 7. If you would like to upgrade to Java 7 and have held off because of Forge then you may upgrade as we do not think that it will cause an incompatibility type of problem. We will continue to try to maintain compatibility with Java 6 for the foreseeable future.
|
|
||||||
|
|
||||||
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
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Agetian
|
|
||||||
Asepetci
|
|
||||||
Diogenes
|
|
||||||
Gos
|
|
||||||
Hellfish
|
|
||||||
Marc
|
|
||||||
Max
|
|
||||||
Nordos
|
|
||||||
RedDeckWins
|
|
||||||
Sidereal
|
|
||||||
Sloth
|
|
||||||
Sol
|
|
||||||
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
|
|
||||||
700
README.txt
700
README.txt
@@ -1,700 +0,0 @@
|
|||||||
Installation Instructions:
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Once the Forge archive has been decompressed you should then be able to launch Forge by using the included launcher. Launching Forge by double clicking on the forge jar file in the past caused a java heap space error. Forge's memory requirements have increased over time and the launchers increase the java heap space available to Forge. Currently you can launch Forge by double clicking on the forge jar file without a java heap space error but this is likely to change as we add in more sounds, icons, etc.
|
|
||||||
|
|
||||||
|
|
||||||
Updating to a newer version Instructions:
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
- User data migration -
|
|
||||||
User data files, like decks, saved gauntlets, and card pictures, are now stored in new directories separate from the program data. When this version of Forge is first run, it will scan the program directory for all user data and automatically migrate the files to their new homes. There are three defined user data directores: userDir, cacheDir, and cardPicsDir, and their locations depend on the standard paths for your operating system:
|
|
||||||
|
|
||||||
Windows:
|
|
||||||
userDir=%APPDATA%/Forge/
|
|
||||||
cacheDir=%LOCALAPPDATA%/Forge/Cache/ (or %APPDATA%/Forge/Cache/ for windows versions before the local/roaming directory split)
|
|
||||||
OSX:
|
|
||||||
userDir=$HOME/Library/Application Support/Forge/
|
|
||||||
cacheDir=$HOME/Library/Caches/Forge/
|
|
||||||
Linux:
|
|
||||||
userDir=$HOME/.forge/
|
|
||||||
cacheDir=$HOME/.cache/forge/
|
|
||||||
|
|
||||||
The appdata directory is hidden by default in Win7. Open a Windows Explorer window (or double-click on My Computer] and in the address field type "%appdata%/forge/" (without the quotes). If that doesn't work, type "%appdata"/roaming/forge".
|
|
||||||
|
|
||||||
cardPicsDir is defined as <cacheDir>/pics/cards/ by default. If you wish to use a non-default directory, please see the forge.profile.preferences.example file located in the Forge installation directory root. You can use this file to, for example, share the card pics directory with another program, such as Magic Workstation.
|
|
||||||
|
|
||||||
If you are using the Mac OS X version of Forge then you will find the forge.profile.preferences.example file by right clicking or control clicking on the Forge.app icon. Select "Show Package Contents" from the contextual menu. A Finder window will open and will display a folder named Contents. Navigate to the folder:
|
|
||||||
/Contents/Resources/Java/
|
|
||||||
and you will find the file named forge.profile.preferences.example.
|
|
||||||
|
|
||||||
For reference, here is the full list of moved directories and files:
|
|
||||||
|
|
||||||
Old location New location
|
|
||||||
---------------- ----------------------
|
|
||||||
res/decks/ <userDir>/decks/
|
|
||||||
res/gauntlet/ <userDir>/gauntlet/
|
|
||||||
res/layouts/ <userDir>/preferences/
|
|
||||||
res/preferences/ <userDir>/preferences/
|
|
||||||
res/quest/data/ <userDir>/quest/saves/
|
|
||||||
res/pics/ <cacheDir>/pics/
|
|
||||||
forge.log <userDir>/forge.log
|
|
||||||
|
|
||||||
- New Import Data dialog -
|
|
||||||
The Import Pictures dialog, accessed via the Content Downloaders submenu, has received an overhaul and has been reincarnated as the Import Data dialog. You may recognize it from the automatic data migration procedure if you had any data to migrate when this version was first started. Instead of just importing pictures from a previous version of Forge, it can now import any kind of Forge data whatsoever. If you have a directory full of deck files, you can use the Import Data dialog to copy or move them to the appropriate directory. If you have just downloaded a torrent of high-quality pics for a particular set, use the Import Data dialog to get them to the proper place. The dialog give you a full listing of all file copy/move operations, so you can see what will happen before you click 'Start Import'.
|
|
||||||
|
|
||||||
An importer option was added for including pictures in set-specific card directories that don't map to any currently known card. This handles the case where people have collected complete sets of pics in anticipation of when Forge supports them.
|
|
||||||
|
|
||||||
|
|
||||||
The Mac OS application version:
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
We have packaged the Forge BETA version as a Mac OS application. You can double click the Forge.app icon to launch the forge application on your Apple computer running Mac OS. This application will automatically increase the java heap space memory for you as it launches. This version does not require the forge.command file and it does not need to start the Terminal application as part of the start up process.
|
|
||||||
|
|
||||||
If you update your OS to Apple OSX 10.8 Mountain Lion and try to launch a new version of forge that you will likely get a dialog which states "File is damaged and cannot be opened. Please move to trash."
|
|
||||||
|
|
||||||
Mountain Lion comes with a new Gatekeeper feature and this is probably blocking your ability to launch this newer version of forge. Visit the link below and follow the instructions. They are fairly long and detailed.
|
|
||||||
|
|
||||||
http://support.apple.com/kb/HT5290?viewlocale=en_US&locale=en_US
|
|
||||||
|
|
||||||
Please note that the issue is most likely caused by Mountain Lion's Gatekeeper feature and it is extremely unlikely that the forge dev team will attempt to get a unique Developer ID from Apple and use it to digitally sign our forge app.
|
|
||||||
|
|
||||||
|
|
||||||
Picture location info:
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
The instructions that were found in this section are now out of date. Current instructions can be found in the CHANGES.txt and forge.profile.properties.example files.
|
|
||||||
|
|
||||||
|
|
||||||
Launching Forge and Memory Issues:
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
In the past, some people noticed java heap space errors and lengthy pauses. The memory requirements for Forge have increased over time. The default setting on your computer for the java heap space may not be enough to prevent the above problems.
|
|
||||||
|
|
||||||
The technically proficient can launch the forge jar with an argument from the CLI. The argument "-Xmx512m" may work for computers with 1 Gig of memory. Computers with 2 Gigs or more of memory should be able to use "-Xmx1024m" as an argument.
|
|
||||||
|
|
||||||
We have created several scripts that will launch the Forge jar with "-Xmx1024m" as an argument. People using Windows OS should double click the "forge.exe" file. People using one of the other *nix OS should double click the "forge.sh" file. People using Apple's Mac OS X should download the Mac version of Forge and then double click the "forge.app" application.
|
|
||||||
|
|
||||||
The script file must be located in the same folder as the Forge jar file and the Forge jar file name can not be changed. Otherwise, the scripts will not work.
|
|
||||||
|
|
||||||
If you have a low end machine you may find that the scripts above will prevent java heap space errors but will find that Forge still runs very slowly at times.
|
|
||||||
|
|
||||||
In this case you can try the following. You can try using low quality pictures rather than the high quality pictures. Or you can try removing all of the jpg pictures from the pics folder.
|
|
||||||
|
|
||||||
|
|
||||||
Forge failed to launch:
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
If you're trying to run Forge for the first time, but it doesn't open up, you can try the following to get some output and help yourself/us solve the problem.
|
|
||||||
|
|
||||||
1) Open up a terminal
|
|
||||||
- Under Windows, press Windows+R, type "cmd", hit enter
|
|
||||||
- Under Linux, you probably know that yourself. Use your distribution's application menu, and search for "terminal" in a group like "utilities".
|
|
||||||
- Launch the program named "Console.app" which can be found in your /Applications/Utilities/ folder. Highlight the "All Messages" option and click on the "Clear Display" button before launching Forge.
|
|
||||||
|
|
||||||
2) Go to the folder where you unpacked Forge
|
|
||||||
- Windows: Let's say your forge is in D:\Programs\Forge.
|
|
||||||
- Type "D:", Enter to change to the D: drive.
|
|
||||||
- Type "cd \Programs\Forge", Enter to change to the directory.
|
|
||||||
- NOTE: On nonenglish systems, you might have problems due to the poor localization of Windows. Go to the innermost directory you find (worst case is "\"), then "dir", Enter to show all folders in that folder. Search for the one you're probably wanting. For Example the German "Programme" could really be "Program Files" or something like that.
|
|
||||||
- NOTE: You might have to "quote" directory names with Spaces in them
|
|
||||||
- Linux: Let's say your forge is in /home/user/Forge
|
|
||||||
- Type "cd /home/user/Forge", Enter
|
|
||||||
- NOTE: You might have to "quote" or 'quote' directory names with Spaces in them
|
|
||||||
- Current versions of Forge no longer include a launcher script for Mac OS, proceed to step three.
|
|
||||||
|
|
||||||
3) Run Forge
|
|
||||||
- On Windows, just type "forge.exe", Enter
|
|
||||||
- On Linux, just type "forge.sh", Enter
|
|
||||||
- Launch the Forge application bundle by double clicking on the program named "Forge.app".
|
|
||||||
|
|
||||||
Now you will probably see some sort of Error in the console. the first few lines contain a message that might help you. If you can't fix the problem yourself, please take the complete output and report your problem on the Forum.
|
|
||||||
|
|
||||||
|
|
||||||
The Card Pictures disappear when you restart Forge:
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
if you're running Windows 7, make sure you're running the program as an admin, otherwise no changes will be made to your system (nothing is saved). In Windows 7, Forge may be happier when run from somewhere in the My Documents structure, (they call them Libraries now???) or from another partition other than C:. The user has little permission to do much on the system drive.
|
|
||||||
|
|
||||||
|
|
||||||
Java Issues:
|
|
||||||
------------
|
|
||||||
|
|
||||||
Forge is likely to be compatible with Java 7 at this time. Some people have used forge with Java 7 and have not reported any problems that are related to Java 7. If you would like to upgrade to Java 7 and have held off because of Forge then you may upgrade as we do not think that it will cause an incompatibility type of problem. We will continue to try to maintain compatibility with Java 6 for the foreseeable future.
|
|
||||||
|
|
||||||
Forge requires Java 6 and will not run if you have an earlier version of Java. You will need to update to Java 6.
|
|
||||||
|
|
||||||
|
|
||||||
Card Picture Issues:
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
The server which contained the high quality card pictures is now off line and these high quality card pictures are no longer available as a download from within the forge application. We apologize, but the current dev team do not maintain this server and this matter is out of our control.
|
|
||||||
|
|
||||||
Some people are choosing to re-download all of the low quality card and card set pictures when they install the next version of forge. This consumes large amounts of bandwidth needlessly. Please be careful!
|
|
||||||
|
|
||||||
|
|
||||||
The instructions that were found in this section are now out of date. Current instructions can be found in the CHANGES.txt and forge.profile.properties.example files.
|
|
||||||
|
|
||||||
|
|
||||||
When you install a new version of Forge, please follow the instructions that can be found in the CHANGES.txt and forge.profile.properties.example files. This will allow you to reuse your previous picture files and other user data files. This way you will only have to download the pictures for the new cards.
|
|
||||||
|
|
||||||
This should save enough bandwidth that everyone will be able to download the new set pictures from the cardforge server. We do appreciate your efforts to save bandwidth. Thank you.
|
|
||||||
|
|
||||||
|
|
||||||
Reporting Bugs:
|
|
||||||
---------------
|
|
||||||
|
|
||||||
To report a bug with an official beta release, please follow the instructions at http://www.slightlymagic.net/wiki/Forge#I_think_I_found_a_bug_in_Forge._What_do_I_do.3F .
|
|
||||||
|
|
||||||
To report a bug (1) with an alpha test, (2) with a nightly build, (3) with something compiled from the official Forge software repository, or (4) with the leading edge (formerly "SVN Bug Reports"), please do not submit your bugs to the forum. Instead, please follow the instructions at http://www.slightlymagic.net/wiki/How_to_File_a_Bug_Report_with_Mantis.
|
|
||||||
|
|
||||||
Forge will now allow you to upload a crash report to the Forge forum at CCGH.
|
|
||||||
|
|
||||||
|
|
||||||
A new very hard tier category in Quest mode:
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
You will notice a new very hard tier category for the opponent. As you change from the previous tier to the next tier (easy to medium, etc.) you will now notice that there is not an abrupt change over. There is now a mixture of decks from the previous tier and the next tier for you to chose from. When you win a match you will complete the advancement to the next tier.
|
|
||||||
|
|
||||||
|
|
||||||
The Forge Booster Draft mode:
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
A significant re-write of the Booster Draft functionality has taken place. Draft from the Full card pool, sets/blocks or custom drafts (like cube). The AI will pick cards more intelligently, and builds decks from picked cards. Old method would pick cards for deck and then stop picking new cards.
|
|
||||||
|
|
||||||
|
|
||||||
The developer mode:
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The developer mode gives us a few new features. These new features will primarily interest the devs as it will now help to test out specific cards while testing combat code changes. You can turn on or off this mode at the Home View -> Game Settings -> Preferences -> Gameplay Options section.
|
|
||||||
|
|
||||||
When turned on the battlefield will have a tab named "Dev Mode". There are a number of useful options in this tab. You can disable loss by milling, generate mana in your mana pool, tutor for card, etc.
|
|
||||||
|
|
||||||
|
|
||||||
New foil card image available:
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Rob and Marc have worked together to add in a new feature which will overlay a card's image with a foil like film. A few random cards will have a foil like image in constructed mode games and possibly quest mode games. There is a check box on the Home View -> Game Settings -> Preferences view that you can use to turn this feature on or off.
|
|
||||||
|
|
||||||
|
|
||||||
Informational icons overlays for cards on the battlefield:
|
|
||||||
----------------------------------------------------------
|
|
||||||
|
|
||||||
The Battlefield will now display three symbolic icons that will overlay creature cards. These icons are used to denote summoning sickness and whether a creature is attacking or blocking. Added additional icons that will be drawn over the cards in the battlefield for phasing and counters at a later date. The attack/block/phasing icons and counters will now also be shown on large cards, only casting cost will be omitted. Lands and tokens with different amounts/types of counters will no longer stack. Tokens with different summoning sickness will no longer stack. Lands that become creatures will now always be moved to the front row.
|
|
||||||
|
|
||||||
|
|
||||||
Optional choice for abilities that are on the stack:
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
When a spell or an ability appears on the stack and it says "(OPTIONAL)" you can right-click it to decide if you want to always accept or to decline it.
|
|
||||||
|
|
||||||
|
|
||||||
Multiple quest files:
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Multiple quest files are now supported. This allows you to start a new quest and give it a unique name, and it will not overwrite your previous quest game file.
|
|
||||||
|
|
||||||
|
|
||||||
The new UI now uses tabbed panes:
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
We now have a tab system for sub-menus in the home screen. Quest mode refactored to fit this tab system. It's now considerably easier to use - less cramped, less clicks, more functionality.
|
|
||||||
|
|
||||||
|
|
||||||
The quest mode card shop:
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
You can now buy PreCon decks, Starter packs, Tournament packs and Fat packs from the quest mode card shop.
|
|
||||||
|
|
||||||
|
|
||||||
Player Avatar pictures:
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
The UI has a few new features including the option to pick an avatar from a collection of pictures. This can be accessed from the Settings -> Avatars tab.
|
|
||||||
|
|
||||||
|
|
||||||
The organizational structure of the /res/decks/ folder:
|
|
||||||
-------------------------------------------------------
|
|
||||||
|
|
||||||
The organizational structure of the /res/decks/ folder has been improved and we now have these six subdirectories:
|
|
||||||
|
|
||||||
/decks/constructed/
|
|
||||||
/decks/cube/
|
|
||||||
/decks/draft/
|
|
||||||
/decks/plane/
|
|
||||||
/decks/scheme/
|
|
||||||
/decks/sealed/
|
|
||||||
|
|
||||||
You can place your deck files from an earlier version of Forge into the /res/decks/ folder. When you next launch Forge these decks will be converted to a newer format and will be moved into the proper subdirectory.
|
|
||||||
|
|
||||||
Please not that your /decks/ directory no longer resides in you /res/ directory and has been moved to <userDir>/decks/.
|
|
||||||
|
|
||||||
|
|
||||||
User-created themes for Forge's background, fonts, colors and icons:
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
When you select a new skin in the Preferences view Forge should save the change to the preference file, quit and then automatically re-launch with the new skin displayed. During testing some people have noticed that Forge is not restarting on their computer and they have to re-launch Forge themselves.
|
|
||||||
|
|
||||||
If anyone is interested in creating additional themes for inclusion in the Forge project then you should visit this topic at CCGH:
|
|
||||||
|
|
||||||
http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=8449
|
|
||||||
|
|
||||||
|
|
||||||
The Battlefield UI:
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The Battlefield UI has a new feature implemented which allows us to move and resize the panels to new places on the battlefield. This allows us to personalize the battlefield display to our own liking. You should try moving panels by clicking and dragging their tabs.
|
|
||||||
|
|
||||||
If you do not like your efforts to personalize the battlefield display you can revert the display to the default layout by clicking on the Dock button labeled "Revert layout".
|
|
||||||
|
|
||||||
|
|
||||||
The pets in quest mode:
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Some adjustments to the pets in quest mode were made. The quest mode plant wall's Deathtouch ability was deemed to be too strong against the AI's attack code and this ability was changed to Wither in this version. This includes a new pet.
|
|
||||||
|
|
||||||
|
|
||||||
The dock tab has a new button labeled "Open Layout":
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
The dock now has a new button labeled "Open Layout" along with old button with original function "Revert Layout". Modifying the battlefield layout will result in your changes being saved to a file named "match_preferred.xml". You can copy and rename that file to share your layouts with other people.
|
|
||||||
|
|
||||||
|
|
||||||
The new Deck Editors:
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
The work on the new UI is now finished and this version adds the new UI to the deck editors. We all would like to thank Doublestrike for his contributions to the new UI.
|
|
||||||
|
|
||||||
The new deck editors include:
|
|
||||||
|
|
||||||
* a better text search (can search for multiple terms, and "not" terms
|
|
||||||
* interval filters for P/T and CMC
|
|
||||||
* add/remove 4
|
|
||||||
* better statistics and draw probabilities
|
|
||||||
* Toggle-able, sort-able, resize-able, move-able columns
|
|
||||||
* and of course uses the drag cell layout.
|
|
||||||
|
|
||||||
|
|
||||||
Performance issues on low end machines:
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
Several people have noticed forge slowing down after playing a number of matches without quitting forge in between the matches that are played. The new UI may be involved somehow. We also hope to have this figured out and fixed in the near future. Please be patient in the meanwhile. A recent fix was implemented that should improve the slowdown problem somewhat.
|
|
||||||
|
|
||||||
A lot of time and effort have gone into fixing the memory leak problems that were recently noticed and reported to the dev team. Doublestrike and Slapshot deserve our applause and we are very thankful. People should be able to now play long multi match sessions without noticing slow downs and pauses.
|
|
||||||
|
|
||||||
Some performance changes were made to Forge and it should now operate more quickly on low end machines. Mid to high level machines are unlikely to notice as much of a performance increase. We tried to hunt down all of the bugs that resulted from these changes but there may still be a bug or two in this beta release.
|
|
||||||
|
|
||||||
|
|
||||||
A note about winning booster packs in quest mode:
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
If you win a quest mode match, you get a booster pack for every 1 or 2 (default) Wins, depending on the difficulty level. If you lose and you are playing on easy mode, you get a booster pack every 1 (default) Loss.
|
|
||||||
|
|
||||||
|
|
||||||
The new UI:
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The first step was to update the battlefield window. The second step was to update the New Game window (now named Home view). We got constructed mode and then quest modes working first. We got the draft and sealed modes working again afterwards.
|
|
||||||
|
|
||||||
The work on the new UI is now for the most part finished. We should not expect major changes or major additions to the UI. Future betas may include a few minor bug fixes to the UI. And we may also include a few minor tweaks.
|
|
||||||
|
|
||||||
|
|
||||||
The new Alpha Strike button:
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
A new Alpha Strike button was added to the dock. The Dock is one of the tabs availble in the battlefield view.
|
|
||||||
|
|
||||||
|
|
||||||
Using Forge with the new Mac OS Mountain Lion:
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
If you update your OS to Apple OSX 10.8 Mountain Lion and try to launch a new version of forge that you will likely get a dialog which states "File is damaged and cannot be opened. Please move to trash."
|
|
||||||
|
|
||||||
Mountain Lion comes with a new Gatekeeper feature and this is probably blocking your ability to launch this newer version of forge. Visit the link below and follow the instructions. They are fairly long and detailed.
|
|
||||||
|
|
||||||
http://support.apple.com/kb/HT5290?viewlocale=en_US&locale=en_US
|
|
||||||
|
|
||||||
Please note that the issue is most likely caused by Mountain Lion's Gatekeeper feature and it is extremely unlikely that the forge dev team will attempt to get a unique Developer ID from Apple and use it to digitally sign our forge app.
|
|
||||||
|
|
||||||
|
|
||||||
The Forge sealed deck mode:
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Sealed Deck mode has had a complete rewrite. Full cardpool, block and custom modes are supported. Custom sealed files in the res/sealed folder are exactly the same as custom draft files, except the file extension ".sealed".
|
|
||||||
|
|
||||||
A distinction may now be made between AI decks and Human decks, with the addition of a deck Metadata "PlayerType", which right now just helps by sorting human decks into the human combo box and AI decks into the AI combo box.
|
|
||||||
|
|
||||||
The Forge sealed deck mode has undergone significant changes. You can find these in the 1.2.14 beta and later versions. Instead of a single sealed deck match, you can now choose a 1-5 round gauntlet-style tournament where you will face increasingly difficult (probably) opponent decks. You can also choose to use starter packs instead of boosters in the block mode, choose to use 3-12 boosters instead of the default 6 in the full cardpool and custom (cube) modes, and so on.
|
|
||||||
|
|
||||||
Perhaps the most notable changes to the sealed deck mode are related to "fantasy blocks" and the greatly increased flexibility you have when you are building your own blocks.
|
|
||||||
|
|
||||||
|
|
||||||
The new Gauntlet mode:
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
A new Gauntlet mode has been added. This mode gives you four options: Quick Gauntlet, Build A Gauntlet, Load Gauntlet and Gauntlet Contests. You can create a group of computer decks to play against by choosing either Custom user decks, Quest Decks, Fully random color decks or Semi-random theme decks.
|
|
||||||
|
|
||||||
|
|
||||||
The new Variant mode (was named Multiplayer):
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
A new multiplayer mode has also been added. You should be able to play against multiple AI opponents at this time. You should note that the current Archenemy mode does not use Schemes at this time.
|
|
||||||
|
|
||||||
A lot of things are planned for this new multiplayer mode and it will take time to finish. Please enjoy what we have at this time and be patient. :)
|
|
||||||
|
|
||||||
Since Multiplayer is so new, not all cards will be 100% compatible right away as we expand scripting to handle multiple players.
|
|
||||||
|
|
||||||
The older match layout files are incompatible with the new multiplayer mode. The original match_default.xml, match_preferred.xml and the match_preferred.xml saved to a different name files have to go and can no longer be used. You can keep your editor_preferred.xml file. But you will have to setup your match view panels using the new match_default.xml file.
|
|
||||||
|
|
||||||
|
|
||||||
The new damage dialog:
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
The new damage dialog now uses the new UI.
|
|
||||||
|
|
||||||
|
|
||||||
When choosing cards, sources, etc. using a list box:
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
When choosing cards, sources, etc. using a list box, the currently selected card will now be visually highlighted on the play field (to better distinguish between e.g. three different cards with the same name on the play field). Now the visual highlighting of a card will also work when declaring the order of blockers.
|
|
||||||
|
|
||||||
|
|
||||||
Return to Ravnica Guild Sealed Deck mode:
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
Added Return to Ravnica Guild Sealed Deck mode. Start a new sealed deck game, choose "Block / Set" and then scroll down until you find "Return to Ravnica Guild Sealed (block)". Select that. From the "Choose Set Combination" menu, select the first option. You will be prompted twice to pick your guild (once for the promo cards, once for the actual booster - you should choose the same guild both times). After that you're ready to go.
|
|
||||||
|
|
||||||
|
|
||||||
Targeting arrows are now available in the battlefield display:
|
|
||||||
--------------------------------------------------------------
|
|
||||||
|
|
||||||
The Targeting Overlay has been fixed and re-enabled. It now correctly shows the targeting arcs in cases when it previously showed them in the wrong direction. The match UI is properly refreshed when the targeting arcs are switched on/off. The defunct "mouseover-only" mode is currently disabled (it crashes Forge, difficult to fix).
|
|
||||||
|
|
||||||
The visuals for targeting arrows has been improved and looks better, with an adaptation of the arrow drawing code from MAGE. Thanks to the MAGE team for permission for the adaptation.
|
|
||||||
|
|
||||||
Some people have noticed slowdowns when Targeting arrows are enabled. The battlefield Dock tab includes a targeting icon. You can set the targeting arrows to Off or to Card mouseover to speed up the game.
|
|
||||||
|
|
||||||
|
|
||||||
The new sound system:
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Forge now has a sound effect system in place. Several basic sounds are linked to the code now and will be enabled when "Enable Sounds" option is checked in the preferences. It supports WAV and AU file formats.
|
|
||||||
|
|
||||||
Currently supported sound effects are:
|
|
||||||
(the ones already available in the standard installation of Forge are marked with a [*])
|
|
||||||
|
|
||||||
AddCounter [*] - add_counter.wav - triggered when a counter is added to a permanent.
|
|
||||||
Artifact[*] - artifact.wav - triggered when an artifact is played.
|
|
||||||
ArtifactCreature [*] - artifact_creature.wav - triggered when an artifact creature is played.
|
|
||||||
BlackLand [*] - black_land.wav - triggered when a land with the "B" mana ability is played.
|
|
||||||
Block [*] - block.wav - triggered when a blocker is assigned.
|
|
||||||
BlueLand [*] - blue_land.wav - triggered when a land with the "U" mana ability is played.
|
|
||||||
Creature [*] - creature.wav - triggered when a creature is played.
|
|
||||||
Damage - damage.wav - triggered when a creature is damaged.
|
|
||||||
Destroy [*] - destroy.wav - triggered when a permanent is destroyed.
|
|
||||||
Discard [*] - discard.wav - triggered when a player discards a card.
|
|
||||||
Draw [*] - draw.wav - triggered when a player draws a card.
|
|
||||||
Enchantment [*] - enchant.wav - triggered when an enchantment is played.
|
|
||||||
EndOfTurn [*] - end_of_turn.wav - triggered at the end of turn.
|
|
||||||
Equip [*] - equip.wav - triggered when an equipment is equipped.
|
|
||||||
FlipCoin [*] - flip_coin.wav - triggered when a coin is flipped.
|
|
||||||
GreenLand [*] - green_land.wav - triggered when a land with the "G" mana ability is played.
|
|
||||||
Instant [*] - instant.wav - triggered when an instant is played.
|
|
||||||
LifeLoss [*] - life_loss.wav - triggered when a player loses life.
|
|
||||||
LoseDuel[*] - lose_duel.wav - triggered when a player loses a duel.
|
|
||||||
ManaBurn - mana_burn.wav - triggered during a mana burn if the appropriate rule is enabled.
|
|
||||||
OtherLand - other_land.wav - triggered when a land with non-color mana abilities or any other land is played.
|
|
||||||
Planeswalker [*] - planeswalker.wav - triggered when a planeswalker is played.
|
|
||||||
Poison [*] - poison.wav - triggered when a player receives a poison counter.
|
|
||||||
RedLand [*] - red_land.wav - triggered when a land with the "R" mana ability is played.
|
|
||||||
Regen - regeneration.wav - triggered when a creature is regenerated.
|
|
||||||
RemoveCounter - remove_counter.wav - triggered when a counter is removed from a permanent.
|
|
||||||
Sacrifice - sacrifice.wav - triggered when a permanent is sacrificed.
|
|
||||||
Sorcery [*] - sorcery.wav - triggered when a sorcery is played.
|
|
||||||
Shuffle [*] - shuffle.wav - triggered when a player shuffles his deck.
|
|
||||||
Tap [*] - tap.wav - triggered when a permanent is tapped.
|
|
||||||
Token [*] - token.wav - triggered when a token is created.
|
|
||||||
Untap [*] - untap.wav - triggered when a permanent is untapped.
|
|
||||||
WhiteLand [*] - white_land.wav - triggered when a land with the "W" mana ability is played.
|
|
||||||
WinDuel [*] - win_duel.wav - triggered when a player wins the duel.
|
|
||||||
|
|
||||||
All sounds use the event bus model now and are not called directly.
|
|
||||||
|
|
||||||
|
|
||||||
The new Vanguard mode:
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
We now have a Vanguard mode implemented. This is a work in progress. The older match layout files are incompatible with the new Vanguard mode. The original match_default.xml, match_preferred.xml and the match_preferred.xml saved to a different name files need to be deleted and can no longer be used. You can keep your editor_preferred.xml file. But you will have to setup your match view panels using the new match_default.xml file.
|
|
||||||
|
|
||||||
|
|
||||||
The new Archenemy mode:
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Schemes have been added to the Archenemy mode. This is a work in progress and there may be a bug or two for us to find.
|
|
||||||
|
|
||||||
|
|
||||||
Quest Worlds:
|
|
||||||
-------------
|
|
||||||
|
|
||||||
This version allows you to travel between the regular quest world and the other worlds (Shandalar, Ravnica, Jamuraa, more may be added in the future) to get different duel opponents and challenges. You will have to complete your current challenges before traveling or you will lose them.
|
|
||||||
|
|
||||||
World-specific format enforcing and starting world selection are available. Something has to be done about locked (non-repeatable) challenges so they do not end up locking other challenges in different worlds.
|
|
||||||
|
|
||||||
|
|
||||||
Forge now has sideboards for the human player:
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
Sideboards have been implemented for Human players. We currently have:
|
|
||||||
|
|
||||||
* Sideboard creation support in relevant deck editor modes.
|
|
||||||
* In-game sideboarding with persistence between rounds in a match and sorting of cards in the in-game sideboard editor.
|
|
||||||
* Sideboard supported as a zone, with some relevant cards already in.
|
|
||||||
* Correct validation of decks, both before the game starts and between the rounds (Limited min 40, Constructed min 60, free-form sideboard/main in Draft and Sealed, 1:1 sideboarding with 0 or 15 cards allowed in sideboard in Constructed (all variants) and Quest; OK to have less than minimum between rounds in a match in all modes if lost cards on ante).
|
|
||||||
* Correct (fingers crossed) interaction of sideboarding with other relevant aspects of Forge rule enforcement (mulligan and ante interactions were corrected, initial hand and library between rounds were both corrected, everything else looks so far so good).
|
|
||||||
|
|
||||||
We don't yet have:
|
|
||||||
|
|
||||||
* AI sideboarding.
|
|
||||||
|
|
||||||
|
|
||||||
The deck conformance/legality limitation:
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
The deck conformance/legality is now a user-toggable preference and is enabled by default. You no longer need to turn on dev mode to play an illegal deck.
|
|
||||||
|
|
||||||
|
|
||||||
Using Forge on displays that are only 600 pixels tall or slightly larger:
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The "Sanctioned Format: Constructed" view should now be compatible with displays that are only 600 pixels tall. The deck list at 600 pixels tall should now display three lines of text rather than less than a single line of text.
|
|
||||||
|
|
||||||
|
|
||||||
We are looking for help in finding additional sound files for the new sound feature:
|
|
||||||
------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
This version of forge includes a few sound files for the new sound effect system. While we have several sounds assigned to a few of the available events there are a number of events that do not yet have a assigned sound file. This should be considered a work in progress and we could use some help in finding interesting sounds that we can add to forge.
|
|
||||||
|
|
||||||
The sound files need to be in wav or au format, wav appears to be more widespread but the code can handle either format. The sound files need to be copyright-free and they should be in the public domain.
|
|
||||||
|
|
||||||
You can either record your own sounds if you have the necessary equipment or you may be able to find an appropriate sound on a website such as http://www.freesound.org/browse/
|
|
||||||
|
|
||||||
You should note that sound files can be large and we would like to avoid this if possible. A good size to shoot for would be 50 K or less. There is a freeware sound editor that may have versions for all operating systems. This app is named Audacity.
|
|
||||||
|
|
||||||
We have a forge forum topic at the Collectible Card Games Headquarters web site that is devoted to finding sounds for this new sound system. Please visit this topic and contribute a sound or two. We can use your help and assistance. :)
|
|
||||||
|
|
||||||
http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=8570
|
|
||||||
|
|
||||||
|
|
||||||
Notes about the second Quest World, Jamuraa:
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
A second Quest World, Jamuraa, has been added to Forge. When playing Quest mode, it is now possible to 'Travel' between the regular Quest environment and the two Worlds, Shandalar and Jamuraa, both of which include special formats, opponents and challenges. Or you can start a new Quest in any of them.
|
|
||||||
|
|
||||||
Like Shandalar, Jamuraa is a fantasy world. Its peaceful existence has recently been wrecked by a planar conjunction that shattered the barriers between Jamuraa and the infernal planes known as Jahim, Saqar, and Jahannam. The demon planeswalkers who rule those planes, and their hellish sister, Lilith, are now extending their influence over Jamuraa and gradually destroying the whole continent. Your task is to fight their minions and ultimately challenge the four demons - but beware, their destructive might is unfathomable!
|
|
||||||
|
|
||||||
From a technical perspective, the following sets are available to the player in Jamuraa:
|
|
||||||
5th Edition, Arabian Nights, Mirage, Visions, Weatherlight.
|
|
||||||
|
|
||||||
Jamuraa contains:
|
|
||||||
- 81 opponent decks, broken down as follows: 13 'easy' decks, 17 'medium' decks, 31 'hard' decks, and 20 'very hard' decks.
|
|
||||||
- 9 challenges, including the 4 demon planeswalkers (the 3 demon rulers and Lilith) and 5 other special scenarios set in Jamuraa. All challenges are repeatable. All are fairly hard, and the 4 demon challenges are especially fiendish.
|
|
||||||
For the most part, the opponent duel and challenge decks are built with the same format restrictions as your own cardpool, and some of the easiest opponent decks were in fact based on a limited cardpool. But there will be exceptions, especially in the hard/very hard decks and challenges, which can be more like Vintage/T1 decks than pure Mirage + 5th Edition decks. There will be older cards here and there, and maybe even a random Tempest card or two (although these are extremely scarce!).
|
|
||||||
Hint: if you find the later 'Vintage' opponent decks unfair or near-impossible to beat with your 5th Edition/Mirage block cards, you can Travel to Shandalar and collect some old power cards there, and then return to Jamuraa. Just remember to complete your challenges before traveling.
|
|
||||||
|
|
||||||
Information on the quest worlds format can be found in this topic:
|
|
||||||
|
|
||||||
http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=9258
|
|
||||||
|
|
||||||
|
|
||||||
New Deck Editor features with improved Filters:
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
Some work is being done on the various deck editors -- including the quest spell shop -- and we hope to add in additional features while improving the UI. Here is a quick tutorial on the new features:
|
|
||||||
|
|
||||||
FILTERS
|
|
||||||
The Filters tab has been removed and filters are now controlled from the Card Catalog tab. Pretty much everything that used to be a checkbox is now represented by a toggle button. The statistics labels at the top for colors and card types can now be clicked to toggle display of the related cards. Clicking the Total Cards label in the upper left will alternately set all filters on and off.
|
|
||||||
|
|
||||||
Text searches are done by just typing the the text box. The results will update as you type. Use the combo box to the right of the text box to set whether the search should be for cards with or without the specified text. The three toggle buttons to the right of the combo box allow you to specify where in the card the text should (or should not) match. Complex text searches, such as for goblin creatures with haste but not with kicker costs, are possible by stacking the search filters. Put the current search filter on the stack by selecting the "Add filter" button to the left of the text box and choosing the "Current text search" option. You can also use the keyboard shortcut Ctrl+Enter (Cmd+Enter on OSX). This will keep your current text search applied, but will clear the text box and allow you to input the next search filter. To perform the example goblin search above, you would:
|
|
||||||
1) Ensure "in" is displayed in the the combo box. Enter "goblin" in the text box and deselect Name and Text so that only Type is selected.
|
|
||||||
2) Hit Ctrl+Enter (Cmd+Enter on OSX). Notice the "Contains: 'goblin" in: type" filter appears below the text box.
|
|
||||||
3) Type "haste" in the text box. Deselect Type and select Text. Hit Ctrl+Enter.
|
|
||||||
4) Change the combo box from "in" to "not in". Type "kicker" in the text box.
|
|
||||||
The shown entries match your combined search filter. Click the little 'x' in the upper right corner of each search widget to remove that filter from the filter stack.
|
|
||||||
|
|
||||||
Format filters allow you to restrict the shown cards to those that are legal in a particular format. Select the "Add filter" button, hover over the "Format" submenu, and choose one of the defined formats. The filter will appear below the text box and will be combined with whatever other filters you have on the stack. Hover the mouse over the filter widget to see details on allowed sets and banned cards. Keep in mind that cards from other, non-listed sets may still appear in the results if they are just reprints of allowed cards.
|
|
||||||
|
|
||||||
Set filters are similar to format filters except that a window will come up with a grid of checkboxes so you can select exactly which sets you would like shown. There is a checkbox at the bottom (off by default) that will allow the filter to include cards reprinted in unselected sets, just like the format filter above. If you don't check this checkbox, only cards printed in the selected sets will be shown, allowing you to focus on just that set's version of a particular card. This is very useful in quest mode when determining which cards in a set you have yet to collect.
|
|
||||||
|
|
||||||
Value range filters allow you to restrict the cards by power, toughness, and/or converted mana cost (CMC). The text boxes that appear in the filter widget are editable and update the shown cards in realtime as you modify the numbers.
|
|
||||||
|
|
||||||
Quest World filters are similar to Format filters in that they restrict the shown cards to a group of sets, respecting lists of banned cards. They are useful when constructing decks that will be valid in the various quest worlds. You can have more than one quest world filter active at the same time; useful if you are constructing a deck that will be used in multiple worlds.
|
|
||||||
|
|
||||||
SPELL SHOP
|
|
||||||
The spell shop interface has also received some usability enhancements. The first one you may notice is the addition of a new column named "Owned". This column is intended to help players decide whether buying an item is likely to be beneficial. The data in this column varies depending on what kind of item the row represents:
|
|
||||||
Cards: A number indicating how many of a particular card a player already owns
|
|
||||||
Preconstructed decks: A "YES" or "NO" indicating whether the deck exists in the player's deck list
|
|
||||||
Booster/fat packs: A percentage indicating how close a player is to completing the related set. "Complete" means at least one of every basic land and at least 4 of every other card.
|
|
||||||
If you don't want this column, it can be turned off in the editor preferences.
|
|
||||||
|
|
||||||
The new "Sell excess cards" button appears above the player's library. Clicking it will sell all cards that are not basic lands until only four copies of the cards remain. It's a one-click "cleanup" of the library and a great way to safely and quickly regain some cash.
|
|
||||||
|
|
||||||
The "Full catalog view" button appears to the left of the "Buy Card" button. Toggling this button will switch between showing the store's inventory and the full catalog. By applying a filter to show only a particular set (or group of sets), players can use this view to discover exactly which cards they do not own. Buying and selling cards is disabled while in this view.
|
|
||||||
|
|
||||||
Multibuy: By selecting any number of items and hitting space (or selecting the "Buy Card" or "Sell Card" buttons), a player can buy one of everything selected.
|
|
||||||
|
|
||||||
Find-as-you-type is now implemented for Deck Editor tables. Just start typing while the table has focus and the next card with a matching string in its name will be highlighted. If more than one card matches, hit Enter to select the next matching card. A popup panel will appear with the search string so you know what you are searching for. If no cards match the string, the string will be highlighted in red. Normally, if you hit the spacebar while a card is selected in one table, the card will be moved to the other table (catalog/deck). When the popup is displayed, space characters are interpreted as part of the search string. Find-as-you-type mode is automatically exited after 5 seconds of inactivity, or hit Escape to exit find-as-you-type mode immediately.
|
|
||||||
|
|
||||||
The Deck Editor has also gained some hotkey and context menu abilities. R-click on a card (or a group of selected cards) for a list of actions and keyboard shortcuts. In particular, you can now transfer cards 4 at a time using the keyboard and interact with the sideboard from anywhere. Also remember that you can jump to the other table with the arrow keys and jump to the text filter with ctrl/cmd+f. From the text filter, you can jump down to the tables by pressing enter.
|
|
||||||
|
|
||||||
|
|
||||||
The Game Log:
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Added a 'copy to clipboard' button on WinLose screen so players can easily copy the game log.
|
|
||||||
|
|
||||||
|
|
||||||
The UI is more keyboard-friendly:
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Work was also done on making the UI more keyboard-friendly. For example, the OK button should now stay focused during matches, so you can advance through the stages by hitting Enter without having to go over and click the button all the time. If you find the button is losing focus, please report it as a bug.
|
|
||||||
|
|
||||||
|
|
||||||
Gatecrash Guild Sealed game mode:
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Gatecrash Guild Sealed game mode has been added. To use it, start a new Sealed Mode Game, select "Block / Set" and "Gatecrash Guild Sealed". Select the first (default) configuration in the "Choose Set Combination" dialog, and when asked to pick your boosters, choose the guild you want twice (once for the guild-specific booster, and then for the extra promo cards).
|
|
||||||
|
|
||||||
The following cards are not included in the guild boosters of this game mode because they are not currently implemented in Forge: Bane Alley Broker, Bioshift, Killing Glare, Simic Manipulator.
|
|
||||||
|
|
||||||
|
|
||||||
New User Preferences:
|
|
||||||
---------------------
|
|
||||||
The display of clones and copied cards is now a matter of user preference. A user to now choose whether a clone/copied card should use its native artwork or the artwork of the card being cloned. So if Infinite Reflection is played and the "Clones use original card art" preference is checked, the cards that become copies of the enchanted card will still use their own art whereas by default they would all use the card art from the enchanted card.
|
|
||||||
|
|
||||||
|
|
||||||
Flippable Cards:
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Flippable cards -- cards that have an alternate card printed upside-down at the bottom -- will now flip their appearance on the battlefield when in a flipped state. They are now also flippable in the card viewer by clicking on the card image, just like double-sided "transform" cards have been. When flipped this way, the details of the flipped side can be examined in the Card Details oracle text.
|
|
||||||
|
|
||||||
|
|
||||||
High Quality Booster Pictures:
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Forge will now download high quality booster pictures. If you are interested in these high quality booster pictures, then you should delete the pictures that are found in your <cacheDir>/pics/boosters/ directory. Then download the new pictures by using the Home View -> Game Settings -> Content Downloaders -> Download Quest Images button.
|
|
||||||
|
|
||||||
|
|
||||||
Flip, Transform and Morph cards:
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
When you mouse over a flip, transform or Morph (controlled by you) card in battlefield, you may hold SHIFT to see other state of that card at the side panel that displays card picture and details.
|
|
||||||
|
|
||||||
|
|
||||||
Alternate sound system:
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Implemented an alternative sound system for people who have issues with sound gradually or instantly disappearing on certain Linux systems. Can be switched on/off in the preferences without needing a restart. Uses the standard Java Sound API so it doesn't require an external dependency. It's non-caching and, as such, less efficient than the regular sound system, so only use it in case you have issues with the default system, otherwise leave the option unchecked.
|
|
||||||
|
|
||||||
|
|
||||||
Human vs Human play over the internet:
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
Some initial code has been added that will at some point in the future allow human vs human play over the internet. This is a work in progress and is far from being implemented at this time. Please be patient. Anyone who is curious can read the messages in the http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=9837 topic.
|
|
||||||
|
|
||||||
|
|
||||||
Random Deck generation:
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Deck generation is now strictly conforming the colors chosen. You won't get any Boros Reckoner in a RG deck, that could be added before the change (because its manacost could be paid with red mana). Avacyn's Piligrim won't be added in a deck that has green but doesn't have white, though it does not consume, but produces white mana. As well there won't be added any creatures whose activated abilities require colors not choosen for a given deck. That is to say that now color identity is used for deck generation, that allows a better filtering of cards.
|
|
||||||
|
|
||||||
|
|
||||||
Single declare attackers step:
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Combined declare attackers step for all Defending Players/Planeswalkers. On declare attackers step you have to click the entiry you are about to attack and then click on the creatures that should attack it. Clicking on a planeswalker or player visually highlights it, so that you will see whos attackers are assigned at the moment. By default your first opponent is pre-selected.
|
|
||||||
|
|
||||||
|
|
||||||
Booster slots:
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Booster slots are now way more customizable. This change allows us to implement DGM boosters correctly. Note that custom cube .draft and .sealed files have changed their format. They require a booster description instead of Num(Rarity) N lines. Find example records in files describing SkieraCube and Juzamjedi cude coming with Forge. Singleton parameter is obsolette, since all cards within a booster slot are unique. Ignore rarity is also deprecated, for Booster options allow you to pick cards regardless of rarity with 'any' word standing for ignored rarity. (ex: 15 Any)
|
|
||||||
|
|
||||||
|
|
||||||
Quest challenge start in play cards:
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
We have received reports that the quest challenge start in play cards are not appearing on the battlefield for people who were playing the classic mode rather than the fantasy mode. This should now be fixed.
|
|
||||||
|
|
||||||
|
|
||||||
Commander:
|
|
||||||
----------
|
|
||||||
|
|
||||||
We are taking baby steps toward Commander but there are some hurdles left before we get there. We need to implement the replacement effect that moves the commander back to your command zone; the cost-changing depending on how many times you've cast your commander; generating colorless mana if you try to generate mana outside your commanders color identity and AI for all the above.
|
|
||||||
|
|
||||||
|
|
||||||
Customize your Sealed Deck games with fantasy blocks:
|
|
||||||
-----------------------------------------------------
|
|
||||||
|
|
||||||
We have an informative topic at CCGH which explains how you can create "fantasy blocks" and the greatly increased flexibility you have when you are building your own blocks. Please see the topic http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=8164 for more information. Also note that the syntax was recently changed, see message http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=8164&view=unread#p117389 for additional information.
|
|
||||||
|
|
||||||
|
|
||||||
Custom Cube:
|
|
||||||
------------
|
|
||||||
|
|
||||||
1. You can create and save your cube as a constructed deck. Place the .dck file in the /res/cube directory.
|
|
||||||
2. To create a description file in the /res/draft folder you need to make a copy of any existing *.draft file and the adjust the deckfile and booster structure to meet your needs.
|
|
||||||
|
|
||||||
This file format is outdated. "NumCards" is no longer used. Instead there should be a line describing a booster.
|
|
||||||
|
|
||||||
Find some examples below:
|
|
||||||
|
|
||||||
Booster: 1 Rare, 11 Common, 3 Uncommon
|
|
||||||
Booster: 5 Common, 5 Uncommon, 5 Rare, 1 Mythic
|
|
||||||
Booster: 16 Any
|
|
||||||
Booster: 10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand
|
|
||||||
|
|
||||||
|
|
||||||
Blank screen when starting a match:
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
This problem should be fixed. At least you'll see an exception/crash report describing why the match didn't start instead of a blank screen.
|
|
||||||
|
|
||||||
|
|
||||||
Duplicate triggers problem solved:
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
This problem has now been fixed. (Mana Vortex required 2 lands, Emrakul gave 2 extra turns, Empty the Warrens creatures two Storm triggers instead of one.)
|
|
||||||
|
|
||||||
|
|
||||||
Constructed mode AI vs AI matches:
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Added support for AI vs AI matches. There is a checkbox which allows you to view the AI vs AI match. This is a work in progress and is not finished. While it appears to work OK there is still a need for a few UI changes. Please be patient. These AI vs AI matches will not be a good way to test out various deck types against one another as the AI does not understand card combos and the AI is still limited at this time.
|
|
||||||
|
|
||||||
|
|
||||||
Constructed mode Human vs Human Hotseat matches:
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Hotseat matches are now possible. This mode has some known bugs to fix. At this time both players must use the same computer. Human vs Human matches via the internet is planned and should become available sometime in the future. Please be patient.
|
|
||||||
|
|
||||||
|
|
||||||
Using predetermined decks in quest mode Challenges:
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
Added the capability to play Challenges vs predetermined decks (along with a few other related options to disallow specific quest mode things). Added Sorin vs Tibalt, and Tibalt vs Sorin as examples of Challenges that force you to use a specific Deck. (They seemed to be the best duel deck compatibility for the AI).
|
|
||||||
|
|
||||||
|
|
||||||
Targeting Overlay:
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Targeting arrows will now be shown for equipments equipping permanents currently under opponent's control (for those rare cases when e.g. an equipped creature gets Switcheroo'd for something else).
|
|
||||||
|
|
||||||
|
|
||||||
Our Lawyers Made Us Do This:
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
This product includes software developed by the Indiana University Extreme! Lab (http://www.extreme.indiana.edu/).
|
|
||||||
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.8</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>
|
||||||
@@ -24,7 +24,12 @@ package forge.game.ai;
|
|||||||
* from the text file.
|
* from the text file.
|
||||||
*/
|
*/
|
||||||
public enum AiProps { /** */
|
public enum AiProps { /** */
|
||||||
AI_MULLIGAN_THRESHOLD ("5"); /** */
|
DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN ("1"), /** */
|
||||||
|
DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE ("3"), /** */
|
||||||
|
DEFAULT_PLANAR_DIE_ROLL_CHANCE ("50"), /** */
|
||||||
|
MULLIGAN_THRESHOLD ("5"), /** */
|
||||||
|
PLANAR_DIE_ROLL_HESITATION_CHANCE ("10"),
|
||||||
|
CHEAT_WITH_MANA_ON_SHUFFLE ("FALSE"); /** */
|
||||||
|
|
||||||
private final String strDefaultVal;
|
private final String strDefaultVal;
|
||||||
|
|
||||||
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.8</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);
|
||||||
|
variantCards = new CardDb(variantsCards, editions, 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.
|
||||||
423
forge-core/src/main/java/forge/card/CardDb.java
Normal file
423
forge-core/src/main/java/forge/card/CardDb.java
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
/*
|
||||||
|
* 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.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.Multimaps;
|
||||||
|
|
||||||
|
import forge.card.CardEdition.CardInSet;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
import forge.util.CollectionSuppliers;
|
||||||
|
import forge.util.Lang;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
|
public final class CardDb implements ICardDatabase {
|
||||||
|
public final static String foilSuffix = "+";
|
||||||
|
private final static int foilSuffixLength = foilSuffix.length();
|
||||||
|
|
||||||
|
// need this to obtain cardReference by name+set+artindex
|
||||||
|
private final Multimap<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 CardDb(Map<String, CardRules> rules, CardEdition.Collection editions0, boolean logMissingCards) {
|
||||||
|
this.rulesByName = rules;
|
||||||
|
this.editions = editions0;
|
||||||
|
List<String> missingCards = new ArrayList<String>();
|
||||||
|
for(CardEdition e : editions.getOrderedEditions()) {
|
||||||
|
boolean worthLogging = logMissingCards && ( e.getType() == CardEdition.Type.CORE || e.getType() == CardEdition.Type.EXPANSION || e.getType() == CardEdition.Type.REPRINT );
|
||||||
|
if(worthLogging)
|
||||||
|
System.out.print(e.getName() + " (" + e.getCards().length + " cards)");
|
||||||
|
String lastCardName = null;
|
||||||
|
int artIdx = 0;
|
||||||
|
for(CardEdition.CardInSet cis : e.getCards()) {
|
||||||
|
if ( cis.name.equals(lastCardName) )
|
||||||
|
artIdx++;
|
||||||
|
else {
|
||||||
|
artIdx = 0;
|
||||||
|
lastCardName = cis.name;
|
||||||
|
}
|
||||||
|
CardRules cr = rulesByName.get(lastCardName);
|
||||||
|
if( cr != null )
|
||||||
|
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx));
|
||||||
|
else if (worthLogging)
|
||||||
|
missingCards.add(cis.name);
|
||||||
|
}
|
||||||
|
if(worthLogging) {
|
||||||
|
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, " | ") );
|
||||||
|
}
|
||||||
|
missingCards.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCard(PaperCard paperCard) {
|
||||||
|
allCardsByName.put(paperCard.name, 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits cardname into Name and set whenever deck line reads as name|set.
|
||||||
|
*/
|
||||||
|
private static ImmutablePair<String, String> splitCardName(final String name) {
|
||||||
|
String cardName = name; // .trim() ?
|
||||||
|
final int pipePos = cardName.indexOf('|');
|
||||||
|
|
||||||
|
if (pipePos >= 0) {
|
||||||
|
final String setName = cardName.substring(pipePos + 1).trim();
|
||||||
|
cardName = cardName.substring(0, pipePos);
|
||||||
|
// only if set is not blank try to load it
|
||||||
|
if (StringUtils.isNotBlank(setName) && !"???".equals(setName)) {
|
||||||
|
return new ImmutablePair<String, String>(cardName, setName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ImmutablePair<String, String>(cardName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFoil(final String cardName) {
|
||||||
|
return cardName.toLowerCase().endsWith(CardDb.foilSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the foil suffix.
|
||||||
|
*
|
||||||
|
* @param cardName the card name
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public String removeFoilSuffix(final String cardName) {
|
||||||
|
return cardName.toLowerCase().endsWith(CardDb.foilSuffix) ? cardName.substring(0, cardName.length() - CardDb.foilSuffixLength) : cardName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard tryGetCard(final String cardName0) {
|
||||||
|
return tryGetCard(cardName0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard tryGetCard(final String cardName0, boolean fromLastSet) {
|
||||||
|
if (null == cardName0) {
|
||||||
|
return null; // obviously
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isFoil = this.isFoil(cardName0);
|
||||||
|
final String cardName = isFoil ? this.removeFoilSuffix(cardName0) : cardName0;
|
||||||
|
|
||||||
|
final ImmutablePair<String, String> nameWithSet = CardDb.splitCardName(cardName);
|
||||||
|
|
||||||
|
final PaperCard res = nameWithSet.right == null
|
||||||
|
? ( fromLastSet ? this.uniqueCardsByName.get(nameWithSet.left) : Aggregates.random(this.allCardsByName.get(nameWithSet.left)) )
|
||||||
|
: tryGetCard(nameWithSet.left, nameWithSet.right);
|
||||||
|
return null != res && isFoil ? getFoiled(res) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard tryGetCardPrintedByDate(final String name0, final boolean fromLatestSet, final Date printedBefore) {
|
||||||
|
final boolean isFoil = this.isFoil(name0);
|
||||||
|
final String cardName = isFoil ? this.removeFoilSuffix(name0) : name0;
|
||||||
|
final ImmutablePair<String, String> nameWithSet = CardDb.splitCardName(cardName);
|
||||||
|
|
||||||
|
PaperCard res = null;
|
||||||
|
if (null != nameWithSet.right) // set explicitly requested, should return card from it and disregard the date
|
||||||
|
res = tryGetCard(nameWithSet.left, nameWithSet.right);
|
||||||
|
else {
|
||||||
|
Collection<PaperCard> cards = this.allCardsByName.get(nameWithSet.left); // cards are sorted by datetime desc
|
||||||
|
int idxRightSet = 0;
|
||||||
|
for (PaperCard card : cards) {
|
||||||
|
if (editions.get(card.getEdition()).getDate().after(printedBefore))
|
||||||
|
idxRightSet++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res = fromLatestSet ? Iterables.get(cards, idxRightSet, null) : Aggregates.random(Iterables.skip(cards, idxRightSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null != res && isFoil ? getFoiled(res) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard tryGetCard(final String cardName, String setName) {
|
||||||
|
return tryGetCard(cardName, setName, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard tryGetCard(final String cardName0, String setName, int index) {
|
||||||
|
final boolean isFoil = this.isFoil(cardName0);
|
||||||
|
final String cardName = isFoil ? this.removeFoilSuffix(cardName0) : cardName0;
|
||||||
|
|
||||||
|
Collection<PaperCard> cards = allCardsByName.get(cardName);
|
||||||
|
if ( null == cards ) return null;
|
||||||
|
|
||||||
|
CardEdition edition = editions.get(setName);
|
||||||
|
if ( null == edition )
|
||||||
|
return tryGetCard(cardName, true); // set not found, try to get the same card from just any set.
|
||||||
|
String effectiveSet = edition.getCode();
|
||||||
|
|
||||||
|
PaperCard result = null;
|
||||||
|
if ( index < 0 ) { // this stands for 'random art'
|
||||||
|
PaperCard[] candidates = new PaperCard[9]; // 9 cards with same name per set is a maximum of what has been printed (Arnchenemy)
|
||||||
|
int cnt = 0;
|
||||||
|
for( PaperCard pc : cards ) {
|
||||||
|
if( pc.getEdition().equalsIgnoreCase(effectiveSet) )
|
||||||
|
candidates[cnt++] = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cnt == 0 ) return null;
|
||||||
|
result = cnt == 1 ? candidates[0] : candidates[MyRandom.getRandom().nextInt(cnt)];
|
||||||
|
} else
|
||||||
|
for( PaperCard pc : cards ) {
|
||||||
|
if( pc.getEdition().equalsIgnoreCase(effectiveSet) && index == pc.getArtIndex() ) {
|
||||||
|
result = pc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( result == null ) return null;
|
||||||
|
return isFoil ? getFoiled(result) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single fetch
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String name) {
|
||||||
|
return this.getCard(name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String name0, final boolean fromLatestSet) {
|
||||||
|
// Sometimes they read from decks things like "CardName|Set" - but we
|
||||||
|
// can handle it
|
||||||
|
final PaperCard result = tryGetCard(name0, fromLatestSet);
|
||||||
|
if (null == result) {
|
||||||
|
throw new NoSuchElementException(String.format("Card '%s' not found in our database.", name0));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCardPrintedByDate(final String name0, final boolean fromLatestSet, Date printedBefore ) {
|
||||||
|
// Sometimes they read from decks things like "CardName|Set" - but we
|
||||||
|
// can handle it
|
||||||
|
final PaperCard result = tryGetCard(name0, fromLatestSet);
|
||||||
|
if (null == result) {
|
||||||
|
throw new NoSuchElementException(String.format("Card '%s' released before %s not found in our database.", name0, printedBefore.toString()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advanced fetch by name+set
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String name, final String set) {
|
||||||
|
return this.getCard(name, set, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard getCard(final String name, final String set, final int artIndex) {
|
||||||
|
final PaperCard result = tryGetCard(name, set, artIndex);
|
||||||
|
if (null == result) {
|
||||||
|
final String message = String.format("Asked for '%s' from '%s' #%d: db didn't find that copy.", name, set, artIndex);
|
||||||
|
throw new NoSuchElementException(message);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 0;
|
||||||
|
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 = 0;
|
||||||
|
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, 0));
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
455
forge-core/src/main/java/forge/card/CardEdition.java
Normal file
455
forge-core/src/main/java/forge/card/CardEdition.java
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
* 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.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
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.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 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().tryGetCard(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 = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
493
forge-core/src/main/java/forge/card/CardRules.java
Normal file
493
forge-core/src/main/java/forge/card/CardRules.java
Normal file
@@ -0,0 +1,493 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import java.util.List;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.util.ComparableOp;
|
import forge.util.ComparableOp;
|
||||||
import forge.util.PredicateString;
|
import forge.util.PredicateString;
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ public final class CardRulesPredicates {
|
|||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
|
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
|
||||||
try {
|
try {
|
||||||
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardCoreType.class, what));
|
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardType.CoreType.class, what));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
return com.google.common.base.Predicates.alwaysFalse();
|
return com.google.common.base.Predicates.alwaysFalse();
|
||||||
}
|
}
|
||||||
@@ -185,7 +186,7 @@ public final class CardRulesPredicates {
|
|||||||
* the type
|
* the type
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> coreType(final boolean isEqual, final CardCoreType type) {
|
public static Predicate<CardRules> coreType(final boolean isEqual, final CardType.CoreType type) {
|
||||||
return new PredicateCoreType(type, isEqual);
|
return new PredicateCoreType(type, isEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +201,7 @@ public final class CardRulesPredicates {
|
|||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
|
public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
|
||||||
try {
|
try {
|
||||||
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardSuperType.class, what));
|
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardType.SuperType.class, what));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
return com.google.common.base.Predicates.alwaysFalse();
|
return com.google.common.base.Predicates.alwaysFalse();
|
||||||
}
|
}
|
||||||
@@ -215,7 +216,7 @@ public final class CardRulesPredicates {
|
|||||||
* the type
|
* the type
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> superType(final boolean isEqual, final CardSuperType type) {
|
public static Predicate<CardRules> superType(final boolean isEqual, final CardType.SuperType type) {
|
||||||
return new PredicateSuperType(type, isEqual);
|
return new PredicateSuperType(type, isEqual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ public final class CardRulesPredicates {
|
|||||||
* Checks for color.
|
* Checks for color.
|
||||||
*
|
*
|
||||||
* @param thatColor
|
* @param thatColor
|
||||||
* the that color
|
* color to check
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> hasColor(final byte thatColor) {
|
public static Predicate<CardRules> hasColor(final byte thatColor) {
|
||||||
@@ -235,13 +236,24 @@ public final class CardRulesPredicates {
|
|||||||
* Checks if is color.
|
* Checks if is color.
|
||||||
*
|
*
|
||||||
* @param thatColor
|
* @param thatColor
|
||||||
* the that color
|
* color to check
|
||||||
* @return the predicate
|
* @return the predicate
|
||||||
*/
|
*/
|
||||||
public static Predicate<CardRules> isColor(final byte thatColor) {
|
public static Predicate<CardRules> isColor(final byte thatColor) {
|
||||||
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
|
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if card can be cast with unlimited mana of given color set.
|
||||||
|
*
|
||||||
|
* @param thatColor
|
||||||
|
* color to check
|
||||||
|
* @return the predicate
|
||||||
|
*/
|
||||||
|
public static Predicate<CardRules> canCastWithAvailable(final byte thatColor) {
|
||||||
|
return new LeafColor(LeafColor.ColorOperator.CanCast, thatColor);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if is exactly that color.
|
* Checks if is exactly that color.
|
||||||
*
|
*
|
||||||
@@ -310,7 +322,7 @@ public final class CardRulesPredicates {
|
|||||||
|
|
||||||
private static class LeafColor implements Predicate<CardRules> {
|
private static class LeafColor implements Predicate<CardRules> {
|
||||||
public enum ColorOperator {
|
public enum ColorOperator {
|
||||||
CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals
|
CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals, CanCast
|
||||||
}
|
}
|
||||||
|
|
||||||
private final LeafColor.ColorOperator op;
|
private final LeafColor.ColorOperator op;
|
||||||
@@ -337,6 +349,8 @@ public final class CardRulesPredicates {
|
|||||||
return subject.getColor().hasAllColors(this.color);
|
return subject.getColor().hasAllColors(this.color);
|
||||||
case HasAnyOf:
|
case HasAnyOf:
|
||||||
return subject.getColor().hasAnyColor(this.color);
|
return subject.getColor().hasAnyColor(this.color);
|
||||||
|
case CanCast:
|
||||||
|
return subject.canCastWithAvailable(this.color);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -396,7 +410,7 @@ public final class CardRulesPredicates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class PredicateCoreType implements Predicate<CardRules> {
|
private static class PredicateCoreType implements Predicate<CardRules> {
|
||||||
private final CardCoreType operand;
|
private final CardType.CoreType operand;
|
||||||
private final boolean shouldBeEqual;
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -407,14 +421,14 @@ public final class CardRulesPredicates {
|
|||||||
return this.shouldBeEqual == card.getType().typeContains(this.operand);
|
return this.shouldBeEqual == card.getType().typeContains(this.operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PredicateCoreType(final CardCoreType type, final boolean wantEqual) {
|
public PredicateCoreType(final CardType.CoreType type, final boolean wantEqual) {
|
||||||
this.operand = type;
|
this.operand = type;
|
||||||
this.shouldBeEqual = wantEqual;
|
this.shouldBeEqual = wantEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PredicateSuperType implements Predicate<CardRules> {
|
private static class PredicateSuperType implements Predicate<CardRules> {
|
||||||
private final CardSuperType operand;
|
private final CardType.SuperType operand;
|
||||||
private final boolean shouldBeEqual;
|
private final boolean shouldBeEqual;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -422,7 +436,7 @@ public final class CardRulesPredicates {
|
|||||||
return this.shouldBeEqual == card.getType().superTypeContains(this.operand);
|
return this.shouldBeEqual == card.getType().superTypeContains(this.operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PredicateSuperType(final CardSuperType type, final boolean wantEqual) {
|
public PredicateSuperType(final CardType.SuperType type, final boolean wantEqual) {
|
||||||
this.operand = type;
|
this.operand = type;
|
||||||
this.shouldBeEqual = wantEqual;
|
this.shouldBeEqual = wantEqual;
|
||||||
}
|
}
|
||||||
@@ -448,21 +462,21 @@ public final class CardRulesPredicates {
|
|||||||
|
|
||||||
/** The Constant isCreature. */
|
/** The Constant isCreature. */
|
||||||
public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates
|
public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates
|
||||||
.coreType(true, CardCoreType.Creature);
|
.coreType(true, CardType.CoreType.Creature);
|
||||||
|
|
||||||
public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates
|
public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates
|
||||||
.superType(true, CardSuperType.Legendary);
|
.superType(true, CardType.SuperType.Legendary);
|
||||||
|
|
||||||
/** The Constant isArtifact. */
|
/** The Constant isArtifact. */
|
||||||
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
|
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
|
||||||
.coreType(true, CardCoreType.Artifact);
|
.coreType(true, CardType.CoreType.Artifact);
|
||||||
|
|
||||||
/** The Constant isEquipment. */
|
/** The Constant isEquipment. */
|
||||||
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
|
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
|
||||||
.subType("Equipment");
|
.subType("Equipment");
|
||||||
|
|
||||||
/** The Constant isLand. */
|
/** The Constant isLand. */
|
||||||
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardCoreType.Land);
|
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardType.CoreType.Land);
|
||||||
|
|
||||||
/** The Constant isBasicLand. */
|
/** The Constant isBasicLand. */
|
||||||
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
|
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
|
||||||
@@ -480,31 +494,17 @@ public final class CardRulesPredicates {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The Constant isPlaneswalker. */
|
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
|
||||||
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true,
|
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
|
||||||
CardCoreType.Planeswalker);
|
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardType.CoreType.Sorcery);
|
||||||
|
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardType.CoreType.Enchantment);
|
||||||
/** The Constant isInstant. */
|
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardType.CoreType.Plane);
|
||||||
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardCoreType.Instant);
|
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardType.CoreType.Phenomenon);
|
||||||
|
|
||||||
/** The Constant isSorcery. */
|
|
||||||
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardCoreType.Sorcery);
|
|
||||||
|
|
||||||
/** The Constant isEnchantment. */
|
|
||||||
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardCoreType.Enchantment);
|
|
||||||
|
|
||||||
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardCoreType.Plane);
|
|
||||||
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardCoreType.Phenomenon);
|
|
||||||
public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON);
|
public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON);
|
||||||
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardCoreType.Scheme);
|
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardType.CoreType.Scheme);
|
||||||
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardCoreType.Vanguard);
|
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
||||||
|
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||||
/** The Constant isNonLand. */
|
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
|
||||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardCoreType.Land);
|
|
||||||
|
|
||||||
/** The Constant isNonCreatureSpell. */
|
|
||||||
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(
|
|
||||||
Presets.IS_CREATURE, Presets.IS_LAND));
|
|
||||||
|
|
||||||
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -533,6 +533,7 @@ public final class CardRulesPredicates {
|
|||||||
/** The Constant isMulticolor. */
|
/** The Constant isMulticolor. */
|
||||||
public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2);
|
public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2);
|
||||||
|
|
||||||
|
/** The Constant isMonocolor. */
|
||||||
public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1);
|
public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1);
|
||||||
|
|
||||||
/** The Constant colors. */
|
/** The Constant colors. */
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
389
forge-core/src/main/java/forge/card/CardType.java
Normal file
389
forge-core/src/main/java/forge/card/CardType.java
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
/*
|
||||||
|
* 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 build 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 {
|
||||||
|
|
||||||
|
/** The Artifact. */
|
||||||
|
Artifact,
|
||||||
|
/** The Creature. */
|
||||||
|
Creature,
|
||||||
|
/** The Enchantment. */
|
||||||
|
Enchantment,
|
||||||
|
/** The Instant. */
|
||||||
|
Instant,
|
||||||
|
/** The Land. */
|
||||||
|
Land,
|
||||||
|
/** The Plane. */
|
||||||
|
Plane,
|
||||||
|
/** The Planeswalker. */
|
||||||
|
Planeswalker,
|
||||||
|
/** The Scheme. */
|
||||||
|
Scheme,
|
||||||
|
/** The Sorcery. */
|
||||||
|
Sorcery,
|
||||||
|
/** The Tribal. */
|
||||||
|
Tribal,
|
||||||
|
/** The Vanguard. */
|
||||||
|
Vanguard,
|
||||||
|
/** The Phenomenon. */
|
||||||
|
Phenomenon
|
||||||
|
}
|
||||||
|
|
||||||
|
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,7 +35,7 @@ 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 int orderWeight;
|
||||||
@@ -55,7 +59,6 @@ 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) {
|
||||||
@@ -253,7 +256,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 +284,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];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
34
forge-core/src/main/java/forge/card/ICardDatabase.java
Normal file
34
forge-core/src/main/java/forge/card/ICardDatabase.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package forge.card;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
public interface ICardDatabase extends Iterable<PaperCard> {
|
||||||
|
PaperCard tryGetCard(String cardName);
|
||||||
|
PaperCard tryGetCard(String cardName, boolean fromLastSet);
|
||||||
|
PaperCard tryGetCard(String cardName, String edition);
|
||||||
|
PaperCard tryGetCard(String cardName, String edition, int artIndex);
|
||||||
|
PaperCard tryGetCardPrintedByDate(String name0, boolean fromLatestSet, Date printedBefore);
|
||||||
|
|
||||||
|
PaperCard getCard(String cardName);
|
||||||
|
PaperCard getCard(String cardName, boolean fromLastSet);
|
||||||
|
PaperCard getCard(String cardName, String edition);
|
||||||
|
PaperCard getCard(String cardName, String edition, int artIndex);
|
||||||
|
PaperCard getCardPrintedByDate(String name0, boolean fromLatestSet, Date printedBefore);
|
||||||
|
|
||||||
|
PaperCard getFoiled(PaperCard cpi);
|
||||||
|
|
||||||
|
int getPrintCount(String cardName, String edition);
|
||||||
|
int getMaxPrintCount(String cardName);
|
||||||
|
|
||||||
|
Collection<PaperCard> getUniqueCards();
|
||||||
|
List<PaperCard> getAllCards();
|
||||||
|
List<PaperCard> getAllCards(Predicate<PaperCard> predicate);
|
||||||
|
|
||||||
|
Predicate<? super PaperCard> wasPrintedInSets(List<String> allowedSetCodes);
|
||||||
|
}
|
||||||
111
forge-core/src/main/java/forge/card/MagicColor.java
Normal file
111
forge-core/src/main/java/forge/card/MagicColor.java
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
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 << 1;
|
||||||
|
public static final byte BLUE = 1 << 2;
|
||||||
|
public static final byte BLACK = 1 << 3;
|
||||||
|
public static final byte RED = 1 << 4;
|
||||||
|
public static final byte GREEN = 1 << 5;
|
||||||
|
|
||||||
|
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 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,9 +108,17 @@ public class PrintSheet {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return cardsWithWeights.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<PaperCard> toFlatList() {
|
||||||
|
return cardsWithWeights.toFlatList();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Reader extends StorageReaderFileSections<PrintSheet> {
|
public static class Reader extends StorageReaderFileSections<PrintSheet> {
|
||||||
public Reader(String fileName) {
|
public Reader(File file) {
|
||||||
super(fileName, PrintSheet.FN_GET_KEY);
|
super(file, PrintSheet.FN_GET_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -117,13 +128,4 @@ public class PrintSheet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return cardsWithWeights.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<PaperCard> toFlatList() {
|
|
||||||
return cardsWithWeights.toFlatList();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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.ItemPoolView;
|
||||||
|
|
||||||
|
|
||||||
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, ItemPoolView<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) {
|
||||||
@@ -19,10 +19,9 @@ package forge.card.mana;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.card.ColorSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* CardManaCost class.
|
* CardManaCost class.
|
||||||
@@ -32,7 +31,7 @@ import forge.card.ColorSet;
|
|||||||
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
|
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final class ManaCost implements Comparable<ManaCost> {
|
public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostShard> {
|
||||||
private List<ManaCostShard> shards;
|
private List<ManaCostShard> shards;
|
||||||
private final int genericCost;
|
private final int genericCost;
|
||||||
private final boolean hasNoCost; // lands cost
|
private final boolean hasNoCost; // lands cost
|
||||||
@@ -97,23 +96,20 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
return "no cost";
|
return "no cost";
|
||||||
}
|
}
|
||||||
if (this.shards.isEmpty()) {
|
if (this.shards.isEmpty()) {
|
||||||
return Integer.toString(this.genericCost);
|
return "{" + this.genericCost + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
boolean isFirst = true;
|
|
||||||
if (this.genericCost > 0) {
|
if (this.genericCost > 0) {
|
||||||
sb.append(this.genericCost);
|
sb.append("{" + this.genericCost + "}");
|
||||||
isFirst = false;
|
|
||||||
}
|
}
|
||||||
for (final ManaCostShard s : this.shards) {
|
for (final ManaCostShard s : this.shards) {
|
||||||
if (!isFirst) {
|
if (s == ManaCostShard.X) {
|
||||||
sb.append(' ');
|
sb.insert(0, s.toString());
|
||||||
} else {
|
} else {
|
||||||
isFirst = false;
|
|
||||||
}
|
|
||||||
sb.append(s.toString());
|
sb.append(s.toString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,15 +139,6 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the shards.
|
|
||||||
*
|
|
||||||
* @return the shards
|
|
||||||
*/
|
|
||||||
public List<ManaCostShard> getShards() {
|
|
||||||
return this.shards;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getShardCount(ManaCostShard which) {
|
public int getShardCount(ManaCostShard which) {
|
||||||
if (which == ManaCostShard.COLORLESS) {
|
if (which == ManaCostShard.COLORLESS) {
|
||||||
return genericCost;
|
return genericCost;
|
||||||
@@ -260,13 +247,12 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Can this mana cost be paid with unlimited mana of given color set.
|
* Can this mana cost be paid with unlimited mana of given color set.
|
||||||
* @param color
|
* @param colorCode
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
public boolean canBePaidWithAvaliable(byte colorCode) {
|
||||||
public boolean canBePaidWithAvaliable(ColorSet color) {
|
|
||||||
for (ManaCostShard shard : shards) {
|
for (ManaCostShard shard : shards) {
|
||||||
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(color.getColor())) {
|
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(colorCode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,4 +274,16 @@ public final class ManaCost implements Comparable<ManaCost> {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ManaCostShard> iterator() {
|
||||||
|
return this.shards.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGlyphCount() { // counts all colored shards or 1 for {0} costs
|
||||||
|
int width = shards.size();
|
||||||
|
if (genericCost > 0 || (genericCost == 0 && width == 0)) {
|
||||||
|
width++;
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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)) {
|
||||||
@@ -35,14 +35,14 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
/* Hybrid */
|
/* Hybrid */
|
||||||
WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"),
|
WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"),
|
||||||
WB(ManaAtom.WHITE | ManaAtom.BLACK, "W/B", "WB"),
|
WB(ManaAtom.WHITE | ManaAtom.BLACK, "W/B", "WB"),
|
||||||
WR(ManaAtom.WHITE | ManaAtom.RED, "W/R", "RW"),
|
|
||||||
WG(ManaAtom.WHITE | ManaAtom.GREEN, "W/G", "GW"),
|
|
||||||
UB(ManaAtom.BLUE | ManaAtom.BLACK, "U/B", "UB"),
|
UB(ManaAtom.BLUE | ManaAtom.BLACK, "U/B", "UB"),
|
||||||
UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"),
|
UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"),
|
||||||
UG(ManaAtom.BLUE | ManaAtom.GREEN, "U/G", "GU"),
|
|
||||||
BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"),
|
BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"),
|
||||||
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
|
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
|
||||||
|
RW(ManaAtom.RED | ManaAtom.WHITE, "R/W", "RW"),
|
||||||
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
|
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
|
||||||
|
GW(ManaAtom.GREEN | ManaAtom.WHITE, "G/W", "GW"),
|
||||||
|
GU(ManaAtom.GREEN | ManaAtom.BLUE, "G/U", "GU"),
|
||||||
|
|
||||||
/* Or 2 colorless */
|
/* Or 2 colorless */
|
||||||
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
||||||
@@ -102,15 +102,12 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
this.shard = value;
|
this.shard = value;
|
||||||
this.cmc = this.getCMC();
|
this.cmc = this.getCMC();
|
||||||
this.cmpc = this.getCmpCost();
|
this.cmpc = this.getCmpCost();
|
||||||
this.stringValue = sValue;
|
this.stringValue = "{" + sValue + "}";
|
||||||
this.imageKey = imgKey;
|
this.imageKey = imgKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN;
|
public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN;
|
||||||
|
|
||||||
|
|
||||||
private int getCMC() {
|
private int getCMC() {
|
||||||
if (0 != (this.shard & ManaAtom.IS_X)) {
|
if (0 != (this.shard & ManaAtom.IS_X)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -267,7 +264,6 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
|||||||
|
|
||||||
public boolean isMonoColor() {
|
public boolean isMonoColor() {
|
||||||
return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 1;
|
return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOr2Colorless() {
|
public boolean isOr2Colorless() {
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
package forge.deck;
|
package forge.deck;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -26,10 +25,9 @@ import java.util.NoSuchElementException;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import forge.Card;
|
import forge.StaticData;
|
||||||
import forge.card.CardDb;
|
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.item.ItemPool;
|
import forge.util.ItemPool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deck section.
|
* Deck section.
|
||||||
@@ -54,15 +52,6 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
this.addAll(cards);
|
this.addAll(cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the.
|
|
||||||
*
|
|
||||||
* @param card
|
|
||||||
* the card
|
|
||||||
*/
|
|
||||||
public void add(final Card card) {
|
|
||||||
this.add(CardDb.getCard(card));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the.
|
* Adds the.
|
||||||
@@ -84,9 +73,9 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
* @param amount the amount
|
* @param amount the amount
|
||||||
*/
|
*/
|
||||||
public void add(final String cardName, final String setCode, final int amount) {
|
public void add(final String cardName, final String setCode, final int amount) {
|
||||||
PaperCard cp = CardDb.instance().tryGetCard(cardName, setCode);
|
PaperCard cp = StaticData.instance().getCommonCards().tryGetCard(cardName, setCode);
|
||||||
if ( cp == null )
|
if ( cp == null )
|
||||||
cp = CardDb.variants().tryGetCard(cardName, setCode);
|
cp = StaticData.instance().getVariantCards().tryGetCard(cardName, setCode);
|
||||||
|
|
||||||
if ( cp != null)
|
if ( cp != null)
|
||||||
this.add(cp, amount);
|
this.add(cp, amount);
|
||||||
@@ -94,18 +83,6 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
throw new RuntimeException(String.format("Card %s from %s is not supported by Forge, as it's neither a known common card nor one of casual variants' card.", cardName, setCode ));
|
throw new RuntimeException(String.format("Card %s from %s is not supported by Forge, as it's neither a known common card nor one of casual variants' card.", cardName, setCode ));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the.
|
|
||||||
*
|
|
||||||
* @param cardList
|
|
||||||
* the card list
|
|
||||||
*/
|
|
||||||
public void add(final List<Card> cardList) {
|
|
||||||
for (final Card c : cardList) {
|
|
||||||
this.add(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add all from a List of CardPrinted.
|
* Add all from a List of CardPrinted.
|
||||||
*
|
*
|
||||||
@@ -124,9 +101,9 @@ public class CardPool extends ItemPool<PaperCard> {
|
|||||||
* @param cardName the card name
|
* @param cardName the card name
|
||||||
*/
|
*/
|
||||||
public void add(final String cardName, int cnt) {
|
public void add(final String cardName, int cnt) {
|
||||||
PaperCard cp = CardDb.instance().tryGetCard(cardName);
|
PaperCard cp = StaticData.instance().getCommonCards().tryGetCard(cardName);
|
||||||
if ( cp == null )
|
if ( cp == null )
|
||||||
cp = CardDb.variants().tryGetCard(cardName);
|
cp = StaticData.instance().getVariantCards().tryGetCard(cardName);
|
||||||
|
|
||||||
if ( cp != null)
|
if ( cp != null)
|
||||||
this.add(cp, cnt);
|
this.add(cp, cnt);
|
||||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
@@ -34,12 +35,12 @@ import com.google.common.base.Function;
|
|||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
import forge.deck.io.DeckFileHeader;
|
import forge.deck.io.DeckFileHeader;
|
||||||
import forge.deck.io.DeckSerializer;
|
import forge.deck.io.DeckSerializer;
|
||||||
import forge.gui.deckeditor.tables.TableSorter;
|
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.item.IPaperCard;
|
import forge.item.IPaperCard;
|
||||||
import forge.item.ItemPoolView;
|
|
||||||
import forge.util.FileSection;
|
import forge.util.FileSection;
|
||||||
import forge.util.FileUtil;
|
import forge.util.FileUtil;
|
||||||
|
import forge.util.ItemPoolSorter;
|
||||||
|
import forge.util.ItemPoolView;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,7 +200,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
|||||||
|
|
||||||
private static List<String> writeCardPool(final ItemPoolView<PaperCard> pool) {
|
private static List<String> writeCardPool(final ItemPoolView<PaperCard> pool) {
|
||||||
final List<Entry<PaperCard, Integer>> main2sort = pool.getOrderedList();
|
final List<Entry<PaperCard, Integer>> main2sort = pool.getOrderedList();
|
||||||
Collections.sort(main2sort, TableSorter.BY_NAME_THEN_SET);
|
Collections.sort(main2sort, ItemPoolSorter.BY_NAME_THEN_SET);
|
||||||
final List<String> out = new ArrayList<String>();
|
final List<String> out = new ArrayList<String>();
|
||||||
for (final Entry<PaperCard, Integer> e : main2sort) {
|
for (final Entry<PaperCard, Integer> e : main2sort) {
|
||||||
out.add(serializeSingleCard(e.getKey(), e.getValue()));
|
out.add(serializeSingleCard(e.getKey(), e.getValue()));
|
||||||
@@ -19,11 +19,13 @@ package forge.deck;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import forge.util.IHasName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class DeckBase implements Serializable, Comparable<DeckBase> {
|
public abstract class DeckBase implements Serializable, Comparable<DeckBase>, IHasName {
|
||||||
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
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase> {
|
|||||||
* @param name0 the name0
|
* @param name0 the name0
|
||||||
*/
|
*/
|
||||||
public DeckBase(final String name0) {
|
public DeckBase(final String name0) {
|
||||||
this.name = name0;
|
this.name = name0.replace('/', '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@@ -50,8 +52,8 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase> {
|
|||||||
/** {@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;
|
||||||
@@ -22,15 +22,14 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.apache.commons.lang.math.IntRange;
|
|
||||||
|
|
||||||
import forge.Singletons;
|
import org.apache.commons.lang3.Range;
|
||||||
import forge.card.CardCoreType;
|
|
||||||
import forge.card.CardDb;
|
import forge.StaticData;
|
||||||
|
import forge.card.CardType;
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.item.IPaperCard;
|
import forge.item.IPaperCard;
|
||||||
import forge.properties.ForgePreferences.FPref;
|
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,26 +38,21 @@ import forge.util.Aggregates;
|
|||||||
public enum DeckFormat {
|
public enum DeckFormat {
|
||||||
|
|
||||||
// Main board: allowed size SB: restriction Max distinct non basic cards
|
// Main board: allowed size SB: restriction Max distinct non basic cards
|
||||||
Constructed ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0, 15), 4),
|
Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||||
QuestDeck ( new IntRange(40, Integer.MAX_VALUE), new IntRange(0, 15), 4),
|
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||||
Limited ( new IntRange(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
|
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
|
||||||
Commander ( new IntRange(99), new IntRange(0, 10), 1),
|
Commander ( Range.is(99), Range.between(0, 10), 1),
|
||||||
Vanguard ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0), 4),
|
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
Planechase ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0), 4),
|
Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
Archenemy ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0), 4);
|
Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4);
|
||||||
|
|
||||||
private final IntRange mainRange;
|
private final Range<Integer> mainRange;
|
||||||
private final IntRange sideRange; // null => no check
|
private final Range<Integer> sideRange; // null => no check
|
||||||
private final int maxCardCopies;
|
private final int maxCardCopies;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new game type.
|
DeckFormat(Range<Integer> main, Range<Integer> side, int maxCopies) {
|
||||||
*
|
|
||||||
* @param isLimited
|
|
||||||
* the is limited
|
|
||||||
*/
|
|
||||||
DeckFormat(IntRange main, IntRange side, int maxCopies) {
|
|
||||||
mainRange = main;
|
mainRange = main;
|
||||||
sideRange = side;
|
sideRange = side;
|
||||||
maxCardCopies = maxCopies;
|
maxCardCopies = maxCopies;
|
||||||
@@ -90,7 +84,7 @@ public enum DeckFormat {
|
|||||||
/**
|
/**
|
||||||
* @return the sideRange
|
* @return the sideRange
|
||||||
*/
|
*/
|
||||||
public IntRange getSideRange() {
|
public Range<Integer> getSideRange() {
|
||||||
return sideRange;
|
return sideRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +92,7 @@ public enum DeckFormat {
|
|||||||
/**
|
/**
|
||||||
* @return the mainRange
|
* @return the mainRange
|
||||||
*/
|
*/
|
||||||
public IntRange getMainRange() {
|
public Range<Integer> getMainRange() {
|
||||||
return mainRange;
|
return mainRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,15 +112,10 @@ public enum DeckFormat {
|
|||||||
return "is not selected";
|
return "is not selected";
|
||||||
}
|
}
|
||||||
|
|
||||||
// That's really a bad dependence
|
|
||||||
if (!Singletons.getModel().getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int deckSize = deck.getMain().countAll();
|
int deckSize = deck.getMain().countAll();
|
||||||
|
|
||||||
int min = getMainRange().getMinimumInteger();
|
int min = getMainRange().getMinimum();
|
||||||
int max = getMainRange().getMaximumInteger();
|
int max = getMainRange().getMaximum();
|
||||||
|
|
||||||
if (deckSize < min) {
|
if (deckSize < min) {
|
||||||
return String.format("should have a minimum of %d cards", min);
|
return String.format("should have a minimum of %d cards", min);
|
||||||
@@ -189,7 +178,7 @@ public enum DeckFormat {
|
|||||||
int phenoms = 0;
|
int phenoms = 0;
|
||||||
for (Entry<PaperCard, Integer> cp : planes) {
|
for (Entry<PaperCard, Integer> cp : planes) {
|
||||||
|
|
||||||
if (cp.getKey().getRules().getType().typeContains(CardCoreType.Phenomenon)) {
|
if (cp.getKey().getRules().getType().typeContains(CardType.CoreType.Phenomenon)) {
|
||||||
phenoms++;
|
phenoms++;
|
||||||
}
|
}
|
||||||
if (cp.getValue() > 1) {
|
if (cp.getValue() > 1) {
|
||||||
@@ -233,7 +222,7 @@ public enum DeckFormat {
|
|||||||
// should group all cards by name, so that different editions of same card are really counted as the same card
|
// 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)) {
|
for (Entry<String, Integer> cp : Aggregates.groupSumBy(tmp, PaperCard.FN_GET_NAME)) {
|
||||||
|
|
||||||
IPaperCard simpleCard = CardDb.instance().getCard(cp.getKey());
|
IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
|
||||||
boolean canHaveMultiple = simpleCard.getRules().getType().isBasicLand() || limitExceptions.contains(cp.getKey());
|
boolean canHaveMultiple = simpleCard.getRules().getType().isBasicLand() || limitExceptions.contains(cp.getKey());
|
||||||
|
|
||||||
if (!canHaveMultiple && cp.getValue() > maxCopies) {
|
if (!canHaveMultiple && cp.getValue() > maxCopies) {
|
||||||
@@ -244,11 +233,11 @@ public enum DeckFormat {
|
|||||||
|
|
||||||
// The sideboard must contain either 0 or 15 cards
|
// The sideboard must contain either 0 or 15 cards
|
||||||
int sideboardSize = deck.has(DeckSection.Sideboard) ? deck.get(DeckSection.Sideboard).countAll() : 0;
|
int sideboardSize = deck.has(DeckSection.Sideboard) ? deck.get(DeckSection.Sideboard).countAll() : 0;
|
||||||
IntRange sbRange = getSideRange();
|
Range<Integer> sbRange = getSideRange();
|
||||||
if (sbRange != null && sideboardSize > 0 && !sbRange.containsInteger(sideboardSize)) {
|
if (sbRange != null && sideboardSize > 0 && !sbRange.contains(sideboardSize)) {
|
||||||
return sbRange.getMinimumInteger() == sbRange.getMaximumInteger()
|
return sbRange.getMinimum() == sbRange.getMaximum()
|
||||||
? String.format("must have a sideboard of %d cards or no sideboard at all", sbRange.getMaximumInteger())
|
? 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.getMinimumInteger(), sbRange.getMaximumInteger());
|
: String.format("must have a sideboard of %d to %d cards or no sideboard at all", sbRange.getMinimum(), sbRange.getMaximum());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -17,12 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
package forge.deck;
|
package forge.deck;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
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.ICardDatabase;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,17 +162,17 @@ public class DeckRecognizer {
|
|||||||
private static final Pattern SEARCH_NUMBERS_IN_FRONT = Pattern.compile("([\\d]{1,2})[^A-Za-wyz]*\\s+(.*)");
|
private static final Pattern SEARCH_NUMBERS_IN_FRONT = Pattern.compile("([\\d]{1,2})[^A-Za-wyz]*\\s+(.*)");
|
||||||
//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+(.*)");
|
||||||
|
|
||||||
/**
|
|
||||||
* Recognize line.
|
private final boolean useLastSet;
|
||||||
*
|
private final ICardDatabase db;
|
||||||
* @param rawLine
|
private Date recognizeCardsPrintedBefore = null;
|
||||||
* the raw_line
|
|
||||||
* @param newestEdition
|
public DeckRecognizer(boolean fromLatestSet, CardDb db) {
|
||||||
* get the newest available edition?
|
useLastSet = fromLatestSet;
|
||||||
*
|
this.db = db;
|
||||||
* @return the token
|
}
|
||||||
*/
|
|
||||||
public static Token recognizeLine(final String rawLine, final boolean newestEdition) {
|
public Token recognizeLine(final String rawLine) {
|
||||||
if (StringUtils.isBlank(rawLine)) {
|
if (StringUtils.isBlank(rawLine)) {
|
||||||
return new Token(TokenType.Comment, 0, rawLine);
|
return new Token(TokenType.Comment, 0, rawLine);
|
||||||
}
|
}
|
||||||
@@ -181,7 +184,7 @@ public class DeckRecognizer {
|
|||||||
if (foundNumbersInFront.matches()) {
|
if (foundNumbersInFront.matches()) {
|
||||||
final String cardName = foundNumbersInFront.group(2);
|
final String cardName = foundNumbersInFront.group(2);
|
||||||
final int amount = Integer.parseInt(foundNumbersInFront.group(1));
|
final int amount = Integer.parseInt(foundNumbersInFront.group(1));
|
||||||
result = DeckRecognizer.recognizePossibleNameAndNumber(cardName, amount, newestEdition);
|
result = recognizePossibleNameAndNumber(cardName, amount);
|
||||||
} /*
|
} /*
|
||||||
* else if (foundNumbersInBack.matches()) { String cardName =
|
* else if (foundNumbersInBack.matches()) { String cardName =
|
||||||
* foundNumbersInBack.group(1); int amount =
|
* foundNumbersInBack.group(1); int amount =
|
||||||
@@ -189,17 +192,25 @@ public class DeckRecognizer {
|
|||||||
* Token(cardName, amount); }
|
* Token(cardName, amount); }
|
||||||
*/
|
*/
|
||||||
else {
|
else {
|
||||||
if (null != CardDb.instance().tryGetCard(line)) {
|
PaperCard pc = tryGetCard(line);
|
||||||
return Token.knownCard(CardDb.instance().getCard(line, newestEdition), 1);
|
if (null != pc) {
|
||||||
|
return Token.knownCard(pc, 1);
|
||||||
}
|
}
|
||||||
result = DeckRecognizer.recognizeNonCard(line, 1);
|
result = DeckRecognizer.recognizeNonCard(line, 1);
|
||||||
}
|
}
|
||||||
return result != null ? result : new Token(TokenType.UnknownText, 0, line);
|
return result != null ? result : new Token(TokenType.UnknownText, 0, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Token recognizePossibleNameAndNumber(final String name, final int n, final boolean newestEdition) {
|
private PaperCard tryGetCard(String text) {
|
||||||
if (null != CardDb.instance().tryGetCard(name)) {
|
if(recognizeCardsPrintedBefore != null )
|
||||||
return Token.knownCard(CardDb.instance().getCard(name, newestEdition), n);
|
return db.tryGetCardPrintedByDate(text, useLastSet, recognizeCardsPrintedBefore);
|
||||||
|
return db.tryGetCard(text, useLastSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token recognizePossibleNameAndNumber(final String name, final int n) {
|
||||||
|
PaperCard pc = tryGetCard(name);
|
||||||
|
if (null != pc) {
|
||||||
|
return Token.knownCard(pc, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: recognize format: http://topdeck.ru/forum/index.php?showtopic=12711
|
// TODO: recognize format: http://topdeck.ru/forum/index.php?showtopic=12711
|
||||||
@@ -260,4 +271,16 @@ public class DeckRecognizer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param month
|
||||||
|
* @param year
|
||||||
|
*/
|
||||||
|
public void setDateConstraint(int month, Integer year) {
|
||||||
|
Calendar ca = Calendar.getInstance();
|
||||||
|
ca.set(year, month, 1);
|
||||||
|
recognizeCardsPrintedBefore = ca.getTime();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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.item.PaperCard;
|
||||||
|
import forge.util.ItemPoolView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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 ItemPoolView<PaperCard> 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,121 @@
|
|||||||
|
/*
|
||||||
|
* 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.item.PaperCard;
|
||||||
|
import forge.util.ItemPoolView;
|
||||||
|
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 ItemPoolView<PaperCard> 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,84 @@
|
|||||||
|
/*
|
||||||
|
* 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.item.PaperCard;
|
||||||
|
import forge.util.ItemPoolView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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 ItemPoolView<PaperCard> 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,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.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.item.PaperCard;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
import forge.util.ItemPool;
|
||||||
|
import forge.util.ItemPoolView;
|
||||||
|
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 ItemPool<PaperCard> tDeck = new ItemPool<PaperCard>(PaperCard.class);
|
||||||
|
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 ItemPoolView<PaperCard> 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(cp);
|
||||||
|
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, false));
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
tDeck.add(cardDb.getCard(cp.getName(), basicLandSet), nLand);
|
||||||
|
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(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
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,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.item.PaperCard;
|
||||||
|
import forge.util.ItemPoolView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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 ItemPoolView<PaperCard> 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.
|
||||||
*
|
*
|
||||||
135
forge-core/src/main/java/forge/deck/io/DeckSerializer.java
Normal file
135
forge-core/src/main/java/forge/deck/io/DeckSerializer.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import forge.deck.Deck;
|
||||||
|
import forge.util.FileSection;
|
||||||
|
import forge.util.FileSectionManual;
|
||||||
|
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 DeckSerializer extends StorageReaderFolder<Deck> implements IItemSerializer<Deck> {
|
||||||
|
private final boolean moveWronglyNamedDecks;
|
||||||
|
public static final String FILE_EXTENSION = ".dck";
|
||||||
|
|
||||||
|
public DeckSerializer(final File deckDir0) {
|
||||||
|
this(deckDir0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeckSerializer(final File deckDir0, boolean moveWrongDecks) {
|
||||||
|
super(deckDir0, Deck.FN_NAME_SELECTOR);
|
||||||
|
moveWronglyNamedDecks = moveWrongDecks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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 static void writeDeck(final Deck d, final File f) {
|
||||||
|
FileUtil.writeFile(f, d.save());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(final Deck unit) {
|
||||||
|
FileUtil.writeFile(this.makeFileFor(unit), unit.save());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 = Deck.fromSections(sections, true);
|
||||||
|
|
||||||
|
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 DeckSerializer.DCK_FILE_FILTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeckFileHeader readDeckMetadata(final Map<String, List<String>> map, final boolean canThrow) {
|
||||||
|
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) {
|
||||||
|
if (canThrow) {
|
||||||
|
throw new OldDeckFileFormatException();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (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 DeckSerializer(subfolder, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -17,19 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
package forge.item;
|
package forge.item;
|
||||||
|
|
||||||
|
import forge.util.IHasName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to define a player's inventory may hold. Should include
|
* Interface to define a player's inventory may hold. Should include
|
||||||
* CardPrinted, Booster, Pets, Plants... etc
|
* CardPrinted, Booster, Pets, Plants... etc
|
||||||
*/
|
*/
|
||||||
public interface InventoryItem {
|
public interface InventoryItem extends IHasName
|
||||||
|
{
|
||||||
/**
|
|
||||||
* An inventory item has to provide a name.
|
|
||||||
*
|
|
||||||
* @return the name
|
|
||||||
*/
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return type as a string.
|
* Return type as a string.
|
||||||
*
|
*
|
||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,30 +17,22 @@
|
|||||||
*/
|
*/
|
||||||
package forge.item;
|
package forge.item;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
import forge.Card;
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A viciously lightweight version of a card, for instances
|
* A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade).
|
||||||
* where a full set of meta and rules is not needed.
|
|
||||||
* <br><br>
|
* <br><br>
|
||||||
* The full set of rules is in the CardRules class.
|
* The full set of rules is in the CardRules class.
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id: CardReference.java 9708 2011-08-09 19:34:12Z jendave $
|
|
||||||
*/
|
*/
|
||||||
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
|
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
|
||||||
// Reference to rules
|
// Reference to rules
|
||||||
private final transient CardRules card;
|
private final transient CardRules rules;
|
||||||
|
|
||||||
// These fields are kinda PK for PrintedCard
|
// These fields are kinda PK for PrintedCard
|
||||||
public final String name;
|
public final String name;
|
||||||
@@ -78,7 +70,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CardRules getRules() {
|
public CardRules getRules() {
|
||||||
return this.card;
|
return this.rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,15 +78,11 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
return this.rarity;
|
return this.rarity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public String getImageKey() {
|
// public String getImageKey() {
|
||||||
// return getImageLocator(getImageName(), getArtIndex(), true, false);
|
// return getImageLocator(getImageName(), getArtIndex(), true, false);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getItemType() {
|
public String getItemType() {
|
||||||
return "Card";
|
return "Card";
|
||||||
@@ -106,7 +94,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
|
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
|
||||||
@Override
|
@Override
|
||||||
public CardRules apply(final PaperCard from) {
|
public CardRules apply(final PaperCard from) {
|
||||||
return from.card;
|
return from.rules;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
|
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
|
||||||
@@ -121,7 +109,9 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
|
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
|
||||||
this.card = c;
|
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.name = c.getName();
|
||||||
this.edition = edition0;
|
this.edition = edition0;
|
||||||
this.artIndex = index;
|
this.artIndex = index;
|
||||||
@@ -129,10 +119,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
this.rarity = rare;
|
this.rarity = rare;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PaperCard makeFoiled(final PaperCard c) {
|
|
||||||
return new PaperCard(c.card, c.edition, c.rarity, c.artIndex, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Want this class to be a key for HashTable
|
// Want this class to be a key for HashTable
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
@@ -186,34 +172,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
// return String.format("%s|%s", name, cardSet);
|
// return String.format("%s|%s", name, cardSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* To forge card.
|
|
||||||
*
|
|
||||||
* @return the card
|
|
||||||
*/
|
|
||||||
private static final Map<PaperCard, Card> cp2card = new HashMap<PaperCard, Card>();
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see forge.item.ICardPrinted#getMatchingForgeCard()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Card getMatchingForgeCard() {
|
|
||||||
Card res = cp2card.get(this);
|
|
||||||
if (null == res) {
|
|
||||||
res = toForgeCard(null);
|
|
||||||
cp2card.put(this, res);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see forge.item.ICardPrinted#toForgeCard(forge.game.player.Player)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Card toForgeCard(Player owner) {
|
|
||||||
final Card c = CardFactory.getCard(this, owner);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
@@ -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;
|
||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
142
forge-core/src/main/java/forge/item/PreconDeck.java
Normal file
142
forge-core/src/main/java/forge/item/PreconDeck.java
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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.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";
|
||||||
|
}
|
||||||
|
|
||||||
|
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(Deck.fromSections(sections), set, description);
|
||||||
|
result.imageFilename = imageFilename;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FilenameFilter getFileFilter() {
|
||||||
|
return DeckSerializer.DCK_FILE_FILTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
213
forge-core/src/main/java/forge/item/SealedProduct.java
Normal file
213
forge-core/src/main/java/forge/item/SealedProduct.java
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
result.add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Random rnd = MyRandom.getRandom();
|
||||||
|
for(T item : source) {
|
||||||
|
int next = rnd.nextInt();
|
||||||
|
for(int i = 0; i < count; i++) {
|
||||||
|
if(next < randoms[i]) {
|
||||||
|
randoms[i] = next;
|
||||||
|
result.set(i, item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
result.add(toAdd);
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic
|
public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic
|
||||||
final Map<K, U> uniques = new Hashtable<K, U>();
|
final Map<K, U> uniques = new Hashtable<K, U>();
|
||||||
for (final U c : source) {
|
for (final U c : source) {
|
||||||
@@ -123,7 +132,6 @@ public class Aggregates {
|
|||||||
return uniques.values();
|
return uniques.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
|
public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
|
||||||
if (source == null) { return null; }
|
if (source == null) { return null; }
|
||||||
int max = Integer.MAX_VALUE;
|
int max = Integer.MAX_VALUE;
|
||||||
@@ -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.
|
||||||
@@ -47,6 +45,18 @@ public final class FileUtil {
|
|||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes two paths and combines them into a valid path string
|
||||||
|
* for the current OS.
|
||||||
|
* <p>
|
||||||
|
* Similar to the Path.Combine() function in .Net.
|
||||||
|
*/
|
||||||
|
public static String pathCombine (String path1, String path2) {
|
||||||
|
File file1 = new File(path1);
|
||||||
|
File file2 = new File(file1, path2);
|
||||||
|
return file2.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* doesFileExist.
|
* doesFileExist.
|
||||||
@@ -97,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) {
|
||||||
@@ -133,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()
|
||||||
@@ -168,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;
|
||||||
9
forge-core/src/main/java/forge/util/IHasName.java
Normal file
9
forge-core/src/main/java/forge/util/IHasName.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package forge.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface IHasName {
|
||||||
|
String getName();
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.util;
|
package forge.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +42,8 @@ public interface IItemReader<T> {
|
|||||||
* @return the item key
|
* @return the item key
|
||||||
*/
|
*/
|
||||||
String getItemKey(T item);
|
String getItemKey(T item);
|
||||||
|
|
||||||
|
Iterable<File> getSubFolders();
|
||||||
|
|
||||||
|
IItemReader<T> getReaderForFolder(File subfolder);
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package forge.util;
|
package forge.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
@@ -37,4 +39,7 @@ public interface IItemSerializer<T> extends IItemReader<T> {
|
|||||||
* @param unit the unit
|
* @param unit the unit
|
||||||
*/
|
*/
|
||||||
void erase(T unit);
|
void erase(T unit);
|
||||||
|
|
||||||
|
|
||||||
|
File getDirectory();
|
||||||
}
|
}
|
||||||
233
forge-core/src/main/java/forge/util/ItemPool.java
Normal file
233
forge-core/src/main/java/forge/util/ItemPool.java
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* 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.Collections;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
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> extends ItemPoolView<T> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Constructors here
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ItemPool Constructor.
|
||||||
|
*
|
||||||
|
* @param cls
|
||||||
|
* a T
|
||||||
|
*/
|
||||||
|
public ItemPool(final Class<T> cls) {
|
||||||
|
super(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final ItemPoolView<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.put((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.put((Tout) srcKey, Integer.valueOf(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Get item view.
|
||||||
|
*
|
||||||
|
* @return a ItemPoolView
|
||||||
|
*/
|
||||||
|
public ItemPoolView<T> getView() {
|
||||||
|
return new ItemPoolView<T>(Collections.unmodifiableMap(this.getItems()), 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.getItems().put(item, Integer.valueOf(this.count(item) + amount));
|
||||||
|
this.isListInSync = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void put(final T item, final int amount) {
|
||||||
|
this.getItems().put(item, amount);
|
||||||
|
this.isListInSync = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isListInSync = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isListInSync = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.getItems().remove(item);
|
||||||
|
} else {
|
||||||
|
this.getItems().put(item, count - amount);
|
||||||
|
}
|
||||||
|
this.isListInSync = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAll(final T item) {
|
||||||
|
return this.getItems().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.getItems().clear();
|
||||||
|
this.isListInSync = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
forge-core/src/main/java/forge/util/ItemPoolSorter.java
Normal file
87
forge-core/src/main/java/forge/util/ItemPoolSorter.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.Comparator;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* TableSorter class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the generic type
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: TableSorter.java 21966 2013-06-05 06:58:32Z Max mtg $
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
// Comparable needs <type>
|
||||||
|
public class ItemPoolSorter<T> implements Comparator<Entry<T, Integer>> {
|
||||||
|
private final boolean ascending;
|
||||||
|
private final Function<Entry<T, Integer>, Comparable<?>> field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructor for TableSorter.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* the field
|
||||||
|
* @param inAscending
|
||||||
|
* a boolean.
|
||||||
|
*/
|
||||||
|
public ItemPoolSorter(final Function<Entry<T, Integer>, Comparable<?>> field, final boolean inAscending) {
|
||||||
|
this.field = field;
|
||||||
|
this.ascending = inAscending;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The Constant byNameThenSet. */
|
||||||
|
public static final ItemPoolSorter<PaperCard> BY_NAME_THEN_SET = new ItemPoolSorter<PaperCard>(
|
||||||
|
new Function<Entry<PaperCard, Integer>, Comparable<?>>() {
|
||||||
|
@Override
|
||||||
|
public Comparable<?> apply(final Entry<PaperCard, Integer> from) {
|
||||||
|
return from.getKey();
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Override
|
||||||
|
public final int compare(final Entry<T, Integer> arg0, final Entry<T, Integer> arg1) {
|
||||||
|
final Comparable obj1 = this.field.apply(arg0);
|
||||||
|
final Comparable obj2 = this.field.apply(arg1);
|
||||||
|
if (obj1 == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (obj2 == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//System.out.println(String.format("%s vs %s _______ %s vs %s", arg0, arg1, obj1, obj2));
|
||||||
|
return this.ascending ? obj1.compareTo(obj2) : obj2.compareTo(obj1);
|
||||||
|
}
|
||||||
|
}
|
||||||
256
forge-core/src/main/java/forge/util/ItemPoolView.java
Normal file
256
forge-core/src/main/java/forge/util/ItemPoolView.java
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* 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.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>
|
||||||
|
* ItemPoolView class.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* an InventoryItem
|
||||||
|
* @author Forge
|
||||||
|
* @version $Id: ItemPoolView.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
|
*/
|
||||||
|
public class ItemPoolView<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
|
||||||
|
public ItemPoolView(final Class<T> cls) {
|
||||||
|
this(new Hashtable<T, Integer>(), cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemPoolView(final Map<T, Integer> inMap, final Class<T> cls) {
|
||||||
|
this.items = inMap;
|
||||||
|
this.myClass = cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data members
|
||||||
|
/** The items. */
|
||||||
|
private final Map<T, Integer> items;
|
||||||
|
|
||||||
|
/** The my class. */
|
||||||
|
private final Class<T> myClass; // class does not keep this in runtime by
|
||||||
|
// itself
|
||||||
|
|
||||||
|
// same thing as above, it was copied to provide sorting (needed by table
|
||||||
|
// views in deck editors)
|
||||||
|
/** The items ordered. */
|
||||||
|
private final transient List<Entry<T, Integer>> itemsOrdered = new ArrayList<Map.Entry<T, Integer>>();
|
||||||
|
|
||||||
|
/** Whether list is in sync. */
|
||||||
|
protected transient boolean isListInSync = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* getOrderedList.
|
||||||
|
*
|
||||||
|
* @return List<Entry<T, Integer>>
|
||||||
|
*/
|
||||||
|
public final List<Entry<T, Integer>> getOrderedList() {
|
||||||
|
if (!this.isListInSync) {
|
||||||
|
this.rebuildOrderedList();
|
||||||
|
}
|
||||||
|
return this.itemsOrdered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildOrderedList() {
|
||||||
|
this.itemsOrdered.clear();
|
||||||
|
if (this.items != null) {
|
||||||
|
for (final Entry<T, Integer> e : this.items.entrySet()) {
|
||||||
|
this.itemsOrdered.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isListInSync = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 items.
|
||||||
|
*
|
||||||
|
* @return the items
|
||||||
|
*/
|
||||||
|
protected Map<T, Integer> getItems() {
|
||||||
|
return this.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the my class.
|
||||||
|
*
|
||||||
|
* @return the myClass
|
||||||
|
*/
|
||||||
|
public Class<T> getMyClass() {
|
||||||
|
return this.myClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To item list string.
|
||||||
|
*
|
||||||
|
* @return the iterable
|
||||||
|
*/
|
||||||
|
public Iterable<String> toItemListString() {
|
||||||
|
final List<String> list = new ArrayList<String>();
|
||||||
|
for (final Entry<T, Integer> e : this.items.entrySet()) {
|
||||||
|
list.add(String.format("%d x %s", e.getValue(), e.getKey().getName()));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
141
forge-core/src/main/java/forge/util/Lang.java
Normal file
141
forge-core/src/main/java/forge/util/Lang.java
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package forge.util;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Lang {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this method.
|
||||||
|
* @param position
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getOrdinal(int position) {
|
||||||
|
String[] sufixes = new String[] { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
|
||||||
|
switch (position % 100) {
|
||||||
|
case 11:
|
||||||
|
case 12:
|
||||||
|
case 13:
|
||||||
|
return position + "th";
|
||||||
|
default:
|
||||||
|
return position + sufixes[position % 10];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String joinHomogenous(String s1, String s2) {
|
||||||
|
boolean has1 = StringUtils.isNotBlank(s1);
|
||||||
|
boolean has2 = StringUtils.isNotBlank(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(Collection<T> objects) { return joinHomogenous(objects, null, "and"); }
|
||||||
|
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor) {
|
||||||
|
return joinHomogenous(objects, accessor, "and");
|
||||||
|
}
|
||||||
|
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor, String lastUnion) {
|
||||||
|
int remaining = objects.size();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (T obj : objects) {
|
||||||
|
remaining--;
|
||||||
|
if (accessor != null) {
|
||||||
|
sb.append(accessor.apply(obj));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sb.append(obj);
|
||||||
|
}
|
||||||
|
if (remaining > 1) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
else if (remaining == 1) {
|
||||||
|
sb.append(" ").append(lastUnion).append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String joinVerb(List<T> subjects, String verb) {
|
||||||
|
return subjects.size() > 1 || !subjectIsSingle3rdPerson(Iterables.getFirst(subjects, "it").toString()) ? verb : verbs3rdPersonSingular(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String joinVerb(String subject, String verb) {
|
||||||
|
return !Lang.subjectIsSingle3rdPerson(subject) ? verb : verbs3rdPersonSingular(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean subjectIsSingle3rdPerson(String subject) {
|
||||||
|
// Will be most simple
|
||||||
|
return !"You".equalsIgnoreCase(subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String verbs3rdPersonSingular(String verb) {
|
||||||
|
// English is simple - just add (s) for multiple objects.
|
||||||
|
return verb + "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPlural(String noun) {
|
||||||
|
return noun + (noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String nounWithAmount(int cnt, String noun) {
|
||||||
|
String countedForm = cnt == 1 ? noun : getPlural(noun);
|
||||||
|
final String strCount;
|
||||||
|
if (cnt == 1) {
|
||||||
|
strCount = startsWithVowel(noun) ? "an " : "a ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strCount = String.valueOf(cnt) + " ";
|
||||||
|
}
|
||||||
|
return strCount + countedForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String nounWithNumeral(int cnt, String noun) {
|
||||||
|
String countedForm = cnt <= 1 ? noun : getPlural(noun);
|
||||||
|
return getNumeral(cnt) + " " + countedForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPossesive(String name) {
|
||||||
|
if ("You".equalsIgnoreCase(name)) return name + "r"; // to get "your"
|
||||||
|
return name.endsWith("s") ? name + "'" : name + "'s";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean startsWithVowel(String word) {
|
||||||
|
return isVowel(word.trim().charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final char[] vowels = { 'a', 'i', 'e', 'o', 'u' };
|
||||||
|
public static boolean isVowel(char letter) {
|
||||||
|
char l = Character.toLowerCase(letter);
|
||||||
|
for (char c : vowels) {
|
||||||
|
if (c == l) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static String[] numbers0 = new String[] {
|
||||||
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
"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 static String getNumeral(int n) {
|
||||||
|
String prefix = n < 0 ? "minus " : "";
|
||||||
|
n = Math.abs(n);
|
||||||
|
if (n >= 0 && n < 20)
|
||||||
|
return prefix + numbers0[n];
|
||||||
|
if (n < 100) {
|
||||||
|
int n1 = n % 10;
|
||||||
|
String ones = n1 == 0 ? "" : numbers0[n1];
|
||||||
|
return prefix + numbers20[(n / 10) - 2] + " " + ones;
|
||||||
|
}
|
||||||
|
return Integer.toString(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.util;
|
package forge.util;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -78,13 +79,13 @@ public class TextUtil {
|
|||||||
int len = input.length();
|
int len = input.length();
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
for (int iC = 0; iC < len; iC++ ) {
|
for (int iC = 0; iC < len; iC++) {
|
||||||
char c = input.charAt(iC);
|
char c = input.charAt(iC);
|
||||||
if( closePar > 0 && c == closePar && nPar > 0 ) { nPar--; }
|
if (closePar > 0 && c == closePar && nPar > 0) { nPar--; }
|
||||||
else if( openPar > 0 && c == openPar ) nPar++;
|
else if (openPar > 0 && c == openPar) nPar++;
|
||||||
|
|
||||||
if( c == delimiter && nPar == 0 && idx < maxEntries) {
|
if (c == delimiter && nPar == 0 && idx < maxEntries) {
|
||||||
if( iC > start || !skipEmpty ) {
|
if (iC > start || !skipEmpty) {
|
||||||
result.add(input.subSequence(start, iC).toString());
|
result.add(input.subSequence(start, iC).toString());
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
@@ -92,13 +93,24 @@ public class TextUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( len > start || !skipEmpty )
|
if (len > start || !skipEmpty)
|
||||||
result.add(input.subSequence(start, len).toString());
|
result.add(input.subSequence(start, len).toString());
|
||||||
|
|
||||||
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
||||||
return trimParenthesis ? StringUtils.stripAll(toReturn, String.valueOf(openPar)) : toReturn;
|
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
|
* Converts an enum value to a printable label but upcasing the first letter
|
||||||
* and lcasing all subsequent letters
|
* and lcasing all subsequent letters
|
||||||
@@ -111,12 +123,20 @@ public class TextUtil {
|
|||||||
public static String buildFourColumnList(String firstLine, Iterable<PaperCard> cAnteRemoved) {
|
public static String buildFourColumnList(String firstLine, Iterable<PaperCard> cAnteRemoved) {
|
||||||
StringBuilder sb = new StringBuilder(firstLine);
|
StringBuilder sb = new StringBuilder(firstLine);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(PaperCard cp: cAnteRemoved) {
|
for (PaperCard cp: cAnteRemoved) {
|
||||||
if ( i != 0 ) sb.append(", ");
|
if (i != 0) { sb.append(", "); }
|
||||||
if ( i % 4 == 0 ) sb.append("\n");
|
if (i % 4 == 0) { sb.append("\n"); }
|
||||||
sb.append(cp);
|
sb.append(cp);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return sb.toString();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
8
forge-core/src/main/java/forge/util/package-info.java
Normal file
8
forge-core/src/main/java/forge/util/package-info.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Max
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package forge.util;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user