mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Compare commits
1620 Commits
forge-1.4.
...
forge-1.5.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<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"/>
|
||||
|
||||
30618
.gitattributes
vendored
30618
.gitattributes
vendored
File diff suppressed because it is too large
Load Diff
26
.gitignore
vendored
26
.gitignore
vendored
@@ -2,20 +2,28 @@
|
||||
/*.iml
|
||||
/*.tmp
|
||||
/.metadata
|
||||
forge-ai/forge-ai.iml
|
||||
forge-ai/target
|
||||
forge-core/forge-core.iml
|
||||
forge-core/target
|
||||
forge-game/target
|
||||
forge-gui/forge-gui.iml
|
||||
forge-gui/forge.profile.properties
|
||||
forge-gui/res/*.log
|
||||
forge-gui/res/PerSetTrackingResults
|
||||
forge-gui/res/cardsfolder/*.bat
|
||||
forge-gui/res/decks
|
||||
forge-gui/res/layouts
|
||||
forge-gui/res/pics*
|
||||
forge-gui/res/pics_product
|
||||
forge-gui/target
|
||||
forge-gui/tools/PerSetTrackingResults
|
||||
forge-gui/tools/oracleScript.log
|
||||
/forge.profile.properties
|
||||
/nbactions.xml
|
||||
/pom.xml.next
|
||||
/pom.xml.releaseBackup
|
||||
/pom.xml.tag
|
||||
/release.properties
|
||||
res/*.log
|
||||
res/PerSetTrackingResults
|
||||
res/cardsfolder/*.bat
|
||||
res/decks
|
||||
res/layouts
|
||||
res/pics*
|
||||
res/pics_product
|
||||
/target
|
||||
/test-output
|
||||
tools/PerSetTrackingResults
|
||||
tools/oracleScript.log
|
||||
|
||||
29
.project
29
.project
@@ -4,36 +4,7 @@
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#Wed Jul 27 18:40:11 EDT 2011
|
||||
eclipse.preferences.version=1
|
||||
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.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.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
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.alignment_for_arguments_in_allocation_expression=16
|
||||
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
|
||||
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.6</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,11 @@ package forge.game.ai;
|
||||
* from the text file.
|
||||
*/
|
||||
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"); /** */
|
||||
|
||||
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.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>forge-core</artifactId>
|
||||
<name>Forge Core</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>14.0.1</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.Lists;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.StaticData;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PrintSheet;
|
||||
import forge.item.SealedProduct;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
/**
|
||||
@@ -50,26 +50,15 @@ import forge.util.TextUtil;
|
||||
*/
|
||||
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 static final synchronized PrintSheet getPrintSheet(String 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);
|
||||
}
|
||||
|
||||
public static final List<PaperCard> getBoosterPack(SealedProductTemplate booster) {
|
||||
public static final List<PaperCard> getBoosterPack(SealedProduct.Template booster) {
|
||||
List<PaperCard> result = new ArrayList<PaperCard>();
|
||||
for(Pair<String, Integer> slot : booster.getSlots()) {
|
||||
String slotType = slot.getLeft(); // add expansion symbol here?
|
||||
@@ -77,7 +66,7 @@ public class BoosterGenerator {
|
||||
|
||||
String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
|
||||
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);
|
||||
result.addAll(ps.random(numCards, true));
|
||||
@@ -100,7 +89,7 @@ public class BoosterGenerator {
|
||||
String mainCode = itMod.next();
|
||||
if ( mainCode.regionMatches(true, 0, "fromSheet", 0, 9)) { // custom print sheet
|
||||
String sheetName = StringUtils.strip(mainCode.substring(9), "()\" ");
|
||||
src = Singletons.getModel().getPrintSheets().get(sheetName).toFlatList();
|
||||
src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||
setPred = Predicates.alwaysTrue();
|
||||
|
||||
} 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, ',', '"', '"');
|
||||
List<PaperCard> srcList = new ArrayList<PaperCard>();
|
||||
for(String cardName: cardNames)
|
||||
srcList.add(CardDb.instance().getCard(cardName));
|
||||
srcList.add(StaticData.instance().getCommonCards().getCard(cardName));
|
||||
src = srcList;
|
||||
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
|
||||
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);
|
||||
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);
|
||||
ps.addAll(Iterables.filter(src, predicateRares));
|
||||
|
||||
Predicate<PaperCard> predicateUncommon = Predicates.and( setPred, IPaperCard.Predicates.Presets.IS_UNCOMMON, extraPred);
|
||||
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.
|
||||
// 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;
|
||||
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(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(MYTHIC)) { toAdd = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE;
|
||||
} else if ( operator.equalsIgnoreCase(RARE)) { toAdd = IPaperCard.Predicates.Presets.IS_RARE;
|
||||
} else if ( operator.equalsIgnoreCase(UNCOMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_UNCOMMON;
|
||||
} else if ( operator.equalsIgnoreCase(COMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_COMMON;
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.LAND) ) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES);
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND;
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.TIME_SHIFTED)) { toAdd = IPaperCard.Predicates.Presets.IS_SPECIAL;
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.MYTHIC)) { toAdd = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE;
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.RARE)) { toAdd = IPaperCard.Predicates.Presets.IS_RARE;
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.UNCOMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_UNCOMMON;
|
||||
} else if ( operator.equalsIgnoreCase(BoosterSlots.COMMON)) { toAdd = IPaperCard.Predicates.Presets.IS_COMMON;
|
||||
} else if ( operator.startsWith("name(") ) {
|
||||
operator = StringUtils.strip(operator.substring(4), "() ");
|
||||
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.
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,13 @@ import forge.card.mana.ManaCost;
|
||||
*/
|
||||
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>());
|
||||
|
||||
478
forge-core/src/main/java/forge/card/CardRules.java
Normal file
478
forge-core/src/main/java/forge/card/CardRules.java
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* 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 = null;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public final class CardRulesPredicates {
|
||||
*/
|
||||
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
|
||||
try {
|
||||
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardCoreType.class, what));
|
||||
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardType.CoreType.class, what));
|
||||
} catch (final Exception e) {
|
||||
return com.google.common.base.Predicates.alwaysFalse();
|
||||
}
|
||||
@@ -185,7 +185,7 @@ public final class CardRulesPredicates {
|
||||
* the type
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public final class CardRulesPredicates {
|
||||
*/
|
||||
public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
|
||||
try {
|
||||
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardSuperType.class, what));
|
||||
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardType.SuperType.class, what));
|
||||
} catch (final Exception e) {
|
||||
return com.google.common.base.Predicates.alwaysFalse();
|
||||
}
|
||||
@@ -215,7 +215,7 @@ public final class CardRulesPredicates {
|
||||
* the type
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ public final class CardRulesPredicates {
|
||||
}
|
||||
|
||||
private static class PredicateCoreType implements Predicate<CardRules> {
|
||||
private final CardCoreType operand;
|
||||
private final CardType.CoreType operand;
|
||||
private final boolean shouldBeEqual;
|
||||
|
||||
@Override
|
||||
@@ -407,14 +407,14 @@ public final class CardRulesPredicates {
|
||||
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.shouldBeEqual = wantEqual;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PredicateSuperType implements Predicate<CardRules> {
|
||||
private final CardSuperType operand;
|
||||
private final CardType.SuperType operand;
|
||||
private final boolean shouldBeEqual;
|
||||
|
||||
@Override
|
||||
@@ -422,7 +422,7 @@ public final class CardRulesPredicates {
|
||||
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.shouldBeEqual = wantEqual;
|
||||
}
|
||||
@@ -448,21 +448,21 @@ public final class CardRulesPredicates {
|
||||
|
||||
/** The Constant isCreature. */
|
||||
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
|
||||
.superType(true, CardSuperType.Legendary);
|
||||
.superType(true, CardType.SuperType.Legendary);
|
||||
|
||||
/** The Constant isArtifact. */
|
||||
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
|
||||
.coreType(true, CardCoreType.Artifact);
|
||||
.coreType(true, CardType.CoreType.Artifact);
|
||||
|
||||
/** The Constant isEquipment. */
|
||||
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
|
||||
.subType("Equipment");
|
||||
|
||||
/** 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. */
|
||||
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
|
||||
@@ -480,31 +480,17 @@ public final class CardRulesPredicates {
|
||||
}
|
||||
};
|
||||
|
||||
/** The Constant isPlaneswalker. */
|
||||
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true,
|
||||
CardCoreType.Planeswalker);
|
||||
|
||||
/** The Constant isInstant. */
|
||||
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardCoreType.Instant);
|
||||
|
||||
/** 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_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
|
||||
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
|
||||
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);
|
||||
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardType.CoreType.Plane);
|
||||
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardType.CoreType.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_VANGUARD = CardRulesPredicates.coreType(true, CardCoreType.Vanguard);
|
||||
|
||||
/** The Constant isNonLand. */
|
||||
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));
|
||||
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardType.CoreType.Scheme);
|
||||
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);
|
||||
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. **/
|
||||
@SuppressWarnings("unchecked")
|
||||
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,6 @@
|
||||
*/
|
||||
package forge.card;
|
||||
|
||||
import forge.Constant;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.util.BinaryUtil;
|
||||
|
||||
@@ -253,7 +252,7 @@ public final class ColorSet implements Comparable<ColorSet> {
|
||||
return "n/a";
|
||||
}
|
||||
String toReturn = MagicColor.toLongString(myColor);
|
||||
if (toReturn == Constant.Color.COLORLESS && myColor != 0) {
|
||||
if (toReturn == MagicColor.Constant.COLORLESS && myColor != 0) {
|
||||
return "multi";
|
||||
}
|
||||
return toReturn;
|
||||
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.Map.Entry;
|
||||
import java.util.ArrayList;
|
||||
@@ -7,7 +8,9 @@ import java.util.Collection;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.util.ItemPool;
|
||||
import forge.deck.CardPool;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.storage.StorageReaderFileSections;
|
||||
|
||||
@@ -30,7 +33,7 @@ public class PrintSheet {
|
||||
this(name0, null);
|
||||
}
|
||||
|
||||
private PrintSheet(String name0, ItemPool<PaperCard> pool) {
|
||||
public PrintSheet(String name0, ItemPool<PaperCard> pool) {
|
||||
name = name0;
|
||||
cardsWithWeights = pool != null ? pool : new ItemPool<PaperCard>(PaperCard.class);
|
||||
}
|
||||
@@ -105,18 +108,6 @@ public class PrintSheet {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Reader extends StorageReaderFileSections<PrintSheet> {
|
||||
public Reader(String fileName) {
|
||||
super(fileName, PrintSheet.FN_GET_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrintSheet read(String title, Iterable<String> body, int idx) {
|
||||
return new PrintSheet(title, CardPool.fromCardList(body));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return cardsWithWeights.isEmpty();
|
||||
}
|
||||
@@ -125,5 +116,16 @@ public class PrintSheet {
|
||||
return cardsWithWeights.toFlatList();
|
||||
}
|
||||
|
||||
public static class Reader extends StorageReaderFileSections<PrintSheet> {
|
||||
public Reader(File file) {
|
||||
super(file, PrintSheet.FN_GET_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrintSheet read(String title, Iterable<String> body, int idx) {
|
||||
return new PrintSheet(title, CardPool.fromCardList(body));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,13 +10,15 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.ItemPoolView;
|
||||
import forge.item.PrintSheet;
|
||||
import forge.item.SealedProduct;
|
||||
import forge.util.ItemPoolView;
|
||||
|
||||
|
||||
public class UnOpenedProduct implements IUnOpenedProduct {
|
||||
|
||||
private final SealedProductTemplate tpl;
|
||||
private final SealedProduct.Template tpl;
|
||||
private final Map<String, PrintSheet> sheets;
|
||||
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)
|
||||
public UnOpenedProduct(SealedProductTemplate template) {
|
||||
public UnOpenedProduct(SealedProduct.Template template) {
|
||||
tpl = template;
|
||||
sheets = null;
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
public UnOpenedProduct(SealedProductTemplate template, Iterable<PaperCard> cards) {
|
||||
public UnOpenedProduct(SealedProduct.Template template, Iterable<PaperCard> cards) {
|
||||
tpl = template;
|
||||
sheets = new TreeMap<String, PrintSheet>();
|
||||
prebuildSheets(cards);
|
||||
}
|
||||
|
||||
public UnOpenedProduct(SealedProductTemplate sealedProductTemplate, Predicate<PaperCard> filterPrinted) {
|
||||
this(sealedProductTemplate, Iterables.filter(CardDb.instance().getAllCards(), filterPrinted));
|
||||
public UnOpenedProduct(SealedProduct.Template sealedProductTemplate, Predicate<PaperCard> filterPrinted) {
|
||||
this(sealedProductTemplate, Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), filterPrinted));
|
||||
}
|
||||
|
||||
private void prebuildSheets(Iterable<PaperCard> sourceList) {
|
||||
@@ -49,7 +49,7 @@ public final class ManaCost implements Comparable<ManaCost> {
|
||||
public static final ManaCost FOUR = new ManaCost(4);
|
||||
|
||||
public static ManaCost get(int cntColorless) {
|
||||
switch (cntColorless) {
|
||||
switch (cntColorless) {
|
||||
case 0: return ZERO;
|
||||
case 1: return ONE;
|
||||
case 2: return TWO;
|
||||
@@ -58,14 +58,14 @@ public final class ManaCost implements Comparable<ManaCost> {
|
||||
}
|
||||
return cntColorless > 0 ? new ManaCost(cntColorless) : NO_COST;
|
||||
}
|
||||
|
||||
|
||||
// pass mana cost parser here
|
||||
private ManaCost(int cmc) {
|
||||
this.hasNoCost = cmc < 0;
|
||||
this.genericCost = cmc < 0 ? 0 : cmc;
|
||||
sealClass(new ArrayList<ManaCostShard>());
|
||||
}
|
||||
|
||||
|
||||
private void sealClass(List<ManaCostShard> shards0) {
|
||||
this.shards = Collections.unmodifiableList(shards0);
|
||||
this.stringValue = this.getSimpleString();
|
||||
@@ -97,22 +97,19 @@ public final class ManaCost implements Comparable<ManaCost> {
|
||||
return "no cost";
|
||||
}
|
||||
if (this.shards.isEmpty()) {
|
||||
return Integer.toString(this.genericCost);
|
||||
return "{" + this.genericCost + "}";
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
if (this.genericCost > 0) {
|
||||
sb.append(this.genericCost);
|
||||
isFirst = false;
|
||||
sb.append("{" + this.genericCost + "}");
|
||||
}
|
||||
for (final ManaCostShard s : this.shards) {
|
||||
if (!isFirst) {
|
||||
sb.append(' ');
|
||||
if (s == ManaCostShard.X) {
|
||||
sb.insert(0, s.toString());
|
||||
} else {
|
||||
isFirst = false;
|
||||
sb.append(s.toString());
|
||||
}
|
||||
sb.append(s.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -196,7 +193,7 @@ public final class ManaCost implements Comparable<ManaCost> {
|
||||
public boolean isZero() {
|
||||
return genericCost == 0 && isPureGeneric();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -287,5 +284,4 @@ public final class ManaCost implements Comparable<ManaCost> {
|
||||
res.sealClass(sh);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,17 @@ public class ManaCostParser implements IParserManaCost {
|
||||
private final String[] cost;
|
||||
private int nextToken;
|
||||
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.
|
||||
@@ -54,7 +65,6 @@ public class ManaCostParser implements IParserManaCost {
|
||||
*/
|
||||
@Override
|
||||
public final ManaCostShard next() {
|
||||
|
||||
final String unparsed = this.cost[this.nextToken++];
|
||||
// System.out.println(unparsed);
|
||||
if (StringUtils.isNumeric(unparsed)) {
|
||||
@@ -23,7 +23,7 @@ import forge.util.BinaryUtil;
|
||||
* The Class CardManaCostShard.
|
||||
*/
|
||||
public enum ManaCostShard implements Comparable<ManaCostShard> {
|
||||
// declaration order matters! Place the shards that offer least ways to be paid for first
|
||||
// declaration order matters! Place the shards that offer least ways to be paid for first
|
||||
|
||||
/* Pure colors */
|
||||
WHITE(ManaAtom.WHITE, "W"),
|
||||
@@ -35,14 +35,14 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
||||
/* Hybrid */
|
||||
WU(ManaAtom.WHITE | ManaAtom.BLUE, "W/U", "WU"),
|
||||
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"),
|
||||
UR(ManaAtom.BLUE | ManaAtom.RED, "U/R", "UR"),
|
||||
UG(ManaAtom.BLUE | ManaAtom.GREEN, "U/G", "GU"),
|
||||
BR(ManaAtom.BLACK | ManaAtom.RED, "B/R", "BR"),
|
||||
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
|
||||
RW(ManaAtom.RED | ManaAtom.WHITE, "R/W", "RW"),
|
||||
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 */
|
||||
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
||||
@@ -55,7 +55,7 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
||||
S(ManaAtom.IS_SNOW, "S"),
|
||||
COLORLESS(ManaAtom.COLORLESS, "1"),
|
||||
|
||||
/* Phyrexian */
|
||||
/* Phyrexian */
|
||||
PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"),
|
||||
PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"),
|
||||
PB(ManaAtom.BLACK | ManaAtom.OR_2_LIFE, "B/P", "PB"),
|
||||
@@ -102,15 +102,12 @@ public enum ManaCostShard implements Comparable<ManaCostShard> {
|
||||
this.shard = value;
|
||||
this.cmc = this.getCMC();
|
||||
this.cmpc = this.getCmpCost();
|
||||
this.stringValue = sValue;
|
||||
this.stringValue = "{" + sValue + "}";
|
||||
this.imageKey = imgKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final int COLORS_SUPERPOSITION = ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN;
|
||||
|
||||
|
||||
private int getCMC() {
|
||||
if (0 != (this.shard & ManaAtom.IS_X)) {
|
||||
return 0;
|
||||
@@ -18,7 +18,6 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -26,10 +25,9 @@ import java.util.NoSuchElementException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.CardDb;
|
||||
import forge.StaticData;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.ItemPool;
|
||||
import forge.util.ItemPool;
|
||||
|
||||
/**
|
||||
* Deck section.
|
||||
@@ -54,15 +52,6 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
this.addAll(cards);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the.
|
||||
*
|
||||
* @param card
|
||||
* the card
|
||||
*/
|
||||
public void add(final Card card) {
|
||||
this.add(CardDb.getCard(card));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the.
|
||||
@@ -84,9 +73,9 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
* @param amount the 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 )
|
||||
cp = CardDb.variants().tryGetCard(cardName, setCode);
|
||||
cp = StaticData.instance().getVariantCards().tryGetCard(cardName, setCode);
|
||||
|
||||
if ( cp != null)
|
||||
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 ));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -124,9 +101,9 @@ public class CardPool extends ItemPool<PaperCard> {
|
||||
* @param cardName the card name
|
||||
*/
|
||||
public void add(final String cardName, int cnt) {
|
||||
PaperCard cp = CardDb.instance().tryGetCard(cardName);
|
||||
PaperCard cp = StaticData.instance().getCommonCards().tryGetCard(cardName);
|
||||
if ( cp == null )
|
||||
cp = CardDb.variants().tryGetCard(cardName);
|
||||
cp = StaticData.instance().getVariantCards().tryGetCard(cardName);
|
||||
|
||||
if ( cp != null)
|
||||
this.add(cp, cnt);
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
@@ -34,12 +35,12 @@ import com.google.common.base.Function;
|
||||
import forge.card.CardDb;
|
||||
import forge.deck.io.DeckFileHeader;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.gui.deckeditor.tables.TableSorter;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.ItemPoolView;
|
||||
import forge.util.FileSection;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.ItemPoolSorter;
|
||||
import forge.util.ItemPoolView;
|
||||
|
||||
|
||||
/**
|
||||
@@ -76,7 +77,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
getOrCreate(DeckSection.Main);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.getName().hashCode();
|
||||
@@ -199,7 +200,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
||||
|
||||
private static List<String> writeCardPool(final ItemPoolView<PaperCard> pool) {
|
||||
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>();
|
||||
for (final Entry<PaperCard, Integer> e : main2sort) {
|
||||
out.add(serializeSingleCard(e.getKey(), e.getValue()));
|
||||
@@ -19,11 +19,13 @@ package forge.deck;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import forge.util.IHasName;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
// gameType is from Constant.GameType, like GameType.Regular
|
||||
|
||||
@@ -36,7 +38,7 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase> {
|
||||
* @param name0 the name0
|
||||
*/
|
||||
public DeckBase(final String name0) {
|
||||
this.name = name0;
|
||||
this.name = name0.replace('/', '_');
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -50,8 +52,8 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase> {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o instanceof Deck) {
|
||||
final Deck d = (Deck) o;
|
||||
if (o instanceof DeckBase) {
|
||||
final DeckBase d = (DeckBase) o;
|
||||
return this.getName().equals(d.getName());
|
||||
}
|
||||
return false;
|
||||
@@ -22,43 +22,37 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang.math.IntRange;
|
||||
|
||||
import forge.Singletons;
|
||||
import forge.card.CardCoreType;
|
||||
import forge.card.CardDb;
|
||||
import org.apache.commons.lang3.Range;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.card.CardType;
|
||||
import forge.card.ColorSet;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
/**
|
||||
* GameType is an enum to determine the type of current game. :)
|
||||
*/
|
||||
public enum DeckFormat {
|
||||
|
||||
|
||||
// Main board: allowed size SB: restriction Max distinct non basic cards
|
||||
Constructed ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0, 15), 4),
|
||||
QuestDeck ( new IntRange(40, Integer.MAX_VALUE), new IntRange(0, 15), 4),
|
||||
Limited ( new IntRange(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
|
||||
Commander ( new IntRange(99), new IntRange(0, 10), 1),
|
||||
Vanguard ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0), 4),
|
||||
Planechase ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0), 4),
|
||||
Archenemy ( new IntRange(60, Integer.MAX_VALUE), new IntRange(0), 4);
|
||||
Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
|
||||
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
|
||||
Commander ( Range.is(99), Range.between(0, 10), 1),
|
||||
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||
Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||
Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4);
|
||||
|
||||
private final IntRange mainRange;
|
||||
private final IntRange sideRange; // null => no check
|
||||
private final Range<Integer> mainRange;
|
||||
private final Range<Integer> sideRange; // null => no check
|
||||
private final int maxCardCopies;
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new game type.
|
||||
*
|
||||
* @param isLimited
|
||||
* the is limited
|
||||
*/
|
||||
DeckFormat(IntRange main, IntRange side, int maxCopies) {
|
||||
|
||||
DeckFormat(Range<Integer> main, Range<Integer> side, int maxCopies) {
|
||||
mainRange = main;
|
||||
sideRange = side;
|
||||
maxCardCopies = maxCopies;
|
||||
@@ -90,7 +84,7 @@ public enum DeckFormat {
|
||||
/**
|
||||
* @return the sideRange
|
||||
*/
|
||||
public IntRange getSideRange() {
|
||||
public Range<Integer> getSideRange() {
|
||||
return sideRange;
|
||||
}
|
||||
|
||||
@@ -98,7 +92,7 @@ public enum DeckFormat {
|
||||
/**
|
||||
* @return the mainRange
|
||||
*/
|
||||
public IntRange getMainRange() {
|
||||
public Range<Integer> getMainRange() {
|
||||
return mainRange;
|
||||
}
|
||||
|
||||
@@ -118,15 +112,10 @@ public enum DeckFormat {
|
||||
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 min = getMainRange().getMinimumInteger();
|
||||
int max = getMainRange().getMaximumInteger();
|
||||
int min = getMainRange().getMinimum();
|
||||
int max = getMainRange().getMaximum();
|
||||
|
||||
if (deckSize < min) {
|
||||
return String.format("should have a minimum of %d cards", min);
|
||||
@@ -189,7 +178,7 @@ public enum DeckFormat {
|
||||
int phenoms = 0;
|
||||
for (Entry<PaperCard, Integer> cp : planes) {
|
||||
|
||||
if (cp.getKey().getRules().getType().typeContains(CardCoreType.Phenomenon)) {
|
||||
if (cp.getKey().getRules().getType().typeContains(CardType.CoreType.Phenomenon)) {
|
||||
phenoms++;
|
||||
}
|
||||
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
|
||||
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());
|
||||
|
||||
if (!canHaveMultiple && cp.getValue() > maxCopies) {
|
||||
@@ -244,11 +233,11 @@ public enum DeckFormat {
|
||||
|
||||
// The sideboard must contain either 0 or 15 cards
|
||||
int sideboardSize = deck.has(DeckSection.Sideboard) ? deck.get(DeckSection.Sideboard).countAll() : 0;
|
||||
IntRange sbRange = getSideRange();
|
||||
if (sbRange != null && sideboardSize > 0 && !sbRange.containsInteger(sideboardSize)) {
|
||||
return sbRange.getMinimumInteger() == sbRange.getMaximumInteger()
|
||||
? String.format("must have a sideboard of %d cards or no sideboard at all", sbRange.getMaximumInteger())
|
||||
: String.format("must have a sideboard of %d to %d cards or no sideboard at all", sbRange.getMinimumInteger(), sbRange.getMaximumInteger());
|
||||
Range<Integer> sbRange = getSideRange();
|
||||
if (sbRange != null && sideboardSize > 0 && !sbRange.contains(sideboardSize)) {
|
||||
return sbRange.getMinimum() == sbRange.getMaximum()
|
||||
? String.format("must have a sideboard of %d cards or no sideboard at all", sbRange.getMaximum())
|
||||
: String.format("must have a sideboard of %d to %d cards or no sideboard at all", sbRange.getMinimum(), sbRange.getMaximum());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -17,12 +17,15 @@
|
||||
*/
|
||||
package forge.deck;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.card.CardDb;
|
||||
import forge.card.ICardDatabase;
|
||||
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 READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)");
|
||||
|
||||
/**
|
||||
* Recognize line.
|
||||
*
|
||||
* @param rawLine
|
||||
* the raw_line
|
||||
* @param newestEdition
|
||||
* get the newest available edition?
|
||||
*
|
||||
* @return the token
|
||||
*/
|
||||
public static Token recognizeLine(final String rawLine, final boolean newestEdition) {
|
||||
|
||||
private final boolean useLastSet;
|
||||
private final ICardDatabase db;
|
||||
private Date recognizeCardsPrintedBefore = null;
|
||||
|
||||
public DeckRecognizer(boolean fromLatestSet, CardDb db) {
|
||||
useLastSet = fromLatestSet;
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
public Token recognizeLine(final String rawLine) {
|
||||
if (StringUtils.isBlank(rawLine)) {
|
||||
return new Token(TokenType.Comment, 0, rawLine);
|
||||
}
|
||||
@@ -181,7 +184,7 @@ public class DeckRecognizer {
|
||||
if (foundNumbersInFront.matches()) {
|
||||
final String cardName = foundNumbersInFront.group(2);
|
||||
final int amount = Integer.parseInt(foundNumbersInFront.group(1));
|
||||
result = DeckRecognizer.recognizePossibleNameAndNumber(cardName, amount, newestEdition);
|
||||
result = recognizePossibleNameAndNumber(cardName, amount);
|
||||
} /*
|
||||
* else if (foundNumbersInBack.matches()) { String cardName =
|
||||
* foundNumbersInBack.group(1); int amount =
|
||||
@@ -189,17 +192,25 @@ public class DeckRecognizer {
|
||||
* Token(cardName, amount); }
|
||||
*/
|
||||
else {
|
||||
if (null != CardDb.instance().tryGetCard(line)) {
|
||||
return Token.knownCard(CardDb.instance().getCard(line, newestEdition), 1);
|
||||
PaperCard pc = tryGetCard(line);
|
||||
if (null != pc) {
|
||||
return Token.knownCard(pc, 1);
|
||||
}
|
||||
result = DeckRecognizer.recognizeNonCard(line, 1);
|
||||
}
|
||||
return result != null ? result : new Token(TokenType.UnknownText, 0, line);
|
||||
}
|
||||
|
||||
private static Token recognizePossibleNameAndNumber(final String name, final int n, final boolean newestEdition) {
|
||||
if (null != CardDb.instance().tryGetCard(name)) {
|
||||
return Token.knownCard(CardDb.instance().getCard(name, newestEdition), n);
|
||||
private PaperCard tryGetCard(String text) {
|
||||
if(recognizeCardsPrintedBefore != null )
|
||||
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
|
||||
@@ -260,4 +271,16 @@ public class DeckRecognizer {
|
||||
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 forge.deck.DeckFormat;
|
||||
import forge.game.player.PlayerType;
|
||||
import forge.util.FileSection;
|
||||
|
||||
/**
|
||||
@@ -48,7 +47,6 @@ public class DeckFileHeader {
|
||||
private static final String PLAYER_TYPE = "PlayerType";
|
||||
|
||||
private final DeckFormat deckType;
|
||||
private final PlayerType playerType;
|
||||
private final boolean customPool;
|
||||
|
||||
private final String name;
|
||||
@@ -56,6 +54,15 @@ public class DeckFileHeader {
|
||||
|
||||
private final Set<String> tags;
|
||||
|
||||
private final boolean intendedForAi;
|
||||
|
||||
/**
|
||||
* @return the intendedForAi
|
||||
*/
|
||||
public boolean isIntendedForAi() {
|
||||
return intendedForAi;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for Constructor.
|
||||
*
|
||||
@@ -67,8 +74,7 @@ public class DeckFileHeader {
|
||||
this.comment = kvPairs.get(DeckFileHeader.COMMENT);
|
||||
this.deckType = DeckFormat.smartValueOf(kvPairs.get(DeckFileHeader.DECK_TYPE), DeckFormat.Constructed);
|
||||
this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL);
|
||||
boolean isForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE));
|
||||
this.playerType = isForAi ? PlayerType.COMPUTER : PlayerType.HUMAN;
|
||||
this.intendedForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE));
|
||||
this.tags = new TreeSet<String>();
|
||||
|
||||
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.
|
||||
*
|
||||
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 forge.Singletons;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.SealedProductTemplate;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
public class BoosterPack extends OpenablePack {
|
||||
public class BoosterPack extends SealedProduct {
|
||||
private final int artIndex;
|
||||
private final int hash;
|
||||
|
||||
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() {
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
public BoosterPack(final String name0, final SealedProductTemplate boosterData) {
|
||||
public BoosterPack(final String name0, final Template 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;
|
||||
hash = super.hashCode() ^ artIndex;
|
||||
}
|
||||
@@ -58,7 +57,7 @@ public class BoosterPack extends OpenablePack {
|
||||
return new BoosterPack(name, contents);
|
||||
}
|
||||
|
||||
public SealedProductTemplate getBoosterData() {
|
||||
public Template getBoosterData() {
|
||||
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.collect.Lists;
|
||||
|
||||
import forge.Card;
|
||||
//import forge.Card;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.PredicateString;
|
||||
|
||||
public interface IPaperCard extends InventoryItem {
|
||||
@@ -161,7 +160,4 @@ public interface IPaperCard extends InventoryItem {
|
||||
|
||||
public abstract String getItemType();
|
||||
|
||||
public abstract Card getMatchingForgeCard();
|
||||
public abstract Card toForgeCard(Player owner);
|
||||
|
||||
}
|
||||
@@ -17,19 +17,14 @@
|
||||
*/
|
||||
package forge.item;
|
||||
|
||||
import forge.util.IHasName;
|
||||
|
||||
/**
|
||||
* Interface to define a player's inventory may hold. Should include
|
||||
* CardPrinted, Booster, Pets, Plants... etc
|
||||
*/
|
||||
public interface InventoryItem {
|
||||
|
||||
/**
|
||||
* An inventory item has to provide a name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
public interface InventoryItem extends IHasName
|
||||
{
|
||||
/**
|
||||
* Return type as a string.
|
||||
*
|
||||
@@ -17,16 +17,10 @@
|
||||
*/
|
||||
package forge.item;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.cardfactory.CardFactory;
|
||||
import forge.game.player.Player;
|
||||
|
||||
|
||||
/**
|
||||
@@ -40,7 +34,7 @@ import forge.game.player.Player;
|
||||
*/
|
||||
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
|
||||
// Reference to rules
|
||||
private final transient CardRules card;
|
||||
private final transient CardRules rules;
|
||||
|
||||
// These fields are kinda PK for PrintedCard
|
||||
public final String name;
|
||||
@@ -55,7 +49,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getEdition() {
|
||||
return this.edition;
|
||||
@@ -78,7 +72,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
|
||||
@Override
|
||||
public CardRules getRules() {
|
||||
return this.card;
|
||||
return this.rules;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,15 +80,11 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
return this.rarity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// @Override
|
||||
// public String getImageKey() {
|
||||
// return getImageLocator(getImageName(), getArtIndex(), true, false);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return "Card";
|
||||
@@ -106,7 +96,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
|
||||
@Override
|
||||
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>() {
|
||||
@@ -114,14 +104,16 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
public String apply(final PaperCard from) {
|
||||
return from.getName();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index) {
|
||||
this(c, edition0, rare, index, false);
|
||||
}
|
||||
|
||||
|
||||
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
|
||||
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.edition = edition0;
|
||||
this.artIndex = index;
|
||||
@@ -129,10 +121,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
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
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
@@ -186,34 +174,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
// 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)
|
||||
*
|
||||
@@ -2,12 +2,10 @@ package forge.item;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.cardfactory.CardFactory;
|
||||
import forge.game.player.Player;
|
||||
|
||||
|
||||
public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
||||
private String name;
|
||||
@@ -61,13 +59,6 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
||||
public String getImageFilename() { return imageFileName; }
|
||||
|
||||
@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; }
|
||||
}
|
||||
149
forge-core/src/main/java/forge/item/PreconDeck.java
Normal file
149
forge-core/src/main/java/forge/item/PreconDeck.java
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 forge.Singletons;
|
||||
import forge.StaticData;
|
||||
import forge.card.BoosterGenerator;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.SealedProductTemplate;
|
||||
|
||||
public class TournamentPack extends OpenablePack {
|
||||
public class TournamentPack extends SealedProduct {
|
||||
|
||||
/** The Constant fnFromSet. */
|
||||
public static final Function<CardEdition, TournamentPack> FN_FROM_SET = new Function<CardEdition, TournamentPack>() {
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
public TournamentPack(final String name0, final SealedProductTemplate boosterData) {
|
||||
public TournamentPack(final String name0, final Template 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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package forge.util.maps;
|
||||
package forge.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -31,8 +31,6 @@ import java.util.regex.Pattern;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import forge.error.BugReporter;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FileUtil class.
|
||||
@@ -46,6 +44,18 @@ public final class FileUtil {
|
||||
private FileUtil() {
|
||||
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>
|
||||
@@ -97,17 +107,12 @@ public final class FileUtil {
|
||||
}
|
||||
p.close();
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + file + " : " + ex);
|
||||
}
|
||||
} // writeAllDecks()
|
||||
|
||||
public static String readFileToString(String filename) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (String line : readFile(filename)) {
|
||||
s.append(line).append('\n');
|
||||
}
|
||||
return s.toString();
|
||||
return TextUtil.join(readFile(filename), "\n");
|
||||
}
|
||||
|
||||
public static List<String> readFile(final String filename) {
|
||||
@@ -133,7 +138,6 @@ public final class FileUtil {
|
||||
}
|
||||
return FileUtil.readAllLines(new FileReader(file), false);
|
||||
} catch (final Exception ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("FileUtil : readFile() error, " + ex);
|
||||
}
|
||||
} // readFile()
|
||||
@@ -168,7 +172,6 @@ public final class FileUtil {
|
||||
}
|
||||
in.close();
|
||||
} catch (final IOException ex) {
|
||||
BugReporter.reportException(ex);
|
||||
throw new RuntimeException("FileUtil : readAllLines() error, " + ex);
|
||||
}
|
||||
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;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -41,4 +42,8 @@ public interface IItemReader<T> {
|
||||
* @return the item key
|
||||
*/
|
||||
String getItemKey(T item);
|
||||
|
||||
Iterable<File> getSubFolders();
|
||||
|
||||
IItemReader<T> getReaderForFolder(File subfolder);
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package forge.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
@@ -37,4 +39,7 @@ public interface IItemSerializer<T> extends IItemReader<T> {
|
||||
* @param unit the unit
|
||||
*/
|
||||
void erase(T unit);
|
||||
|
||||
|
||||
File getDirectory();
|
||||
}
|
||||
@@ -15,16 +15,18 @@
|
||||
* 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;
|
||||
package forge.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import forge.item.InventoryItem;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* CardPool class.
|
||||
* ItemPool class.
|
||||
* </p>
|
||||
* Represents a list of cards with amount of each
|
||||
* Represents a list of items with amount of each
|
||||
*
|
||||
* @param <T>
|
||||
* an Object
|
||||
@@ -82,59 +84,59 @@ public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> {
|
||||
* @return a ItemPoolView
|
||||
*/
|
||||
public ItemPoolView<T> getView() {
|
||||
return new ItemPoolView<T>(Collections.unmodifiableMap(this.getCards()), this.getMyClass());
|
||||
return new ItemPoolView<T>(Collections.unmodifiableMap(this.getItems()), this.getMyClass());
|
||||
}
|
||||
|
||||
// Cards manipulation
|
||||
// Items manipulation
|
||||
/**
|
||||
*
|
||||
* Add Card.
|
||||
* Add a single item.
|
||||
*
|
||||
* @param card
|
||||
* @param item
|
||||
* a T
|
||||
*/
|
||||
public void add(final T card) {
|
||||
this.add(card, 1);
|
||||
public void add(final T item) {
|
||||
this.add(item, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* add method.
|
||||
* Add multiple items.
|
||||
*
|
||||
* @param card
|
||||
* @param item
|
||||
* a T
|
||||
* @param amount
|
||||
* a int
|
||||
*/
|
||||
public void add(final T card, final int amount) {
|
||||
public void add(final T item, final int amount) {
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
this.getCards().put(card, Integer.valueOf(this.count(card) + amount));
|
||||
this.setListInSync(false);
|
||||
this.getItems().put(item, Integer.valueOf(this.count(item) + amount));
|
||||
this.isListInSync = false;
|
||||
}
|
||||
|
||||
private void put(final T card, final int amount) {
|
||||
this.getCards().put(card, amount);
|
||||
this.setListInSync(false);
|
||||
private void put(final T item, final int amount) {
|
||||
this.getItems().put(item, amount);
|
||||
this.isListInSync = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* addAllCards.
|
||||
* addAllFlat.
|
||||
*
|
||||
* @param <U>
|
||||
* a InventoryItem
|
||||
* @param cards
|
||||
* @param items
|
||||
* a Iterable<U>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <U extends InventoryItem> void addAllFlat(final Iterable<U> cards) {
|
||||
for (final U cr : cards) {
|
||||
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.setListInSync(false);
|
||||
this.isListInSync = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,40 +155,40 @@ public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> {
|
||||
this.add((T) e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
this.setListInSync(false);
|
||||
this.isListInSync = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Remove.
|
||||
*
|
||||
* @param card
|
||||
* @param item
|
||||
* a T
|
||||
*/
|
||||
public boolean remove(final T card) {
|
||||
return this.remove(card, 1);
|
||||
public boolean remove(final T item) {
|
||||
return this.remove(item, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Remove.
|
||||
*
|
||||
* @param card
|
||||
* @param item
|
||||
* a T
|
||||
* @param amount
|
||||
* a int
|
||||
*/
|
||||
public boolean remove(final T card, final int amount) {
|
||||
final int count = this.count(card);
|
||||
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.getCards().remove(card);
|
||||
this.getItems().remove(item);
|
||||
} else {
|
||||
this.getCards().put(card, count - amount);
|
||||
this.getItems().put(item, count - amount);
|
||||
}
|
||||
this.setListInSync(false);
|
||||
this.isListInSync = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -221,7 +223,7 @@ public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> {
|
||||
* Clear.
|
||||
*/
|
||||
public void clear() {
|
||||
this.getCards().clear();
|
||||
this.setListInSync(false);
|
||||
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;
|
||||
}
|
||||
}
|
||||
134
forge-core/src/main/java/forge/util/Lang.java
Normal file
134
forge-core/src/main/java/forge/util/Lang.java
Normal file
@@ -0,0 +1,134 @@
|
||||
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(", ");
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ public class TextUtil {
|
||||
return mapAsString.toString();
|
||||
}
|
||||
|
||||
public static String[] split(CharSequence input, char delimiter) {
|
||||
public static String[] split(CharSequence input, char delimiter) {
|
||||
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '\0', '\0', true);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class TextUtil {
|
||||
|
||||
/**
|
||||
* Split string separated by a single char delimiter, can take parenthesis in account
|
||||
* It's faster than String.split, and allows parenthesis
|
||||
* It's faster than String.split, and allows parenthesis
|
||||
*/
|
||||
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int maxEntries, char openPar, char closePar, boolean skipEmpty) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
@@ -98,7 +98,18 @@ public class TextUtil {
|
||||
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
return trimParenthesis ? StringUtils.stripAll(toReturn, String.valueOf(openPar)) : toReturn;
|
||||
}
|
||||
|
||||
|
||||
public static String join(Iterable<String> strs, String delim) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String str : strs) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(delim);
|
||||
}
|
||||
sb.append(str);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an enum value to a printable label but upcasing the first letter
|
||||
* and lcasing all subsequent letters
|
||||
54
forge-core/src/main/java/forge/util/ThreadUtil.java
Normal file
54
forge-core/src/main/java/forge/util/ThreadUtil.java
Normal file
@@ -0,0 +1,54 @@
|
||||
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;
|
||||
49
forge-core/src/main/java/forge/util/storage/IStorage.java
Normal file
49
forge-core/src/main/java/forge/util/storage/IStorage.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.storage;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.util.IHasName;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
*/
|
||||
public interface IStorage<T> extends Iterable<T>, IHasName {
|
||||
|
||||
T get(final String name);
|
||||
|
||||
T find(final Predicate<T> condition);
|
||||
// todo: find(final Predicate<T> condition, boolean recursive).
|
||||
|
||||
Collection<String> getItemNames();
|
||||
|
||||
boolean contains(final String name);
|
||||
|
||||
int size();
|
||||
|
||||
void add(final T deck);
|
||||
|
||||
void delete(final String deckName);
|
||||
|
||||
IStorage<IStorage<T>> getFolders();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user